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 mute_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 mute_mask = ucontrol->value.integer.value[0]; 152 153 err = regmap_update_bits(mvc->regmap, mc->reg, 154 TEGRA210_MVC_MUTE_MASK, 155 mute_mask << TEGRA210_MVC_MUTE_SHIFT); 156 if (err < 0) 157 goto end; 158 159 return 1; 160 161 end: 162 pm_runtime_put(cmpnt->dev); 163 return err; 164 } 165 166 static int tegra210_mvc_get_vol(struct snd_kcontrol *kcontrol, 167 struct snd_ctl_elem_value *ucontrol) 168 { 169 struct soc_mixer_control *mc = 170 (struct soc_mixer_control *)kcontrol->private_value; 171 struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); 172 struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); 173 u8 chan = (mc->reg - TEGRA210_MVC_TARGET_VOL) / REG_SIZE; 174 s32 val = mvc->volume[chan]; 175 176 if (mvc->curve_type == CURVE_POLY) { 177 val = ((val >> 16) * 100) >> 8; 178 } else { 179 val = (val * 100) >> 8; 180 val += 12000; 181 } 182 183 ucontrol->value.integer.value[0] = val; 184 185 return 0; 186 } 187 188 static int tegra210_mvc_put_vol(struct snd_kcontrol *kcontrol, 189 struct snd_ctl_elem_value *ucontrol) 190 { 191 struct soc_mixer_control *mc = 192 (struct soc_mixer_control *)kcontrol->private_value; 193 struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); 194 struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); 195 unsigned int reg = mc->reg; 196 unsigned int value; 197 u8 chan; 198 int err; 199 200 pm_runtime_get_sync(cmpnt->dev); 201 202 /* Check if VOLUME_SWITCH is triggered */ 203 err = regmap_read_poll_timeout(mvc->regmap, TEGRA210_MVC_SWITCH, 204 value, !(value & TEGRA210_MVC_VOLUME_SWITCH_MASK), 205 10, 10000); 206 if (err < 0) 207 goto end; 208 209 chan = (reg - TEGRA210_MVC_TARGET_VOL) / REG_SIZE; 210 211 tegra210_mvc_conv_vol(mvc, chan, 212 ucontrol->value.integer.value[0]); 213 214 /* Configure init volume same as target volume */ 215 regmap_write(mvc->regmap, 216 TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_INIT_VOL, chan), 217 mvc->volume[chan]); 218 219 regmap_write(mvc->regmap, reg, mvc->volume[chan]); 220 221 regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH, 222 TEGRA210_MVC_VOLUME_SWITCH_MASK, 223 TEGRA210_MVC_VOLUME_SWITCH_TRIGGER); 224 225 return 1; 226 227 end: 228 pm_runtime_put(cmpnt->dev); 229 return err; 230 } 231 232 static void tegra210_mvc_reset_vol_settings(struct tegra210_mvc *mvc, 233 struct device *dev) 234 { 235 int i; 236 237 /* Change volume to default init for new curve type */ 238 if (mvc->curve_type == CURVE_POLY) { 239 for (i = 0; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++) 240 mvc->volume[i] = TEGRA210_MVC_INIT_VOL_DEFAULT_POLY; 241 } else { 242 for (i = 0; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++) 243 mvc->volume[i] = TEGRA210_MVC_INIT_VOL_DEFAULT_LINEAR; 244 } 245 246 pm_runtime_get_sync(dev); 247 248 /* Program curve type */ 249 regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL, 250 TEGRA210_MVC_CURVE_TYPE_MASK, 251 mvc->curve_type << 252 TEGRA210_MVC_CURVE_TYPE_SHIFT); 253 254 /* Init volume for all channels */ 255 for (i = 0; i < TEGRA210_MVC_MAX_CHAN_COUNT; i++) { 256 regmap_write(mvc->regmap, 257 TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_INIT_VOL, i), 258 mvc->volume[i]); 259 regmap_write(mvc->regmap, 260 TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_TARGET_VOL, i), 261 mvc->volume[i]); 262 } 263 264 /* Trigger volume switch */ 265 regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH, 266 TEGRA210_MVC_VOLUME_SWITCH_MASK, 267 TEGRA210_MVC_VOLUME_SWITCH_TRIGGER); 268 269 pm_runtime_put(dev); 270 } 271 272 static int tegra210_mvc_get_curve_type(struct snd_kcontrol *kcontrol, 273 struct snd_ctl_elem_value *ucontrol) 274 { 275 struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); 276 struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); 277 278 ucontrol->value.integer.value[0] = mvc->curve_type; 279 280 return 0; 281 } 282 283 static int tegra210_mvc_put_curve_type(struct snd_kcontrol *kcontrol, 284 struct snd_ctl_elem_value *ucontrol) 285 { 286 struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); 287 struct tegra210_mvc *mvc = snd_soc_component_get_drvdata(cmpnt); 288 int value; 289 290 regmap_read(mvc->regmap, TEGRA210_MVC_ENABLE, &value); 291 if (value & TEGRA210_MVC_EN) { 292 dev_err(cmpnt->dev, 293 "Curve type can't be set when MVC is running\n"); 294 return -EINVAL; 295 } 296 297 if (mvc->curve_type == ucontrol->value.integer.value[0]) 298 return 0; 299 300 mvc->curve_type = ucontrol->value.integer.value[0]; 301 302 tegra210_mvc_reset_vol_settings(mvc, cmpnt->dev); 303 304 return 1; 305 } 306 307 static int tegra210_mvc_set_audio_cif(struct tegra210_mvc *mvc, 308 struct snd_pcm_hw_params *params, 309 unsigned int reg) 310 { 311 unsigned int channels, audio_bits; 312 struct tegra_cif_conf cif_conf; 313 314 memset(&cif_conf, 0, sizeof(struct tegra_cif_conf)); 315 316 channels = params_channels(params); 317 318 switch (params_format(params)) { 319 case SNDRV_PCM_FORMAT_S16_LE: 320 audio_bits = TEGRA_ACIF_BITS_16; 321 break; 322 case SNDRV_PCM_FORMAT_S32_LE: 323 audio_bits = TEGRA_ACIF_BITS_32; 324 break; 325 default: 326 return -EINVAL; 327 } 328 329 cif_conf.audio_ch = channels; 330 cif_conf.client_ch = channels; 331 cif_conf.audio_bits = audio_bits; 332 cif_conf.client_bits = audio_bits; 333 334 tegra_set_cif(mvc->regmap, reg, &cif_conf); 335 336 return 0; 337 } 338 339 static int tegra210_mvc_hw_params(struct snd_pcm_substream *substream, 340 struct snd_pcm_hw_params *params, 341 struct snd_soc_dai *dai) 342 { 343 struct device *dev = dai->dev; 344 struct tegra210_mvc *mvc = snd_soc_dai_get_drvdata(dai); 345 int err, val; 346 347 /* 348 * Soft Reset: Below performs module soft reset which clears 349 * all FSM logic, flushes flow control of FIFO and resets the 350 * state register. It also brings module back to disabled 351 * state (without flushing the data in the pipe). 352 */ 353 regmap_write(mvc->regmap, TEGRA210_MVC_SOFT_RESET, 1); 354 355 err = regmap_read_poll_timeout(mvc->regmap, TEGRA210_MVC_SOFT_RESET, 356 val, !val, 10, 10000); 357 if (err < 0) { 358 dev_err(dev, "SW reset failed, err = %d\n", err); 359 return err; 360 } 361 362 /* Set RX CIF */ 363 err = tegra210_mvc_set_audio_cif(mvc, params, TEGRA210_MVC_RX_CIF_CTRL); 364 if (err) { 365 dev_err(dev, "Can't set MVC RX CIF: %d\n", err); 366 return err; 367 } 368 369 /* Set TX CIF */ 370 err = tegra210_mvc_set_audio_cif(mvc, params, TEGRA210_MVC_TX_CIF_CTRL); 371 if (err) { 372 dev_err(dev, "Can't set MVC TX CIF: %d\n", err); 373 return err; 374 } 375 376 tegra210_mvc_write_ram(mvc->regmap); 377 378 /* Program poly_n1, poly_n2, duration */ 379 regmap_write(mvc->regmap, TEGRA210_MVC_POLY_N1, gain_params.poly_n1); 380 regmap_write(mvc->regmap, TEGRA210_MVC_POLY_N2, gain_params.poly_n2); 381 regmap_write(mvc->regmap, TEGRA210_MVC_DURATION, gain_params.duration); 382 383 /* Program duration_inv */ 384 regmap_write(mvc->regmap, TEGRA210_MVC_DURATION_INV, 385 gain_params.duration_inv); 386 387 return 0; 388 } 389 390 static const struct snd_soc_dai_ops tegra210_mvc_dai_ops = { 391 .hw_params = tegra210_mvc_hw_params, 392 }; 393 394 static const char * const tegra210_mvc_curve_type_text[] = { 395 "Poly", 396 "Linear", 397 }; 398 399 static const struct soc_enum tegra210_mvc_curve_type_ctrl = 400 SOC_ENUM_SINGLE_EXT(2, tegra210_mvc_curve_type_text); 401 402 #define TEGRA210_MVC_VOL_CTRL(chan) \ 403 SOC_SINGLE_EXT("Channel" #chan " Volume", \ 404 TEGRA210_MVC_REG_OFFSET(TEGRA210_MVC_TARGET_VOL, \ 405 (chan - 1)), \ 406 0, 16000, 0, tegra210_mvc_get_vol, \ 407 tegra210_mvc_put_vol) 408 409 static const struct snd_kcontrol_new tegra210_mvc_vol_ctrl[] = { 410 /* Per channel volume control */ 411 TEGRA210_MVC_VOL_CTRL(1), 412 TEGRA210_MVC_VOL_CTRL(2), 413 TEGRA210_MVC_VOL_CTRL(3), 414 TEGRA210_MVC_VOL_CTRL(4), 415 TEGRA210_MVC_VOL_CTRL(5), 416 TEGRA210_MVC_VOL_CTRL(6), 417 TEGRA210_MVC_VOL_CTRL(7), 418 TEGRA210_MVC_VOL_CTRL(8), 419 420 /* Per channel mute */ 421 SOC_SINGLE_EXT("Per Chan Mute Mask", 422 TEGRA210_MVC_CTRL, 0, TEGRA210_MUTE_MASK_EN, 0, 423 tegra210_mvc_get_mute, tegra210_mvc_put_mute), 424 425 SOC_ENUM_EXT("Curve Type", tegra210_mvc_curve_type_ctrl, 426 tegra210_mvc_get_curve_type, tegra210_mvc_put_curve_type), 427 }; 428 429 static struct snd_soc_dai_driver tegra210_mvc_dais[] = { 430 /* Input */ 431 { 432 .name = "MVC-RX-CIF", 433 .playback = { 434 .stream_name = "RX-CIF-Playback", 435 .channels_min = 1, 436 .channels_max = 8, 437 .rates = SNDRV_PCM_RATE_8000_192000, 438 .formats = SNDRV_PCM_FMTBIT_S8 | 439 SNDRV_PCM_FMTBIT_S16_LE | 440 SNDRV_PCM_FMTBIT_S32_LE, 441 }, 442 .capture = { 443 .stream_name = "RX-CIF-Capture", 444 .channels_min = 1, 445 .channels_max = 8, 446 .rates = SNDRV_PCM_RATE_8000_192000, 447 .formats = SNDRV_PCM_FMTBIT_S8 | 448 SNDRV_PCM_FMTBIT_S16_LE | 449 SNDRV_PCM_FMTBIT_S32_LE, 450 }, 451 }, 452 453 /* Output */ 454 { 455 .name = "MVC-TX-CIF", 456 .playback = { 457 .stream_name = "TX-CIF-Playback", 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 .capture = { 466 .stream_name = "TX-CIF-Capture", 467 .channels_min = 1, 468 .channels_max = 8, 469 .rates = SNDRV_PCM_RATE_8000_192000, 470 .formats = SNDRV_PCM_FMTBIT_S8 | 471 SNDRV_PCM_FMTBIT_S16_LE | 472 SNDRV_PCM_FMTBIT_S32_LE, 473 }, 474 .ops = &tegra210_mvc_dai_ops, 475 } 476 }; 477 478 static const struct snd_soc_dapm_widget tegra210_mvc_widgets[] = { 479 SND_SOC_DAPM_AIF_IN("RX", NULL, 0, SND_SOC_NOPM, 0, 0), 480 SND_SOC_DAPM_AIF_OUT("TX", NULL, 0, TEGRA210_MVC_ENABLE, 481 TEGRA210_MVC_EN_SHIFT, 0), 482 }; 483 484 #define MVC_ROUTES(sname) \ 485 { "RX XBAR-" sname, NULL, "XBAR-TX" }, \ 486 { "RX-CIF-" sname, NULL, "RX XBAR-" sname }, \ 487 { "RX", NULL, "RX-CIF-" sname }, \ 488 { "TX-CIF-" sname, NULL, "TX" }, \ 489 { "TX XBAR-" sname, NULL, "TX-CIF-" sname }, \ 490 { "XBAR-RX", NULL, "TX XBAR-" sname } 491 492 static const struct snd_soc_dapm_route tegra210_mvc_routes[] = { 493 { "TX", NULL, "RX" }, 494 MVC_ROUTES("Playback"), 495 MVC_ROUTES("Capture"), 496 }; 497 498 static const struct snd_soc_component_driver tegra210_mvc_cmpnt = { 499 .dapm_widgets = tegra210_mvc_widgets, 500 .num_dapm_widgets = ARRAY_SIZE(tegra210_mvc_widgets), 501 .dapm_routes = tegra210_mvc_routes, 502 .num_dapm_routes = ARRAY_SIZE(tegra210_mvc_routes), 503 .controls = tegra210_mvc_vol_ctrl, 504 .num_controls = ARRAY_SIZE(tegra210_mvc_vol_ctrl), 505 }; 506 507 static bool tegra210_mvc_rd_reg(struct device *dev, unsigned int reg) 508 { 509 switch (reg) { 510 case TEGRA210_MVC_RX_STATUS ... TEGRA210_MVC_CONFIG_ERR_TYPE: 511 return true; 512 default: 513 return false; 514 }; 515 } 516 517 static bool tegra210_mvc_wr_reg(struct device *dev, unsigned int reg) 518 { 519 switch (reg) { 520 case TEGRA210_MVC_RX_INT_MASK ... TEGRA210_MVC_RX_CIF_CTRL: 521 case TEGRA210_MVC_TX_INT_MASK ... TEGRA210_MVC_TX_CIF_CTRL: 522 case TEGRA210_MVC_ENABLE ... TEGRA210_MVC_CG: 523 case TEGRA210_MVC_CTRL ... TEGRA210_MVC_CFG_RAM_DATA: 524 return true; 525 default: 526 return false; 527 } 528 } 529 530 static bool tegra210_mvc_volatile_reg(struct device *dev, unsigned int reg) 531 { 532 switch (reg) { 533 case TEGRA210_MVC_RX_STATUS: 534 case TEGRA210_MVC_RX_INT_STATUS: 535 case TEGRA210_MVC_RX_INT_SET: 536 537 case TEGRA210_MVC_TX_STATUS: 538 case TEGRA210_MVC_TX_INT_STATUS: 539 case TEGRA210_MVC_TX_INT_SET: 540 541 case TEGRA210_MVC_SOFT_RESET: 542 case TEGRA210_MVC_STATUS: 543 case TEGRA210_MVC_INT_STATUS: 544 case TEGRA210_MVC_SWITCH: 545 case TEGRA210_MVC_CFG_RAM_CTRL: 546 case TEGRA210_MVC_CFG_RAM_DATA: 547 case TEGRA210_MVC_PEAK_VALUE: 548 case TEGRA210_MVC_CTRL: 549 return true; 550 default: 551 return false; 552 } 553 } 554 555 static const struct regmap_config tegra210_mvc_regmap_config = { 556 .reg_bits = 32, 557 .reg_stride = 4, 558 .val_bits = 32, 559 .max_register = TEGRA210_MVC_CONFIG_ERR_TYPE, 560 .writeable_reg = tegra210_mvc_wr_reg, 561 .readable_reg = tegra210_mvc_rd_reg, 562 .volatile_reg = tegra210_mvc_volatile_reg, 563 .reg_defaults = tegra210_mvc_reg_defaults, 564 .num_reg_defaults = ARRAY_SIZE(tegra210_mvc_reg_defaults), 565 .cache_type = REGCACHE_FLAT, 566 }; 567 568 static const struct of_device_id tegra210_mvc_of_match[] = { 569 { .compatible = "nvidia,tegra210-mvc" }, 570 {}, 571 }; 572 MODULE_DEVICE_TABLE(of, tegra210_mvc_of_match); 573 574 static int tegra210_mvc_platform_probe(struct platform_device *pdev) 575 { 576 struct device *dev = &pdev->dev; 577 struct tegra210_mvc *mvc; 578 void __iomem *regs; 579 int err; 580 581 mvc = devm_kzalloc(dev, sizeof(*mvc), GFP_KERNEL); 582 if (!mvc) 583 return -ENOMEM; 584 585 dev_set_drvdata(dev, mvc); 586 587 mvc->curve_type = CURVE_LINEAR; 588 mvc->ctrl_value = TEGRA210_MVC_CTRL_DEFAULT; 589 590 regs = devm_platform_ioremap_resource(pdev, 0); 591 if (IS_ERR(regs)) 592 return PTR_ERR(regs); 593 594 mvc->regmap = devm_regmap_init_mmio(dev, regs, 595 &tegra210_mvc_regmap_config); 596 if (IS_ERR(mvc->regmap)) { 597 dev_err(dev, "regmap init failed\n"); 598 return PTR_ERR(mvc->regmap); 599 } 600 601 regcache_cache_only(mvc->regmap, true); 602 603 err = devm_snd_soc_register_component(dev, &tegra210_mvc_cmpnt, 604 tegra210_mvc_dais, 605 ARRAY_SIZE(tegra210_mvc_dais)); 606 if (err) { 607 dev_err(dev, "can't register MVC component, err: %d\n", err); 608 return err; 609 } 610 611 pm_runtime_enable(dev); 612 613 tegra210_mvc_reset_vol_settings(mvc, &pdev->dev); 614 615 return 0; 616 } 617 618 static int tegra210_mvc_platform_remove(struct platform_device *pdev) 619 { 620 pm_runtime_disable(&pdev->dev); 621 622 return 0; 623 } 624 625 static const struct dev_pm_ops tegra210_mvc_pm_ops = { 626 SET_RUNTIME_PM_OPS(tegra210_mvc_runtime_suspend, 627 tegra210_mvc_runtime_resume, NULL) 628 SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 629 pm_runtime_force_resume) 630 }; 631 632 static struct platform_driver tegra210_mvc_driver = { 633 .driver = { 634 .name = "tegra210-mvc", 635 .of_match_table = tegra210_mvc_of_match, 636 .pm = &tegra210_mvc_pm_ops, 637 }, 638 .probe = tegra210_mvc_platform_probe, 639 .remove = tegra210_mvc_platform_remove, 640 }; 641 module_platform_driver(tegra210_mvc_driver) 642 643 MODULE_AUTHOR("Arun Shamanna Lakshmi <aruns@nvidia.com>"); 644 MODULE_DESCRIPTION("Tegra210 MVC ASoC driver"); 645 MODULE_LICENSE("GPL v2"); 646