364 lines · c
1// SPDX-License-Identifier: GPL-2.0+2//3// Littlemill audio support4//5// Copyright 2011 Wolfson Microelectronics6 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 = snd_soc_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 = snd_soc_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 = snd_soc_substream_to_rtd(substream);108 struct snd_soc_dai *codec_dai = snd_soc_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_NF160 | 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_NF168 | 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 176static 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 = snd_soc_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 227static 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 234static 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 246static 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 260static struct snd_soc_jack littlemill_headset;261static 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 272static 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 = snd_soc_rtd_to_codec(rtd, 0)->component;282 aif1_dai = snd_soc_rtd_to_codec(rtd, 0);283 284 rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[1]);285 aif2_dai = snd_soc_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 317static 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 336static 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 350static struct platform_driver littlemill_driver = {351 .driver = {352 .name = "littlemill",353 .pm = &snd_soc_pm_ops,354 },355 .probe = littlemill_probe,356};357 358module_platform_driver(littlemill_driver);359 360MODULE_DESCRIPTION("Littlemill audio support");361MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");362MODULE_LICENSE("GPL");363MODULE_ALIAS("platform:littlemill");364