xref: /openbmc/linux/sound/soc/tegra/tegra210_mvc.c (revision df0e68c1)
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