File Index
Files and directories
Sign in
FILE

tegra186_dspk.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/tegra/tegra186_dspk.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2// SPDX-FileCopyrightText: Copyright (c) 2020-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
  3//
  4// tegra186_dspk.c - Tegra186 DSPK driver
  5
  6#include <linux/clk.h>
  7#include <linux/device.h>
  8#include <linux/mod_devicetable.h>
  9#include <linux/module.h>
 10#include <linux/of.h>
 11#include <linux/platform_device.h>
 12#include <linux/pm_runtime.h>
 13#include <linux/regmap.h>
 14#include <sound/core.h>
 15#include <sound/pcm_params.h>
 16#include <sound/soc.h>
 17#include "tegra186_dspk.h"
 18#include "tegra_cif.h"
 19
 20static const struct reg_default tegra186_dspk_reg_defaults[] = {
 21	{ TEGRA186_DSPK_RX_INT_MASK, 0x00000007 },
 22	{ TEGRA186_DSPK_RX_CIF_CTRL, 0x00007700 },
 23	{ TEGRA186_DSPK_CG,	     0x00000001 },
 24	{ TEGRA186_DSPK_CORE_CTRL,   0x00000310 },
 25	{ TEGRA186_DSPK_CODEC_CTRL,  0x03000000 },
 26};
 27
 28static int tegra186_dspk_get_fifo_th(struct snd_kcontrol *kcontrol,
 29				     struct snd_ctl_elem_value *ucontrol)
 30{
 31	struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
 32	struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
 33
 34	ucontrol->value.integer.value[0] = dspk->rx_fifo_th;
 35
 36	return 0;
 37}
 38
 39static int tegra186_dspk_put_fifo_th(struct snd_kcontrol *kcontrol,
 40				     struct snd_ctl_elem_value *ucontrol)
 41{
 42	struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
 43	struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
 44	int value = ucontrol->value.integer.value[0];
 45
 46	if (value == dspk->rx_fifo_th)
 47		return 0;
 48
 49	dspk->rx_fifo_th = value;
 50
 51	return 1;
 52}
 53
 54static int tegra186_dspk_get_osr_val(struct snd_kcontrol *kcontrol,
 55				     struct snd_ctl_elem_value *ucontrol)
 56{
 57	struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
 58	struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
 59
 60	ucontrol->value.enumerated.item[0] = dspk->osr_val;
 61
 62	return 0;
 63}
 64
 65static int tegra186_dspk_put_osr_val(struct snd_kcontrol *kcontrol,
 66				     struct snd_ctl_elem_value *ucontrol)
 67{
 68	struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
 69	struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
 70	unsigned int value = ucontrol->value.enumerated.item[0];
 71
 72	if (value == dspk->osr_val)
 73		return 0;
 74
 75	dspk->osr_val = value;
 76
 77	return 1;
 78}
 79
 80static int tegra186_dspk_get_pol_sel(struct snd_kcontrol *kcontrol,
 81				     struct snd_ctl_elem_value *ucontrol)
 82{
 83	struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
 84	struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
 85
 86	ucontrol->value.enumerated.item[0] = dspk->lrsel;
 87
 88	return 0;
 89}
 90
 91static int tegra186_dspk_put_pol_sel(struct snd_kcontrol *kcontrol,
 92				     struct snd_ctl_elem_value *ucontrol)
 93{
 94	struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
 95	struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
 96	unsigned int value = ucontrol->value.enumerated.item[0];
 97
 98	if (value == dspk->lrsel)
 99		return 0;
100
101	dspk->lrsel = value;
102
103	return 1;
104}
105
106static int tegra186_dspk_get_ch_sel(struct snd_kcontrol *kcontrol,
107				    struct snd_ctl_elem_value *ucontrol)
108{
109	struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
110	struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
111
112	ucontrol->value.enumerated.item[0] = dspk->ch_sel;
113
114	return 0;
115}
116
117static int tegra186_dspk_put_ch_sel(struct snd_kcontrol *kcontrol,
118				    struct snd_ctl_elem_value *ucontrol)
119{
120	struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
121	struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
122	unsigned int value = ucontrol->value.enumerated.item[0];
123
124	if (value == dspk->ch_sel)
125		return 0;
126
127	dspk->ch_sel = value;
128
129	return 1;
130}
131
132static int tegra186_dspk_get_mono_to_stereo(struct snd_kcontrol *kcontrol,
133					    struct snd_ctl_elem_value *ucontrol)
134{
135	struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
136	struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
137
138	ucontrol->value.enumerated.item[0] = dspk->mono_to_stereo;
139
140	return 0;
141}
142
143static int tegra186_dspk_put_mono_to_stereo(struct snd_kcontrol *kcontrol,
144					    struct snd_ctl_elem_value *ucontrol)
145{
146	struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
147	struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
148	unsigned int value = ucontrol->value.enumerated.item[0];
149
150	if (value == dspk->mono_to_stereo)
151		return 0;
152
153	dspk->mono_to_stereo = value;
154
155	return 1;
156}
157
158static int tegra186_dspk_get_stereo_to_mono(struct snd_kcontrol *kcontrol,
159					    struct snd_ctl_elem_value *ucontrol)
160{
161	struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
162	struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
163
164	ucontrol->value.enumerated.item[0] = dspk->stereo_to_mono;
165
166	return 0;
167}
168
169static int tegra186_dspk_put_stereo_to_mono(struct snd_kcontrol *kcontrol,
170					    struct snd_ctl_elem_value *ucontrol)
171{
172	struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
173	struct tegra186_dspk *dspk = snd_soc_component_get_drvdata(codec);
174	unsigned int value = ucontrol->value.enumerated.item[0];
175
176	if (value == dspk->stereo_to_mono)
177		return 0;
178
179	dspk->stereo_to_mono = value;
180
181	return 1;
182}
183
184static int tegra186_dspk_runtime_suspend(struct device *dev)
185{
186	struct tegra186_dspk *dspk = dev_get_drvdata(dev);
187
188	regcache_cache_only(dspk->regmap, true);
189	regcache_mark_dirty(dspk->regmap);
190
191	clk_disable_unprepare(dspk->clk_dspk);
192
193	return 0;
194}
195
196static int tegra186_dspk_runtime_resume(struct device *dev)
197{
198	struct tegra186_dspk *dspk = dev_get_drvdata(dev);
199	int err;
200
201	err = clk_prepare_enable(dspk->clk_dspk);
202	if (err) {
203		dev_err(dev, "failed to enable DSPK clock, err: %d\n", err);
204		return err;
205	}
206
207	regcache_cache_only(dspk->regmap, false);
208	regcache_sync(dspk->regmap);
209
210	return 0;
211}
212
213static int tegra186_dspk_hw_params(struct snd_pcm_substream *substream,
214				   struct snd_pcm_hw_params *params,
215				   struct snd_soc_dai *dai)
216{
217	struct tegra186_dspk *dspk = snd_soc_dai_get_drvdata(dai);
218	unsigned int channels, srate, dspk_clk;
219	struct device *dev = dai->dev;
220	struct tegra_cif_conf cif_conf;
221	unsigned int max_th;
222	int err;
223
224	memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));
225
226	channels = params_channels(params);
227	cif_conf.audio_ch = channels;
228
229	/* Client channel */
230	switch (dspk->ch_sel) {
231	case DSPK_CH_SELECT_LEFT:
232	case DSPK_CH_SELECT_RIGHT:
233		cif_conf.client_ch = 1;
234		break;
235	case DSPK_CH_SELECT_STEREO:
236		cif_conf.client_ch = 2;
237		break;
238	default:
239		dev_err(dev, "Invalid DSPK client channels\n");
240		return -EINVAL;
241	}
242
243	switch (params_format(params)) {
244	case SNDRV_PCM_FORMAT_S16_LE:
245		cif_conf.audio_bits = TEGRA_ACIF_BITS_16;
246		cif_conf.client_bits = TEGRA_ACIF_BITS_16;
247		break;
248	case SNDRV_PCM_FORMAT_S24_LE:
249	case SNDRV_PCM_FORMAT_S32_LE:
250		cif_conf.audio_bits = TEGRA_ACIF_BITS_32;
251		cif_conf.client_bits = TEGRA_ACIF_BITS_24;
252		break;
253	default:
254		dev_err(dev, "unsupported format!\n");
255		return -EOPNOTSUPP;
256	}
257
258	srate = params_rate(params);
259
260	/* RX FIFO threshold in terms of frames */
261	max_th = (TEGRA186_DSPK_RX_FIFO_DEPTH / cif_conf.audio_ch) - 1;
262
263	if (dspk->rx_fifo_th > max_th)
264		dspk->rx_fifo_th = max_th;
265
266	cif_conf.threshold = dspk->rx_fifo_th;
267	cif_conf.mono_conv = dspk->mono_to_stereo;
268	cif_conf.stereo_conv = dspk->stereo_to_mono;
269
270	tegra_set_cif(dspk->regmap, TEGRA186_DSPK_RX_CIF_CTRL,
271		      &cif_conf);
272
273	/*
274	 * DSPK clock and PDM codec clock should be synchronous with 4:1 ratio,
275	 * this is because it takes 4 clock cycles to send out one sample to
276	 * codec by sigma delta modulator. Finally the clock rate is a multiple
277	 * of 'Over Sampling Ratio', 'Sample Rate' and 'Interface Clock Ratio'.
278	 */
279	dspk_clk = (DSPK_OSR_FACTOR << dspk->osr_val) * srate * DSPK_CLK_RATIO;
280
281	err = clk_set_rate(dspk->clk_dspk, dspk_clk);
282	if (err) {
283		dev_err(dev, "can't set DSPK clock rate %u, err: %d\n",
284			dspk_clk, err);
285
286		return err;
287	}
288
289	regmap_update_bits(dspk->regmap,
290			   /* Reg */
291			   TEGRA186_DSPK_CORE_CTRL,
292			   /* Mask */
293			   TEGRA186_DSPK_OSR_MASK |
294			   TEGRA186_DSPK_CHANNEL_SELECT_MASK |
295			   TEGRA186_DSPK_CTRL_LRSEL_POLARITY_MASK,
296			   /* Value */
297			   (dspk->osr_val << DSPK_OSR_SHIFT) |
298			   ((dspk->ch_sel + 1) << CH_SEL_SHIFT) |
299			   (dspk->lrsel << LRSEL_POL_SHIFT));
300
301	return 0;
302}
303
304static const struct snd_soc_dai_ops tegra186_dspk_dai_ops = {
305	.hw_params	= tegra186_dspk_hw_params,
306};
307
308static struct snd_soc_dai_driver tegra186_dspk_dais[] = {
309	{
310	    .name = "DSPK-CIF",
311	    .playback = {
312		.stream_name = "CIF-Playback",
313		.channels_min = 1,
314		.channels_max = 2,
315		.rates = SNDRV_PCM_RATE_8000_48000,
316		.formats = SNDRV_PCM_FMTBIT_S16_LE |
317			   SNDRV_PCM_FMTBIT_S24_LE |
318			   SNDRV_PCM_FMTBIT_S32_LE,
319	    },
320	},
321	{
322	    .name = "DSPK-DAP",
323	    .playback = {
324		.stream_name = "DAP-Playback",
325		.channels_min = 1,
326		.channels_max = 2,
327		.rates = SNDRV_PCM_RATE_8000_48000,
328		.formats = SNDRV_PCM_FMTBIT_S16_LE |
329			   SNDRV_PCM_FMTBIT_S24_LE |
330			   SNDRV_PCM_FMTBIT_S32_LE,
331	    },
332	    .ops = &tegra186_dspk_dai_ops,
333	    .symmetric_rate = 1,
334	},
335};
336
337static const struct snd_soc_dapm_widget tegra186_dspk_widgets[] = {
338	SND_SOC_DAPM_AIF_IN("RX", NULL, 0, TEGRA186_DSPK_ENABLE, 0, 0),
339	SND_SOC_DAPM_SPK("SPK", NULL),
340};
341
342static const struct snd_soc_dapm_route tegra186_dspk_routes[] = {
343	{ "XBAR-Playback",	NULL,	"XBAR-TX" },
344	{ "CIF-Playback",	NULL,	"XBAR-Playback" },
345	{ "RX",			NULL,	"CIF-Playback" },
346	{ "DAP-Playback",	NULL,	"RX" },
347	{ "SPK",		NULL,	"DAP-Playback" },
348};
349
350static const char * const tegra186_dspk_ch_sel_text[] = {
351	"Left", "Right", "Stereo",
352};
353
354static const struct soc_enum tegra186_dspk_ch_sel_enum =
355	SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tegra186_dspk_ch_sel_text),
356			tegra186_dspk_ch_sel_text);
357
358static const char * const tegra186_dspk_osr_text[] = {
359	"OSR_32", "OSR_64", "OSR_128", "OSR_256",
360};
361
362static const struct soc_enum tegra186_dspk_osr_enum =
363	SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tegra186_dspk_osr_text),
364			tegra186_dspk_osr_text);
365
366static const char * const tegra186_dspk_lrsel_text[] = {
367	"Left", "Right",
368};
369
370static const char * const tegra186_dspk_mono_conv_text[] = {
371	"Zero", "Copy",
372};
373
374static const struct soc_enum tegra186_dspk_mono_conv_enum =
375	SOC_ENUM_SINGLE(SND_SOC_NOPM, 0,
376			ARRAY_SIZE(tegra186_dspk_mono_conv_text),
377			tegra186_dspk_mono_conv_text);
378
379static const char * const tegra186_dspk_stereo_conv_text[] = {
380	"CH0", "CH1", "AVG",
381};
382
383static const struct soc_enum tegra186_dspk_stereo_conv_enum =
384	SOC_ENUM_SINGLE(SND_SOC_NOPM, 0,
385			ARRAY_SIZE(tegra186_dspk_stereo_conv_text),
386			tegra186_dspk_stereo_conv_text);
387
388static const struct soc_enum tegra186_dspk_lrsel_enum =
389	SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(tegra186_dspk_lrsel_text),
390			tegra186_dspk_lrsel_text);
391
392static const struct snd_kcontrol_new tegrat186_dspk_controls[] = {
393	SOC_SINGLE_EXT("FIFO Threshold", SND_SOC_NOPM, 0,
394		       TEGRA186_DSPK_RX_FIFO_DEPTH - 1, 0,
395		       tegra186_dspk_get_fifo_th, tegra186_dspk_put_fifo_th),
396	SOC_ENUM_EXT("OSR Value", tegra186_dspk_osr_enum,
397		     tegra186_dspk_get_osr_val, tegra186_dspk_put_osr_val),
398	SOC_ENUM_EXT("LR Polarity Select", tegra186_dspk_lrsel_enum,
399		     tegra186_dspk_get_pol_sel, tegra186_dspk_put_pol_sel),
400	SOC_ENUM_EXT("Channel Select", tegra186_dspk_ch_sel_enum,
401		     tegra186_dspk_get_ch_sel, tegra186_dspk_put_ch_sel),
402	SOC_ENUM_EXT("Mono To Stereo", tegra186_dspk_mono_conv_enum,
403		     tegra186_dspk_get_mono_to_stereo,
404		     tegra186_dspk_put_mono_to_stereo),
405	SOC_ENUM_EXT("Stereo To Mono", tegra186_dspk_stereo_conv_enum,
406		     tegra186_dspk_get_stereo_to_mono,
407		     tegra186_dspk_put_stereo_to_mono),
408};
409
410static const struct snd_soc_component_driver tegra186_dspk_cmpnt = {
411	.dapm_widgets = tegra186_dspk_widgets,
412	.num_dapm_widgets = ARRAY_SIZE(tegra186_dspk_widgets),
413	.dapm_routes = tegra186_dspk_routes,
414	.num_dapm_routes = ARRAY_SIZE(tegra186_dspk_routes),
415	.controls = tegrat186_dspk_controls,
416	.num_controls = ARRAY_SIZE(tegrat186_dspk_controls),
417};
418
419static bool tegra186_dspk_wr_reg(struct device *dev, unsigned int reg)
420{
421	switch (reg) {
422	case TEGRA186_DSPK_RX_INT_MASK ... TEGRA186_DSPK_RX_CIF_CTRL:
423	case TEGRA186_DSPK_ENABLE ... TEGRA186_DSPK_CG:
424	case TEGRA186_DSPK_CORE_CTRL ... TEGRA186_DSPK_CODEC_CTRL:
425		return true;
426	default:
427		return false;
428	}
429}
430
431static bool tegra186_dspk_rd_reg(struct device *dev, unsigned int reg)
432{
433	if (tegra186_dspk_wr_reg(dev, reg))
434		return true;
435
436	switch (reg) {
437	case TEGRA186_DSPK_RX_STATUS:
438	case TEGRA186_DSPK_RX_INT_STATUS:
439	case TEGRA186_DSPK_STATUS:
440	case TEGRA186_DSPK_INT_STATUS:
441		return true;
442	default:
443		return false;
444	}
445}
446
447static bool tegra186_dspk_volatile_reg(struct device *dev, unsigned int reg)
448{
449	switch (reg) {
450	case TEGRA186_DSPK_RX_STATUS:
451	case TEGRA186_DSPK_RX_INT_STATUS:
452	case TEGRA186_DSPK_STATUS:
453	case TEGRA186_DSPK_INT_STATUS:
454		return true;
455	default:
456		return false;
457	}
458}
459
460static const struct regmap_config tegra186_dspk_regmap = {
461	.reg_bits		= 32,
462	.reg_stride		= 4,
463	.val_bits		= 32,
464	.max_register		= TEGRA186_DSPK_CODEC_CTRL,
465	.writeable_reg		= tegra186_dspk_wr_reg,
466	.readable_reg		= tegra186_dspk_rd_reg,
467	.volatile_reg		= tegra186_dspk_volatile_reg,
468	.reg_defaults		= tegra186_dspk_reg_defaults,
469	.num_reg_defaults	= ARRAY_SIZE(tegra186_dspk_reg_defaults),
470	.cache_type		= REGCACHE_FLAT,
471};
472
473static const struct of_device_id tegra186_dspk_of_match[] = {
474	{ .compatible = "nvidia,tegra186-dspk" },
475	{},
476};
477MODULE_DEVICE_TABLE(of, tegra186_dspk_of_match);
478
479static int tegra186_dspk_platform_probe(struct platform_device *pdev)
480{
481	struct device *dev = &pdev->dev;
482	struct tegra186_dspk *dspk;
483	void __iomem *regs;
484	int err;
485
486	dspk = devm_kzalloc(dev, sizeof(*dspk), GFP_KERNEL);
487	if (!dspk)
488		return -ENOMEM;
489
490	dspk->osr_val = DSPK_OSR_64;
491	dspk->lrsel = DSPK_LRSEL_LEFT;
492	dspk->ch_sel = DSPK_CH_SELECT_STEREO;
493	dspk->mono_to_stereo = 0; /* "Zero" */
494
495	dev_set_drvdata(dev, dspk);
496
497	dspk->clk_dspk = devm_clk_get(dev, "dspk");
498	if (IS_ERR(dspk->clk_dspk)) {
499		dev_err(dev, "can't retrieve DSPK clock\n");
500		return PTR_ERR(dspk->clk_dspk);
501	}
502
503	regs = devm_platform_ioremap_resource(pdev, 0);
504	if (IS_ERR(regs))
505		return PTR_ERR(regs);
506
507	dspk->regmap = devm_regmap_init_mmio(dev, regs, &tegra186_dspk_regmap);
508	if (IS_ERR(dspk->regmap)) {
509		dev_err(dev, "regmap init failed\n");
510		return PTR_ERR(dspk->regmap);
511	}
512
513	regcache_cache_only(dspk->regmap, true);
514
515	err = devm_snd_soc_register_component(dev, &tegra186_dspk_cmpnt,
516					      tegra186_dspk_dais,
517					      ARRAY_SIZE(tegra186_dspk_dais));
518	if (err) {
519		dev_err(dev, "can't register DSPK component, err: %d\n",
520			err);
521		return err;
522	}
523
524	pm_runtime_enable(dev);
525
526	return 0;
527}
528
529static void tegra186_dspk_platform_remove(struct platform_device *pdev)
530{
531	pm_runtime_disable(&pdev->dev);
532}
533
534static const struct dev_pm_ops tegra186_dspk_pm_ops = {
535	RUNTIME_PM_OPS(tegra186_dspk_runtime_suspend,
536		       tegra186_dspk_runtime_resume, NULL)
537	SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
538};
539
540static struct platform_driver tegra186_dspk_driver = {
541	.driver = {
542		.name = "tegra186-dspk",
543		.of_match_table = tegra186_dspk_of_match,
544		.pm = pm_ptr(&tegra186_dspk_pm_ops),
545	},
546	.probe = tegra186_dspk_platform_probe,
547	.remove = tegra186_dspk_platform_remove,
548};
549module_platform_driver(tegra186_dspk_driver);
550
551MODULE_AUTHOR("Mohan Kumar <mkumard@nvidia.com>");
552MODULE_AUTHOR("Sameer Pujar <spujar@nvidia.com>");
553MODULE_DESCRIPTION("Tegra186 ASoC DSPK driver");
554MODULE_LICENSE("GPL v2");
~/ Read-only file preview.