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