File Index
Files and directories
Sign in
FILE

bells.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/samsung/bells.c C
  1// SPDX-License-Identifier: GPL-2.0+
  2//
  3// Bells audio support
  4//
  5// Copyright 2012 Wolfson Microelectronics
  6
  7#include <sound/soc.h>
  8#include <sound/soc-dapm.h>
  9#include <sound/jack.h>
 10#include <linux/module.h>
 11
 12#include "../codecs/wm5102.h"
 13#include "../codecs/wm9081.h"
 14
 15/* BCLK2 is fixed at this currently */
 16#define BCLK2_RATE (64 * 8000)
 17
 18/*
 19 * Expect a 24.576MHz crystal if one is fitted (the driver will function
 20 * if this is not fitted).
 21 */
 22#define MCLK_RATE 24576000
 23
 24#define SYS_AUDIO_RATE 44100
 25#define SYS_MCLK_RATE  (SYS_AUDIO_RATE * 512)
 26
 27#define DAI_AP_DSP    0
 28#define DAI_DSP_CODEC 1
 29#define DAI_CODEC_CP  2
 30#define DAI_CODEC_SUB 3
 31
 32struct bells_drvdata {
 33	int sysclk_rate;
 34	int asyncclk_rate;
 35};
 36
 37static struct bells_drvdata wm2200_drvdata = {
 38	.sysclk_rate = 22579200,
 39};
 40
 41static struct bells_drvdata wm5102_drvdata = {
 42	.sysclk_rate = 45158400,
 43	.asyncclk_rate = 49152000,
 44};
 45
 46static struct bells_drvdata wm5110_drvdata = {
 47	.sysclk_rate = 135475200,
 48	.asyncclk_rate = 147456000,
 49};
 50
 51static int bells_set_bias_level(struct snd_soc_card *card,
 52				struct snd_soc_dapm_context *dapm,
 53				enum snd_soc_bias_level level)
 54{
 55	struct snd_soc_pcm_runtime *rtd;
 56	struct snd_soc_dai *codec_dai;
 57	struct snd_soc_component *component;
 58	struct bells_drvdata *bells = card->drvdata;
 59	int ret;
 60
 61	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_DSP_CODEC]);
 62	codec_dai = snd_soc_rtd_to_codec(rtd, 0);
 63	component = codec_dai->component;
 64
 65	if (snd_soc_dapm_to_dev(dapm) != codec_dai->dev)
 66		return 0;
 67
 68	switch (level) {
 69	case SND_SOC_BIAS_PREPARE:
 70		if (snd_soc_dapm_get_bias_level(dapm) != SND_SOC_BIAS_STANDBY)
 71			break;
 72
 73		ret = snd_soc_component_set_pll(component, WM5102_FLL1,
 74					    ARIZONA_FLL_SRC_MCLK1,
 75					    MCLK_RATE,
 76					    bells->sysclk_rate);
 77		if (ret < 0)
 78			pr_err("Failed to start FLL: %d\n", ret);
 79
 80		if (bells->asyncclk_rate) {
 81			ret = snd_soc_component_set_pll(component, WM5102_FLL2,
 82						    ARIZONA_FLL_SRC_AIF2BCLK,
 83						    BCLK2_RATE,
 84						    bells->asyncclk_rate);
 85			if (ret < 0)
 86				pr_err("Failed to start FLL: %d\n", ret);
 87		}
 88		break;
 89
 90	default:
 91		break;
 92	}
 93
 94	return 0;
 95}
 96
 97static int bells_set_bias_level_post(struct snd_soc_card *card,
 98				     struct snd_soc_dapm_context *dapm,
 99				     enum snd_soc_bias_level level)
