File Index
Files and directories
Sign in
FILE

tegra210_mixer.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/tegra/tegra210_mixer.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2// SPDX-FileCopyrightText: Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES.
  3// All rights reserved.
  4//
  5// tegra210_mixer.c - Tegra210 MIXER driver
  6
  7#include <linux/clk.h>
  8#include <linux/device.h>
  9#include <linux/io.h>
 10#include <linux/mod_devicetable.h>
 11#include <linux/module.h>
 12#include <linux/platform_device.h>
 13#include <linux/pm_runtime.h>
 14#include <linux/regmap.h>
 15#include <sound/core.h>
 16#include <sound/pcm.h>
 17#include <sound/pcm_params.h>
 18#include <sound/soc.h>
 19
 20#include "tegra210_mixer.h"
 21#include "tegra_cif.h"
 22
 23#define MIXER_REG(reg, id)	((reg) + ((id) * TEGRA210_MIXER_REG_STRIDE))
 24#define MIXER_REG_BASE(reg)	((reg) % TEGRA210_MIXER_REG_STRIDE)
 25
 26#define MIXER_GAIN_CFG_RAM_ADDR(id)					\
 27	(TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_0 +				\
 28	 ((id) * TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_STRIDE))
 29
 30#define MIXER_RX_REG_DEFAULTS(id)					\
 31	{ MIXER_REG(TEGRA210_MIXER_RX1_CIF_CTRL, id), 0x00007700},	\
 32	{ MIXER_REG(TEGRA210_MIXER_RX1_CTRL, id), 0x00010823},	\
 33	{ MIXER_REG(TEGRA210_MIXER_RX1_PEAK_CTRL, id), 0x000012c0}
 34
 35#define MIXER_TX_REG_DEFAULTS(id)					\
 36	{ MIXER_REG(TEGRA210_MIXER_TX1_INT_MASK, (id)), 0x00000001},	\
 37	{ MIXER_REG(TEGRA210_MIXER_TX1_CIF_CTRL, (id)), 0x00007700}
 38
 39#define REG_DURATION_PARAM(reg, i) ((reg) + NUM_GAIN_POLY_COEFFS + 1 + (i))
 40
 41static const struct reg_default tegra210_mixer_reg_defaults[] = {
 42	/* Inputs */
 43	MIXER_RX_REG_DEFAULTS(0),
 44	MIXER_RX_REG_DEFAULTS(1),
 45	MIXER_RX_REG_DEFAULTS(2),
 46	MIXER_RX_REG_DEFAULTS(3),
 47	MIXER_RX_REG_DEFAULTS(4),
 48	MIXER_RX_REG_DEFAULTS(5),
 49	MIXER_RX_REG_DEFAULTS(6),
 50	MIXER_RX_REG_DEFAULTS(7),
 51	MIXER_RX_REG_DEFAULTS(8),
 52	MIXER_RX_REG_DEFAULTS(9),
 53	/* Outputs */
 54	MIXER_TX_REG_DEFAULTS(0),
 55	MIXER_TX_REG_DEFAULTS(1),
 56	MIXER_TX_REG_DEFAULTS(2),
 57	MIXER_TX_REG_DEFAULTS(3),
 58	MIXER_TX_REG_DEFAULTS(4),
 59
 60	{ TEGRA210_MIXER_CG, 0x00000001},
 61	{ TEGRA210_MIXER_GAIN_CFG_RAM_CTRL, 0x00004000},
 62	{ TEGRA210_MIXER_PEAKM_RAM_CTRL, 0x00004000},
 63	{ TEGRA210_MIXER_ENABLE, 0x1 },
 64};
 65
 66/* Default gain parameters */
 67static const struct tegra210_mixer_gain_params gain_params = {
 68	/* Polynomial coefficients */
 69	{ 0, 0, 0, 0, 0, 0, 0, 0x1000000, 0 },
 70	/* Gain value */
 71	0x10000,
 72	/* Duration Parameters */
 73	{ 0, 0, 0x400, 0x8000000 },
 74};
 75
 76static int tegra210_mixer_runtime_suspend(struct device *dev)
 77{
 78	struct tegra210_mixer *mixer = dev_get_drvdata(dev);
 79
 80	regcache_cache_only(mixer->regmap, true);
 81	regcache_mark_dirty(mixer->regmap);
 82
 83	return 0;
 84}
 85
 86static int tegra210_mixer_runtime_resume(struct device *dev)
 87{
 88	struct tegra210_mixer *mixer = dev_get_drvdata(dev);
 89
 90	regcache_cache_only(mixer->regmap, false);
 91	regcache_sync(mixer->regmap);
 92
 93	return 0;
 94}
 95
 96static int tegra210_mixer_write_ram(struct tegra210_mixer *mixer,
 97				    unsigned int addr,
 98				    unsigned int coef)
 99{
100	unsigned int reg, val;
101	int err;
102
103	/* Check if busy */
104	err = regmap_read_poll_timeout(mixer->regmap,
105				       TEGRA210_MIXER_GAIN_CFG_RAM_CTRL,
106				       val, !(val & 0x80000000), 10, 10000);
107	if (err < 0)
108		return err;
109
110	reg = (addr << TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_SHIFT) &
111	      TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_MASK;
112	reg |= TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_INIT_EN;
113	reg |= TEGRA210_MIXER_GAIN_CFG_RAM_RW_WRITE;
114	reg |= TEGRA210_MIXER_GAIN_CFG_RAM_SEQ_ACCESS_EN;
115
116	regmap_write(mixer->regmap,
117		     TEGRA210_MIXER_GAIN_CFG_RAM_CTRL,
118		     reg);
119	regmap_write(mixer->regmap,
120		     TEGRA210_MIXER_GAIN_CFG_RAM_DATA,
121		     coef);
122
123	return 0;
124}
125
126static int tegra210_mixer_configure_gain(struct snd_soc_component *cmpnt,
127					 unsigned int id, bool instant_gain)
128{
129	struct tegra210_mixer *mixer = snd_soc_component_get_drvdata(cmpnt);
130	unsigned int reg = MIXER_GAIN_CFG_RAM_ADDR(id);
131	int err, i;
132
133	pm_runtime_get_sync(cmpnt->dev);
134
135	/* Write default gain poly coefficients */
136	for (i = 0; i < NUM_GAIN_POLY_COEFFS; i++) {
137		err = tegra210_mixer_write_ram(mixer, reg + i,
138					       gain_params.poly_coeff[i]);
139
140		if (err < 0)
141			goto rpm_put;
142	}
143
144	/* Write stored gain value */
145	err = tegra210_mixer_write_ram(mixer, reg + NUM_GAIN_POLY_COEFFS,
146				       mixer->gain_value[id]);
147	if (err < 0)
148		goto rpm_put;
149
150	/* Write duration parameters */
151	for (i = 0; i < NUM_DURATION_PARMS; i++) {
152		int val;
153
154		if (instant_gain)
155			val = 1;
156		else
157			val = gain_params.duration[i];
158
159		err = tegra210_mixer_write_ram(mixer,
160					       REG_DURATION_PARAM(reg, i),
161					       val);
162		if (err < 0)
163			goto rpm_put;
164	}
165
166	/* Trigger to apply gain configurations */
167	err = tegra210_mixer_write_ram(mixer, reg + REG_CFG_DONE_TRIGGER,
168				       VAL_CFG_DONE_TRIGGER);
169
170rpm_put:
171	pm_runtime_put(cmpnt->dev);
172
173	return err;
174}
175
176static int tegra210_mixer_get_gain(struct snd_kcontrol *kcontrol,
177				   struct snd_ctl_elem_value *ucontrol)
178{
179	struct soc_mixer_control *mc =
180		(struct soc_mixer_control *)kcontrol->private_value;
181	struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
182	struct tegra210_mixer *mixer = snd_soc_component_get_drvdata(cmpnt);
183	unsigned int reg = mc->reg;
184	unsigned int i;
185
186	i = (reg - TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_0) /
187	    TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_STRIDE;
188
189	ucontrol->value.integer.value[0] = mixer->gain_value[i];
190
191	return 0;
192}
193
194static int tegra210_mixer_apply_gain(struct snd_kcontrol *kcontrol,
195				     struct snd_ctl_elem_value *ucontrol,
196				     bool instant_gain)
197{
198	struct soc_mixer_control *mc =
199		(struct soc_mixer_control *)kcontrol->private_value;
200	struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
201	struct tegra210_mixer *mixer = snd_soc_component_get_drvdata(cmpnt);
202	unsigned int reg = mc->reg, id;
203	int err;
204
205	/* Save gain value for specific MIXER input */
206	id = (reg - TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_0) /
207	     TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_STRIDE;
208
209	if (mixer->gain_value[id] == ucontrol->value.integer.value[0])
210		return 0;
211
212	mixer->gain_value[id] = ucontrol->value.integer.value[0];
213
214	err = tegra210_mixer_configure_gain(cmpnt, id, instant_gain);
215	if (err) {
216		dev_err(cmpnt->dev, "Failed to apply gain\n");
217		return err;
218	}
219
220	return 1;
221}
222
223static int tegra210_mixer_put_gain(struct snd_kcontrol *kcontrol,
224				   struct snd_ctl_elem_value *ucontrol)
225{
226	return tegra210_mixer_apply_gain(kcontrol, ucontrol, false);
227}
228
229static int tegra210_mixer_put_instant_gain(struct snd_kcontrol *kcontrol,
230					   struct snd_ctl_elem_value *ucontrol)
231{
232	return tegra210_mixer_apply_gain(kcontrol, ucontrol, true);
233}
234
235static int tegra210_mixer_set_audio_cif(struct tegra210_mixer *mixer,
236					struct snd_pcm_hw_params *params,
237					unsigned int reg,
238					unsigned int id)
239{
240	unsigned int channels, audio_bits;
241	struct tegra_cif_conf cif_conf;
242
243	memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));
244
245	channels = params_channels(params);
246
247	switch (params_format(params)) {
248	case SNDRV_PCM_FORMAT_S16_LE:
249		audio_bits = TEGRA_ACIF_BITS_16;
250		break;
251	case SNDRV_PCM_FORMAT_S24_LE:
252	case SNDRV_PCM_FORMAT_S32_LE:
253		audio_bits = TEGRA_ACIF_BITS_32;
254		break;
255	default:
256		return -EINVAL;
257	}
258
259	cif_conf.audio_ch = channels;
260	cif_conf.client_ch = channels;
261	cif_conf.audio_bits = audio_bits;
262	cif_conf.client_bits = audio_bits;
263
264	tegra_set_cif(mixer->regmap,
265		      reg + (id * TEGRA210_MIXER_REG_STRIDE),
266		      &cif_conf);
267
268	return 0;
269}
270
271static int tegra210_mixer_in_hw_params(struct snd_pcm_substream *substream,
272				       struct snd_pcm_hw_params *params,
273				       struct snd_soc_dai *dai)
274{
275	struct tegra210_mixer *mixer = snd_soc_dai_get_drvdata(dai);
276	int err;
277
278	err = tegra210_mixer_set_audio_cif(mixer, params,
279					   TEGRA210_MIXER_RX1_CIF_CTRL,
280					   dai->id);
281	if (err < 0)
282		return err;
283
284	return tegra210_mixer_configure_gain(dai->component, dai->id, false);
285}
286
287static int tegra210_mixer_out_hw_params(struct snd_pcm_substream *substream,
288					struct snd_pcm_hw_params *params,
289					struct snd_soc_dai *dai)
290{
291	struct tegra210_mixer *mixer = snd_soc_dai_get_drvdata(dai);
292
293	return tegra210_mixer_set_audio_cif(mixer, params,
294					    TEGRA210_MIXER_TX1_CIF_CTRL,
295					    dai->id - TEGRA210_MIXER_RX_MAX);
296}
297
298static const struct snd_soc_dai_ops tegra210_mixer_out_dai_ops = {
299	.hw_params	= tegra210_mixer_out_hw_params,
300};
301
302static const struct snd_soc_dai_ops tegra210_mixer_in_dai_ops = {
303	.hw_params	= tegra210_mixer_in_hw_params,
304};
305
306#define IN_DAI(id)						\
307	{							\
308		.name = "MIXER-RX-CIF"#id,			\
309		.playback = {					\
310			.stream_name = "RX" #id "-CIF-Playback",\
311			.channels_min = 1,			\
312			.channels_max = 8,			\
313			.rates = SNDRV_PCM_RATE_8000_192000,	\
314			.formats = SNDRV_PCM_FMTBIT_S8 |	\
315				SNDRV_PCM_FMTBIT_S16_LE |	\
316				SNDRV_PCM_FMTBIT_S24_LE |	\
317				SNDRV_PCM_FMTBIT_S32_LE,	\
318		},						\
319		.capture = {					\
320			.stream_name = "RX" #id "-CIF-Capture",	\
321			.channels_min = 1,			\
322			.channels_max = 8,			\
323			.rates = SNDRV_PCM_RATE_8000_192000,	\
324			.formats = SNDRV_PCM_FMTBIT_S8 |	\
325				SNDRV_PCM_FMTBIT_S16_LE |	\
326				SNDRV_PCM_FMTBIT_S24_LE |	\
327				SNDRV_PCM_FMTBIT_S32_LE,	\
328		},						\
329		.ops = &tegra210_mixer_in_dai_ops,		\
330	}
331
332#define OUT_DAI(id)						\
333	{							\
334		.name = "MIXER-TX-CIF" #id,			\
335		.playback = {					\
336			.stream_name = "TX" #id "-CIF-Playback",\
337			.channels_min = 1,			\
338			.channels_max = 8,			\
339			.rates = SNDRV_PCM_RATE_8000_192000,	\
340			.formats = SNDRV_PCM_FMTBIT_S8 |	\
341				SNDRV_PCM_FMTBIT_S16_LE |	\
342				SNDRV_PCM_FMTBIT_S24_LE |	\
343				SNDRV_PCM_FMTBIT_S32_LE,	\
344		},						\
345		.capture = {					\
346			.stream_name = "TX" #id "-CIF-Capture",	\
347			.channels_min = 1,			\
348			.channels_max = 8,			\
349			.rates = SNDRV_PCM_RATE_8000_192000,	\
350			.formats = SNDRV_PCM_FMTBIT_S8 |	\
351				SNDRV_PCM_FMTBIT_S16_LE |	\
352				SNDRV_PCM_FMTBIT_S24_LE |	\
353				SNDRV_PCM_FMTBIT_S32_LE,	\
354		},						\
355		.ops = &tegra210_mixer_out_dai_ops,		\
356	}
357
358static struct snd_soc_dai_driver tegra210_mixer_dais[] = {
359	/* Mixer Input */
360	IN_DAI(1),
361	IN_DAI(2),
362	IN_DAI(3),
363	IN_DAI(4),
364	IN_DAI(5),
365	IN_DAI(6),
366	IN_DAI(7),
367	IN_DAI(8),
368	IN_DAI(9),
369	IN_DAI(10),
370
371	/* Mixer Output */
372	OUT_DAI(1),
373	OUT_DAI(2),
374	OUT_DAI(3),
375	OUT_DAI(4),
376	OUT_DAI(5),
377};
378
379#define ADDER_CTRL_DECL(name, reg)			\
380	static const struct snd_kcontrol_new name[] = {	\
381		SOC_DAPM_SINGLE("RX1", reg, 0, 1, 0),	\
382		SOC_DAPM_SINGLE("RX2", reg, 1, 1, 0),	\
383		SOC_DAPM_SINGLE("RX3", reg, 2, 1, 0),	\
384		SOC_DAPM_SINGLE("RX4", reg, 3, 1, 0),	\
385		SOC_DAPM_SINGLE("RX5", reg, 4, 1, 0),	\
386		SOC_DAPM_SINGLE("RX6", reg, 5, 1, 0),	\
387		SOC_DAPM_SINGLE("RX7", reg, 6, 1, 0),	\
388		SOC_DAPM_SINGLE("RX8", reg, 7, 1, 0),	\
389		SOC_DAPM_SINGLE("RX9", reg, 8, 1, 0),	\
390		SOC_DAPM_SINGLE("RX10", reg, 9, 1, 0),	\
391	}
392
393ADDER_CTRL_DECL(adder1, TEGRA210_MIXER_TX1_ADDER_CONFIG);
394ADDER_CTRL_DECL(adder2, TEGRA210_MIXER_TX2_ADDER_CONFIG);
395ADDER_CTRL_DECL(adder3, TEGRA210_MIXER_TX3_ADDER_CONFIG);
396ADDER_CTRL_DECL(adder4, TEGRA210_MIXER_TX4_ADDER_CONFIG);
397ADDER_CTRL_DECL(adder5, TEGRA210_MIXER_TX5_ADDER_CONFIG);
398
399#define GAIN_CTRL(id)	\
400	SOC_SINGLE_EXT("RX" #id " Gain Volume",			\
401		       MIXER_GAIN_CFG_RAM_ADDR((id) - 1), 0,	\
402		       0x20000, 0, tegra210_mixer_get_gain,	\
403		       tegra210_mixer_put_gain),		\
404	SOC_SINGLE_EXT("RX" #id " Instant Gain Volume",		\
405		       MIXER_GAIN_CFG_RAM_ADDR((id) - 1), 0,	\
406		       0x20000, 0, tegra210_mixer_get_gain,	\
407		       tegra210_mixer_put_instant_gain),
408
409/* Volume controls for all MIXER inputs */
410static const struct snd_kcontrol_new tegra210_mixer_gain_ctls[] = {
411	GAIN_CTRL(1)
412	GAIN_CTRL(2)
413	GAIN_CTRL(3)
414	GAIN_CTRL(4)
415	GAIN_CTRL(5)
416	GAIN_CTRL(6)
417	GAIN_CTRL(7)
418	GAIN_CTRL(8)
419	GAIN_CTRL(9)
420	GAIN_CTRL(10)
421};
422
423static const struct snd_soc_dapm_widget tegra210_mixer_widgets[] = {
424	SND_SOC_DAPM_AIF_IN("RX1", NULL, 0, SND_SOC_NOPM, 0, 0),
425	SND_SOC_DAPM_AIF_IN("RX2", NULL, 0, SND_SOC_NOPM, 0, 0),
426	SND_SOC_DAPM_AIF_IN("RX3", NULL, 0, SND_SOC_NOPM, 0, 0),
427	SND_SOC_DAPM_AIF_IN("RX4", NULL, 0, SND_SOC_NOPM, 0, 0),
428	SND_SOC_DAPM_AIF_IN("RX5", NULL, 0, SND_SOC_NOPM, 0, 0),
429	SND_SOC_DAPM_AIF_IN("RX6", NULL, 0, SND_SOC_NOPM, 0, 0),
430	SND_SOC_DAPM_AIF_IN("RX7", NULL, 0, SND_SOC_NOPM, 0, 0),
431	SND_SOC_DAPM_AIF_IN("RX8", NULL, 0, SND_SOC_NOPM, 0, 0),
432	SND_SOC_DAPM_AIF_IN("RX9", NULL, 0, SND_SOC_NOPM, 0, 0),
433	SND_SOC_DAPM_AIF_IN("RX10", NULL, 0, SND_SOC_NOPM, 0, 0),
434	SND_SOC_DAPM_AIF_OUT("TX1", NULL, 0, TEGRA210_MIXER_TX1_ENABLE, 0, 0),
435	SND_SOC_DAPM_AIF_OUT("TX2", NULL, 0, TEGRA210_MIXER_TX2_ENABLE, 0, 0),
436	SND_SOC_DAPM_AIF_OUT("TX3", NULL, 0, TEGRA210_MIXER_TX3_ENABLE, 0, 0),
437	SND_SOC_DAPM_AIF_OUT("TX4", NULL, 0, TEGRA210_MIXER_TX4_ENABLE, 0, 0),
438	SND_SOC_DAPM_AIF_OUT("TX5", NULL, 0, TEGRA210_MIXER_TX5_ENABLE, 0, 0),
439	SND_SOC_DAPM_MIXER("Adder1", SND_SOC_NOPM, 1, 0, adder1,
440			   ARRAY_SIZE(adder1)),
441	SND_SOC_DAPM_MIXER("Adder2", SND_SOC_NOPM, 1, 0, adder2,
442			   ARRAY_SIZE(adder2)),
443	SND_SOC_DAPM_MIXER("Adder3", SND_SOC_NOPM, 1, 0, adder3,
444			   ARRAY_SIZE(adder3)),
445	SND_SOC_DAPM_MIXER("Adder4", SND_SOC_NOPM, 1, 0, adder4,
446			   ARRAY_SIZE(adder4)),
447	SND_SOC_DAPM_MIXER("Adder5", SND_SOC_NOPM, 1, 0, adder5,
448			   ARRAY_SIZE(adder5)),
449};
450
451#define RX_ROUTES(id, sname)						   \
452	{ "RX" #id " XBAR-" sname,	NULL,	"RX" #id " XBAR-TX" },	   \
453	{ "RX" #id "-CIF-" sname,	NULL,	"RX" #id " XBAR-" sname }, \
454	{ "RX" #id,			NULL,	"RX" #id "-CIF-" sname }
455
456#define MIXER_RX_ROUTES(id)		\
457	RX_ROUTES(id, "Playback"),	\
458	RX_ROUTES(id, "Capture")
459
460#define ADDER_ROUTES(id, sname)						  \
461	{ "Adder" #id,			"RX1",	"RX1" },		  \
462	{ "Adder" #id,			"RX2",	"RX2" },		  \
463	{ "Adder" #id,			"RX3",	"RX3" },		  \
464	{ "Adder" #id,			"RX4",	"RX4" },		  \
465	{ "Adder" #id,			"RX5",	"RX5" },		  \
466	{ "Adder" #id,			"RX6",	"RX6" },		  \
467	{ "Adder" #id,			"RX7",	"RX7" },		  \
468	{ "Adder" #id,			"RX8",	"RX8" },		  \
469	{ "Adder" #id,			"RX9",	"RX9" },		  \
470	{ "Adder" #id,			"RX10",	"RX10" },		  \
471	{ "TX" #id,			NULL,	"Adder" #id },		  \
472	{ "TX" #id "-CIF-" sname,	NULL,	"TX" #id },		  \
473	{ "TX" #id " XBAR-" sname,	NULL,	"TX" #id "-CIF-" sname }, \
474	{ "TX" #id " XBAR-RX",		NULL,	"TX" #id " XBAR-" sname } \
475
476#define TX_ROUTES(id, sname)		\
477	ADDER_ROUTES(1, sname),		\
478	ADDER_ROUTES(2, sname),		\
479	ADDER_ROUTES(3, sname),		\
480	ADDER_ROUTES(4, sname),		\
481	ADDER_ROUTES(5, sname)
482
483#define MIXER_TX_ROUTES(id)		\
484	TX_ROUTES(id, "Playback"),	\
485	TX_ROUTES(id, "Capture")
486
487static const struct snd_soc_dapm_route tegra210_mixer_routes[] = {
488	/* Input */
489	MIXER_RX_ROUTES(1),
490	MIXER_RX_ROUTES(2),
491	MIXER_RX_ROUTES(3),
492	MIXER_RX_ROUTES(4),
493	MIXER_RX_ROUTES(5),
494	MIXER_RX_ROUTES(6),
495	MIXER_RX_ROUTES(7),
496	MIXER_RX_ROUTES(8),
497	MIXER_RX_ROUTES(9),
498	MIXER_RX_ROUTES(10),
499	/* Output */
500	MIXER_TX_ROUTES(1),
501	MIXER_TX_ROUTES(2),
502	MIXER_TX_ROUTES(3),
503	MIXER_TX_ROUTES(4),
504	MIXER_TX_ROUTES(5),
505};
506
507static const struct snd_soc_component_driver tegra210_mixer_cmpnt = {
508	.dapm_widgets		= tegra210_mixer_widgets,
509	.num_dapm_widgets	= ARRAY_SIZE(tegra210_mixer_widgets),
510	.dapm_routes		= tegra210_mixer_routes,
511	.num_dapm_routes	= ARRAY_SIZE(tegra210_mixer_routes),
512	.controls		= tegra210_mixer_gain_ctls,
513	.num_controls		= ARRAY_SIZE(tegra210_mixer_gain_ctls),
514};
515
516static bool tegra210_mixer_wr_reg(struct device *dev,
517				unsigned int reg)
518{
519	if (reg < TEGRA210_MIXER_RX_LIMIT)
520		reg = MIXER_REG_BASE(reg);
521	else if (reg < TEGRA210_MIXER_TX_LIMIT)
522		reg = MIXER_REG_BASE(reg) + TEGRA210_MIXER_TX1_ENABLE;
523
524	switch (reg) {
525	case TEGRA210_MIXER_RX1_SOFT_RESET:
526	case TEGRA210_MIXER_RX1_CIF_CTRL ... TEGRA210_MIXER_RX1_PEAK_CTRL:
527
528	case TEGRA210_MIXER_TX1_ENABLE:
529	case TEGRA210_MIXER_TX1_SOFT_RESET:
530	case TEGRA210_MIXER_TX1_INT_MASK ... TEGRA210_MIXER_TX1_ADDER_CONFIG:
531
532	case TEGRA210_MIXER_ENABLE ... TEGRA210_MIXER_CG:
533	case TEGRA210_MIXER_GAIN_CFG_RAM_CTRL ... TEGRA210_MIXER_CTRL:
534		return true;
535	default:
536		return false;
537	}
538}
539
540static bool tegra210_mixer_rd_reg(struct device *dev,
541				unsigned int reg)
542{
543	if (reg < TEGRA210_MIXER_RX_LIMIT)
544		reg = MIXER_REG_BASE(reg);
545	else if (reg < TEGRA210_MIXER_TX_LIMIT)
546		reg = MIXER_REG_BASE(reg) + TEGRA210_MIXER_TX1_ENABLE;
547
548	switch (reg) {
549	case TEGRA210_MIXER_RX1_SOFT_RESET ... TEGRA210_MIXER_RX1_SAMPLE_COUNT:
550	case TEGRA210_MIXER_TX1_ENABLE ... TEGRA210_MIXER_TX1_ADDER_CONFIG:
551	case TEGRA210_MIXER_ENABLE ... TEGRA210_MIXER_CTRL:
552		return true;
553	default:
554		return false;
555	}
556}
557
558static bool tegra210_mixer_volatile_reg(struct device *dev,
559				unsigned int reg)
560{
561	if (reg < TEGRA210_MIXER_RX_LIMIT)
562		reg = MIXER_REG_BASE(reg);
563	else if (reg < TEGRA210_MIXER_TX_LIMIT)
564		reg = MIXER_REG_BASE(reg) + TEGRA210_MIXER_TX1_ENABLE;
565
566	switch (reg) {
567	case TEGRA210_MIXER_RX1_SOFT_RESET:
568	case TEGRA210_MIXER_RX1_STATUS:
569
570	case TEGRA210_MIXER_TX1_SOFT_RESET:
571	case TEGRA210_MIXER_TX1_STATUS:
572	case TEGRA210_MIXER_TX1_INT_STATUS:
573	case TEGRA210_MIXER_TX1_INT_SET:
574
575	case TEGRA210_MIXER_SOFT_RESET:
576	case TEGRA210_MIXER_STATUS:
577	case TEGRA210_MIXER_INT_STATUS:
578	case TEGRA210_MIXER_GAIN_CFG_RAM_CTRL:
579	case TEGRA210_MIXER_GAIN_CFG_RAM_DATA:
580	case TEGRA210_MIXER_PEAKM_RAM_CTRL:
581	case TEGRA210_MIXER_PEAKM_RAM_DATA:
582		return true;
583	default:
584		return false;
585	}
586}
587
588static bool tegra210_mixer_precious_reg(struct device *dev,
589				unsigned int reg)
590{
591	switch (reg) {
592	case TEGRA210_MIXER_GAIN_CFG_RAM_DATA:
593	case TEGRA210_MIXER_PEAKM_RAM_DATA:
594		return true;
595	default:
596		return false;
597	}
598}
599
600static const struct regmap_config tegra210_mixer_regmap_config = {
601	.reg_bits		= 32,
602	.reg_stride		= 4,
603	.val_bits		= 32,
604	.max_register		= TEGRA210_MIXER_CTRL,
605	.writeable_reg		= tegra210_mixer_wr_reg,
606	.readable_reg		= tegra210_mixer_rd_reg,
607	.volatile_reg		= tegra210_mixer_volatile_reg,
608	.precious_reg		= tegra210_mixer_precious_reg,
609	.reg_defaults		= tegra210_mixer_reg_defaults,
610	.num_reg_defaults	= ARRAY_SIZE(tegra210_mixer_reg_defaults),
611	.cache_type		= REGCACHE_FLAT,
612};
613
614static const struct of_device_id tegra210_mixer_of_match[] = {
615	{ .compatible = "nvidia,tegra210-amixer" },
616	{},
617};
618MODULE_DEVICE_TABLE(of, tegra210_mixer_of_match);
619
620static int tegra210_mixer_platform_probe(struct platform_device *pdev)
621{
622	struct device *dev = &pdev->dev;
623	struct tegra210_mixer *mixer;
624	void __iomem *regs;
625	int err, i;
626
627	mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
628	if (!mixer)
629		return -ENOMEM;
630
631	dev_set_drvdata(dev, mixer);
632
633	/* Use default gain value for all MIXER inputs */
634	for (i = 0; i < TEGRA210_MIXER_RX_MAX; i++)
635		mixer->gain_value[i] = gain_params.gain_value;
636
637	regs = devm_platform_ioremap_resource(pdev, 0);
638	if (IS_ERR(regs))
639		return PTR_ERR(regs);
640
641	mixer->regmap = devm_regmap_init_mmio(dev, regs,
642					      &tegra210_mixer_regmap_config);
643	if (IS_ERR(mixer->regmap)) {
644		dev_err(dev, "regmap init failed\n");
645		return PTR_ERR(mixer->regmap);
646	}
647
648	regcache_cache_only(mixer->regmap, true);
649
650	err = devm_snd_soc_register_component(dev, &tegra210_mixer_cmpnt,
651					      tegra210_mixer_dais,
652					      ARRAY_SIZE(tegra210_mixer_dais));
653	if (err) {
654		dev_err(dev, "can't register MIXER component, err: %d\n", err);
655		return err;
656	}
657
658	pm_runtime_enable(dev);
659
660	return 0;
661}
662
663static void tegra210_mixer_platform_remove(struct platform_device *pdev)
664{
665	pm_runtime_disable(&pdev->dev);
666}
667
668static const struct dev_pm_ops tegra210_mixer_pm_ops = {
669	RUNTIME_PM_OPS(tegra210_mixer_runtime_suspend,
670		       tegra210_mixer_runtime_resume, NULL)
671	SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
672};
673
674static struct platform_driver tegra210_mixer_driver = {
675	.driver = {
676		.name = "tegra210_mixer",
677		.of_match_table = tegra210_mixer_of_match,
678		.pm = pm_ptr(&tegra210_mixer_pm_ops),
679	},
680	.probe = tegra210_mixer_platform_probe,
681	.remove = tegra210_mixer_platform_remove,
682};
683module_platform_driver(tegra210_mixer_driver);
684
685MODULE_AUTHOR("Arun Shamanna Lakshmi <aruns@nvidia.com>");
686MODULE_DESCRIPTION("Tegra210 MIXER ASoC driver");
687MODULE_LICENSE("GPL v2");
~/ Read-only file preview.