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