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; 139e539891fSSameer Pujar u8 mute_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 151e539891fSSameer Pujar mute_mask = ucontrol->value.integer.value[0]; 152e539891fSSameer Pujar 153e539891fSSameer Pujar err = regmap_update_bits(mvc->regmap, mc->reg, 154e539891fSSameer Pujar TEGRA210_MVC_MUTE_MASK, 155e539891fSSameer Pujar mute_mask << TEGRA210_MVC_MUTE_SHIFT); 156e539891fSSameer Pujar if (err < 0) 157e539891fSSameer Pujar goto end; 158e539891fSSameer Pujar 159e539891fSSameer Pujar return 1; 160e539891fSSameer Pujar 161e539891fSSameer Pujar end: 162e539891fSSameer Pujar pm_runtime_put(cmpnt->dev); 163e539891fSSameer Pujar return err; 164e539891fSSameer Pujar } 165e539891fSSameer Pujar 166e539891fSSameer Pujar static int tegra210_mvc_get_vol(struct snd_kcontrol *kcontrol, 167e539891fSSameer Pujar struct snd_ctl_elem_value *ucontrol) 168e539891fSSameer Pujar { 169e539891fSSameer Pujar struct soc_mixer_control *mc = 170e539891fSSameer Pujar (struct soc_mixer_control *)kcontrol->private_value; 171e539891fSSameer Pujar struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); 172e539891fSSameer Pujar struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); 173e539891fSSameer Pujar u8 chan = (mc->reg - TEGRA210_MVC_TARGET_VOL) / REG_SIZE; 174e539891fSSameer Pujar s32 val = mvc->volume[chan]; 175e539891fSSameer Pujar 176e539891fSSameer Pujar if (mvc->curve_type == CURVE_POLY) { 177e539891fSSameer Pujar val = ((val >> 16) * 100) >> 8; 178e539891fSSameer Pujar } else { 179e539891fSSameer Pujar val = (val * 100) >> 8; 180e539891fSSameer Pujar val += 12000; 181e539891fSSameer Pujar } 182e539891fSSameer Pujar 183e539891fSSameer Pujar ucontrol->value.integer.value[0] = val; 184e539891fSSameer Pujar 185e539891fSSameer Pujar return 0; 186e539891fSSameer Pujar } 187e539891fSSameer Pujar 188e539891fSSameer Pujar static int tegra210_mvc_put_vol(struct snd_kcontrol *kcontrol, 189e539891fSSameer Pujar struct snd_ctl_elem_value *ucontrol) 190e539891fSSameer Pujar { 191e539891fSSameer Pujar struct soc_mixer_control *mc = 192e539891fSSameer Pujar (struct soc_mixer_control *)kcontrol->private_value; 193e539891fSSameer Pujar struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); 194e539891fSSameer Pujar struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); 195e539891fSSameer Pujar unsigned int reg = mc->reg; 196e539891fSSameer Pujar unsigned int value; 197e539891fSSameer Pujar u8 chan; 198e539891fSSameer Pujar int err; 199e539891fSSameer Pujar 200e539891fSSameer Pujar pm_runtime_get_sync(cmpnt->dev); 201e539891fSSameer Pujar 202e539891fSSameer Pujar /* Check if VOLUME_SWITCH is triggered */ 203e539891fSSameer Pujar err = regmap_read_poll_timeout(mvc->regmap, TEGRA210_MVC_SWITCH, 204e539891fSSameer Pujar value, !(value & TEGRA210_MVC_VOLUME_SWITCH_MASK), 205e539891fSSameer Pujar 10, 10000); 206e539891fSSameer Pujar if (err < 0) 207e539891fSSameer Pujar goto end; 208e539891fSSameer Pujar 209e539891fSSameer Pujar chan = (reg - TEGRA210_MVC_TARGET_VOL) / REG_SIZE; 210e539891fSSameer Pujar 211e539891fSSameer Pujar tegra210_mvc_conv_vol(mvc, chan, 212e539891fSSameer Pujar ucontrol->value.integer.value[0]); 213e539891fSSameer Pujar 214e539891fSSameer Pujar /* Configure init volume same as target volume */ 215e539891fSSameer Pujar regmap_write(mvc->regmap, 216e539891fSSameer Pujar TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_INIT_VOL, chan), 217e539891fSSameer Pujar mvc->volume[chan]); 218e539891fSSameer Pujar 219e539891fSSameer Pujar regmap_write(mvc->regmap, reg, mvc->volume[chan]); 220e539891fSSameer Pujar 221e539891fSSameer Pujar regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH, 222e539891fSSameer Pujar TEGRA210_MVC_VOLUME_SWITCH_MASK, 223e539891fSSameer Pujar TEGRA210_MVC_VOLUME_SWITCH_TRIGGER); 224e539891fSSameer Pujar 225e539891fSSameer Pujar return 1; 226e539891fSSameer Pujar 227e539891fSSameer Pujar end: 228e539891fSSameer Pujar pm_runtime_put(cmpnt->dev); 229e539891fSSameer Pujar return err; 230e539891fSSameer Pujar } 231e539891fSSameer Pujar 232e539891fSSameer Pujar static void tegra210_mvc_reset_vol_settings(struct tegra210_mvc *mvc, 233e539891fSSameer Pujar struct device *dev) 234e539891fSSameer Pujar { 235e539891fSSameer Pujar int i; 236e539891fSSameer Pujar 237e539891fSSameer Pujar /* Change volume to default init for new curve type */ 238e539891fSSameer Pujar if (mvc->curve_type == CURVE_POLY) { 239e539891fSSameer Pujar for (i = 0; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++) 240e539891fSSameer Pujar mvc->volume[i] = TEGRA210_MVC_INIT_VOL_DEFAULT_POLY; 241e539891fSSameer Pujar } else { 242e539891fSSameer Pujar for (i = 0; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++) 243e539891fSSameer Pujar mvc->volume[i] = TEGRA210_MVC_INIT_VOL_DEFAULT_LINEAR; 244e539891fSSameer Pujar } 245e539891fSSameer Pujar 246e539891fSSameer Pujar pm_runtime_get_sync(dev); 247e539891fSSameer Pujar 248e539891fSSameer Pujar /* Program curve type */ 249e539891fSSameer Pujar regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL, 250e539891fSSameer Pujar TEGRA210_MVC_CURVE_TYPE_MASK, 251e539891fSSameer Pujar mvc->curve_type << 252e539891fSSameer Pujar TEGRA210_MVC_CURVE_TYPE_SHIFT); 253e539891fSSameer Pujar 254e539891fSSameer Pujar /* Init volume for all channels */ 255e539891fSSameer Pujar for (i = 0; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++) { 256e539891fSSameer Pujar regmap_write(mvc->regmap, 257e539891fSSameer Pujar TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_INIT_VOL, i), 258e539891fSSameer Pujar mvc->volume[i]); 259e539891fSSameer Pujar regmap_write(mvc->regmap, 260e539891fSSameer Pujar TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_TARGET_VOL, i), 261e539891fSSameer Pujar mvc->volume[i]); 262e539891fSSameer Pujar } 263e539891fSSameer Pujar 264e539891fSSameer Pujar /* Trigger volume switch */ 265e539891fSSameer Pujar regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH, 266e539891fSSameer Pujar TEGRA210_MVC_VOLUME_SWITCH_MASK, 267e539891fSSameer Pujar TEGRA210_MVC_VOLUME_SWITCH_TRIGGER); 268e539891fSSameer Pujar 269e539891fSSameer Pujar pm_runtime_put(dev); 270e539891fSSameer Pujar } 271e539891fSSameer Pujar 272e539891fSSameer Pujar static int tegra210_mvc_get_curve_type(struct snd_kcontrol *kcontrol, 273e539891fSSameer Pujar struct snd_ctl_elem_value *ucontrol) 274e539891fSSameer Pujar { 275e539891fSSameer Pujar struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); 276e539891fSSameer Pujar struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); 277e539891fSSameer Pujar 278e539891fSSameer Pujar ucontrol->value.integer.value[0] = mvc->curve_type; 279e539891fSSameer Pujar 280e539891fSSameer Pujar return 0; 281e539891fSSameer Pujar } 282e539891fSSameer Pujar 283e539891fSSameer Pujar static int tegra210_mvc_put_curve_type(struct snd_kcontrol *kcontrol, 284e539891fSSameer Pujar struct snd_ctl_elem_value *ucontrol) 285e539891fSSameer Pujar { 286e539891fSSameer Pujar struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); 287e539891fSSameer Pujar struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); 288e539891fSSameer Pujar int value; 289e539891fSSameer Pujar 290e539891fSSameer Pujar regmap_read(mvc->regmap, TEGRA210_MVC_ENABLE, &value); 291e539891fSSameer Pujar if (value & TEGRA210_MVC_EN) { 292e539891fSSameer Pujar dev_err(cmpnt->dev, 293e539891fSSameer Pujar "Curve type can't be set when MVC is running\n"); 294e539891fSSameer Pujar return -EINVAL; 295e539891fSSameer Pujar } 296e539891fSSameer Pujar 297e539891fSSameer Pujar if (mvc->curve_type == ucontrol->value.integer.value[0]) 298e539891fSSameer Pujar return 0; 299e539891fSSameer Pujar 300e539891fSSameer Pujar mvc->curve_type = ucontrol->value.integer.value[0]; 301e539891fSSameer Pujar 302e539891fSSameer Pujar tegra210_mvc_reset_vol_settings(mvc, cmpnt->dev); 303e539891fSSameer Pujar 304e539891fSSameer Pujar return 1; 305e539891fSSameer Pujar } 306e539891fSSameer Pujar 307e539891fSSameer Pujar static int tegra210_mvc_set_audio_cif(struct tegra210_mvc *mvc, 308e539891fSSameer Pujar struct snd_pcm_hw_params *params, 309e539891fSSameer Pujar unsigned int reg) 310e539891fSSameer Pujar { 311e539891fSSameer Pujar unsigned int channels, audio_bits; 312e539891fSSameer Pujar struct tegra_cif_conf cif_conf; 313e539891fSSameer Pujar 314e539891fSSameer Pujar memset(&cif_conf, 0, sizeof(struct tegra_cif_conf)); 315e539891fSSameer Pujar 316e539891fSSameer Pujar channels = params_channels(params); 317e539891fSSameer Pujar 318e539891fSSameer Pujar switch (params_format(params)) { 319e539891fSSameer Pujar case SNDRV_PCM_FORMAT_S16_LE: 320e539891fSSameer Pujar audio_bits = TEGRA_ACIF_BITS_16; 321e539891fSSameer Pujar break; 322e539891fSSameer Pujar case SNDRV_PCM_FORMAT_S32_LE: 323e539891fSSameer Pujar audio_bits = TEGRA_ACIF_BITS_32; 324e539891fSSameer Pujar break; 325e539891fSSameer Pujar default: 326e539891fSSameer Pujar return -EINVAL; 327e539891fSSameer Pujar } 328e539891fSSameer Pujar 329e539891fSSameer Pujar cif_conf.audio_ch = channels; 330e539891fSSameer Pujar cif_conf.client_ch = channels; 331e539891fSSameer Pujar cif_conf.audio_bits = audio_bits; 332e539891fSSameer Pujar cif_conf.client_bits = audio_bits; 333e539891fSSameer Pujar 334e539891fSSameer Pujar tegra_set_cif(mvc->regmap, reg, &cif_conf); 335e539891fSSameer Pujar 336e539891fSSameer Pujar return 0; 337e539891fSSameer Pujar } 338e539891fSSameer Pujar 339e539891fSSameer Pujar static int tegra210_mvc_hw_params(struct snd_pcm_substream *substream, 340e539891fSSameer Pujar struct snd_pcm_hw_params *params, 341e539891fSSameer Pujar struct snd_soc_dai *dai) 342e539891fSSameer Pujar { 343e539891fSSameer Pujar struct device *dev = dai->dev; 344e539891fSSameer Pujar struct tegra210_mvc *mvc = snd_soc_dai_get_drvdata(dai); 345e539891fSSameer Pujar int err, val; 346e539891fSSameer Pujar 347e539891fSSameer Pujar /* 348e539891fSSameer Pujar * Soft Reset: Below performs module soft reset which clears 349e539891fSSameer Pujar * all FSM logic, flushes flow control of FIFO and resets the 350e539891fSSameer Pujar * state register. It also brings module back to disabled 351e539891fSSameer Pujar * state (without flushing the data in the pipe). 352e539891fSSameer Pujar */ 353e539891fSSameer Pujar regmap_write(mvc->regmap, TEGRA210_MVC_SOFT_RESET, 1); 354e539891fSSameer Pujar 355e539891fSSameer Pujar err = regmap_read_poll_timeout(mvc->regmap, TEGRA210_MVC_SOFT_RESET, 356e539891fSSameer Pujar val, !val, 10, 10000); 357e539891fSSameer Pujar if (err < 0) { 358e539891fSSameer Pujar dev_err(dev, "SW reset failed, err = %d\n", err); 359e539891fSSameer Pujar return err; 360e539891fSSameer Pujar } 361e539891fSSameer Pujar 362e539891fSSameer Pujar /* Set RX CIF */ 363e539891fSSameer Pujar err = tegra210_mvc_set_audio_cif(mvc, params, TEGRA210_MVC_RX_CIF_CTRL); 364e539891fSSameer Pujar if (err) { 365e539891fSSameer Pujar dev_err(dev, "Can't set MVC RX CIF: %d\n", err); 366e539891fSSameer Pujar return err; 367e539891fSSameer Pujar } 368e539891fSSameer Pujar 369e539891fSSameer Pujar /* Set TX CIF */ 370e539891fSSameer Pujar err = tegra210_mvc_set_audio_cif(mvc, params, TEGRA210_MVC_TX_CIF_CTRL); 371e539891fSSameer Pujar if (err) { 372e539891fSSameer Pujar dev_err(dev, "Can't set MVC TX CIF: %d\n", err); 373e539891fSSameer Pujar return err; 374e539891fSSameer Pujar } 375e539891fSSameer Pujar 376e539891fSSameer Pujar tegra210_mvc_write_ram(mvc->regmap); 377e539891fSSameer Pujar 378e539891fSSameer Pujar /* Program poly_n1, poly_n2, duration */ 379e539891fSSameer Pujar regmap_write(mvc->regmap, TEGRA210_MVC_POLY_N1, gain_params.poly_n1); 380e539891fSSameer Pujar regmap_write(mvc->regmap, TEGRA210_MVC_POLY_N2, gain_params.poly_n2); 381e539891fSSameer Pujar regmap_write(mvc->regmap, TEGRA210_MVC_DURATION, gain_params.duration); 382e539891fSSameer Pujar 383e539891fSSameer Pujar /* Program duration_inv */ 384e539891fSSameer Pujar regmap_write(mvc->regmap, TEGRA210_MVC_DURATION_INV, 385e539891fSSameer Pujar gain_params.duration_inv); 386e539891fSSameer Pujar 387e539891fSSameer Pujar return 0; 388e539891fSSameer Pujar } 389e539891fSSameer Pujar 390*313fab48SRikard Falkeborn static const struct snd_soc_dai_ops tegra210_mvc_dai_ops = { 391e539891fSSameer Pujar .hw_params = tegra210_mvc_hw_params, 392e539891fSSameer Pujar }; 393e539891fSSameer Pujar 394e539891fSSameer Pujar static const char * const tegra210_mvc_curve_type_text[] = { 395e539891fSSameer Pujar "Poly", 396e539891fSSameer Pujar "Linear", 397e539891fSSameer Pujar }; 398e539891fSSameer Pujar 399e539891fSSameer Pujar static const struct soc_enum tegra210_mvc_curve_type_ctrl = 400e539891fSSameer Pujar SOC_ENUM_SINGLE_EXT(2, tegra210_mvc_curve_type_text); 401e539891fSSameer Pujar 402e539891fSSameer Pujar #define TEGRA210_MVC_VOL_CTRL(chan) \ 403e539891fSSameer Pujar SOC_SINGLE_EXT("Channel" #chan " Volume", \ 404e539891fSSameer Pujar TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_TARGET_VOL, \ 405e539891fSSameer Pujar (chan - 1)), \ 406e539891fSSameer Pujar 0, 16000, 0, tegra210_mvc_get_vol, \ 407e539891fSSameer Pujar tegra210_mvc_put_vol) 408e539891fSSameer Pujar 409e539891fSSameer Pujar static const struct snd_kcontrol_new tegra210_mvc_vol_ctrl[] = { 410e539891fSSameer Pujar /* Per channel volume control */ 411e539891fSSameer Pujar TEGRA210_MVC_VOL_CTRL(1), 412e539891fSSameer Pujar TEGRA210_MVC_VOL_CTRL(2), 413e539891fSSameer Pujar TEGRA210_MVC_VOL_CTRL(3), 414e539891fSSameer Pujar TEGRA210_MVC_VOL_CTRL(4), 415e539891fSSameer Pujar TEGRA210_MVC_VOL_CTRL(5), 416e539891fSSameer Pujar TEGRA210_MVC_VOL_CTRL(6), 417e539891fSSameer Pujar TEGRA210_MVC_VOL_CTRL(7), 418e539891fSSameer Pujar TEGRA210_MVC_VOL_CTRL(8), 419e539891fSSameer Pujar 420e539891fSSameer Pujar /* Per channel mute */ 421e539891fSSameer Pujar SOC_SINGLE_EXT("Per Chan Mute Mask", 422e539891fSSameer Pujar TEGRA210_MVC_CTRL, 0, TEGRA210_MUTE_MASK_EN, 0, 423e539891fSSameer Pujar tegra210_mvc_get_mute, tegra210_mvc_put_mute), 424e539891fSSameer Pujar 425e539891fSSameer Pujar SOC_ENUM_EXT("Curve Type", tegra210_mvc_curve_type_ctrl, 426e539891fSSameer Pujar tegra210_mvc_get_curve_type, tegra210_mvc_put_curve_type), 427e539891fSSameer Pujar }; 428e539891fSSameer Pujar 429e539891fSSameer Pujar static struct snd_soc_dai_driver tegra210_mvc_dais[] = { 430e539891fSSameer Pujar /* Input */ 431e539891fSSameer Pujar { 432e539891fSSameer Pujar .name = "MVC-RX-CIF", 433e539891fSSameer Pujar .playback = { 434e539891fSSameer Pujar .stream_name = "RX-CIF-Playback", 435e539891fSSameer Pujar .channels_min = 1, 436e539891fSSameer Pujar .channels_max = 8, 437e539891fSSameer Pujar .rates = SNDRV_PCM_RATE_8000_192000, 438e539891fSSameer Pujar .formats = SNDRV_PCM_FMTBIT_S8 | 439e539891fSSameer Pujar SNDRV_PCM_FMTBIT_S16_LE | 440e539891fSSameer Pujar SNDRV_PCM_FMTBIT_S32_LE, 441e539891fSSameer Pujar }, 442e539891fSSameer Pujar .capture = { 443e539891fSSameer Pujar .stream_name = "RX-CIF-Capture", 444e539891fSSameer Pujar .channels_min = 1, 445e539891fSSameer Pujar .channels_max = 8, 446e539891fSSameer Pujar .rates = SNDRV_PCM_RATE_8000_192000, 447e539891fSSameer Pujar .formats = SNDRV_PCM_FMTBIT_S8 | 448e539891fSSameer Pujar SNDRV_PCM_FMTBIT_S16_LE | 449e539891fSSameer Pujar SNDRV_PCM_FMTBIT_S32_LE, 450e539891fSSameer Pujar }, 451e539891fSSameer Pujar }, 452e539891fSSameer Pujar 453e539891fSSameer Pujar /* Output */ 454e539891fSSameer Pujar { 455e539891fSSameer Pujar .name = "MVC-TX-CIF", 456e539891fSSameer Pujar .playback = { 457e539891fSSameer Pujar .stream_name = "TX-CIF-Playback", 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 .capture = { 466e539891fSSameer Pujar .stream_name = "TX-CIF-Capture", 467e539891fSSameer Pujar .channels_min = 1, 468e539891fSSameer Pujar .channels_max = 8, 469e539891fSSameer Pujar .rates = SNDRV_PCM_RATE_8000_192000, 470e539891fSSameer Pujar .formats = SNDRV_PCM_FMTBIT_S8 | 471e539891fSSameer Pujar SNDRV_PCM_FMTBIT_S16_LE | 472e539891fSSameer Pujar SNDRV_PCM_FMTBIT_S32_LE, 473e539891fSSameer Pujar }, 474e539891fSSameer Pujar .ops = &tegra210_mvc_dai_ops, 475e539891fSSameer Pujar } 476e539891fSSameer Pujar }; 477e539891fSSameer Pujar 478e539891fSSameer Pujar static const struct snd_soc_dapm_widget tegra210_mvc_widgets[] = { 479e539891fSSameer Pujar SND_SOC_DAPM_AIF_IN("RX", NULL, 0, SND_SOC_NOPM, 0, 0), 480e539891fSSameer Pujar SND_SOC_DAPM_AIF_OUT("TX", NULL, 0, TEGRA210_MVC_ENABLE, 481e539891fSSameer Pujar TEGRA210_MVC_EN_SHIFT, 0), 482e539891fSSameer Pujar }; 483e539891fSSameer Pujar 484e539891fSSameer Pujar #define MVC_ROUTES(sname) \ 485e539891fSSameer Pujar { "RX XBAR-" sname, NULL, "XBAR-TX" }, \ 486e539891fSSameer Pujar { "RX-CIF-" sname, NULL, "RX XBAR-" sname }, \ 487e539891fSSameer Pujar { "RX", NULL, "RX-CIF-" sname }, \ 488e539891fSSameer Pujar { "TX-CIF-" sname, NULL, "TX" }, \ 489e539891fSSameer Pujar { "TX XBAR-" sname, NULL, "TX-CIF-" sname }, \ 490e539891fSSameer Pujar { "XBAR-RX", NULL, "TX XBAR-" sname } 491e539891fSSameer Pujar 492e539891fSSameer Pujar static const struct snd_soc_dapm_route tegra210_mvc_routes[] = { 493e539891fSSameer Pujar { "TX", NULL, "RX" }, 494e539891fSSameer Pujar MVC_ROUTES("Playback"), 495e539891fSSameer Pujar MVC_ROUTES("Capture"), 496e539891fSSameer Pujar }; 497e539891fSSameer Pujar 498e539891fSSameer Pujar static const struct snd_soc_component_driver tegra210_mvc_cmpnt = { 499e539891fSSameer Pujar .dapm_widgets = tegra210_mvc_widgets, 500e539891fSSameer Pujar .num_dapm_widgets = ARRAY_SIZE(tegra210_mvc_widgets), 501e539891fSSameer Pujar .dapm_routes = tegra210_mvc_routes, 502e539891fSSameer Pujar .num_dapm_routes = ARRAY_SIZE(tegra210_mvc_routes), 503e539891fSSameer Pujar .controls = tegra210_mvc_vol_ctrl, 504e539891fSSameer Pujar .num_controls = ARRAY_SIZE(tegra210_mvc_vol_ctrl), 505e539891fSSameer Pujar }; 506e539891fSSameer Pujar 507e539891fSSameer Pujar static bool tegra210_mvc_rd_reg(struct device *dev, unsigned int reg) 508e539891fSSameer Pujar { 509e539891fSSameer Pujar switch (reg) { 510e539891fSSameer Pujar case TEGRA210_MVC_RX_STATUS ... TEGRA210_MVC_CONFIG_ERR_TYPE: 511e539891fSSameer Pujar return true; 512e539891fSSameer Pujar default: 513e539891fSSameer Pujar return false; 514e539891fSSameer Pujar }; 515e539891fSSameer Pujar } 516e539891fSSameer Pujar 517e539891fSSameer Pujar static bool tegra210_mvc_wr_reg(struct device *dev, unsigned int reg) 518e539891fSSameer Pujar { 519e539891fSSameer Pujar switch (reg) { 520e539891fSSameer Pujar case TEGRA210_MVC_RX_INT_MASK ... TEGRA210_MVC_RX_CIF_CTRL: 521e539891fSSameer Pujar case TEGRA210_MVC_TX_INT_MASK ... TEGRA210_MVC_TX_CIF_CTRL: 522e539891fSSameer Pujar case TEGRA210_MVC_ENABLE ... TEGRA210_MVC_CG: 523e539891fSSameer Pujar case TEGRA210_MVC_CTRL ... TEGRA210_MVC_CFG_RAM_DATA: 524e539891fSSameer Pujar return true; 525e539891fSSameer Pujar default: 526e539891fSSameer Pujar return false; 527e539891fSSameer Pujar } 528e539891fSSameer Pujar } 529e539891fSSameer Pujar 530e539891fSSameer Pujar static bool tegra210_mvc_volatile_reg(struct device *dev, unsigned int reg) 531e539891fSSameer Pujar { 532e539891fSSameer Pujar switch (reg) { 533e539891fSSameer Pujar case TEGRA210_MVC_RX_STATUS: 534e539891fSSameer Pujar case TEGRA210_MVC_RX_INT_STATUS: 535e539891fSSameer Pujar case TEGRA210_MVC_RX_INT_SET: 536e539891fSSameer Pujar 537e539891fSSameer Pujar case TEGRA210_MVC_TX_STATUS: 538e539891fSSameer Pujar case TEGRA210_MVC_TX_INT_STATUS: 539e539891fSSameer Pujar case TEGRA210_MVC_TX_INT_SET: 540e539891fSSameer Pujar 541e539891fSSameer Pujar case TEGRA210_MVC_SOFT_RESET: 542e539891fSSameer Pujar case TEGRA210_MVC_STATUS: 543e539891fSSameer Pujar case TEGRA210_MVC_INT_STATUS: 544e539891fSSameer Pujar case TEGRA210_MVC_SWITCH: 545e539891fSSameer Pujar case TEGRA210_MVC_CFG_RAM_CTRL: 546e539891fSSameer Pujar case TEGRA210_MVC_CFG_RAM_DATA: 547e539891fSSameer Pujar case TEGRA210_MVC_PEAK_VALUE: 548e539891fSSameer Pujar case TEGRA210_MVC_CTRL: 549e539891fSSameer Pujar return true; 550e539891fSSameer Pujar default: 551e539891fSSameer Pujar return false; 552e539891fSSameer Pujar } 553e539891fSSameer Pujar } 554e539891fSSameer Pujar 555e539891fSSameer Pujar static const struct regmap_config tegra210_mvc_regmap_config = { 556e539891fSSameer Pujar .reg_bits = 32, 557e539891fSSameer Pujar .reg_stride = 4, 558e539891fSSameer Pujar .val_bits = 32, 559e539891fSSameer Pujar .max_register = TEGRA210_MVC_CONFIG_ERR_TYPE, 560e539891fSSameer Pujar .writeable_reg = tegra210_mvc_wr_reg, 561e539891fSSameer Pujar .readable_reg = tegra210_mvc_rd_reg, 562e539891fSSameer Pujar .volatile_reg = tegra210_mvc_volatile_reg, 563e539891fSSameer Pujar .reg_defaults = tegra210_mvc_reg_defaults, 564e539891fSSameer Pujar .num_reg_defaults = ARRAY_SIZE(tegra210_mvc_reg_defaults), 565e539891fSSameer Pujar .cache_type = REGCACHE_FLAT, 566e539891fSSameer Pujar }; 567e539891fSSameer Pujar 568e539891fSSameer Pujar static const struct of_device_id tegra210_mvc_of_match[] = { 569e539891fSSameer Pujar { .compatible = "nvidia,tegra210-mvc" }, 570e539891fSSameer Pujar {}, 571e539891fSSameer Pujar }; 572e539891fSSameer Pujar MODULE_DEVICE_TABLE(of, tegra210_mvc_of_match); 573e539891fSSameer Pujar 574e539891fSSameer Pujar static int tegra210_mvc_platform_probe(struct platform_device *pdev) 575e539891fSSameer Pujar { 576e539891fSSameer Pujar struct device *dev = &pdev->dev; 577e539891fSSameer Pujar struct tegra210_mvc *mvc; 578e539891fSSameer Pujar void __iomem *regs; 579e539891fSSameer Pujar int err; 580e539891fSSameer Pujar 581e539891fSSameer Pujar mvc = devm_kzalloc(dev, sizeof(*mvc), GFP_KERNEL); 582e539891fSSameer Pujar if (!mvc) 583e539891fSSameer Pujar return -ENOMEM; 584e539891fSSameer Pujar 585e539891fSSameer Pujar dev_set_drvdata(dev, mvc); 586e539891fSSameer Pujar 587e539891fSSameer Pujar mvc->curve_type = CURVE_LINEAR; 588e539891fSSameer Pujar mvc->ctrl_value = TEGRA210_MVC_CTRL_DEFAULT; 589e539891fSSameer Pujar 590e539891fSSameer Pujar regs = devm_platform_ioremap_resource(pdev, 0); 591e539891fSSameer Pujar if (IS_ERR(regs)) 592e539891fSSameer Pujar return PTR_ERR(regs); 593e539891fSSameer Pujar 594e539891fSSameer Pujar mvc->regmap = devm_regmap_init_mmio(dev, regs, 595e539891fSSameer Pujar &tegra210_mvc_regmap_config); 596e539891fSSameer Pujar if (IS_ERR(mvc->regmap)) { 597e539891fSSameer Pujar dev_err(dev, "regmap init failed\n"); 598e539891fSSameer Pujar return PTR_ERR(mvc->regmap); 599e539891fSSameer Pujar } 600e539891fSSameer Pujar 601e539891fSSameer Pujar regcache_cache_only(mvc->regmap, true); 602e539891fSSameer Pujar 603e539891fSSameer Pujar err = devm_snd_soc_register_component(dev, &tegra210_mvc_cmpnt, 604e539891fSSameer Pujar tegra210_mvc_dais, 605e539891fSSameer Pujar ARRAY_SIZE(tegra210_mvc_dais)); 606e539891fSSameer Pujar if (err) { 607e539891fSSameer Pujar dev_err(dev, "can't register MVC component, err: %d\n", err); 608e539891fSSameer Pujar return err; 609e539891fSSameer Pujar } 610e539891fSSameer Pujar 611e539891fSSameer Pujar pm_runtime_enable(dev); 612e539891fSSameer Pujar 613e539891fSSameer Pujar tegra210_mvc_reset_vol_settings(mvc, &pdev->dev); 614e539891fSSameer Pujar 615e539891fSSameer Pujar return 0; 616e539891fSSameer Pujar } 617e539891fSSameer Pujar 618e539891fSSameer Pujar static int tegra210_mvc_platform_remove(struct platform_device *pdev) 619e539891fSSameer Pujar { 620e539891fSSameer Pujar pm_runtime_disable(&pdev->dev); 621e539891fSSameer Pujar 622e539891fSSameer Pujar return 0; 623e539891fSSameer Pujar } 624e539891fSSameer Pujar 625e539891fSSameer Pujar static const struct dev_pm_ops tegra210_mvc_pm_ops = { 626e539891fSSameer Pujar SET_RUNTIME_PM_OPS(tegra210_mvc_runtime_suspend, 627e539891fSSameer Pujar tegra210_mvc_runtime_resume, NULL) 628e539891fSSameer Pujar SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 629e539891fSSameer Pujar pm_runtime_force_resume) 630e539891fSSameer Pujar }; 631e539891fSSameer Pujar 632e539891fSSameer Pujar static struct platform_driver tegra210_mvc_driver = { 633e539891fSSameer Pujar .driver = { 634e539891fSSameer Pujar .name = "tegra210-mvc", 635e539891fSSameer Pujar .of_match_table = tegra210_mvc_of_match, 636e539891fSSameer Pujar .pm = &tegra210_mvc_pm_ops, 637e539891fSSameer Pujar }, 638e539891fSSameer Pujar .probe = tegra210_mvc_platform_probe, 639e539891fSSameer Pujar .remove = tegra210_mvc_platform_remove, 640e539891fSSameer Pujar }; 641e539891fSSameer Pujar module_platform_driver(tegra210_mvc_driver) 642e539891fSSameer Pujar 643e539891fSSameer Pujar MODULE_AUTHOR("Arun Shamanna Lakshmi <aruns@nvidia.com>"); 644e539891fSSameer Pujar MODULE_DESCRIPTION("Tegra210 MVC ASoC driver"); 645e539891fSSameer Pujar MODULE_LICENSE("GPL v2"); 646