File Index
Files and directories
Sign in
FILE

fsl_mqs.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/fsl/fsl_mqs.c C
  1// SPDX-License-Identifier: GPL-2.0
  2//
  3// ALSA SoC IMX MQS driver
  4//
  5// Copyright (C) 2014-2015 Freescale Semiconductor, Inc.
  6// Copyright 2019 NXP
  7
  8#include <linux/clk.h>
  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>
 15#include <linux/pm.h>
 16#include <linux/slab.h>
 17#include <sound/soc.h>
 18#include <sound/pcm.h>
 19#include <sound/initval.h>
 20
 21#define REG_MQS_CTRL		0x00
 22
 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)
 31
 32enum reg_type {
 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 */
 36};
 37
 38/**
 39 * struct fsl_mqs_soc_data - soc specific data
 40 *
 41 * @type: control register space type
 42 * @sm_index: index from definition in system manager
 43 * @ctrl_off: control register offset
 44 * @en_mask: enable bit mask
 45 * @en_shift: enable bit shift
 46 * @rst_mask: reset bit mask
 47 * @rst_shift: reset bit shift
 48 * @osr_mask: oversample bit mask
 49 * @osr_shift: oversample bit shift
 50 * @div_mask: clock divider mask
 51 * @div_shift: clock divider bit shift
 52 */
 53struct fsl_mqs_soc_data {
 54	enum reg_type type;
 55	int  sm_index;
 56	int  ctrl_off;
 57	int  en_mask;
 58	int  en_shift;
 59	int  rst_mask;
 60	int  rst_shift;
 61	int  osr_mask;
 62	int  osr_shift;
 63	int  div_mask;
 64	int  div_shift;
 65};
 66
 67/* codec private data */
 68struct fsl_mqs {
 69	struct regmap *regmap;
 70	struct clk *mclk;
 71	struct clk *ipg;
 72	const struct fsl_mqs_soc_data *soc;
 73
 74	unsigned int reg_mqs_ctrl;
 75};
 76
 77#define FSL_MQS_RATES	(SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
 78#define FSL_MQS_FORMATS	SNDRV_PCM_FMTBIT_S16_LE
 79
 80static int fsl_mqs_sm_read(void *context, unsigned int reg, unsigned int *val)
 81{
 82	struct fsl_mqs *mqs_priv = context;
 83	int num = 1;
 84
 85	if (IS_ENABLED(CONFIG_IMX_SCMI_MISC_DRV) &&
 86	    mqs_priv->soc->ctrl_off == reg)
 87		return scmi_imx_misc_ctrl_get(mqs_priv->soc->sm_index, &num, val);
 88
 89	return -EINVAL;
 90};
 91
 92static int fsl_mqs_sm_write(void *context, unsigned int reg, unsigned int val)
 93{
 94	struct fsl_mqs *mqs_priv = context;
 95
 96	if (IS_ENABLED(CONFIG_IMX_SCMI_MISC_DRV) &&
 97	    mqs_priv->soc->ctrl_off == reg)
 98		return scmi_imx_misc_ctrl_set(mqs_priv->soc->sm_index, val);
 99
100	return -EINVAL;
101};
102
103static int fsl_mqs_hw_params(struct snd_pcm_substream *substream,
104			     struct snd_pcm_hw_params *params,
105			     struct snd_soc_dai *dai)
106{
107	struct snd_soc_component *component = dai->component;
108	struct fsl_mqs *mqs_priv = snd_soc_component_get_drvdata(component);
109	unsigned long mclk_rate;
110	int div, res;
111	int lrclk;
112
113	mclk_rate = clk_get_rate(mqs_priv->mclk);
114	lrclk = params_rate(params);
115
116	/*
117	 * mclk_rate / (oversample(32,64) * FS * 2 * divider ) = repeat_rate;
118	 * if repeat_rate is 8, mqs can achieve better quality.
119	 * oversample rate is fix to 32 currently.
120	 */
121	div = mclk_rate / (32 * lrclk * 2 * 8);
122	res = mclk_rate % (32 * lrclk * 2 * 8);
123
124	if (res == 0 && div > 0 && div <= 256) {
125		regmap_update_bits(mqs_priv->regmap, mqs_priv->soc->ctrl_off,
126				   mqs_priv->soc->div_mask,
127				   (div - 1) << mqs_priv->soc->div_shift);
128		regmap_update_bits(mqs_priv->regmap, mqs_priv->soc->ctrl_off,
129				   mqs_priv->soc->osr_mask, 0);
130	} else {
131		dev_err(component->dev, "can't get proper divider\n");
132	}
133
134	return 0;
135}
136
137static int fsl_mqs_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
138{
139	/* Only LEFT_J & SLAVE mode is supported. */
140	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
141	case SND_SOC_DAIFMT_LEFT_J:
142		break;
143	default:
144		return -EINVAL;
145	}
146
147	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
148	case SND_SOC_DAIFMT_NB_NF:
149		break;
150	default:
151		return -EINVAL;
152	}
153
154	switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
155	case SND_SOC_DAIFMT_CBC_CFC:
156		break;
157	default:
158		return -EINVAL;
159	}
160
161	return 0;
162}
163
164static int fsl_mqs_startup(struct snd_pcm_substream *substream,
165			   struct snd_soc_dai *dai)
166{
167	struct snd_soc_component *component = dai->component;
168	struct fsl_mqs *mqs_priv = snd_soc_component_get_drvdata(component);
169
170	regmap_update_bits(mqs_priv->regmap, mqs_priv->soc->ctrl_off,
171			   mqs_priv->soc->en_mask,
172			   1 << mqs_priv->soc->en_shift);
173	return 0;
174}
175
176static void fsl_mqs_shutdown(struct snd_pcm_substream *substream,
177			     struct snd_soc_dai *dai)
178{
179	struct snd_soc_component *component = dai->component;
180	struct fsl_mqs *mqs_priv = snd_soc_component_get_drvdata(component);
181
182	regmap_update_bits(mqs_priv->regmap, mqs_priv->soc->ctrl_off,
183			   mqs_priv->soc->en_mask, 0);
184}
185
186static const struct snd_soc_component_driver soc_codec_fsl_mqs = {
187	.idle_bias_on = 1,
188};
189
190static const struct snd_soc_dai_ops fsl_mqs_dai_ops = {
191	.startup = fsl_mqs_startup,
192	.shutdown = fsl_mqs_shutdown,
193	.hw_params = fsl_mqs_hw_params,
194	.set_fmt = fsl_mqs_set_dai_fmt,
195};
196
197static struct snd_soc_dai_driver fsl_mqs_dai = {
198	.name		= "fsl-mqs-dai",
199	.playback	= {
200		.stream_name	= "Playback",
201		.channels_min	= 2,
202		.channels_max	= 2,
203		.rates		= FSL_MQS_RATES,
204		.formats	= FSL_MQS_FORMATS,
205	},
206	.ops = &fsl_mqs_dai_ops,
207};
208
209static const struct regmap_config fsl_mqs_regmap_config = {
210	.reg_bits = 32,
211	.reg_stride = 4,
212	.val_bits = 32,
213	.max_register = REG_MQS_CTRL,
214	.cache_type = REGCACHE_NONE,
215};
216
217static const struct regmap_config fsl_mqs_sm_regmap = {
218	.reg_bits = 32,
219	.val_bits = 32,
220	.reg_read = fsl_mqs_sm_read,
221	.reg_write = fsl_mqs_sm_write,
222};
223
224static int fsl_mqs_probe(struct platform_device *pdev)
225{
226	struct device_node *np = pdev->dev.of_node;
227	struct device_node *gpr_np = NULL;
228	struct fsl_mqs *mqs_priv;
229	void __iomem *regs;
230	int ret;
231
232	mqs_priv = devm_kzalloc(&pdev->dev, sizeof(*mqs_priv), GFP_KERNEL);
233	if (!mqs_priv)
234		return -ENOMEM;
235
236	/* On i.MX6sx the MQS control register is in GPR domain
237	 * But in i.MX8QM/i.MX8QXP the control register is moved
238	 * to its own domain.
239	 */
240	mqs_priv->soc = of_device_get_match_data(&pdev->dev);
241
242	if (mqs_priv->soc->type == TYPE_REG_GPR) {
243		gpr_np = of_parse_phandle(np, "gpr", 0);
244		if (!gpr_np) {
245			dev_err(&pdev->dev, "failed to get gpr node by phandle\n");
246			return -EINVAL;
247		}
248
249		mqs_priv->regmap = syscon_node_to_regmap(gpr_np);
250		of_node_put(gpr_np);
251		if (IS_ERR(mqs_priv->regmap)) {
252			dev_err(&pdev->dev, "failed to get gpr regmap\n");
253			return PTR_ERR(mqs_priv->regmap);
254		}
255	} else if (mqs_priv->soc->type == TYPE_REG_SM) {
256		mqs_priv->regmap = devm_regmap_init(&pdev->dev,
257						    NULL,
258						    mqs_priv,
259						    &fsl_mqs_sm_regmap);
260		if (IS_ERR(mqs_priv->regmap)) {
261			dev_err(&pdev->dev, "failed to init regmap: %ld\n",
262				PTR_ERR(mqs_priv->regmap));
263			return PTR_ERR(mqs_priv->regmap);
264		}
265	} else {
266		regs = devm_platform_ioremap_resource(pdev, 0);
267		if (IS_ERR(regs))
268			return PTR_ERR(regs);
269
270		mqs_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
271							     "core",
272							     regs,
273							     &fsl_mqs_regmap_config);
274		if (IS_ERR(mqs_priv->regmap)) {
275			dev_err(&pdev->dev, "failed to init regmap: %ld\n",
276				PTR_ERR(mqs_priv->regmap));
277			return PTR_ERR(mqs_priv->regmap);
278		}
279
280		mqs_priv->ipg = devm_clk_get(&pdev->dev, "core");
281		if (IS_ERR(mqs_priv->ipg)) {
282			dev_err(&pdev->dev, "failed to get the clock: %ld\n",
283				PTR_ERR(mqs_priv->ipg));
284			return PTR_ERR(mqs_priv->ipg);
285		}
286	}
287
288	mqs_priv->mclk = devm_clk_get(&pdev->dev, "mclk");
289	if (IS_ERR(mqs_priv->mclk)) {
290		dev_err(&pdev->dev, "failed to get the clock: %ld\n",
291			PTR_ERR(mqs_priv->mclk));
292		return PTR_ERR(mqs_priv->mclk);
293	}
294
295	dev_set_drvdata(&pdev->dev, mqs_priv);
296	pm_runtime_enable(&pdev->dev);
297
298	ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_fsl_mqs,
299			&fsl_mqs_dai, 1);
300	if (ret)
301		return ret;
302
303	return 0;
304}
305
306static void fsl_mqs_remove(struct platform_device *pdev)
307{
308	pm_runtime_disable(&pdev->dev);
309}
310
311static int fsl_mqs_runtime_resume(struct device *dev)
312{
313	struct fsl_mqs *mqs_priv = dev_get_drvdata(dev);
314	int ret;
315
316	ret = clk_prepare_enable(mqs_priv->ipg);
317	if (ret) {
318		dev_err(dev, "failed to enable ipg clock\n");
319		return ret;
320	}
321
322	ret = clk_prepare_enable(mqs_priv->mclk);
323	if (ret) {
324		dev_err(dev, "failed to enable mclk clock\n");
325		clk_disable_unprepare(mqs_priv->ipg);
326		return ret;
327	}
328
329	regmap_write(mqs_priv->regmap, mqs_priv->soc->ctrl_off, mqs_priv->reg_mqs_ctrl);
330	return 0;
331}
332
333static int fsl_mqs_runtime_suspend(struct device *dev)
334{
335	struct fsl_mqs *mqs_priv = dev_get_drvdata(dev);
336
337	regmap_read(mqs_priv->regmap, mqs_priv->soc->ctrl_off, &mqs_priv->reg_mqs_ctrl);
338
339	clk_disable_unprepare(mqs_priv->mclk);
340	clk_disable_unprepare(mqs_priv->ipg);
341
342	return 0;
343}
344
345static const struct dev_pm_ops fsl_mqs_pm_ops = {
346	RUNTIME_PM_OPS(fsl_mqs_runtime_suspend, fsl_mqs_runtime_resume, NULL)
347	SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
348};
349
350static const struct fsl_mqs_soc_data fsl_mqs_imx8qm_data = {
351	.type = TYPE_REG_OWN,
352	.ctrl_off = REG_MQS_CTRL,
353	.en_mask  = MQS_EN_MASK,
354	.en_shift = MQS_EN_SHIFT,
355	.rst_mask = MQS_SW_RST_MASK,
356	.rst_shift = MQS_SW_RST_SHIFT,
357	.osr_mask = MQS_OVERSAMPLE_MASK,
358	.osr_shift = MQS_OVERSAMPLE_SHIFT,
359	.div_mask = MQS_CLK_DIV_MASK,
360	.div_shift = MQS_CLK_DIV_SHIFT,
361};
362
363static const struct fsl_mqs_soc_data fsl_mqs_imx6sx_data = {
364	.type = TYPE_REG_GPR,
365	.ctrl_off = IOMUXC_GPR2,
366	.en_mask  = IMX6SX_GPR2_MQS_EN_MASK,
367	.en_shift = IMX6SX_GPR2_MQS_EN_SHIFT,
368	.rst_mask = IMX6SX_GPR2_MQS_SW_RST_MASK,
369	.rst_shift = IMX6SX_GPR2_MQS_SW_RST_SHIFT,
370	.osr_mask  = IMX6SX_GPR2_MQS_OVERSAMPLE_MASK,
371	.osr_shift = IMX6SX_GPR2_MQS_OVERSAMPLE_SHIFT,
372	.div_mask  = IMX6SX_GPR2_MQS_CLK_DIV_MASK,
373	.div_shift = IMX6SX_GPR2_MQS_CLK_DIV_SHIFT,
374};
375
376static const struct fsl_mqs_soc_data fsl_mqs_imx93_data = {
377	.type = TYPE_REG_GPR,
378	.ctrl_off = 0x20,
379	.en_mask  = BIT(1),
380	.en_shift = 1,
381	.rst_mask = BIT(2),
382	.rst_shift = 2,
383	.osr_mask = BIT(3),
384	.osr_shift = 3,
385	.div_mask = GENMASK(15, 8),
386	.div_shift = 8,
387};
388
389static const struct fsl_mqs_soc_data fsl_mqs_imx95_aon_data = {
390	.type = TYPE_REG_SM,
391	.sm_index = SCMI_IMX95_CTRL_MQS1_SETTINGS,
392	.ctrl_off = 0x88,
393	.en_mask  = BIT(1),
394	.en_shift = 1,
395	.rst_mask = BIT(2),
396	.rst_shift = 2,
397	.osr_mask = BIT(3),
398	.osr_shift = 3,
399	.div_mask = GENMASK(15, 8),
400	.div_shift = 8,
401};
402
403static const struct fsl_mqs_soc_data fsl_mqs_imx95_netc_data = {
404	.type = TYPE_REG_GPR,
405	.ctrl_off = 0x0,
406	.en_mask  = BIT(2),
407	.en_shift = 2,
408	.rst_mask = BIT(3),
409	.rst_shift = 3,
410	.osr_mask = BIT(4),
411	.osr_shift = 4,
412	.div_mask = GENMASK(16, 9),
413	.div_shift = 9,
414};
415
416static const struct fsl_mqs_soc_data fsl_mqs_imx943_aon_data = {
417	.type = TYPE_REG_SM,
418	.sm_index = SCMI_IMX94_CTRL_MQS1_SETTINGS,
419	.ctrl_off = 0x88,
420	.en_mask  = BIT(1),
421	.en_shift = 1,
422	.rst_mask = BIT(2),
423	.rst_shift = 2,
424	.osr_mask = BIT(3),
425	.osr_shift = 3,
426	.div_mask = GENMASK(15, 8),
427	.div_shift = 8,
428};
429
430static const struct fsl_mqs_soc_data fsl_mqs_imx943_wakeup_data = {
431	.type = TYPE_REG_SM,
432	.sm_index = SCMI_IMX94_CTRL_MQS2_SETTINGS,
433	.ctrl_off = 0x10,
434	.en_mask  = BIT(1),
435	.en_shift = 1,
436	.rst_mask = BIT(2),
437	.rst_shift = 2,
438	.osr_mask = BIT(3),
439	.osr_shift = 3,
440	.div_mask = GENMASK(15, 8),
441	.div_shift = 8,
442};
443
444static const struct of_device_id fsl_mqs_dt_ids[] = {
445	{ .compatible = "fsl,imx8qm-mqs", .data = &fsl_mqs_imx8qm_data },
446	{ .compatible = "fsl,imx6sx-mqs", .data = &fsl_mqs_imx6sx_data },
447	{ .compatible = "fsl,imx93-mqs", .data = &fsl_mqs_imx93_data },
448	{ .compatible = "fsl,imx95-aonmix-mqs", .data = &fsl_mqs_imx95_aon_data },
449	{ .compatible = "fsl,imx95-netcmix-mqs", .data = &fsl_mqs_imx95_netc_data },
450	{ .compatible = "fsl,imx943-aonmix-mqs", .data = &fsl_mqs_imx943_aon_data },
451	{ .compatible = "fsl,imx943-wakeupmix-mqs", .data = &fsl_mqs_imx943_wakeup_data },
452	{}
453};
454MODULE_DEVICE_TABLE(of, fsl_mqs_dt_ids);
455
456static struct platform_driver fsl_mqs_driver = {
457	.probe		= fsl_mqs_probe,
458	.remove		= fsl_mqs_remove,
459	.driver		= {
460		.name	= "fsl-mqs",
461		.of_match_table = fsl_mqs_dt_ids,
462		.pm = pm_ptr(&fsl_mqs_pm_ops),
463	},
464};
465
466module_platform_driver(fsl_mqs_driver);
467
468MODULE_AUTHOR("Shengjiu Wang <Shengjiu.Wang@nxp.com>");
469MODULE_DESCRIPTION("MQS codec driver");
470MODULE_LICENSE("GPL v2");
471MODULE_ALIAS("platform:fsl-mqs");
~/ Read-only file preview.