Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/sound/soc/kirkwood/kirkwood-i2s.c
10817 views
1
/*
2
* kirkwood-i2s.c
3
*
4
* (c) 2010 Arnaud Patard <[email protected]>
5
* (c) 2010 Arnaud Patard <[email protected]>
6
*
7
* This program is free software; you can redistribute it and/or modify it
8
* under the terms of the GNU General Public License as published by the
9
* Free Software Foundation; either version 2 of the License, or (at your
10
* option) any later version.
11
*/
12
13
#include <linux/init.h>
14
#include <linux/module.h>
15
#include <linux/platform_device.h>
16
#include <linux/io.h>
17
#include <linux/slab.h>
18
#include <linux/mbus.h>
19
#include <linux/delay.h>
20
#include <sound/pcm.h>
21
#include <sound/pcm_params.h>
22
#include <sound/soc.h>
23
#include <plat/audio.h>
24
#include "kirkwood.h"
25
26
#define DRV_NAME "kirkwood-i2s"
27
28
#define KIRKWOOD_I2S_RATES \
29
(SNDRV_PCM_RATE_44100 | \
30
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
31
#define KIRKWOOD_I2S_FORMATS \
32
(SNDRV_PCM_FMTBIT_S16_LE | \
33
SNDRV_PCM_FMTBIT_S24_LE | \
34
SNDRV_PCM_FMTBIT_S32_LE)
35
36
static int kirkwood_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
37
unsigned int fmt)
38
{
39
struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(cpu_dai);
40
unsigned long mask;
41
unsigned long value;
42
43
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
44
case SND_SOC_DAIFMT_RIGHT_J:
45
mask = KIRKWOOD_I2S_CTL_RJ;
46
break;
47
case SND_SOC_DAIFMT_LEFT_J:
48
mask = KIRKWOOD_I2S_CTL_LJ;
49
break;
50
case SND_SOC_DAIFMT_I2S:
51
mask = KIRKWOOD_I2S_CTL_I2S;
52
break;
53
default:
54
return -EINVAL;
55
}
56
57
/*
58
* Set same format for playback and record
59
* This avoids some troubles.
60
*/
61
value = readl(priv->io+KIRKWOOD_I2S_PLAYCTL);
62
value &= ~KIRKWOOD_I2S_CTL_JUST_MASK;
63
value |= mask;
64
writel(value, priv->io+KIRKWOOD_I2S_PLAYCTL);
65
66
value = readl(priv->io+KIRKWOOD_I2S_RECCTL);
67
value &= ~KIRKWOOD_I2S_CTL_JUST_MASK;
68
value |= mask;
69
writel(value, priv->io+KIRKWOOD_I2S_RECCTL);
70
71
return 0;
72
}
73
74
static inline void kirkwood_set_dco(void __iomem *io, unsigned long rate)
75
{
76
unsigned long value;
77
78
value = KIRKWOOD_DCO_CTL_OFFSET_0;
79
switch (rate) {
80
default:
81
case 44100:
82
value |= KIRKWOOD_DCO_CTL_FREQ_11;
83
break;
84
case 48000:
85
value |= KIRKWOOD_DCO_CTL_FREQ_12;
86
break;
87
case 96000:
88
value |= KIRKWOOD_DCO_CTL_FREQ_24;
89
break;
90
}
91
writel(value, io + KIRKWOOD_DCO_CTL);
92
93
/* wait for dco locked */
94
do {
95
cpu_relax();
96
value = readl(io + KIRKWOOD_DCO_SPCR_STATUS);
97
value &= KIRKWOOD_DCO_SPCR_STATUS;
98
} while (value == 0);
99
}
100
101
static int kirkwood_i2s_startup(struct snd_pcm_substream *substream,
102
struct snd_soc_dai *dai)
103
{
104
struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
105
106
snd_soc_dai_set_dma_data(dai, substream, priv);
107
return 0;
108
}
109
110
static int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream,
111
struct snd_pcm_hw_params *params,
112
struct snd_soc_dai *dai)
113
{
114
struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
115
unsigned int i2s_reg, reg;
116
unsigned long i2s_value, value;
117
118
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
119
i2s_reg = KIRKWOOD_I2S_PLAYCTL;
120
reg = KIRKWOOD_PLAYCTL;
121
} else {
122
i2s_reg = KIRKWOOD_I2S_RECCTL;
123
reg = KIRKWOOD_RECCTL;
124
}
125
126
/* set dco conf */
127
kirkwood_set_dco(priv->io, params_rate(params));
128
129
i2s_value = readl(priv->io+i2s_reg);
130
i2s_value &= ~KIRKWOOD_I2S_CTL_SIZE_MASK;
131
132
value = readl(priv->io+reg);
133
value &= ~KIRKWOOD_PLAYCTL_SIZE_MASK;
134
135
/*
136
* Size settings in play/rec i2s control regs and play/rec control
137
* regs must be the same.
138
*/
139
switch (params_format(params)) {
140
case SNDRV_PCM_FORMAT_S16_LE:
141
i2s_value |= KIRKWOOD_I2S_CTL_SIZE_16;
142
value |= KIRKWOOD_PLAYCTL_SIZE_16_C;
143
break;
144
/*
145
* doesn't work... S20_3LE != kirkwood 20bit format ?
146
*
147
case SNDRV_PCM_FORMAT_S20_3LE:
148
i2s_value |= KIRKWOOD_I2S_CTL_SIZE_20;
149
value |= KIRKWOOD_PLAYCTL_SIZE_20;
150
break;
151
*/
152
case SNDRV_PCM_FORMAT_S24_LE:
153
i2s_value |= KIRKWOOD_I2S_CTL_SIZE_24;
154
value |= KIRKWOOD_PLAYCTL_SIZE_24;
155
break;
156
case SNDRV_PCM_FORMAT_S32_LE:
157
i2s_value |= KIRKWOOD_I2S_CTL_SIZE_32;
158
value |= KIRKWOOD_PLAYCTL_SIZE_32;
159
break;
160
default:
161
return -EINVAL;
162
}
163
164
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
165
value &= ~KIRKWOOD_PLAYCTL_MONO_MASK;
166
if (params_channels(params) == 1)
167
value |= KIRKWOOD_PLAYCTL_MONO_BOTH;
168
else
169
value |= KIRKWOOD_PLAYCTL_MONO_OFF;
170
}
171
172
writel(i2s_value, priv->io+i2s_reg);
173
writel(value, priv->io+reg);
174
175
return 0;
176
}
177
178
static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream,
179
int cmd, struct snd_soc_dai *dai)
180
{
181
struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
182
unsigned long value;
183
184
/*
185
* specs says KIRKWOOD_PLAYCTL must be read 2 times before
186
* changing it. So read 1 time here and 1 later.
187
*/
188
value = readl(priv->io + KIRKWOOD_PLAYCTL);
189
190
switch (cmd) {
191
case SNDRV_PCM_TRIGGER_START:
192
/* stop audio, enable interrupts */
193
value = readl(priv->io + KIRKWOOD_PLAYCTL);
194
value |= KIRKWOOD_PLAYCTL_PAUSE;
195
writel(value, priv->io + KIRKWOOD_PLAYCTL);
196
197
value = readl(priv->io + KIRKWOOD_INT_MASK);
198
value |= KIRKWOOD_INT_CAUSE_PLAY_BYTES;
199
writel(value, priv->io + KIRKWOOD_INT_MASK);
200
201
/* configure audio & enable i2s playback */
202
value = readl(priv->io + KIRKWOOD_PLAYCTL);
203
value &= ~KIRKWOOD_PLAYCTL_BURST_MASK;
204
value &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE
205
| KIRKWOOD_PLAYCTL_SPDIF_EN);
206
207
if (priv->burst == 32)
208
value |= KIRKWOOD_PLAYCTL_BURST_32;
209
else
210
value |= KIRKWOOD_PLAYCTL_BURST_128;
211
value |= KIRKWOOD_PLAYCTL_I2S_EN;
212
writel(value, priv->io + KIRKWOOD_PLAYCTL);
213
break;
214
215
case SNDRV_PCM_TRIGGER_STOP:
216
/* stop audio, disable interrupts */
217
value = readl(priv->io + KIRKWOOD_PLAYCTL);
218
value |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE;
219
writel(value, priv->io + KIRKWOOD_PLAYCTL);
220
221
value = readl(priv->io + KIRKWOOD_INT_MASK);
222
value &= ~KIRKWOOD_INT_CAUSE_PLAY_BYTES;
223
writel(value, priv->io + KIRKWOOD_INT_MASK);
224
225
/* disable all playbacks */
226
value = readl(priv->io + KIRKWOOD_PLAYCTL);
227
value &= ~(KIRKWOOD_PLAYCTL_I2S_EN | KIRKWOOD_PLAYCTL_SPDIF_EN);
228
writel(value, priv->io + KIRKWOOD_PLAYCTL);
229
break;
230
231
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
232
case SNDRV_PCM_TRIGGER_SUSPEND:
233
value = readl(priv->io + KIRKWOOD_PLAYCTL);
234
value |= KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE;
235
writel(value, priv->io + KIRKWOOD_PLAYCTL);
236
break;
237
238
case SNDRV_PCM_TRIGGER_RESUME:
239
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
240
value = readl(priv->io + KIRKWOOD_PLAYCTL);
241
value &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE);
242
writel(value, priv->io + KIRKWOOD_PLAYCTL);
243
break;
244
245
default:
246
return -EINVAL;
247
}
248
249
return 0;
250
}
251
252
static int kirkwood_i2s_rec_trigger(struct snd_pcm_substream *substream,
253
int cmd, struct snd_soc_dai *dai)
254
{
255
struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
256
unsigned long value;
257
258
value = readl(priv->io + KIRKWOOD_RECCTL);
259
260
switch (cmd) {
261
case SNDRV_PCM_TRIGGER_START:
262
/* stop audio, enable interrupts */
263
value = readl(priv->io + KIRKWOOD_RECCTL);
264
value |= KIRKWOOD_RECCTL_PAUSE;
265
writel(value, priv->io + KIRKWOOD_RECCTL);
266
267
value = readl(priv->io + KIRKWOOD_INT_MASK);
268
value |= KIRKWOOD_INT_CAUSE_REC_BYTES;
269
writel(value, priv->io + KIRKWOOD_INT_MASK);
270
271
/* configure audio & enable i2s record */
272
value = readl(priv->io + KIRKWOOD_RECCTL);
273
value &= ~KIRKWOOD_RECCTL_BURST_MASK;
274
value &= ~KIRKWOOD_RECCTL_MONO;
275
value &= ~(KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE
276
| KIRKWOOD_RECCTL_SPDIF_EN);
277
278
if (priv->burst == 32)
279
value |= KIRKWOOD_RECCTL_BURST_32;
280
else
281
value |= KIRKWOOD_RECCTL_BURST_128;
282
value |= KIRKWOOD_RECCTL_I2S_EN;
283
284
writel(value, priv->io + KIRKWOOD_RECCTL);
285
break;
286
287
case SNDRV_PCM_TRIGGER_STOP:
288
/* stop audio, disable interrupts */
289
value = readl(priv->io + KIRKWOOD_RECCTL);
290
value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE;
291
writel(value, priv->io + KIRKWOOD_RECCTL);
292
293
value = readl(priv->io + KIRKWOOD_INT_MASK);
294
value &= ~KIRKWOOD_INT_CAUSE_REC_BYTES;
295
writel(value, priv->io + KIRKWOOD_INT_MASK);
296
297
/* disable all records */
298
value = readl(priv->io + KIRKWOOD_RECCTL);
299
value &= ~(KIRKWOOD_RECCTL_I2S_EN | KIRKWOOD_RECCTL_SPDIF_EN);
300
writel(value, priv->io + KIRKWOOD_RECCTL);
301
break;
302
303
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
304
case SNDRV_PCM_TRIGGER_SUSPEND:
305
value = readl(priv->io + KIRKWOOD_RECCTL);
306
value |= KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE;
307
writel(value, priv->io + KIRKWOOD_RECCTL);
308
break;
309
310
case SNDRV_PCM_TRIGGER_RESUME:
311
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
312
value = readl(priv->io + KIRKWOOD_RECCTL);
313
value &= ~(KIRKWOOD_RECCTL_PAUSE | KIRKWOOD_RECCTL_MUTE);
314
writel(value, priv->io + KIRKWOOD_RECCTL);
315
break;
316
317
default:
318
return -EINVAL;
319
}
320
321
return 0;
322
}
323
324
static int kirkwood_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
325
struct snd_soc_dai *dai)
326
{
327
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
328
return kirkwood_i2s_play_trigger(substream, cmd, dai);
329
else
330
return kirkwood_i2s_rec_trigger(substream, cmd, dai);
331
332
return 0;
333
}
334
335
static int kirkwood_i2s_probe(struct snd_soc_dai *dai)
336
{
337
struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
338
unsigned long value;
339
unsigned int reg_data;
340
341
/* put system in a "safe" state : */
342
/* disable audio interrupts */
343
writel(0xffffffff, priv->io + KIRKWOOD_INT_CAUSE);
344
writel(0, priv->io + KIRKWOOD_INT_MASK);
345
346
reg_data = readl(priv->io + 0x1200);
347
reg_data &= (~(0x333FF8));
348
reg_data |= 0x111D18;
349
writel(reg_data, priv->io + 0x1200);
350
351
msleep(500);
352
353
reg_data = readl(priv->io + 0x1200);
354
reg_data &= (~(0x333FF8));
355
reg_data |= 0x111D18;
356
writel(reg_data, priv->io + 0x1200);
357
358
/* disable playback/record */
359
value = readl(priv->io + KIRKWOOD_PLAYCTL);
360
value &= ~(KIRKWOOD_PLAYCTL_I2S_EN|KIRKWOOD_PLAYCTL_SPDIF_EN);
361
writel(value, priv->io + KIRKWOOD_PLAYCTL);
362
363
value = readl(priv->io + KIRKWOOD_RECCTL);
364
value &= ~(KIRKWOOD_RECCTL_I2S_EN | KIRKWOOD_RECCTL_SPDIF_EN);
365
writel(value, priv->io + KIRKWOOD_RECCTL);
366
367
return 0;
368
369
}
370
371
static int kirkwood_i2s_remove(struct snd_soc_dai *dai)
372
{
373
return 0;
374
}
375
376
static struct snd_soc_dai_ops kirkwood_i2s_dai_ops = {
377
.startup = kirkwood_i2s_startup,
378
.trigger = kirkwood_i2s_trigger,
379
.hw_params = kirkwood_i2s_hw_params,
380
.set_fmt = kirkwood_i2s_set_fmt,
381
};
382
383
384
static struct snd_soc_dai_driver kirkwood_i2s_dai = {
385
.probe = kirkwood_i2s_probe,
386
.remove = kirkwood_i2s_remove,
387
.playback = {
388
.channels_min = 1,
389
.channels_max = 2,
390
.rates = KIRKWOOD_I2S_RATES,
391
.formats = KIRKWOOD_I2S_FORMATS,},
392
.capture = {
393
.channels_min = 1,
394
.channels_max = 2,
395
.rates = KIRKWOOD_I2S_RATES,
396
.formats = KIRKWOOD_I2S_FORMATS,},
397
.ops = &kirkwood_i2s_dai_ops,
398
};
399
400
static __devinit int kirkwood_i2s_dev_probe(struct platform_device *pdev)
401
{
402
struct resource *mem;
403
struct kirkwood_asoc_platform_data *data =
404
pdev->dev.platform_data;
405
struct kirkwood_dma_data *priv;
406
int err;
407
408
priv = kzalloc(sizeof(struct kirkwood_dma_data), GFP_KERNEL);
409
if (!priv) {
410
dev_err(&pdev->dev, "allocation failed\n");
411
err = -ENOMEM;
412
goto error;
413
}
414
dev_set_drvdata(&pdev->dev, priv);
415
416
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
417
if (!mem) {
418
dev_err(&pdev->dev, "platform_get_resource failed\n");
419
err = -ENXIO;
420
goto err_alloc;
421
}
422
423
priv->mem = request_mem_region(mem->start, SZ_16K, DRV_NAME);
424
if (!priv->mem) {
425
dev_err(&pdev->dev, "request_mem_region failed\n");
426
err = -EBUSY;
427
goto error;
428
}
429
430
priv->io = ioremap(priv->mem->start, SZ_16K);
431
if (!priv->io) {
432
dev_err(&pdev->dev, "ioremap failed\n");
433
err = -ENOMEM;
434
goto err_iomem;
435
}
436
437
priv->irq = platform_get_irq(pdev, 0);
438
if (priv->irq <= 0) {
439
dev_err(&pdev->dev, "platform_get_irq failed\n");
440
err = -ENXIO;
441
goto err_ioremap;
442
}
443
444
if (!data || !data->dram) {
445
dev_err(&pdev->dev, "no platform data ?!\n");
446
err = -EINVAL;
447
goto err_ioremap;
448
}
449
450
priv->dram = data->dram;
451
priv->burst = data->burst;
452
453
return snd_soc_register_dai(&pdev->dev, &kirkwood_i2s_dai);
454
455
err_ioremap:
456
iounmap(priv->io);
457
err_iomem:
458
release_mem_region(priv->mem->start, SZ_16K);
459
err_alloc:
460
kfree(priv);
461
error:
462
return err;
463
}
464
465
static __devexit int kirkwood_i2s_dev_remove(struct platform_device *pdev)
466
{
467
struct kirkwood_dma_data *priv = dev_get_drvdata(&pdev->dev);
468
469
snd_soc_unregister_dai(&pdev->dev);
470
iounmap(priv->io);
471
release_mem_region(priv->mem->start, SZ_16K);
472
kfree(priv);
473
474
return 0;
475
}
476
477
static struct platform_driver kirkwood_i2s_driver = {
478
.probe = kirkwood_i2s_dev_probe,
479
.remove = kirkwood_i2s_dev_remove,
480
.driver = {
481
.name = DRV_NAME,
482
.owner = THIS_MODULE,
483
},
484
};
485
486
static int __init kirkwood_i2s_init(void)
487
{
488
return platform_driver_register(&kirkwood_i2s_driver);
489
}
490
module_init(kirkwood_i2s_init);
491
492
static void __exit kirkwood_i2s_exit(void)
493
{
494
platform_driver_unregister(&kirkwood_i2s_driver);
495
}
496
module_exit(kirkwood_i2s_exit);
497
498
/* Module information */
499
MODULE_AUTHOR("Arnaud Patard, <[email protected]>");
500
MODULE_DESCRIPTION("Kirkwood I2S SoC Interface");
501
MODULE_LICENSE("GPL");
502
MODULE_ALIAS("platform:kirkwood-i2s");
503
504