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