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 <asm/platform_sst_audio.h> 19 #include <sound/pcm.h> 20 #include <sound/pcm_params.h> 21 #include <sound/soc.h> 22 #include <sound/soc-acpi.h> 23 #include "../../codecs/da7213.h" 24 #include "../atom/sst-atom-controls.h" 25 26 static const struct snd_kcontrol_new controls[] = { 27 SOC_DAPM_PIN_SWITCH("Headphone Jack"), 28 SOC_DAPM_PIN_SWITCH("Headset Mic"), 29 SOC_DAPM_PIN_SWITCH("Mic"), 30 SOC_DAPM_PIN_SWITCH("Aux In"), 31 }; 32 33 static const struct snd_soc_dapm_widget dapm_widgets[] = { 34 SND_SOC_DAPM_HP("Headphone Jack", NULL), 35 SND_SOC_DAPM_MIC("Headset Mic", NULL), 36 SND_SOC_DAPM_MIC("Mic", NULL), 37 SND_SOC_DAPM_LINE("Aux In", NULL), 38 }; 39 40 static const struct snd_soc_dapm_route audio_map[] = { 41 {"Headphone Jack", NULL, "HPL"}, 42 {"Headphone Jack", NULL, "HPR"}, 43 44 {"AUXL", NULL, "Aux In"}, 45 {"AUXR", NULL, "Aux In"}, 46 47 /* Assume Mic1 is linked to Headset and Mic2 to on-board mic */ 48 {"MIC1", NULL, "Headset Mic"}, 49 {"MIC2", NULL, "Mic"}, 50 51 /* SOC-codec link */ 52 {"ssp2 Tx", NULL, "codec_out0"}, 53 {"ssp2 Tx", NULL, "codec_out1"}, 54 {"codec_in0", NULL, "ssp2 Rx"}, 55 {"codec_in1", NULL, "ssp2 Rx"}, 56 57 {"Playback", NULL, "ssp2 Tx"}, 58 {"ssp2 Rx", NULL, "Capture"}, 59 }; 60 61 static int codec_fixup(struct snd_soc_pcm_runtime *rtd, 62 struct snd_pcm_hw_params *params) 63 { 64 int ret; 65 struct snd_interval *rate = hw_param_interval(params, 66 SNDRV_PCM_HW_PARAM_RATE); 67 struct snd_interval *channels = hw_param_interval(params, 68 SNDRV_PCM_HW_PARAM_CHANNELS); 69 70 /* The DSP will convert the FE rate to 48k, stereo, 24bits */ 71 rate->min = rate->max = 48000; 72 channels->min = channels->max = 2; 73 74 /* set SSP2 to 24-bit */ 75 params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); 76 77 /* 78 * Default mode for SSP configuration is TDM 4 slot, override config 79 * with explicit setting to I2S 2ch 24-bit. The word length is set with 80 * dai_set_tdm_slot() since there is no other API exposed 81 */ 82 ret = snd_soc_dai_set_fmt(rtd->cpu_dai, 83 SND_SOC_DAIFMT_I2S | 84 SND_SOC_DAIFMT_NB_NF | 85 SND_SOC_DAIFMT_CBS_CFS); 86 if (ret < 0) { 87 dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); 88 return ret; 89 } 90 91 ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24); 92 if (ret < 0) { 93 dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); 94 return ret; 95 } 96 97 return 0; 98 } 99 100 static int aif1_startup(struct snd_pcm_substream *substream) 101 { 102 return snd_pcm_hw_constraint_single(substream->runtime, 103 SNDRV_PCM_HW_PARAM_RATE, 48000); 104 } 105 106 static int aif1_hw_params(struct snd_pcm_substream *substream, 107 struct snd_pcm_hw_params *params) 108 { 109 struct snd_soc_pcm_runtime *rtd = substream->private_data; 110 struct snd_soc_dai *codec_dai = rtd->codec_dai; 111 int ret; 112 113 ret = snd_soc_dai_set_sysclk(codec_dai, DA7213_CLKSRC_MCLK, 114 19200000, SND_SOC_CLOCK_IN); 115 if (ret < 0) 116 dev_err(codec_dai->dev, "can't set codec sysclk configuration\n"); 117 118 ret = snd_soc_dai_set_pll(codec_dai, 0, 119 DA7213_SYSCLK_PLL_SRM, 0, DA7213_PLL_FREQ_OUT_98304000); 120 if (ret < 0) { 121 dev_err(codec_dai->dev, "failed to start PLL: %d\n", ret); 122 return -EIO; 123 } 124 125 return ret; 126 } 127 128 static int aif1_hw_free(struct snd_pcm_substream *substream) 129 { 130 struct snd_soc_pcm_runtime *rtd = substream->private_data; 131 struct snd_soc_dai *codec_dai = rtd->codec_dai; 132 int ret; 133 134 ret = snd_soc_dai_set_pll(codec_dai, 0, 135 DA7213_SYSCLK_MCLK, 0, 0); 136 if (ret < 0) { 137 dev_err(codec_dai->dev, "failed to stop PLL: %d\n", ret); 138 return -EIO; 139 } 140 141 return ret; 142 } 143 144 static const struct snd_soc_ops aif1_ops = { 145 .startup = aif1_startup, 146 }; 147 148 static const struct snd_soc_ops ssp2_ops = { 149 .hw_params = aif1_hw_params, 150 .hw_free = aif1_hw_free, 151 152 }; 153 154 static struct snd_soc_dai_link dailink[] = { 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 = &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 = &aif1_ops, 179 }, 180 /* CODEC<->CODEC link */ 181 /* back ends */ 182 { 183 .name = "SSP2-Codec", 184 .id = 0, 185 .cpu_dai_name = "ssp2-port", 186 .platform_name = "sst-mfld-platform", 187 .no_pcm = 1, 188 .codec_dai_name = "da7213-hifi", 189 .codec_name = "i2c-DLGS7213:00", 190 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 191 | SND_SOC_DAIFMT_CBS_CFS, 192 .be_hw_params_fixup = codec_fixup, 193 .nonatomic = true, 194 .dpcm_playback = 1, 195 .dpcm_capture = 1, 196 .ops = &ssp2_ops, 197 }, 198 }; 199 200 /* SoC card */ 201 static struct snd_soc_card bytcht_da7213_card = { 202 .name = "bytcht-da7213", 203 .owner = THIS_MODULE, 204 .dai_link = dailink, 205 .num_links = ARRAY_SIZE(dailink), 206 .controls = controls, 207 .num_controls = ARRAY_SIZE(controls), 208 .dapm_widgets = dapm_widgets, 209 .num_dapm_widgets = ARRAY_SIZE(dapm_widgets), 210 .dapm_routes = audio_map, 211 .num_dapm_routes = ARRAY_SIZE(audio_map), 212 }; 213 214 static char codec_name[SND_ACPI_I2C_ID_LEN]; 215 216 static int bytcht_da7213_probe(struct platform_device *pdev) 217 { 218 struct snd_soc_card *card; 219 struct snd_soc_acpi_mach *mach; 220 const char *platform_name; 221 struct acpi_device *adev; 222 int dai_index = 0; 223 int ret_val = 0; 224 int i; 225 226 mach = (&pdev->dev)->platform_data; 227 card = &bytcht_da7213_card; 228 card->dev = &pdev->dev; 229 230 /* fix index of codec dai */ 231 for (i = 0; i < ARRAY_SIZE(dailink); i++) { 232 if (!strcmp(dailink[i].codec_name, "i2c-DLGS7213:00")) { 233 dai_index = i; 234 break; 235 } 236 } 237 238 /* fixup codec name based on HID */ 239 adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1); 240 if (adev) { 241 snprintf(codec_name, sizeof(codec_name), 242 "i2c-%s", acpi_dev_name(adev)); 243 put_device(&adev->dev); 244 dailink[dai_index].codec_name = codec_name; 245 } 246 247 /* override plaform name, if required */ 248 platform_name = mach->mach_params.platform; 249 250 ret_val = snd_soc_fixup_dai_links_platform_name(card, platform_name); 251 if (ret_val) 252 return ret_val; 253 254 ret_val = devm_snd_soc_register_card(&pdev->dev, card); 255 if (ret_val) { 256 dev_err(&pdev->dev, 257 "snd_soc_register_card failed %d\n", ret_val); 258 return ret_val; 259 } 260 platform_set_drvdata(pdev, card); 261 return ret_val; 262 } 263 264 static struct platform_driver bytcht_da7213_driver = { 265 .driver = { 266 .name = "bytcht_da7213", 267 }, 268 .probe = bytcht_da7213_probe, 269 }; 270 module_platform_driver(bytcht_da7213_driver); 271 272 MODULE_DESCRIPTION("ASoC Intel(R) Baytrail/Cherrytrail+DA7213 Machine driver"); 273 MODULE_AUTHOR("Pierre-Louis Bossart"); 274 MODULE_LICENSE("GPL v2"); 275 MODULE_ALIAS("platform:bytcht_da7213"); 276