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