1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * bytcht-da7213.c - ASoc Machine driver for Intel Baytrail and 4 * Cherrytrail-based platforms, with Dialog DA7213 codec 5 * 6 * Copyright (C) 2017 Intel Corporation 7 * Author: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> 8 * 9 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 10 * 11 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 12 */ 13 14 #include <linux/module.h> 15 #include <linux/acpi.h> 16 #include <linux/platform_device.h> 17 #include <linux/slab.h> 18 #include <sound/pcm.h> 19 #include <sound/pcm_params.h> 20 #include <sound/soc.h> 21 #include <sound/soc-acpi.h> 22 #include "../../codecs/da7213.h" 23 #include "../atom/sst-atom-controls.h" 24 25 static const struct snd_kcontrol_new controls[] = { 26 SOC_DAPM_PIN_SWITCH("Headphone Jack"), 27 SOC_DAPM_PIN_SWITCH("Headset Mic"), 28 SOC_DAPM_PIN_SWITCH("Mic"), 29 SOC_DAPM_PIN_SWITCH("Aux In"), 30 }; 31 32 static const struct snd_soc_dapm_widget dapm_widgets[] = { 33 SND_SOC_DAPM_HP("Headphone Jack", NULL), 34 SND_SOC_DAPM_MIC("Headset Mic", NULL), 35 SND_SOC_DAPM_MIC("Mic", NULL), 36 SND_SOC_DAPM_LINE("Aux In", NULL), 37 }; 38 39 static const struct snd_soc_dapm_route audio_map[] = { 40 {"Headphone Jack", NULL, "HPL"}, 41 {"Headphone Jack", NULL, "HPR"}, 42 43 {"AUXL", NULL, "Aux In"}, 44 {"AUXR", NULL, "Aux In"}, 45 46 /* Assume Mic1 is linked to Headset and Mic2 to on-board mic */ 47 {"MIC1", NULL, "Headset Mic"}, 48 {"MIC2", NULL, "Mic"}, 49 50 /* SOC-codec link */ 51 {"ssp2 Tx", NULL, "codec_out0"}, 52 {"ssp2 Tx", NULL, "codec_out1"}, 53 {"codec_in0", NULL, "ssp2 Rx"}, 54 {"codec_in1", NULL, "ssp2 Rx"}, 55 56 {"Playback", NULL, "ssp2 Tx"}, 57 {"ssp2 Rx", NULL, "Capture"}, 58 }; 59 60 static int codec_fixup(struct snd_soc_pcm_runtime *rtd, 61 struct snd_pcm_hw_params *params) 62 { 63 int ret; 64 struct snd_interval *rate = hw_param_interval(params, 65 SNDRV_PCM_HW_PARAM_RATE); 66 struct snd_interval *channels = hw_param_interval(params, 67 SNDRV_PCM_HW_PARAM_CHANNELS); 68 69 /* The DSP will convert the FE rate to 48k, stereo, 24bits */ 70 rate->min = rate->max = 48000; 71 channels->min = channels->max = 2; 72 73 /* set SSP2 to 24-bit */ 74 params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); 75 76 /* 77 * Default mode for SSP configuration is TDM 4 slot, override config 78 * with explicit setting to I2S 2ch 24-bit. The word length is set with 79 * dai_set_tdm_slot() since there is no other API exposed 80 */ 81 ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0), 82 SND_SOC_DAIFMT_I2S | 83 SND_SOC_DAIFMT_NB_NF | 84 SND_SOC_DAIFMT_CBS_CFS); 85 if (ret < 0) { 86 dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); 87 return ret; 88 } 89 90 ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, 24); 91 if (ret < 0) { 92 dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); 93 return ret; 94 } 95 96 return 0; 97 } 98 99 static int aif1_startup(struct snd_pcm_substream *substream) 100 { 101 return snd_pcm_hw_constraint_single(substream->runtime, 102 SNDRV_PCM_HW_PARAM_RATE, 48000); 103 } 104 105 static int aif1_hw_params(struct snd_pcm_substream *substream, 106 struct snd_pcm_hw_params *params) 107 { 108 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 109 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 110 int ret; 111 112 ret = snd_soc_dai_set_sysclk(codec_dai, DA7213_CLKSRC_MCLK, 113 19200000, SND_SOC_CLOCK_IN); 114 if (ret < 0) 115 dev_err(codec_dai->dev, "can't set codec sysclk configuration\n"); 116 117 ret = snd_soc_dai_set_pll(codec_dai, 0, 118 DA7213_SYSCLK_PLL_SRM, 0, DA7213_PLL_FREQ_OUT_98304000); 119 if (ret < 0) { 120 dev_err(codec_dai->dev, "failed to start PLL: %d\n", ret); 121 return -EIO; 122 } 123 124 return ret; 125 } 126 127 static int aif1_hw_free(struct snd_pcm_substream *substream) 128 { 129 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 130 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 131 int ret; 132 133 ret = snd_soc_dai_set_pll(codec_dai, 0, 134 DA7213_SYSCLK_MCLK, 0, 0); 135 if (ret < 0) { 136 dev_err(codec_dai->dev, "failed to stop PLL: %d\n", ret); 137 return -EIO; 138 } 139 140 return ret; 141 } 142 143 static const struct snd_soc_ops aif1_ops = { 144 .startup = aif1_startup, 145 }; 146 147 static const struct snd_soc_ops ssp2_ops = { 148 .hw_params = aif1_hw_params, 149 .hw_free = aif1_hw_free, 150 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_port, 163 DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port"))); 164 SND_SOC_DAILINK_DEF(ssp2_codec, 165 DAILINK_COMP_ARRAY(COMP_CODEC("i2c-DLGS7213:00", 166 "da7213-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 dailink[] = { 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 = &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 = &aif1_ops, 189 SND_SOC_DAILINK_REG(deepbuffer, dummy, platform), 190 }, 191 /* CODEC<->CODEC link */ 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 .be_hw_params_fixup = codec_fixup, 200 .nonatomic = true, 201 .dpcm_playback = 1, 202 .dpcm_capture = 1, 203 .ops = &ssp2_ops, 204 SND_SOC_DAILINK_REG(ssp2_port, ssp2_codec, platform), 205 }, 206 }; 207 208 #if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) 209 /* use space before codec name to simplify card ID, and simplify driver name */ 210 #define CARD_NAME "bytcht da7213" /* card name will be 'sof-bytcht da7213' */ 211 #define DRIVER_NAME "SOF" 212 #else 213 #define CARD_NAME "bytcht-da7213" 214 #define DRIVER_NAME NULL /* card name will be used for driver name */ 215 #endif 216 217 /* SoC card */ 218 static struct snd_soc_card bytcht_da7213_card = { 219 .name = CARD_NAME, 220 .driver_name = DRIVER_NAME, 221 .owner = THIS_MODULE, 222 .dai_link = dailink, 223 .num_links = ARRAY_SIZE(dailink), 224 .controls = controls, 225 .num_controls = ARRAY_SIZE(controls), 226 .dapm_widgets = dapm_widgets, 227 .num_dapm_widgets = ARRAY_SIZE(dapm_widgets), 228 .dapm_routes = audio_map, 229 .num_dapm_routes = ARRAY_SIZE(audio_map), 230 }; 231 232 static char codec_name[SND_ACPI_I2C_ID_LEN]; 233 234 static int bytcht_da7213_probe(struct platform_device *pdev) 235 { 236 struct snd_soc_card *card; 237 struct snd_soc_acpi_mach *mach; 238 const char *platform_name; 239 struct acpi_device *adev; 240 int dai_index = 0; 241 int ret_val = 0; 242 int i; 243 244 mach = pdev->dev.platform_data; 245 card = &bytcht_da7213_card; 246 card->dev = &pdev->dev; 247 248 /* fix index of codec dai */ 249 for (i = 0; i < ARRAY_SIZE(dailink); i++) { 250 if (!strcmp(dailink[i].codecs->name, "i2c-DLGS7213:00")) { 251 dai_index = i; 252 break; 253 } 254 } 255 256 /* fixup codec name based on HID */ 257 adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1); 258 if (adev) { 259 snprintf(codec_name, sizeof(codec_name), 260 "i2c-%s", acpi_dev_name(adev)); 261 put_device(&adev->dev); 262 dailink[dai_index].codecs->name = codec_name; 263 } 264 265 /* override plaform name, if required */ 266 platform_name = mach->mach_params.platform; 267 268 ret_val = snd_soc_fixup_dai_links_platform_name(card, platform_name); 269 if (ret_val) 270 return ret_val; 271 272 ret_val = devm_snd_soc_register_card(&pdev->dev, card); 273 if (ret_val) { 274 dev_err(&pdev->dev, 275 "snd_soc_register_card failed %d\n", ret_val); 276 return ret_val; 277 } 278 platform_set_drvdata(pdev, card); 279 return ret_val; 280 } 281 282 static struct platform_driver bytcht_da7213_driver = { 283 .driver = { 284 .name = "bytcht_da7213", 285 #if IS_ENABLED(CONFIG_SND_SOC_SOF_BAYTRAIL) 286 .pm = &snd_soc_pm_ops, 287 #endif 288 }, 289 .probe = bytcht_da7213_probe, 290 }; 291 module_platform_driver(bytcht_da7213_driver); 292 293 MODULE_DESCRIPTION("ASoC Intel(R) Baytrail/Cherrytrail+DA7213 Machine driver"); 294 MODULE_AUTHOR("Pierre-Louis Bossart"); 295 MODULE_LICENSE("GPL v2"); 296 MODULE_ALIAS("platform:bytcht_da7213"); 297