1
2
3
4
5
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
24static 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
41static 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
51static 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
63static 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
79static 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
93static void tegra210_mvc_conv_vol(struct tegra210_mvc *mvc, u8 chan, s32 val)
94{
95
96
97
98
99
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
111static 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
131static 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
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
169end:
170 pm_runtime_put(cmpnt->dev);
171 return err;
172}
173
174static 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
196static 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
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
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
241end:
242 pm_runtime_put(cmpnt->dev);
243 return err;
244}
245
246static void tegra210_mvc_reset_vol_settings(struct tegra210_mvc *mvc,
247 struct device *dev)
248{
249 int i;
250
251
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
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
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
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
286static 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
297static 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
321static 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
353static 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
363
364
365
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
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
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
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
398 regmap_write(mvc->regmap, TEGRA210_MVC_DURATION_INV,
399 gain_params.duration_inv);
400
401 return 0;
402}
403
404static const struct snd_soc_dai_ops tegra210_mvc_dai_ops = {
405 .hw_params = tegra210_mvc_hw_params,
406};
407
408static const char * const tegra210_mvc_curve_type_text[] = {
409 "Poly",
410 "Linear",
411};
412
413static 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
423static const struct snd_kcontrol_new tegra210_mvc_vol_ctrl[] = {
424
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
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
443static struct snd_soc_dai_driver tegra210_mvc_dais[] = {
444
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
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
492static 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
506static const struct snd_soc_dapm_route tegra210_mvc_routes[] = {
507 { "TX", NULL, "RX" },
508 MVC_ROUTES("Playback"),
509 MVC_ROUTES("Capture"),
510};
511
512static 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
521static 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
531static 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
544static 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
569static 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
582static const struct of_device_id tegra210_mvc_of_match[] = {
583 { .compatible = "nvidia,tegra210-mvc" },
584 {},
585};
586MODULE_DEVICE_TABLE(of, tegra210_mvc_of_match);
587
588static 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
632static int tegra210_mvc_platform_remove(struct platform_device *pdev)
633{
634 pm_runtime_disable(&pdev->dev);
635
636 return 0;
637}
638
639static 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
646static 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};
655module_platform_driver(tegra210_mvc_driver)
656
657MODULE_AUTHOR("Arun Shamanna Lakshmi <aruns@nvidia.com>");
658MODULE_DESCRIPTION("Tegra210 MVC ASoC driver");
659MODULE_LICENSE("GPL v2");
660