Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/sound/soc/samsung/s3c-i2s-v2.c
10817 views
1
/* sound/soc/samsung/s3c-i2c-v2.c
2
*
3
* ALSA Soc Audio Layer - I2S core for newer Samsung SoCs.
4
*
5
* Copyright (c) 2006 Wolfson Microelectronics PLC.
6
* Graeme Gregory [email protected]
7
* [email protected]
8
*
9
* Copyright (c) 2008, 2007, 2004-2005 Simtec Electronics
10
* http://armlinux.simtec.co.uk/
11
* Ben Dooks <[email protected]>
12
*
13
* This program is free software; you can redistribute it and/or modify it
14
* under the terms of the GNU General Public License as published by the
15
* Free Software Foundation; either version 2 of the License, or (at your
16
* option) any later version.
17
*/
18
19
#include <linux/delay.h>
20
#include <linux/clk.h>
21
#include <linux/io.h>
22
23
#include <sound/soc.h>
24
#include <sound/pcm_params.h>
25
26
#include <mach/dma.h>
27
28
#include "regs-i2s-v2.h"
29
#include "s3c-i2s-v2.h"
30
#include "dma.h"
31
32
#undef S3C_IIS_V2_SUPPORTED
33
34
#if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413) \
35
|| defined(CONFIG_CPU_S5PV210)
36
#define S3C_IIS_V2_SUPPORTED
37
#endif
38
39
#ifdef CONFIG_PLAT_S3C64XX
40
#define S3C_IIS_V2_SUPPORTED
41
#endif
42
43
#ifndef S3C_IIS_V2_SUPPORTED
44
#error Unsupported CPU model
45
#endif
46
47
#define S3C2412_I2S_DEBUG_CON 0
48
49
static inline struct s3c_i2sv2_info *to_info(struct snd_soc_dai *cpu_dai)
50
{
51
return snd_soc_dai_get_drvdata(cpu_dai);
52
}
53
54
#define bit_set(v, b) (((v) & (b)) ? 1 : 0)
55
56
#if S3C2412_I2S_DEBUG_CON
57
static void dbg_showcon(const char *fn, u32 con)
58
{
59
printk(KERN_DEBUG "%s: LRI=%d, TXFEMPT=%d, RXFEMPT=%d, TXFFULL=%d, RXFFULL=%d\n", fn,
60
bit_set(con, S3C2412_IISCON_LRINDEX),
61
bit_set(con, S3C2412_IISCON_TXFIFO_EMPTY),
62
bit_set(con, S3C2412_IISCON_RXFIFO_EMPTY),
63
bit_set(con, S3C2412_IISCON_TXFIFO_FULL),
64
bit_set(con, S3C2412_IISCON_RXFIFO_FULL));
65
66
printk(KERN_DEBUG "%s: PAUSE: TXDMA=%d, RXDMA=%d, TXCH=%d, RXCH=%d\n",
67
fn,
68
bit_set(con, S3C2412_IISCON_TXDMA_PAUSE),
69
bit_set(con, S3C2412_IISCON_RXDMA_PAUSE),
70
bit_set(con, S3C2412_IISCON_TXCH_PAUSE),
71
bit_set(con, S3C2412_IISCON_RXCH_PAUSE));
72
printk(KERN_DEBUG "%s: ACTIVE: TXDMA=%d, RXDMA=%d, IIS=%d\n", fn,
73
bit_set(con, S3C2412_IISCON_TXDMA_ACTIVE),
74
bit_set(con, S3C2412_IISCON_RXDMA_ACTIVE),
75
bit_set(con, S3C2412_IISCON_IIS_ACTIVE));
76
}
77
#else
78
static inline void dbg_showcon(const char *fn, u32 con)
79
{
80
}
81
#endif
82
83
84
/* Turn on or off the transmission path. */
85
static void s3c2412_snd_txctrl(struct s3c_i2sv2_info *i2s, int on)
86
{
87
void __iomem *regs = i2s->regs;
88
u32 fic, con, mod;
89
90
pr_debug("%s(%d)\n", __func__, on);
91
92
fic = readl(regs + S3C2412_IISFIC);
93
con = readl(regs + S3C2412_IISCON);
94
mod = readl(regs + S3C2412_IISMOD);
95
96
pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
97
98
if (on) {
99
con |= S3C2412_IISCON_TXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE;
100
con &= ~S3C2412_IISCON_TXDMA_PAUSE;
101
con &= ~S3C2412_IISCON_TXCH_PAUSE;
102
103
switch (mod & S3C2412_IISMOD_MODE_MASK) {
104
case S3C2412_IISMOD_MODE_TXONLY:
105
case S3C2412_IISMOD_MODE_TXRX:
106
/* do nothing, we are in the right mode */
107
break;
108
109
case S3C2412_IISMOD_MODE_RXONLY:
110
mod &= ~S3C2412_IISMOD_MODE_MASK;
111
mod |= S3C2412_IISMOD_MODE_TXRX;
112
break;
113
114
default:
115
dev_err(i2s->dev, "TXEN: Invalid MODE %x in IISMOD\n",
116
mod & S3C2412_IISMOD_MODE_MASK);
117
break;
118
}
119
120
writel(con, regs + S3C2412_IISCON);
121
writel(mod, regs + S3C2412_IISMOD);
122
} else {
123
/* Note, we do not have any indication that the FIFO problems
124
* tha the S3C2410/2440 had apply here, so we should be able
125
* to disable the DMA and TX without resetting the FIFOS.
126
*/
127
128
con |= S3C2412_IISCON_TXDMA_PAUSE;
129
con |= S3C2412_IISCON_TXCH_PAUSE;
130
con &= ~S3C2412_IISCON_TXDMA_ACTIVE;
131
132
switch (mod & S3C2412_IISMOD_MODE_MASK) {
133
case S3C2412_IISMOD_MODE_TXRX:
134
mod &= ~S3C2412_IISMOD_MODE_MASK;
135
mod |= S3C2412_IISMOD_MODE_RXONLY;
136
break;
137
138
case S3C2412_IISMOD_MODE_TXONLY:
139
mod &= ~S3C2412_IISMOD_MODE_MASK;
140
con &= ~S3C2412_IISCON_IIS_ACTIVE;
141
break;
142
143
default:
144
dev_err(i2s->dev, "TXDIS: Invalid MODE %x in IISMOD\n",
145
mod & S3C2412_IISMOD_MODE_MASK);
146
break;
147
}
148
149
writel(mod, regs + S3C2412_IISMOD);
150
writel(con, regs + S3C2412_IISCON);
151
}
152
153
fic = readl(regs + S3C2412_IISFIC);
154
dbg_showcon(__func__, con);
155
pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
156
}
157
158
static void s3c2412_snd_rxctrl(struct s3c_i2sv2_info *i2s, int on)
159
{
160
void __iomem *regs = i2s->regs;
161
u32 fic, con, mod;
162
163
pr_debug("%s(%d)\n", __func__, on);
164
165
fic = readl(regs + S3C2412_IISFIC);
166
con = readl(regs + S3C2412_IISCON);
167
mod = readl(regs + S3C2412_IISMOD);
168
169
pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
170
171
if (on) {
172
con |= S3C2412_IISCON_RXDMA_ACTIVE | S3C2412_IISCON_IIS_ACTIVE;
173
con &= ~S3C2412_IISCON_RXDMA_PAUSE;
174
con &= ~S3C2412_IISCON_RXCH_PAUSE;
175
176
switch (mod & S3C2412_IISMOD_MODE_MASK) {
177
case S3C2412_IISMOD_MODE_TXRX:
178
case S3C2412_IISMOD_MODE_RXONLY:
179
/* do nothing, we are in the right mode */
180
break;
181
182
case S3C2412_IISMOD_MODE_TXONLY:
183
mod &= ~S3C2412_IISMOD_MODE_MASK;
184
mod |= S3C2412_IISMOD_MODE_TXRX;
185
break;
186
187
default:
188
dev_err(i2s->dev, "RXEN: Invalid MODE %x in IISMOD\n",
189
mod & S3C2412_IISMOD_MODE_MASK);
190
}
191
192
writel(mod, regs + S3C2412_IISMOD);
193
writel(con, regs + S3C2412_IISCON);
194
} else {
195
/* See txctrl notes on FIFOs. */
196
197
con &= ~S3C2412_IISCON_RXDMA_ACTIVE;
198
con |= S3C2412_IISCON_RXDMA_PAUSE;
199
con |= S3C2412_IISCON_RXCH_PAUSE;
200
201
switch (mod & S3C2412_IISMOD_MODE_MASK) {
202
case S3C2412_IISMOD_MODE_RXONLY:
203
con &= ~S3C2412_IISCON_IIS_ACTIVE;
204
mod &= ~S3C2412_IISMOD_MODE_MASK;
205
break;
206
207
case S3C2412_IISMOD_MODE_TXRX:
208
mod &= ~S3C2412_IISMOD_MODE_MASK;
209
mod |= S3C2412_IISMOD_MODE_TXONLY;
210
break;
211
212
default:
213
dev_err(i2s->dev, "RXDIS: Invalid MODE %x in IISMOD\n",
214
mod & S3C2412_IISMOD_MODE_MASK);
215
}
216
217
writel(con, regs + S3C2412_IISCON);
218
writel(mod, regs + S3C2412_IISMOD);
219
}
220
221
fic = readl(regs + S3C2412_IISFIC);
222
pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
223
}
224
225
#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
226
227
/*
228
* Wait for the LR signal to allow synchronisation to the L/R clock
229
* from the codec. May only be needed for slave mode.
230
*/
231
static int s3c2412_snd_lrsync(struct s3c_i2sv2_info *i2s)
232
{
233
u32 iiscon;
234
unsigned long loops = msecs_to_loops(5);
235
236
pr_debug("Entered %s\n", __func__);
237
238
while (--loops) {
239
iiscon = readl(i2s->regs + S3C2412_IISCON);
240
if (iiscon & S3C2412_IISCON_LRINDEX)
241
break;
242
243
cpu_relax();
244
}
245
246
if (!loops) {
247
printk(KERN_ERR "%s: timeout\n", __func__);
248
return -ETIMEDOUT;
249
}
250
251
return 0;
252
}
253
254
/*
255
* Set S3C2412 I2S DAI format
256
*/
257
static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
258
unsigned int fmt)
259
{
260
struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
261
u32 iismod;
262
263
pr_debug("Entered %s\n", __func__);
264
265
iismod = readl(i2s->regs + S3C2412_IISMOD);
266
pr_debug("hw_params r: IISMOD: %x \n", iismod);
267
268
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
269
case SND_SOC_DAIFMT_CBM_CFM:
270
i2s->master = 0;
271
iismod |= S3C2412_IISMOD_SLAVE;
272
break;
273
case SND_SOC_DAIFMT_CBS_CFS:
274
i2s->master = 1;
275
iismod &= ~S3C2412_IISMOD_SLAVE;
276
break;
277
default:
278
pr_err("unknwon master/slave format\n");
279
return -EINVAL;
280
}
281
282
iismod &= ~S3C2412_IISMOD_SDF_MASK;
283
284
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
285
case SND_SOC_DAIFMT_RIGHT_J:
286
iismod |= S3C2412_IISMOD_LR_RLOW;
287
iismod |= S3C2412_IISMOD_SDF_MSB;
288
break;
289
case SND_SOC_DAIFMT_LEFT_J:
290
iismod |= S3C2412_IISMOD_LR_RLOW;
291
iismod |= S3C2412_IISMOD_SDF_LSB;
292
break;
293
case SND_SOC_DAIFMT_I2S:
294
iismod &= ~S3C2412_IISMOD_LR_RLOW;
295
iismod |= S3C2412_IISMOD_SDF_IIS;
296
break;
297
default:
298
pr_err("Unknown data format\n");
299
return -EINVAL;
300
}
301
302
writel(iismod, i2s->regs + S3C2412_IISMOD);
303
pr_debug("hw_params w: IISMOD: %x \n", iismod);
304
return 0;
305
}
306
307
static int s3c_i2sv2_hw_params(struct snd_pcm_substream *substream,
308
struct snd_pcm_hw_params *params,
309
struct snd_soc_dai *dai)
310
{
311
struct s3c_i2sv2_info *i2s = to_info(dai);
312
struct s3c_dma_params *dma_data;
313
u32 iismod;
314
315
pr_debug("Entered %s\n", __func__);
316
317
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
318
dma_data = i2s->dma_playback;
319
else
320
dma_data = i2s->dma_capture;
321
322
snd_soc_dai_set_dma_data(dai, substream, dma_data);
323
324
/* Working copies of register */
325
iismod = readl(i2s->regs + S3C2412_IISMOD);
326
pr_debug("%s: r: IISMOD: %x\n", __func__, iismod);
327
328
iismod &= ~S3C64XX_IISMOD_BLC_MASK;
329
/* Sample size */
330
switch (params_format(params)) {
331
case SNDRV_PCM_FORMAT_S8:
332
iismod |= S3C64XX_IISMOD_BLC_8BIT;
333
break;
334
case SNDRV_PCM_FORMAT_S16_LE:
335
break;
336
case SNDRV_PCM_FORMAT_S24_LE:
337
iismod |= S3C64XX_IISMOD_BLC_24BIT;
338
break;
339
}
340
341
writel(iismod, i2s->regs + S3C2412_IISMOD);
342
pr_debug("%s: w: IISMOD: %x\n", __func__, iismod);
343
344
return 0;
345
}
346
347
static int s3c_i2sv2_set_sysclk(struct snd_soc_dai *cpu_dai,
348
int clk_id, unsigned int freq, int dir)
349
{
350
struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
351
u32 iismod = readl(i2s->regs + S3C2412_IISMOD);
352
353
pr_debug("Entered %s\n", __func__);
354
pr_debug("%s r: IISMOD: %x\n", __func__, iismod);
355
356
switch (clk_id) {
357
case S3C_I2SV2_CLKSRC_PCLK:
358
iismod &= ~S3C2412_IISMOD_IMS_SYSMUX;
359
break;
360
361
case S3C_I2SV2_CLKSRC_AUDIOBUS:
362
iismod |= S3C2412_IISMOD_IMS_SYSMUX;
363
break;
364
365
case S3C_I2SV2_CLKSRC_CDCLK:
366
/* Error if controller doesn't have the CDCLKCON bit */
367
if (!(i2s->feature & S3C_FEATURE_CDCLKCON))
368
return -EINVAL;
369
370
switch (dir) {
371
case SND_SOC_CLOCK_IN:
372
iismod |= S3C64XX_IISMOD_CDCLKCON;
373
break;
374
case SND_SOC_CLOCK_OUT:
375
iismod &= ~S3C64XX_IISMOD_CDCLKCON;
376
break;
377
default:
378
return -EINVAL;
379
}
380
break;
381
382
default:
383
return -EINVAL;
384
}
385
386
writel(iismod, i2s->regs + S3C2412_IISMOD);
387
pr_debug("%s w: IISMOD: %x\n", __func__, iismod);
388
389
return 0;
390
}
391
392
static int s3c2412_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
393
struct snd_soc_dai *dai)
394
{
395
struct snd_soc_pcm_runtime *rtd = substream->private_data;
396
struct s3c_i2sv2_info *i2s = to_info(rtd->cpu_dai);
397
int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
398
unsigned long irqs;
399
int ret = 0;
400
struct s3c_dma_params *dma_data =
401
snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
402
403
pr_debug("Entered %s\n", __func__);
404
405
switch (cmd) {
406
case SNDRV_PCM_TRIGGER_START:
407
/* On start, ensure that the FIFOs are cleared and reset. */
408
409
writel(capture ? S3C2412_IISFIC_RXFLUSH : S3C2412_IISFIC_TXFLUSH,
410
i2s->regs + S3C2412_IISFIC);
411
412
/* clear again, just in case */
413
writel(0x0, i2s->regs + S3C2412_IISFIC);
414
415
case SNDRV_PCM_TRIGGER_RESUME:
416
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
417
if (!i2s->master) {
418
ret = s3c2412_snd_lrsync(i2s);
419
if (ret)
420
goto exit_err;
421
}
422
423
local_irq_save(irqs);
424
425
if (capture)
426
s3c2412_snd_rxctrl(i2s, 1);
427
else
428
s3c2412_snd_txctrl(i2s, 1);
429
430
local_irq_restore(irqs);
431
432
/*
433
* Load the next buffer to DMA to meet the reqirement
434
* of the auto reload mechanism of S3C24XX.
435
* This call won't bother S3C64XX.
436
*/
437
s3c2410_dma_ctrl(dma_data->channel, S3C2410_DMAOP_STARTED);
438
439
break;
440
441
case SNDRV_PCM_TRIGGER_STOP:
442
case SNDRV_PCM_TRIGGER_SUSPEND:
443
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
444
local_irq_save(irqs);
445
446
if (capture)
447
s3c2412_snd_rxctrl(i2s, 0);
448
else
449
s3c2412_snd_txctrl(i2s, 0);
450
451
local_irq_restore(irqs);
452
break;
453
default:
454
ret = -EINVAL;
455
break;
456
}
457
458
exit_err:
459
return ret;
460
}
461
462
/*
463
* Set S3C2412 Clock dividers
464
*/
465
static int s3c2412_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
466
int div_id, int div)
467
{
468
struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
469
u32 reg;
470
471
pr_debug("%s(%p, %d, %d)\n", __func__, cpu_dai, div_id, div);
472
473
switch (div_id) {
474
case S3C_I2SV2_DIV_BCLK:
475
switch (div) {
476
case 16:
477
div = S3C2412_IISMOD_BCLK_16FS;
478
break;
479
480
case 32:
481
div = S3C2412_IISMOD_BCLK_32FS;
482
break;
483
484
case 24:
485
div = S3C2412_IISMOD_BCLK_24FS;
486
break;
487
488
case 48:
489
div = S3C2412_IISMOD_BCLK_48FS;
490
break;
491
492
default:
493
return -EINVAL;
494
}
495
496
reg = readl(i2s->regs + S3C2412_IISMOD);
497
reg &= ~S3C2412_IISMOD_BCLK_MASK;
498
writel(reg | div, i2s->regs + S3C2412_IISMOD);
499
500
pr_debug("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD));
501
break;
502
503
case S3C_I2SV2_DIV_RCLK:
504
switch (div) {
505
case 256:
506
div = S3C2412_IISMOD_RCLK_256FS;
507
break;
508
509
case 384:
510
div = S3C2412_IISMOD_RCLK_384FS;
511
break;
512
513
case 512:
514
div = S3C2412_IISMOD_RCLK_512FS;
515
break;
516
517
case 768:
518
div = S3C2412_IISMOD_RCLK_768FS;
519
break;
520
521
default:
522
return -EINVAL;
523
}
524
525
reg = readl(i2s->regs + S3C2412_IISMOD);
526
reg &= ~S3C2412_IISMOD_RCLK_MASK;
527
writel(reg | div, i2s->regs + S3C2412_IISMOD);
528
pr_debug("%s: MOD=%08x\n", __func__, readl(i2s->regs + S3C2412_IISMOD));
529
break;
530
531
case S3C_I2SV2_DIV_PRESCALER:
532
if (div >= 0) {
533
writel((div << 8) | S3C2412_IISPSR_PSREN,
534
i2s->regs + S3C2412_IISPSR);
535
} else {
536
writel(0x0, i2s->regs + S3C2412_IISPSR);
537
}
538
pr_debug("%s: PSR=%08x\n", __func__, readl(i2s->regs + S3C2412_IISPSR));
539
break;
540
541
default:
542
return -EINVAL;
543
}
544
545
return 0;
546
}
547
548
static snd_pcm_sframes_t s3c2412_i2s_delay(struct snd_pcm_substream *substream,
549
struct snd_soc_dai *dai)
550
{
551
struct s3c_i2sv2_info *i2s = to_info(dai);
552
u32 reg = readl(i2s->regs + S3C2412_IISFIC);
553
snd_pcm_sframes_t delay;
554
555
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
556
delay = S3C2412_IISFIC_TXCOUNT(reg);
557
else
558
delay = S3C2412_IISFIC_RXCOUNT(reg);
559
560
return delay;
561
}
562
563
struct clk *s3c_i2sv2_get_clock(struct snd_soc_dai *cpu_dai)
564
{
565
struct s3c_i2sv2_info *i2s = to_info(cpu_dai);
566
u32 iismod = readl(i2s->regs + S3C2412_IISMOD);
567
568
if (iismod & S3C2412_IISMOD_IMS_SYSMUX)
569
return i2s->iis_cclk;
570
else
571
return i2s->iis_pclk;
572
}
573
EXPORT_SYMBOL_GPL(s3c_i2sv2_get_clock);
574
575
/* default table of all avaialable root fs divisors */
576
static unsigned int iis_fs_tab[] = { 256, 512, 384, 768 };
577
578
int s3c_i2sv2_iis_calc_rate(struct s3c_i2sv2_rate_calc *info,
579
unsigned int *fstab,
580
unsigned int rate, struct clk *clk)
581
{
582
unsigned long clkrate = clk_get_rate(clk);
583
unsigned int div;
584
unsigned int fsclk;
585
unsigned int actual;
586
unsigned int fs;
587
unsigned int fsdiv;
588
signed int deviation = 0;
589
unsigned int best_fs = 0;
590
unsigned int best_div = 0;
591
unsigned int best_rate = 0;
592
unsigned int best_deviation = INT_MAX;
593
594
pr_debug("Input clock rate %ldHz\n", clkrate);
595
596
if (fstab == NULL)
597
fstab = iis_fs_tab;
598
599
for (fs = 0; fs < ARRAY_SIZE(iis_fs_tab); fs++) {
600
fsdiv = iis_fs_tab[fs];
601
602
fsclk = clkrate / fsdiv;
603
div = fsclk / rate;
604
605
if ((fsclk % rate) > (rate / 2))
606
div++;
607
608
if (div <= 1)
609
continue;
610
611
actual = clkrate / (fsdiv * div);
612
deviation = actual - rate;
613
614
printk(KERN_DEBUG "%ufs: div %u => result %u, deviation %d\n",
615
fsdiv, div, actual, deviation);
616
617
deviation = abs(deviation);
618
619
if (deviation < best_deviation) {
620
best_fs = fsdiv;
621
best_div = div;
622
best_rate = actual;
623
best_deviation = deviation;
624
}
625
626
if (deviation == 0)
627
break;
628
}
629
630
printk(KERN_DEBUG "best: fs=%u, div=%u, rate=%u\n",
631
best_fs, best_div, best_rate);
632
633
info->fs_div = best_fs;
634
info->clk_div = best_div;
635
636
return 0;
637
}
638
EXPORT_SYMBOL_GPL(s3c_i2sv2_iis_calc_rate);
639
640
int s3c_i2sv2_probe(struct snd_soc_dai *dai,
641
struct s3c_i2sv2_info *i2s,
642
unsigned long base)
643
{
644
struct device *dev = dai->dev;
645
unsigned int iismod;
646
647
i2s->dev = dev;
648
649
/* record our i2s structure for later use in the callbacks */
650
snd_soc_dai_set_drvdata(dai, i2s);
651
652
i2s->regs = ioremap(base, 0x100);
653
if (i2s->regs == NULL) {
654
dev_err(dev, "cannot ioremap registers\n");
655
return -ENXIO;
656
}
657
658
i2s->iis_pclk = clk_get(dev, "iis");
659
if (IS_ERR(i2s->iis_pclk)) {
660
dev_err(dev, "failed to get iis_clock\n");
661
iounmap(i2s->regs);
662
return -ENOENT;
663
}
664
665
clk_enable(i2s->iis_pclk);
666
667
/* Mark ourselves as in TXRX mode so we can run through our cleanup
668
* process without warnings. */
669
iismod = readl(i2s->regs + S3C2412_IISMOD);
670
iismod |= S3C2412_IISMOD_MODE_TXRX;
671
writel(iismod, i2s->regs + S3C2412_IISMOD);
672
s3c2412_snd_txctrl(i2s, 0);
673
s3c2412_snd_rxctrl(i2s, 0);
674
675
return 0;
676
}
677
EXPORT_SYMBOL_GPL(s3c_i2sv2_probe);
678
679
#ifdef CONFIG_PM
680
static int s3c2412_i2s_suspend(struct snd_soc_dai *dai)
681
{
682
struct s3c_i2sv2_info *i2s = to_info(dai);
683
u32 iismod;
684
685
if (dai->active) {
686
i2s->suspend_iismod = readl(i2s->regs + S3C2412_IISMOD);
687
i2s->suspend_iiscon = readl(i2s->regs + S3C2412_IISCON);
688
i2s->suspend_iispsr = readl(i2s->regs + S3C2412_IISPSR);
689
690
/* some basic suspend checks */
691
692
iismod = readl(i2s->regs + S3C2412_IISMOD);
693
694
if (iismod & S3C2412_IISCON_RXDMA_ACTIVE)
695
pr_warning("%s: RXDMA active?\n", __func__);
696
697
if (iismod & S3C2412_IISCON_TXDMA_ACTIVE)
698
pr_warning("%s: TXDMA active?\n", __func__);
699
700
if (iismod & S3C2412_IISCON_IIS_ACTIVE)
701
pr_warning("%s: IIS active\n", __func__);
702
}
703
704
return 0;
705
}
706
707
static int s3c2412_i2s_resume(struct snd_soc_dai *dai)
708
{
709
struct s3c_i2sv2_info *i2s = to_info(dai);
710
711
pr_info("dai_active %d, IISMOD %08x, IISCON %08x\n",
712
dai->active, i2s->suspend_iismod, i2s->suspend_iiscon);
713
714
if (dai->active) {
715
writel(i2s->suspend_iiscon, i2s->regs + S3C2412_IISCON);
716
writel(i2s->suspend_iismod, i2s->regs + S3C2412_IISMOD);
717
writel(i2s->suspend_iispsr, i2s->regs + S3C2412_IISPSR);
718
719
writel(S3C2412_IISFIC_RXFLUSH | S3C2412_IISFIC_TXFLUSH,
720
i2s->regs + S3C2412_IISFIC);
721
722
ndelay(250);
723
writel(0x0, i2s->regs + S3C2412_IISFIC);
724
}
725
726
return 0;
727
}
728
#else
729
#define s3c2412_i2s_suspend NULL
730
#define s3c2412_i2s_resume NULL
731
#endif
732
733
int s3c_i2sv2_register_dai(struct device *dev, int id,
734
struct snd_soc_dai_driver *drv)
735
{
736
struct snd_soc_dai_ops *ops = drv->ops;
737
738
ops->trigger = s3c2412_i2s_trigger;
739
if (!ops->hw_params)
740
ops->hw_params = s3c_i2sv2_hw_params;
741
ops->set_fmt = s3c2412_i2s_set_fmt;
742
ops->set_clkdiv = s3c2412_i2s_set_clkdiv;
743
ops->set_sysclk = s3c_i2sv2_set_sysclk;
744
745
/* Allow overriding by (for example) IISv4 */
746
if (!ops->delay)
747
ops->delay = s3c2412_i2s_delay;
748
749
drv->suspend = s3c2412_i2s_suspend;
750
drv->resume = s3c2412_i2s_resume;
751
752
return snd_soc_register_dai(dev, drv);
753
}
754
EXPORT_SYMBOL_GPL(s3c_i2sv2_register_dai);
755
756
MODULE_LICENSE("GPL");
757
758