~hui.wang/alsa-driver/dkms-packaging.audiosdw-ppa

« back to all changes in this revision

Viewing changes to buildroot/src/oem-audiosdw-lp1836324-1ubuntu1.3/soc/samsung/s3c24xx-i2s.c

  • Committer: Hui Wang
  • Date: 2019-12-13 02:41:40 UTC
  • Revision ID: hui.wang@canonical.com-20191213024140-1cprdcbl3122fn85
insert pc-oem-dkms

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// SPDX-License-Identifier: GPL-2.0+
 
2
//
 
3
// s3c24xx-i2s.c  --  ALSA Soc Audio Layer
 
4
//
 
5
// (c) 2006 Wolfson Microelectronics PLC.
 
6
// Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
 
7
//
 
8
// Copyright 2004-2005 Simtec Electronics
 
9
//      http://armlinux.simtec.co.uk/
 
10
//      Ben Dooks <ben@simtec.co.uk>
 
11
 
 
12
#include <linux/delay.h>
 
13
#include <linux/clk.h>
 
14
#include <linux/io.h>
 
15
#include <linux/gpio.h>
 
16
#include <linux/module.h>
 
17
 
 
18
#include <sound/soc.h>
 
19
#include <sound/pcm_params.h>
 
20
 
 
21
#include <mach/gpio-samsung.h>
 
22
#include <plat/gpio-cfg.h>
 
23
#include "regs-iis.h"
 
24
 
 
25
#include "dma.h"
 
26
#include "s3c24xx-i2s.h"
 
27
 
 
28
static struct snd_dmaengine_dai_dma_data s3c24xx_i2s_pcm_stereo_out = {
 
29
        .chan_name      = "tx",
 
30
        .addr_width     = 2,
 
31
};
 
32
 
 
33
static struct snd_dmaengine_dai_dma_data s3c24xx_i2s_pcm_stereo_in = {
 
34
        .chan_name      = "rx",
 
35
        .addr_width     = 2,
 
36
};
 
37
 
 
38
struct s3c24xx_i2s_info {
 
39
        void __iomem    *regs;
 
40
        struct clk      *iis_clk;
 
41
        u32             iiscon;
 
42
        u32             iismod;
 
43
        u32             iisfcon;
 
44
        u32             iispsr;
 
45
};
 
46
static struct s3c24xx_i2s_info s3c24xx_i2s;
 
47
 
 
48
static void s3c24xx_snd_txctrl(int on)
 
