Date:2010-04-24 12:35:07 (13 years 11 months ago)
Author:Lars C.
Commit:2a5b209ba3d546e96e98cf70719030725f2207d3
Message:Add jz4740 SoC sound drivers

Files: sound/soc/Kconfig (1 diff)
sound/soc/Makefile (1 diff)
sound/soc/jz4740/Kconfig (1 diff)
sound/soc/jz4740/Makefile (1 diff)
sound/soc/jz4740/jz4740-i2s.c (1 diff)
sound/soc/jz4740/jz4740-i2s.h (1 diff)
sound/soc/jz4740/jz4740-pcm.c (1 diff)
sound/soc/jz4740/jz4740-pcm.h (1 diff)

Change Details

sound/soc/Kconfig
3636source "sound/soc/s6000/Kconfig"
3737source "sound/soc/sh/Kconfig"
3838source "sound/soc/txx9/Kconfig"
39source "sound/soc/jz4740/Kconfig"
3940
4041# Supported codecs
4142source "sound/soc/codecs/Kconfig"
sound/soc/Makefile
1414obj-$(CONFIG_SND_SOC) += s6000/
1515obj-$(CONFIG_SND_SOC) += sh/
1616obj-$(CONFIG_SND_SOC) += txx9/
17obj-$(CONFIG_SND_SOC) += jz4740/
sound/soc/jz4740/Kconfig
1config SND_JZ4740_SOC
2    tristate "SoC Audio for Ingenic JZ4740 SoC"
3    depends on SOC_JZ4740 && SND_SOC
4    help
5      Say Y or M if you want to add support for codecs attached to
6      the Jz4740 AC97, I2S or SSP interface. You will also need
7      to select the audio interfaces to support below.
8
9config SND_JZ4740_SOC_I2S
10    depends on SND_JZ4740_SOC
11    tristate "SoC Audio (I2S protocol) for Ingenic jz4740 chip"
12    help
13      Say Y if you want to use I2S protocol and I2S codec on Ingenic Jz4740 QI_LB60 board.
sound/soc/jz4740/Makefile
1#
2# Jz4740 Platform Support
3#
4snd-soc-jz4740-objs := jz4740-pcm.o
5snd-soc-jz4740-i2s-objs := jz4740-i2s.o
6
7obj-$(CONFIG_SND_JZ4740_SOC) += snd-soc-jz4740.o
8obj-$(CONFIG_SND_JZ4740_SOC_I2S) += snd-soc-jz4740-i2s.o
9
sound/soc/jz4740/jz4740-i2s.c
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/init.h>
16#include <linux/io.h>
17#include <linux/kernel.h>
18#include <linux/module.h>
19#include <linux/platform_device.h>
20#include <linux/slab.h>
21
22#include <linux/clk.h>
23#include <linux/delay.h>
24
25#include <linux/dma-mapping.h>
26
27#include <sound/core.h>
28#include <sound/pcm.h>
29#include <sound/pcm_params.h>
30#include <sound/soc.h>
31#include <sound/soc-dapm.h>
32#include <sound/initval.h>
33
34#include "jz4740-i2s.h"
35#include "jz4740-pcm.h"
36
37#define JZ_REG_AIC_CONF 0x00
38#define JZ_REG_AIC_CTRL 0x04
39#define JZ_REG_AIC_I2S_FMT 0x10
40#define JZ_REG_AIC_FIFO_STATUS 0x14
41#define JZ_REG_AIC_I2S_STATUS 0x1c
42#define JZ_REG_AIC_CLK_DIV 0x30
43#define JZ_REG_AIC_FIFO 0x34
44
45#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_MASK (0xf << 12)
46#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_MASK (0xf << 8)
47#define JZ_AIC_CONF_OVERFLOW_PLAY_LAST BIT(6)
48#define JZ_AIC_CONF_INTERNAL_CODEC BIT(5)
49#define JZ_AIC_CONF_I2S BIT(4)
50#define JZ_AIC_CONF_RESET BIT(3)
51#define JZ_AIC_CONF_BIT_CLK_MASTER BIT(2)
52#define JZ_AIC_CONF_SYNC_CLK_MASTER BIT(1)
53#define JZ_AIC_CONF_ENABLE BIT(0)
54
55#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 12
56#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 8
57
58#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK (0x7 << 19)
59#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK (0x7 << 16)
60#define JZ_AIC_CTRL_ENABLE_RX_DMA BIT(15)
61#define JZ_AIC_CTRL_ENABLE_TX_DMA BIT(14)
62#define JZ_AIC_CTRL_MONO_TO_STEREO BIT(11)
63#define JZ_AIC_CTRL_SWITCH_ENDIANNESS BIT(10)
64#define JZ_AIC_CTRL_SIGNED_TO_UNSIGNED BIT(9)
65#define JZ_AIC_CTRL_FLUSH BIT(8)
66#define JZ_AIC_CTRL_ENABLE_ROR_INT BIT(6)
67#define JZ_AIC_CTRL_ENABLE_TUR_INT BIT(5)
68#define JZ_AIC_CTRL_ENABLE_RFS_INT BIT(4)
69#define JZ_AIC_CTRL_ENABLE_TFS_INT BIT(3)
70#define JZ_AIC_CTRL_ENABLE_LOOPBACK BIT(2)
71#define JZ_AIC_CTRL_ENABLE_PLAYBACK BIT(1)
72#define JZ_AIC_CTRL_ENABLE_CAPTURE BIT(0)
73
74#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET 19
75#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET 16
76
77#define JZ_AIC_I2S_FMT_DISABLE_BIT_CLK BIT(12)
78#define JZ_AIC_I2S_FMT_ENABLE_SYS_CLK BIT(4)
79#define JZ_AIC_I2S_FMT_MSB BIT(0)
80
81#define JZ_AIC_I2S_STATUS_BUSY BIT(2)
82
83#define JZ_AIC_CLK_DIV_MASK 0xf
84
85struct jz4740_i2s {
86    struct resource *mem;
87    void __iomem *base;
88    dma_addr_t phys_base;
89
90    struct clk *clk_aic;
91    struct clk *clk_i2s;
92
93    struct jz4740_pcm_config capture_pcm_config;
94    struct jz4740_pcm_config playback_pcm_config;
95};
96
97static struct jz4740_dma_config jz4740_i2s_dma_playback_config = {
98    .src_width = JZ4740_DMA_WIDTH_16BIT,
99    .dst_width = JZ4740_DMA_WIDTH_32BIT,
100    .transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE,
101    .request_type = JZ4740_DMA_TYPE_AIC_TRANSMIT,
102    .flags = JZ4740_DMA_SRC_AUTOINC,
103    .mode = JZ4740_DMA_MODE_SINGLE,
104};
105
106static struct jz4740_dma_config jz4740_i2s_dma_capture_config = {
107    .src_width = JZ4740_DMA_WIDTH_32BIT,
108    .dst_width = JZ4740_DMA_WIDTH_16BIT,
109    .transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE,
110    .request_type = JZ4740_DMA_TYPE_AIC_RECEIVE,
111    .flags = JZ4740_DMA_DST_AUTOINC,
112    .mode = JZ4740_DMA_MODE_SINGLE,
113};
114
115
116static inline uint32_t jz4740_i2s_read(const struct jz4740_i2s *i2s,
117        unsigned int reg)
118{
119    return readl(i2s->base + reg);
120}
121
122static inline void jz4740_i2s_write(const struct jz4740_i2s *i2s,
123        unsigned int reg, uint32_t value)
124{
125    writel(value, i2s->base + reg);
126}
127
128static inline struct jz4740_i2s *jz4740_dai_to_i2s(struct snd_soc_dai *dai)
129{
130    return dai->private_data;
131}
132
133static int jz4740_i2s_startup(struct snd_pcm_substream *substream,
134        struct snd_soc_dai *dai)
135{
136    struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
137    uint32_t conf, ctrl;
138
139    if (dai->active)
140        return 0;
141
142    conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
143    ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
144
145    conf |= JZ_AIC_CONF_ENABLE;
146    ctrl |= JZ_AIC_CTRL_FLUSH;
147
148
149    jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
150    clk_enable(i2s->clk_i2s);
151    jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
152
153    return 0;
154}
155
156static void jz4740_i2s_shutdown(struct snd_pcm_substream *substream, struct
157                              snd_soc_dai *dai)
158{
159    struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
160    uint32_t conf;
161
162    if (!dai->active)
163        return;
164
165    conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
166    conf &= ~JZ_AIC_CONF_ENABLE;
167    jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
168
169    clk_disable(i2s->clk_i2s);
170}
171
172
173static int jz4740_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
174                   struct snd_soc_dai *dai)
175{
176    struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
177    bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
178
179    uint32_t ctrl;
180    uint32_t mask;
181
182    if (playback) {
183        mask = JZ_AIC_CTRL_ENABLE_PLAYBACK | JZ_AIC_CTRL_ENABLE_TX_DMA;
184    } else {
185        mask = JZ_AIC_CTRL_ENABLE_CAPTURE | JZ_AIC_CTRL_ENABLE_RX_DMA;
186    }
187
188    ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
189
190    switch (cmd) {
191    case SNDRV_PCM_TRIGGER_START:
192    case SNDRV_PCM_TRIGGER_RESUME:
193    case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
194        ctrl |= mask;
195        break;
196    case SNDRV_PCM_TRIGGER_STOP:
197    case SNDRV_PCM_TRIGGER_SUSPEND:
198    case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
199        ctrl &= ~mask;
200        break;
201    default:
202        return -EINVAL;
203    }
204
205    jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
206
207    return 0;
208}
209
210
211static int jz4740_i2s_set_fmt(struct snd_soc_dai *dai,
212                   unsigned int fmt)
213{
214    struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
215
216    uint32_t format = 0;
217    uint32_t conf;
218
219    conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
220
221    conf &= ~(JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER);
222
223    switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
224    case SND_SOC_DAIFMT_CBS_CFS:
225        conf |= JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER;
226        format |= JZ_AIC_I2S_FMT_ENABLE_SYS_CLK;
227        break;
228    case SND_SOC_DAIFMT_CBM_CFS:
229        conf |= JZ_AIC_CONF_SYNC_CLK_MASTER;
230        break;
231    case SND_SOC_DAIFMT_CBS_CFM:
232        conf |= JZ_AIC_CONF_BIT_CLK_MASTER;
233        break;
234    case SND_SOC_DAIFMT_CBM_CFM:
235        break;
236    default:
237        return -EINVAL;
238    }
239
240    switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
241    case SND_SOC_DAIFMT_MSB:
242        format |= JZ_AIC_I2S_FMT_MSB;
243        break;
244    case SND_SOC_DAIFMT_I2S:
245        break;
246    default:
247        return -EINVAL;
248    }
249
250    switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
251    case SND_SOC_DAIFMT_NB_NF:
252        break;
253    default:
254        return -EINVAL;
255    }
256
257    jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
258    jz4740_i2s_write(i2s, JZ_REG_AIC_I2S_FMT, format);
259
260    return 0;
261}
262
263static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream,
264                 struct snd_pcm_hw_params *params,
265                 struct snd_soc_dai *dai)
266{
267    struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
268    bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
269    enum jz4740_dma_width dma_width;
270    struct jz4740_pcm_config *pcm_config;
271    unsigned int sample_size;
272    uint32_t ctrl;
273
274    ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
275
276    switch (params_format(params)) {
277    case SNDRV_PCM_FORMAT_S8:
278        sample_size = 0;
279        dma_width = JZ4740_DMA_WIDTH_8BIT;
280        break;
281    case SNDRV_PCM_FORMAT_S16:
282        sample_size = 1;
283        dma_width = JZ4740_DMA_WIDTH_16BIT;
284        break;
285    default:
286        return -EINVAL;
287    }
288
289    if (playback) {
290        ctrl &= ~JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK;
291        ctrl |= sample_size << JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET;
292    } else {
293        ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK;
294        ctrl |= sample_size << JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET;
295    }
296
297    switch (params_channels(params)) {
298    case 2:
299        break;
300    case 1:
301        if (playback) {
302            ctrl |= JZ_AIC_CTRL_MONO_TO_STEREO;
303            break;
304        }
305    default: /* Falltrough */
306        return -EINVAL;
307    }
308
309    jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
310
311    if (playback) {
312        jz4740_i2s_dma_playback_config.src_width = dma_width;
313        pcm_config = &i2s->playback_pcm_config;
314        pcm_config->dma_config = &jz4740_i2s_dma_playback_config;
315    } else {
316        jz4740_i2s_dma_capture_config.dst_width = dma_width;
317        pcm_config = &i2s->capture_pcm_config;
318        pcm_config->dma_config = &jz4740_i2s_dma_capture_config;
319    }
320    pcm_config->fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO;
321
322    snd_soc_dai_set_dma_data(dai, substream, pcm_config);
323
324    return 0;
325}
326
327static int jz4740_i2s_set_clkdiv(struct snd_soc_dai *dai,
328                  int div_id, int div)
329{
330    struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
331
332    switch (div_id) {
333    case JZ4740_I2S_BIT_CLK:
334        if (div & 1 || div > 16)
335            return -EINVAL;
336        jz4740_i2s_write(i2s, JZ_REG_AIC_CLK_DIV, div - 1);
337        break;
338    default:
339        return -EINVAL;
340    }
341
342    return 0;
343}
344
345static int jz4740_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id,
346                  unsigned int freq, int dir)
347{
348    struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
349    int ret = 0;
350    struct clk *parent;
351
352    switch (clk_id) {
353    case JZ4740_I2S_CLKSRC_EXT:
354        parent = clk_get(NULL, "ext");
355        clk_set_parent(i2s->clk_i2s, parent);
356        break;
357    case JZ4740_I2S_CLKSRC_PLL:
358        parent = clk_get(NULL, "pll half");
359        clk_set_parent(i2s->clk_i2s, parent);
360        ret = clk_set_rate(i2s->clk_i2s, freq);
361        break;
362    default:
363        return -EINVAL;
364    }
365    clk_put(parent);
366
367    return ret;
368}
369
370static int jz4740_i2s_suspend(struct snd_soc_dai *dai)
371{
372    struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
373    uint32_t conf;
374
375    if (dai->active) {
376        conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
377        conf &= ~JZ_AIC_CONF_ENABLE;
378        jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
379
380        clk_disable(i2s->clk_i2s);
381    }
382
383    clk_disable(i2s->clk_aic);
384
385    return 0;
386}
387
388static int jz4740_i2s_resume(struct snd_soc_dai *dai)
389{
390    struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
391    uint32_t conf;
392
393    clk_enable(i2s->clk_aic);
394
395    if (dai->active) {
396        clk_enable(i2s->clk_i2s);
397
398        conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
399        conf |= JZ_AIC_CONF_ENABLE;
400        jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
401
402    }
403
404    return 0;
405}
406
407static int jz4740_i2s_probe(struct platform_device *pdev, struct snd_soc_dai *dai)
408{
409    struct jz4740_i2s *i2s = jz4740_dai_to_i2s(dai);
410    uint32_t conf;
411
412    conf = (7 << JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) |
413        (8 << JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) |
414        JZ_AIC_CONF_OVERFLOW_PLAY_LAST |
415        JZ_AIC_CONF_I2S |
416        JZ_AIC_CONF_INTERNAL_CODEC;
417
418    jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, JZ_AIC_CONF_RESET);
419    jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
420
421    return 0;
422}
423
424
425static struct snd_soc_dai_ops jz4740_i2s_dai_ops = {
426    .startup = jz4740_i2s_startup,
427    .shutdown = jz4740_i2s_shutdown,
428    .trigger = jz4740_i2s_trigger,
429    .hw_params = jz4740_i2s_hw_params,
430    .set_fmt = jz4740_i2s_set_fmt,
431    .set_clkdiv = jz4740_i2s_set_clkdiv,
432    .set_sysclk = jz4740_i2s_set_sysclk,
433};
434
435#define JZ4740_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | \
436        SNDRV_PCM_FMTBIT_S16_LE)
437
438struct snd_soc_dai jz4740_i2s_dai = {
439    .name = "jz4740-i2s",
440    .probe = jz4740_i2s_probe,
441    .playback = {
442        .channels_min = 1,
443        .channels_max = 2,
444        .rates = SNDRV_PCM_RATE_8000_44100,
445        .formats = JZ4740_I2S_FMTS,
446    },
447    .capture = {
448        .channels_min = 2,
449        .channels_max = 2,
450        .rates = SNDRV_PCM_RATE_8000_44100,
451        .formats = JZ4740_I2S_FMTS,
452    },
453    .symmetric_rates = 1,
454    .ops = &jz4740_i2s_dai_ops,
455    .suspend = jz4740_i2s_suspend,
456    .resume = jz4740_i2s_resume,
457};
458EXPORT_SYMBOL_GPL(jz4740_i2s_dai);
459
460static int __devinit jz4740_i2s_dev_probe(struct platform_device *pdev)
461{
462    struct jz4740_i2s *i2s;
463    int ret;
464
465    i2s = kzalloc(sizeof(*i2s), GFP_KERNEL);
466
467    if (!i2s)
468        return -ENOMEM;
469
470    i2s->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
471
472    if (!i2s->mem) {
473        ret = -ENOENT;
474        goto err_free;
475    }
476
477    i2s->mem = request_mem_region(i2s->mem->start, resource_size(i2s->mem),
478                pdev->name);
479
480    if (!i2s->mem) {
481        ret = -EBUSY;
482        goto err_free;
483    }
484
485    i2s->base = ioremap_nocache(i2s->mem->start, resource_size(i2s->mem));
486
487    if (!i2s->base) {
488        ret = -EBUSY;
489        goto err_release_mem_region;
490    }
491
492    i2s->phys_base = i2s->mem->start;
493
494    jz4740_i2s_dai.private_data = i2s;
495
496    ret = snd_soc_register_dai(&jz4740_i2s_dai);
497
498    i2s->clk_aic = clk_get(&pdev->dev, "aic");
499
500    if (IS_ERR(i2s->clk_aic)) {
501        ret = PTR_ERR(i2s->clk_aic);
502        goto err_iounmap;
503    }
504
505
506    i2s->clk_i2s = clk_get(&pdev->dev, "i2s");
507
508    if (IS_ERR(i2s->clk_i2s)) {
509        ret = PTR_ERR(i2s->clk_i2s);
510        goto err_iounmap;
511    }
512
513    clk_enable(i2s->clk_aic);
514
515    platform_set_drvdata(pdev, i2s);
516
517    return 0;
518
519err_iounmap:
520    iounmap(i2s->base);
521err_release_mem_region:
522    release_mem_region(i2s->mem->start, resource_size(i2s->mem));
523err_free:
524    kfree(i2s);
525
526    return ret;
527}
528
529static int __devexit jz4740_i2s_dev_remove(struct platform_device *pdev)
530{
531    struct jz4740_i2s *i2s = platform_get_drvdata(pdev);
532
533    snd_soc_unregister_dai(&jz4740_i2s_dai);
534
535    clk_disable(i2s->clk_aic);
536    clk_put(i2s->clk_i2s);
537    clk_put(i2s->clk_aic);
538
539    iounmap(i2s->base);
540    release_mem_region(i2s->mem->start, resource_size(i2s->mem));
541
542    platform_set_drvdata(pdev, NULL);
543    kfree(i2s);
544
545    return 0;
546}
547
548static struct platform_driver jz4740_i2s_driver = {
549    .probe = jz4740_i2s_dev_probe,
550    .remove = __devexit_p(jz4740_i2s_dev_remove),
551    .driver = {
552        .name = "jz4740-i2s",
553        .owner = THIS_MODULE,
554    },
555};
556
557static int __init jz4740_i2s_init(void)
558{
559    return platform_driver_register(&jz4740_i2s_driver);
560}
561module_init(jz4740_i2s_init);
562
563static void __exit jz4740_i2s_exit(void)
564{
565    platform_driver_unregister(&jz4740_i2s_driver);
566}
567module_exit(jz4740_i2s_exit);
568
569MODULE_AUTHOR("Lars-Peter Clausen, <lars@metafoo.de>");
570MODULE_DESCRIPTION("Ingenic JZ4740 SoC I2S driver");
571MODULE_LICENSE("GPL");
572MODULE_ALIAS("platform:jz4740-i2s");
sound/soc/jz4740/jz4740-i2s.h
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License version 2 as
4 * published by the Free Software Foundation.
5 */
6
7#ifndef _JZ4740_I2S_H
8#define _JZ4740_I2S_H
9
10/* I2S clock source */
11#define JZ4740_I2S_CLKSRC_EXT 0
12#define JZ4740_I2S_CLKSRC_PLL 1
13
14#define JZ4740_I2S_BIT_CLK 0
15
16extern struct snd_soc_dai jz4740_i2s_dai;
17
18#endif
sound/soc/jz4740/jz4740-pcm.c
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/init.h>
16#include <linux/interrupt.h>
17#include <linux/kernel.h>
18#include <linux/module.h>
19#include <linux/slab.h>
20
21#include <linux/dma-mapping.h>
22
23#include <sound/core.h>
24#include <sound/pcm.h>
25#include <sound/pcm_params.h>
26#include <sound/soc.h>
27
28#include <asm/mach-jz4740/dma.h>
29#include "jz4740-pcm.h"
30
31struct jz4740_runtime_data {
32    unsigned int dma_period;
33    dma_addr_t dma_start;
34    dma_addr_t dma_pos;
35    dma_addr_t dma_end;
36
37    struct jz4740_dma_chan *dma;
38
39    dma_addr_t fifo_addr;
40};
41
42/* identify hardware playback capabilities */
43static const struct snd_pcm_hardware jz4740_pcm_hardware = {
44    .info = SNDRV_PCM_INFO_MMAP |
45        SNDRV_PCM_INFO_MMAP_VALID |
46        SNDRV_PCM_INFO_INTERLEAVED |
47        SNDRV_PCM_INFO_BLOCK_TRANSFER,
48    .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
49
50    .rates = SNDRV_PCM_RATE_8000_48000,
51    .channels_min = 1,
52    .channels_max = 2,
53    .period_bytes_min = 32,
54    .period_bytes_max = 2 * PAGE_SIZE,
55    .periods_min = 2,
56    .periods_max = 128,
57    .buffer_bytes_max = 128 * 2 * PAGE_SIZE,
58    .fifo_size = 32,
59};
60
61static void jz4740_pcm_start_transfer(struct jz4740_runtime_data *prtd, int stream)
62{
63    unsigned int count;
64
65    if (prtd->dma_pos + prtd->dma_period > prtd->dma_end)
66        count = prtd->dma_end - prtd->dma_pos;
67    else
68        count = prtd->dma_period;
69
70    jz4740_dma_disable(prtd->dma);
71
72    if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
73        jz4740_dma_set_src_addr(prtd->dma, prtd->dma_pos);
74        jz4740_dma_set_dst_addr(prtd->dma, prtd->fifo_addr);
75    } else {
76        jz4740_dma_set_src_addr(prtd->dma, prtd->fifo_addr);
77        jz4740_dma_set_dst_addr(prtd->dma, prtd->dma_pos);
78    }
79
80    jz4740_dma_set_transfer_count(prtd->dma, count);
81
82    jz4740_dma_enable(prtd->dma);
83
84    prtd->dma_pos += prtd->dma_period;
85    if (prtd->dma_pos >= prtd->dma_end)
86        prtd->dma_pos = prtd->dma_start;
87}
88
89static void jz4740_pcm_dma_transfer_done(struct jz4740_dma_chan *dma, int err,
90    void *dev_id)
91{
92    struct snd_pcm_substream *substream = dev_id;
93    struct snd_pcm_runtime *runtime = substream->runtime;
94    struct jz4740_runtime_data *prtd = runtime->private_data;
95
96    snd_pcm_period_elapsed(substream);
97
98    jz4740_pcm_start_transfer(prtd, substream->stream);
99}
100
101static int jz4740_pcm_hw_params(struct snd_pcm_substream *substream,
102    struct snd_pcm_hw_params *params)
103{
104    struct snd_pcm_runtime *runtime = substream->runtime;
105    struct jz4740_runtime_data *prtd = runtime->private_data;
106    struct snd_soc_pcm_runtime *rtd = substream->private_data;
107    struct jz4740_pcm_config *config;
108
109    config = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream);
110    if (!prtd->dma) {
111        const char *dma_channel_name;
112        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
113            dma_channel_name = "PCM Playback";
114        else
115            dma_channel_name = "PCM Capture";
116
117        prtd->dma = jz4740_dma_request(substream, dma_channel_name);
118    }
119
120    if (!prtd->dma)
121        return -EBUSY;
122
123    jz4740_dma_configure(prtd->dma, config->dma_config);
124    prtd->fifo_addr = config->fifo_addr;
125
126    jz4740_dma_set_complete_cb(prtd->dma, jz4740_pcm_dma_transfer_done);
127
128    snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
129    runtime->dma_bytes = params_buffer_bytes(params);
130
131    prtd->dma_period = params_period_bytes(params);
132    prtd->dma_start = runtime->dma_addr;
133    prtd->dma_pos = prtd->dma_start;
134    prtd->dma_end = prtd->dma_start + runtime->dma_bytes;
135
136    return 0;
137}
138
139static int jz4740_pcm_hw_free(struct snd_pcm_substream *substream)
140{
141    struct jz4740_runtime_data *prtd = substream->runtime->private_data;
142
143    snd_pcm_set_runtime_buffer(substream, NULL);
144    if (prtd->dma) {
145        jz4740_dma_free(prtd->dma);
146        prtd->dma = NULL;
147    }
148
149    return 0;
150}
151
152static int jz4740_pcm_prepare(struct snd_pcm_substream *substream)
153{
154    struct jz4740_runtime_data *prtd = substream->runtime->private_data;
155    int ret = 0;
156
157    if (!prtd->dma)
158            return 0;
159
160    prtd->dma_pos = prtd->dma_start;
161
162    return ret;
163}
164
165static int jz4740_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
166{
167    struct snd_pcm_runtime *runtime = substream->runtime;
168    struct jz4740_runtime_data *prtd = runtime->private_data;
169
170    int ret = 0;
171
172    switch (cmd) {
173    case SNDRV_PCM_TRIGGER_START:
174    case SNDRV_PCM_TRIGGER_RESUME:
175    case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
176        jz4740_pcm_start_transfer(prtd, substream->stream);
177        break;
178    case SNDRV_PCM_TRIGGER_STOP:
179    case SNDRV_PCM_TRIGGER_SUSPEND:
180    case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
181        jz4740_dma_disable(prtd->dma);
182        break;
183    default:
184        ret = -EINVAL;
185    }
186
187    return ret;
188}
189
190static snd_pcm_uframes_t jz4740_pcm_pointer(struct snd_pcm_substream *substream)
191{
192    struct snd_pcm_runtime *runtime = substream->runtime;
193    struct jz4740_runtime_data *prtd = runtime->private_data;
194    unsigned long count, pos;
195    snd_pcm_uframes_t offset;
196    struct jz4740_dma_chan *dma = prtd->dma;
197
198    count = jz4740_dma_get_residue(dma);
199    if (prtd->dma_pos == prtd->dma_start)
200        pos = prtd->dma_end - prtd->dma_start - count;
201    else
202        pos = prtd->dma_pos - prtd->dma_start - count;
203
204    offset = bytes_to_frames(runtime, pos);
205    if (offset >= runtime->buffer_size)
206        offset = 0;
207
208    return offset;
209}
210
211static int jz4740_pcm_open(struct snd_pcm_substream *substream)
212{
213    struct snd_pcm_runtime *runtime = substream->runtime;
214    struct jz4740_runtime_data *prtd;
215
216    snd_soc_set_runtime_hwparams(substream, &jz4740_pcm_hardware);
217    prtd = kzalloc(sizeof(struct jz4740_runtime_data), GFP_KERNEL);
218
219    if (prtd == NULL)
220        return -ENOMEM;
221
222    runtime->private_data = prtd;
223    return 0;
224}
225
226static int jz4740_pcm_close(struct snd_pcm_substream *substream)
227{
228    struct snd_pcm_runtime *runtime = substream->runtime;
229    struct jz4740_runtime_data *prtd = runtime->private_data;
230
231    kfree(prtd);
232
233    return 0;
234}
235
236static int jz4740_pcm_mmap(struct snd_pcm_substream *substream,
237    struct vm_area_struct *vma)
238{
239    return remap_pfn_range(vma, vma->vm_start,
240            substream->dma_buffer.addr >> PAGE_SHIFT,
241            vma->vm_end - vma->vm_start, vma->vm_page_prot);
242}
243
244static struct snd_pcm_ops jz4740_pcm_ops = {
245    .open = jz4740_pcm_open,
246    .close = jz4740_pcm_close,
247    .ioctl = snd_pcm_lib_ioctl,
248    .hw_params = jz4740_pcm_hw_params,
249    .hw_free = jz4740_pcm_hw_free,
250    .prepare = jz4740_pcm_prepare,
251    .trigger = jz4740_pcm_trigger,
252    .pointer = jz4740_pcm_pointer,
253    .mmap = jz4740_pcm_mmap,
254};
255
256static int jz4740_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
257{
258    struct snd_pcm_substream *substream = pcm->streams[stream].substream;
259    struct snd_dma_buffer *buf = &substream->dma_buffer;
260    size_t size = jz4740_pcm_hardware.buffer_bytes_max;
261
262    buf->dev.type = SNDRV_DMA_TYPE_DEV;
263    buf->dev.dev = pcm->card->dev;
264    buf->private_data = NULL;
265
266    buf->area = dma_alloc_noncoherent(pcm->card->dev, size,
267                      &buf->addr, GFP_KERNEL);
268    if (!buf->area)
269        return -ENOMEM;
270
271    buf->bytes = size;
272
273    return 0;
274}
275
276static void jz4740_pcm_free(struct snd_pcm *pcm)
277{
278    struct snd_pcm_substream *substream;
279    struct snd_dma_buffer *buf;
280    int stream;
281
282    for (stream = 0; stream < 2; stream++) {
283        substream = pcm->streams[stream].substream;
284        if (!substream)
285            continue;
286
287        buf = &substream->dma_buffer;
288        if (!buf->area)
289            continue;
290
291        dma_free_noncoherent(pcm->card->dev, buf->bytes,
292          buf->area, buf->addr);
293        buf->area = NULL;
294    }
295}
296
297static u64 jz4740_pcm_dmamask = DMA_BIT_MASK(32);
298
299int jz4740_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
300    struct snd_pcm *pcm)
301{
302    int ret = 0;
303
304    if (!card->dev->dma_mask)
305        card->dev->dma_mask = &jz4740_pcm_dmamask;
306
307    if (!card->dev->coherent_dma_mask)
308        card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
309
310    if (dai->playback.channels_min) {
311        ret = jz4740_pcm_preallocate_dma_buffer(pcm,
312            SNDRV_PCM_STREAM_PLAYBACK);
313        if (ret)
314            goto err;
315    }
316
317    if (dai->capture.channels_min) {
318        ret = jz4740_pcm_preallocate_dma_buffer(pcm,
319            SNDRV_PCM_STREAM_CAPTURE);
320        if (ret)
321            goto err;
322    }
323
324err:
325    return ret;
326}
327
328struct snd_soc_platform jz4740_soc_platform = {
329        .name = "jz4740-pcm",
330        .pcm_ops = &jz4740_pcm_ops,
331        .pcm_new = jz4740_pcm_new,
332        .pcm_free = jz4740_pcm_free,
333};
334EXPORT_SYMBOL_GPL(jz4740_soc_platform);
335
336static int __init jz4740_soc_platform_init(void)
337{
338    return snd_soc_register_platform(&jz4740_soc_platform);
339}
340module_init(jz4740_soc_platform_init);
341
342static void __exit jz4740_soc_platform_exit(void)
343{
344    snd_soc_unregister_platform(&jz4740_soc_platform);
345}
346module_exit(jz4740_soc_platform_exit);
347
348MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
349MODULE_DESCRIPTION("Ingenic SoC JZ4740 PCM driver");
350MODULE_LICENSE("GPL");
sound/soc/jz4740/jz4740-pcm.h
1/*
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 as
5 * published by the Free Software Foundation.
6 */
7
8#ifndef _JZ4740_PCM_H
9#define _JZ4740_PCM_H
10
11#include <linux/dma-mapping.h>
12#include <asm/mach-jz4740/dma.h>
13
14/* platform data */
15extern struct snd_soc_platform jz4740_soc_platform;
16
17struct jz4740_pcm_config {
18    struct jz4740_dma_config *dma_config;
19    phys_addr_t fifo_addr;
20};
21
22#endif

Archive Download the corresponding diff file



interactive