Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 //
0003 // tegra210_mvc.c - Tegra210 MVC driver
0004 //
0005 // Copyright (c) 2021 NVIDIA CORPORATION.  All rights reserved.
0006 
0007 #include <linux/clk.h>
0008 #include <linux/device.h>
0009 #include <linux/io.h>
0010 #include <linux/module.h>
0011 #include <linux/of.h>
0012 #include <linux/of_device.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/pm_runtime.h>
0015 #include <linux/regmap.h>
0016 #include <sound/core.h>
0017 #include <sound/pcm.h>
0018 #include <sound/pcm_params.h>
0019 #include <sound/soc.h>
0020 
0021 #include "tegra210_mvc.h"
0022 #include "tegra_cif.h"
0023 
0024 static const struct reg_default tegra210_mvc_reg_defaults[] = {
0025     { TEGRA210_MVC_RX_INT_MASK, 0x00000001},
0026     { TEGRA210_MVC_RX_CIF_CTRL, 0x00007700},
0027     { TEGRA210_MVC_TX_INT_MASK, 0x00000001},
0028     { TEGRA210_MVC_TX_CIF_CTRL, 0x00007700},
0029     { TEGRA210_MVC_CG, 0x1},
0030     { TEGRA210_MVC_CTRL, TEGRA210_MVC_CTRL_DEFAULT},
0031     { TEGRA210_MVC_INIT_VOL, 0x00800000},
0032     { TEGRA210_MVC_TARGET_VOL, 0x00800000},
0033     { TEGRA210_MVC_DURATION, 0x000012c0},
0034     { TEGRA210_MVC_DURATION_INV, 0x0006d3a0},
0035     { TEGRA210_MVC_POLY_N1, 0x0000007d},
0036     { TEGRA210_MVC_POLY_N2, 0x00000271},
0037     { TEGRA210_MVC_PEAK_CTRL, 0x000012c0},
0038     { TEGRA210_MVC_CFG_RAM_CTRL, 0x00004000},
0039 };
0040 
0041 static const struct tegra210_mvc_gain_params gain_params = {
0042     .poly_coeff = { 23738319, 659403, -3680,
0043             15546680, 2530732, -120985,
0044             12048422, 5527252, -785042 },
0045     .poly_n1 = 16,
0046     .poly_n2 = 63,
0047     .duration = 150,
0048     .duration_inv = 14316558,
0049 };
0050 
0051 static int __maybe_unused tegra210_mvc_runtime_suspend(struct device *dev)
0052 {
0053     struct tegra210_mvc *mvc = dev_get_drvdata(dev);
0054 
0055     regmap_read(mvc->regmap, TEGRA210_MVC_CTRL, &(mvc->ctrl_value));
0056 
0057     regcache_cache_only(mvc->regmap, true);
0058     regcache_mark_dirty(mvc->regmap);
0059 
0060     return 0;
0061 }
0062 
0063 static int __maybe_unused tegra210_mvc_runtime_resume(struct device *dev)
0064 {
0065     struct tegra210_mvc *mvc = dev_get_drvdata(dev);
0066 
0067     regcache_cache_only(mvc->regmap, false);
0068     regcache_sync(mvc->regmap);
0069 
0070     regmap_write(mvc->regmap, TEGRA210_MVC_CTRL, mvc->ctrl_value);
0071     regmap_update_bits(mvc->regmap,
0072                TEGRA210_MVC_SWITCH,
0073                TEGRA210_MVC_VOLUME_SWITCH_MASK,
0074                TEGRA210_MVC_VOLUME_SWITCH_TRIGGER);
0075 
0076     return 0;
0077 }
0078 
0079 static void tegra210_mvc_write_ram(struct regmap *regmap)
0080 {
0081     int i;
0082 
0083     regmap_write(regmap, TEGRA210_MVC_CFG_RAM_CTRL,
0084              TEGRA210_MVC_CFG_RAM_CTRL_SEQ_ACCESS_EN |
0085              TEGRA210_MVC_CFG_RAM_CTRL_ADDR_INIT_EN |
0086              TEGRA210_MVC_CFG_RAM_CTRL_RW_WRITE);
0087 
0088     for (i = 0; i < NUM_GAIN_POLY_COEFFS; i++)
0089         regmap_write(regmap, TEGRA210_MVC_CFG_RAM_DATA,
0090                  gain_params.poly_coeff[i]);
0091 }
0092 
0093 static void tegra210_mvc_conv_vol(struct tegra210_mvc *mvc, u8 chan, s32 val)
0094 {
0095     /*
0096      * Volume control read from mixer control is with
0097      * 100x scaling; for CURVE_POLY the reg range
0098      * is 0-100 (linear, Q24) and for CURVE_LINEAR
0099      * it is -120dB to +40dB (Q8)
0100      */
0101     if (mvc->curve_type == CURVE_POLY) {
0102         if (val > 10000)
0103             val = 10000;
0104         mvc->volume[chan] = ((val * (1<<8)) / 100) << 16;
0105     } else {
0106         val -= 12000;
0107         mvc->volume[chan] = (val * (1<<8)) / 100;
0108     }
0109 }
0110 
0111 static u32 tegra210_mvc_get_ctrl_reg(struct snd_kcontrol *kcontrol)
0112 {
0113     struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
0114     struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
0115     u32 val;
0116 
0117     pm_runtime_get_sync(cmpnt->dev);
0118     regmap_read(mvc->regmap, TEGRA210_MVC_CTRL, &val);
0119     pm_runtime_put(cmpnt->dev);
0120 
0121     return val;
0122 }
0123 
0124 static int tegra210_mvc_get_mute(struct snd_kcontrol *kcontrol,
0125                  struct snd_ctl_elem_value *ucontrol)
0126 {
0127     u32 val = tegra210_mvc_get_ctrl_reg(kcontrol);
0128     u8 mute_mask = TEGRA210_GET_MUTE_VAL(val);
0129 
0130     /*
0131      * If per channel control is enabled, then return
0132      * exact mute/unmute setting of all channels.
0133      *
0134      * Else report setting based on CH0 bit to reflect
0135      * the correct HW state.
0136      */
0137     if (val & TEGRA210_MVC_PER_CHAN_CTRL_EN) {
0138         ucontrol->value.integer.value[0] = mute_mask;
0139     } else {
0140         if (mute_mask & TEGRA210_MVC_CH0_MUTE_EN)
0141             ucontrol->value.integer.value[0] =
0142                 TEGRA210_MUTE_MASK_EN;
0143         else
0144             ucontrol->value.integer.value[0] = 0;
0145     }
0146 
0147     return 0;
0148 }
0149 
0150 static int tegra210_mvc_get_master_mute(struct snd_kcontrol *kcontrol,
0151                     struct snd_ctl_elem_value *ucontrol)
0152 {
0153     u32 val = tegra210_mvc_get_ctrl_reg(kcontrol);
0154     u8 mute_mask = TEGRA210_GET_MUTE_VAL(val);
0155 
0156     /*
0157      * If per channel control is disabled, then return
0158      * master mute/unmute setting based on CH0 bit.
0159      *
0160      * Else report settings based on state of all
0161      * channels.
0162      */
0163     if (!(val & TEGRA210_MVC_PER_CHAN_CTRL_EN)) {
0164         ucontrol->value.integer.value[0] =
0165             mute_mask & TEGRA210_MVC_CH0_MUTE_EN;
0166     } else {
0167         if (mute_mask == TEGRA210_MUTE_MASK_EN)
0168             ucontrol->value.integer.value[0] =
0169                 TEGRA210_MVC_CH0_MUTE_EN;
0170         else
0171             ucontrol->value.integer.value[0] = 0;
0172     }
0173 
0174     return 0;
0175 }
0176 
0177 static int tegra210_mvc_volume_switch_timeout(struct snd_soc_component *cmpnt)
0178 {
0179     struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
0180     u32 value;
0181     int err;
0182 
0183     err = regmap_read_poll_timeout(mvc->regmap, TEGRA210_MVC_SWITCH,
0184             value, !(value & TEGRA210_MVC_VOLUME_SWITCH_MASK),
0185             10, 10000);
0186     if (err < 0)
0187         dev_err(cmpnt->dev,
0188             "Volume switch trigger is still active, err = %d\n",
0189             err);
0190 
0191     return err;
0192 }
0193 
0194 static int tegra210_mvc_update_mute(struct snd_kcontrol *kcontrol,
0195                     struct snd_ctl_elem_value *ucontrol,
0196                     bool per_chan_ctrl)
0197 {
0198     struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
0199     struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
0200     u32 mute_val = ucontrol->value.integer.value[0];
0201     u32 per_ch_ctrl_val;
0202     bool change = false;
0203     int err;
0204 
0205     pm_runtime_get_sync(cmpnt->dev);
0206 
0207     err = tegra210_mvc_volume_switch_timeout(cmpnt);
0208     if (err < 0)
0209         goto end;
0210 
0211     if (per_chan_ctrl) {
0212         per_ch_ctrl_val = TEGRA210_MVC_PER_CHAN_CTRL_EN;
0213     } else {
0214         per_ch_ctrl_val = 0;
0215 
0216         if (mute_val)
0217             mute_val = TEGRA210_MUTE_MASK_EN;
0218     }
0219 
0220     regmap_update_bits_check(mvc->regmap, TEGRA210_MVC_CTRL,
0221                  TEGRA210_MVC_MUTE_MASK,
0222                  mute_val << TEGRA210_MVC_MUTE_SHIFT,
0223                  &change);
0224 
0225     if (change) {
0226         regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
0227                    TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK,
0228                    per_ch_ctrl_val);
0229 
0230         regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH,
0231                    TEGRA210_MVC_VOLUME_SWITCH_MASK,
0232                    TEGRA210_MVC_VOLUME_SWITCH_TRIGGER);
0233     }
0234 
0235 end:
0236     pm_runtime_put(cmpnt->dev);
0237 
0238     if (err < 0)
0239         return err;
0240 
0241     if (change)
0242         return 1;
0243 
0244     return 0;
0245 }
0246 
0247 static int tegra210_mvc_put_mute(struct snd_kcontrol *kcontrol,
0248                  struct snd_ctl_elem_value *ucontrol)
0249 {
0250     return tegra210_mvc_update_mute(kcontrol, ucontrol, true);
0251 }
0252 
0253 static int tegra210_mvc_put_master_mute(struct snd_kcontrol *kcontrol,
0254                     struct snd_ctl_elem_value *ucontrol)
0255 {
0256     return tegra210_mvc_update_mute(kcontrol, ucontrol, false);
0257 }
0258 
0259 static int tegra210_mvc_get_vol(struct snd_kcontrol *kcontrol,
0260                 struct snd_ctl_elem_value *ucontrol)
0261 {
0262     struct soc_mixer_control *mc =
0263         (struct soc_mixer_control *)kcontrol->private_value;
0264     struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
0265     struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
0266     u8 chan = TEGRA210_MVC_GET_CHAN(mc->reg, TEGRA210_MVC_TARGET_VOL);
0267     s32 val = mvc->volume[chan];
0268 
0269     if (mvc->curve_type == CURVE_POLY) {
0270         val = ((val >> 16) * 100) >> 8;
0271     } else {
0272         val = (val * 100) >> 8;
0273         val += 12000;
0274     }
0275 
0276     ucontrol->value.integer.value[0] = val;
0277 
0278     return 0;
0279 }
0280 
0281 static int tegra210_mvc_get_master_vol(struct snd_kcontrol *kcontrol,
0282                        struct snd_ctl_elem_value *ucontrol)
0283 {
0284     return tegra210_mvc_get_vol(kcontrol, ucontrol);
0285 }
0286 
0287 static int tegra210_mvc_update_vol(struct snd_kcontrol *kcontrol,
0288                    struct snd_ctl_elem_value *ucontrol,
0289                    bool per_ch_enable)
0290 {
0291     struct soc_mixer_control *mc =
0292         (struct soc_mixer_control *)kcontrol->private_value;
0293     struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
0294     struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
0295     u8 chan = TEGRA210_MVC_GET_CHAN(mc->reg, TEGRA210_MVC_TARGET_VOL);
0296     int old_volume = mvc->volume[chan];
0297     int err, i;
0298 
0299     pm_runtime_get_sync(cmpnt->dev);
0300 
0301     err = tegra210_mvc_volume_switch_timeout(cmpnt);
0302     if (err < 0)
0303         goto end;
0304 
0305     tegra210_mvc_conv_vol(mvc, chan, ucontrol->value.integer.value[0]);
0306 
0307     if (mvc->volume[chan] == old_volume) {
0308         err = 0;
0309         goto end;
0310     }
0311 
0312     if (per_ch_enable) {
0313         regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
0314                    TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK,
0315                    TEGRA210_MVC_PER_CHAN_CTRL_EN);
0316     } else {
0317         regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
0318                    TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK, 0);
0319 
0320         for (i = 1; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++)
0321             mvc->volume[i] = mvc->volume[chan];
0322     }
0323 
0324     /* Configure init volume same as target volume */
0325     regmap_write(mvc->regmap,
0326         TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_INIT_VOL, chan),
0327         mvc->volume[chan]);
0328 
0329     regmap_write(mvc->regmap, mc->reg, mvc->volume[chan]);
0330 
0331     regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH,
0332                TEGRA210_MVC_VOLUME_SWITCH_MASK,
0333                TEGRA210_MVC_VOLUME_SWITCH_TRIGGER);
0334 
0335     err = 1;
0336 
0337 end:
0338     pm_runtime_put(cmpnt->dev);
0339 
0340     return err;
0341 }
0342 
0343 static int tegra210_mvc_put_vol(struct snd_kcontrol *kcontrol,
0344                 struct snd_ctl_elem_value *ucontrol)
0345 {
0346     return tegra210_mvc_update_vol(kcontrol, ucontrol, true);
0347 }
0348 
0349 static int tegra210_mvc_put_master_vol(struct snd_kcontrol *kcontrol,
0350                        struct snd_ctl_elem_value *ucontrol)
0351 {
0352     return tegra210_mvc_update_vol(kcontrol, ucontrol, false);
0353 }
0354 
0355 static void tegra210_mvc_reset_vol_settings(struct tegra210_mvc *mvc,
0356                         struct device *dev)
0357 {
0358     int i;
0359 
0360     /* Change volume to default init for new curve type */
0361     if (mvc->curve_type == CURVE_POLY) {
0362         for (i = 0; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++)
0363             mvc->volume[i] = TEGRA210_MVC_INIT_VOL_DEFAULT_POLY;
0364     } else {
0365         for (i = 0; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++)
0366             mvc->volume[i] = TEGRA210_MVC_INIT_VOL_DEFAULT_LINEAR;
0367     }
0368 
0369     pm_runtime_get_sync(dev);
0370 
0371     /* Program curve type */
0372     regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
0373                TEGRA210_MVC_CURVE_TYPE_MASK,
0374                mvc->curve_type <<
0375                TEGRA210_MVC_CURVE_TYPE_SHIFT);
0376 
0377     /* Init volume for all channels */
0378     for (i = 0; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++) {
0379         regmap_write(mvc->regmap,
0380             TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_INIT_VOL, i),
0381             mvc->volume[i]);
0382         regmap_write(mvc->regmap,
0383             TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_TARGET_VOL, i),
0384             mvc->volume[i]);
0385     }
0386 
0387     /* Trigger volume switch */
0388     regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH,
0389                TEGRA210_MVC_VOLUME_SWITCH_MASK,
0390                TEGRA210_MVC_VOLUME_SWITCH_TRIGGER);
0391 
0392     pm_runtime_put(dev);
0393 }
0394 
0395 static int tegra210_mvc_get_curve_type(struct snd_kcontrol *kcontrol,
0396                        struct snd_ctl_elem_value *ucontrol)
0397 {
0398     struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
0399     struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
0400 
0401     ucontrol->value.enumerated.item[0] = mvc->curve_type;
0402 
0403     return 0;
0404 }
0405 
0406 static int tegra210_mvc_put_curve_type(struct snd_kcontrol *kcontrol,
0407                        struct snd_ctl_elem_value *ucontrol)
0408 {
0409     struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
0410     struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt);
0411     unsigned int value;
0412 
0413     regmap_read(mvc->regmap, TEGRA210_MVC_ENABLE, &value);
0414     if (value & TEGRA210_MVC_EN) {
0415         dev_err(cmpnt->dev,
0416             "Curve type can't be set when MVC is running\n");
0417         return -EINVAL;
0418     }
0419 
0420     if (mvc->curve_type == ucontrol->value.enumerated.item[0])
0421         return 0;
0422 
0423     mvc->curve_type = ucontrol->value.enumerated.item[0];
0424 
0425     tegra210_mvc_reset_vol_settings(mvc, cmpnt->dev);
0426 
0427     return 1;
0428 }
0429 
0430 static int tegra210_mvc_set_audio_cif(struct tegra210_mvc *mvc,
0431                       struct snd_pcm_hw_params *params,
0432                       unsigned int reg)
0433 {
0434     unsigned int channels, audio_bits;
0435     struct tegra_cif_conf cif_conf;
0436 
0437     memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));
0438 
0439     channels = params_channels(params);
0440 
0441     switch (params_format(params)) {
0442     case SNDRV_PCM_FORMAT_S16_LE:
0443         audio_bits = TEGRA_ACIF_BITS_16;
0444         break;
0445     case SNDRV_PCM_FORMAT_S32_LE:
0446         audio_bits = TEGRA_ACIF_BITS_32;
0447         break;
0448     default:
0449         return -EINVAL;
0450     }
0451 
0452     cif_conf.audio_ch = channels;
0453     cif_conf.client_ch = channels;
0454     cif_conf.audio_bits = audio_bits;
0455     cif_conf.client_bits = audio_bits;
0456 
0457     tegra_set_cif(mvc->regmap, reg, &cif_conf);
0458 
0459     return 0;
0460 }
0461 
0462 static int tegra210_mvc_hw_params(struct snd_pcm_substream *substream,
0463                   struct snd_pcm_hw_params *params,
0464                   struct snd_soc_dai *dai)
0465 {
0466     struct device *dev = dai->dev;
0467     struct tegra210_mvc *mvc = snd_soc_dai_get_drvdata(dai);
0468     int err, val;
0469 
0470     /*
0471      * Soft Reset: Below performs module soft reset which clears
0472      * all FSM logic, flushes flow control of FIFO and resets the
0473      * state register. It also brings module back to disabled
0474      * state (without flushing the data in the pipe).
0475      */
0476     regmap_write(mvc->regmap, TEGRA210_MVC_SOFT_RESET, 1);
0477 
0478     err = regmap_read_poll_timeout(mvc->regmap, TEGRA210_MVC_SOFT_RESET,
0479                        val, !val, 10, 10000);
0480     if (err < 0) {
0481         dev_err(dev, "SW reset failed, err = %d\n", err);
0482         return err;
0483     }
0484 
0485     /* Set RX CIF */
0486     err = tegra210_mvc_set_audio_cif(mvc, params, TEGRA210_MVC_RX_CIF_CTRL);
0487     if (err) {
0488         dev_err(dev, "Can't set MVC RX CIF: %d\n", err);
0489         return err;
0490     }
0491 
0492     /* Set TX CIF */
0493     err = tegra210_mvc_set_audio_cif(mvc, params, TEGRA210_MVC_TX_CIF_CTRL);
0494     if (err) {
0495         dev_err(dev, "Can't set MVC TX CIF: %d\n", err);
0496         return err;
0497     }
0498 
0499     tegra210_mvc_write_ram(mvc->regmap);
0500 
0501     /* Program poly_n1, poly_n2, duration */
0502     regmap_write(mvc->regmap, TEGRA210_MVC_POLY_N1, gain_params.poly_n1);
0503     regmap_write(mvc->regmap, TEGRA210_MVC_POLY_N2, gain_params.poly_n2);
0504     regmap_write(mvc->regmap, TEGRA210_MVC_DURATION, gain_params.duration);
0505 
0506     /* Program duration_inv */
0507     regmap_write(mvc->regmap, TEGRA210_MVC_DURATION_INV,
0508              gain_params.duration_inv);
0509 
0510     return 0;
0511 }
0512 
0513 static const struct snd_soc_dai_ops tegra210_mvc_dai_ops = {
0514     .hw_params  = tegra210_mvc_hw_params,
0515 };
0516 
0517 static const char * const tegra210_mvc_curve_type_text[] = {
0518     "Poly",
0519     "Linear",
0520 };
0521 
0522 static const struct soc_enum tegra210_mvc_curve_type_ctrl =
0523     SOC_ENUM_SINGLE_EXT(2, tegra210_mvc_curve_type_text);
0524 
0525 #define TEGRA210_MVC_VOL_CTRL(chan)                 \
0526     SOC_SINGLE_EXT("Channel" #chan " Volume",           \
0527                TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_TARGET_VOL, \
0528                            (chan - 1)),     \
0529                0, 16000, 0, tegra210_mvc_get_vol,       \
0530                tegra210_mvc_put_vol)
0531 
0532 static const struct snd_kcontrol_new tegra210_mvc_vol_ctrl[] = {
0533     /* Per channel volume control */
0534     TEGRA210_MVC_VOL_CTRL(1),
0535     TEGRA210_MVC_VOL_CTRL(2),
0536     TEGRA210_MVC_VOL_CTRL(3),
0537     TEGRA210_MVC_VOL_CTRL(4),
0538     TEGRA210_MVC_VOL_CTRL(5),
0539     TEGRA210_MVC_VOL_CTRL(6),
0540     TEGRA210_MVC_VOL_CTRL(7),
0541     TEGRA210_MVC_VOL_CTRL(8),
0542 
0543     /* Per channel mute */
0544     SOC_SINGLE_EXT("Per Chan Mute Mask",
0545                TEGRA210_MVC_CTRL, 0, TEGRA210_MUTE_MASK_EN, 0,
0546                tegra210_mvc_get_mute, tegra210_mvc_put_mute),
0547 
0548     /* Master volume */
0549     SOC_SINGLE_EXT("Volume", TEGRA210_MVC_TARGET_VOL, 0, 16000, 0,
0550                tegra210_mvc_get_master_vol,
0551                tegra210_mvc_put_master_vol),
0552 
0553     /* Master mute */
0554     SOC_SINGLE_EXT("Mute", TEGRA210_MVC_CTRL, 0, 1, 0,
0555                tegra210_mvc_get_master_mute,
0556                tegra210_mvc_put_master_mute),
0557 
0558     SOC_ENUM_EXT("Curve Type", tegra210_mvc_curve_type_ctrl,
0559              tegra210_mvc_get_curve_type, tegra210_mvc_put_curve_type),
0560 };
0561 
0562 static struct snd_soc_dai_driver tegra210_mvc_dais[] = {
0563     /* Input */
0564     {
0565         .name = "MVC-RX-CIF",
0566         .playback = {
0567             .stream_name = "RX-CIF-Playback",
0568             .channels_min = 1,
0569             .channels_max = 8,
0570             .rates = SNDRV_PCM_RATE_8000_192000,
0571             .formats = SNDRV_PCM_FMTBIT_S8 |
0572                 SNDRV_PCM_FMTBIT_S16_LE |
0573                 SNDRV_PCM_FMTBIT_S32_LE,
0574         },
0575         .capture = {
0576             .stream_name = "RX-CIF-Capture",
0577             .channels_min = 1,
0578             .channels_max = 8,
0579             .rates = SNDRV_PCM_RATE_8000_192000,
0580             .formats = SNDRV_PCM_FMTBIT_S8 |
0581                 SNDRV_PCM_FMTBIT_S16_LE |
0582                 SNDRV_PCM_FMTBIT_S32_LE,
0583         },
0584     },
0585 
0586     /* Output */
0587     {
0588         .name = "MVC-TX-CIF",
0589         .playback = {
0590             .stream_name = "TX-CIF-Playback",
0591             .channels_min = 1,
0592             .channels_max = 8,
0593             .rates = SNDRV_PCM_RATE_8000_192000,
0594             .formats = SNDRV_PCM_FMTBIT_S8 |
0595                 SNDRV_PCM_FMTBIT_S16_LE |
0596                 SNDRV_PCM_FMTBIT_S32_LE,
0597         },
0598         .capture = {
0599             .stream_name = "TX-CIF-Capture",
0600             .channels_min = 1,
0601             .channels_max = 8,
0602             .rates = SNDRV_PCM_RATE_8000_192000,
0603             .formats = SNDRV_PCM_FMTBIT_S8 |
0604                 SNDRV_PCM_FMTBIT_S16_LE |
0605                 SNDRV_PCM_FMTBIT_S32_LE,
0606         },
0607         .ops = &tegra210_mvc_dai_ops,
0608     }
0609 };
0610 
0611 static const struct snd_soc_dapm_widget tegra210_mvc_widgets[] = {
0612     SND_SOC_DAPM_AIF_IN("RX", NULL, 0, SND_SOC_NOPM, 0, 0),
0613     SND_SOC_DAPM_AIF_OUT("TX", NULL, 0, TEGRA210_MVC_ENABLE,
0614                  TEGRA210_MVC_EN_SHIFT, 0),
0615 };
0616 
0617 #define MVC_ROUTES(sname)                   \
0618     { "RX XBAR-" sname, NULL,   "XBAR-TX" },        \
0619     { "RX-CIF-" sname,  NULL,   "RX XBAR-" sname }, \
0620     { "RX",         NULL,   "RX-CIF-" sname },  \
0621     { "TX-CIF-" sname,  NULL,   "TX" },         \
0622     { "TX XBAR-" sname, NULL,   "TX-CIF-" sname },  \
0623     { "XBAR-RX",            NULL,   "TX XBAR-" sname }
0624 
0625 static const struct snd_soc_dapm_route tegra210_mvc_routes[] = {
0626     { "TX", NULL, "RX" },
0627     MVC_ROUTES("Playback"),
0628     MVC_ROUTES("Capture"),
0629 };
0630 
0631 static const struct snd_soc_component_driver tegra210_mvc_cmpnt = {
0632     .dapm_widgets       = tegra210_mvc_widgets,
0633     .num_dapm_widgets   = ARRAY_SIZE(tegra210_mvc_widgets),
0634     .dapm_routes        = tegra210_mvc_routes,
0635     .num_dapm_routes    = ARRAY_SIZE(tegra210_mvc_routes),
0636     .controls       = tegra210_mvc_vol_ctrl,
0637     .num_controls       = ARRAY_SIZE(tegra210_mvc_vol_ctrl),
0638 };
0639 
0640 static bool tegra210_mvc_rd_reg(struct device *dev, unsigned int reg)
0641 {
0642     switch (reg) {
0643     case TEGRA210_MVC_RX_STATUS ... TEGRA210_MVC_CONFIG_ERR_TYPE:
0644         return true;
0645     default:
0646         return false;
0647     };
0648 }
0649 
0650 static bool tegra210_mvc_wr_reg(struct device *dev, unsigned int reg)
0651 {
0652     switch (reg) {
0653     case TEGRA210_MVC_RX_INT_MASK ... TEGRA210_MVC_RX_CIF_CTRL:
0654     case TEGRA210_MVC_TX_INT_MASK ... TEGRA210_MVC_TX_CIF_CTRL:
0655     case TEGRA210_MVC_ENABLE ... TEGRA210_MVC_CG:
0656     case TEGRA210_MVC_CTRL ... TEGRA210_MVC_CFG_RAM_DATA:
0657         return true;
0658     default:
0659         return false;
0660     }
0661 }
0662 
0663 static bool tegra210_mvc_volatile_reg(struct device *dev, unsigned int reg)
0664 {
0665     switch (reg) {
0666     case TEGRA210_MVC_RX_STATUS:
0667     case TEGRA210_MVC_RX_INT_STATUS:
0668     case TEGRA210_MVC_RX_INT_SET:
0669 
0670     case TEGRA210_MVC_TX_STATUS:
0671     case TEGRA210_MVC_TX_INT_STATUS:
0672     case TEGRA210_MVC_TX_INT_SET:
0673 
0674     case TEGRA210_MVC_SOFT_RESET:
0675     case TEGRA210_MVC_STATUS:
0676     case TEGRA210_MVC_INT_STATUS:
0677     case TEGRA210_MVC_SWITCH:
0678     case TEGRA210_MVC_CFG_RAM_CTRL:
0679     case TEGRA210_MVC_CFG_RAM_DATA:
0680     case TEGRA210_MVC_PEAK_VALUE:
0681     case TEGRA210_MVC_CTRL:
0682         return true;
0683     default:
0684         return false;
0685     }
0686 }
0687 
0688 static const struct regmap_config tegra210_mvc_regmap_config = {
0689     .reg_bits       = 32,
0690     .reg_stride     = 4,
0691     .val_bits       = 32,
0692     .max_register       = TEGRA210_MVC_CONFIG_ERR_TYPE,
0693     .writeable_reg      = tegra210_mvc_wr_reg,
0694     .readable_reg       = tegra210_mvc_rd_reg,
0695     .volatile_reg       = tegra210_mvc_volatile_reg,
0696     .reg_defaults       = tegra210_mvc_reg_defaults,
0697     .num_reg_defaults   = ARRAY_SIZE(tegra210_mvc_reg_defaults),
0698     .cache_type     = REGCACHE_FLAT,
0699 };
0700 
0701 static const struct of_device_id tegra210_mvc_of_match[] = {
0702     { .compatible = "nvidia,tegra210-mvc" },
0703     {},
0704 };
0705 MODULE_DEVICE_TABLE(of, tegra210_mvc_of_match);
0706 
0707 static int tegra210_mvc_platform_probe(struct platform_device *pdev)
0708 {
0709     struct device *dev = &pdev->dev;
0710     struct tegra210_mvc *mvc;
0711     void __iomem *regs;
0712     int err;
0713 
0714     mvc = devm_kzalloc(dev, sizeof(*mvc), GFP_KERNEL);
0715     if (!mvc)
0716         return -ENOMEM;
0717 
0718     dev_set_drvdata(dev, mvc);
0719 
0720     mvc->curve_type = CURVE_LINEAR;
0721     mvc->ctrl_value = TEGRA210_MVC_CTRL_DEFAULT;
0722 
0723     regs = devm_platform_ioremap_resource(pdev, 0);
0724     if (IS_ERR(regs))
0725         return PTR_ERR(regs);
0726 
0727     mvc->regmap = devm_regmap_init_mmio(dev, regs,
0728                         &tegra210_mvc_regmap_config);
0729     if (IS_ERR(mvc->regmap)) {
0730         dev_err(dev, "regmap init failed\n");
0731         return PTR_ERR(mvc->regmap);
0732     }
0733 
0734     regcache_cache_only(mvc->regmap, true);
0735 
0736     err = devm_snd_soc_register_component(dev, &tegra210_mvc_cmpnt,
0737                           tegra210_mvc_dais,
0738                           ARRAY_SIZE(tegra210_mvc_dais));
0739     if (err) {
0740         dev_err(dev, "can't register MVC component, err: %d\n", err);
0741         return err;
0742     }
0743 
0744     pm_runtime_enable(dev);
0745 
0746     tegra210_mvc_reset_vol_settings(mvc, &pdev->dev);
0747 
0748     return 0;
0749 }
0750 
0751 static int tegra210_mvc_platform_remove(struct platform_device *pdev)
0752 {
0753     pm_runtime_disable(&pdev->dev);
0754 
0755     return 0;
0756 }
0757 
0758 static const struct dev_pm_ops tegra210_mvc_pm_ops = {
0759     SET_RUNTIME_PM_OPS(tegra210_mvc_runtime_suspend,
0760                tegra210_mvc_runtime_resume, NULL)
0761     SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
0762                 pm_runtime_force_resume)
0763 };
0764 
0765 static struct platform_driver tegra210_mvc_driver = {
0766     .driver = {
0767         .name = "tegra210-mvc",
0768         .of_match_table = tegra210_mvc_of_match,
0769         .pm = &tegra210_mvc_pm_ops,
0770     },
0771     .probe = tegra210_mvc_platform_probe,
0772     .remove = tegra210_mvc_platform_remove,
0773 };
0774 module_platform_driver(tegra210_mvc_driver)
0775 
0776 MODULE_AUTHOR("Arun Shamanna Lakshmi <aruns@nvidia.com>");
0777 MODULE_DESCRIPTION("Tegra210 MVC ASoC driver");
0778 MODULE_LICENSE("GPL v2");