1 // SPDX-License-Identifier: GPL-2.0
3 // ALSA SoC IMX MQS driver
5 // Copyright (C) 2014-2015 Freescale Semiconductor, Inc.
9 #include <linux/firmware/imx/sm.h>
10 #include <linux/module.h>
11 #include <linux/moduleparam.h>
12 #include <linux/mfd/syscon.h>
13 #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
14 #include <linux/pm_runtime.h>
16 #include <linux/slab.h>
17 #include <sound/soc.h>
18 #include <sound/pcm.h>
19 #include <sound/initval.h>
21 #define REG_MQS_CTRL 0x00
23 #define MQS_EN_MASK (0x1 << 28)
24 #define MQS_EN_SHIFT (28)
25 #define MQS_SW_RST_MASK (0x1 << 24)
26 #define MQS_SW_RST_SHIFT (24)
27 #define MQS_OVERSAMPLE_MASK (0x1 << 20)
28 #define MQS_OVERSAMPLE_SHIFT (20)
29 #define MQS_CLK_DIV_MASK (0xFF << 0)
30 #define MQS_CLK_DIV_SHIFT (0)
33 TYPE_REG_OWN
, /* module own register space */
34 TYPE_REG_GPR
, /* register in GPR space */
35 TYPE_REG_SM
, /* System Manager controls the register */
39 * struct fsl_mqs_soc_data - soc specific data
41 * @type: control register space type
42 * @ctrl_off: control register offset
43 * @en_mask: enable bit mask
44 * @en_shift: enable bit shift
45 * @rst_mask: reset bit mask
46 * @rst_shift: reset bit shift
47 * @osr_mask: oversample bit mask
48 * @osr_shift: oversample bit shift
49 * @div_mask: clock divider mask
50 * @div_shift: clock divider bit shift
52 struct fsl_mqs_soc_data
{
65 /* codec private data */
67 struct regmap
*regmap
;
70 const struct fsl_mqs_soc_data
*soc
;
72 unsigned int reg_mqs_ctrl
;
75 #define FSL_MQS_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
76 #define FSL_MQS_FORMATS SNDRV_PCM_FMTBIT_S16_LE
78 static int fsl_mqs_sm_read(void *context
, unsigned int reg
, unsigned int *val
)
80 struct fsl_mqs
*mqs_priv
= context
;
83 if (IS_ENABLED(CONFIG_IMX_SCMI_MISC_DRV
) &&
84 mqs_priv
->soc
->ctrl_off
== reg
)
85 return scmi_imx_misc_ctrl_get(SCMI_IMX_CTRL_MQS1_SETTINGS
, &num
, val
);
90 static int fsl_mqs_sm_write(void *context
, unsigned int reg
, unsigned int val
)
92 struct fsl_mqs
*mqs_priv
= context
;
94 if (IS_ENABLED(CONFIG_IMX_SCMI_MISC_DRV
) &&
95 mqs_priv
->soc
->ctrl_off
== reg
)
96 return scmi_imx_misc_ctrl_set(SCMI_IMX_CTRL_MQS1_SETTINGS
, val
);
101 static int fsl_mqs_hw_params(struct snd_pcm_substream
*substream
,
102 struct snd_pcm_hw_params
*params
,
103 struct snd_soc_dai
*dai
)
105 struct snd_soc_component
*component
= dai
->component
;
106 struct fsl_mqs
*mqs_priv
= snd_soc_component_get_drvdata(component
);
107 unsigned long mclk_rate
;
111 mclk_rate
= clk_get_rate(mqs_priv
->mclk
);
112 lrclk
= params_rate(params
);
115 * mclk_rate / (oversample(32,64) * FS * 2 * divider ) = repeat_rate;
116 * if repeat_rate is 8, mqs can achieve better quality.
117 * oversample rate is fix to 32 currently.
119 div
= mclk_rate
/ (32 * lrclk
* 2 * 8);
120 res
= mclk_rate
% (32 * lrclk
* 2 * 8);
122 if (res
== 0 && div
> 0 && div
<= 256) {
123 regmap_update_bits(mqs_priv
->regmap
, mqs_priv
->soc
->ctrl_off
,
124 mqs_priv
->soc
->div_mask
,
125 (div
- 1) << mqs_priv
->soc
->div_shift
);
126 regmap_update_bits(mqs_priv
->regmap
, mqs_priv
->soc
->ctrl_off
,
127 mqs_priv
->soc
->osr_mask
, 0);
129 dev_err(component
->dev
, "can't get proper divider\n");
135 static int fsl_mqs_set_dai_fmt(struct snd_soc_dai
*dai
, unsigned int fmt
)
137 /* Only LEFT_J & SLAVE mode is supported. */
138 switch (fmt
& SND_SOC_DAIFMT_FORMAT_MASK
) {
139 case SND_SOC_DAIFMT_LEFT_J
:
145 switch (fmt
& SND_SOC_DAIFMT_INV_MASK
) {
146 case SND_SOC_DAIFMT_NB_NF
:
152 switch (fmt
& SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK
) {
153 case SND_SOC_DAIFMT_CBC_CFC
:
162 static int fsl_mqs_startup(struct snd_pcm_substream
*substream
,
163 struct snd_soc_dai
*dai
)
165 struct snd_soc_component
*component
= dai
->component
;
166 struct fsl_mqs
*mqs_priv
= snd_soc_component_get_drvdata(component
);
168 regmap_update_bits(mqs_priv
->regmap
, mqs_priv
->soc
->ctrl_off
,
169 mqs_priv
->soc
->en_mask
,
170 1 << mqs_priv
->soc
->en_shift
);
174 static void fsl_mqs_shutdown(struct snd_pcm_substream
*substream
,
175 struct snd_soc_dai
*dai
)
177 struct snd_soc_component
*component
= dai
->component
;
178 struct fsl_mqs
*mqs_priv
= snd_soc_component_get_drvdata(component
);
180 regmap_update_bits(mqs_priv
->regmap
, mqs_priv
->soc
->ctrl_off
,
181 mqs_priv
->soc
->en_mask
, 0);
184 static const struct snd_soc_component_driver soc_codec_fsl_mqs
= {
188 static const struct snd_soc_dai_ops fsl_mqs_dai_ops
= {
189 .startup
= fsl_mqs_startup
,
190 .shutdown
= fsl_mqs_shutdown
,
191 .hw_params
= fsl_mqs_hw_params
,
192 .set_fmt
= fsl_mqs_set_dai_fmt
,
195 static struct snd_soc_dai_driver fsl_mqs_dai
= {
196 .name
= "fsl-mqs-dai",
198 .stream_name
= "Playback",
201 .rates
= FSL_MQS_RATES
,
202 .formats
= FSL_MQS_FORMATS
,
204 .ops
= &fsl_mqs_dai_ops
,
207 static const struct regmap_config fsl_mqs_regmap_config
= {
211 .max_register
= REG_MQS_CTRL
,
212 .cache_type
= REGCACHE_NONE
,
215 static const struct regmap_config fsl_mqs_sm_regmap
= {
218 .reg_read
= fsl_mqs_sm_read
,
219 .reg_write
= fsl_mqs_sm_write
,
222 static int fsl_mqs_probe(struct platform_device
*pdev
)
224 struct device_node
*np
= pdev
->dev
.of_node
;
225 struct device_node
*gpr_np
= NULL
;
226 struct fsl_mqs
*mqs_priv
;
230 mqs_priv
= devm_kzalloc(&pdev
->dev
, sizeof(*mqs_priv
), GFP_KERNEL
);
234 /* On i.MX6sx the MQS control register is in GPR domain
235 * But in i.MX8QM/i.MX8QXP the control register is moved
238 mqs_priv
->soc
= of_device_get_match_data(&pdev
->dev
);
240 if (mqs_priv
->soc
->type
== TYPE_REG_GPR
) {
241 gpr_np
= of_parse_phandle(np
, "gpr", 0);
243 dev_err(&pdev
->dev
, "failed to get gpr node by phandle\n");
247 mqs_priv
->regmap
= syscon_node_to_regmap(gpr_np
);
249 if (IS_ERR(mqs_priv
->regmap
)) {
250 dev_err(&pdev
->dev
, "failed to get gpr regmap\n");
251 return PTR_ERR(mqs_priv
->regmap
);
253 } else if (mqs_priv
->soc
->type
== TYPE_REG_SM
) {
254 mqs_priv
->regmap
= devm_regmap_init(&pdev
->dev
,
258 if (IS_ERR(mqs_priv
->regmap
)) {
259 dev_err(&pdev
->dev
, "failed to init regmap: %ld\n",
260 PTR_ERR(mqs_priv
->regmap
));
261 return PTR_ERR(mqs_priv
->regmap
);
264 regs
= devm_platform_ioremap_resource(pdev
, 0);
266 return PTR_ERR(regs
);
268 mqs_priv
->regmap
= devm_regmap_init_mmio_clk(&pdev
->dev
,
271 &fsl_mqs_regmap_config
);
272 if (IS_ERR(mqs_priv
->regmap
)) {
273 dev_err(&pdev
->dev
, "failed to init regmap: %ld\n",
274 PTR_ERR(mqs_priv
->regmap
));
275 return PTR_ERR(mqs_priv
->regmap
);
278 mqs_priv
->ipg
= devm_clk_get(&pdev
->dev
, "core");
279 if (IS_ERR(mqs_priv
->ipg
)) {
280 dev_err(&pdev
->dev
, "failed to get the clock: %ld\n",
281 PTR_ERR(mqs_priv
->ipg
));
282 return PTR_ERR(mqs_priv
->ipg
);
286 mqs_priv
->mclk
= devm_clk_get(&pdev
->dev
, "mclk");
287 if (IS_ERR(mqs_priv
->mclk
)) {
288 dev_err(&pdev
->dev
, "failed to get the clock: %ld\n",
289 PTR_ERR(mqs_priv
->mclk
));
290 return PTR_ERR(mqs_priv
->mclk
);
293 dev_set_drvdata(&pdev
->dev
, mqs_priv
);
294 pm_runtime_enable(&pdev
->dev
);
296 ret
= devm_snd_soc_register_component(&pdev
->dev
, &soc_codec_fsl_mqs
,
304 static void fsl_mqs_remove(struct platform_device
*pdev
)
306 pm_runtime_disable(&pdev
->dev
);
309 static int fsl_mqs_runtime_resume(struct device
*dev
)
311 struct fsl_mqs
*mqs_priv
= dev_get_drvdata(dev
);
314 ret
= clk_prepare_enable(mqs_priv
->ipg
);
316 dev_err(dev
, "failed to enable ipg clock\n");
320 ret
= clk_prepare_enable(mqs_priv
->mclk
);
322 dev_err(dev
, "failed to enable mclk clock\n");
323 clk_disable_unprepare(mqs_priv
->ipg
);
327 regmap_write(mqs_priv
->regmap
, mqs_priv
->soc
->ctrl_off
, mqs_priv
->reg_mqs_ctrl
);
331 static int fsl_mqs_runtime_suspend(struct device
*dev
)
333 struct fsl_mqs
*mqs_priv
= dev_get_drvdata(dev
);
335 regmap_read(mqs_priv
->regmap
, mqs_priv
->soc
->ctrl_off
, &mqs_priv
->reg_mqs_ctrl
);
337 clk_disable_unprepare(mqs_priv
->mclk
);
338 clk_disable_unprepare(mqs_priv
->ipg
);
343 static const struct dev_pm_ops fsl_mqs_pm_ops
= {
344 RUNTIME_PM_OPS(fsl_mqs_runtime_suspend
, fsl_mqs_runtime_resume
, NULL
)
345 SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend
, pm_runtime_force_resume
)
348 static const struct fsl_mqs_soc_data fsl_mqs_imx8qm_data
= {
349 .type
= TYPE_REG_OWN
,
350 .ctrl_off
= REG_MQS_CTRL
,
351 .en_mask
= MQS_EN_MASK
,
352 .en_shift
= MQS_EN_SHIFT
,
353 .rst_mask
= MQS_SW_RST_MASK
,
354 .rst_shift
= MQS_SW_RST_SHIFT
,
355 .osr_mask
= MQS_OVERSAMPLE_MASK
,
356 .osr_shift
= MQS_OVERSAMPLE_SHIFT
,
357 .div_mask
= MQS_CLK_DIV_MASK
,
358 .div_shift
= MQS_CLK_DIV_SHIFT
,
361 static const struct fsl_mqs_soc_data fsl_mqs_imx6sx_data
= {
362 .type
= TYPE_REG_GPR
,
363 .ctrl_off
= IOMUXC_GPR2
,
364 .en_mask
= IMX6SX_GPR2_MQS_EN_MASK
,
365 .en_shift
= IMX6SX_GPR2_MQS_EN_SHIFT
,
366 .rst_mask
= IMX6SX_GPR2_MQS_SW_RST_MASK
,
367 .rst_shift
= IMX6SX_GPR2_MQS_SW_RST_SHIFT
,
368 .osr_mask
= IMX6SX_GPR2_MQS_OVERSAMPLE_MASK
,
369 .osr_shift
= IMX6SX_GPR2_MQS_OVERSAMPLE_SHIFT
,
370 .div_mask
= IMX6SX_GPR2_MQS_CLK_DIV_MASK
,
371 .div_shift
= IMX6SX_GPR2_MQS_CLK_DIV_SHIFT
,
374 static const struct fsl_mqs_soc_data fsl_mqs_imx93_data
= {
375 .type
= TYPE_REG_GPR
,
383 .div_mask
= GENMASK(15, 8),
387 static const struct fsl_mqs_soc_data fsl_mqs_imx95_aon_data
= {
396 .div_mask
= GENMASK(15, 8),
400 static const struct fsl_mqs_soc_data fsl_mqs_imx95_netc_data
= {
401 .type
= TYPE_REG_GPR
,
409 .div_mask
= GENMASK(16, 9),
413 static const struct of_device_id fsl_mqs_dt_ids
[] = {
414 { .compatible
= "fsl,imx8qm-mqs", .data
= &fsl_mqs_imx8qm_data
},
415 { .compatible
= "fsl,imx6sx-mqs", .data
= &fsl_mqs_imx6sx_data
},
416 { .compatible
= "fsl,imx93-mqs", .data
= &fsl_mqs_imx93_data
},
417 { .compatible
= "fsl,imx95-aonmix-mqs", .data
= &fsl_mqs_imx95_aon_data
},
418 { .compatible
= "fsl,imx95-netcmix-mqs", .data
= &fsl_mqs_imx95_netc_data
},
421 MODULE_DEVICE_TABLE(of
, fsl_mqs_dt_ids
);
423 static struct platform_driver fsl_mqs_driver
= {
424 .probe
= fsl_mqs_probe
,
425 .remove
= fsl_mqs_remove
,
428 .of_match_table
= fsl_mqs_dt_ids
,
429 .pm
= pm_ptr(&fsl_mqs_pm_ops
),
433 module_platform_driver(fsl_mqs_driver
);
435 MODULE_AUTHOR("Shengjiu Wang <Shengjiu.Wang@nxp.com>");
436 MODULE_DESCRIPTION("MQS codec driver");
437 MODULE_LICENSE("GPL v2");
438 MODULE_ALIAS("platform:fsl-mqs");