1 // SPDX-License-Identifier: GPL-2.0-only 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/gpio/consumer.h> 10 #include <linux/module.h> 11 #include <linux/platform_device.h> 12 #include <linux/slab.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 = asoc_rtd_to_codec(rtd, 0)->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(asoc_rtd_to_codec(rtd, 0), 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(asoc_rtd_to_codec(rtd, 0), 50); 101 102 return 0; 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(asoc_rtd_to_cpu(rtd, 0), 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(asoc_rtd_to_cpu(rtd, 0), 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 SND_SOC_DAILINK_DEF(dummy, 155 DAILINK_COMP_ARRAY(COMP_DUMMY())); 156 157 SND_SOC_DAILINK_DEF(media, 158 DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai"))); 159 160 SND_SOC_DAILINK_DEF(deepbuffer, 161 DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai"))); 162 163 SND_SOC_DAILINK_DEF(ssp2, 164 DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port"))); 165 166 SND_SOC_DAILINK_DEF(cx2072x, 167 DAILINK_COMP_ARRAY(COMP_CODEC("i2c-14F10720:00", "cx2072x-hifi"))); 168 169 SND_SOC_DAILINK_DEF(platform, 170 DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform"))); 171 172 static struct snd_soc_dai_link byt_cht_cx2072x_dais[] = { 173 [MERR_DPCM_AUDIO] = { 174 .name = "Audio Port", 175 .stream_name = "Audio", 176 .nonatomic = true, 177 .dynamic = 1, 178 .dpcm_playback = 1, 179 .dpcm_capture = 1, 180 .ops = &byt_cht_cx2072x_aif1_ops, 181 SND_SOC_DAILINK_REG(media, dummy, platform), 182 }, 183 [MERR_DPCM_DEEP_BUFFER] = { 184 .name = "Deep-Buffer Audio Port", 185 .stream_name = "Deep-Buffer Audio", 186 .nonatomic = true, 187 .dynamic = 1, 188 .dpcm_playback = 1, 189 .ops = &byt_cht_cx2072x_aif1_ops, 190 SND_SOC_DAILINK_REG(deepbuffer, dummy, platform), 191 }, 192 /* back ends */ 193 { 194 .name = "SSP2-Codec", 195 .id = 0, 196 .no_pcm = 1, 197 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 198 | SND_SOC_DAIFMT_CBS_CFS, 199 .init = byt_cht_cx2072x_init, 200 .be_hw_params_fixup = byt_cht_cx2072x_fixup, 201 .nonatomic = true, 202 .dpcm_playback = 1, 203 .dpcm_capture = 1, 204 SND_SOC_DAILINK_REG(ssp2, cx2072x, platform), 205 }, 206 }; 207 208 /* use space before codec name to simplify card ID, and simplify driver name */ 209 #define SOF_CARD_NAME "bytcht cx2072x" /* card name will be 'sof-bytcht cx2072x' */ 210 #define SOF_DRIVER_NAME "SOF" 211 212 #define CARD_NAME "bytcht-cx2072x" 213 #define DRIVER_NAME NULL /* card name will be used for driver name */ 214 215 /* SoC card */ 216 static struct snd_soc_card byt_cht_cx2072x_card = { 217 .name = CARD_NAME, 218 .driver_name = DRIVER_NAME, 219 .owner = THIS_MODULE, 220 .dai_link = byt_cht_cx2072x_dais, 221 .num_links = ARRAY_SIZE(byt_cht_cx2072x_dais), 222 .dapm_widgets = byt_cht_cx2072x_widgets, 223 .num_dapm_widgets = ARRAY_SIZE(byt_cht_cx2072x_widgets), 224 .dapm_routes = byt_cht_cx2072x_audio_map, 225 .num_dapm_routes = ARRAY_SIZE(byt_cht_cx2072x_audio_map), 226 .controls = byt_cht_cx2072x_controls, 227 .num_controls = ARRAY_SIZE(byt_cht_cx2072x_controls), 228 }; 229 230 static char codec_name[SND_ACPI_I2C_ID_LEN]; 231 232 static int snd_byt_cht_cx2072x_probe(struct platform_device *pdev) 233 { 234 struct snd_soc_acpi_mach *mach; 235 struct acpi_device *adev; 236 int dai_index = 0; 237 bool sof_parent; 238 int i, ret; 239 240 byt_cht_cx2072x_card.dev = &pdev->dev; 241 mach = dev_get_platdata(&pdev->dev); 242 243 /* fix index of codec dai */ 244 for (i = 0; i < ARRAY_SIZE(byt_cht_cx2072x_dais); i++) { 245 if (!strcmp(byt_cht_cx2072x_dais[i].codecs->name, 246 "i2c-14F10720:00")) { 247 dai_index = i; 248 break; 249 } 250 } 251 252 /* fixup codec name based on HID */ 253 adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1); 254 if (adev) { 255 snprintf(codec_name, sizeof(codec_name), "i2c-%s", 256 acpi_dev_name(adev)); 257 put_device(&adev->dev); 258 byt_cht_cx2072x_dais[dai_index].codecs->name = codec_name; 259 } 260 261 /* override plaform name, if required */ 262 ret = snd_soc_fixup_dai_links_platform_name(&byt_cht_cx2072x_card, 263 mach->mach_params.platform); 264 if (ret) 265 return ret; 266 267 sof_parent = snd_soc_acpi_sof_parent(&pdev->dev); 268 269 /* set card and driver name */ 270 if (sof_parent) { 271 byt_cht_cx2072x_card.name = SOF_CARD_NAME; 272 byt_cht_cx2072x_card.driver_name = SOF_DRIVER_NAME; 273 } else { 274 byt_cht_cx2072x_card.name = CARD_NAME; 275 byt_cht_cx2072x_card.driver_name = DRIVER_NAME; 276 } 277 278 /* set pm ops */ 279 if (sof_parent) 280 pdev->dev.driver->pm = &snd_soc_pm_ops; 281 282 return devm_snd_soc_register_card(&pdev->dev, &byt_cht_cx2072x_card); 283 } 284 285 static struct platform_driver snd_byt_cht_cx2072x_driver = { 286 .driver = { 287 .name = "bytcht_cx2072x", 288 }, 289 .probe = snd_byt_cht_cx2072x_probe, 290 }; 291 module_platform_driver(snd_byt_cht_cx2072x_driver); 292 293 MODULE_DESCRIPTION("ASoC Intel(R) Baytrail/Cherrytrail Machine driver"); 294 MODULE_LICENSE("GPL v2"); 295 MODULE_ALIAS("platform:bytcht_cx2072x"); 296