49
{
 
50
        u32 iisfcon;
 
51
        u32 iiscon;
 
52
        u32 iismod;
 
53
 
 
54
        iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
 
55
        iiscon  = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
 
56
        iismod  = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
 
57
 
 
58
        pr_debug("r: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
 
59
 
 
60
        if (on) {
 
61
                iisfcon |= S3C2410_IISFCON_TXDMA | S3C2410_IISFCON_TXENABLE;
 
62
                iiscon  |= S3C2410_IISCON_TXDMAEN | S3C2410_IISCON_IISEN;
 
63
                iiscon  &= ~S3C2410_IISCON_TXIDLE;
 
64
                iismod  |= S3C2410_IISMOD_TXMODE;
 
65
 
 
66
                writel(iismod,  s3c24xx_i2s.regs + S3C2410_IISMOD);
 
67
                writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
 
68
                writel(iiscon,  s3c24xx_i2s.regs + S3C2410_IISCON);
 
69
        } else {
 
70
                /* note, we have to disable the FIFOs otherwise bad things
 
71
                 * seem to happen when the DMA stops. According to the
 
72
                 * Samsung supplied kernel, this should allow the DMA
 
73
                 * engine and FIFOs to reset. If this isn't allowed, the
 
74
                 * DMA engine will simply freeze randomly.
 
75
                 */
 
76
 
 
77
                iisfcon &= ~S3C2410_IISFCON_TXENABLE;
 
78
                iisfcon &= ~S3C2410_IISFCON_TXDMA;
 
79
                iiscon  |=  S3C2410_IISCON_TXIDLE;
 
80
                iiscon  &= ~S3C2410_IISCON_TXDMAEN;
 
81
                iismod  &= ~S3C2410_IISMOD_TXMODE;
 
82
 
 
83
                writel(iiscon,  s3c24xx_i2s.regs + S3C2410_IISCON);
 
84
                writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
 
85
                writel(iismod,  s3c24xx_i2s.regs + S3C2410_IISMOD);
 
86
        }
 
87
 
 
88
        pr_debug("w: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
 
89
}
 
90
 
 
91
static void s3c24xx_snd_rxctrl(int on)
 
92
{
 
93
        u32 iisfcon;
 
94
        u32 iiscon;
 
95
        u32 iismod;
 
96
 
 
97
        iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
 
98
        iiscon  = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
 
99
        iismod  = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
 
100
 
 
101
        pr_debug("r: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
 
102
 
 
103
        if (on) {
 
104
                iisfcon |= S3C2410_IISFCON_RXDMA | S3C2410_IISFCON_RXENABLE;
 
105
                iiscon  |= S3C2410_IISCON_RXDMAEN | S3C2410_IISCON_IISEN;
 
106
                iiscon  &= ~S3C2410_IISCON_RXIDLE;
 
107
                iismod  |= S3C2410_IISMOD_RXMODE;
 
108
 
 
109
                writel(iismod,  s3c24xx_i2s.regs + S3C2410_IISMOD);
 
110
                writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
 
111
                writel(iiscon,  s3c24xx_i2s.regs + S3C2410_IISCON);
 
112
        } else {
 
113
                /* note, we have to disable the FIFOs otherwise bad things
 
114
                 * seem to happen when the DMA stops. According to the
 
115
                 * Samsung supplied kernel, this should allow the DMA
 
116
                 * engine and FIFOs to reset. If this isn't allowed, the
 
117
                 * DMA engine will simply freeze randomly.
 
118
                 */
 
119
 
 
120
                iisfcon &= ~S3C2410_IISFCON_RXENABLE;
 
121
                iisfcon &= ~S3C2410_IISFCON_RXDMA;
 
122
                iiscon  |= S3C2410_IISCON_RXIDLE;
 
123
                iiscon  &= ~S3C2410_IISCON_RXDMAEN;
 
124
                iismod  &= ~S3C2410_IISMOD_RXMODE;
 
125
 
 
126
                writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
 
127
                writel(iiscon,  s3c24xx_i2s.regs + S3C2410_IISCON);
 
128
                writel(iismod,  s3c24xx_i2s.regs + S3C2410_IISMOD);
 
129
        }
 
130
 
 
131
        pr_debug("w: IISCON: %x IISMOD: %x IISFCON: %x\n", iiscon, iismod, iisfcon);
 
132
}
 
133
 
 
134
/*
 
135
 * Wait for the LR signal to allow synchronisation to the L/R clock
 
136
 * from the codec. May only be needed for slave mode.
 
137
 */
 
138
static int s3c24xx_snd_lrsync(void)
 
139
{
 
140
        u32 iiscon;
 
141
        int timeout = 50; /* 5ms */
 
142
 
 
143
        while (1) {
 
144
                iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
 
145
                if (iiscon & S3C2410_IISCON_LRINDEX)
 
146
                        break;
 
147
 
 
148
                if (!timeout--)
 
149
                        return -ETIMEDOUT;
 
150
                udelay(100);
 
151
        }
 
152
 
 
153
        return 0;
 
154
}
 
155
 
 
156
/*
 
157
 * Check whether CPU is the master or slave
 
158
 */
 
159
static inline int s3c24xx_snd_is_clkmaster(void)
 
160
{
 
161
        return (readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & S3C2410_IISMOD_SLAVE) ? 0:1;
 
162
}
 
163
 
 
164
/*
 
165
 * Set S3C24xx I2S DAI format
 
166
 */
 
167
static int s3c24xx_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
 
168
                unsigned int fmt)
 
169
{
 
170
        u32 iismod;
 
171
 
 
172
        iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
 
173
        pr_debug("hw_params r: IISMOD: %x \n", iismod);
 
174
 
 
175
        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 
176
        case SND_SOC_DAIFMT_CBM_CFM:
 
177
                iismod |= S3C2410_IISMOD_SLAVE;
 
178
                break;
 
179
        case SND_SOC_DAIFMT_CBS_CFS:
 
180
                iismod &= ~S3C2410_IISMOD_SLAVE;
 
181
                break;
 
182
        default:
 
183
                return -EINVAL;
 
184
        }
 
185
 
 
186
        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 
187
        case SND_SOC_DAIFMT_LEFT_J:
 
188
                iismod |= S3C2410_IISMOD_MSB;
 
189
                break;
 
190
        case SND_SOC_DAIFMT_I2S:
 
191
                iismod &= ~S3C2410_IISMOD_MSB;
 
192
                break;
 
193
        default:
 
194
                return -EINVAL;
 
195
        }
 
196
 
 
197
        writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
 
198
        pr_debug("hw_params w: IISMOD: %x \n", iismod);
 
199
 
 
200
        return 0;
 
201
}
 
