1 // SPDX-License-Identifier: GPL-2.0-only
2 // SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES.
3 // All rights reserved.
5 // tegra210_ope.c - Tegra210 OPE 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_mbdrc.h"
21 #include "tegra210_ope.h"
22 #include "tegra210_peq.h"
23 #include "tegra_cif.h"
25 static const struct reg_default tegra210_ope_reg_defaults
[] = {
26 { TEGRA210_OPE_RX_INT_MASK
, 0x00000001},
27 { TEGRA210_OPE_RX_CIF_CTRL
, 0x00007700},
28 { TEGRA210_OPE_TX_INT_MASK
, 0x00000001},
29 { TEGRA210_OPE_TX_CIF_CTRL
, 0x00007700},
30 { TEGRA210_OPE_CG
, 0x1},
33 static int tegra210_ope_set_audio_cif(struct tegra210_ope
*ope
,
34 struct snd_pcm_hw_params
*params
,
37 int channels
, audio_bits
;
38 struct tegra_cif_conf cif_conf
;
40 memset(&cif_conf
, 0, sizeof(struct tegra_cif_conf
));
42 channels
= params_channels(params
);
46 switch (params_format(params
)) {
47 case SNDRV_PCM_FORMAT_S16_LE
:
48 audio_bits
= TEGRA_ACIF_BITS_16
;
50 case SNDRV_PCM_FORMAT_S24_LE
:
51 case SNDRV_PCM_FORMAT_S32_LE
:
52 audio_bits
= TEGRA_ACIF_BITS_32
;
58 cif_conf
.audio_ch
= channels
;
59 cif_conf
.client_ch
= channels
;
60 cif_conf
.audio_bits
= audio_bits
;
61 cif_conf
.client_bits
= audio_bits
;
63 tegra_set_cif(ope
->regmap
, reg
, &cif_conf
);
68 static int tegra210_ope_hw_params(struct snd_pcm_substream
*substream
,
69 struct snd_pcm_hw_params
*params
,
70 struct snd_soc_dai
*dai
)
72 struct device
*dev
= dai
->dev
;
73 struct tegra210_ope
*ope
= snd_soc_dai_get_drvdata(dai
);
76 /* Set RX and TX CIF */
77 err
= tegra210_ope_set_audio_cif(ope
, params
,
78 TEGRA210_OPE_RX_CIF_CTRL
);
80 dev_err(dev
, "Can't set OPE RX CIF: %d\n", err
);
84 err
= tegra210_ope_set_audio_cif(ope
, params
,
85 TEGRA210_OPE_TX_CIF_CTRL
);
87 dev_err(dev
, "Can't set OPE TX CIF: %d\n", err
);
91 tegra210_mbdrc_hw_params(dai
->component
);
96 static int tegra210_ope_component_probe(struct snd_soc_component
*cmpnt
)
98 struct tegra210_ope
*ope
= dev_get_drvdata(cmpnt
->dev
);
100 tegra210_peq_component_init(cmpnt
);
101 tegra210_mbdrc_component_init(cmpnt
);
104 * The OPE, PEQ and MBDRC functionalities are combined under one
105 * device registered by OPE driver. In fact OPE HW block includes
106 * sub blocks PEQ and MBDRC. However driver registers separate
107 * regmap interfaces for each of these. ASoC core depends on
108 * dev_get_regmap() to populate the regmap field for a given ASoC
109 * component. A component can have one regmap reference and since
110 * the DAPM routes depend on OPE regmap only, below explicit
111 * assignment is done to highlight this. This is needed for ASoC
112 * core to access correct regmap during DAPM path setup.
114 snd_soc_component_init_regmap(cmpnt
, ope
->regmap
);
119 static const struct snd_soc_dai_ops tegra210_ope_dai_ops
= {
120 .hw_params
= tegra210_ope_hw_params
,
123 static struct snd_soc_dai_driver tegra210_ope_dais
[] = {
125 .name
= "OPE-RX-CIF",
127 .stream_name
= "RX-CIF-Playback",
130 .rates
= SNDRV_PCM_RATE_8000_192000
,
131 .formats
= SNDRV_PCM_FMTBIT_S8
|
132 SNDRV_PCM_FMTBIT_S16_LE
|
133 SNDRV_PCM_FMTBIT_S24_LE
|
134 SNDRV_PCM_FMTBIT_S32_LE
,
137 .stream_name
= "RX-CIF-Capture",
140 .rates
= SNDRV_PCM_RATE_8000_192000
,
141 .formats
= SNDRV_PCM_FMTBIT_S8
|
142 SNDRV_PCM_FMTBIT_S16_LE
|
143 SNDRV_PCM_FMTBIT_S24_LE
|
144 SNDRV_PCM_FMTBIT_S32_LE
,
148 .name
= "OPE-TX-CIF",
150 .stream_name
= "TX-CIF-Playback",
153 .rates
= SNDRV_PCM_RATE_8000_192000
,
154 .formats
= SNDRV_PCM_FMTBIT_S8
|
155 SNDRV_PCM_FMTBIT_S16_LE
|
156 SNDRV_PCM_FMTBIT_S24_LE
|
157 SNDRV_PCM_FMTBIT_S32_LE
,
160 .stream_name
= "TX-CIF-Capture",
163 .rates
= SNDRV_PCM_RATE_8000_192000
,
164 .formats
= SNDRV_PCM_FMTBIT_S8
|
165 SNDRV_PCM_FMTBIT_S16_LE
|
166 SNDRV_PCM_FMTBIT_S24_LE
|
167 SNDRV_PCM_FMTBIT_S32_LE
,
169 .ops
= &tegra210_ope_dai_ops
,
173 static const struct snd_soc_dapm_widget tegra210_ope_widgets
[] = {
174 SND_SOC_DAPM_AIF_IN("RX", NULL
, 0, SND_SOC_NOPM
, 0, 0),
175 SND_SOC_DAPM_AIF_OUT("TX", NULL
, 0, TEGRA210_OPE_ENABLE
,
176 TEGRA210_OPE_EN_SHIFT
, 0),
179 #define OPE_ROUTES(sname) \
180 { "RX XBAR-" sname, NULL, "XBAR-TX" }, \
181 { "RX-CIF-" sname, NULL, "RX XBAR-" sname }, \
182 { "RX", NULL, "RX-CIF-" sname }, \
183 { "TX-CIF-" sname, NULL, "TX" }, \
184 { "TX XBAR-" sname, NULL, "TX-CIF-" sname }, \
185 { "XBAR-RX", NULL, "TX XBAR-" sname }
187 static const struct snd_soc_dapm_route tegra210_ope_routes
[] = {
188 { "TX", NULL
, "RX" },
189 OPE_ROUTES("Playback"),
190 OPE_ROUTES("Capture"),
193 static const char * const tegra210_ope_data_dir_text
[] = {
198 static const struct soc_enum tegra210_ope_data_dir_enum
=
199 SOC_ENUM_SINGLE(TEGRA210_OPE_DIR
, TEGRA210_OPE_DIR_SHIFT
,
200 2, tegra210_ope_data_dir_text
);
202 static int tegra210_ope_get_data_dir(struct snd_kcontrol
*kcontrol
,
203 struct snd_ctl_elem_value
*ucontrol
)
205 struct snd_soc_component
*cmpnt
= snd_soc_kcontrol_component(kcontrol
);
206 struct tegra210_ope
*ope
= snd_soc_component_get_drvdata(cmpnt
);
208 ucontrol
->value
.enumerated
.item
[0] = ope
->data_dir
;
213 static int tegra210_ope_put_data_dir(struct snd_kcontrol
*kcontrol
,
214 struct snd_ctl_elem_value
*ucontrol
)
216 struct snd_soc_component
*cmpnt
= snd_soc_kcontrol_component(kcontrol
);
217 struct tegra210_ope
*ope
= snd_soc_component_get_drvdata(cmpnt
);
218 unsigned int value
= ucontrol
->value
.enumerated
.item
[0];
220 if (value
== ope
->data_dir
)
223 ope
->data_dir
= value
;
228 static const struct snd_kcontrol_new tegra210_ope_controls
[] = {
229 SOC_ENUM_EXT("Data Flow Direction", tegra210_ope_data_dir_enum
,
230 tegra210_ope_get_data_dir
, tegra210_ope_put_data_dir
),
233 static const struct snd_soc_component_driver tegra210_ope_cmpnt
= {
234 .probe
= tegra210_ope_component_probe
,
235 .dapm_widgets
= tegra210_ope_widgets
,
236 .num_dapm_widgets
= ARRAY_SIZE(tegra210_ope_widgets
),
237 .dapm_routes
= tegra210_ope_routes
,
238 .num_dapm_routes
= ARRAY_SIZE(tegra210_ope_routes
),
239 .controls
= tegra210_ope_controls
,
240 .num_controls
= ARRAY_SIZE(tegra210_ope_controls
),
243 static bool tegra210_ope_wr_reg(struct device
*dev
, unsigned int reg
)
246 case TEGRA210_OPE_RX_INT_MASK
... TEGRA210_OPE_RX_CIF_CTRL
:
247 case TEGRA210_OPE_TX_INT_MASK
... TEGRA210_OPE_TX_CIF_CTRL
:
248 case TEGRA210_OPE_ENABLE
... TEGRA210_OPE_CG
:
249 case TEGRA210_OPE_DIR
:
256 static bool tegra210_ope_rd_reg(struct device
*dev
, unsigned int reg
)
258 if (tegra210_ope_wr_reg(dev
, reg
))
262 case TEGRA210_OPE_RX_STATUS
:
263 case TEGRA210_OPE_RX_INT_STATUS
:
264 case TEGRA210_OPE_TX_STATUS
:
265 case TEGRA210_OPE_TX_INT_STATUS
:
266 case TEGRA210_OPE_STATUS
:
267 case TEGRA210_OPE_INT_STATUS
:
274 static bool tegra210_ope_volatile_reg(struct device
*dev
, unsigned int reg
)
277 case TEGRA210_OPE_RX_STATUS
:
278 case TEGRA210_OPE_RX_INT_STATUS
:
279 case TEGRA210_OPE_TX_STATUS
:
280 case TEGRA210_OPE_TX_INT_STATUS
:
281 case TEGRA210_OPE_SOFT_RESET
:
282 case TEGRA210_OPE_STATUS
:
283 case TEGRA210_OPE_INT_STATUS
:
290 static const struct regmap_config tegra210_ope_regmap_config
= {
294 .max_register
= TEGRA210_OPE_DIR
,
295 .writeable_reg
= tegra210_ope_wr_reg
,
296 .readable_reg
= tegra210_ope_rd_reg
,
297 .volatile_reg
= tegra210_ope_volatile_reg
,
298 .reg_defaults
= tegra210_ope_reg_defaults
,
299 .num_reg_defaults
= ARRAY_SIZE(tegra210_ope_reg_defaults
),
300 .cache_type
= REGCACHE_FLAT
,
303 static int tegra210_ope_probe(struct platform_device
*pdev
)
305 struct device
*dev
= &pdev
->dev
;
306 struct tegra210_ope
*ope
;
310 ope
= devm_kzalloc(dev
, sizeof(*ope
), GFP_KERNEL
);
314 regs
= devm_platform_ioremap_resource(pdev
, 0);
316 return PTR_ERR(regs
);
318 ope
->regmap
= devm_regmap_init_mmio(dev
, regs
,
319 &tegra210_ope_regmap_config
);
320 if (IS_ERR(ope
->regmap
)) {
321 dev_err(dev
, "regmap init failed\n");
322 return PTR_ERR(ope
->regmap
);
325 regcache_cache_only(ope
->regmap
, true);
327 dev_set_drvdata(dev
, ope
);
329 err
= tegra210_peq_regmap_init(pdev
);
331 dev_err(dev
, "PEQ init failed\n");
335 err
= tegra210_mbdrc_regmap_init(pdev
);
337 dev_err(dev
, "MBDRC init failed\n");
341 err
= devm_snd_soc_register_component(dev
, &tegra210_ope_cmpnt
,
343 ARRAY_SIZE(tegra210_ope_dais
));
345 dev_err(dev
, "can't register OPE component, err: %d\n", err
);
349 pm_runtime_enable(dev
);
354 static void tegra210_ope_remove(struct platform_device
*pdev
)
356 pm_runtime_disable(&pdev
->dev
);
359 static int __maybe_unused
tegra210_ope_runtime_suspend(struct device
*dev
)
361 struct tegra210_ope
*ope
= dev_get_drvdata(dev
);
363 tegra210_peq_save(ope
->peq_regmap
, ope
->peq_biquad_gains
,
364 ope
->peq_biquad_shifts
);
366 regcache_cache_only(ope
->mbdrc_regmap
, true);
367 regcache_cache_only(ope
->peq_regmap
, true);
368 regcache_cache_only(ope
->regmap
, true);
370 regcache_mark_dirty(ope
->regmap
);
371 regcache_mark_dirty(ope
->peq_regmap
);
372 regcache_mark_dirty(ope
->mbdrc_regmap
);
377 static int __maybe_unused
tegra210_ope_runtime_resume(struct device
*dev
)
379 struct tegra210_ope
*ope
= dev_get_drvdata(dev
);
381 regcache_cache_only(ope
->regmap
, false);
382 regcache_cache_only(ope
->peq_regmap
, false);
383 regcache_cache_only(ope
->mbdrc_regmap
, false);
385 regcache_sync(ope
->regmap
);
386 regcache_sync(ope
->peq_regmap
);
387 regcache_sync(ope
->mbdrc_regmap
);
389 tegra210_peq_restore(ope
->peq_regmap
, ope
->peq_biquad_gains
,
390 ope
->peq_biquad_shifts
);
395 static const struct dev_pm_ops tegra210_ope_pm_ops
= {
396 SET_RUNTIME_PM_OPS(tegra210_ope_runtime_suspend
,
397 tegra210_ope_runtime_resume
, NULL
)
398 SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend
,
399 pm_runtime_force_resume
)
402 static const struct of_device_id tegra210_ope_of_match
[] = {
403 { .compatible
= "nvidia,tegra210-ope" },
406 MODULE_DEVICE_TABLE(of
, tegra210_ope_of_match
);
408 static struct platform_driver tegra210_ope_driver
= {
410 .name
= "tegra210-ope",
411 .of_match_table
= tegra210_ope_of_match
,
412 .pm
= &tegra210_ope_pm_ops
,
414 .probe
= tegra210_ope_probe
,
415 .remove
= tegra210_ope_remove
,
417 module_platform_driver(tegra210_ope_driver
)
419 MODULE_AUTHOR("Sumit Bhattacharya <sumitb@nvidia.com>");
420 MODULE_DESCRIPTION("Tegra210 OPE ASoC driver");
421 MODULE_LICENSE("GPL");