littlemill.c (8413B)
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 15static int sample_rate = 44100; 16 17static 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 64static 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 104static 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 133static const struct snd_soc_ops littlemill_ops = { 134 .hw_params = littlemill_hw_params, 135}; 136 137static 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 145SND_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 150SND_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 155static 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 .params = &baseband_params, 171 SND_SOC_DAILINK_REG(baseband), 172 }, 173}; 174 175static int bbclk_ev(struct snd_soc_dapm_widget *w, 176 struct snd_kcontrol *kcontrol, int event) 177{ 178 struct snd_soc_card *card = w->dapm->card; 179 struct snd_soc_pcm_runtime *rtd; 180 struct snd_soc_dai *aif2_dai; 181 int ret; 182 183 rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[1]); 184 aif2_dai = asoc_rtd_to_cpu(rtd, 0); 185 186 switch (event) { 187 case SND_SOC_DAPM_PRE_PMU: 188 ret = snd_soc_dai_set_pll(aif2_dai, WM8994_FLL2, 189 WM8994_FLL_SRC_BCLK, 64 * 8000, 190 8000 * 256); 191 if (ret < 0) { 192 pr_err("Failed to start FLL: %d\n", ret); 193 return ret; 194 } 195 196 ret = snd_soc_dai_set_sysclk(aif2_dai, WM8994_SYSCLK_FLL2, 197 8000 * 256, 198 SND_SOC_CLOCK_IN); 199 if (ret < 0) { 200 pr_err("Failed to set SYSCLK: %d\n", ret); 201 return ret; 202 } 203 break; 204 case SND_SOC_DAPM_POST_PMD: 205 ret = snd_soc_dai_set_sysclk(aif2_dai, WM8994_SYSCLK_MCLK2, 206 32768, SND_SOC_CLOCK_IN); 207 if (ret < 0) { 208 pr_err("Failed to switch away from FLL2: %d\n", ret); 209 return ret; 210 } 211 212 ret = snd_soc_dai_set_pll(aif2_dai, WM8994_FLL2, 213 0, 0, 0); 214 if (ret < 0) { 215 pr_err("Failed to stop FLL2: %d\n", ret); 216 return ret; 217 } 218 break; 219 default: 220 return -EINVAL; 221 } 222 223 return 0; 224} 225 226static const struct snd_kcontrol_new controls[] = { 227 SOC_DAPM_PIN_SWITCH("WM1250 Input"), 228 SOC_DAPM_PIN_SWITCH("WM1250 Output"), 229}; 230 231static const struct snd_soc_dapm_widget widgets[] = { 232 SND_SOC_DAPM_HP("Headphone", NULL), 233 234 SND_SOC_DAPM_MIC("AMIC", NULL), 235 SND_SOC_DAPM_MIC("DMIC", NULL), 236 237 SND_SOC_DAPM_SUPPLY_S("Baseband Clock", -1, SND_SOC_NOPM, 0, 0, 238 bbclk_ev, 239 SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), 240}; 241 242static const struct snd_soc_dapm_route audio_paths[] = { 243 { "Headphone", NULL, "HPOUT1L" }, 244 { "Headphone", NULL, "HPOUT1R" }, 245 246 { "AMIC", NULL, "MICBIAS1" }, /* Default for AMICBIAS jumper */ 247 { "IN1LN", NULL, "AMIC" }, 248 249 { "DMIC", NULL, "MICBIAS2" }, /* Default for DMICBIAS jumper */ 250 { "DMIC1DAT", NULL, "DMIC" }, 251 { "DMIC2DAT", NULL, "DMIC" }, 252 253 { "AIF2CLK", NULL, "Baseband Clock" }, 254}; 255 256static struct snd_soc_jack littlemill_headset; 257 258static int littlemill_late_probe(struct snd_soc_card *card) 259{ 260 struct snd_soc_pcm_runtime *rtd; 261 struct snd_soc_component *component; 262 struct snd_soc_dai *aif1_dai; 263 struct snd_soc_dai *aif2_dai; 264 int ret; 265 266 rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[0]); 267 component = asoc_rtd_to_codec(rtd, 0)->component; 268 aif1_dai = asoc_rtd_to_codec(rtd, 0); 269 270 rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[1]); 271 aif2_dai = asoc_rtd_to_cpu(rtd, 0); 272 273 ret = snd_soc_dai_set_sysclk(aif1_dai, WM8994_SYSCLK_MCLK2, 274 32768, SND_SOC_CLOCK_IN); 275 if (ret < 0) 276 return ret; 277 278 ret = snd_soc_dai_set_sysclk(aif2_dai, WM8994_SYSCLK_MCLK2, 279 32768, SND_SOC_CLOCK_IN); 280 if (ret < 0) 281 return ret; 282 283 ret = snd_soc_card_jack_new(card, "Headset", 284 SND_JACK_HEADSET | SND_JACK_MECHANICAL | 285 SND_JACK_BTN_0 | SND_JACK_BTN_1 | 286 SND_JACK_BTN_2 | SND_JACK_BTN_3 | 287 SND_JACK_BTN_4 | SND_JACK_BTN_5, 288 &littlemill_headset); 289 if (ret) 290 return ret; 291 292 /* This will check device compatibility itself */ 293 wm8958_mic_detect(component, &littlemill_headset, NULL, NULL, NULL, NULL); 294 295 /* As will this */ 296 wm8994_mic_detect(component, &littlemill_headset, 1); 297 298 return 0; 299} 300 301static struct snd_soc_card littlemill = { 302 .name = "Littlemill", 303 .owner = THIS_MODULE, 304 .dai_link = littlemill_dai, 305 .num_links = ARRAY_SIZE(littlemill_dai), 306 307 .set_bias_level = littlemill_set_bias_level, 308 .set_bias_level_post = littlemill_set_bias_level_post, 309 310 .controls = controls, 311 .num_controls = ARRAY_SIZE(controls), 312 .dapm_widgets = widgets, 313 .num_dapm_widgets = ARRAY_SIZE(widgets), 314 .dapm_routes = audio_paths, 315 .num_dapm_routes = ARRAY_SIZE(audio_paths), 316 317 .late_probe = littlemill_late_probe, 318}; 319 320static int littlemill_probe(struct platform_device *pdev) 321{ 322 struct snd_soc_card *card = &littlemill; 323 int ret; 324 325 card->dev = &pdev->dev; 326 327 ret = devm_snd_soc_register_card(&pdev->dev, card); 328 if (ret) 329 dev_err_probe(&pdev->dev, ret, "snd_soc_register_card() failed\n"); 330 331 return ret; 332} 333 334static struct platform_driver littlemill_driver = { 335 .driver = { 336 .name = "littlemill", 337 .pm = &snd_soc_pm_ops, 338 }, 339 .probe = littlemill_probe, 340}; 341 342module_platform_driver(littlemill_driver); 343 344MODULE_DESCRIPTION("Littlemill audio support"); 345MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 346MODULE_LICENSE("GPL"); 347MODULE_ALIAS("platform:littlemill");