202
 
 
203
static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream,
 
204
                                 struct snd_pcm_hw_params *params,
 
205
                                 struct snd_soc_dai *dai)
 
206
{
 
207
        struct snd_dmaengine_dai_dma_data *dma_data;
 
208
        u32 iismod;
 
209
 
 
210
        dma_data = snd_soc_dai_get_dma_data(dai, substream);
 
211
 
 
212
        /* Working copies of register */
 
213
        iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
 
214
        pr_debug("hw_params r: IISMOD: %x\n", iismod);
 
215
 
 
216
        switch (params_width(params)) {
 
217
        case 8:
 
218
                iismod &= ~S3C2410_IISMOD_16BIT;
 
219
                dma_data->addr_width = 1;
 
220
                break;
 
221
        case 16:
 
222
                iismod |= S3C2410_IISMOD_16BIT;
 
223
                dma_data->addr_width = 2;
 
224
                break;
 
225
        default:
 
226
                return -EINVAL;
 
227
        }
 
228
 
 
229
        writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
 
230
        pr_debug("hw_params w: IISMOD: %x\n", iismod);
 
231
 
 
232
        return 0;
 
233
}
 
234
 
 
235
static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
 
236
                               struct snd_soc_dai *dai)
 
237
{
 
238
        int ret = 0;
 
239
 
 
240
        switch (cmd) {
 
241
        case SNDRV_PCM_TRIGGER_START:
 
242
        case SNDRV_PCM_TRIGGER_RESUME:
 
243
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 
244
                if (!s3c24xx_snd_is_clkmaster()) {
 
245
                        ret = s3c24xx_snd_lrsync();
 
246
                        if (ret)
 
247
                                goto exit_err;
 
248
                }
 
249
 
 
250
                if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
 
251
                        s3c24xx_snd_rxctrl(1);
 
252
                else
 
253
                        s3c24xx_snd_txctrl(1);
 
254
 
 
255
                break;
 
256
        case SNDRV_PCM_TRIGGER_STOP:
 
257
        case SNDRV_PCM_TRIGGER_SUSPEND:
 
258
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 
259
                if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
 
260
                        s3c24xx_snd_rxctrl(0);
 
261
                else
 
262
                        s3c24xx_snd_txctrl(0);
 
263
                break;
 
264
        default:
 
265
                ret = -EINVAL;
 
266
                break;
 
267
        }
 
268
 
 
269
exit_err:
 
270
        return ret;
 
271
}
 
272
 
 
273
/*
 
274
 * Set S3C24xx Clock source
 
275
 */
 
276
static int s3c24xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
 
277
        int clk_id, unsigned int freq, int dir)
 
278
{
 
279
        u32 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
 
280
 
 
281
        iismod &= ~S3C2440_IISMOD_MPLL;
 
282
 
 
283
        switch (clk_id) {
 
284
        case S3C24XX_CLKSRC_PCLK:
 
285
                break;
 
286
        case S3C24XX_CLKSRC_MPLL:
 
287
                iismod |= S3C2440_IISMOD_MPLL;
 
288
                break;
 
289
        default:
 
290
                return -EINVAL;
 
291
        }
 
292
 
 
293
        writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
 
294
        return 0;
 
295
}
 
