1 // SPDX-License-Identifier: GPL-2.0+ 2 // 3 // Littlemill audio support 4 // 5 // Copyright 2011 Wolfson Microelectronics 6 7 #include <sound/soc.h> 8 #include <sound/soc-dapm.h> 9 #include <sound/jack.h> 10 #include <linux/gpio.h> 11 #include <linux/module.h> 12 13 #include "../codecs/wm8994.h" 14 15 static int sample_rate = 44100; 16 17 static int littlemill_set_bias_level(struct snd_soc_card *card, 18 struct snd_soc_dapm_context *dapm, 19 enum snd_soc_bias_level level) 20 { 21 struct snd_soc_pcm_runtime *rtd; 22 struct snd_soc_dai *aif1_dai; 23 int ret; 24 25 rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); 26 aif1_dai = asoc_rtd_to_codec(rtd, 0); 27 28 if (dapm->dev != aif1_dai->dev) 29 return 0; 30 31 switch (level) { 32 case SND_SOC_BIAS_PREPARE: 33 /* 34 * If we've not already clocked things via hw_params() 35 * then do so now, otherwise these are noops. 36 */ 37 if (dapm->bias_level == SND_SOC_BIAS_STANDBY) { 38 ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1, 39 WM8994_FLL_SRC_MCLK2, 32768, 40 sample_rate * 512); 41 if (ret < 0) { 42 pr_err("Failed to start FLL: %d\n", ret); 43 return ret; 44 } 45 46 ret = snd_soc_dai_set_sysclk(aif1_dai, 47 WM8994_SYSCLK_FLL1, 48 sample_rate * 512, 49 SND_SOC_CLOCK_IN); 50 if (ret < 0) { 51 pr_err("Failed to set SYSCLK: %d\n", ret); 52 return ret; 53 } 54 } 55 break; 56 57 default: 58 break; 59 } 60 61 return 0; 62 } 63 64 static int littlemill_set_bias_level_post(struct snd_soc_card *card, 65 struct snd_soc_dapm_context *dapm, 66 enum snd_soc_bias_level level) 67 { 68 struct snd_soc_pcm_runtime *rtd; 69 struct snd_soc_dai *aif1_dai; 70 int ret; 71 72 rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); 73 aif1_dai = asoc_rtd_to_codec(rtd, 0); 74 75 if (dapm->dev != aif1_dai->dev) 76 return 0; 77 78 switch (level) { 79 case SND_SOC_BIAS_STANDBY: 80 ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2, 81 32768, SND_SOC_CLOCK_IN); 82 if (ret < 0) { 83 pr_err("Failed to switch away from FLL1: %d\n", ret); 84 return ret; 85 } 86 87 ret = snd_soc_dai_set_pll(aif1_dai, WM8994_FLL1, 88 0, 0, 0); 89 if (ret < 0) { 90 pr_err("Failed to stop FLL1: %d\n", ret); 91 return ret; 92 } 93 break; 94 95 default: 96 break; 97 } 98 99 dapm->bias_level = level; 100 101 return 0; 102 } 103 104 static int littlemill_hw_params(struct snd_pcm_substream *substream, 105 struct snd_pcm_hw_params *params) 106 { 107 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 108 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 109 int ret; 110 111 sample_rate = params_rate(params); 112 113 ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, 114 WM8994_FLL_SRC_MCLK2, 32768, 115 sample_rate * 512); 116 if (ret < 0) { 117 pr_err("Failed to start FLL: %d\n", ret); 118 return ret; 119 } 120 121 ret = snd_soc_dai_set_sysclk(codec_dai, 122 WM8994_SYSCLK_FLL1, 123 sample_rate * 512, 124 SND_SOC_CLOCK_IN); 125 if (ret < 0) { 126 pr_err("Failed to set SYSCLK: %d\n", ret); 127 return ret; 128 } 129 130 return 0; 131 } 132 133 static const struct snd_soc_ops littlemill_ops = { 134 .hw_params = littlemill_hw_params, 135 }; 136 137 static const struct snd_soc_pcm_stream baseband_params = { 138 .formats = SNDRV_PCM_FMTBIT_S32_LE, 139 .rate_min = 8000, 140 .rate_max = 8000, 141 .channels_min = 2, 142 .channels_max = 2, 143 }; 144 145 SND_SOC_DAILINK_DEFS(cpu, 146 DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")), 147 DAILINK_COMP_ARRAY(COMP_CODEC("wm8994-codec", "wm8994-aif1")), 148 DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0"))); 149 150 SND_SOC_DAILINK_DEFS(baseband, 151 DAILINK_COMP_ARRAY(COMP_CPU("wm8994-aif2")), 152 DAILINK_COMP_ARRAY(COMP_CODEC("wm1250-ev1.1-0027", 153 "wm1250-ev1"))); 154 155 static struct snd_soc_dai_link littlemill_dai[] = { 156 { 157 .name = "CPU", 158 .stream_name = "CPU", 159 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 160 | SND_SOC_DAIFMT_CBM_CFM, 161 .ops = &littlemill_ops, 162 SND_SOC_DAILINK_REG(cpu), 163 }, 164 { 165 .name = "Baseband", 166 .stream_name = "Baseband", 167 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF 168 | SND_SOC_DAIFMT_CBM_CFM, 169 .ignore_suspend = 1, 170 .c2c_params = &baseband_params, 171 .num_c2c_params = 1, 172 SND_SOC_DAILINK_REG(baseband), 173 }, 174 }; 175 176 static int bbclk_ev(struct snd_soc_dapm_widget *w, 177 struct snd_kcontrol *kcontrol, int event) 178 { 179 struct snd_soc_card *card = w->dapm->card; 180 struct snd_soc_pcm_runtime *rtd; 181 struct snd_soc_dai *aif2_dai; 182 int ret; 183 184 rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[1]); 185 aif2_dai = asoc_rtd_to_cpu(rtd, 0); 186 187 switch (event) { 188 case SND_SOC_DAPM_PRE_PMU: 189 ret = snd_soc_dai_set_pll(aif2_dai, WM8994_FLL2, 190 WM8994_FLL_SRC_BCLK, 64 * 8000, 191 8000 * 256); 192 if (ret < 0) { 193 pr_err("Failed to start FLL: %d\n", ret); 194 return ret; 195 } 196 197 ret = snd_soc_dai_set_sysclk(aif2_dai, WM8994_SYSCLK_FLL2, 198 8000 * 256, 199 SND_SOC_CLOCK_IN); 200 if (ret < 0) { 201 pr_err("Failed to set SYSCLK: %d\n", ret); 202 return ret; 203 } 204 break; 205 case SND_SOC_DAPM_POST_PMD: 206 ret = snd_soc_dai_set_sysclk(aif2_dai, WM8994_SYSCLK_MCLK2, 207 32768, SND_SOC_CLOCK_IN); 208 if (ret < 0) { 209 pr_err("Failed to switch away from FLL2: %d\n", ret); 210 return ret; 211 } 212 213 ret = snd_soc_dai_set_pll(aif2_dai, WM8994_FLL2, 214 0, 0, 0); 215 if (ret < 0) { 216 pr_err("Failed to stop FLL2: %d\n", ret); 217 return ret; 218 } 219 break; 220 default: 221 return -EINVAL; 222 } 223 224 return 0; 225 } 226 227 static const struct snd_kcontrol_new controls[] = { 228 SOC_DAPM_PIN_SWITCH("Headphone"), 229 SOC_DAPM_PIN_SWITCH("Headset Mic"), 230 SOC_DAPM_PIN_SWITCH("WM1250 Input"), 231 SOC_DAPM_PIN_SWITCH("WM1250 Output"), 232 }; 233 234 static const struct snd_soc_dapm_widget widgets[] = { 235 SND_SOC_DAPM_HP("Headphone", NULL), 236 SND_SOC_DAPM_HP("Headset Mic", NULL), 237 238 SND_SOC_DAPM_MIC("AMIC", NULL), 239 SND_SOC_DAPM_MIC("DMIC", NULL), 240 241 SND_SOC_DAPM_SUPPLY_S("Baseband Clock", -1, SND_SOC_NOPM, 0, 0, 242 bbclk_ev, 243 SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), 244 }; 245 246 static const struct snd_soc_dapm_route audio_paths[] = { 247 { "Headphone", NULL, "HPOUT1L" }, 248 { "Headphone", NULL, "HPOUT1R" }, 249 250 { "AMIC", NULL, "MICBIAS1" }, /* Default for AMICBIAS jumper */ 251 { "IN1LN", NULL, "AMIC" }, 252 253 { "DMIC", NULL, "MICBIAS2" }, /* Default for DMICBIAS jumper */ 254 { "DMIC1DAT", NULL, "DMIC" }, 255 { "DMIC2DAT", NULL, "DMIC" }, 256 257 { "AIF2CLK", NULL, "Baseband Clock" }, 258 }; 259 260 static struct snd_soc_jack littlemill_headset; 261 static struct snd_soc_jack_pin littlemill_headset_pins[] = { 262 { 263 .pin = "Headphone", 264 .mask = SND_JACK_HEADPHONE, 265 }, 266 { 267 .pin = "Headset Mic", 268 .mask = SND_JACK_MICROPHONE, 269 }, 270 }; 271 272 static int littlemill_late_probe(struct snd_soc_card *card) 273 { 274 struct snd_soc_pcm_runtime *rtd; 275 struct snd_soc_component *component; 276 struct snd_soc_dai *aif1_dai; 277 struct snd_soc_dai *aif2_dai; 278 int ret; 279 280 rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); 281 component = asoc_rtd_to_codec(rtd, 0)->component; 282 aif1_dai = asoc_rtd_to_codec(rtd, 0); 283 284 rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[1]); 285 aif2_dai = asoc_rtd_to_cpu(rtd, 0); 286 287 ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2, 288 32768, SND_SOC_CLOCK_IN); 289 if (ret < 0) 290 return ret; 291 292 ret = snd_soc_dai_set_sysclk(aif2_dai, WM8994_SYSCLK_MCLK2, 293 32768, SND_SOC_CLOCK_IN); 294 if (ret < 0) 295 return ret; 296 297 ret = snd_soc_card_jack_new_pins(card, "Headset", 298 SND_JACK_HEADSET | SND_JACK_MECHANICAL | 299 SND_JACK_BTN_0 | SND_JACK_BTN_1 | 300 SND_JACK_BTN_2 | SND_JACK_BTN_3 | 301 SND_JACK_BTN_4 | SND_JACK_BTN_5, 302 &littlemill_headset, 303 littlemill_headset_pins, 304 ARRAY_SIZE(littlemill_headset_pins)); 305 if (ret) 306 return ret; 307 308 /* This will check device compatibility itself */ 309 wm8958_mic_detect(component, &littlemill_headset, NULL, NULL, NULL, NULL); 310 311 /* As will this */ 312 wm8994_mic_detect(component, &littlemill_headset, 1); 313 314 return 0; 315 } 316 317 static struct snd_soc_card littlemill = { 318 .name = "Littlemill", 319 .owner = THIS_MODULE, 320 .dai_link = littlemill_dai, 321 .num_links = ARRAY_SIZE(littlemill_dai), 322 323 .set_bias_level = littlemill_set_bias_level, 324 .set_bias_level_post = littlemill_set_bias_level_post, 325 326 .controls = controls, 327 .num_controls = ARRAY_SIZE(controls), 328 .dapm_widgets = widgets, 329 .num_dapm_widgets = ARRAY_SIZE(widgets), 330 .dapm_routes = audio_paths, 331 .num_dapm_routes = ARRAY_SIZE(audio_paths), 332 333 .late_probe = littlemill_late_probe, 334 }; 335 336 static int littlemill_probe(struct platform_device *pdev) 337 { 338 struct snd_soc_card *card = &littlemill; 339 int ret; 340 341 card->dev = &pdev->dev; 342 343 ret = devm_snd_soc_register_card(&pdev->dev, card); 344 if (ret) 345 dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); 346 347 return ret; 348 } 349 350 static struct platform_driver littlemill_driver = { 351 .driver = { 352 .name = "littlemill", 353 .pm = &snd_soc_pm_ops, 354 }, 355 .probe = littlemill_probe, 356 }; 357 358 module_platform_driver(littlemill_driver); 359 360 MODULE_DESCRIPTION("Littlemill audio support"); 361 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 362 MODULE_LICENSE("GPL"); 363 MODULE_ALIAS("platform:littlemill"); 364