1 // SPDX-License-Identifier: GPL-2.0
3 * NXP AUDMIX ALSA SoC Digital Audio Interface (DAI) driver
9 #include <linux/module.h>
10 #include <linux/of_platform.h>
11 #include <linux/pm_runtime.h>
12 #include <sound/soc.h>
13 #include <sound/pcm_params.h>
15 #include "fsl_audmix.h"
17 #define SOC_ENUM_SINGLE_S(xreg, xshift, xtexts) \
18 SOC_ENUM_SINGLE(xreg, xshift, ARRAY_SIZE(xtexts), xtexts)
21 *tdm_sel
[] = { "TDM1", "TDM2", },
22 *mode_sel
[] = { "Disabled", "TDM1", "TDM2", "Mixed", },
23 *width_sel
[] = { "16b", "18b", "20b", "24b", "32b", },
24 *endis_sel
[] = { "Disabled", "Enabled", },
25 *updn_sel
[] = { "Downward", "Upward", },
26 *mask_sel
[] = { "Unmask", "Mask", };
28 static const struct soc_enum fsl_audmix_enum
[] = {
29 /* FSL_AUDMIX_CTR enums */
30 SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR
, FSL_AUDMIX_CTR_MIXCLK_SHIFT
, tdm_sel
),
31 SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR
, FSL_AUDMIX_CTR_OUTSRC_SHIFT
, mode_sel
),
32 SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR
, FSL_AUDMIX_CTR_OUTWIDTH_SHIFT
, width_sel
),
33 SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR
, FSL_AUDMIX_CTR_MASKRTDF_SHIFT
, mask_sel
),
34 SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR
, FSL_AUDMIX_CTR_MASKCKDF_SHIFT
, mask_sel
),
35 SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR
, FSL_AUDMIX_CTR_SYNCMODE_SHIFT
, endis_sel
),
36 SOC_ENUM_SINGLE_S(FSL_AUDMIX_CTR
, FSL_AUDMIX_CTR_SYNCSRC_SHIFT
, tdm_sel
),
37 /* FSL_AUDMIX_ATCR0 enums */
38 SOC_ENUM_SINGLE_S(FSL_AUDMIX_ATCR0
, 0, endis_sel
),
39 SOC_ENUM_SINGLE_S(FSL_AUDMIX_ATCR0
, 1, updn_sel
),
40 /* FSL_AUDMIX_ATCR1 enums */
41 SOC_ENUM_SINGLE_S(FSL_AUDMIX_ATCR1
, 0, endis_sel
),
42 SOC_ENUM_SINGLE_S(FSL_AUDMIX_ATCR1
, 1, updn_sel
),
45 struct fsl_audmix_state
{
51 static const struct fsl_audmix_state prms
[4][4] = {{
52 /* DIS->DIS, do nothing */
53 { .tdms
= 0, .clk
= 0, .msg
= "" },
55 { .tdms
= 1, .clk
= 1, .msg
= "DIS->TDM1: TDM1 not started!\n" },
57 { .tdms
= 2, .clk
= 2, .msg
= "DIS->TDM2: TDM2 not started!\n" },
59 { .tdms
= 3, .clk
= 0, .msg
= "DIS->MIX: Please start both TDMs!\n" }
61 { .tdms
= 1, .clk
= 0, .msg
= "TDM1->DIS: TDM1 not started!\n" },
62 /* TDM1->TDM1, do nothing */
63 { .tdms
= 0, .clk
= 0, .msg
= "" },
65 { .tdms
= 3, .clk
= 2, .msg
= "TDM1->TDM2: Please start both TDMs!\n" },
67 { .tdms
= 3, .clk
= 0, .msg
= "TDM1->MIX: Please start both TDMs!\n" }
69 { .tdms
= 2, .clk
= 0, .msg
= "TDM2->DIS: TDM2 not started!\n" },
71 { .tdms
= 3, .clk
= 1, .msg
= "TDM2->TDM1: Please start both TDMs!\n" },
72 /* TDM2->TDM2, do nothing */
73 { .tdms
= 0, .clk
= 0, .msg
= "" },
75 { .tdms
= 3, .clk
= 0, .msg
= "TDM2->MIX: Please start both TDMs!\n" }
77 { .tdms
= 3, .clk
= 0, .msg
= "MIX->DIS: Please start both TDMs!\n" },
79 { .tdms
= 3, .clk
= 1, .msg
= "MIX->TDM1: Please start both TDMs!\n" },
81 { .tdms
= 3, .clk
= 2, .msg
= "MIX->TDM2: Please start both TDMs!\n" },
82 /* MIX->MIX, do nothing */
83 { .tdms
= 0, .clk
= 0, .msg
= "" }
86 static int fsl_audmix_state_trans(struct snd_soc_component
*comp
,
87 unsigned int *mask
, unsigned int *ctr
,
88 const struct fsl_audmix_state prm
)
90 struct fsl_audmix
*priv
= snd_soc_component_get_drvdata(comp
);
91 /* Enforce all required TDMs are started */
92 if ((priv
->tdms
& prm
.tdms
) != prm
.tdms
) {
93 dev_dbg(comp
->dev
, "%s", prm
.msg
);
101 (*mask
) |= FSL_AUDMIX_CTR_MIXCLK_MASK
;
102 (*ctr
) |= FSL_AUDMIX_CTR_MIXCLK(prm
.clk
- 1);
111 static int fsl_audmix_put_mix_clk_src(struct snd_kcontrol
*kcontrol
,
112 struct snd_ctl_elem_value
*ucontrol
)
114 struct snd_soc_component
*comp
= snd_kcontrol_chip(kcontrol
);
115 struct fsl_audmix
*priv
= snd_soc_component_get_drvdata(comp
);
116 struct soc_enum
*e
= (struct soc_enum
*)kcontrol
->private_value
;
117 unsigned int *item
= ucontrol
->value
.enumerated
.item
;
118 unsigned int reg_val
, val
, mix_clk
;
121 /* Get current state */
122 ret
= snd_soc_component_read(comp
, FSL_AUDMIX_CTR
, ®_val
);
126 mix_clk
= ((reg_val
& FSL_AUDMIX_CTR_MIXCLK_MASK
)
127 >> FSL_AUDMIX_CTR_MIXCLK_SHIFT
);
128 val
= snd_soc_enum_item_to_val(e
, item
[0]);
130 dev_dbg(comp
->dev
, "TDMs=x%08x, val=x%08x\n", priv
->tdms
, val
);
133 * Ensure the current selected mixer clock is available
134 * for configuration propagation
136 if (!(priv
->tdms
& BIT(mix_clk
))) {
138 "Started TDM%d needed for config propagation!\n",
143 if (!(priv
->tdms
& BIT(val
))) {
145 "The selected clock source has no TDM%d enabled!\n",
150 return snd_soc_put_enum_double(kcontrol
, ucontrol
);
153 static int fsl_audmix_put_out_src(struct snd_kcontrol
*kcontrol
,
154 struct snd_ctl_elem_value
*ucontrol
)
156 struct snd_soc_component
*comp
= snd_kcontrol_chip(kcontrol
);
157 struct fsl_audmix
*priv
= snd_soc_component_get_drvdata(comp
);
158 struct soc_enum
*e
= (struct soc_enum
*)kcontrol
->private_value
;
159 unsigned int *item
= ucontrol
->value
.enumerated
.item
;
160 u32 out_src
, mix_clk
;
161 unsigned int reg_val
, val
, mask
= 0, ctr
= 0;
164 /* Get current state */
165 ret
= snd_soc_component_read(comp
, FSL_AUDMIX_CTR
, ®_val
);
170 out_src
= ((reg_val
& FSL_AUDMIX_CTR_OUTSRC_MASK
)
171 >> FSL_AUDMIX_CTR_OUTSRC_SHIFT
);
172 mix_clk
= ((reg_val
& FSL_AUDMIX_CTR_MIXCLK_MASK
)
173 >> FSL_AUDMIX_CTR_MIXCLK_SHIFT
);
176 val
= snd_soc_enum_item_to_val(e
, item
[0]);
178 dev_dbg(comp
->dev
, "TDMs=x%08x, val=x%08x\n", priv
->tdms
, val
);
180 /* Check if state is changing ... */
184 * Ensure the current selected mixer clock is available
185 * for configuration propagation
187 if (!(priv
->tdms
& BIT(mix_clk
))) {
189 "Started TDM%d needed for config propagation!\n",
194 /* Check state transition constraints */
195 ret
= fsl_audmix_state_trans(comp
, &mask
, &ctr
, prms
[out_src
][val
]);
199 /* Complete transition to new state */
200 mask
|= FSL_AUDMIX_CTR_OUTSRC_MASK
;
201 ctr
|= FSL_AUDMIX_CTR_OUTSRC(val
);
203 return snd_soc_component_update_bits(comp
, FSL_AUDMIX_CTR
, mask
, ctr
);
206 static const struct snd_kcontrol_new fsl_audmix_snd_controls
[] = {
207 /* FSL_AUDMIX_CTR controls */
208 SOC_ENUM_EXT("Mixing Clock Source", fsl_audmix_enum
[0],
209 snd_soc_get_enum_double
, fsl_audmix_put_mix_clk_src
),
210 SOC_ENUM_EXT("Output Source", fsl_audmix_enum
[1],
211 snd_soc_get_enum_double
, fsl_audmix_put_out_src
),
212 SOC_ENUM("Output Width", fsl_audmix_enum
[2]),
213 SOC_ENUM("Frame Rate Diff Error", fsl_audmix_enum
[3]),
214 SOC_ENUM("Clock Freq Diff Error", fsl_audmix_enum
[4]),
215 SOC_ENUM("Sync Mode Config", fsl_audmix_enum
[5]),
216 SOC_ENUM("Sync Mode Clk Source", fsl_audmix_enum
[6]),
217 /* TDM1 Attenuation controls */
218 SOC_ENUM("TDM1 Attenuation", fsl_audmix_enum
[7]),
219 SOC_ENUM("TDM1 Attenuation Direction", fsl_audmix_enum
[8]),
220 SOC_SINGLE("TDM1 Attenuation Step Divider", FSL_AUDMIX_ATCR0
,
222 SOC_SINGLE("TDM1 Attenuation Initial Value", FSL_AUDMIX_ATIVAL0
,
224 SOC_SINGLE("TDM1 Attenuation Step Up Factor", FSL_AUDMIX_ATSTPUP0
,
226 SOC_SINGLE("TDM1 Attenuation Step Down Factor", FSL_AUDMIX_ATSTPDN0
,
228 SOC_SINGLE("TDM1 Attenuation Step Target", FSL_AUDMIX_ATSTPTGT0
,
230 /* TDM2 Attenuation controls */
231 SOC_ENUM("TDM2 Attenuation", fsl_audmix_enum
[9]),
232 SOC_ENUM("TDM2 Attenuation Direction", fsl_audmix_enum
[10]),
233 SOC_SINGLE("TDM2 Attenuation Step Divider", FSL_AUDMIX_ATCR1
,
235 SOC_SINGLE("TDM2 Attenuation Initial Value", FSL_AUDMIX_ATIVAL1
,
237 SOC_SINGLE("TDM2 Attenuation Step Up Factor", FSL_AUDMIX_ATSTPUP1
,
239 SOC_SINGLE("TDM2 Attenuation Step Down Factor", FSL_AUDMIX_ATSTPDN1
,
241 SOC_SINGLE("TDM2 Attenuation Step Target", FSL_AUDMIX_ATSTPTGT1
,
245 static int fsl_audmix_dai_set_fmt(struct snd_soc_dai
*dai
, unsigned int fmt
)
247 struct snd_soc_component
*comp
= dai
->component
;
248 u32 mask
= 0, ctr
= 0;
250 /* AUDMIX is working in DSP_A format only */
251 switch (fmt
& SND_SOC_DAIFMT_FORMAT_MASK
) {
252 case SND_SOC_DAIFMT_DSP_A
:
258 /* For playback the AUDMIX is slave, and for record is master */
259 switch (fmt
& SND_SOC_DAIFMT_MASTER_MASK
) {
260 case SND_SOC_DAIFMT_CBM_CFM
:
261 case SND_SOC_DAIFMT_CBS_CFS
:
267 switch (fmt
& SND_SOC_DAIFMT_INV_MASK
) {
268 case SND_SOC_DAIFMT_IB_NF
:
269 /* Output data will be written on positive edge of the clock */
270 ctr
|= FSL_AUDMIX_CTR_OUTCKPOL(0);
272 case SND_SOC_DAIFMT_NB_NF
:
273 /* Output data will be written on negative edge of the clock */
274 ctr
|= FSL_AUDMIX_CTR_OUTCKPOL(1);
280 mask
|= FSL_AUDMIX_CTR_OUTCKPOL_MASK
;
282 return snd_soc_component_update_bits(comp
, FSL_AUDMIX_CTR
, mask
, ctr
);
285 static int fsl_audmix_dai_trigger(struct snd_pcm_substream
*substream
, int cmd
,
286 struct snd_soc_dai
*dai
)
288 struct fsl_audmix
*priv
= snd_soc_dai_get_drvdata(dai
);
289 unsigned long lock_flags
;
291 /* Capture stream shall not be handled */
292 if (substream
->stream
== SNDRV_PCM_STREAM_CAPTURE
)
296 case SNDRV_PCM_TRIGGER_START
:
297 case SNDRV_PCM_TRIGGER_RESUME
:
298 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
299 spin_lock_irqsave(&priv
->lock
, lock_flags
);
300 priv
->tdms
|= BIT(dai
->driver
->id
);
301 spin_unlock_irqrestore(&priv
->lock
, lock_flags
);
303 case SNDRV_PCM_TRIGGER_STOP
:
304 case SNDRV_PCM_TRIGGER_SUSPEND
:
305 case SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
306 spin_lock_irqsave(&priv
->lock
, lock_flags
);
307 priv
->tdms
&= ~BIT(dai
->driver
->id
);
308 spin_unlock_irqrestore(&priv
->lock
, lock_flags
);
317 static const struct snd_soc_dai_ops fsl_audmix_dai_ops
= {
318 .set_fmt
= fsl_audmix_dai_set_fmt
,
319 .trigger
= fsl_audmix_dai_trigger
,
322 static struct snd_soc_dai_driver fsl_audmix_dai
[] = {
327 .stream_name
= "AUDMIX-Playback-0",
332 .rates
= SNDRV_PCM_RATE_8000_96000
,
333 .formats
= FSL_AUDMIX_FORMATS
,
336 .stream_name
= "AUDMIX-Capture-0",
341 .rates
= SNDRV_PCM_RATE_8000_96000
,
342 .formats
= FSL_AUDMIX_FORMATS
,
344 .ops
= &fsl_audmix_dai_ops
,
350 .stream_name
= "AUDMIX-Playback-1",
355 .rates
= SNDRV_PCM_RATE_8000_96000
,
356 .formats
= FSL_AUDMIX_FORMATS
,
359 .stream_name
= "AUDMIX-Capture-1",
364 .rates
= SNDRV_PCM_RATE_8000_96000
,
365 .formats
= FSL_AUDMIX_FORMATS
,
367 .ops
= &fsl_audmix_dai_ops
,
371 static const struct snd_soc_component_driver fsl_audmix_component
= {
372 .name
= "fsl-audmix-dai",
373 .controls
= fsl_audmix_snd_controls
,
374 .num_controls
= ARRAY_SIZE(fsl_audmix_snd_controls
),
377 static bool fsl_audmix_readable_reg(struct device
*dev
, unsigned int reg
)
382 case FSL_AUDMIX_ATCR0
:
383 case FSL_AUDMIX_ATIVAL0
:
384 case FSL_AUDMIX_ATSTPUP0
:
385 case FSL_AUDMIX_ATSTPDN0
:
386 case FSL_AUDMIX_ATSTPTGT0
:
387 case FSL_AUDMIX_ATTNVAL0
:
388 case FSL_AUDMIX_ATSTP0
:
389 case FSL_AUDMIX_ATCR1
:
390 case FSL_AUDMIX_ATIVAL1
:
391 case FSL_AUDMIX_ATSTPUP1
:
392 case FSL_AUDMIX_ATSTPDN1
:
393 case FSL_AUDMIX_ATSTPTGT1
:
394 case FSL_AUDMIX_ATTNVAL1
:
395 case FSL_AUDMIX_ATSTP1
:
402 static bool fsl_audmix_writeable_reg(struct device
*dev
, unsigned int reg
)
406 case FSL_AUDMIX_ATCR0
:
407 case FSL_AUDMIX_ATIVAL0
:
408 case FSL_AUDMIX_ATSTPUP0
:
409 case FSL_AUDMIX_ATSTPDN0
:
410 case FSL_AUDMIX_ATSTPTGT0
:
411 case FSL_AUDMIX_ATCR1
:
412 case FSL_AUDMIX_ATIVAL1
:
413 case FSL_AUDMIX_ATSTPUP1
:
414 case FSL_AUDMIX_ATSTPDN1
:
415 case FSL_AUDMIX_ATSTPTGT1
:
422 static const struct reg_default fsl_audmix_reg
[] = {
423 { FSL_AUDMIX_CTR
, 0x00060 },
424 { FSL_AUDMIX_STR
, 0x00003 },
425 { FSL_AUDMIX_ATCR0
, 0x00000 },
426 { FSL_AUDMIX_ATIVAL0
, 0x3FFFF },
427 { FSL_AUDMIX_ATSTPUP0
, 0x2AAAA },
428 { FSL_AUDMIX_ATSTPDN0
, 0x30000 },
429 { FSL_AUDMIX_ATSTPTGT0
, 0x00010 },
430 { FSL_AUDMIX_ATTNVAL0
, 0x00000 },
431 { FSL_AUDMIX_ATSTP0
, 0x00000 },
432 { FSL_AUDMIX_ATCR1
, 0x00000 },
433 { FSL_AUDMIX_ATIVAL1
, 0x3FFFF },
434 { FSL_AUDMIX_ATSTPUP1
, 0x2AAAA },
435 { FSL_AUDMIX_ATSTPDN1
, 0x30000 },
436 { FSL_AUDMIX_ATSTPTGT1
, 0x00010 },
437 { FSL_AUDMIX_ATTNVAL1
, 0x00000 },
438 { FSL_AUDMIX_ATSTP1
, 0x00000 },
441 static const struct regmap_config fsl_audmix_regmap_config
= {
445 .max_register
= FSL_AUDMIX_ATSTP1
,
446 .reg_defaults
= fsl_audmix_reg
,
447 .num_reg_defaults
= ARRAY_SIZE(fsl_audmix_reg
),
448 .readable_reg
= fsl_audmix_readable_reg
,
449 .writeable_reg
= fsl_audmix_writeable_reg
,
450 .cache_type
= REGCACHE_FLAT
,
453 static const struct of_device_id fsl_audmix_ids
[] = {
455 .compatible
= "fsl,imx8qm-audmix",
456 .data
= "imx-audmix",
460 MODULE_DEVICE_TABLE(of
, fsl_audmix_ids
);
462 static int fsl_audmix_probe(struct platform_device
*pdev
)
464 struct device
*dev
= &pdev
->dev
;
465 struct fsl_audmix
*priv
;
467 const struct of_device_id
*of_id
;
471 of_id
= of_match_device(fsl_audmix_ids
, dev
);
472 if (!of_id
|| !of_id
->data
)
477 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
481 /* Get the addresses */
482 regs
= devm_platform_ioremap_resource(pdev
, 0);
484 return PTR_ERR(regs
);
486 priv
->regmap
= devm_regmap_init_mmio_clk(dev
, "ipg", regs
,
487 &fsl_audmix_regmap_config
);
488 if (IS_ERR(priv
->regmap
)) {
489 dev_err(dev
, "failed to init regmap\n");
490 return PTR_ERR(priv
->regmap
);
493 priv
->ipg_clk
= devm_clk_get(dev
, "ipg");
494 if (IS_ERR(priv
->ipg_clk
)) {
495 dev_err(dev
, "failed to get ipg clock\n");
496 return PTR_ERR(priv
->ipg_clk
);
499 spin_lock_init(&priv
->lock
);
500 platform_set_drvdata(pdev
, priv
);
501 pm_runtime_enable(dev
);
503 ret
= devm_snd_soc_register_component(dev
, &fsl_audmix_component
,
505 ARRAY_SIZE(fsl_audmix_dai
));
507 dev_err(dev
, "failed to register ASoC DAI\n");
511 priv
->pdev
= platform_device_register_data(dev
, mdrv
, 0, NULL
, 0);
512 if (IS_ERR(priv
->pdev
)) {
513 ret
= PTR_ERR(priv
->pdev
);
514 dev_err(dev
, "failed to register platform %s: %d\n", mdrv
, ret
);
521 pm_runtime_disable(dev
);
525 static int fsl_audmix_remove(struct platform_device
*pdev
)
527 struct fsl_audmix
*priv
= dev_get_drvdata(&pdev
->dev
);
529 pm_runtime_disable(&pdev
->dev
);
532 platform_device_unregister(priv
->pdev
);
538 static int fsl_audmix_runtime_resume(struct device
*dev
)
540 struct fsl_audmix
*priv
= dev_get_drvdata(dev
);
543 ret
= clk_prepare_enable(priv
->ipg_clk
);
545 dev_err(dev
, "Failed to enable IPG clock: %d\n", ret
);
549 regcache_cache_only(priv
->regmap
, false);
550 regcache_mark_dirty(priv
->regmap
);
552 return regcache_sync(priv
->regmap
);
555 static int fsl_audmix_runtime_suspend(struct device
*dev
)
557 struct fsl_audmix
*priv
= dev_get_drvdata(dev
);
559 regcache_cache_only(priv
->regmap
, true);
561 clk_disable_unprepare(priv
->ipg_clk
);
565 #endif /* CONFIG_PM */
567 static const struct dev_pm_ops fsl_audmix_pm
= {
568 SET_RUNTIME_PM_OPS(fsl_audmix_runtime_suspend
,
569 fsl_audmix_runtime_resume
,
571 SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend
,
572 pm_runtime_force_resume
)
575 static struct platform_driver fsl_audmix_driver
= {
576 .probe
= fsl_audmix_probe
,
577 .remove
= fsl_audmix_remove
,
579 .name
= "fsl-audmix",
580 .of_match_table
= fsl_audmix_ids
,
581 .pm
= &fsl_audmix_pm
,
584 module_platform_driver(fsl_audmix_driver
);
586 MODULE_DESCRIPTION("NXP AUDMIX ASoC DAI driver");
587 MODULE_AUTHOR("Viorel Suman <viorel.suman@nxp.com>");
588 MODULE_ALIAS("platform:fsl-audmix");
589 MODULE_LICENSE("GPL v2");