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