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 1110d242698SSameer Pujar static u32 tegra210_mvc_get_ctrl_reg(struct snd_kcontrol *kcontrol) 112e539891fSSameer Pujar { 113e539891fSSameer Pujar struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); 114e539891fSSameer Pujar struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); 115e539891fSSameer Pujar u32 val; 116e539891fSSameer Pujar 117e539891fSSameer Pujar pm_runtime_get_sync(cmpnt->dev); 118e539891fSSameer Pujar regmap_read(mvc->regmap, TEGRA210_MVC_CTRL, &val); 119e539891fSSameer Pujar pm_runtime_put(cmpnt->dev); 120e539891fSSameer Pujar 1210d242698SSameer Pujar return val; 1220d242698SSameer Pujar } 123e539891fSSameer Pujar 1240d242698SSameer Pujar static int tegra210_mvc_get_mute(struct snd_kcontrol *kcontrol, 1250d242698SSameer Pujar struct snd_ctl_elem_value *ucontrol) 1260d242698SSameer Pujar { 1270d242698SSameer Pujar u32 val = tegra210_mvc_get_ctrl_reg(kcontrol); 1280d242698SSameer Pujar u8 mute_mask = TEGRA210_GET_MUTE_VAL(val); 1290d242698SSameer Pujar 1300d242698SSameer Pujar /* 1310d242698SSameer Pujar * If per channel control is enabled, then return 1320d242698SSameer Pujar * exact mute/unmute setting of all channels. 1330d242698SSameer Pujar * 1340d242698SSameer Pujar * Else report setting based on CH0 bit to reflect 1350d242698SSameer Pujar * the correct HW state. 1360d242698SSameer Pujar */ 1370d242698SSameer Pujar if (val & TEGRA210_MVC_PER_CHAN_CTRL_EN) { 138e539891fSSameer Pujar ucontrol->value.integer.value[0] = mute_mask; 1390d242698SSameer Pujar } else { 1400d242698SSameer Pujar if (mute_mask & TEGRA210_MVC_CH0_MUTE_EN) 1410d242698SSameer Pujar ucontrol->value.integer.value[0] = 1420d242698SSameer Pujar TEGRA210_MUTE_MASK_EN; 1430d242698SSameer Pujar else 1440d242698SSameer Pujar ucontrol->value.integer.value[0] = 0; 1450d242698SSameer Pujar } 1460d242698SSameer Pujar 1470d242698SSameer Pujar return 0; 1480d242698SSameer Pujar } 1490d242698SSameer Pujar 1500d242698SSameer Pujar static int tegra210_mvc_get_master_mute(struct snd_kcontrol *kcontrol, 1510d242698SSameer Pujar struct snd_ctl_elem_value *ucontrol) 1520d242698SSameer Pujar { 1530d242698SSameer Pujar u32 val = tegra210_mvc_get_ctrl_reg(kcontrol); 1540d242698SSameer Pujar u8 mute_mask = TEGRA210_GET_MUTE_VAL(val); 1550d242698SSameer Pujar 1560d242698SSameer Pujar /* 1570d242698SSameer Pujar * If per channel control is disabled, then return 1580d242698SSameer Pujar * master mute/unmute setting based on CH0 bit. 1590d242698SSameer Pujar * 1600d242698SSameer Pujar * Else report settings based on state of all 1610d242698SSameer Pujar * channels. 1620d242698SSameer Pujar */ 1630d242698SSameer Pujar if (!(val & TEGRA210_MVC_PER_CHAN_CTRL_EN)) { 1640d242698SSameer Pujar ucontrol->value.integer.value[0] = 1650d242698SSameer Pujar mute_mask & TEGRA210_MVC_CH0_MUTE_EN; 1660d242698SSameer Pujar } else { 1670d242698SSameer Pujar if (mute_mask == TEGRA210_MUTE_MASK_EN) 1680d242698SSameer Pujar ucontrol->value.integer.value[0] = 1690d242698SSameer Pujar TEGRA210_MVC_CH0_MUTE_EN; 1700d242698SSameer Pujar else 1710d242698SSameer Pujar ucontrol->value.integer.value[0] = 0; 1720d242698SSameer Pujar } 1730d242698SSameer Pujar 1740d242698SSameer Pujar return 0; 1750d242698SSameer Pujar } 1760d242698SSameer Pujar 1770d242698SSameer Pujar static int tegra210_mvc_volume_switch_timeout(struct snd_soc_component *cmpnt) 1780d242698SSameer Pujar { 1790d242698SSameer Pujar struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); 1800d242698SSameer Pujar u32 value; 1810d242698SSameer Pujar int err; 1820d242698SSameer Pujar 1830d242698SSameer Pujar err = regmap_read_poll_timeout(mvc->regmap, TEGRA210_MVC_SWITCH, 1840d242698SSameer Pujar value, !(value & TEGRA210_MVC_VOLUME_SWITCH_MASK), 1850d242698SSameer Pujar 10, 10000); 1860d242698SSameer Pujar if (err < 0) 1870d242698SSameer Pujar dev_err(cmpnt->dev, 1880d242698SSameer Pujar "Volume switch trigger is still active, err = %d\n", 1890d242698SSameer Pujar err); 1900d242698SSameer Pujar 1910d242698SSameer Pujar return err; 1920d242698SSameer Pujar } 1930d242698SSameer Pujar 1940d242698SSameer Pujar static int tegra210_mvc_update_mute(struct snd_kcontrol *kcontrol, 1950d242698SSameer Pujar struct snd_ctl_elem_value *ucontrol, 1960d242698SSameer Pujar bool per_chan_ctrl) 1970d242698SSameer Pujar { 1980d242698SSameer Pujar struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); 1990d242698SSameer Pujar struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); 2000d242698SSameer Pujar u32 mute_val = ucontrol->value.integer.value[0]; 2010d242698SSameer Pujar u32 per_ch_ctrl_val; 2020d242698SSameer Pujar bool change = false; 2030d242698SSameer Pujar int err; 2040d242698SSameer Pujar 2050d242698SSameer Pujar pm_runtime_get_sync(cmpnt->dev); 2060d242698SSameer Pujar 2070d242698SSameer Pujar err = tegra210_mvc_volume_switch_timeout(cmpnt); 2080d242698SSameer Pujar if (err < 0) 2090d242698SSameer Pujar goto end; 2100d242698SSameer Pujar 2110d242698SSameer Pujar if (per_chan_ctrl) { 2120d242698SSameer Pujar per_ch_ctrl_val = TEGRA210_MVC_PER_CHAN_CTRL_EN; 2130d242698SSameer Pujar } else { 2140d242698SSameer Pujar per_ch_ctrl_val = 0; 2150d242698SSameer Pujar 2160d242698SSameer Pujar if (mute_val) 2170d242698SSameer Pujar mute_val = TEGRA210_MUTE_MASK_EN; 2180d242698SSameer Pujar } 2190d242698SSameer Pujar 2200d242698SSameer Pujar regmap_update_bits_check(mvc->regmap, TEGRA210_MVC_CTRL, 2210d242698SSameer Pujar TEGRA210_MVC_MUTE_MASK, 2220d242698SSameer Pujar mute_val << TEGRA210_MVC_MUTE_SHIFT, 2230d242698SSameer Pujar &change); 2240d242698SSameer Pujar 2250d242698SSameer Pujar if (change) { 2260d242698SSameer Pujar regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL, 2270d242698SSameer Pujar TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK, 2280d242698SSameer Pujar per_ch_ctrl_val); 2290d242698SSameer Pujar 2300d242698SSameer Pujar regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH, 2310d242698SSameer Pujar TEGRA210_MVC_VOLUME_SWITCH_MASK, 2320d242698SSameer Pujar TEGRA210_MVC_VOLUME_SWITCH_TRIGGER); 2330d242698SSameer Pujar } 2340d242698SSameer Pujar 2350d242698SSameer Pujar end: 2360d242698SSameer Pujar pm_runtime_put(cmpnt->dev); 2370d242698SSameer Pujar 2380d242698SSameer Pujar if (err < 0) 2390d242698SSameer Pujar return err; 2400d242698SSameer Pujar 2410d242698SSameer Pujar if (change) 2420d242698SSameer Pujar return 1; 243e539891fSSameer Pujar 244e539891fSSameer Pujar return 0; 245e539891fSSameer Pujar } 246e539891fSSameer Pujar 247e539891fSSameer Pujar static int tegra210_mvc_put_mute(struct snd_kcontrol *kcontrol, 248e539891fSSameer Pujar struct snd_ctl_elem_value *ucontrol) 249e539891fSSameer Pujar { 2500d242698SSameer Pujar return tegra210_mvc_update_mute(kcontrol, ucontrol, true); 251c7b34b51SSameer Pujar } 252e539891fSSameer Pujar 2530d242698SSameer Pujar static int tegra210_mvc_put_master_mute(struct snd_kcontrol *kcontrol, 2540d242698SSameer Pujar struct snd_ctl_elem_value *ucontrol) 2550d242698SSameer Pujar { 2560d242698SSameer Pujar return tegra210_mvc_update_mute(kcontrol, ucontrol, false); 257e539891fSSameer Pujar } 258e539891fSSameer Pujar 259e539891fSSameer Pujar static int tegra210_mvc_get_vol(struct snd_kcontrol *kcontrol, 260e539891fSSameer Pujar struct snd_ctl_elem_value *ucontrol) 261e539891fSSameer Pujar { 262e539891fSSameer Pujar struct soc_mixer_control *mc = 263e539891fSSameer Pujar (struct soc_mixer_control *)kcontrol->private_value; 264e539891fSSameer Pujar struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); 265e539891fSSameer Pujar struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); 2660d242698SSameer Pujar u8 chan = TEGRA210_MVC_GET_CHAN(mc->reg, TEGRA210_MVC_TARGET_VOL); 267e539891fSSameer Pujar s32 val = mvc->volume[chan]; 268e539891fSSameer Pujar 269e539891fSSameer Pujar if (mvc->curve_type == CURVE_POLY) { 270e539891fSSameer Pujar val = ((val >> 16) * 100) >> 8; 271e539891fSSameer Pujar } else { 272e539891fSSameer Pujar val = (val * 100) >> 8; 273e539891fSSameer Pujar val += 12000; 274e539891fSSameer Pujar } 275e539891fSSameer Pujar 276e539891fSSameer Pujar ucontrol->value.integer.value[0] = val; 277e539891fSSameer Pujar 278e539891fSSameer Pujar return 0; 279e539891fSSameer Pujar } 280e539891fSSameer Pujar 2810d242698SSameer Pujar static int tegra210_mvc_get_master_vol(struct snd_kcontrol *kcontrol, 282e539891fSSameer Pujar struct snd_ctl_elem_value *ucontrol) 283e539891fSSameer Pujar { 2840d242698SSameer Pujar return tegra210_mvc_get_vol(kcontrol, ucontrol); 2850d242698SSameer Pujar } 2860d242698SSameer Pujar 2870d242698SSameer Pujar static int tegra210_mvc_update_vol(struct snd_kcontrol *kcontrol, 2880d242698SSameer Pujar struct snd_ctl_elem_value *ucontrol, 2890d242698SSameer Pujar bool per_ch_enable) 2900d242698SSameer Pujar { 291e539891fSSameer Pujar struct soc_mixer_control *mc = 292e539891fSSameer Pujar (struct soc_mixer_control *)kcontrol->private_value; 293e539891fSSameer Pujar struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); 294e539891fSSameer Pujar struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); 2950d242698SSameer Pujar u8 chan = TEGRA210_MVC_GET_CHAN(mc->reg, TEGRA210_MVC_TARGET_VOL); 2960d242698SSameer Pujar int old_volume = mvc->volume[chan]; 2970d242698SSameer Pujar int err, i; 298e539891fSSameer Pujar 299e539891fSSameer Pujar pm_runtime_get_sync(cmpnt->dev); 300e539891fSSameer Pujar 3010d242698SSameer Pujar err = tegra210_mvc_volume_switch_timeout(cmpnt); 302e539891fSSameer Pujar if (err < 0) 303e539891fSSameer Pujar goto end; 304e539891fSSameer Pujar 3050d242698SSameer Pujar tegra210_mvc_conv_vol(mvc, chan, ucontrol->value.integer.value[0]); 306e539891fSSameer Pujar 307c7b34b51SSameer Pujar if (mvc->volume[chan] == old_volume) { 308c7b34b51SSameer Pujar err = 0; 309c7b34b51SSameer Pujar goto end; 310c7b34b51SSameer Pujar } 311c7b34b51SSameer Pujar 3120d242698SSameer Pujar if (per_ch_enable) { 3130d242698SSameer Pujar regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL, 3140d242698SSameer Pujar TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK, 3150d242698SSameer Pujar TEGRA210_MVC_PER_CHAN_CTRL_EN); 3160d242698SSameer Pujar } else { 3170d242698SSameer Pujar regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL, 3180d242698SSameer Pujar TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK, 0); 3190d242698SSameer Pujar 3200d242698SSameer Pujar for (i = 1; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++) 3210d242698SSameer Pujar mvc->volume[i] = mvc->volume[chan]; 3220d242698SSameer Pujar } 3230d242698SSameer Pujar 324e539891fSSameer Pujar /* Configure init volume same as target volume */ 325e539891fSSameer Pujar regmap_write(mvc->regmap, 326e539891fSSameer Pujar TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_INIT_VOL, chan), 327e539891fSSameer Pujar mvc->volume[chan]); 328e539891fSSameer Pujar 3290d242698SSameer Pujar regmap_write(mvc->regmap, mc->reg, mvc->volume[chan]); 330e539891fSSameer Pujar 331e539891fSSameer Pujar regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH, 332e539891fSSameer Pujar TEGRA210_MVC_VOLUME_SWITCH_MASK, 333e539891fSSameer Pujar TEGRA210_MVC_VOLUME_SWITCH_TRIGGER); 334e539891fSSameer Pujar 33570408f75SSameer Pujar err = 1; 336e539891fSSameer Pujar 337e539891fSSameer Pujar end: 338e539891fSSameer Pujar pm_runtime_put(cmpnt->dev); 3390d242698SSameer Pujar 340e539891fSSameer Pujar return err; 341e539891fSSameer Pujar } 342e539891fSSameer Pujar 3430d242698SSameer Pujar static int tegra210_mvc_put_vol(struct snd_kcontrol *kcontrol, 3440d242698SSameer Pujar struct snd_ctl_elem_value *ucontrol) 3450d242698SSameer Pujar { 3460d242698SSameer Pujar return tegra210_mvc_update_vol(kcontrol, ucontrol, true); 3470d242698SSameer Pujar } 3480d242698SSameer Pujar 3490d242698SSameer Pujar static int tegra210_mvc_put_master_vol(struct snd_kcontrol *kcontrol, 3500d242698SSameer Pujar struct snd_ctl_elem_value *ucontrol) 3510d242698SSameer Pujar { 3520d242698SSameer Pujar return tegra210_mvc_update_vol(kcontrol, ucontrol, false); 3530d242698SSameer Pujar } 3540d242698SSameer Pujar 355e539891fSSameer Pujar static void tegra210_mvc_reset_vol_settings(struct tegra210_mvc *mvc, 356e539891fSSameer Pujar struct device *dev) 357e539891fSSameer Pujar { 358e539891fSSameer Pujar int i; 359e539891fSSameer Pujar 360e539891fSSameer Pujar /* Change volume to default init for new curve type */ 361e539891fSSameer Pujar if (mvc->curve_type == CURVE_POLY) { 362e539891fSSameer Pujar for (i = 0; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++) 363e539891fSSameer Pujar mvc->volume[i] = TEGRA210_MVC_INIT_VOL_DEFAULT_POLY; 364e539891fSSameer Pujar } else { 365e539891fSSameer Pujar for (i = 0; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++) 366e539891fSSameer Pujar mvc->volume[i] = TEGRA210_MVC_INIT_VOL_DEFAULT_LINEAR; 367e539891fSSameer Pujar } 368e539891fSSameer Pujar 369e539891fSSameer Pujar pm_runtime_get_sync(dev); 370e539891fSSameer Pujar 371e539891fSSameer Pujar /* Program curve type */ 372e539891fSSameer Pujar regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL, 373e539891fSSameer Pujar TEGRA210_MVC_CURVE_TYPE_MASK, 374e539891fSSameer Pujar mvc->curve_type << 375e539891fSSameer Pujar TEGRA210_MVC_CURVE_TYPE_SHIFT); 376e539891fSSameer Pujar 377e539891fSSameer Pujar /* Init volume for all channels */ 378e539891fSSameer Pujar for (i = 0; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++) { 379e539891fSSameer Pujar regmap_write(mvc->regmap, 380e539891fSSameer Pujar TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_INIT_VOL, i), 381e539891fSSameer Pujar mvc->volume[i]); 382e539891fSSameer Pujar regmap_write(mvc->regmap, 383e539891fSSameer Pujar TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_TARGET_VOL, i), 384e539891fSSameer Pujar mvc->volume[i]); 385e539891fSSameer Pujar } 386e539891fSSameer Pujar 387e539891fSSameer Pujar /* Trigger volume switch */ 388e539891fSSameer Pujar regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH, 389e539891fSSameer Pujar TEGRA210_MVC_VOLUME_SWITCH_MASK, 390e539891fSSameer Pujar TEGRA210_MVC_VOLUME_SWITCH_TRIGGER); 391e539891fSSameer Pujar 392e539891fSSameer Pujar pm_runtime_put(dev); 393e539891fSSameer Pujar } 394e539891fSSameer Pujar 395e539891fSSameer Pujar static int tegra210_mvc_get_curve_type(struct snd_kcontrol *kcontrol, 396e539891fSSameer Pujar struct snd_ctl_elem_value *ucontrol) 397e539891fSSameer Pujar { 398e539891fSSameer Pujar struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); 399e539891fSSameer Pujar struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); 400e539891fSSameer Pujar 4016762965dSSameer Pujar ucontrol->value.enumerated.item[0] = mvc->curve_type; 402e539891fSSameer Pujar 403e539891fSSameer Pujar return 0; 404e539891fSSameer Pujar } 405e539891fSSameer Pujar 406e539891fSSameer Pujar static int tegra210_mvc_put_curve_type(struct snd_kcontrol *kcontrol, 407e539891fSSameer Pujar struct snd_ctl_elem_value *ucontrol) 408e539891fSSameer Pujar { 409e539891fSSameer Pujar struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); 410e539891fSSameer Pujar struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); 4116762965dSSameer Pujar unsigned int value; 412e539891fSSameer Pujar 413e539891fSSameer Pujar regmap_read(mvc->regmap, TEGRA210_MVC_ENABLE, &value); 414e539891fSSameer Pujar if (value & TEGRA210_MVC_EN) { 415e539891fSSameer Pujar dev_err(cmpnt->dev, 416e539891fSSameer Pujar "Curve type can't be set when MVC is running\n"); 417e539891fSSameer Pujar return -EINVAL; 418e539891fSSameer Pujar } 419e539891fSSameer Pujar 4206762965dSSameer Pujar if (mvc->curve_type == ucontrol->value.enumerated.item[0]) 421e539891fSSameer Pujar return 0; 422e539891fSSameer Pujar 4236762965dSSameer Pujar mvc->curve_type = ucontrol->value.enumerated.item[0]; 424e539891fSSameer Pujar 425e539891fSSameer Pujar tegra210_mvc_reset_vol_settings(mvc, cmpnt->dev); 426e539891fSSameer Pujar 427e539891fSSameer Pujar return 1; 428e539891fSSameer Pujar } 429e539891fSSameer Pujar 430e539891fSSameer Pujar static int tegra210_mvc_set_audio_cif(struct tegra210_mvc *mvc, 431e539891fSSameer Pujar struct snd_pcm_hw_params *params, 432e539891fSSameer Pujar unsigned int reg) 433e539891fSSameer Pujar { 434e539891fSSameer Pujar unsigned int channels, audio_bits; 435e539891fSSameer Pujar struct tegra_cif_conf cif_conf; 436e539891fSSameer Pujar 437e539891fSSameer Pujar memset(&cif_conf, 0, sizeof(struct tegra_cif_conf)); 438e539891fSSameer Pujar 439e539891fSSameer Pujar channels = params_channels(params); 440e539891fSSameer Pujar 441e539891fSSameer Pujar switch (params_format(params)) { 442e539891fSSameer Pujar case SNDRV_PCM_FORMAT_S16_LE: 443e539891fSSameer Pujar audio_bits = TEGRA_ACIF_BITS_16; 444e539891fSSameer Pujar break; 445e539891fSSameer Pujar case SNDRV_PCM_FORMAT_S32_LE: 446e539891fSSameer Pujar audio_bits = TEGRA_ACIF_BITS_32; 447e539891fSSameer Pujar break; 448e539891fSSameer Pujar default: 449e539891fSSameer Pujar return -EINVAL; 450e539891fSSameer Pujar } 451e539891fSSameer Pujar 452e539891fSSameer Pujar cif_conf.audio_ch = channels; 453e539891fSSameer Pujar cif_conf.client_ch = channels; 454e539891fSSameer Pujar cif_conf.audio_bits = audio_bits; 455e539891fSSameer Pujar cif_conf.client_bits = audio_bits; 456e539891fSSameer Pujar 457e539891fSSameer Pujar tegra_set_cif(mvc->regmap, reg, &cif_conf); 458e539891fSSameer Pujar 459e539891fSSameer Pujar return 0; 460e539891fSSameer Pujar } 461e539891fSSameer Pujar 462e539891fSSameer Pujar static int tegra210_mvc_hw_params(struct snd_pcm_substream *substream, 463e539891fSSameer Pujar struct snd_pcm_hw_params *params, 464e539891fSSameer Pujar struct snd_soc_dai *dai) 465e539891fSSameer Pujar { 466e539891fSSameer Pujar struct device *dev = dai->dev; 467e539891fSSameer Pujar struct tegra210_mvc *mvc = snd_soc_dai_get_drvdata(dai); 468e539891fSSameer Pujar int err, val; 469e539891fSSameer Pujar 470e539891fSSameer Pujar /* 471e539891fSSameer Pujar * Soft Reset: Below performs module soft reset which clears 472e539891fSSameer Pujar * all FSM logic, flushes flow control of FIFO and resets the 473e539891fSSameer Pujar * state register. It also brings module back to disabled 474e539891fSSameer Pujar * state (without flushing the data in the pipe). 475e539891fSSameer Pujar */ 476e539891fSSameer Pujar regmap_write(mvc->regmap, TEGRA210_MVC_SOFT_RESET, 1); 477e539891fSSameer Pujar 478e539891fSSameer Pujar err = regmap_read_poll_timeout(mvc->regmap, TEGRA210_MVC_SOFT_RESET, 479e539891fSSameer Pujar val, !val, 10, 10000); 480e539891fSSameer Pujar if (err < 0) { 481e539891fSSameer Pujar dev_err(dev, "SW reset failed, err = %d\n", err); 482e539891fSSameer Pujar return err; 483e539891fSSameer Pujar } 484e539891fSSameer Pujar 485e539891fSSameer Pujar /* Set RX CIF */ 486e539891fSSameer Pujar err = tegra210_mvc_set_audio_cif(mvc, params, TEGRA210_MVC_RX_CIF_CTRL); 487e539891fSSameer Pujar if (err) { 488e539891fSSameer Pujar dev_err(dev, "Can't set MVC RX CIF: %d\n", err); 489e539891fSSameer Pujar return err; 490e539891fSSameer Pujar } 491e539891fSSameer Pujar 492e539891fSSameer Pujar /* Set TX CIF */ 493e539891fSSameer Pujar err = tegra210_mvc_set_audio_cif(mvc, params, TEGRA210_MVC_TX_CIF_CTRL); 494e539891fSSameer Pujar if (err) { 495e539891fSSameer Pujar dev_err(dev, "Can't set MVC TX CIF: %d\n", err); 496e539891fSSameer Pujar return err; 497e539891fSSameer Pujar } 498e539891fSSameer Pujar 499e539891fSSameer Pujar tegra210_mvc_write_ram(mvc->regmap); 500e539891fSSameer Pujar 501e539891fSSameer Pujar /* Program poly_n1, poly_n2, duration */ 502e539891fSSameer Pujar regmap_write(mvc->regmap, TEGRA210_MVC_POLY_N1, gain_params.poly_n1); 503e539891fSSameer Pujar regmap_write(mvc->regmap, TEGRA210_MVC_POLY_N2, gain_params.poly_n2); 504e539891fSSameer Pujar regmap_write(mvc->regmap, TEGRA210_MVC_DURATION, gain_params.duration); 505e539891fSSameer Pujar 506e539891fSSameer Pujar /* Program duration_inv */ 507e539891fSSameer Pujar regmap_write(mvc->regmap, TEGRA210_MVC_DURATION_INV, 508e539891fSSameer Pujar gain_params.duration_inv); 509e539891fSSameer Pujar 510e539891fSSameer Pujar return 0; 511e539891fSSameer Pujar } 512e539891fSSameer Pujar 513313fab48SRikard Falkeborn static const struct snd_soc_dai_ops tegra210_mvc_dai_ops = { 514e539891fSSameer Pujar .hw_params = tegra210_mvc_hw_params, 515e539891fSSameer Pujar }; 516e539891fSSameer Pujar 517e539891fSSameer Pujar static const char * const tegra210_mvc_curve_type_text[] = { 518e539891fSSameer Pujar "Poly", 519e539891fSSameer Pujar "Linear", 520e539891fSSameer Pujar }; 521e539891fSSameer Pujar 522e539891fSSameer Pujar static const struct soc_enum tegra210_mvc_curve_type_ctrl = 523e539891fSSameer Pujar SOC_ENUM_SINGLE_EXT(2, tegra210_mvc_curve_type_text); 524e539891fSSameer Pujar 525e539891fSSameer Pujar #define TEGRA210_MVC_VOL_CTRL(chan) \ 526e539891fSSameer Pujar SOC_SINGLE_EXT("Channel" #chan " Volume", \ 527e539891fSSameer Pujar TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_TARGET_VOL, \ 528e539891fSSameer Pujar (chan - 1)), \ 529e539891fSSameer Pujar 0, 16000, 0, tegra210_mvc_get_vol, \ 530e539891fSSameer Pujar tegra210_mvc_put_vol) 531e539891fSSameer Pujar 532e539891fSSameer Pujar static const struct snd_kcontrol_new tegra210_mvc_vol_ctrl[] = { 533e539891fSSameer Pujar /* Per channel volume control */ 534e539891fSSameer Pujar TEGRA210_MVC_VOL_CTRL(1), 535e539891fSSameer Pujar TEGRA210_MVC_VOL_CTRL(2), 536e539891fSSameer Pujar TEGRA210_MVC_VOL_CTRL(3), 537e539891fSSameer Pujar TEGRA210_MVC_VOL_CTRL(4), 538e539891fSSameer Pujar TEGRA210_MVC_VOL_CTRL(5), 539e539891fSSameer Pujar TEGRA210_MVC_VOL_CTRL(6), 540e539891fSSameer Pujar TEGRA210_MVC_VOL_CTRL(7), 541e539891fSSameer Pujar TEGRA210_MVC_VOL_CTRL(8), 542e539891fSSameer Pujar 543e539891fSSameer Pujar /* Per channel mute */ 544e539891fSSameer Pujar SOC_SINGLE_EXT("Per Chan Mute Mask", 545e539891fSSameer Pujar TEGRA210_MVC_CTRL, 0, TEGRA210_MUTE_MASK_EN, 0, 546e539891fSSameer Pujar tegra210_mvc_get_mute, tegra210_mvc_put_mute), 547e539891fSSameer Pujar 5480d242698SSameer Pujar /* Master volume */ 5490d242698SSameer Pujar SOC_SINGLE_EXT("Volume", TEGRA210_MVC_TARGET_VOL, 0, 16000, 0, 5500d242698SSameer Pujar tegra210_mvc_get_master_vol, 5510d242698SSameer Pujar tegra210_mvc_put_master_vol), 5520d242698SSameer Pujar 5530d242698SSameer Pujar /* Master mute */ 5540d242698SSameer Pujar SOC_SINGLE_EXT("Mute", TEGRA210_MVC_CTRL, 0, 1, 0, 5550d242698SSameer Pujar tegra210_mvc_get_master_mute, 5560d242698SSameer Pujar tegra210_mvc_put_master_mute), 5570d242698SSameer Pujar 558e539891fSSameer Pujar SOC_ENUM_EXT("Curve Type", tegra210_mvc_curve_type_ctrl, 559e539891fSSameer Pujar tegra210_mvc_get_curve_type, tegra210_mvc_put_curve_type), 560e539891fSSameer Pujar }; 561e539891fSSameer Pujar 562e539891fSSameer Pujar static struct snd_soc_dai_driver tegra210_mvc_dais[] = { 563e539891fSSameer Pujar /* Input */ 564e539891fSSameer Pujar { 565e539891fSSameer Pujar .name = "MVC-RX-CIF", 566e539891fSSameer Pujar .playback = { 567e539891fSSameer Pujar .stream_name = "RX-CIF-Playback", 568e539891fSSameer Pujar .channels_min = 1, 569e539891fSSameer Pujar .channels_max = 8, 570e539891fSSameer Pujar .rates = SNDRV_PCM_RATE_8000_192000, 571e539891fSSameer Pujar .formats = SNDRV_PCM_FMTBIT_S8 | 572e539891fSSameer Pujar SNDRV_PCM_FMTBIT_S16_LE | 573e539891fSSameer Pujar SNDRV_PCM_FMTBIT_S32_LE, 574e539891fSSameer Pujar }, 575e539891fSSameer Pujar .capture = { 576e539891fSSameer Pujar .stream_name = "RX-CIF-Capture", 577e539891fSSameer Pujar .channels_min = 1, 578e539891fSSameer Pujar .channels_max = 8, 579e539891fSSameer Pujar .rates = SNDRV_PCM_RATE_8000_192000, 580e539891fSSameer Pujar .formats = SNDRV_PCM_FMTBIT_S8 | 581e539891fSSameer Pujar SNDRV_PCM_FMTBIT_S16_LE | 582e539891fSSameer Pujar SNDRV_PCM_FMTBIT_S32_LE, 583e539891fSSameer Pujar }, 584e539891fSSameer Pujar }, 585e539891fSSameer Pujar 586e539891fSSameer Pujar /* Output */ 587e539891fSSameer Pujar { 588e539891fSSameer Pujar .name = "MVC-TX-CIF", 589e539891fSSameer Pujar .playback = { 590e539891fSSameer Pujar .stream_name = "TX-CIF-Playback", 591e539891fSSameer Pujar .channels_min = 1, 592e539891fSSameer Pujar .channels_max = 8, 593e539891fSSameer Pujar .rates = SNDRV_PCM_RATE_8000_192000, 594e539891fSSameer Pujar .formats = SNDRV_PCM_FMTBIT_S8 | 595e539891fSSameer Pujar SNDRV_PCM_FMTBIT_S16_LE | 596e539891fSSameer Pujar SNDRV_PCM_FMTBIT_S32_LE, 597e539891fSSameer Pujar }, 598e539891fSSameer Pujar .capture = { 599e539891fSSameer Pujar .stream_name = "TX-CIF-Capture", 600e539891fSSameer Pujar .channels_min = 1, 601e539891fSSameer Pujar .channels_max = 8, 602e539891fSSameer Pujar .rates = SNDRV_PCM_RATE_8000_192000, 603e539891fSSameer Pujar .formats = SNDRV_PCM_FMTBIT_S8 | 604e539891fSSameer Pujar SNDRV_PCM_FMTBIT_S16_LE | 605e539891fSSameer Pujar SNDRV_PCM_FMTBIT_S32_LE, 606e539891fSSameer Pujar }, 607e539891fSSameer Pujar .ops = &tegra210_mvc_dai_ops, 608e539891fSSameer Pujar } 609e539891fSSameer Pujar }; 610e539891fSSameer Pujar 611e539891fSSameer Pujar static const struct snd_soc_dapm_widget tegra210_mvc_widgets[] = { 612e539891fSSameer Pujar SND_SOC_DAPM_AIF_IN("RX", NULL, 0, SND_SOC_NOPM, 0, 0), 613e539891fSSameer Pujar SND_SOC_DAPM_AIF_OUT("TX", NULL, 0, TEGRA210_MVC_ENABLE, 614e539891fSSameer Pujar TEGRA210_MVC_EN_SHIFT, 0), 615e539891fSSameer Pujar }; 616e539891fSSameer Pujar 617e539891fSSameer Pujar #define MVC_ROUTES(sname) \ 618e539891fSSameer Pujar { "RX XBAR-" sname, NULL, "XBAR-TX" }, \ 619e539891fSSameer Pujar { "RX-CIF-" sname, NULL, "RX XBAR-" sname }, \ 620e539891fSSameer Pujar { "RX", NULL, "RX-CIF-" sname }, \ 621e539891fSSameer Pujar { "TX-CIF-" sname, NULL, "TX" }, \ 622e539891fSSameer Pujar { "TX XBAR-" sname, NULL, "TX-CIF-" sname }, \ 623e539891fSSameer Pujar { "XBAR-RX", NULL, "TX XBAR-" sname } 624e539891fSSameer Pujar 625e539891fSSameer Pujar static const struct snd_soc_dapm_route tegra210_mvc_routes[] = { 626e539891fSSameer Pujar { "TX", NULL, "RX" }, 627e539891fSSameer Pujar MVC_ROUTES("Playback"), 628e539891fSSameer Pujar MVC_ROUTES("Capture"), 629e539891fSSameer Pujar }; 630e539891fSSameer Pujar 631e539891fSSameer Pujar static const struct snd_soc_component_driver tegra210_mvc_cmpnt = { 632e539891fSSameer Pujar .dapm_widgets = tegra210_mvc_widgets, 633e539891fSSameer Pujar .num_dapm_widgets = ARRAY_SIZE(tegra210_mvc_widgets), 634e539891fSSameer Pujar .dapm_routes = tegra210_mvc_routes, 635e539891fSSameer Pujar .num_dapm_routes = ARRAY_SIZE(tegra210_mvc_routes), 636e539891fSSameer Pujar .controls = tegra210_mvc_vol_ctrl, 637e539891fSSameer Pujar .num_controls = ARRAY_SIZE(tegra210_mvc_vol_ctrl), 638e539891fSSameer Pujar }; 639e539891fSSameer Pujar 640e539891fSSameer Pujar static bool tegra210_mvc_rd_reg(struct device *dev, unsigned int reg) 641e539891fSSameer Pujar { 642e539891fSSameer Pujar switch (reg) { 643e539891fSSameer Pujar case TEGRA210_MVC_RX_STATUS ... TEGRA210_MVC_CONFIG_ERR_TYPE: 644e539891fSSameer Pujar return true; 645e539891fSSameer Pujar default: 646e539891fSSameer Pujar return false; 647e539891fSSameer Pujar }; 648e539891fSSameer Pujar } 649e539891fSSameer Pujar 650e539891fSSameer Pujar static bool tegra210_mvc_wr_reg(struct device *dev, unsigned int reg) 651e539891fSSameer Pujar { 652e539891fSSameer Pujar switch (reg) { 653e539891fSSameer Pujar case TEGRA210_MVC_RX_INT_MASK ... TEGRA210_MVC_RX_CIF_CTRL: 654e539891fSSameer Pujar case TEGRA210_MVC_TX_INT_MASK ... TEGRA210_MVC_TX_CIF_CTRL: 655e539891fSSameer Pujar case TEGRA210_MVC_ENABLE ... TEGRA210_MVC_CG: 656e539891fSSameer Pujar case TEGRA210_MVC_CTRL ... TEGRA210_MVC_CFG_RAM_DATA: 657e539891fSSameer Pujar return true; 658e539891fSSameer Pujar default: 659e539891fSSameer Pujar return false; 660e539891fSSameer Pujar } 661e539891fSSameer Pujar } 662e539891fSSameer Pujar 663e539891fSSameer Pujar static bool tegra210_mvc_volatile_reg(struct device *dev, unsigned int reg) 664e539891fSSameer Pujar { 665e539891fSSameer Pujar switch (reg) { 666e539891fSSameer Pujar case TEGRA210_MVC_RX_STATUS: 667e539891fSSameer Pujar case TEGRA210_MVC_RX_INT_STATUS: 668e539891fSSameer Pujar case TEGRA210_MVC_RX_INT_SET: 669e539891fSSameer Pujar 670e539891fSSameer Pujar case TEGRA210_MVC_TX_STATUS: 671e539891fSSameer Pujar case TEGRA210_MVC_TX_INT_STATUS: 672e539891fSSameer Pujar case TEGRA210_MVC_TX_INT_SET: 673e539891fSSameer Pujar 674e539891fSSameer Pujar case TEGRA210_MVC_SOFT_RESET: 675e539891fSSameer Pujar case TEGRA210_MVC_STATUS: 676e539891fSSameer Pujar case TEGRA210_MVC_INT_STATUS: 677e539891fSSameer Pujar case TEGRA210_MVC_SWITCH: 678e539891fSSameer Pujar case TEGRA210_MVC_CFG_RAM_CTRL: 679e539891fSSameer Pujar case TEGRA210_MVC_CFG_RAM_DATA: 680e539891fSSameer Pujar case TEGRA210_MVC_PEAK_VALUE: 681e539891fSSameer Pujar case TEGRA210_MVC_CTRL: 682e539891fSSameer Pujar return true; 683e539891fSSameer Pujar default: 684e539891fSSameer Pujar return false; 685e539891fSSameer Pujar } 686e539891fSSameer Pujar } 687e539891fSSameer Pujar 688e539891fSSameer Pujar static const struct regmap_config tegra210_mvc_regmap_config = { 689e539891fSSameer Pujar .reg_bits = 32, 690e539891fSSameer Pujar .reg_stride = 4, 691e539891fSSameer Pujar .val_bits = 32, 692e539891fSSameer Pujar .max_register = TEGRA210_MVC_CONFIG_ERR_TYPE, 693e539891fSSameer Pujar .writeable_reg = tegra210_mvc_wr_reg, 694e539891fSSameer Pujar .readable_reg = tegra210_mvc_rd_reg, 695e539891fSSameer Pujar .volatile_reg = tegra210_mvc_volatile_reg, 696e539891fSSameer Pujar .reg_defaults = tegra210_mvc_reg_defaults, 697e539891fSSameer Pujar .num_reg_defaults = ARRAY_SIZE(tegra210_mvc_reg_defaults), 698e539891fSSameer Pujar .cache_type = REGCACHE_FLAT, 699e539891fSSameer Pujar }; 700e539891fSSameer Pujar 701e539891fSSameer Pujar static const struct of_device_id tegra210_mvc_of_match[] = { 702e539891fSSameer Pujar { .compatible = "nvidia,tegra210-mvc" }, 703e539891fSSameer Pujar {}, 704e539891fSSameer Pujar }; 705e539891fSSameer Pujar MODULE_DEVICE_TABLE(of, tegra210_mvc_of_match); 706e539891fSSameer Pujar 707e539891fSSameer Pujar static int tegra210_mvc_platform_probe(struct platform_device *pdev) 708e539891fSSameer Pujar { 709e539891fSSameer Pujar struct device *dev = &pdev->dev; 710e539891fSSameer Pujar struct tegra210_mvc *mvc; 711e539891fSSameer Pujar void __iomem *regs; 712e539891fSSameer Pujar int err; 713e539891fSSameer Pujar 714e539891fSSameer Pujar mvc = devm_kzalloc(dev, sizeof(*mvc), GFP_KERNEL); 715e539891fSSameer Pujar if (!mvc) 716e539891fSSameer Pujar return -ENOMEM; 717e539891fSSameer Pujar 718e539891fSSameer Pujar dev_set_drvdata(dev, mvc); 719e539891fSSameer Pujar 720e539891fSSameer Pujar mvc->curve_type = CURVE_LINEAR; 721e539891fSSameer Pujar mvc->ctrl_value = TEGRA210_MVC_CTRL_DEFAULT; 722e539891fSSameer Pujar 723e539891fSSameer Pujar regs = devm_platform_ioremap_resource(pdev, 0); 724e539891fSSameer Pujar if (IS_ERR(regs)) 725e539891fSSameer Pujar return PTR_ERR(regs); 726e539891fSSameer Pujar 727e539891fSSameer Pujar mvc->regmap = devm_regmap_init_mmio(dev, regs, 728e539891fSSameer Pujar &tegra210_mvc_regmap_config); 729e539891fSSameer Pujar if (IS_ERR(mvc->regmap)) { 730e539891fSSameer Pujar dev_err(dev, "regmap init failed\n"); 731e539891fSSameer Pujar return PTR_ERR(mvc->regmap); 732e539891fSSameer Pujar } 733e539891fSSameer Pujar 734e539891fSSameer Pujar regcache_cache_only(mvc->regmap, true); 735e539891fSSameer Pujar 736e539891fSSameer Pujar err = devm_snd_soc_register_component(dev, &tegra210_mvc_cmpnt, 737e539891fSSameer Pujar tegra210_mvc_dais, 738e539891fSSameer Pujar ARRAY_SIZE(tegra210_mvc_dais)); 739e539891fSSameer Pujar if (err) { 740e539891fSSameer Pujar dev_err(dev, "can't register MVC component, err: %d\n", err); 741e539891fSSameer Pujar return err; 742e539891fSSameer Pujar } 743e539891fSSameer Pujar 744e539891fSSameer Pujar pm_runtime_enable(dev); 745e539891fSSameer Pujar 746e539891fSSameer Pujar tegra210_mvc_reset_vol_settings(mvc, &pdev->dev); 747e539891fSSameer Pujar 748e539891fSSameer Pujar return 0; 749e539891fSSameer Pujar } 750e539891fSSameer Pujar 751*f94195ffSUwe Kleine-König static void tegra210_mvc_platform_remove(struct platform_device *pdev) 752e539891fSSameer Pujar { 753e539891fSSameer Pujar pm_runtime_disable(&pdev->dev); 754e539891fSSameer Pujar } 755e539891fSSameer Pujar 756e539891fSSameer Pujar static const struct dev_pm_ops tegra210_mvc_pm_ops = { 757e539891fSSameer Pujar SET_RUNTIME_PM_OPS(tegra210_mvc_runtime_suspend, 758e539891fSSameer Pujar tegra210_mvc_runtime_resume, NULL) 759c83d263aSSameer Pujar SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 760e539891fSSameer Pujar pm_runtime_force_resume) 761e539891fSSameer Pujar }; 762e539891fSSameer Pujar 763e539891fSSameer Pujar static struct platform_driver tegra210_mvc_driver = { 764e539891fSSameer Pujar .driver = { 765e539891fSSameer Pujar .name = "tegra210-mvc", 766e539891fSSameer Pujar .of_match_table = tegra210_mvc_of_match, 767e539891fSSameer Pujar .pm = &tegra210_mvc_pm_ops, 768e539891fSSameer Pujar }, 769e539891fSSameer Pujar .probe = tegra210_mvc_platform_probe, 770*f94195ffSUwe Kleine-König .remove_new = tegra210_mvc_platform_remove, 771e539891fSSameer Pujar }; 772e539891fSSameer Pujar module_platform_driver(tegra210_mvc_driver) 773e539891fSSameer Pujar 774e539891fSSameer Pujar MODULE_AUTHOR("Arun Shamanna Lakshmi <aruns@nvidia.com>"); 775e539891fSSameer Pujar MODULE_DESCRIPTION("Tegra210 MVC ASoC driver"); 776e539891fSSameer Pujar MODULE_LICENSE("GPL v2"); 777