296
 
 
297
/*
 
298
 * Set S3C24xx Clock dividers
 
299
 */
 
300
static int s3c24xx_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai,
 
301
        int div_id, int div)
 
302
{
 
303
        u32 reg;
 
304
 
 
305
        switch (div_id) {
 
306
        case S3C24XX_DIV_BCLK:
 
307
                reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~S3C2410_IISMOD_FS_MASK;
 
308
                writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
 
309
                break;
 
310
        case S3C24XX_DIV_MCLK:
 
311
                reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~(S3C2410_IISMOD_384FS);
 
312
                writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
 
313
                break;
 
314
        case S3C24XX_DIV_PRESCALER:
 
315
                writel(div, s3c24xx_i2s.regs + S3C2410_IISPSR);
 
316
                reg = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
 
317
                writel(reg | S3C2410_IISCON_PSCEN, s3c24xx_i2s.regs + S3C2410_IISCON);
 
318
                break;
 
319
        default:
 
320
                return -EINVAL;
 
321
        }
 
322
 
 
323
        return 0;
 
324
}
 
325
 
 
326
/*
 
327
 * To avoid duplicating clock code, allow machine driver to
 
328
 * get the clockrate from here.
 
329
 */
 
330
u32 s3c24xx_i2s_get_clockrate(void)
 
331
{
 
332
        return clk_get_rate(s3c24xx_i2s.iis_clk);
 
333
}
 
334
EXPORT_SYMBOL_GPL(s3c24xx_i2s_get_clockrate);
 
335
 
 
336
static int s3c24xx_i2s_probe(struct snd_soc_dai *dai)
 
337
{
 
338
        int ret;
 
339
        snd_soc_dai_init_dma_data(dai, &s3c24xx_i2s_pcm_stereo_out,
 
340
                                        &s3c24xx_i2s_pcm_stereo_in);
 
341
 
 
342
        s3c24xx_i2s.iis_clk = devm_clk_get(dai->dev, "iis");
 
343
        if (IS_ERR(s3c24xx_i2s.iis_clk)) {
 
344
                pr_err("failed to get iis_clock\n");
 
345
                return PTR_ERR(s3c24xx_i2s.iis_clk);
 
346
        }
 
347
        ret = clk_prepare_enable(s3c24xx_i2s.iis_clk);
 
348
        if (ret)
 
349
                return ret;
 
350
 
 
351
        /* Configure the I2S pins (GPE0...GPE4) in correct mode */
 
352
        s3c_gpio_cfgall_range(S3C2410_GPE(0), 5, S3C_GPIO_SFN(2),
 
353
                              S3C_GPIO_PULL_NONE);
 
354
 
 
355
        writel(S3C2410_IISCON_IISEN, s3c24xx_i2s.regs + S3C2410_IISCON);
 
356
 
 
357
        s3c24xx_snd_txctrl(0);
 
358
        s3c24xx_snd_rxctrl(0);
 
359
 
 
360
        return 0;
 
361
}
 
362
 
 
363
#ifdef CONFIG_PM
 
364
static int s3c24xx_i2s_suspend(struct snd_soc_dai *cpu_dai)
 
365
{
 
366
        s3c24xx_i2s.iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
 
367
        s3c24xx_i2s.iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
 
368
        s3c24xx_i2s.iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
 
369
        s3c24xx_i2s.iispsr = readl(s3c24xx_i2s.regs + S3C2410_IISPSR);
 
370
 
 
371
        clk_disable_unprepare(s3c24xx_i2s.iis_clk);
 
372
 
 
373
        return 0;
 
374
}
 
375
 
 
376
static int s3c24xx_i2s_resume(struct snd_soc_dai *cpu_dai)
 