100{
101	struct snd_soc_pcm_runtime *rtd;
102	struct snd_soc_dai *codec_dai;
103	struct snd_soc_component *component;
104	struct bells_drvdata *bells = card->drvdata;
105	int ret;
106
107	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_DSP_CODEC]);
108	codec_dai = snd_soc_rtd_to_codec(rtd, 0);
109	component = codec_dai->component;
110
111	if (snd_soc_dapm_to_dev(dapm) != codec_dai->dev)
112		return 0;
113
114	switch (level) {
115	case SND_SOC_BIAS_STANDBY:
116		ret = snd_soc_component_set_pll(component, WM5102_FLL1, 0, 0, 0);
117		if (ret < 0) {
118			pr_err("Failed to stop FLL: %d\n", ret);
119			return ret;
120		}
121
122		if (bells->asyncclk_rate) {
123			ret = snd_soc_component_set_pll(component, WM5102_FLL2,
124						    0, 0, 0);
125			if (ret < 0) {
126				pr_err("Failed to stop FLL: %d\n", ret);
127				return ret;
128			}
129		}
130		break;
131
132	default:
133		break;
134	}
135
136	return 0;
137}
138
139static int bells_late_probe(struct snd_soc_card *card)
140{
141	struct bells_drvdata *bells = card->drvdata;
142	struct snd_soc_pcm_runtime *rtd;
143	struct snd_soc_component *wm0010;
144	struct snd_soc_component *component;
145	struct snd_soc_dai *aif1_dai;
146	struct snd_soc_dai *aif2_dai;
147	struct snd_soc_dai *aif3_dai;
148	struct snd_soc_dai *wm9081_dai;
149	int ret;
150
151	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_AP_DSP]);
152	wm0010 = snd_soc_rtd_to_codec(rtd, 0)->component;
153
154	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_DSP_CODEC]);
155	component = snd_soc_rtd_to_codec(rtd, 0)->component;
156	aif1_dai = snd_soc_rtd_to_codec(rtd, 0);
157
158	ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_SYSCLK,
159				       ARIZONA_CLK_SRC_FLL1,
160				       bells->sysclk_rate,
161				       SND_SOC_CLOCK_IN);
162	if (ret != 0) {
163		dev_err(component->dev, "Failed to set SYSCLK: %d\n", ret);
164		return ret;
165	}
166
167	ret = snd_soc_component_set_sysclk(wm0010, 0, 0, SYS_MCLK_RATE, 0);
168	if (ret != 0) {
169		dev_err(wm0010->dev, "Failed to set WM0010 clock: %d\n", ret);
170		return ret;
171	}
172
173	ret = snd_soc_dai_set_sysclk(aif1_dai, ARIZONA_CLK_SYSCLK, 0, 0);
174	if (ret != 0)
175		dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret);
176
177	ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_OPCLK, 0,
178				       SYS_MCLK_RATE, SND_SOC_CLOCK_OUT);
179	if (ret != 0)
180		dev_err(component->dev, "Failed to set OPCLK: %d\n", ret);
181
182	if (card->num_rtd == DAI_CODEC_CP)
183		return 0;
184
185	ret = snd_soc_component_set_sysclk(component, ARIZONA_CLK_ASYNCCLK,
186				       ARIZONA_CLK_SRC_FLL2,
187				       bells->asyncclk_rate,
188				       SND_SOC_CLOCK_IN);
189	if (ret != 0) {
190		dev_err(component->dev, "Failed to set ASYNCCLK: %d\n", ret);
191		return ret;
192	}
193
194	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_CODEC_CP]);
195	aif2_dai = snd_soc_rtd_to_cpu(rtd, 0);
196
197	ret = snd_soc_dai_set_sysclk(aif2_dai, ARIZONA_CLK_ASYNCCLK, 0, 0);
198	if (ret != 0) {
199		dev_err(aif2_dai->dev, "Failed to set AIF2 clock: %d\n", ret);
200		return ret;
201	}
202
203	if (card->num_rtd == DAI_CODEC_SUB)
204		return 0;
205
206	rtd = snd_soc_get_pcm_runtime(card, &card->dai_link[DAI_CODEC_SUB]);
207	aif3_dai = snd_soc_rtd_to_cpu(rtd, 0);
208	wm9081_dai = snd_soc_rtd_to_codec(rtd, 0);
209
210	ret = snd_soc_dai_set_sysclk(aif3_dai, ARIZONA_CLK_SYSCLK, 0, 0);
211	if (ret != 0) {
212		dev_err(aif1_dai->dev, "Failed to set AIF1 clock: %d\n", ret);
213		return ret;
214	}
215
216	ret = snd_soc_component_set_sysclk(wm9081_dai->component, WM9081_SYSCLK_MCLK,
217				       0, SYS_MCLK_RATE, 0);
218	if (ret != 0) {
219		dev_err(wm9081_dai->dev, "Failed to set MCLK: %d\n", ret);
220		return ret;
221	}
222
223	return 0;
224}
225
226static const struct snd_soc_pcm_stream baseband_params = {
227	.formats = SNDRV_PCM_FMTBIT_S32_LE,
228	.rate_min = 8000,
229	.rate_max = 8000,
230	.channels_min = 2,
231	.channels_max = 2,
232};
233
234static const struct snd_soc_pcm_stream sub_params = {
235	.formats = SNDRV_PCM_FMTBIT_S32_LE,
236	.rate_min = SYS_AUDIO_RATE,
237	.rate_max = SYS_AUDIO_RATE,
238	.channels_min = 2,
239	.channels_max = 2,
240};
241
242SND_SOC_DAILINK_DEFS(wm2200_cpu_dsp,
243	DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")),
244	DAILINK_COMP_ARRAY(COMP_CODEC("spi0.0", "wm0010-sdi1")),
245	DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));
246
247SND_SOC_DAILINK_DEFS(wm2200_dsp_codec,
248	DAILINK_COMP_ARRAY(COMP_CPU("wm0010-sdi2")),
249	DAILINK_COMP_ARRAY(COMP_CODEC("wm2200.1-003a", "wm2200")));
250
251static struct snd_soc_dai_link bells_dai_wm2200[] = {
252	{
253		.name = "CPU-DSP",
254		.stream_name = "CPU-DSP",
255		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
256				| SND_SOC_DAIFMT_CBP_CFP,
257		SND_SOC_DAILINK_REG(wm2200_cpu_dsp),
258	},
259	{
260		.name = "DSP-CODEC",
261		.stream_name = "DSP-CODEC",
262		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
263				| SND_SOC_DAIFMT_CBP_CFP,
264		.c2c_params = &sub_params,
265		.num_c2c_params = 1,
266		.ignore_suspend = 1,
267		SND_SOC_DAILINK_REG(wm2200_dsp_codec),
268	},
269};
270
271SND_SOC_DAILINK_DEFS(wm5102_cpu_dsp,
272	DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")),
273	DAILINK_COMP_ARRAY(COMP_CODEC("spi0.0", "wm0010-sdi1")),
274	DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));
275
276SND_SOC_DAILINK_DEFS(wm5102_dsp_codec,
277	DAILINK_COMP_ARRAY(COMP_CPU("wm0010-sdi2")),
278	DAILINK_COMP_ARRAY(COMP_CODEC("wm5102-codec", "wm5102-aif1")));
279
280SND_SOC_DAILINK_DEFS(wm5102_baseband,
281	DAILINK_COMP_ARRAY(COMP_CPU("wm5102-aif2")),
282	DAILINK_COMP_ARRAY(COMP_CODEC("wm1250-ev1.1-0027", "wm1250-ev1")));
283
284SND_SOC_DAILINK_DEFS(wm5102_sub,
285	DAILINK_COMP_ARRAY(COMP_CPU("wm5102-aif3")),
286	DAILINK_COMP_ARRAY(COMP_CODEC("wm9081.1-006c", "wm9081-hifi")));
287
288static struct snd_soc_dai_link bells_dai_wm5102[] = {
289	{
290		.name = "CPU-DSP",
291		.stream_name = "CPU-DSP",
292		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
293				| SND_SOC_DAIFMT_CBP_CFP,
294		SND_SOC_DAILINK_REG(wm5102_cpu_dsp),
295	},
296	{
297		.name = "DSP-CODEC",
298		.stream_name = "DSP-CODEC",
299		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
300				| SND_SOC_DAIFMT_CBP_CFP,
301		.c2c_params = &sub_params,
302		.num_c2c_params = 1,
303		.ignore_suspend = 1,
304		SND_SOC_DAILINK_REG(wm5102_dsp_codec),
305	},
306	{
307		.name = "Baseband",
308		.stream_name = "Baseband",
309		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
310				| SND_SOC_DAIFMT_CBP_CFP,
311		.ignore_suspend = 1,
312		.c2c_params = &baseband_params,
313		.num_c2c_params = 1,
314		SND_SOC_DAILINK_REG(wm5102_baseband),
315	},
316	{
317		.name = "Sub",
318		.stream_name = "Sub",
319		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
320				| SND_SOC_DAIFMT_CBC_CFC,
321		.ignore_suspend = 1,
322		.c2c_params = &sub_params,
323		.num_c2c_params = 1,
324		SND_SOC_DAILINK_REG(wm5102_sub),
325	},
326};
327
328SND_SOC_DAILINK_DEFS(wm5110_cpu_dsp,
329	DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.0")),
330	DAILINK_COMP_ARRAY(COMP_CODEC("spi0.0", "wm0010-sdi1")),
331	DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0")));
332
333SND_SOC_DAILINK_DEFS(wm5110_dsp_codec,
334	DAILINK_COMP_ARRAY(COMP_CPU("wm0010-sdi2")),
335	DAILINK_COMP_ARRAY(COMP_CODEC("wm5110-codec", "wm5110-aif1")));
336
337SND_SOC_DAILINK_DEFS(wm5110_baseband,
338	DAILINK_COMP_ARRAY(COMP_CPU("wm5110-aif2")),
339	DAILINK_COMP_ARRAY(COMP_CODEC("wm1250-ev1.1-0027", "wm1250-ev1")));
340
341
342SND_SOC_DAILINK_DEFS(wm5110_sub,
343	DAILINK_COMP_ARRAY(COMP_CPU("wm5110-aif3")),
344	DAILINK_COMP_ARRAY(COMP_CODEC("wm9081.1-006c", "wm9081-hifi")));
345
346static struct snd_soc_dai_link bells_dai_wm5110[] = {
347	{
348		.name = "CPU-DSP",
349		.stream_name = "CPU-DSP",
350		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
351				| SND_SOC_DAIFMT_CBP_CFP,
352		SND_SOC_DAILINK_REG(wm5110_cpu_dsp),
353	},
354	{
355		.name = "DSP-CODEC",
356		.stream_name = "DSP-CODEC",
357		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
358				| SND_SOC_DAIFMT_CBP_CFP,
359		.c2c_params = &sub_params,
360		.num_c2c_params = 1,
361		.ignore_suspend = 1,
362		SND_SOC_DAILINK_REG(wm5110_dsp_codec),
363	},
364	{
365		.name = "Baseband",
366		.stream_name = "Baseband",
367		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
368				| SND_SOC_DAIFMT_CBP_CFP,
369		.ignore_suspend = 1,
370		.c2c_params = &baseband_params,
371		.num_c2c_params = 1,
372		SND_SOC_DAILINK_REG(wm5110_baseband),
373	},
374	{
375		.name = "Sub",
376		.stream_name = "Sub",
377		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
378				| SND_SOC_DAIFMT_CBC_CFC,
379		.ignore_suspend = 1,
380		.c2c_params = &sub_params,
381		.num_c2c_params = 1,
382		SND_SOC_DAILINK_REG(wm5110_sub),
383	},
384};
385
386static struct snd_soc_codec_conf bells_codec_conf[] = {
387	{
388		.dlc = COMP_CODEC_CONF("wm9081.1-006c"),
389		.name_prefix = "Sub",
390	},
391};
392
393static const struct snd_soc_dapm_widget bells_widgets[] = {
394	SND_SOC_DAPM_MIC("DMIC", NULL),
395};
396
397static const struct snd_soc_dapm_route bells_routes[] = {
398	{ "Sub CLK_SYS", NULL, "OPCLK" },
399	{ "CLKIN", NULL, "OPCLK" },
400
401	{ "DMIC", NULL, "MICBIAS2" },
402	{ "IN2L", NULL, "DMIC" },
403	{ "IN2R", NULL, "DMIC" },
404};
405
406static struct snd_soc_card bells_cards[] = {
407	{
408		.name = "Bells WM2200",
409		.owner = THIS_MODULE,
410		.dai_link = bells_dai_wm2200,
411		.num_links = ARRAY_SIZE(bells_dai_wm2200),
412		.codec_conf = bells_codec_conf,
413		.num_configs = ARRAY_SIZE(bells_codec_conf),
414
415		.late_probe = bells_late_probe,
416
417		.dapm_widgets = bells_widgets,
418		.num_dapm_widgets = ARRAY_SIZE(bells_widgets),
419		.dapm_routes = bells_routes,
420		.num_dapm_routes = ARRAY_SIZE(bells_routes),
421
422		.set_bias_level = bells_set_bias_level,
423		.set_bias_level_post = bells_set_bias_level_post,
424
425		.drvdata = &wm2200_drvdata,
426	},
427	{
428		.name = "Bells WM5102",
429		.owner = THIS_MODULE,
430		.dai_link = bells_dai_wm5102,
431		.num_links = ARRAY_SIZE(bells_dai_wm5102),
432		.codec_conf = bells_codec_conf,
433		.num_configs = ARRAY_SIZE(bells_codec_conf),
434
435		.late_probe = bells_late_probe,
436
437		.dapm_widgets = bells_widgets,
438		.num_dapm_widgets = ARRAY_SIZE(bells_widgets),
439		.dapm_routes = bells_routes,
440		.num_dapm_routes = ARRAY_SIZE(bells_routes),
441
442		.set_bias_level = bells_set_bias_level,
443		.set_bias_level_post = bells_set_bias_level_post,
444
445		.drvdata = &wm5102_drvdata,
446	},
447	{
448		.name = "Bells WM5110",
449		.owner = THIS_MODULE,
450		.dai_link = bells_dai_wm5110,
451		.num_links = ARRAY_SIZE(bells_dai_wm5110),
452		.codec_conf = bells_codec_conf,
453		.num_configs = ARRAY_SIZE(bells_codec_conf),
454
455		.late_probe = bells_late_probe,
456
457		.dapm_widgets = bells_widgets,
458		.num_dapm_widgets = ARRAY_SIZE(bells_widgets),
459		.dapm_routes = bells_routes,
460		.num_dapm_routes = ARRAY_SIZE(bells_routes),
461
462		.set_bias_level = bells_set_bias_level,
463		.set_bias_level_post = bells_set_bias_level_post,
464
465		.drvdata = &wm5110_drvdata,
466	},
467};
468
469static int bells_probe(struct platform_device *pdev)
470{
471	int ret;
472
473	bells_cards[pdev->id].dev = &pdev->dev;
474
475	ret = devm_snd_soc_register_card(&pdev->dev, &bells_cards[pdev->id]);
476	if (ret)
477		dev_err(&pdev->dev,
478			"snd_soc_register_card(%s) failed: %d\n",
479			bells_cards[pdev->id].name, ret);
480
481	return ret;
482}
483
484static struct platform_driver bells_driver = {
485	.driver = {
486		.name = "bells",
487		.pm = &snd_soc_pm_ops,
488	},
489	.probe = bells_probe,
490};
491
492module_platform_driver(bells_driver);
493
494MODULE_DESCRIPTION("Bells audio support");
495MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
496MODULE_LICENSE("GPL");
497MODULE_ALIAS("platform:bells");
~/ Read-only file preview.