Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/fsl/fsl_mqs.c
26428 views
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
32
enum 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
*/
53
struct 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 */
68
struct 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
80
static 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
92
static 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
103
static 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
137
static 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
164
static 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
176
static 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
186
static const struct snd_soc_component_driver soc_codec_fsl_mqs = {
187
.idle_bias_on = 1,
188
};
189
190
static 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
197
static 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
209
static 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
217
static 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
224
static 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
306
static void fsl_mqs_remove(struct platform_device *pdev)
307
{
308
pm_runtime_disable(&pdev->dev);
309
}
310
311
static 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
333
static 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
345
static 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
350
static 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
363
static 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
376
static 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
389
static 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
403
static 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
416
static 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
430
static 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
444
static 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
};
454
MODULE_DEVICE_TABLE(of, fsl_mqs_dt_ids);
455
456
static 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
466
module_platform_driver(fsl_mqs_driver);
467
468
MODULE_AUTHOR("Shengjiu Wang <[email protected]>");
469
MODULE_DESCRIPTION("MQS codec driver");
470
MODULE_LICENSE("GPL v2");
471
MODULE_ALIAS("platform:fsl-mqs");
472
473