377
{
 
378
        int ret;
 
379
 
 
380
        ret = clk_prepare_enable(s3c24xx_i2s.iis_clk);
 
381
        if (ret)
 
382
                return ret;
 
383
 
 
384
        writel(s3c24xx_i2s.iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
 
385
        writel(s3c24xx_i2s.iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
 
386
        writel(s3c24xx_i2s.iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
 
387
        writel(s3c24xx_i2s.iispsr, s3c24xx_i2s.regs + S3C2410_IISPSR);
 
388
 
 
389
        return 0;
 
390
}
 
391
#else
 
392
#define s3c24xx_i2s_suspend NULL
 
393
#define s3c24xx_i2s_resume NULL
 
394
#endif
 
395
 
 
396
#define S3C24XX_I2S_RATES \
 
397
        (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
 
398
        SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
 
399
        SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
 
400
 
 
401
static const struct snd_soc_dai_ops s3c24xx_i2s_dai_ops = {
 
402
        .trigger        = s3c24xx_i2s_trigger,
 
403
        .hw_params      = s3c24xx_i2s_hw_params,
 
404
        .set_fmt        = s3c24xx_i2s_set_fmt,
 
405
        .set_clkdiv     = s3c24xx_i2s_set_clkdiv,
 
406
        .set_sysclk     = s3c24xx_i2s_set_sysclk,
 
407
};
 
408
 
 
409
static struct snd_soc_dai_driver s3c24xx_i2s_dai = {
 
410
        .probe = s3c24xx_i2s_probe,
 
411
        .suspend = s3c24xx_i2s_suspend,
 
412
        .resume = s3c24xx_i2s_resume,
 
413
        .playback = {
 
414
                .channels_min = 2,
 
415
                .channels_max = 2,
 
416
                .rates = S3C24XX_I2S_RATES,
 
417
                .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
 
418
        .capture = {
 
419
                .channels_min = 2,
 
420
                .channels_max = 2,
 
421
                .rates = S3C24XX_I2S_RATES,
 
422
                .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
 
423
        .ops = &s3c24xx_i2s_dai_ops,
 
424
};
 
425
 
 
426
static const struct snd_soc_component_driver s3c24xx_i2s_component = {
 
427
        .name           = "s3c24xx-i2s",
 
428
};
 
429
 
 
430
static int s3c24xx_iis_dev_probe(struct platform_device *pdev)
 
431
{
 
432
        struct resource *res;
 
433
        int ret;
 
434
 
 
435
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 
436
        s3c24xx_i2s.regs = devm_ioremap_resource(&pdev->dev, res);
 
437
        if (IS_ERR(s3c24xx_i2s.regs))
 
438
                return PTR_ERR(s3c24xx_i2s.regs);
 
439
 
 
440
        s3c24xx_i2s_pcm_stereo_out.addr = res->start + S3C2410_IISFIFO;
 
441
        s3c24xx_i2s_pcm_stereo_in.addr = res->start + S3C2410_IISFIFO;
 
442
 
 
443
        ret = samsung_asoc_dma_platform_register(&pdev->dev, NULL,
 
444
                                                 "tx", "rx", NULL);
 
445
        if (ret) {
 
446
                dev_err(&pdev->dev, "Failed to register the DMA: %d\n", ret);
 
447
                return ret;
 
448
        }
 
449
 
 
450
        ret = devm_snd_soc_register_component(&pdev->dev,
 
451
                        &s3c24xx_i2s_component, &s3c24xx_i2s_dai, 1);
 
452
        if (ret)
 
453
                dev_err(&pdev->dev, "Failed to register the DAI\n");
 
454
 
 
455
        return ret;
 
456
}
 
457
 
 
458
static struct platform_driver s3c24xx_iis_driver = {
 
459
        .probe  = s3c24xx_iis_dev_probe,
 
460
        .driver = {
 
461
                .name = "s3c24xx-iis",
 
462
        },
 
463
};
 
464
 
 
465
module_platform_driver(s3c24xx_iis_driver);
 
466
 
 
467
/* Module information */
 
468
MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
 
469
MODULE_DESCRIPTION("s3c24xx I2S SoC Interface");
 
470
MODULE_LICENSE("GPL");
 
471
MODULE_ALIAS("platform:s3c24xx-iis");