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 SND_SOC_DAILINK_DEF(dummy, 154 DAILINK_COMP_ARRAY(COMP_DUMMY())); 155 156 SND_SOC_DAILINK_DEF(media, 157 DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai"))); 158 159 SND_SOC_DAILINK_DEF(deepbuffer, 160 DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai"))); 161 162 SND_SOC_DAILINK_DEF(ssp2, 163 DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port"))); 164 165 SND_SOC_DAILINK_DEF(cx2072x, 166 DAILINK_COMP_ARRAY(COMP_CODEC("i2c-14F10720:00", "cx2072x-hifi"))); 167 168 SND_SOC_DAILINK_DEF(platform, 169 DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform"))); 170 171 static struct snd_soc_dai_link byt_cht_cx2072x_dais[] = { 172 [MERR_DPCM_AUDIO] = { 173 .name = "Audio Port", 174 .stream_name = "Audio", 175 .nonatomic = true, 176 .dynamic = 1, 177 .dpcm_playback = 1, 178 .dpcm_capture = 1, 179 .ops = &byt_cht_cx2072x_aif1_ops, 180 SND_SOC_DAILINK_REG(media, dummy, platform), 181 }, 182 [MERR_DPCM_DEEP_BUFFER] = { 183 .name = "Deep-Buffer Audio Port", 184 .stream_name = "Deep-Buffer Audio", 185 .nonatomic = true, 186 .dynamic = 1, 187 .dpcm_playback = 1, 188 .ops = &byt_cht_cx2072x_aif1_ops, 189 SND_SOC_DAILINK_REG(deepbuffer, dummy, platform), 190 }, 191 /* back ends */ 192 { 193 .name = "SSP2-Codec", 194 .id = 0, 195 .no_pcm = 1, 196 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 197 | SND_SOC_DAIFMT_CBS_CFS, 198 .init = byt_cht_cx2072x_init, 199 .be_hw_params_fixup = byt_cht_cx2072x_fixup, 200 .nonatomic = true, 201 .dpcm_playback = 1, 202 .dpcm_capture = 1, 203 SND_SOC_DAILINK_REG(ssp2, cx2072x, platform), 204 }, 205 }; 206 207 /* SoC card */ 208 static struct snd_soc_card byt_cht_cx2072x_card = { 209 .name = "bytcht-cx2072x", 210 .owner = THIS_MODULE, 211 .dai_link = byt_cht_cx2072x_dais, 212 .num_links = ARRAY_SIZE(byt_cht_cx2072x_dais), 213 .dapm_widgets = byt_cht_cx2072x_widgets, 214 .num_dapm_widgets = ARRAY_SIZE(byt_cht_cx2072x_widgets), 215 .dapm_routes = byt_cht_cx2072x_audio_map, 216 .num_dapm_routes = ARRAY_SIZE(byt_cht_cx2072x_audio_map), 217 .controls = byt_cht_cx2072x_controls, 218 .num_controls = ARRAY_SIZE(byt_cht_cx2072x_controls), 219 }; 220 221 static char codec_name[SND_ACPI_I2C_ID_LEN]; 222 223 static int snd_byt_cht_cx2072x_probe(struct platform_device *pdev) 224 { 225 struct snd_soc_acpi_mach *mach; 226 struct acpi_device *adev; 227 int dai_index = 0; 228 int i, ret; 229 230 byt_cht_cx2072x_card.dev = &pdev->dev; 231 mach = dev_get_platdata(&pdev->dev); 232 233 /* fix index of codec dai */ 234 for (i = 0; i < ARRAY_SIZE(byt_cht_cx2072x_dais); i++) { 235 if (!strcmp(byt_cht_cx2072x_dais[i].codecs->name, 236 "i2c-14F10720:00")) { 237 dai_index = i; 238 break; 239 } 240 } 241 242 /* fixup codec name based on HID */ 243 adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1); 244 if (adev) { 245 snprintf(codec_name, sizeof(codec_name), "i2c-%s", 246 acpi_dev_name(adev)); 247 put_device(&adev->dev); 248 byt_cht_cx2072x_dais[dai_index].codecs->name = codec_name; 249 } 250 251 /* override plaform name, if required */ 252 ret = snd_soc_fixup_dai_links_platform_name(&byt_cht_cx2072x_card, 253 mach->mach_params.platform); 254 if (ret) 255 return ret; 256 257 return devm_snd_soc_register_card(&pdev->dev, &byt_cht_cx2072x_card); 258 } 259 260 static struct platform_driver snd_byt_cht_cx2072x_driver = { 261 .driver = { 262 .name = "bytcht_cx2072x", 263 }, 264 .probe = snd_byt_cht_cx2072x_probe, 265 }; 266 module_platform_driver(snd_byt_cht_cx2072x_driver); 267 268 MODULE_DESCRIPTION("ASoC Intel(R) Baytrail/Cherrytrail Machine driver"); 269 MODULE_LICENSE("GPL v2"); 270 MODULE_ALIAS("platform:bytcht_cx2072x"); 271