Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/samsung/pcm.c
26427 views
1
// SPDX-License-Identifier: GPL-2.0
2
//
3
// ALSA SoC Audio Layer - S3C PCM-Controller driver
4
//
5
// Copyright (c) 2009 Samsung Electronics Co. Ltd
6
// Author: Jaswinder Singh <[email protected]>
7
// based upon I2S drivers by Ben Dooks.
8
9
#include <linux/clk.h>
10
#include <linux/io.h>
11
#include <linux/module.h>
12
#include <linux/pm_runtime.h>
13
14
#include <sound/soc.h>
15
#include <sound/pcm_params.h>
16
17
#include <linux/platform_data/asoc-s3c.h>
18
19
#include "dma.h"
20
#include "pcm.h"
21
22
/*Register Offsets */
23
#define S3C_PCM_CTL 0x00
24
#define S3C_PCM_CLKCTL 0x04
25
#define S3C_PCM_TXFIFO 0x08
26
#define S3C_PCM_RXFIFO 0x0C
27
#define S3C_PCM_IRQCTL 0x10
28
#define S3C_PCM_IRQSTAT 0x14
29
#define S3C_PCM_FIFOSTAT 0x18
30
#define S3C_PCM_CLRINT 0x20
31
32
/* PCM_CTL Bit-Fields */
33
#define S3C_PCM_CTL_TXDIPSTICK_MASK 0x3f
34
#define S3C_PCM_CTL_TXDIPSTICK_SHIFT 13
35
#define S3C_PCM_CTL_RXDIPSTICK_MASK 0x3f
36
#define S3C_PCM_CTL_RXDIPSTICK_SHIFT 7
37
#define S3C_PCM_CTL_TXDMA_EN (0x1 << 6)
38
#define S3C_PCM_CTL_RXDMA_EN (0x1 << 5)
39
#define S3C_PCM_CTL_TXMSB_AFTER_FSYNC (0x1 << 4)
40
#define S3C_PCM_CTL_RXMSB_AFTER_FSYNC (0x1 << 3)
41
#define S3C_PCM_CTL_TXFIFO_EN (0x1 << 2)
42
#define S3C_PCM_CTL_RXFIFO_EN (0x1 << 1)
43
#define S3C_PCM_CTL_ENABLE (0x1 << 0)
44
45
/* PCM_CLKCTL Bit-Fields */
46
#define S3C_PCM_CLKCTL_SERCLK_EN (0x1 << 19)
47
#define S3C_PCM_CLKCTL_SERCLKSEL_PCLK (0x1 << 18)
48
#define S3C_PCM_CLKCTL_SCLKDIV_MASK 0x1ff
49
#define S3C_PCM_CLKCTL_SYNCDIV_MASK 0x1ff
50
#define S3C_PCM_CLKCTL_SCLKDIV_SHIFT 9
51
#define S3C_PCM_CLKCTL_SYNCDIV_SHIFT 0
52
53
/* PCM_TXFIFO Bit-Fields */
54
#define S3C_PCM_TXFIFO_DVALID (0x1 << 16)
55
#define S3C_PCM_TXFIFO_DATA_MSK (0xffff << 0)
56
57
/* PCM_RXFIFO Bit-Fields */
58
#define S3C_PCM_RXFIFO_DVALID (0x1 << 16)
59
#define S3C_PCM_RXFIFO_DATA_MSK (0xffff << 0)
60
61
/* PCM_IRQCTL Bit-Fields */
62
#define S3C_PCM_IRQCTL_IRQEN (0x1 << 14)
63
#define S3C_PCM_IRQCTL_WRDEN (0x1 << 12)
64
#define S3C_PCM_IRQCTL_TXEMPTYEN (0x1 << 11)
65
#define S3C_PCM_IRQCTL_TXALMSTEMPTYEN (0x1 << 10)
66
#define S3C_PCM_IRQCTL_TXFULLEN (0x1 << 9)
67
#define S3C_PCM_IRQCTL_TXALMSTFULLEN (0x1 << 8)
68
#define S3C_PCM_IRQCTL_TXSTARVEN (0x1 << 7)
69
#define S3C_PCM_IRQCTL_TXERROVRFLEN (0x1 << 6)
70
#define S3C_PCM_IRQCTL_RXEMPTEN (0x1 << 5)
71
#define S3C_PCM_IRQCTL_RXALMSTEMPTEN (0x1 << 4)
72
#define S3C_PCM_IRQCTL_RXFULLEN (0x1 << 3)
73
#define S3C_PCM_IRQCTL_RXALMSTFULLEN (0x1 << 2)
74
#define S3C_PCM_IRQCTL_RXSTARVEN (0x1 << 1)
75
#define S3C_PCM_IRQCTL_RXERROVRFLEN (0x1 << 0)
76
77
/* PCM_IRQSTAT Bit-Fields */
78
#define S3C_PCM_IRQSTAT_IRQPND (0x1 << 13)
79
#define S3C_PCM_IRQSTAT_WRD_XFER (0x1 << 12)
80
#define S3C_PCM_IRQSTAT_TXEMPTY (0x1 << 11)
81
#define S3C_PCM_IRQSTAT_TXALMSTEMPTY (0x1 << 10)
82
#define S3C_PCM_IRQSTAT_TXFULL (0x1 << 9)
83
#define S3C_PCM_IRQSTAT_TXALMSTFULL (0x1 << 8)
84
#define S3C_PCM_IRQSTAT_TXSTARV (0x1 << 7)
85
#define S3C_PCM_IRQSTAT_TXERROVRFL (0x1 << 6)
86
#define S3C_PCM_IRQSTAT_RXEMPT (0x1 << 5)
87
#define S3C_PCM_IRQSTAT_RXALMSTEMPT (0x1 << 4)
88
#define S3C_PCM_IRQSTAT_RXFULL (0x1 << 3)
89
#define S3C_PCM_IRQSTAT_RXALMSTFULL (0x1 << 2)
90
#define S3C_PCM_IRQSTAT_RXSTARV (0x1 << 1)
91
#define S3C_PCM_IRQSTAT_RXERROVRFL (0x1 << 0)
92
93
/* PCM_FIFOSTAT Bit-Fields */
94
#define S3C_PCM_FIFOSTAT_TXCNT_MSK (0x3f << 14)
95
#define S3C_PCM_FIFOSTAT_TXFIFOEMPTY (0x1 << 13)
96
#define S3C_PCM_FIFOSTAT_TXFIFOALMSTEMPTY (0x1 << 12)
97
#define S3C_PCM_FIFOSTAT_TXFIFOFULL (0x1 << 11)
98
#define S3C_PCM_FIFOSTAT_TXFIFOALMSTFULL (0x1 << 10)
99
#define S3C_PCM_FIFOSTAT_RXCNT_MSK (0x3f << 4)
100
#define S3C_PCM_FIFOSTAT_RXFIFOEMPTY (0x1 << 3)
101
#define S3C_PCM_FIFOSTAT_RXFIFOALMSTEMPTY (0x1 << 2)
102
#define S3C_PCM_FIFOSTAT_RXFIFOFULL (0x1 << 1)
103
#define S3C_PCM_FIFOSTAT_RXFIFOALMSTFULL (0x1 << 0)
104
105
/**
106
* struct s3c_pcm_info - S3C PCM Controller information
107
* @lock: Spin lock
108
* @dev: The parent device passed to use from the probe.
109
* @regs: The pointer to the device register block.
110
* @sclk_per_fs: number of sclk per frame sync
111
* @idleclk: Whether to keep PCMSCLK enabled even when idle (no active xfer)
112
* @pclk: the PCLK_PCM (pcm) clock pointer
113
* @cclk: the SCLK_AUDIO (audio-bus) clock pointer
114
* @dma_playback: DMA information for playback channel.
115
* @dma_capture: DMA information for capture channel.
116
*/
117
struct s3c_pcm_info {
118
spinlock_t lock;
119
struct device *dev;
120
void __iomem *regs;
121
122
unsigned int sclk_per_fs;
123
124
/* Whether to keep PCMSCLK enabled even when idle(no active xfer) */
125
unsigned int idleclk;
126
127
struct clk *pclk;
128
struct clk *cclk;
129
130
struct snd_dmaengine_dai_dma_data *dma_playback;
131
struct snd_dmaengine_dai_dma_data *dma_capture;
132
};
133
134
static struct snd_dmaengine_dai_dma_data s3c_pcm_stereo_out[] = {
135
[0] = {
136
.addr_width = 4,
137
},
138
[1] = {
139
.addr_width = 4,
140
},
141
};
142
143
static struct snd_dmaengine_dai_dma_data s3c_pcm_stereo_in[] = {
144
[0] = {
145
.addr_width = 4,
146
},
147
[1] = {
148
.addr_width = 4,
149
},
150
};
151
152
static struct s3c_pcm_info s3c_pcm[2];
153
154
static void s3c_pcm_snd_txctrl(struct s3c_pcm_info *pcm, int on)
155
{
156
void __iomem *regs = pcm->regs;
157
u32 ctl, clkctl;
158
159
clkctl = readl(regs + S3C_PCM_CLKCTL);
160
ctl = readl(regs + S3C_PCM_CTL);
161
ctl &= ~(S3C_PCM_CTL_TXDIPSTICK_MASK
162
<< S3C_PCM_CTL_TXDIPSTICK_SHIFT);
163
164
if (on) {
165
ctl |= S3C_PCM_CTL_TXDMA_EN;
166
ctl |= S3C_PCM_CTL_TXFIFO_EN;
167
ctl |= S3C_PCM_CTL_ENABLE;
168
ctl |= (0x4<<S3C_PCM_CTL_TXDIPSTICK_SHIFT);
169
clkctl |= S3C_PCM_CLKCTL_SERCLK_EN;
170
} else {
171
ctl &= ~S3C_PCM_CTL_TXDMA_EN;
172
ctl &= ~S3C_PCM_CTL_TXFIFO_EN;
173
174
if (!(ctl & S3C_PCM_CTL_RXFIFO_EN)) {
175
ctl &= ~S3C_PCM_CTL_ENABLE;
176
if (!pcm->idleclk)
177
clkctl |= S3C_PCM_CLKCTL_SERCLK_EN;
178
}
179
}
180
181
writel(clkctl, regs + S3C_PCM_CLKCTL);
182
writel(ctl, regs + S3C_PCM_CTL);
183
}
184
185
static void s3c_pcm_snd_rxctrl(struct s3c_pcm_info *pcm, int on)
186
{
187
void __iomem *regs = pcm->regs;
188
u32 ctl, clkctl;
189
190
ctl = readl(regs + S3C_PCM_CTL);
191
clkctl = readl(regs + S3C_PCM_CLKCTL);
192
ctl &= ~(S3C_PCM_CTL_RXDIPSTICK_MASK
193
<< S3C_PCM_CTL_RXDIPSTICK_SHIFT);
194
195
if (on) {
196
ctl |= S3C_PCM_CTL_RXDMA_EN;
197
ctl |= S3C_PCM_CTL_RXFIFO_EN;
198
ctl |= S3C_PCM_CTL_ENABLE;
199
ctl |= (0x20<<S3C_PCM_CTL_RXDIPSTICK_SHIFT);
200
clkctl |= S3C_PCM_CLKCTL_SERCLK_EN;
201
} else {
202
ctl &= ~S3C_PCM_CTL_RXDMA_EN;
203
ctl &= ~S3C_PCM_CTL_RXFIFO_EN;
204
205
if (!(ctl & S3C_PCM_CTL_TXFIFO_EN)) {
206
ctl &= ~S3C_PCM_CTL_ENABLE;
207
if (!pcm->idleclk)
208
clkctl |= S3C_PCM_CLKCTL_SERCLK_EN;
209
}
210
}
211
212
writel(clkctl, regs + S3C_PCM_CLKCTL);
213
writel(ctl, regs + S3C_PCM_CTL);
214
}
215
216
static int s3c_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
217
struct snd_soc_dai *dai)
218
{
219
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
220
struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
221
unsigned long flags;
222
223
dev_dbg(pcm->dev, "Entered %s\n", __func__);
224
225
switch (cmd) {
226
case SNDRV_PCM_TRIGGER_START:
227
case SNDRV_PCM_TRIGGER_RESUME:
228
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
229
spin_lock_irqsave(&pcm->lock, flags);
230
231
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
232
s3c_pcm_snd_rxctrl(pcm, 1);
233
else
234
s3c_pcm_snd_txctrl(pcm, 1);
235
236
spin_unlock_irqrestore(&pcm->lock, flags);
237
break;
238
239
case SNDRV_PCM_TRIGGER_STOP:
240
case SNDRV_PCM_TRIGGER_SUSPEND:
241
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
242
spin_lock_irqsave(&pcm->lock, flags);
243
244
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
245
s3c_pcm_snd_rxctrl(pcm, 0);
246
else
247
s3c_pcm_snd_txctrl(pcm, 0);
248
249
spin_unlock_irqrestore(&pcm->lock, flags);
250
break;
251
252
default:
253
return -EINVAL;
254
}
255
256
return 0;
257
}
258
259
static int s3c_pcm_hw_params(struct snd_pcm_substream *substream,
260
struct snd_pcm_hw_params *params,
261
struct snd_soc_dai *socdai)
262
{
263
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
264
struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(snd_soc_rtd_to_cpu(rtd, 0));
265
void __iomem *regs = pcm->regs;
266
struct clk *clk;
267
int sclk_div, sync_div;
268
unsigned long flags;
269
u32 clkctl;
270
271
dev_dbg(pcm->dev, "Entered %s\n", __func__);
272
273
/* Strictly check for sample size */
274
switch (params_width(params)) {
275
case 16:
276
break;
277
default:
278
return -EINVAL;
279
}
280
281
spin_lock_irqsave(&pcm->lock, flags);
282
283
/* Get hold of the PCMSOURCE_CLK */
284
clkctl = readl(regs + S3C_PCM_CLKCTL);
285
if (clkctl & S3C_PCM_CLKCTL_SERCLKSEL_PCLK)
286
clk = pcm->pclk;
287
else
288
clk = pcm->cclk;
289
290
/* Set the SCLK divider */
291
sclk_div = clk_get_rate(clk) / pcm->sclk_per_fs /
292
params_rate(params) / 2 - 1;
293
294
clkctl &= ~(S3C_PCM_CLKCTL_SCLKDIV_MASK
295
<< S3C_PCM_CLKCTL_SCLKDIV_SHIFT);
296
clkctl |= ((sclk_div & S3C_PCM_CLKCTL_SCLKDIV_MASK)
297
<< S3C_PCM_CLKCTL_SCLKDIV_SHIFT);
298
299
/* Set the SYNC divider */
300
sync_div = pcm->sclk_per_fs - 1;
301
302
clkctl &= ~(S3C_PCM_CLKCTL_SYNCDIV_MASK
303
<< S3C_PCM_CLKCTL_SYNCDIV_SHIFT);
304
clkctl |= ((sync_div & S3C_PCM_CLKCTL_SYNCDIV_MASK)
305
<< S3C_PCM_CLKCTL_SYNCDIV_SHIFT);
306
307
writel(clkctl, regs + S3C_PCM_CLKCTL);
308
309
spin_unlock_irqrestore(&pcm->lock, flags);
310
311
dev_dbg(pcm->dev, "PCMSOURCE_CLK-%lu SCLK=%ufs SCLK_DIV=%d SYNC_DIV=%d\n",
312
clk_get_rate(clk), pcm->sclk_per_fs,
313
sclk_div, sync_div);
314
315
return 0;
316
}
317
318
static int s3c_pcm_set_fmt(struct snd_soc_dai *cpu_dai,
319
unsigned int fmt)
320
{
321
struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(cpu_dai);
322
void __iomem *regs = pcm->regs;
323
unsigned long flags;
324
int ret = 0;
325
u32 ctl;
326
327
dev_dbg(pcm->dev, "Entered %s\n", __func__);
328
329
spin_lock_irqsave(&pcm->lock, flags);
330
331
ctl = readl(regs + S3C_PCM_CTL);
332
333
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
334
case SND_SOC_DAIFMT_IB_NF:
335
/* Nothing to do, IB_NF by default */
336
break;
337
default:
338
dev_err(pcm->dev, "Unsupported clock inversion!\n");
339
ret = -EINVAL;
340
goto exit;
341
}
342
343
switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
344
case SND_SOC_DAIFMT_BP_FP:
345
/* Nothing to do, Master by default */
346
break;
347
default:
348
dev_err(pcm->dev, "Unsupported master/slave format!\n");
349
ret = -EINVAL;
350
goto exit;
351
}
352
353
switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
354
case SND_SOC_DAIFMT_CONT:
355
pcm->idleclk = 1;
356
break;
357
case SND_SOC_DAIFMT_GATED:
358
pcm->idleclk = 0;
359
break;
360
default:
361
dev_err(pcm->dev, "Invalid Clock gating request!\n");
362
ret = -EINVAL;
363
goto exit;
364
}
365
366
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
367
case SND_SOC_DAIFMT_DSP_A:
368
ctl |= S3C_PCM_CTL_TXMSB_AFTER_FSYNC;
369
ctl |= S3C_PCM_CTL_RXMSB_AFTER_FSYNC;
370
break;
371
case SND_SOC_DAIFMT_DSP_B:
372
ctl &= ~S3C_PCM_CTL_TXMSB_AFTER_FSYNC;
373
ctl &= ~S3C_PCM_CTL_RXMSB_AFTER_FSYNC;
374
break;
375
default:
376
dev_err(pcm->dev, "Unsupported data format!\n");
377
ret = -EINVAL;
378
goto exit;
379
}
380
381
writel(ctl, regs + S3C_PCM_CTL);
382
383
exit:
384
spin_unlock_irqrestore(&pcm->lock, flags);
385
386
return ret;
387
}
388
389
static int s3c_pcm_set_clkdiv(struct snd_soc_dai *cpu_dai,
390
int div_id, int div)
391
{
392
struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(cpu_dai);
393
394
switch (div_id) {
395
case S3C_PCM_SCLK_PER_FS:
396
pcm->sclk_per_fs = div;
397
break;
398
399
default:
400
return -EINVAL;
401
}
402
403
return 0;
404
}
405
406
static int s3c_pcm_set_sysclk(struct snd_soc_dai *cpu_dai,
407
int clk_id, unsigned int freq, int dir)
408
{
409
struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(cpu_dai);
410
void __iomem *regs = pcm->regs;
411
u32 clkctl = readl(regs + S3C_PCM_CLKCTL);
412
413
switch (clk_id) {
414
case S3C_PCM_CLKSRC_PCLK:
415
clkctl |= S3C_PCM_CLKCTL_SERCLKSEL_PCLK;
416
break;
417
418
case S3C_PCM_CLKSRC_MUX:
419
clkctl &= ~S3C_PCM_CLKCTL_SERCLKSEL_PCLK;
420
421
if (clk_get_rate(pcm->cclk) != freq)
422
clk_set_rate(pcm->cclk, freq);
423
424
break;
425
426
default:
427
return -EINVAL;
428
}
429
430
writel(clkctl, regs + S3C_PCM_CLKCTL);
431
432
return 0;
433
}
434
435
static int s3c_pcm_dai_probe(struct snd_soc_dai *dai)
436
{
437
struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(dai);
438
439
snd_soc_dai_init_dma_data(dai, pcm->dma_playback, pcm->dma_capture);
440
441
return 0;
442
}
443
444
static const struct snd_soc_dai_ops s3c_pcm_dai_ops = {
445
.probe = s3c_pcm_dai_probe,
446
.set_sysclk = s3c_pcm_set_sysclk,
447
.set_clkdiv = s3c_pcm_set_clkdiv,
448
.trigger = s3c_pcm_trigger,
449
.hw_params = s3c_pcm_hw_params,
450
.set_fmt = s3c_pcm_set_fmt,
451
};
452
453
#define S3C_PCM_RATES SNDRV_PCM_RATE_8000_96000
454
455
#define S3C_PCM_DAI_DECLARE \
456
.symmetric_rate = 1, \
457
.ops = &s3c_pcm_dai_ops, \
458
.playback = { \
459
.channels_min = 2, \
460
.channels_max = 2, \
461
.rates = S3C_PCM_RATES, \
462
.formats = SNDRV_PCM_FMTBIT_S16_LE, \
463
}, \
464
.capture = { \
465
.channels_min = 2, \
466
.channels_max = 2, \
467
.rates = S3C_PCM_RATES, \
468
.formats = SNDRV_PCM_FMTBIT_S16_LE, \
469
}
470
471
static struct snd_soc_dai_driver s3c_pcm_dai[] = {
472
[0] = {
473
.name = "samsung-pcm.0",
474
S3C_PCM_DAI_DECLARE,
475
},
476
[1] = {
477
.name = "samsung-pcm.1",
478
S3C_PCM_DAI_DECLARE,
479
},
480
};
481
482
static const struct snd_soc_component_driver s3c_pcm_component = {
483
.name = "s3c-pcm",
484
.legacy_dai_naming = 1,
485
};
486
487
static int s3c_pcm_dev_probe(struct platform_device *pdev)
488
{
489
struct s3c_pcm_info *pcm;
490
struct resource *mem_res;
491
struct s3c_audio_pdata *pcm_pdata;
492
dma_filter_fn filter;
493
int ret;
494
495
/* Check for valid device index */
496
if ((pdev->id < 0) || pdev->id >= ARRAY_SIZE(s3c_pcm)) {
497
dev_err(&pdev->dev, "id %d out of range\n", pdev->id);
498
return -EINVAL;
499
}
500
501
pcm_pdata = pdev->dev.platform_data;
502
503
if (pcm_pdata && pcm_pdata->cfg_gpio && pcm_pdata->cfg_gpio(pdev)) {
504
dev_err(&pdev->dev, "Unable to configure gpio\n");
505
return -EINVAL;
506
}
507
508
pcm = &s3c_pcm[pdev->id];
509
pcm->dev = &pdev->dev;
510
511
spin_lock_init(&pcm->lock);
512
513
/* Default is 128fs */
514
pcm->sclk_per_fs = 128;
515
516
pcm->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem_res);
517
if (IS_ERR(pcm->regs))
518
return PTR_ERR(pcm->regs);
519
520
pcm->cclk = devm_clk_get(&pdev->dev, "audio-bus");
521
if (IS_ERR(pcm->cclk)) {
522
dev_err(&pdev->dev, "failed to get audio-bus clock\n");
523
return PTR_ERR(pcm->cclk);
524
}
525
ret = clk_prepare_enable(pcm->cclk);
526
if (ret)
527
return ret;
528
529
/* record our pcm structure for later use in the callbacks */
530
dev_set_drvdata(&pdev->dev, pcm);
531
532
pcm->pclk = devm_clk_get(&pdev->dev, "pcm");
533
if (IS_ERR(pcm->pclk)) {
534
dev_err(&pdev->dev, "failed to get pcm clock\n");
535
ret = PTR_ERR(pcm->pclk);
536
goto err_dis_cclk;
537
}
538
ret = clk_prepare_enable(pcm->pclk);
539
if (ret)
540
goto err_dis_cclk;
541
542
s3c_pcm_stereo_in[pdev->id].addr = mem_res->start + S3C_PCM_RXFIFO;
543
s3c_pcm_stereo_out[pdev->id].addr = mem_res->start + S3C_PCM_TXFIFO;
544
545
filter = NULL;
546
if (pcm_pdata) {
547
s3c_pcm_stereo_in[pdev->id].filter_data = pcm_pdata->dma_capture;
548
s3c_pcm_stereo_out[pdev->id].filter_data = pcm_pdata->dma_playback;
549
filter = pcm_pdata->dma_filter;
550
}
551
552
pcm->dma_capture = &s3c_pcm_stereo_in[pdev->id];
553
pcm->dma_playback = &s3c_pcm_stereo_out[pdev->id];
554
555
ret = samsung_asoc_dma_platform_register(&pdev->dev, filter,
556
NULL, NULL, NULL);
557
if (ret) {
558
dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret);
559
goto err_dis_pclk;
560
}
561
562
pm_runtime_enable(&pdev->dev);
563
564
ret = devm_snd_soc_register_component(&pdev->dev, &s3c_pcm_component,
565
&s3c_pcm_dai[pdev->id], 1);
566
if (ret != 0) {
567
dev_err(&pdev->dev, "failed to get register DAI: %d\n", ret);
568
goto err_dis_pm;
569
}
570
571
return 0;
572
573
err_dis_pm:
574
pm_runtime_disable(&pdev->dev);
575
err_dis_pclk:
576
clk_disable_unprepare(pcm->pclk);
577
err_dis_cclk:
578
clk_disable_unprepare(pcm->cclk);
579
return ret;
580
}
581
582
static void s3c_pcm_dev_remove(struct platform_device *pdev)
583
{
584
struct s3c_pcm_info *pcm = &s3c_pcm[pdev->id];
585
586
pm_runtime_disable(&pdev->dev);
587
clk_disable_unprepare(pcm->cclk);
588
clk_disable_unprepare(pcm->pclk);
589
}
590
591
static struct platform_driver s3c_pcm_driver = {
592
.probe = s3c_pcm_dev_probe,
593
.remove = s3c_pcm_dev_remove,
594
.driver = {
595
.name = "samsung-pcm",
596
},
597
};
598
599
module_platform_driver(s3c_pcm_driver);
600
601
/* Module information */
602
MODULE_AUTHOR("Jaswinder Singh, <[email protected]>");
603
MODULE_DESCRIPTION("S3C PCM Controller Driver");
604
MODULE_LICENSE("GPL");
605
MODULE_ALIAS("platform:samsung-pcm");
606
607