brintos

brintos / linux-shallow public Read only

0
0
Text · 18.9 KiB · e07e2f1 Raw
684 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2//3// tegra210_mixer.c - Tegra210 MIXER driver4//5// Copyright (c) 2021 NVIDIA CORPORATION.  All rights reserved.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 __maybe_unused 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 __maybe_unused 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		else157			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_soc_kcontrol_component(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_soc_kcontrol_component(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_S32_LE:252		audio_bits = TEGRA_ACIF_BITS_32;253		break;254	default:255		return -EINVAL;256	}257 258	cif_conf.audio_ch = channels;259	cif_conf.client_ch = channels;260	cif_conf.audio_bits = audio_bits;261	cif_conf.client_bits = audio_bits;262 263	tegra_set_cif(mixer->regmap,264		      reg + (id * TEGRA210_MIXER_REG_STRIDE),265		      &cif_conf);266 267	return 0;268}269 270static int tegra210_mixer_in_hw_params(struct snd_pcm_substream *substream,271				       struct snd_pcm_hw_params *params,272				       struct snd_soc_dai *dai)273{274	struct tegra210_mixer *mixer = snd_soc_dai_get_drvdata(dai);275	int err;276 277	err = tegra210_mixer_set_audio_cif(mixer, params,278					   TEGRA210_MIXER_RX1_CIF_CTRL,279					   dai->id);280	if (err < 0)281		return err;282 283	return tegra210_mixer_configure_gain(dai->component, dai->id, false);284}285 286static int tegra210_mixer_out_hw_params(struct snd_pcm_substream *substream,287					struct snd_pcm_hw_params *params,288					struct snd_soc_dai *dai)289{290	struct tegra210_mixer *mixer = snd_soc_dai_get_drvdata(dai);291 292	return tegra210_mixer_set_audio_cif(mixer, params,293					    TEGRA210_MIXER_TX1_CIF_CTRL,294					    dai->id - TEGRA210_MIXER_RX_MAX);295}296 297static const struct snd_soc_dai_ops tegra210_mixer_out_dai_ops = {298	.hw_params	= tegra210_mixer_out_hw_params,299};300 301static const struct snd_soc_dai_ops tegra210_mixer_in_dai_ops = {302	.hw_params	= tegra210_mixer_in_hw_params,303};304 305#define IN_DAI(id)						\306	{							\307		.name = "MIXER-RX-CIF"#id,			\308		.playback = {					\309			.stream_name = "RX" #id "-CIF-Playback",\310			.channels_min = 1,			\311			.channels_max = 8,			\312			.rates = SNDRV_PCM_RATE_8000_192000,	\313			.formats = SNDRV_PCM_FMTBIT_S8 |	\314				SNDRV_PCM_FMTBIT_S16_LE |	\315				SNDRV_PCM_FMTBIT_S32_LE,	\316		},						\317		.capture = {					\318			.stream_name = "RX" #id "-CIF-Capture",	\319			.channels_min = 1,			\320			.channels_max = 8,			\321			.rates = SNDRV_PCM_RATE_8000_192000,	\322			.formats = SNDRV_PCM_FMTBIT_S8 |	\323				SNDRV_PCM_FMTBIT_S16_LE |	\324				SNDRV_PCM_FMTBIT_S32_LE,	\325		},						\326		.ops = &tegra210_mixer_in_dai_ops,		\327	}328 329#define OUT_DAI(id)						\330	{							\331		.name = "MIXER-TX-CIF" #id,			\332		.playback = {					\333			.stream_name = "TX" #id "-CIF-Playback",\334			.channels_min = 1,			\335			.channels_max = 8,			\336			.rates = SNDRV_PCM_RATE_8000_192000,	\337			.formats = SNDRV_PCM_FMTBIT_S8 |	\338				SNDRV_PCM_FMTBIT_S16_LE |	\339				SNDRV_PCM_FMTBIT_S32_LE,	\340		},						\341		.capture = {					\342			.stream_name = "TX" #id "-CIF-Capture",	\343			.channels_min = 1,			\344			.channels_max = 8,			\345			.rates = SNDRV_PCM_RATE_8000_192000,	\346			.formats = SNDRV_PCM_FMTBIT_S8 |	\347				SNDRV_PCM_FMTBIT_S16_LE |	\348				SNDRV_PCM_FMTBIT_S32_LE,	\349		},						\350		.ops = &tegra210_mixer_out_dai_ops,		\351	}352 353static struct snd_soc_dai_driver tegra210_mixer_dais[] = {354	/* Mixer Input */355	IN_DAI(1),356	IN_DAI(2),357	IN_DAI(3),358	IN_DAI(4),359	IN_DAI(5),360	IN_DAI(6),361	IN_DAI(7),362	IN_DAI(8),363	IN_DAI(9),364	IN_DAI(10),365 366	/* Mixer Output */367	OUT_DAI(1),368	OUT_DAI(2),369	OUT_DAI(3),370	OUT_DAI(4),371	OUT_DAI(5),372};373 374#define ADDER_CTRL_DECL(name, reg)			\375	static const struct snd_kcontrol_new name[] = {	\376		SOC_DAPM_SINGLE("RX1", reg, 0, 1, 0),	\377		SOC_DAPM_SINGLE("RX2", reg, 1, 1, 0),	\378		SOC_DAPM_SINGLE("RX3", reg, 2, 1, 0),	\379		SOC_DAPM_SINGLE("RX4", reg, 3, 1, 0),	\380		SOC_DAPM_SINGLE("RX5", reg, 4, 1, 0),	\381		SOC_DAPM_SINGLE("RX6", reg, 5, 1, 0),	\382		SOC_DAPM_SINGLE("RX7", reg, 6, 1, 0),	\383		SOC_DAPM_SINGLE("RX8", reg, 7, 1, 0),	\384		SOC_DAPM_SINGLE("RX9", reg, 8, 1, 0),	\385		SOC_DAPM_SINGLE("RX10", reg, 9, 1, 0),	\386	}387 388ADDER_CTRL_DECL(adder1, TEGRA210_MIXER_TX1_ADDER_CONFIG);389ADDER_CTRL_DECL(adder2, TEGRA210_MIXER_TX2_ADDER_CONFIG);390ADDER_CTRL_DECL(adder3, TEGRA210_MIXER_TX3_ADDER_CONFIG);391ADDER_CTRL_DECL(adder4, TEGRA210_MIXER_TX4_ADDER_CONFIG);392ADDER_CTRL_DECL(adder5, TEGRA210_MIXER_TX5_ADDER_CONFIG);393 394#define GAIN_CTRL(id)	\395	SOC_SINGLE_EXT("RX" #id " Gain Volume",			\396		       MIXER_GAIN_CFG_RAM_ADDR((id) - 1), 0,	\397		       0x20000, 0, tegra210_mixer_get_gain,	\398		       tegra210_mixer_put_gain),		\399	SOC_SINGLE_EXT("RX" #id " Instant Gain Volume",		\400		       MIXER_GAIN_CFG_RAM_ADDR((id) - 1), 0,	\401		       0x20000, 0, tegra210_mixer_get_gain,	\402		       tegra210_mixer_put_instant_gain),403 404/* Volume controls for all MIXER inputs */405static const struct snd_kcontrol_new tegra210_mixer_gain_ctls[] = {406	GAIN_CTRL(1)407	GAIN_CTRL(2)408	GAIN_CTRL(3)409	GAIN_CTRL(4)410	GAIN_CTRL(5)411	GAIN_CTRL(6)412	GAIN_CTRL(7)413	GAIN_CTRL(8)414	GAIN_CTRL(9)415	GAIN_CTRL(10)416};417 418static const struct snd_soc_dapm_widget tegra210_mixer_widgets[] = {419	SND_SOC_DAPM_AIF_IN("RX1", NULL, 0, SND_SOC_NOPM, 0, 0),420	SND_SOC_DAPM_AIF_IN("RX2", NULL, 0, SND_SOC_NOPM, 0, 0),421	SND_SOC_DAPM_AIF_IN("RX3", NULL, 0, SND_SOC_NOPM, 0, 0),422	SND_SOC_DAPM_AIF_IN("RX4", NULL, 0, SND_SOC_NOPM, 0, 0),423	SND_SOC_DAPM_AIF_IN("RX5", NULL, 0, SND_SOC_NOPM, 0, 0),424	SND_SOC_DAPM_AIF_IN("RX6", NULL, 0, SND_SOC_NOPM, 0, 0),425	SND_SOC_DAPM_AIF_IN("RX7", NULL, 0, SND_SOC_NOPM, 0, 0),426	SND_SOC_DAPM_AIF_IN("RX8", NULL, 0, SND_SOC_NOPM, 0, 0),427	SND_SOC_DAPM_AIF_IN("RX9", NULL, 0, SND_SOC_NOPM, 0, 0),428	SND_SOC_DAPM_AIF_IN("RX10", NULL, 0, SND_SOC_NOPM, 0, 0),429	SND_SOC_DAPM_AIF_OUT("TX1", NULL, 0, TEGRA210_MIXER_TX1_ENABLE, 0, 0),430	SND_SOC_DAPM_AIF_OUT("TX2", NULL, 0, TEGRA210_MIXER_TX2_ENABLE, 0, 0),431	SND_SOC_DAPM_AIF_OUT("TX3", NULL, 0, TEGRA210_MIXER_TX3_ENABLE, 0, 0),432	SND_SOC_DAPM_AIF_OUT("TX4", NULL, 0, TEGRA210_MIXER_TX4_ENABLE, 0, 0),433	SND_SOC_DAPM_AIF_OUT("TX5", NULL, 0, TEGRA210_MIXER_TX5_ENABLE, 0, 0),434	SND_SOC_DAPM_MIXER("Adder1", SND_SOC_NOPM, 1, 0, adder1,435			   ARRAY_SIZE(adder1)),436	SND_SOC_DAPM_MIXER("Adder2", SND_SOC_NOPM, 1, 0, adder2,437			   ARRAY_SIZE(adder2)),438	SND_SOC_DAPM_MIXER("Adder3", SND_SOC_NOPM, 1, 0, adder3,439			   ARRAY_SIZE(adder3)),440	SND_SOC_DAPM_MIXER("Adder4", SND_SOC_NOPM, 1, 0, adder4,441			   ARRAY_SIZE(adder4)),442	SND_SOC_DAPM_MIXER("Adder5", SND_SOC_NOPM, 1, 0, adder5,443			   ARRAY_SIZE(adder5)),444};445 446#define RX_ROUTES(id, sname)						   \447	{ "RX" #id " XBAR-" sname,	NULL,	"RX" #id " XBAR-TX" },	   \448	{ "RX" #id "-CIF-" sname,	NULL,	"RX" #id " XBAR-" sname }, \449	{ "RX" #id,			NULL,	"RX" #id "-CIF-" sname }450 451#define MIXER_RX_ROUTES(id)		\452	RX_ROUTES(id, "Playback"),	\453	RX_ROUTES(id, "Capture")454 455#define ADDER_ROUTES(id, sname)						  \456	{ "Adder" #id,			"RX1",	"RX1" },		  \457	{ "Adder" #id,			"RX2",	"RX2" },		  \458	{ "Adder" #id,			"RX3",	"RX3" },		  \459	{ "Adder" #id,			"RX4",	"RX4" },		  \460	{ "Adder" #id,			"RX5",	"RX5" },		  \461	{ "Adder" #id,			"RX6",	"RX6" },		  \462	{ "Adder" #id,			"RX7",	"RX7" },		  \463	{ "Adder" #id,			"RX8",	"RX8" },		  \464	{ "Adder" #id,			"RX9",	"RX9" },		  \465	{ "Adder" #id,			"RX10",	"RX10" },		  \466	{ "TX" #id,			NULL,	"Adder" #id },		  \467	{ "TX" #id "-CIF-" sname,	NULL,	"TX" #id },		  \468	{ "TX" #id " XBAR-" sname,	NULL,	"TX" #id "-CIF-" sname }, \469	{ "TX" #id " XBAR-RX",		NULL,	"TX" #id " XBAR-" sname } \470 471#define TX_ROUTES(id, sname)		\472	ADDER_ROUTES(1, sname),		\473	ADDER_ROUTES(2, sname),		\474	ADDER_ROUTES(3, sname),		\475	ADDER_ROUTES(4, sname),		\476	ADDER_ROUTES(5, sname)477 478#define MIXER_TX_ROUTES(id)		\479	TX_ROUTES(id, "Playback"),	\480	TX_ROUTES(id, "Capture")481 482static const struct snd_soc_dapm_route tegra210_mixer_routes[] = {483	/* Input */484	MIXER_RX_ROUTES(1),485	MIXER_RX_ROUTES(2),486	MIXER_RX_ROUTES(3),487	MIXER_RX_ROUTES(4),488	MIXER_RX_ROUTES(5),489	MIXER_RX_ROUTES(6),490	MIXER_RX_ROUTES(7),491	MIXER_RX_ROUTES(8),492	MIXER_RX_ROUTES(9),493	MIXER_RX_ROUTES(10),494	/* Output */495	MIXER_TX_ROUTES(1),496	MIXER_TX_ROUTES(2),497	MIXER_TX_ROUTES(3),498	MIXER_TX_ROUTES(4),499	MIXER_TX_ROUTES(5),500};501 502static const struct snd_soc_component_driver tegra210_mixer_cmpnt = {503	.dapm_widgets		= tegra210_mixer_widgets,504	.num_dapm_widgets	= ARRAY_SIZE(tegra210_mixer_widgets),505	.dapm_routes		= tegra210_mixer_routes,506	.num_dapm_routes	= ARRAY_SIZE(tegra210_mixer_routes),507	.controls		= tegra210_mixer_gain_ctls,508	.num_controls		= ARRAY_SIZE(tegra210_mixer_gain_ctls),509};510 511static bool tegra210_mixer_wr_reg(struct device *dev,512				unsigned int reg)513{514	if (reg < TEGRA210_MIXER_RX_LIMIT)515		reg = MIXER_REG_BASE(reg);516	else if (reg < TEGRA210_MIXER_TX_LIMIT)517		reg = MIXER_REG_BASE(reg) + TEGRA210_MIXER_TX1_ENABLE;518 519	switch (reg) {520	case TEGRA210_MIXER_RX1_SOFT_RESET:521	case TEGRA210_MIXER_RX1_CIF_CTRL ... TEGRA210_MIXER_RX1_PEAK_CTRL:522 523	case TEGRA210_MIXER_TX1_ENABLE:524	case TEGRA210_MIXER_TX1_SOFT_RESET:525	case TEGRA210_MIXER_TX1_INT_MASK ... TEGRA210_MIXER_TX1_ADDER_CONFIG:526 527	case TEGRA210_MIXER_ENABLE ... TEGRA210_MIXER_CG:528	case TEGRA210_MIXER_GAIN_CFG_RAM_CTRL ... TEGRA210_MIXER_CTRL:529		return true;530	default:531		return false;532	}533}534 535static bool tegra210_mixer_rd_reg(struct device *dev,536				unsigned int reg)537{538	if (reg < TEGRA210_MIXER_RX_LIMIT)539		reg = MIXER_REG_BASE(reg);540	else if (reg < TEGRA210_MIXER_TX_LIMIT)541		reg = MIXER_REG_BASE(reg) + TEGRA210_MIXER_TX1_ENABLE;542 543	switch (reg) {544	case TEGRA210_MIXER_RX1_SOFT_RESET ... TEGRA210_MIXER_RX1_SAMPLE_COUNT:545	case TEGRA210_MIXER_TX1_ENABLE ... TEGRA210_MIXER_TX1_ADDER_CONFIG:546	case TEGRA210_MIXER_ENABLE ... TEGRA210_MIXER_CTRL:547		return true;548	default:549		return false;550	}551}552 553static bool tegra210_mixer_volatile_reg(struct device *dev,554				unsigned int reg)555{556	if (reg < TEGRA210_MIXER_RX_LIMIT)557		reg = MIXER_REG_BASE(reg);558	else if (reg < TEGRA210_MIXER_TX_LIMIT)559		reg = MIXER_REG_BASE(reg) + TEGRA210_MIXER_TX1_ENABLE;560 561	switch (reg) {562	case TEGRA210_MIXER_RX1_SOFT_RESET:563	case TEGRA210_MIXER_RX1_STATUS:564 565	case TEGRA210_MIXER_TX1_SOFT_RESET:566	case TEGRA210_MIXER_TX1_STATUS:567	case TEGRA210_MIXER_TX1_INT_STATUS:568	case TEGRA210_MIXER_TX1_INT_SET:569 570	case TEGRA210_MIXER_SOFT_RESET:571	case TEGRA210_MIXER_STATUS:572	case TEGRA210_MIXER_INT_STATUS:573	case TEGRA210_MIXER_GAIN_CFG_RAM_CTRL:574	case TEGRA210_MIXER_GAIN_CFG_RAM_DATA:575	case TEGRA210_MIXER_PEAKM_RAM_CTRL:576	case TEGRA210_MIXER_PEAKM_RAM_DATA:577		return true;578	default:579		return false;580	}581}582 583static bool tegra210_mixer_precious_reg(struct device *dev,584				unsigned int reg)585{586	switch (reg) {587	case TEGRA210_MIXER_GAIN_CFG_RAM_DATA:588	case TEGRA210_MIXER_PEAKM_RAM_DATA:589		return true;590	default:591		return false;592	}593}594 595static const struct regmap_config tegra210_mixer_regmap_config = {596	.reg_bits		= 32,597	.reg_stride		= 4,598	.val_bits		= 32,599	.max_register		= TEGRA210_MIXER_CTRL,600	.writeable_reg		= tegra210_mixer_wr_reg,601	.readable_reg		= tegra210_mixer_rd_reg,602	.volatile_reg		= tegra210_mixer_volatile_reg,603	.precious_reg		= tegra210_mixer_precious_reg,604	.reg_defaults		= tegra210_mixer_reg_defaults,605	.num_reg_defaults	= ARRAY_SIZE(tegra210_mixer_reg_defaults),606	.cache_type		= REGCACHE_FLAT,607};608 609static const struct of_device_id tegra210_mixer_of_match[] = {610	{ .compatible = "nvidia,tegra210-amixer" },611	{},612};613MODULE_DEVICE_TABLE(of, tegra210_mixer_of_match);614 615static int tegra210_mixer_platform_probe(struct platform_device *pdev)616{617	struct device *dev = &pdev->dev;618	struct tegra210_mixer *mixer;619	void __iomem *regs;620	int err, i;621 622	mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);623	if (!mixer)624		return -ENOMEM;625 626	dev_set_drvdata(dev, mixer);627 628	/* Use default gain value for all MIXER inputs */629	for (i = 0; i < TEGRA210_MIXER_RX_MAX; i++)630		mixer->gain_value[i] = gain_params.gain_value;631 632	regs = devm_platform_ioremap_resource(pdev, 0);633	if (IS_ERR(regs))634		return PTR_ERR(regs);635 636	mixer->regmap = devm_regmap_init_mmio(dev, regs,637					      &tegra210_mixer_regmap_config);638	if (IS_ERR(mixer->regmap)) {639		dev_err(dev, "regmap init failed\n");640		return PTR_ERR(mixer->regmap);641	}642 643	regcache_cache_only(mixer->regmap, true);644 645	err = devm_snd_soc_register_component(dev, &tegra210_mixer_cmpnt,646					      tegra210_mixer_dais,647					      ARRAY_SIZE(tegra210_mixer_dais));648	if (err) {649		dev_err(dev, "can't register MIXER component, err: %d\n", err);650		return err;651	}652 653	pm_runtime_enable(dev);654 655	return 0;656}657 658static void tegra210_mixer_platform_remove(struct platform_device *pdev)659{660	pm_runtime_disable(&pdev->dev);661}662 663static const struct dev_pm_ops tegra210_mixer_pm_ops = {664	SET_RUNTIME_PM_OPS(tegra210_mixer_runtime_suspend,665			   tegra210_mixer_runtime_resume, NULL)666	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,667				pm_runtime_force_resume)668};669 670static struct platform_driver tegra210_mixer_driver = {671	.driver = {672		.name = "tegra210_mixer",673		.of_match_table = tegra210_mixer_of_match,674		.pm = &tegra210_mixer_pm_ops,675	},676	.probe = tegra210_mixer_platform_probe,677	.remove = tegra210_mixer_platform_remove,678};679module_platform_driver(tegra210_mixer_driver);680 681MODULE_AUTHOR("Arun Shamanna Lakshmi <aruns@nvidia.com>");682MODULE_DESCRIPTION("Tegra210 MIXER ASoC driver");683MODULE_LICENSE("GPL v2");684