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