Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/fsl/fsl_audmix.c
26427 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* NXP AUDMIX ALSA SoC Digital Audio Interface (DAI) driver
4
*
5
* Copyright 2017 NXP
6
*/
7
8
#include <linux/clk.h>
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>
14
15
#include "fsl_audmix.h"
16
17
#define SOC_ENUM_SINGLE_S(xreg, xshift, xtexts) \
18
SOC_ENUM_SINGLE(xreg, xshift, ARRAY_SIZE(xtexts), xtexts)
19
20
static const char
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", };
27
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),
43
};
44
45
struct fsl_audmix_state {
46
u8 tdms;
47
u8 clk;
48
char msg[64];
49
};
50
51
static const struct fsl_audmix_state prms[4][4] = {{
52
/* DIS->DIS, do nothing */
53
{ .tdms = 0, .clk = 0, .msg = "" },
54
/* DIS->TDM1*/
55
{ .tdms = 1, .clk = 1, .msg = "DIS->TDM1: TDM1 not started!\n" },
56
/* DIS->TDM2*/
57
{ .tdms = 2, .clk = 2, .msg = "DIS->TDM2: TDM2 not started!\n" },
58
/* DIS->MIX */
59
{ .tdms = 3, .clk = 0, .msg = "DIS->MIX: Please start both TDMs!\n" }
60
}, { /* TDM1->DIS */
61
{ .tdms = 1, .clk = 0, .msg = "TDM1->DIS: TDM1 not started!\n" },
62
/* TDM1->TDM1, do nothing */
63
{ .tdms = 0, .clk = 0, .msg = "" },
64
/* TDM1->TDM2 */
65
{ .tdms = 3, .clk = 2, .msg = "TDM1->TDM2: Please start both TDMs!\n" },
66
/* TDM1->MIX */
67
{ .tdms = 3, .clk = 0, .msg = "TDM1->MIX: Please start both TDMs!\n" }
68
}, { /* TDM2->DIS */
69
{ .tdms = 2, .clk = 0, .msg = "TDM2->DIS: TDM2 not started!\n" },
70
/* TDM2->TDM1 */
71
{ .tdms = 3, .clk = 1, .msg = "TDM2->TDM1: Please start both TDMs!\n" },
72
/* TDM2->TDM2, do nothing */
73
{ .tdms = 0, .clk = 0, .msg = "" },
74
/* TDM2->MIX */
75
{ .tdms = 3, .clk = 0, .msg = "TDM2->MIX: Please start both TDMs!\n" }
76
}, { /* MIX->DIS */
77
{ .tdms = 3, .clk = 0, .msg = "MIX->DIS: Please start both TDMs!\n" },
78
/* MIX->TDM1 */
79
{ .tdms = 3, .clk = 1, .msg = "MIX->TDM1: Please start both TDMs!\n" },
80
/* MIX->TDM2 */
81
{ .tdms = 3, .clk = 2, .msg = "MIX->TDM2: Please start both TDMs!\n" },
82
/* MIX->MIX, do nothing */
83
{ .tdms = 0, .clk = 0, .msg = "" }
84
}, };
85
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)
89
{
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);
94
return -EINVAL;
95
}
96
97
switch (prm.clk) {
98
case 1:
99
case 2:
100
/* Set mix clock */
101
(*mask) |= FSL_AUDMIX_CTR_MIXCLK_MASK;
102
(*ctr) |= FSL_AUDMIX_CTR_MIXCLK(prm.clk - 1);
103
break;
104
default:
105
break;
106
}
107
108
return 0;
109
}
110
111
static int fsl_audmix_put_mix_clk_src(struct snd_kcontrol *kcontrol,
112
struct snd_ctl_elem_value *ucontrol)
113
{
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;
119
120
/* Get current state */
121
reg_val = snd_soc_component_read(comp, FSL_AUDMIX_CTR);
122
mix_clk = ((reg_val & FSL_AUDMIX_CTR_MIXCLK_MASK)
123
>> FSL_AUDMIX_CTR_MIXCLK_SHIFT);
124
val = snd_soc_enum_item_to_val(e, item[0]);
125
126
dev_dbg(comp->dev, "TDMs=x%08x, val=x%08x\n", priv->tdms, val);
127
128
/**
129
* Ensure the current selected mixer clock is available
130
* for configuration propagation
131
*/
132
if (!(priv->tdms & BIT(mix_clk))) {
133
dev_err(comp->dev,
134
"Started TDM%d needed for config propagation!\n",
135
mix_clk + 1);
136
return -EINVAL;
137
}
138
139
if (!(priv->tdms & BIT(val))) {
140
dev_err(comp->dev,
141
"The selected clock source has no TDM%d enabled!\n",
142
val + 1);
143
return -EINVAL;
144
}
145
146
return snd_soc_put_enum_double(kcontrol, ucontrol);
147
}
148
149
static int fsl_audmix_put_out_src(struct snd_kcontrol *kcontrol,
150
struct snd_ctl_elem_value *ucontrol)
151
{
152
struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
153
struct fsl_audmix *priv = snd_soc_component_get_drvdata(comp);
154
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
155
unsigned int *item = ucontrol->value.enumerated.item;
156
u32 out_src, mix_clk;
157
unsigned int reg_val, val, mask = 0, ctr = 0;
158
int ret;
159
160
/* Get current state */
161
reg_val = snd_soc_component_read(comp, FSL_AUDMIX_CTR);
162
163
/* "From" state */
164
out_src = ((reg_val & FSL_AUDMIX_CTR_OUTSRC_MASK)
165
>> FSL_AUDMIX_CTR_OUTSRC_SHIFT);
166
mix_clk = ((reg_val & FSL_AUDMIX_CTR_MIXCLK_MASK)
167
>> FSL_AUDMIX_CTR_MIXCLK_SHIFT);
168
169
/* "To" state */
170
val = snd_soc_enum_item_to_val(e, item[0]);
171
172
dev_dbg(comp->dev, "TDMs=x%08x, val=x%08x\n", priv->tdms, val);
173
174
/* Check if state is changing ... */
175
if (out_src == val)
176
return 0;
177
/**
178
* Ensure the current selected mixer clock is available
179
* for configuration propagation
180
*/
181
if (!(priv->tdms & BIT(mix_clk))) {
182
dev_err(comp->dev,
183
"Started TDM%d needed for config propagation!\n",
184
mix_clk + 1);
185
return -EINVAL;
186
}
187
188
/* Check state transition constraints */
189
ret = fsl_audmix_state_trans(comp, &mask, &ctr, prms[out_src][val]);
190
if (ret)
191
return ret;
192
193
/* Complete transition to new state */
194
mask |= FSL_AUDMIX_CTR_OUTSRC_MASK;
195
ctr |= FSL_AUDMIX_CTR_OUTSRC(val);
196
197
return snd_soc_component_update_bits(comp, FSL_AUDMIX_CTR, mask, ctr);
198
}
199
200
static const struct snd_kcontrol_new fsl_audmix_snd_controls[] = {
201
/* FSL_AUDMIX_CTR controls */
202
SOC_ENUM_EXT("Mixing Clock Source", fsl_audmix_enum[0],
203
snd_soc_get_enum_double, fsl_audmix_put_mix_clk_src),
204
SOC_ENUM_EXT("Output Source", fsl_audmix_enum[1],
205
snd_soc_get_enum_double, fsl_audmix_put_out_src),
206
SOC_ENUM("Output Width", fsl_audmix_enum[2]),
207
SOC_ENUM("Frame Rate Diff Error", fsl_audmix_enum[3]),
208
SOC_ENUM("Clock Freq Diff Error", fsl_audmix_enum[4]),
209
SOC_ENUM("Sync Mode Config", fsl_audmix_enum[5]),
210
SOC_ENUM("Sync Mode Clk Source", fsl_audmix_enum[6]),
211
/* TDM1 Attenuation controls */
212
SOC_ENUM("TDM1 Attenuation", fsl_audmix_enum[7]),
213
SOC_ENUM("TDM1 Attenuation Direction", fsl_audmix_enum[8]),
214
SOC_SINGLE("TDM1 Attenuation Step Divider", FSL_AUDMIX_ATCR0,
215
2, 0x00fff, 0),
216
SOC_SINGLE("TDM1 Attenuation Initial Value", FSL_AUDMIX_ATIVAL0,
217
0, 0x3ffff, 0),
218
SOC_SINGLE("TDM1 Attenuation Step Up Factor", FSL_AUDMIX_ATSTPUP0,
219
0, 0x3ffff, 0),
220
SOC_SINGLE("TDM1 Attenuation Step Down Factor", FSL_AUDMIX_ATSTPDN0,
221
0, 0x3ffff, 0),
222
SOC_SINGLE("TDM1 Attenuation Step Target", FSL_AUDMIX_ATSTPTGT0,
223
0, 0x3ffff, 0),
224
/* TDM2 Attenuation controls */
225
SOC_ENUM("TDM2 Attenuation", fsl_audmix_enum[9]),
226
SOC_ENUM("TDM2 Attenuation Direction", fsl_audmix_enum[10]),
227
SOC_SINGLE("TDM2 Attenuation Step Divider", FSL_AUDMIX_ATCR1,
228
2, 0x00fff, 0),
229
SOC_SINGLE("TDM2 Attenuation Initial Value", FSL_AUDMIX_ATIVAL1,
230
0, 0x3ffff, 0),
231
SOC_SINGLE("TDM2 Attenuation Step Up Factor", FSL_AUDMIX_ATSTPUP1,
232
0, 0x3ffff, 0),
233
SOC_SINGLE("TDM2 Attenuation Step Down Factor", FSL_AUDMIX_ATSTPDN1,
234
0, 0x3ffff, 0),
235
SOC_SINGLE("TDM2 Attenuation Step Target", FSL_AUDMIX_ATSTPTGT1,
236
0, 0x3ffff, 0),
237
};
238
239
static int fsl_audmix_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
240
{
241
struct snd_soc_component *comp = dai->component;
242
u32 mask = 0, ctr = 0;
243
244
/* AUDMIX is working in DSP_A format only */
245
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
246
case SND_SOC_DAIFMT_DSP_A:
247
break;
248
default:
249
return -EINVAL;
250
}
251
252
/* For playback the AUDMIX is consumer, and for record is provider */
253
switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
254
case SND_SOC_DAIFMT_BC_FC:
255
case SND_SOC_DAIFMT_BP_FP:
256
break;
257
default:
258
return -EINVAL;
259
}
260
261
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
262
case SND_SOC_DAIFMT_IB_NF:
263
/* Output data will be written on positive edge of the clock */
264
ctr |= FSL_AUDMIX_CTR_OUTCKPOL(0);
265
break;
266
case SND_SOC_DAIFMT_NB_NF:
267
/* Output data will be written on negative edge of the clock */
268
ctr |= FSL_AUDMIX_CTR_OUTCKPOL(1);
269
break;
270
default:
271
return -EINVAL;
272
}
273
274
mask |= FSL_AUDMIX_CTR_OUTCKPOL_MASK;
275
276
return snd_soc_component_update_bits(comp, FSL_AUDMIX_CTR, mask, ctr);
277
}
278
279
static int fsl_audmix_dai_trigger(struct snd_pcm_substream *substream, int cmd,
280
struct snd_soc_dai *dai)
281
{
282
struct fsl_audmix *priv = snd_soc_dai_get_drvdata(dai);
283
unsigned long lock_flags;
284
285
/* Capture stream shall not be handled */
286
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
287
return 0;
288
289
switch (cmd) {
290
case SNDRV_PCM_TRIGGER_START:
291
case SNDRV_PCM_TRIGGER_RESUME:
292
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
293
spin_lock_irqsave(&priv->lock, lock_flags);
294
priv->tdms |= BIT(dai->driver->id);
295
spin_unlock_irqrestore(&priv->lock, lock_flags);
296
break;
297
case SNDRV_PCM_TRIGGER_STOP:
298
case SNDRV_PCM_TRIGGER_SUSPEND:
299
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
300
spin_lock_irqsave(&priv->lock, lock_flags);
301
priv->tdms &= ~BIT(dai->driver->id);
302
spin_unlock_irqrestore(&priv->lock, lock_flags);
303
break;
304
default:
305
return -EINVAL;
306
}
307
308
return 0;
309
}
310
311
static const struct snd_soc_dai_ops fsl_audmix_dai_ops = {
312
.set_fmt = fsl_audmix_dai_set_fmt,
313
.trigger = fsl_audmix_dai_trigger,
314
};
315
316
static struct snd_soc_dai_driver fsl_audmix_dai[] = {
317
{
318
.id = 0,
319
.name = "audmix-0",
320
.playback = {
321
.stream_name = "AUDMIX-Playback-0",
322
.channels_min = 8,
323
.channels_max = 8,
324
.rate_min = 8000,
325
.rate_max = 96000,
326
.rates = SNDRV_PCM_RATE_8000_96000,
327
.formats = FSL_AUDMIX_FORMATS,
328
},
329
.ops = &fsl_audmix_dai_ops,
330
},
331
{
332
.id = 1,
333
.name = "audmix-1",
334
.playback = {
335
.stream_name = "AUDMIX-Playback-1",
336
.channels_min = 8,
337
.channels_max = 8,
338
.rate_min = 8000,
339
.rate_max = 96000,
340
.rates = SNDRV_PCM_RATE_8000_96000,
341
.formats = FSL_AUDMIX_FORMATS,
342
},
343
.ops = &fsl_audmix_dai_ops,
344
},
345
{
346
.id = 2,
347
.name = "audmix-2",
348
.capture = {
349
.stream_name = "AUDMIX-Capture-0",
350
.channels_min = 8,
351
.channels_max = 8,
352
.rate_min = 8000,
353
.rate_max = 96000,
354
.rates = SNDRV_PCM_RATE_8000_96000,
355
.formats = FSL_AUDMIX_FORMATS,
356
},
357
.ops = &fsl_audmix_dai_ops,
358
},
359
};
360
361
static const struct snd_soc_component_driver fsl_audmix_component = {
362
.name = "fsl-audmix-dai",
363
.controls = fsl_audmix_snd_controls,
364
.num_controls = ARRAY_SIZE(fsl_audmix_snd_controls),
365
};
366
367
static bool fsl_audmix_readable_reg(struct device *dev, unsigned int reg)
368
{
369
switch (reg) {
370
case FSL_AUDMIX_CTR:
371
case FSL_AUDMIX_STR:
372
case FSL_AUDMIX_ATCR0:
373
case FSL_AUDMIX_ATIVAL0:
374
case FSL_AUDMIX_ATSTPUP0:
375
case FSL_AUDMIX_ATSTPDN0:
376
case FSL_AUDMIX_ATSTPTGT0:
377
case FSL_AUDMIX_ATTNVAL0:
378
case FSL_AUDMIX_ATSTP0:
379
case FSL_AUDMIX_ATCR1:
380
case FSL_AUDMIX_ATIVAL1:
381
case FSL_AUDMIX_ATSTPUP1:
382
case FSL_AUDMIX_ATSTPDN1:
383
case FSL_AUDMIX_ATSTPTGT1:
384
case FSL_AUDMIX_ATTNVAL1:
385
case FSL_AUDMIX_ATSTP1:
386
return true;
387
default:
388
return false;
389
}
390
}
391
392
static bool fsl_audmix_writeable_reg(struct device *dev, unsigned int reg)
393
{
394
switch (reg) {
395
case FSL_AUDMIX_CTR:
396
case FSL_AUDMIX_ATCR0:
397
case FSL_AUDMIX_ATIVAL0:
398
case FSL_AUDMIX_ATSTPUP0:
399
case FSL_AUDMIX_ATSTPDN0:
400
case FSL_AUDMIX_ATSTPTGT0:
401
case FSL_AUDMIX_ATCR1:
402
case FSL_AUDMIX_ATIVAL1:
403
case FSL_AUDMIX_ATSTPUP1:
404
case FSL_AUDMIX_ATSTPDN1:
405
case FSL_AUDMIX_ATSTPTGT1:
406
return true;
407
default:
408
return false;
409
}
410
}
411
412
static const struct reg_default fsl_audmix_reg[] = {
413
{ FSL_AUDMIX_CTR, 0x00060 },
414
{ FSL_AUDMIX_STR, 0x00003 },
415
{ FSL_AUDMIX_ATCR0, 0x00000 },
416
{ FSL_AUDMIX_ATIVAL0, 0x3FFFF },
417
{ FSL_AUDMIX_ATSTPUP0, 0x2AAAA },
418
{ FSL_AUDMIX_ATSTPDN0, 0x30000 },
419
{ FSL_AUDMIX_ATSTPTGT0, 0x00010 },
420
{ FSL_AUDMIX_ATTNVAL0, 0x00000 },
421
{ FSL_AUDMIX_ATSTP0, 0x00000 },
422
{ FSL_AUDMIX_ATCR1, 0x00000 },
423
{ FSL_AUDMIX_ATIVAL1, 0x3FFFF },
424
{ FSL_AUDMIX_ATSTPUP1, 0x2AAAA },
425
{ FSL_AUDMIX_ATSTPDN1, 0x30000 },
426
{ FSL_AUDMIX_ATSTPTGT1, 0x00010 },
427
{ FSL_AUDMIX_ATTNVAL1, 0x00000 },
428
{ FSL_AUDMIX_ATSTP1, 0x00000 },
429
};
430
431
static const struct regmap_config fsl_audmix_regmap_config = {
432
.reg_bits = 32,
433
.reg_stride = 4,
434
.val_bits = 32,
435
.max_register = FSL_AUDMIX_ATSTP1,
436
.reg_defaults = fsl_audmix_reg,
437
.num_reg_defaults = ARRAY_SIZE(fsl_audmix_reg),
438
.readable_reg = fsl_audmix_readable_reg,
439
.writeable_reg = fsl_audmix_writeable_reg,
440
.cache_type = REGCACHE_FLAT,
441
};
442
443
static const struct of_device_id fsl_audmix_ids[] = {
444
{
445
.compatible = "fsl,imx8qm-audmix",
446
},
447
{ /* sentinel */ }
448
};
449
MODULE_DEVICE_TABLE(of, fsl_audmix_ids);
450
451
static int fsl_audmix_probe(struct platform_device *pdev)
452
{
453
struct device *dev = &pdev->dev;
454
struct fsl_audmix *priv;
455
void __iomem *regs;
456
int ret;
457
458
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
459
if (!priv)
460
return -ENOMEM;
461
462
/* Get the addresses */
463
regs = devm_platform_ioremap_resource(pdev, 0);
464
if (IS_ERR(regs))
465
return PTR_ERR(regs);
466
467
priv->regmap = devm_regmap_init_mmio(dev, regs, &fsl_audmix_regmap_config);
468
if (IS_ERR(priv->regmap)) {
469
dev_err(dev, "failed to init regmap\n");
470
return PTR_ERR(priv->regmap);
471
}
472
473
priv->ipg_clk = devm_clk_get(dev, "ipg");
474
if (IS_ERR(priv->ipg_clk)) {
475
dev_err(dev, "failed to get ipg clock\n");
476
return PTR_ERR(priv->ipg_clk);
477
}
478
479
spin_lock_init(&priv->lock);
480
platform_set_drvdata(pdev, priv);
481
pm_runtime_enable(dev);
482
483
ret = devm_snd_soc_register_component(dev, &fsl_audmix_component,
484
fsl_audmix_dai,
485
ARRAY_SIZE(fsl_audmix_dai));
486
if (ret) {
487
dev_err(dev, "failed to register ASoC DAI\n");
488
goto err_disable_pm;
489
}
490
491
/*
492
* If dais property exist, then register the imx-audmix card driver.
493
* otherwise, it should be linked by audio graph card.
494
*/
495
if (of_find_property(pdev->dev.of_node, "dais", NULL)) {
496
priv->pdev = platform_device_register_data(dev, "imx-audmix", 0, NULL, 0);
497
if (IS_ERR(priv->pdev)) {
498
ret = PTR_ERR(priv->pdev);
499
dev_err(dev, "failed to register platform: %d\n", ret);
500
goto err_disable_pm;
501
}
502
}
503
504
return 0;
505
506
err_disable_pm:
507
pm_runtime_disable(dev);
508
return ret;
509
}
510
511
static void fsl_audmix_remove(struct platform_device *pdev)
512
{
513
struct fsl_audmix *priv = dev_get_drvdata(&pdev->dev);
514
515
pm_runtime_disable(&pdev->dev);
516
517
if (priv->pdev)
518
platform_device_unregister(priv->pdev);
519
}
520
521
static int fsl_audmix_runtime_resume(struct device *dev)
522
{
523
struct fsl_audmix *priv = dev_get_drvdata(dev);
524
int ret;
525
526
ret = clk_prepare_enable(priv->ipg_clk);
527
if (ret) {
528
dev_err(dev, "Failed to enable IPG clock: %d\n", ret);
529
return ret;
530
}
531
532
regcache_cache_only(priv->regmap, false);
533
regcache_mark_dirty(priv->regmap);
534
535
return regcache_sync(priv->regmap);
536
}
537
538
static int fsl_audmix_runtime_suspend(struct device *dev)
539
{
540
struct fsl_audmix *priv = dev_get_drvdata(dev);
541
542
regcache_cache_only(priv->regmap, true);
543
544
clk_disable_unprepare(priv->ipg_clk);
545
546
return 0;
547
}
548
549
static const struct dev_pm_ops fsl_audmix_pm = {
550
RUNTIME_PM_OPS(fsl_audmix_runtime_suspend, fsl_audmix_runtime_resume,
551
NULL)
552
SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
553
};
554
555
static struct platform_driver fsl_audmix_driver = {
556
.probe = fsl_audmix_probe,
557
.remove = fsl_audmix_remove,
558
.driver = {
559
.name = "fsl-audmix",
560
.of_match_table = fsl_audmix_ids,
561
.pm = pm_ptr(&fsl_audmix_pm),
562
},
563
};
564
module_platform_driver(fsl_audmix_driver);
565
566
MODULE_DESCRIPTION("NXP AUDMIX ASoC DAI driver");
567
MODULE_AUTHOR("Viorel Suman <[email protected]>");
568
MODULE_ALIAS("platform:fsl-audmix");
569
MODULE_LICENSE("GPL v2");
570
571