source: trunk/target/linux/xburst/files-2.6.32/sound/soc/jz4740/jz4740-i2s.c @ 19098

Last change on this file since 19098 was 19098, checked in by lars, 7 years ago

Merge xburst target.

File size: 12.8 KB
Line 
1/*
2 *  Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
3 *
4 *  This program is free software; you can redistribute  it and/or modify it
5 *  under  the terms of  the GNU General  Public License as published by the
6 *  Free Software Foundation;  either version 2 of the  License, or (at your
7 *  option) any later version.
8 *
9 *  You should have received a copy of the  GNU General Public License along
10 *  with this program; if not, write  to the Free Software Foundation, Inc.,
11 *  675 Mass Ave, Cambridge, MA 02139, USA.
12 *
13 */
14
15#include <linux/kernel.h>
16#include <linux/module.h>
17#include <linux/init.h>
18#include <linux/dma-mapping.h>
19#include <linux/platform_device.h>
20#include <linux/clk.h>
21#include <linux/io.h>
22#include <linux/delay.h>
23
24#include <sound/core.h>
25#include <sound/pcm.h>
26#include <sound/pcm_params.h>
27#include <sound/soc.h>
28#include <sound/soc-dapm.h>
29#include <sound/initval.h>
30
31#include "jz4740-i2s.h"
32#include "jz4740-pcm.h"
33
34#define JZ_REG_AIC_CONF         0x00
35#define JZ_REG_AIC_CTRL         0x04
36#define JZ_REG_AIC_I2S_FMT      0x10
37#define JZ_REG_AIC_FIFO_STATUS  0x14
38#define JZ_REG_AIC_I2S_STATUS   0x1c
39#define JZ_REG_AIC_CLK_DIV      0x30
40#define JZ_REG_AIC_FIFO         0x34
41
42#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_MASK (0xf << 12)
43#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_MASK (0xf <<  8)
44#define JZ_AIC_CONF_OVERFLOW_PLAY_LAST BIT(6)
45#define JZ_AIC_CONF_INTERNAL_CODEC BIT(5)
46#define JZ_AIC_CONF_I2S BIT(4)
47#define JZ_AIC_CONF_RESET BIT(3)
48#define JZ_AIC_CONF_BIT_CLK_MASTER BIT(2)
49#define JZ_AIC_CONF_SYNC_CLK_MASTER BIT(1)
50#define JZ_AIC_CONF_ENABLE BIT(0)
51
52#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 12
53#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 8
54
55#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK (0x7 << 19)
56#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK (0x7 << 16)
57#define JZ_AIC_CTRL_ENABLE_RX_DMA BIT(15)
58#define JZ_AIC_CTRL_ENABLE_TX_DMA BIT(14)
59#define JZ_AIC_CTRL_MONO_TO_STEREO BIT(11)
60#define JZ_AIC_CTRL_SWITCH_ENDIANNESS BIT(10)
61#define JZ_AIC_CTRL_SIGNED_TO_UNSIGNED BIT(9)
62#define JZ_AIC_CTRL_FLUSH               BIT(8)
63#define JZ_AIC_CTRL_ENABLE_ROR_INT BIT(6)
64#define JZ_AIC_CTRL_ENABLE_TUR_INT BIT(5)
65#define JZ_AIC_CTRL_ENABLE_RFS_INT BIT(4)
66#define JZ_AIC_CTRL_ENABLE_TFS_INT BIT(3)
67#define JZ_AIC_CTRL_ENABLE_LOOPBACK BIT(2)
68#define JZ_AIC_CTRL_ENABLE_PLAYBACK BIT(1)
69#define JZ_AIC_CTRL_ENABLE_CAPTURE BIT(0)
70
71#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET 19
72#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET  16
73
74#define JZ_AIC_I2S_FMT_DISABLE_BIT_CLK BIT(12)
75#define JZ_AIC_I2S_FMT_ENABLE_SYS_CLK BIT(4)
76#define JZ_AIC_I2S_FMT_MSB BIT(0)
77
78#define JZ_AIC_I2S_STATUS_BUSY BIT(2)
79
80#define JZ_AIC_CLK_DIV_MASK 0xf
81
82struct jz4740_i2s {
83        struct resource *mem;
84        void __iomem *base;
85        dma_addr_t phys_base;
86
87        struct clk *clk;
88
89        struct jz4740_pcm_config pcm_config;
90};
91
92static struct jz4740_dma_config jz4740_i2s_dma_playback_config = {
93        .src_width = JZ4740_DMA_WIDTH_16BIT,
94        .dst_width = JZ4740_DMA_WIDTH_32BIT,
95        .transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE,
96        .request_type = JZ4740_DMA_TYPE_AIC_TRANSMIT,
97        .flags = JZ4740_DMA_SRC_AUTOINC,
98        .mode = JZ4740_DMA_MODE_SINGLE,
99};
100
101static struct jz4740_dma_config jz4740_i2s_dma_capture_config = {
102        .src_width = JZ4740_DMA_WIDTH_32BIT,
103        .dst_width = JZ4740_DMA_WIDTH_16BIT,
104        .transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE,
105        .request_type = JZ4740_DMA_TYPE_AIC_RECEIVE,
106        .flags = JZ4740_DMA_DST_AUTOINC,
107        .mode = JZ4740_DMA_MODE_SINGLE,
108};
109
110
111static inline uint32_t jz4740_i2s_read(const struct jz4740_i2s *i2s, unsigned int reg)
112{
113        return readl(i2s->base + reg);
114}
115
116static inline void jz4740_i2s_write(const struct jz4740_i2s *i2s, unsigned
117int reg, uint32_t value)
118{
119        writel(value, i2s->base + reg);
120}
121
122static inline struct jz4740_i2s *jz4740_dai_to_i2s(struct snd_soc_dai *dai)
123{
124        return dai->private_data;
125}
126
127static int jz4740_i2s_startup(struct snd_pcm_substream *substream, struct
128                              snd_soc_dai *dai)
129{
130        struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
131        uint32_t conf, ctrl;
132
133        if (dai->active)
134                return 0;
135
136        conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
137        ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
138
139        conf |= JZ_AIC_CONF_ENABLE;
140        ctrl |= JZ_AIC_CTRL_FLUSH;
141
142
143        jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
144        clk_enable(i2s->clk);
145        jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
146
147        return 0;
148}
149
150static void jz4740_i2s_shutdown(struct snd_pcm_substream *substream, struct
151                              snd_soc_dai *dai)
152{
153        struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
154        uint32_t conf;
155
156        if (dai->active)
157                return;
158
159        conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
160        conf &= ~JZ_AIC_CONF_ENABLE;
161        jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
162
163        clk_disable(i2s->clk);
164}
165
166
167static int jz4740_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
168                               struct snd_soc_dai *dai)
169{
170        struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
171        bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
172
173        uint32_t ctrl;
174        uint32_t mask;
175
176        if (playback) {
177            mask = JZ_AIC_CTRL_ENABLE_PLAYBACK |
178                   JZ_AIC_CTRL_ENABLE_TX_DMA;
179        } else {
180            mask = JZ_AIC_CTRL_ENABLE_CAPTURE |
181                   JZ_AIC_CTRL_ENABLE_RX_DMA;
182        }
183
184        ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
185
186        switch (cmd) {
187        case SNDRV_PCM_TRIGGER_START:
188        case SNDRV_PCM_TRIGGER_RESUME:
189        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
190            ctrl |= mask;
191            break;
192        case SNDRV_PCM_TRIGGER_STOP:
193        case SNDRV_PCM_TRIGGER_SUSPEND:
194        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
195            ctrl &= ~mask;
196            break;
197        default:
198            return -EINVAL;
199        }
200
201        jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL,ctrl);
202
203        return 0;
204}
205
206
207static int jz4740_i2s_set_fmt(struct snd_soc_dai *dai,
208                               unsigned int fmt)
209{
210        struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
211
212        uint32_t format = 0;
213        uint32_t conf;
214
215        conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
216
217        conf &= ~(JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER);
218
219        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
220        case SND_SOC_DAIFMT_CBS_CFS:
221            conf |= JZ_AIC_CONF_BIT_CLK_MASTER |
222                    JZ_AIC_CONF_SYNC_CLK_MASTER;
223            format |= JZ_AIC_I2S_FMT_ENABLE_SYS_CLK;
224            break;
225        case SND_SOC_DAIFMT_CBM_CFS:
226            conf |= JZ_AIC_CONF_SYNC_CLK_MASTER;
227            break;
228        case SND_SOC_DAIFMT_CBS_CFM:
229            conf |= JZ_AIC_CONF_BIT_CLK_MASTER;
230            break;
231        case SND_SOC_DAIFMT_CBM_CFM:
232            break;
233        default:
234            return -EINVAL;
235        }
236
237        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
238        case SND_SOC_DAIFMT_MSB:
239            format |= JZ_AIC_I2S_FMT_MSB;
240            break;
241        case SND_SOC_DAIFMT_I2S:
242            break;
243        default:
244            return -EINVAL;
245        }
246
247        switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
248        case SND_SOC_DAIFMT_NB_NF:
249            break;
250        default:
251            return -EINVAL;
252        }
253
254        jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
255        jz4740_i2s_write(i2s, JZ_REG_AIC_I2S_FMT, format);
256
257        return 0;
258}
259
260static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream,
261                                 struct snd_pcm_hw_params *params,
262                                 struct snd_soc_dai *dai)
263{
264        struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
265        bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
266        int sample_size;
267        enum jz4740_dma_width dma_width;
268        uint32_t ctrl;
269
270        ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
271
272        switch (params_format(params)) {
273        case SNDRV_PCM_FORMAT_S8:
274            sample_size = 0;
275                dma_width = JZ4740_DMA_WIDTH_8BIT;
276            break;
277        case SNDRV_PCM_FORMAT_S16:
278            sample_size = 1;
279                dma_width = JZ4740_DMA_WIDTH_16BIT;
280                break;
281        default:
282                return -EINVAL;
283        }
284
285        if (playback) {
286                ctrl &= ~JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK;
287            ctrl |= sample_size << JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET;
288        } else {
289                ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK;
290            ctrl |= sample_size << JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET;
291        }
292
293        switch (params_channels(params)) {
294        case 2:
295            break;
296        case 1:
297            if (playback) {
298                    ctrl |= JZ_AIC_CTRL_MONO_TO_STEREO;
299                    break;
300            }
301        default:
302            return -EINVAL;
303        }
304
305        jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
306
307        /* This is quite ugly, but apperently it's offical method for passing dma
308         * config to the pcm module */
309        if (playback) {
310                jz4740_i2s_dma_playback_config.src_width = dma_width;
311                i2s->pcm_config.dma_config = &jz4740_i2s_dma_playback_config;
312        } else {
313                jz4740_i2s_dma_capture_config.dst_width = dma_width;
314                i2s->pcm_config.dma_config = &jz4740_i2s_dma_capture_config;
315        }
316        i2s->pcm_config.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO;
317
318        dai->dma_data = &i2s->pcm_config;
319
320        return 0;
321}
322
323static int jz4740_i2s_set_clkdiv(struct snd_soc_dai *dai,
324                                  int div_id, int div)
325{
326        struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
327
328        switch (div_id) {
329        case JZ4740_I2S_BIT_CLK:
330                if (div & 1 || div > 16)
331                        return -EINVAL;
332                jz4740_i2s_write(i2s, JZ_REG_AIC_CLK_DIV, div - 1);
333                break;
334        default:
335                return -EINVAL;
336        }
337
338        return 0;
339}
340
341static int jz4740_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id,
342                                  unsigned int freq, int dir)
343{
344        struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
345        int ret = 0;
346        struct clk *parent;
347
348        switch (clk_id) {
349        case JZ4740_I2S_CLKSRC_EXT:
350                parent = clk_get(NULL, "ext");
351                clk_set_parent(i2s->clk, parent);
352                break;
353        case JZ4740_I2S_CLKSRC_PLL:
354                parent = clk_get(NULL, "pll half");
355                clk_set_parent(i2s->clk, parent);
356                ret = clk_set_rate(i2s->clk, freq);
357                break;
358        default:
359                return -EINVAL;
360        }
361        clk_put(parent);
362
363        return ret;
364}
365
366static int jz4740_i2s_suspend(struct snd_soc_dai *dai)
367{
368        struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
369        uint32_t conf;
370
371        if (!dai->active)
372                return 0;
373
374        conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
375        conf &= ~JZ_AIC_CONF_ENABLE;
376        jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
377
378        clk_disable(i2s->clk);
379        return 0;
380}
381
382static int jz4740_i2s_resume(struct snd_soc_dai *dai)
383{
384        struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
385        uint32_t conf;
386
387        if (!dai->active)
388                return 0;
389
390        clk_enable(i2s->clk);
391
392        conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
393        conf |= JZ_AIC_CONF_ENABLE;
394        jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
395
396        return 0;
397}
398
399
400static int jz4740_i2s_probe(struct platform_device *pdev, struct snd_soc_dai *dai)
401{
402        struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
403        uint32_t conf;
404
405        conf = (7 << JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) |
406               (8 << JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) |
407               JZ_AIC_CONF_OVERFLOW_PLAY_LAST |
408                   JZ_AIC_CONF_I2S |
409               JZ_AIC_CONF_INTERNAL_CODEC;
410
411        jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, JZ_AIC_CONF_RESET);
412        jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
413
414        return 0;
415}
416
417
418static struct snd_soc_dai_ops jz4740_i2s_dai_ops = {
419        .startup = jz4740_i2s_startup,
420        .shutdown = jz4740_i2s_shutdown,
421        .trigger = jz4740_i2s_trigger,
422        .hw_params = jz4740_i2s_hw_params,
423        .set_fmt = jz4740_i2s_set_fmt,
424        .set_clkdiv = jz4740_i2s_set_clkdiv,
425        .set_sysclk = jz4740_i2s_set_sysclk,
426};
427
428#define JZ4740_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | \
429                SNDRV_PCM_FMTBIT_S16_LE)
430
431struct snd_soc_dai jz4740_i2s_dai = {
432        .name = "jz4740-i2s",
433        .probe = jz4740_i2s_probe,
434        .playback = {
435                .channels_min = 1,
436                .channels_max = 2,
437                .rates = SNDRV_PCM_RATE_8000_44100,
438                .formats = JZ4740_I2S_FMTS,
439        },
440        .capture = {
441                .channels_min = 2,
442                .channels_max = 2,
443                .rates = SNDRV_PCM_RATE_8000_44100,
444                .formats = JZ4740_I2S_FMTS,
445        },
446        .symmetric_rates = 1,
447        .ops = &jz4740_i2s_dai_ops,
448        .suspend = jz4740_i2s_suspend,
449        .resume = jz4740_i2s_resume,
450};
451
452static int __devinit jz4740_i2s_dev_probe(struct platform_device *pdev)
453{
454        struct jz4740_i2s *i2s;
455        int ret;
456
457        i2s = kzalloc(sizeof(*i2s), GFP_KERNEL);
458
459        if (!i2s)
460                return -ENOMEM;
461
462        i2s->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
463
464        if (!i2s->mem) {
465                ret = -ENOENT;
466                goto err_free;
467        }
468
469        i2s->mem = request_mem_region(i2s->mem->start, resource_size(i2s->mem),
470                                pdev->name);
471
472        if (!i2s->mem) {
473                ret = -EBUSY;
474                goto err_free;
475        }
476
477        i2s->base = ioremap_nocache(i2s->mem->start, resource_size(i2s->mem));
478
479        if (!i2s->base) {
480                ret = -EBUSY;
481                goto err_release_mem_region;
482        }
483
484        i2s->phys_base = i2s->mem->start;
485
486        jz4740_i2s_dai.private_data = i2s;
487
488        ret = snd_soc_register_dai(&jz4740_i2s_dai);
489
490        i2s->clk = clk_get(&pdev->dev, "i2s");
491
492        if (IS_ERR(i2s->clk)) {
493                ret = PTR_ERR(i2s->clk);
494                goto err_iounmap;
495        }
496
497        platform_set_drvdata(pdev, i2s);
498
499        return 0;
500
501err_iounmap:
502        iounmap(i2s->base);
503err_release_mem_region:
504        release_mem_region(i2s->mem->start, resource_size(i2s->mem));
505err_free:
506        kfree(i2s);
507
508        return ret;
509}
510
511static int __devexit jz4740_i2s_dev_remove(struct platform_device *pdev)
512{
513        struct jz4740_i2s *i2s = platform_get_drvdata(pdev);
514
515        snd_soc_unregister_dai(&jz4740_i2s_dai);
516
517        clk_put(i2s->clk);
518
519        iounmap(i2s->base);
520        release_mem_region(i2s->mem->start, resource_size(i2s->mem));
521
522        platform_set_drvdata(pdev, NULL);
523        kfree(i2s);
524
525        return 0;
526}
527
528static struct platform_driver jz4740_i2s_driver = {
529        .probe = jz4740_i2s_dev_probe,
530        .remove = __devexit_p(jz4740_i2s_dev_remove),
531        .driver = {
532                .name = "jz4740-i2s",
533                .owner = THIS_MODULE,
534        },
535};
536
537static int __init jz4740_i2s_init(void)
538{
539        return platform_driver_register(&jz4740_i2s_driver);
540}
541module_init(jz4740_i2s_init);
542
543static void __exit jz4740_i2s_exit(void)
544{
545        platform_driver_unregister(&jz4740_i2s_driver);
546}
547module_exit(jz4740_i2s_exit);
548
549MODULE_AUTHOR("Lars-Peter Clausen, <lars@metafoo.de>");
550MODULE_DESCRIPTION("Ingenic JZ4740 SoC I2S driver");
551MODULE_LICENSE("GPL");
552MODULE_ALIAS("platform:jz4740-i2s");
Note: See TracBrowser for help on using the repository browser.