cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

tegra210_mvc.c (20800B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2//
      3// tegra210_mvc.c - Tegra210 MVC driver
      4//
      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/module.h>
     11#include <linux/of.h>
     12#include <linux/of_device.h>
     13#include <linux/platform_device.h>
     14#include <linux/pm_runtime.h>
     15#include <linux/regmap.h>
     16#include <sound/core.h>
     17#include <sound/pcm.h>
     18#include <sound/pcm_params.h>
     19#include <sound/soc.h>
     20
     21#include "tegra210_mvc.h"
     22#include "tegra_cif.h"
     23
     24static const struct reg_default tegra210_mvc_reg_defaults[] = {
     25	{ TEGRA210_MVC_RX_INT_MASK, 0x00000001},
     26	{ TEGRA210_MVC_RX_CIF_CTRL, 0x00007700},
     27	{ TEGRA210_MVC_TX_INT_MASK, 0x00000001},
     28	{ TEGRA210_MVC_TX_CIF_CTRL, 0x00007700},
     29	{ TEGRA210_MVC_CG, 0x1},
     30	{ TEGRA210_MVC_CTRL, TEGRA210_MVC_CTRL_DEFAULT},
     31	{ TEGRA210_MVC_INIT_VOL, 0x00800000},
     32	{ TEGRA210_MVC_TARGET_VOL, 0x00800000},
     33	{ TEGRA210_MVC_DURATION, 0x000012c0},
     34	{ TEGRA210_MVC_DURATION_INV, 0x0006d3a0},
     35	{ TEGRA210_MVC_POLY_N1, 0x0000007d},
     36	{ TEGRA210_MVC_POLY_N2, 0x00000271},
     37	{ TEGRA210_MVC_PEAK_CTRL, 0x000012c0},
     38	{ TEGRA210_MVC_CFG_RAM_CTRL, 0x00004000},
     39};
     40
     41static const struct tegra210_mvc_gain_params gain_params = {
     42	.poly_coeff = { 23738319, 659403, -3680,
     43			15546680, 2530732, -120985,
     44			12048422, 5527252, -785042 },
     45	.poly_n1 = 16,
     46	.poly_n2 = 63,
     47	.duration = 150,
     48	.duration_inv = 14316558,
     49};
     50
     51static int __maybe_unused tegra210_mvc_runtime_suspend(struct device *dev)
     52{
     53	struct tegra210_mvc *mvc = dev_get_drvdata(dev);
     54
     55	regmap_read(mvc->regmap, TEGRA210_MVC_CTRL, &(mvc->ctrl_value));
     56
     57	regcache_cache_only(mvc->regmap, true);
     58	regcache_mark_dirty(mvc->regmap);
     59
     60	return 0;
     61}
     62
     63static int __maybe_unused tegra210_mvc_runtime_resume(struct device *dev)
     64{
     65	struct tegra210_mvc *mvc = dev_get_drvdata(dev);
     66
     67	regcache_cache_only(mvc->regmap, false);
     68	regcache_sync(mvc->regmap);
     69
     70	regmap_write(mvc->regmap, TEGRA210_MVC_CTRL, mvc->ctrl_value);
     71	regmap_update_bits(mvc->regmap,
     72			   TEGRA210_MVC_SWITCH,
     73			   TEGRA210_MVC_VOLUME_SWITCH_MASK,
     74			   TEGRA210_MVC_VOLUME_SWITCH_TRIGGER);
     75
     76	return 0;
     77}
     78
     79static void tegra210_mvc_write_ram(struct regmap *regmap)
     80{
     81	int i;
     82
     83	regmap_write(regmap, TEGRA210_MVC_CFG_RAM_CTRL,
     84		     TEGRA210_MVC_CFG_RAM_CTRL_SEQ_ACCESS_EN |
     85		     TEGRA210_MVC_CFG_RAM_CTRL_ADDR_INIT_EN |
     86		     TEGRA210_MVC_CFG_RAM_CTRL_RW_WRITE);
     87
     88	for (i = 0; i < NUM_GAIN_POLY_COEFFS; i++)
     89		regmap_write(regmap, TEGRA210_MVC_CFG_RAM_DATA,
     90			     gain_params.poly_coeff[i]);
     91}
     92
     93static void tegra210_mvc_conv_vol(struct tegra210_mvc *mvc, u8 chan, s32 val)
     94{
     95	/*
     96	 * Volume control read from mixer control is with
     97	 * 100x scaling; for CURVE_POLY the reg range
     98	 * is 0-100 (linear, Q24) and for CURVE_LINEAR
     99	 * it is -120dB to +40dB (Q8)
    100	 */
    101	if (mvc->curve_type == CURVE_POLY) {
    102		if (val > 10000)
    103			val = 10000;
    104		mvc->volume[chan] = ((val * (1<<8)) / 100) << 16;
    105	} else {
    106		val -= 12000;
    107		mvc->volume[chan] = (val * (1<<8)) / 100;
    108	}
    109}
    110
    111static u32 tegra210_mvc_get_ctrl_reg(struct snd_kcontrol *kcontrol)
    112{
    113	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
    114	struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
    115	u32 val;
    116
    117	pm_runtime_get_sync(cmpnt->dev);
    118	regmap_read(mvc->regmap, TEGRA210_MVC_CTRL, &val);
    119	pm_runtime_put(cmpnt->dev);
    120
    121	return val;
    122}
    123
    124static int tegra210_mvc_get_mute(struct snd_kcontrol *kcontrol,
    125				 struct snd_ctl_elem_value *ucontrol)
    126{
    127	u32 val = tegra210_mvc_get_ctrl_reg(kcontrol);
    128	u8 mute_mask = TEGRA210_GET_MUTE_VAL(val);
    129
    130	/*
    131	 * If per channel control is enabled, then return
    132	 * exact mute/unmute setting of all channels.
    133	 *
    134	 * Else report setting based on CH0 bit to reflect
    135	 * the correct HW state.
    136	 */
    137	if (val & TEGRA210_MVC_PER_CHAN_CTRL_EN) {
    138		ucontrol->value.integer.value[0] = mute_mask;
    139	} else {
    140		if (mute_mask & TEGRA210_MVC_CH0_MUTE_EN)
    141			ucontrol->value.integer.value[0] =
    142				TEGRA210_MUTE_MASK_EN;
    143		else
    144			ucontrol->value.integer.value[0] = 0;
    145	}
    146
    147	return 0;
    148}
    149
    150static int tegra210_mvc_get_master_mute(struct snd_kcontrol *kcontrol,
    151					struct snd_ctl_elem_value *ucontrol)
    152{
    153	u32 val = tegra210_mvc_get_ctrl_reg(kcontrol);
    154	u8 mute_mask = TEGRA210_GET_MUTE_VAL(val);
    155
    156	/*
    157	 * If per channel control is disabled, then return
    158	 * master mute/unmute setting based on CH0 bit.
    159	 *
    160	 * Else report settings based on state of all
    161	 * channels.
    162	 */
    163	if (!(val & TEGRA210_MVC_PER_CHAN_CTRL_EN)) {
    164		ucontrol->value.integer.value[0] =
    165			mute_mask & TEGRA210_MVC_CH0_MUTE_EN;
    166	} else {
    167		if (mute_mask == TEGRA210_MUTE_MASK_EN)
    168			ucontrol->value.integer.value[0] =
    169				TEGRA210_MVC_CH0_MUTE_EN;
    170		else
    171			ucontrol->value.integer.value[0] = 0;
    172	}
    173
    174	return 0;
    175}
    176
    177static int tegra210_mvc_volume_switch_timeout(struct snd_soc_component *cmpnt)
    178{
    179	struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
    180	u32 value;
    181	int err;
    182
    183	err = regmap_read_poll_timeout(mvc->regmap, TEGRA210_MVC_SWITCH,
    184			value, !(value & TEGRA210_MVC_VOLUME_SWITCH_MASK),
    185			10, 10000);
    186	if (err < 0)
    187		dev_err(cmpnt->dev,
    188			"Volume switch trigger is still active, err = %d\n",
    189			err);
    190
    191	return err;
    192}
    193
    194static int tegra210_mvc_update_mute(struct snd_kcontrol *kcontrol,
    195				    struct snd_ctl_elem_value *ucontrol,
    196				    bool per_chan_ctrl)
    197{
    198	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
    199	struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
    200	u32 mute_val = ucontrol->value.integer.value[0];
    201	u32 per_ch_ctrl_val;
    202	bool change = false;
    203	int err;
    204
    205	pm_runtime_get_sync(cmpnt->dev);
    206
    207	err = tegra210_mvc_volume_switch_timeout(cmpnt);
    208	if (err < 0)
    209		goto end;
    210
    211	if (per_chan_ctrl) {
    212		per_ch_ctrl_val = TEGRA210_MVC_PER_CHAN_CTRL_EN;
    213	} else {
    214		per_ch_ctrl_val = 0;
    215
    216		if (mute_val)
    217			mute_val = TEGRA210_MUTE_MASK_EN;
    218	}
    219
    220	regmap_update_bits_check(mvc->regmap, TEGRA210_MVC_CTRL,
    221				 TEGRA210_MVC_MUTE_MASK,
    222				 mute_val << TEGRA210_MVC_MUTE_SHIFT,
    223				 &change);
    224
    225	if (change) {
    226		regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
    227				   TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK,
    228				   per_ch_ctrl_val);
    229
    230		regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH,
    231				   TEGRA210_MVC_VOLUME_SWITCH_MASK,
    232				   TEGRA210_MVC_VOLUME_SWITCH_TRIGGER);
    233	}
    234
    235end:
    236	pm_runtime_put(cmpnt->dev);
    237
    238	if (err < 0)
    239		return err;
    240
    241	if (change)
    242		return 1;
    243
    244	return 0;
    245}
    246
    247static int tegra210_mvc_put_mute(struct snd_kcontrol *kcontrol,
    248				 struct snd_ctl_elem_value *ucontrol)
    249{
    250	return tegra210_mvc_update_mute(kcontrol, ucontrol, true);
    251}
    252
    253static int tegra210_mvc_put_master_mute(struct snd_kcontrol *kcontrol,
    254					struct snd_ctl_elem_value *ucontrol)
    255{
    256	return tegra210_mvc_update_mute(kcontrol, ucontrol, false);
    257}
    258
    259static int tegra210_mvc_get_vol(struct snd_kcontrol *kcontrol,
    260				struct snd_ctl_elem_value *ucontrol)
    261{
    262	struct soc_mixer_control *mc =
    263		(struct soc_mixer_control *)kcontrol->private_value;
    264	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
    265	struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
    266	u8 chan = TEGRA210_MVC_GET_CHAN(mc->reg, TEGRA210_MVC_TARGET_VOL);
    267	s32 val = mvc->volume[chan];
    268
    269	if (mvc->curve_type == CURVE_POLY) {
    270		val = ((val >> 16) * 100) >> 8;
    271	} else {
    272		val = (val * 100) >> 8;
    273		val += 12000;
    274	}
    275
    276	ucontrol->value.integer.value[0] = val;
    277
    278	return 0;
    279}
    280
    281static int tegra210_mvc_get_master_vol(struct snd_kcontrol *kcontrol,
    282				       struct snd_ctl_elem_value *ucontrol)
    283{
    284	return tegra210_mvc_get_vol(kcontrol, ucontrol);
    285}
    286
    287static int tegra210_mvc_update_vol(struct snd_kcontrol *kcontrol,
    288				   struct snd_ctl_elem_value *ucontrol,
    289				   bool per_ch_enable)
    290{
    291	struct soc_mixer_control *mc =
    292		(struct soc_mixer_control *)kcontrol->private_value;
    293	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
    294	struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
    295	u8 chan = TEGRA210_MVC_GET_CHAN(mc->reg, TEGRA210_MVC_TARGET_VOL);
    296	int old_volume = mvc->volume[chan];
    297	int err, i;
    298
    299	pm_runtime_get_sync(cmpnt->dev);
    300
    301	err = tegra210_mvc_volume_switch_timeout(cmpnt);
    302	if (err < 0)
    303		goto end;
    304
    305	tegra210_mvc_conv_vol(mvc, chan, ucontrol->value.integer.value[0]);
    306
    307	if (mvc->volume[chan] == old_volume) {
    308		err = 0;
    309		goto end;
    310	}
    311
    312	if (per_ch_enable) {
    313		regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
    314				   TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK,
    315				   TEGRA210_MVC_PER_CHAN_CTRL_EN);
    316	} else {
    317		regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
    318				   TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK, 0);
    319
    320		for (i = 1; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++)
    321			mvc->volume[i] = mvc->volume[chan];
    322	}
    323
    324	/* Configure init volume same as target volume */
    325	regmap_write(mvc->regmap,
    326		TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_INIT_VOL, chan),
    327		mvc->volume[chan]);
    328
    329	regmap_write(mvc->regmap, mc->reg, mvc->volume[chan]);
    330
    331	regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH,
    332			   TEGRA210_MVC_VOLUME_SWITCH_MASK,
    333			   TEGRA210_MVC_VOLUME_SWITCH_TRIGGER);
    334
    335	err = 1;
    336
    337end:
    338	pm_runtime_put(cmpnt->dev);
    339
    340	return err;
    341}
    342
    343static int tegra210_mvc_put_vol(struct snd_kcontrol *kcontrol,
    344				struct snd_ctl_elem_value *ucontrol)
    345{
    346	return tegra210_mvc_update_vol(kcontrol, ucontrol, true);
    347}
    348
    349static int tegra210_mvc_put_master_vol(struct snd_kcontrol *kcontrol,
    350				       struct snd_ctl_elem_value *ucontrol)
    351{
    352	return tegra210_mvc_update_vol(kcontrol, ucontrol, false);
    353}
    354
    355static void tegra210_mvc_reset_vol_settings(struct tegra210_mvc *mvc,
    356					    struct device *dev)
    357{
    358	int i;
    359
    360	/* Change volume to default init for new curve type */
    361	if (mvc->curve_type == CURVE_POLY) {
    362		for (i = 0; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++)
    363			mvc->volume[i] = TEGRA210_MVC_INIT_VOL_DEFAULT_POLY;
    364	} else {
    365		for (i = 0; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++)
    366			mvc->volume[i] = TEGRA210_MVC_INIT_VOL_DEFAULT_LINEAR;
    367	}
    368
    369	pm_runtime_get_sync(dev);
    370
    371	/* Program curve type */
    372	regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
    373			   TEGRA210_MVC_CURVE_TYPE_MASK,
    374			   mvc->curve_type <<
    375			   TEGRA210_MVC_CURVE_TYPE_SHIFT);
    376
    377	/* Init volume for all channels */
    378	for (i = 0; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++) {
    379		regmap_write(mvc->regmap,
    380			TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_INIT_VOL, i),
    381			mvc->volume[i]);
    382		regmap_write(mvc->regmap,
    383			TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_TARGET_VOL, i),
    384			mvc->volume[i]);
    385	}
    386
    387	/* Trigger volume switch */
    388	regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH,
    389			   TEGRA210_MVC_VOLUME_SWITCH_MASK,
    390			   TEGRA210_MVC_VOLUME_SWITCH_TRIGGER);
    391
    392	pm_runtime_put(dev);
    393}
    394
    395static int tegra210_mvc_get_curve_type(struct snd_kcontrol *kcontrol,
    396				       struct snd_ctl_elem_value *ucontrol)
    397{
    398	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
    399	struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
    400
    401	ucontrol->value.enumerated.item[0] = mvc->curve_type;
    402
    403	return 0;
    404}
    405
    406static int tegra210_mvc_put_curve_type(struct snd_kcontrol *kcontrol,
    407				       struct snd_ctl_elem_value *ucontrol)
    408{
    409	struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
    410	struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
    411	unsigned int value;
    412
    413	regmap_read(mvc->regmap, TEGRA210_MVC_ENABLE, &value);
    414	if (value & TEGRA210_MVC_EN) {
    415		dev_err(cmpnt->dev,
    416			"Curve type can't be set when MVC is running\n");
    417		return -EINVAL;
    418	}
    419
    420	if (mvc->curve_type == ucontrol->value.enumerated.item[0])
    421		return 0;
    422
    423	mvc->curve_type = ucontrol->value.enumerated.item[0];
    424
    425	tegra210_mvc_reset_vol_settings(mvc, cmpnt->dev);
    426
    427	return 1;
    428}
    429
    430static int tegra210_mvc_set_audio_cif(struct tegra210_mvc *mvc,
    431				      struct snd_pcm_hw_params *params,
    432				      unsigned int reg)
    433{
    434	unsigned int channels, audio_bits;
    435	struct tegra_cif_conf cif_conf;
    436
    437	memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));
    438
    439	channels = params_channels(params);
    440
    441	switch (params_format(params)) {
    442	case SNDRV_PCM_FORMAT_S16_LE:
    443		audio_bits = TEGRA_ACIF_BITS_16;
    444		break;
    445	case SNDRV_PCM_FORMAT_S32_LE:
    446		audio_bits = TEGRA_ACIF_BITS_32;
    447		break;
    448	default:
    449		return -EINVAL;
    450	}
    451
    452	cif_conf.audio_ch = channels;
    453	cif_conf.client_ch = channels;
    454	cif_conf.audio_bits = audio_bits;
    455	cif_conf.client_bits = audio_bits;
    456
    457	tegra_set_cif(mvc->regmap, reg, &cif_conf);
    458
    459	return 0;
    460}
    461
    462static int tegra210_mvc_hw_params(struct snd_pcm_substream *substream,
    463				  struct snd_pcm_hw_params *params,
    464				  struct snd_soc_dai *dai)
    465{
    466	struct device *dev = dai->dev;
    467	struct tegra210_mvc *mvc = snd_soc_dai_get_drvdata(dai);
    468	int err, val;
    469
    470	/*
    471	 * Soft Reset: Below performs module soft reset which clears
    472	 * all FSM logic, flushes flow control of FIFO and resets the
    473	 * state register. It also brings module back to disabled
    474	 * state (without flushing the data in the pipe).
    475	 */
    476	regmap_write(mvc->regmap, TEGRA210_MVC_SOFT_RESET, 1);
    477
    478	err = regmap_read_poll_timeout(mvc->regmap, TEGRA210_MVC_SOFT_RESET,
    479				       val, !val, 10, 10000);
    480	if (err < 0) {
    481		dev_err(dev, "SW reset failed, err = %d\n", err);
    482		return err;
    483	}
    484
    485	/* Set RX CIF */
    486	err = tegra210_mvc_set_audio_cif(mvc, params, TEGRA210_MVC_RX_CIF_CTRL);
    487	if (err) {
    488		dev_err(dev, "Can't set MVC RX CIF: %d\n", err);
    489		return err;
    490	}
    491
    492	/* Set TX CIF */
    493	err = tegra210_mvc_set_audio_cif(mvc, params, TEGRA210_MVC_TX_CIF_CTRL);
    494	if (err) {
    495		dev_err(dev, "Can't set MVC TX CIF: %d\n", err);
    496		return err;
    497	}
    498
    499	tegra210_mvc_write_ram(mvc->regmap);
    500
    501	/* Program poly_n1, poly_n2, duration */
    502	regmap_write(mvc->regmap, TEGRA210_MVC_POLY_N1, gain_params.poly_n1);
    503	regmap_write(mvc->regmap, TEGRA210_MVC_POLY_N2, gain_params.poly_n2);
    504	regmap_write(mvc->regmap, TEGRA210_MVC_DURATION, gain_params.duration);
    505
    506	/* Program duration_inv */
    507	regmap_write(mvc->regmap, TEGRA210_MVC_DURATION_INV,
    508		     gain_params.duration_inv);
    509
    510	return 0;
    511}
    512
    513static const struct snd_soc_dai_ops tegra210_mvc_dai_ops = {
    514	.hw_params	= tegra210_mvc_hw_params,
    515};
    516
    517static const char * const tegra210_mvc_curve_type_text[] = {
    518	"Poly",
    519	"Linear",
    520};
    521
    522static const struct soc_enum tegra210_mvc_curve_type_ctrl =
    523	SOC_ENUM_SINGLE_EXT(2, tegra210_mvc_curve_type_text);
    524
    525#define TEGRA210_MVC_VOL_CTRL(chan)					\
    526	SOC_SINGLE_EXT("Channel" #chan " Volume",			\
    527		       TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_TARGET_VOL, \
    528					       (chan - 1)),		\
    529		       0, 16000, 0, tegra210_mvc_get_vol,		\
    530		       tegra210_mvc_put_vol)
    531
    532static const struct snd_kcontrol_new tegra210_mvc_vol_ctrl[] = {
    533	/* Per channel volume control */
    534	TEGRA210_MVC_VOL_CTRL(1),
    535	TEGRA210_MVC_VOL_CTRL(2),
    536	TEGRA210_MVC_VOL_CTRL(3),
    537	TEGRA210_MVC_VOL_CTRL(4),
    538	TEGRA210_MVC_VOL_CTRL(5),
    539	TEGRA210_MVC_VOL_CTRL(6),
    540	TEGRA210_MVC_VOL_CTRL(7),
    541	TEGRA210_MVC_VOL_CTRL(8),
    542
    543	/* Per channel mute */
    544	SOC_SINGLE_EXT("Per Chan Mute Mask",
    545		       TEGRA210_MVC_CTRL, 0, TEGRA210_MUTE_MASK_EN, 0,
    546		       tegra210_mvc_get_mute, tegra210_mvc_put_mute),
    547
    548	/* Master volume */
    549	SOC_SINGLE_EXT("Volume", TEGRA210_MVC_TARGET_VOL, 0, 16000, 0,
    550		       tegra210_mvc_get_master_vol,
    551		       tegra210_mvc_put_master_vol),
    552
    553	/* Master mute */
    554	SOC_SINGLE_EXT("Mute", TEGRA210_MVC_CTRL, 0, 1, 0,
    555		       tegra210_mvc_get_master_mute,
    556		       tegra210_mvc_put_master_mute),
    557
    558	SOC_ENUM_EXT("Curve Type", tegra210_mvc_curve_type_ctrl,
    559		     tegra210_mvc_get_curve_type, tegra210_mvc_put_curve_type),
    560};
    561
    562static struct snd_soc_dai_driver tegra210_mvc_dais[] = {
    563	/* Input */
    564	{
    565		.name = "MVC-RX-CIF",
    566		.playback = {
    567			.stream_name = "RX-CIF-Playback",
    568			.channels_min = 1,
    569			.channels_max = 8,
    570			.rates = SNDRV_PCM_RATE_8000_192000,
    571			.formats = SNDRV_PCM_FMTBIT_S8 |
    572				SNDRV_PCM_FMTBIT_S16_LE |
    573				SNDRV_PCM_FMTBIT_S32_LE,
    574		},
    575		.capture = {
    576			.stream_name = "RX-CIF-Capture",
    577			.channels_min = 1,
    578			.channels_max = 8,
    579			.rates = SNDRV_PCM_RATE_8000_192000,
    580			.formats = SNDRV_PCM_FMTBIT_S8 |
    581				SNDRV_PCM_FMTBIT_S16_LE |
    582				SNDRV_PCM_FMTBIT_S32_LE,
    583		},
    584	},
    585
    586	/* Output */
    587	{
    588		.name = "MVC-TX-CIF",
    589		.playback = {
    590			.stream_name = "TX-CIF-Playback",
    591			.channels_min = 1,
    592			.channels_max = 8,
    593			.rates = SNDRV_PCM_RATE_8000_192000,
    594			.formats = SNDRV_PCM_FMTBIT_S8 |
    595				SNDRV_PCM_FMTBIT_S16_LE |
    596				SNDRV_PCM_FMTBIT_S32_LE,
    597		},
    598		.capture = {
    599			.stream_name = "TX-CIF-Capture",
    600			.channels_min = 1,
    601			.channels_max = 8,
    602			.rates = SNDRV_PCM_RATE_8000_192000,
    603			.formats = SNDRV_PCM_FMTBIT_S8 |
    604				SNDRV_PCM_FMTBIT_S16_LE |
    605				SNDRV_PCM_FMTBIT_S32_LE,
    606		},
    607		.ops = &tegra210_mvc_dai_ops,
    608	}
    609};
    610
    611static const struct snd_soc_dapm_widget tegra210_mvc_widgets[] = {
    612	SND_SOC_DAPM_AIF_IN("RX", NULL, 0, SND_SOC_NOPM, 0, 0),
    613	SND_SOC_DAPM_AIF_OUT("TX", NULL, 0, TEGRA210_MVC_ENABLE,
    614			     TEGRA210_MVC_EN_SHIFT, 0),
    615};
    616
    617#define MVC_ROUTES(sname)					\
    618	{ "RX XBAR-" sname,	NULL,	"XBAR-TX" },		\
    619	{ "RX-CIF-" sname,	NULL,	"RX XBAR-" sname },	\
    620	{ "RX",			NULL,	"RX-CIF-" sname },	\
    621	{ "TX-CIF-" sname,	NULL,	"TX" },			\
    622	{ "TX XBAR-" sname,	NULL,	"TX-CIF-" sname },	\
    623	{ "XBAR-RX",            NULL,   "TX XBAR-" sname }
    624
    625static const struct snd_soc_dapm_route tegra210_mvc_routes[] = {
    626	{ "TX", NULL, "RX" },
    627	MVC_ROUTES("Playback"),
    628	MVC_ROUTES("Capture"),
    629};
    630
    631static const struct snd_soc_component_driver tegra210_mvc_cmpnt = {
    632	.dapm_widgets		= tegra210_mvc_widgets,
    633	.num_dapm_widgets	= ARRAY_SIZE(tegra210_mvc_widgets),
    634	.dapm_routes		= tegra210_mvc_routes,
    635	.num_dapm_routes	= ARRAY_SIZE(tegra210_mvc_routes),
    636	.controls		= tegra210_mvc_vol_ctrl,
    637	.num_controls		= ARRAY_SIZE(tegra210_mvc_vol_ctrl),
    638};
    639
    640static bool tegra210_mvc_rd_reg(struct device *dev, unsigned int reg)
    641{
    642	switch (reg) {
    643	case TEGRA210_MVC_RX_STATUS ... TEGRA210_MVC_CONFIG_ERR_TYPE:
    644		return true;
    645	default:
    646		return false;
    647	};
    648}
    649
    650static bool tegra210_mvc_wr_reg(struct device *dev, unsigned int reg)
    651{
    652	switch (reg) {
    653	case TEGRA210_MVC_RX_INT_MASK ... TEGRA210_MVC_RX_CIF_CTRL:
    654	case TEGRA210_MVC_TX_INT_MASK ... TEGRA210_MVC_TX_CIF_CTRL:
    655	case TEGRA210_MVC_ENABLE ... TEGRA210_MVC_CG:
    656	case TEGRA210_MVC_CTRL ... TEGRA210_MVC_CFG_RAM_DATA:
    657		return true;
    658	default:
    659		return false;
    660	}
    661}
    662
    663static bool tegra210_mvc_volatile_reg(struct device *dev, unsigned int reg)
    664{
    665	switch (reg) {
    666	case TEGRA210_MVC_RX_STATUS:
    667	case TEGRA210_MVC_RX_INT_STATUS:
    668	case TEGRA210_MVC_RX_INT_SET:
    669
    670	case TEGRA210_MVC_TX_STATUS:
    671	case TEGRA210_MVC_TX_INT_STATUS:
    672	case TEGRA210_MVC_TX_INT_SET:
    673
    674	case TEGRA210_MVC_SOFT_RESET:
    675	case TEGRA210_MVC_STATUS:
    676	case TEGRA210_MVC_INT_STATUS:
    677	case TEGRA210_MVC_SWITCH:
    678	case TEGRA210_MVC_CFG_RAM_CTRL:
    679	case TEGRA210_MVC_CFG_RAM_DATA:
    680	case TEGRA210_MVC_PEAK_VALUE:
    681	case TEGRA210_MVC_CTRL:
    682		return true;
    683	default:
    684		return false;
    685	}
    686}
    687
    688static const struct regmap_config tegra210_mvc_regmap_config = {
    689	.reg_bits		= 32,
    690	.reg_stride		= 4,
    691	.val_bits		= 32,
    692	.max_register		= TEGRA210_MVC_CONFIG_ERR_TYPE,
    693	.writeable_reg		= tegra210_mvc_wr_reg,
    694	.readable_reg		= tegra210_mvc_rd_reg,
    695	.volatile_reg		= tegra210_mvc_volatile_reg,
    696	.reg_defaults		= tegra210_mvc_reg_defaults,
    697	.num_reg_defaults	= ARRAY_SIZE(tegra210_mvc_reg_defaults),
    698	.cache_type		= REGCACHE_FLAT,
    699};
    700
    701static const struct of_device_id tegra210_mvc_of_match[] = {
    702	{ .compatible = "nvidia,tegra210-mvc" },
    703	{},
    704};
    705MODULE_DEVICE_TABLE(of, tegra210_mvc_of_match);
    706
    707static int tegra210_mvc_platform_probe(struct platform_device *pdev)
    708{
    709	struct device *dev = &pdev->dev;
    710	struct tegra210_mvc *mvc;
    711	void __iomem *regs;
    712	int err;
    713
    714	mvc = devm_kzalloc(dev, sizeof(*mvc), GFP_KERNEL);
    715	if (!mvc)
    716		return -ENOMEM;
    717
    718	dev_set_drvdata(dev, mvc);
    719
    720	mvc->curve_type = CURVE_LINEAR;
    721	mvc->ctrl_value = TEGRA210_MVC_CTRL_DEFAULT;
    722
    723	regs = devm_platform_ioremap_resource(pdev, 0);
    724	if (IS_ERR(regs))
    725		return PTR_ERR(regs);
    726
    727	mvc->regmap = devm_regmap_init_mmio(dev, regs,
    728					    &tegra210_mvc_regmap_config);
    729	if (IS_ERR(mvc->regmap)) {
    730		dev_err(dev, "regmap init failed\n");
    731		return PTR_ERR(mvc->regmap);
    732	}
    733
    734	regcache_cache_only(mvc->regmap, true);
    735
    736	err = devm_snd_soc_register_component(dev, &tegra210_mvc_cmpnt,
    737					      tegra210_mvc_dais,
    738					      ARRAY_SIZE(tegra210_mvc_dais));
    739	if (err) {
    740		dev_err(dev, "can't register MVC component, err: %d\n", err);
    741		return err;
    742	}
    743
    744	pm_runtime_enable(dev);
    745
    746	tegra210_mvc_reset_vol_settings(mvc, &pdev->dev);
    747
    748	return 0;
    749}
    750
    751static int tegra210_mvc_platform_remove(struct platform_device *pdev)
    752{
    753	pm_runtime_disable(&pdev->dev);
    754
    755	return 0;
    756}
    757
    758static const struct dev_pm_ops tegra210_mvc_pm_ops = {
    759	SET_RUNTIME_PM_OPS(tegra210_mvc_runtime_suspend,
    760			   tegra210_mvc_runtime_resume, NULL)
    761	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
    762				pm_runtime_force_resume)
    763};
    764
    765static struct platform_driver tegra210_mvc_driver = {
    766	.driver = {
    767		.name = "tegra210-mvc",
    768		.of_match_table = tegra210_mvc_of_match,
    769		.pm = &tegra210_mvc_pm_ops,
    770	},
    771	.probe = tegra210_mvc_platform_probe,
    772	.remove = tegra210_mvc_platform_remove,
    773};
    774module_platform_driver(tegra210_mvc_driver)
    775
    776MODULE_AUTHOR("Arun Shamanna Lakshmi <aruns@nvidia.com>");
    777MODULE_DESCRIPTION("Tegra210 MVC ASoC driver");
    778MODULE_LICENSE("GPL v2");