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