1 // SPDX-License-Identifier: GPL-2.0-only
2 // SPDX-FileCopyrightText: Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES.
3 // All rights reserved.
5 // tegra210_adx.c - Tegra210 ADX driver
8 #include <linux/device.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>
20 #include "tegra210_adx.h"
21 #include "tegra_cif.h"
23 static const struct reg_default tegra210_adx_reg_defaults
[] = {
24 { TEGRA210_ADX_RX_INT_MASK
, 0x00000001},
25 { TEGRA210_ADX_RX_CIF_CTRL
, 0x00007000},
26 { TEGRA210_ADX_TX_INT_MASK
, 0x0000000f },
27 { TEGRA210_ADX_TX1_CIF_CTRL
, 0x00007000},
28 { TEGRA210_ADX_TX2_CIF_CTRL
, 0x00007000},
29 { TEGRA210_ADX_TX3_CIF_CTRL
, 0x00007000},
30 { TEGRA210_ADX_TX4_CIF_CTRL
, 0x00007000},
31 { TEGRA210_ADX_CG
, 0x1},
32 { TEGRA210_ADX_CFG_RAM_CTRL
, 0x00004000},
35 static void tegra210_adx_write_map_ram(struct tegra210_adx
*adx
)
39 regmap_write(adx
->regmap
, TEGRA210_ADX_CFG_RAM_CTRL
,
40 TEGRA210_ADX_CFG_RAM_CTRL_SEQ_ACCESS_EN
|
41 TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN
|
42 TEGRA210_ADX_CFG_RAM_CTRL_RW_WRITE
);
44 for (i
= 0; i
< TEGRA210_ADX_RAM_DEPTH
; i
++)
45 regmap_write(adx
->regmap
, TEGRA210_ADX_CFG_RAM_DATA
,
48 regmap_write(adx
->regmap
, TEGRA210_ADX_IN_BYTE_EN0
, adx
->byte_mask
[0]);
49 regmap_write(adx
->regmap
, TEGRA210_ADX_IN_BYTE_EN1
, adx
->byte_mask
[1]);
52 static int tegra210_adx_startup(struct snd_pcm_substream
*substream
,
53 struct snd_soc_dai
*dai
)
55 struct tegra210_adx
*adx
= snd_soc_dai_get_drvdata(dai
);
59 /* Ensure if ADX status is disabled */
60 err
= regmap_read_poll_timeout_atomic(adx
->regmap
, TEGRA210_ADX_STATUS
,
61 val
, !(val
& 0x1), 10, 10000);
63 dev_err(dai
->dev
, "failed to stop ADX, err = %d\n", err
);
68 * Soft Reset: Below performs module soft reset which clears
69 * all FSM logic, flushes flow control of FIFO and resets the
70 * state register. It also brings module back to disabled
71 * state (without flushing the data in the pipe).
73 regmap_update_bits(adx
->regmap
, TEGRA210_ADX_SOFT_RESET
,
74 TEGRA210_ADX_SOFT_RESET_SOFT_RESET_MASK
,
75 TEGRA210_ADX_SOFT_RESET_SOFT_EN
);
77 err
= regmap_read_poll_timeout(adx
->regmap
, TEGRA210_ADX_SOFT_RESET
,
78 val
, !(val
& 0x1), 10, 10000);
80 dev_err(dai
->dev
, "failed to reset ADX, err = %d\n", err
);
87 static int __maybe_unused
tegra210_adx_runtime_suspend(struct device
*dev
)
89 struct tegra210_adx
*adx
= dev_get_drvdata(dev
);
91 regcache_cache_only(adx
->regmap
, true);
92 regcache_mark_dirty(adx
->regmap
);
97 static int __maybe_unused
tegra210_adx_runtime_resume(struct device
*dev
)
99 struct tegra210_adx
*adx
= dev_get_drvdata(dev
);
101 regcache_cache_only(adx
->regmap
, false);
102 regcache_sync(adx
->regmap
);
104 tegra210_adx_write_map_ram(adx
);
109 static int tegra210_adx_set_audio_cif(struct snd_soc_dai
*dai
,
110 unsigned int channels
,
111 snd_pcm_format_t format
,
114 struct tegra210_adx
*adx
= snd_soc_dai_get_drvdata(dai
);
115 struct tegra_cif_conf cif_conf
;
118 memset(&cif_conf
, 0, sizeof(struct tegra_cif_conf
));
120 if (channels
< 1 || channels
> 16)
124 case SNDRV_PCM_FORMAT_S8
:
125 audio_bits
= TEGRA_ACIF_BITS_8
;
127 case SNDRV_PCM_FORMAT_S16_LE
:
128 audio_bits
= TEGRA_ACIF_BITS_16
;
130 case SNDRV_PCM_FORMAT_S24_LE
:
131 case SNDRV_PCM_FORMAT_S32_LE
:
132 audio_bits
= TEGRA_ACIF_BITS_32
;
138 cif_conf
.audio_ch
= channels
;
139 cif_conf
.client_ch
= channels
;
140 cif_conf
.audio_bits
= audio_bits
;
141 cif_conf
.client_bits
= audio_bits
;
143 tegra_set_cif(adx
->regmap
, reg
, &cif_conf
);
148 static int tegra210_adx_out_hw_params(struct snd_pcm_substream
*substream
,
149 struct snd_pcm_hw_params
*params
,
150 struct snd_soc_dai
*dai
)
152 return tegra210_adx_set_audio_cif(dai
, params_channels(params
),
153 params_format(params
),
154 TEGRA210_ADX_TX1_CIF_CTRL
+ ((dai
->id
- 1) * TEGRA210_ADX_AUDIOCIF_CH_STRIDE
));
157 static int tegra210_adx_in_hw_params(struct snd_pcm_substream
*substream
,
158 struct snd_pcm_hw_params
*params
,
159 struct snd_soc_dai
*dai
)
161 return tegra210_adx_set_audio_cif(dai
, params_channels(params
),
162 params_format(params
),
163 TEGRA210_ADX_RX_CIF_CTRL
);
166 static int tegra210_adx_get_byte_map(struct snd_kcontrol
*kcontrol
,
167 struct snd_ctl_elem_value
*ucontrol
)
169 struct snd_soc_component
*cmpnt
= snd_soc_kcontrol_component(kcontrol
);
170 struct tegra210_adx
*adx
= snd_soc_component_get_drvdata(cmpnt
);
171 struct soc_mixer_control
*mc
;
172 unsigned char *bytes_map
= (unsigned char *)&adx
->map
;
175 mc
= (struct soc_mixer_control
*)kcontrol
->private_value
;
176 enabled
= adx
->byte_mask
[mc
->reg
/ 32] & (1 << (mc
->reg
% 32));
179 * TODO: Simplify this logic to just return from bytes_map[]
181 * Presently below is required since bytes_map[] is
182 * tightly packed and cannot store the control value of 256.
183 * Byte mask state is used to know if 256 needs to be returned.
184 * Note that for control value of 256, the put() call stores 0
185 * in the bytes_map[] and disables the corresponding bit in
189 ucontrol
->value
.integer
.value
[0] = bytes_map
[mc
->reg
];
191 ucontrol
->value
.integer
.value
[0] = 256;
196 static int tegra210_adx_put_byte_map(struct snd_kcontrol
*kcontrol
,
197 struct snd_ctl_elem_value
*ucontrol
)
199 struct snd_soc_component
*cmpnt
= snd_soc_kcontrol_component(kcontrol
);
200 struct tegra210_adx
*adx
= snd_soc_component_get_drvdata(cmpnt
);
201 unsigned char *bytes_map
= (unsigned char *)&adx
->map
;
202 int value
= ucontrol
->value
.integer
.value
[0];
203 struct soc_mixer_control
*mc
=
204 (struct soc_mixer_control
*)kcontrol
->private_value
;
205 unsigned int mask_val
= adx
->byte_mask
[mc
->reg
/ 32];
207 if (value
>= 0 && value
<= 255)
208 mask_val
|= (1 << (mc
->reg
% 32));
210 mask_val
&= ~(1 << (mc
->reg
% 32));
212 if (mask_val
== adx
->byte_mask
[mc
->reg
/ 32])
215 /* Update byte map and slot */
216 bytes_map
[mc
->reg
] = value
% 256;
217 adx
->byte_mask
[mc
->reg
/ 32] = mask_val
;
222 static const struct snd_soc_dai_ops tegra210_adx_in_dai_ops
= {
223 .hw_params
= tegra210_adx_in_hw_params
,
224 .startup
= tegra210_adx_startup
,
227 static const struct snd_soc_dai_ops tegra210_adx_out_dai_ops
= {
228 .hw_params
= tegra210_adx_out_hw_params
,
233 .name = "ADX-RX-CIF", \
235 .stream_name = "RX-CIF-Playback", \
237 .channels_max = 16, \
238 .rates = SNDRV_PCM_RATE_8000_192000, \
239 .formats = SNDRV_PCM_FMTBIT_S8 | \
240 SNDRV_PCM_FMTBIT_S16_LE | \
241 SNDRV_PCM_FMTBIT_S24_LE | \
242 SNDRV_PCM_FMTBIT_S32_LE, \
245 .stream_name = "RX-CIF-Capture", \
247 .channels_max = 16, \
248 .rates = SNDRV_PCM_RATE_8000_192000, \
249 .formats = SNDRV_PCM_FMTBIT_S8 | \
250 SNDRV_PCM_FMTBIT_S16_LE | \
251 SNDRV_PCM_FMTBIT_S24_LE | \
252 SNDRV_PCM_FMTBIT_S32_LE, \
254 .ops = &tegra210_adx_in_dai_ops, \
257 #define OUT_DAI(id) \
259 .name = "ADX-TX" #id "-CIF", \
261 .stream_name = "TX" #id "-CIF-Playback",\
263 .channels_max = 16, \
264 .rates = SNDRV_PCM_RATE_8000_192000, \
265 .formats = SNDRV_PCM_FMTBIT_S8 | \
266 SNDRV_PCM_FMTBIT_S16_LE | \
267 SNDRV_PCM_FMTBIT_S16_LE | \
268 SNDRV_PCM_FMTBIT_S32_LE, \
271 .stream_name = "TX" #id "-CIF-Capture", \
273 .channels_max = 16, \
274 .rates = SNDRV_PCM_RATE_8000_192000, \
275 .formats = SNDRV_PCM_FMTBIT_S8 | \
276 SNDRV_PCM_FMTBIT_S16_LE | \
277 SNDRV_PCM_FMTBIT_S16_LE | \
278 SNDRV_PCM_FMTBIT_S32_LE, \
280 .ops = &tegra210_adx_out_dai_ops, \
283 static struct snd_soc_dai_driver tegra210_adx_dais
[] = {
291 static const struct snd_soc_dapm_widget tegra210_adx_widgets
[] = {
292 SND_SOC_DAPM_AIF_IN("RX", NULL
, 0, TEGRA210_ADX_ENABLE
,
293 TEGRA210_ADX_ENABLE_SHIFT
, 0),
294 SND_SOC_DAPM_AIF_OUT("TX1", NULL
, 0, TEGRA210_ADX_CTRL
, 0, 0),
295 SND_SOC_DAPM_AIF_OUT("TX2", NULL
, 0, TEGRA210_ADX_CTRL
, 1, 0),
296 SND_SOC_DAPM_AIF_OUT("TX3", NULL
, 0, TEGRA210_ADX_CTRL
, 2, 0),
297 SND_SOC_DAPM_AIF_OUT("TX4", NULL
, 0, TEGRA210_ADX_CTRL
, 3, 0),
300 #define STREAM_ROUTES(id, sname) \
301 { "XBAR-" sname, NULL, "XBAR-TX" }, \
302 { "RX-CIF-" sname, NULL, "XBAR-" sname }, \
303 { "RX", NULL, "RX-CIF-" sname }, \
304 { "TX" #id, NULL, "RX" }, \
305 { "TX" #id "-CIF-" sname, NULL, "TX" #id }, \
306 { "TX" #id " XBAR-" sname, NULL, "TX" #id "-CIF-" sname }, \
307 { "TX" #id " XBAR-RX", NULL, "TX" #id " XBAR-" sname }
309 #define ADX_ROUTES(id) \
310 STREAM_ROUTES(id, "Playback"), \
311 STREAM_ROUTES(id, "Capture")
313 #define STREAM_ROUTES(id, sname) \
314 { "XBAR-" sname, NULL, "XBAR-TX" }, \
315 { "RX-CIF-" sname, NULL, "XBAR-" sname }, \
316 { "RX", NULL, "RX-CIF-" sname }, \
317 { "TX" #id, NULL, "RX" }, \
318 { "TX" #id "-CIF-" sname, NULL, "TX" #id }, \
319 { "TX" #id " XBAR-" sname, NULL, "TX" #id "-CIF-" sname }, \
320 { "TX" #id " XBAR-RX", NULL, "TX" #id " XBAR-" sname }
322 #define ADX_ROUTES(id) \
323 STREAM_ROUTES(id, "Playback"), \
324 STREAM_ROUTES(id, "Capture")
326 static const struct snd_soc_dapm_route tegra210_adx_routes
[] = {
333 #define TEGRA210_ADX_BYTE_MAP_CTRL(reg) \
334 SOC_SINGLE_EXT("Byte Map " #reg, reg, 0, 256, 0, \
335 tegra210_adx_get_byte_map, \
336 tegra210_adx_put_byte_map)
338 static struct snd_kcontrol_new tegra210_adx_controls
[] = {
339 TEGRA210_ADX_BYTE_MAP_CTRL(0),
340 TEGRA210_ADX_BYTE_MAP_CTRL(1),
341 TEGRA210_ADX_BYTE_MAP_CTRL(2),
342 TEGRA210_ADX_BYTE_MAP_CTRL(3),
343 TEGRA210_ADX_BYTE_MAP_CTRL(4),
344 TEGRA210_ADX_BYTE_MAP_CTRL(5),
345 TEGRA210_ADX_BYTE_MAP_CTRL(6),
346 TEGRA210_ADX_BYTE_MAP_CTRL(7),
347 TEGRA210_ADX_BYTE_MAP_CTRL(8),
348 TEGRA210_ADX_BYTE_MAP_CTRL(9),
349 TEGRA210_ADX_BYTE_MAP_CTRL(10),
350 TEGRA210_ADX_BYTE_MAP_CTRL(11),
351 TEGRA210_ADX_BYTE_MAP_CTRL(12),
352 TEGRA210_ADX_BYTE_MAP_CTRL(13),
353 TEGRA210_ADX_BYTE_MAP_CTRL(14),
354 TEGRA210_ADX_BYTE_MAP_CTRL(15),
355 TEGRA210_ADX_BYTE_MAP_CTRL(16),
356 TEGRA210_ADX_BYTE_MAP_CTRL(17),
357 TEGRA210_ADX_BYTE_MAP_CTRL(18),
358 TEGRA210_ADX_BYTE_MAP_CTRL(19),
359 TEGRA210_ADX_BYTE_MAP_CTRL(20),
360 TEGRA210_ADX_BYTE_MAP_CTRL(21),
361 TEGRA210_ADX_BYTE_MAP_CTRL(22),
362 TEGRA210_ADX_BYTE_MAP_CTRL(23),
363 TEGRA210_ADX_BYTE_MAP_CTRL(24),
364 TEGRA210_ADX_BYTE_MAP_CTRL(25),
365 TEGRA210_ADX_BYTE_MAP_CTRL(26),
366 TEGRA210_ADX_BYTE_MAP_CTRL(27),
367 TEGRA210_ADX_BYTE_MAP_CTRL(28),
368 TEGRA210_ADX_BYTE_MAP_CTRL(29),
369 TEGRA210_ADX_BYTE_MAP_CTRL(30),
370 TEGRA210_ADX_BYTE_MAP_CTRL(31),
371 TEGRA210_ADX_BYTE_MAP_CTRL(32),
372 TEGRA210_ADX_BYTE_MAP_CTRL(33),
373 TEGRA210_ADX_BYTE_MAP_CTRL(34),
374 TEGRA210_ADX_BYTE_MAP_CTRL(35),
375 TEGRA210_ADX_BYTE_MAP_CTRL(36),
376 TEGRA210_ADX_BYTE_MAP_CTRL(37),
377 TEGRA210_ADX_BYTE_MAP_CTRL(38),
378 TEGRA210_ADX_BYTE_MAP_CTRL(39),
379 TEGRA210_ADX_BYTE_MAP_CTRL(40),
380 TEGRA210_ADX_BYTE_MAP_CTRL(41),
381 TEGRA210_ADX_BYTE_MAP_CTRL(42),
382 TEGRA210_ADX_BYTE_MAP_CTRL(43),
383 TEGRA210_ADX_BYTE_MAP_CTRL(44),
384 TEGRA210_ADX_BYTE_MAP_CTRL(45),
385 TEGRA210_ADX_BYTE_MAP_CTRL(46),
386 TEGRA210_ADX_BYTE_MAP_CTRL(47),
387 TEGRA210_ADX_BYTE_MAP_CTRL(48),
388 TEGRA210_ADX_BYTE_MAP_CTRL(49),
389 TEGRA210_ADX_BYTE_MAP_CTRL(50),
390 TEGRA210_ADX_BYTE_MAP_CTRL(51),
391 TEGRA210_ADX_BYTE_MAP_CTRL(52),
392 TEGRA210_ADX_BYTE_MAP_CTRL(53),
393 TEGRA210_ADX_BYTE_MAP_CTRL(54),
394 TEGRA210_ADX_BYTE_MAP_CTRL(55),
395 TEGRA210_ADX_BYTE_MAP_CTRL(56),
396 TEGRA210_ADX_BYTE_MAP_CTRL(57),
397 TEGRA210_ADX_BYTE_MAP_CTRL(58),
398 TEGRA210_ADX_BYTE_MAP_CTRL(59),
399 TEGRA210_ADX_BYTE_MAP_CTRL(60),
400 TEGRA210_ADX_BYTE_MAP_CTRL(61),
401 TEGRA210_ADX_BYTE_MAP_CTRL(62),
402 TEGRA210_ADX_BYTE_MAP_CTRL(63),
405 static const struct snd_soc_component_driver tegra210_adx_cmpnt
= {
406 .dapm_widgets
= tegra210_adx_widgets
,
407 .num_dapm_widgets
= ARRAY_SIZE(tegra210_adx_widgets
),
408 .dapm_routes
= tegra210_adx_routes
,
409 .num_dapm_routes
= ARRAY_SIZE(tegra210_adx_routes
),
410 .controls
= tegra210_adx_controls
,
411 .num_controls
= ARRAY_SIZE(tegra210_adx_controls
),
414 static bool tegra210_adx_wr_reg(struct device
*dev
,
418 case TEGRA210_ADX_TX_INT_MASK
... TEGRA210_ADX_TX4_CIF_CTRL
:
419 case TEGRA210_ADX_RX_INT_MASK
... TEGRA210_ADX_RX_CIF_CTRL
:
420 case TEGRA210_ADX_ENABLE
... TEGRA210_ADX_CG
:
421 case TEGRA210_ADX_CTRL
... TEGRA210_ADX_IN_BYTE_EN1
:
422 case TEGRA210_ADX_CFG_RAM_CTRL
... TEGRA210_ADX_CFG_RAM_DATA
:
429 static bool tegra210_adx_rd_reg(struct device
*dev
,
433 case TEGRA210_ADX_RX_STATUS
... TEGRA210_ADX_CFG_RAM_DATA
:
440 static bool tegra210_adx_volatile_reg(struct device
*dev
,
444 case TEGRA210_ADX_RX_STATUS
:
445 case TEGRA210_ADX_RX_INT_STATUS
:
446 case TEGRA210_ADX_RX_INT_SET
:
447 case TEGRA210_ADX_TX_STATUS
:
448 case TEGRA210_ADX_TX_INT_STATUS
:
449 case TEGRA210_ADX_TX_INT_SET
:
450 case TEGRA210_ADX_SOFT_RESET
:
451 case TEGRA210_ADX_STATUS
:
452 case TEGRA210_ADX_INT_STATUS
:
453 case TEGRA210_ADX_CFG_RAM_CTRL
:
454 case TEGRA210_ADX_CFG_RAM_DATA
:
463 static const struct regmap_config tegra210_adx_regmap_config
= {
467 .max_register
= TEGRA210_ADX_CFG_RAM_DATA
,
468 .writeable_reg
= tegra210_adx_wr_reg
,
469 .readable_reg
= tegra210_adx_rd_reg
,
470 .volatile_reg
= tegra210_adx_volatile_reg
,
471 .reg_defaults
= tegra210_adx_reg_defaults
,
472 .num_reg_defaults
= ARRAY_SIZE(tegra210_adx_reg_defaults
),
473 .cache_type
= REGCACHE_FLAT
,
476 static const struct of_device_id tegra210_adx_of_match
[] = {
477 { .compatible
= "nvidia,tegra210-adx" },
480 MODULE_DEVICE_TABLE(of
, tegra210_adx_of_match
);
482 static int tegra210_adx_platform_probe(struct platform_device
*pdev
)
484 struct device
*dev
= &pdev
->dev
;
485 struct tegra210_adx
*adx
;
489 adx
= devm_kzalloc(dev
, sizeof(*adx
), GFP_KERNEL
);
493 dev_set_drvdata(dev
, adx
);
495 regs
= devm_platform_ioremap_resource(pdev
, 0);
497 return PTR_ERR(regs
);
499 adx
->regmap
= devm_regmap_init_mmio(dev
, regs
,
500 &tegra210_adx_regmap_config
);
501 if (IS_ERR(adx
->regmap
)) {
502 dev_err(dev
, "regmap init failed\n");
503 return PTR_ERR(adx
->regmap
);
506 regcache_cache_only(adx
->regmap
, true);
508 err
= devm_snd_soc_register_component(dev
, &tegra210_adx_cmpnt
,
510 ARRAY_SIZE(tegra210_adx_dais
));
512 dev_err(dev
, "can't register ADX component, err: %d\n", err
);
516 pm_runtime_enable(dev
);
521 static void tegra210_adx_platform_remove(struct platform_device
*pdev
)
523 pm_runtime_disable(&pdev
->dev
);
526 static const struct dev_pm_ops tegra210_adx_pm_ops
= {
527 SET_RUNTIME_PM_OPS(tegra210_adx_runtime_suspend
,
528 tegra210_adx_runtime_resume
, NULL
)
529 SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend
,
530 pm_runtime_force_resume
)
533 static struct platform_driver tegra210_adx_driver
= {
535 .name
= "tegra210-adx",
536 .of_match_table
= tegra210_adx_of_match
,
537 .pm
= &tegra210_adx_pm_ops
,
539 .probe
= tegra210_adx_platform_probe
,
540 .remove
= tegra210_adx_platform_remove
,
542 module_platform_driver(tegra210_adx_driver
);
544 MODULE_AUTHOR("Arun Shamanna Lakshmi <aruns@nvidia.com>");
545 MODULE_DESCRIPTION("Tegra210 ADX ASoC driver");
546 MODULE_LICENSE("GPL v2");