1 // SPDX-License-Identifier: GPL-2.0 2 // 3 // ASoC DPCM Machine driver for Baytrail / Cherrytrail platforms with 4 // CX2072X codec 5 // 6 7 #include <linux/acpi.h> 8 #include <linux/device.h> 9 #include <linux/module.h> 10 #include <linux/platform_device.h> 11 #include <linux/slab.h> 12 #include <asm/platform_sst_audio.h> 13 #include <sound/pcm.h> 14 #include <sound/pcm_params.h> 15 #include <sound/jack.h> 16 #include <sound/soc.h> 17 #include <sound/soc-acpi.h> 18 #include "../../codecs/cx2072x.h" 19 #include "../atom/sst-atom-controls.h" 20 21 static const struct snd_soc_dapm_widget byt_cht_cx2072x_widgets[] = { 22 SND_SOC_DAPM_HP("Headphone", NULL), 23 SND_SOC_DAPM_MIC("Headset Mic", NULL), 24 SND_SOC_DAPM_MIC("Int Mic", NULL), 25 SND_SOC_DAPM_SPK("Ext Spk", NULL), 26 }; 27 28 static const struct snd_soc_dapm_route byt_cht_cx2072x_audio_map[] = { 29 /* External Speakers: HFL, HFR */ 30 {"Headphone", NULL, "PORTA"}, 31 {"Ext Spk", NULL, "PORTG"}, 32 {"PORTC", NULL, "Int Mic"}, 33 {"PORTD", NULL, "Headset Mic"}, 34 35 {"Playback", NULL, "ssp2 Tx"}, 36 {"ssp2 Tx", NULL, "codec_out0"}, 37 {"ssp2 Tx", NULL, "codec_out1"}, 38 {"codec_in0", NULL, "ssp2 Rx"}, 39 {"codec_in1", NULL, "ssp2 Rx"}, 40 {"ssp2 Rx", NULL, "Capture"}, 41 }; 42 43 static const struct snd_kcontrol_new byt_cht_cx2072x_controls[] = { 44 SOC_DAPM_PIN_SWITCH("Headphone"), 45 SOC_DAPM_PIN_SWITCH("Headset Mic"), 46 SOC_DAPM_PIN_SWITCH("Int Mic"), 47 SOC_DAPM_PIN_SWITCH("Ext Spk"), 48 }; 49 50 static struct snd_soc_jack byt_cht_cx2072x_headset; 51 52 /* Headset jack detection DAPM pins */ 53 static struct snd_soc_jack_pin byt_cht_cx2072x_headset_pins[] = { 54 { 55 .pin = "Headset Mic", 56 .mask = SND_JACK_MICROPHONE, 57 }, 58 { 59 .pin = "Headphone", 60 .mask = SND_JACK_HEADPHONE, 61 }, 62 }; 63 64 static const struct acpi_gpio_params byt_cht_cx2072x_headset_gpios; 65 static const struct acpi_gpio_mapping byt_cht_cx2072x_acpi_gpios[] = { 66 { "headset-gpios", &byt_cht_cx2072x_headset_gpios, 1 }, 67 {}, 68 }; 69 70 static int byt_cht_cx2072x_init(struct snd_soc_pcm_runtime *rtd) 71 { 72 struct snd_soc_card *card = rtd->card; 73 struct snd_soc_component *codec = rtd->codec_dai->component; 74 int ret; 75 76 if (devm_acpi_dev_add_driver_gpios(codec->dev, 77 byt_cht_cx2072x_acpi_gpios)) 78 dev_warn(rtd->dev, "Unable to add GPIO mapping table\n"); 79 80 card->dapm.idle_bias_off = true; 81 82 /* set the default PLL rate, the clock is handled by the codec driver */ 83 ret = snd_soc_dai_set_sysclk(rtd->codec_dai, CX2072X_MCLK_EXTERNAL_PLL, 84 19200000, SND_SOC_CLOCK_IN); 85 if (ret) { 86 dev_err(rtd->dev, "Could not set sysclk\n"); 87 return ret; 88 } 89 90 ret = snd_soc_card_jack_new(card, "Headset", 91 SND_JACK_HEADSET | SND_JACK_BTN_0, 92 &byt_cht_cx2072x_headset, 93 byt_cht_cx2072x_headset_pins, 94 ARRAY_SIZE(byt_cht_cx2072x_headset_pins)); 95 if (ret) 96 return ret; 97 98 snd_soc_component_set_jack(codec, &byt_cht_cx2072x_headset, NULL); 99 100 snd_soc_dai_set_bclk_ratio(rtd->codec_dai, 50); 101 102 return ret; 103 } 104 105 static int byt_cht_cx2072x_fixup(struct snd_soc_pcm_runtime *rtd, 106 struct snd_pcm_hw_params *params) 107 { 108 struct snd_interval *rate = 109 hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); 110 struct snd_interval *channels = 111 hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); 112 int ret; 113 114 /* The DSP will covert the FE rate to 48k, stereo, 24bits */ 115 rate->min = rate->max = 48000; 116 channels->min = channels->max = 2; 117 118 /* set SSP2 to 24-bit */ 119 params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); 120 121 /* 122 * Default mode for SSP configuration is TDM 4 slot, override config 123 * with explicit setting to I2S 2ch 24-bit. The word length is set with 124 * dai_set_tdm_slot() since there is no other API exposed 125 */ 126 ret = snd_soc_dai_set_fmt(rtd->cpu_dai, 127 SND_SOC_DAIFMT_I2S | 128 SND_SOC_DAIFMT_NB_NF | 129 SND_SOC_DAIFMT_CBS_CFS); 130 if (ret < 0) { 131 dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); 132 return ret; 133 } 134 135 ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24); 136 if (ret < 0) { 137 dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); 138 return ret; 139 } 140 141 return 0; 142 } 143 144 static int byt_cht_cx2072x_aif1_startup(struct snd_pcm_substream *substream) 145 { 146 return snd_pcm_hw_constraint_single(substream->runtime, 147 SNDRV_PCM_HW_PARAM_RATE, 48000); 148 } 149 150 static struct snd_soc_ops byt_cht_cx2072x_aif1_ops = { 151 .startup = byt_cht_cx2072x_aif1_startup, 152 }; 153 154 static struct snd_soc_dai_link byt_cht_cx2072x_dais[] = { 155 [MERR_DPCM_AUDIO] = { 156 .name = "Audio Port", 157 .stream_name = "Audio", 158 .cpu_dai_name = "media-cpu-dai", 159 .codec_dai_name = "snd-soc-dummy-dai", 160 .codec_name = "snd-soc-dummy", 161 .platform_name = "sst-mfld-platform", 162 .nonatomic = true, 163 .dynamic = 1, 164 .dpcm_playback = 1, 165 .dpcm_capture = 1, 166 .ops = &byt_cht_cx2072x_aif1_ops, 167 }, 168 [MERR_DPCM_DEEP_BUFFER] = { 169 .name = "Deep-Buffer Audio Port", 170 .stream_name = "Deep-Buffer Audio", 171 .cpu_dai_name = "deepbuffer-cpu-dai", 172 .codec_dai_name = "snd-soc-dummy-dai", 173 .codec_name = "snd-soc-dummy", 174 .platform_name = "sst-mfld-platform", 175 .nonatomic = true, 176 .dynamic = 1, 177 .dpcm_playback = 1, 178 .ops = &byt_cht_cx2072x_aif1_ops, 179 }, 180 /* back ends */ 181 { 182 .name = "SSP2-Codec", 183 .id = 0, 184 .cpu_dai_name = "ssp2-port", 185 .platform_name = "sst-mfld-platform", 186 .no_pcm = 1, 187 .codec_dai_name = "cx2072x-hifi", 188 .codec_name = "i2c-14F10720:00", 189 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 190 | SND_SOC_DAIFMT_CBS_CFS, 191 .init = byt_cht_cx2072x_init, 192 .be_hw_params_fixup = byt_cht_cx2072x_fixup, 193 .nonatomic = true, 194 .dpcm_playback = 1, 195 .dpcm_capture = 1, 196 }, 197 }; 198 199 /* SoC card */ 200 static struct snd_soc_card byt_cht_cx2072x_card = { 201 .name = "bytcht-cx2072x", 202 .owner = THIS_MODULE, 203 .dai_link = byt_cht_cx2072x_dais, 204 .num_links = ARRAY_SIZE(byt_cht_cx2072x_dais), 205 .dapm_widgets = byt_cht_cx2072x_widgets, 206 .num_dapm_widgets = ARRAY_SIZE(byt_cht_cx2072x_widgets), 207 .dapm_routes = byt_cht_cx2072x_audio_map, 208 .num_dapm_routes = ARRAY_SIZE(byt_cht_cx2072x_audio_map), 209 .controls = byt_cht_cx2072x_controls, 210 .num_controls = ARRAY_SIZE(byt_cht_cx2072x_controls), 211 }; 212 213 static char codec_name[SND_ACPI_I2C_ID_LEN]; 214 215 static int snd_byt_cht_cx2072x_probe(struct platform_device *pdev) 216 { 217 struct snd_soc_acpi_mach *mach; 218 struct acpi_device *adev; 219 int dai_index = 0; 220 int i, ret; 221 222 byt_cht_cx2072x_card.dev = &pdev->dev; 223 mach = dev_get_platdata(&pdev->dev); 224 225 /* fix index of codec dai */ 226 for (i = 0; i < ARRAY_SIZE(byt_cht_cx2072x_dais); i++) { 227 if (!strcmp(byt_cht_cx2072x_dais[i].codec_name, 228 "i2c-14F10720:00")) { 229 dai_index = i; 230 break; 231 } 232 } 233 234 /* fixup codec name based on HID */ 235 adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1); 236 if (adev) { 237 snprintf(codec_name, sizeof(codec_name), "i2c-%s", 238 acpi_dev_name(adev)); 239 put_device(&adev->dev); 240 byt_cht_cx2072x_dais[dai_index].codec_name = codec_name; 241 } 242 243 /* override plaform name, if required */ 244 ret = snd_soc_fixup_dai_links_platform_name(&byt_cht_cx2072x_card, 245 mach->mach_params.platform); 246 if (ret) 247 return ret; 248 249 return devm_snd_soc_register_card(&pdev->dev, &byt_cht_cx2072x_card); 250 } 251 252 static struct platform_driver snd_byt_cht_cx2072x_driver = { 253 .driver = { 254 .name = "bytcht_cx2072x", 255 }, 256 .probe = snd_byt_cht_cx2072x_probe, 257 }; 258 module_platform_driver(snd_byt_cht_cx2072x_driver); 259 260 MODULE_DESCRIPTION("ASoC Intel(R) Baytrail/Cherrytrail Machine driver"); 261 MODULE_LICENSE("GPL v2"); 262 MODULE_ALIAS("platform:bytcht_cx2072x"); 263