Date:2013-04-27 21:26:30 (7 years 2 months ago)
Author:Lars C.
Commit:654fa4606ec498505bac64ceb8c5ccffb18da55d
Message:ASoC: jz4740: Use the generic dmaengine PCM driver

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Files: sound/soc/jz4740/Kconfig (1 diff)
sound/soc/jz4740/jz4740-i2s.c (7 diffs)
sound/soc/jz4740/jz4740-pcm.c (2 diffs)

Change Details

sound/soc/jz4740/Kconfig
11config SND_JZ4740_SOC
22    tristate "SoC Audio for Ingenic JZ4740 SoC"
33    depends on MACH_JZ4740 && SND_SOC
4    select SND_SOC_GENERIC_DMAENGINE_PCM
45    help
56      Say Y or M if you want to add support for codecs attached to
67      the JZ4740 I2S interface. You will also need to select the audio
sound/soc/jz4740/jz4740-i2s.c
2929#include <sound/pcm_params.h>
3030#include <sound/soc.h>
3131#include <sound/initval.h>
32#include <sound/dmaengine_pcm.h>
33
34#include <asm/mach-jz4740/dma.h>
3235
3336#include "jz4740-i2s.h"
34#include "jz4740-pcm.h"
37
3538
3639#define JZ_REG_AIC_CONF 0x00
3740#define JZ_REG_AIC_CTRL 0x04
...... 
8992    struct clk *clk_aic;
9093    struct clk *clk_i2s;
9194
92    struct jz4740_pcm_config pcm_config_playback;
93    struct jz4740_pcm_config pcm_config_capture;
95    struct snd_dmaengine_dai_dma_data playback_dma_data;
96    struct snd_dmaengine_dai_dma_data capture_dma_data;
9497};
9598
9699static inline uint32_t jz4740_i2s_read(const struct jz4740_i2s *i2s,
...... 
233236    struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
234237{
235238    struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
236    enum jz4740_dma_width dma_width;
237    struct jz4740_pcm_config *pcm_config;
238239    unsigned int sample_size;
239240    uint32_t ctrl;
240241
...... 
243244    switch (params_format(params)) {
244245    case SNDRV_PCM_FORMAT_S8:
245246        sample_size = 0;
246        dma_width = JZ4740_DMA_WIDTH_8BIT;
247247        break;
248248    case SNDRV_PCM_FORMAT_S16:
249249        sample_size = 1;
250        dma_width = JZ4740_DMA_WIDTH_16BIT;
251250        break;
252251    default:
253252        return -EINVAL;
...... 
260259            ctrl |= JZ_AIC_CTRL_MONO_TO_STEREO;
261260        else
262261            ctrl &= ~JZ_AIC_CTRL_MONO_TO_STEREO;
263
264        pcm_config = &i2s->pcm_config_playback;
265        pcm_config->dma_config.dst_width = dma_width;
266
267262    } else {
268263        ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK;
269264        ctrl |= sample_size << JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET;
270
271        pcm_config = &i2s->pcm_config_capture;
272        pcm_config->dma_config.src_width = dma_width;
273265    }
274266
275267    jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
276268
277    snd_soc_dai_set_dma_data(dai, substream, pcm_config);
278
279269    return 0;
280270}
281271
...... 
342332
343333static void jz4740_i2c_init_pcm_config(struct jz4740_i2s *i2s)
344334{
345    struct jz4740_dma_config *dma_config;
335    struct snd_dmaengine_dai_dma_data *dma_data;
346336
347337    /* Playback */
348    dma_config = &i2s->pcm_config_playback.dma_config;
349    dma_config->src_width = JZ4740_DMA_WIDTH_32BIT;
350    dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE;
351    dma_config->request_type = JZ4740_DMA_TYPE_AIC_TRANSMIT;
352    dma_config->flags = JZ4740_DMA_SRC_AUTOINC;
353    dma_config->mode = JZ4740_DMA_MODE_SINGLE;
354    i2s->pcm_config_playback.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO;
338    dma_data = &i2s->playback_dma_data;
339    dma_data->maxburst = 16;
340    dma_data->slave_id = JZ4740_DMA_TYPE_AIC_TRANSMIT;
341    dma_data->addr = i2s->phys_base + JZ_REG_AIC_FIFO;
355342
356343    /* Capture */
357    dma_config = &i2s->pcm_config_capture.dma_config;
358    dma_config->dst_width = JZ4740_DMA_WIDTH_32BIT;
359    dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE;
360    dma_config->request_type = JZ4740_DMA_TYPE_AIC_RECEIVE;
361    dma_config->flags = JZ4740_DMA_DST_AUTOINC;
362    dma_config->mode = JZ4740_DMA_MODE_SINGLE;
363    i2s->pcm_config_capture.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO;
344    dma_data = &i2s->capture_dma_data;
345    dma_data->maxburst = 16;
346    dma_data->slave_id = JZ4740_DMA_TYPE_AIC_RECEIVE;
347    dma_data->addr = i2s->phys_base + JZ_REG_AIC_FIFO;
364348}
365349
366350static int jz4740_i2s_dai_probe(struct snd_soc_dai *dai)
...... 
371355    clk_prepare_enable(i2s->clk_aic);
372356
373357    jz4740_i2c_init_pcm_config(i2s);
358    dai->playback_dma_data = &i2s->playback_dma_data;
359    dai->capture_dma_data = &i2s->capture_dma_data;
374360
375361    conf = (7 << JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) |
376362        (8 << JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) |
sound/soc/jz4740/jz4740-pcm.c
1919#include <linux/platform_device.h>
2020#include <linux/slab.h>
2121
22#include <linux/dma-mapping.h>
22#include <sound/dmaengine_pcm.h>
2323
24#include <sound/core.h>
25#include <sound/pcm.h>
26#include <sound/pcm_params.h>
27#include <sound/soc.h>
28
29#include <asm/mach-jz4740/dma.h>
30#include "jz4740-pcm.h"
31
32struct jz4740_runtime_data {
33    unsigned long dma_period;
34    dma_addr_t dma_start;
35    dma_addr_t dma_pos;
36    dma_addr_t dma_end;
37
38    struct jz4740_dma_chan *dma;
39
40    dma_addr_t fifo_addr;
41};
42
43/* identify hardware playback capabilities */
4424static const struct snd_pcm_hardware jz4740_pcm_hardware = {
4525    .info = SNDRV_PCM_INFO_MMAP |
4626        SNDRV_PCM_INFO_MMAP_VALID |
4727        SNDRV_PCM_INFO_INTERLEAVED |
4828        SNDRV_PCM_INFO_BLOCK_TRANSFER,
4929    .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
50
51    .rates = SNDRV_PCM_RATE_8000_48000,
52    .channels_min = 1,
53    .channels_max = 2,
5430    .period_bytes_min = 16,
5531    .period_bytes_max = 2 * PAGE_SIZE,
5632    .periods_min = 2,
...... 
5935    .fifo_size = 32,
6036};
6137
62static void jz4740_pcm_start_transfer(struct jz4740_runtime_data *prtd,
63    struct snd_pcm_substream *substream)
64{
65    unsigned long count;
66
67    if (prtd->dma_pos == prtd->dma_end)
68        prtd->dma_pos = prtd->dma_start;
69
70    if (prtd->dma_pos + prtd->dma_period > prtd->dma_end)
71        count = prtd->dma_end - prtd->dma_pos;
72    else
73        count = prtd->dma_period;
74
75    jz4740_dma_disable(prtd->dma);
76
77    if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
78        jz4740_dma_set_src_addr(prtd->dma, prtd->dma_pos);
79        jz4740_dma_set_dst_addr(prtd->dma, prtd->fifo_addr);
80    } else {
81        jz4740_dma_set_src_addr(prtd->dma, prtd->fifo_addr);
82        jz4740_dma_set_dst_addr(prtd->dma, prtd->dma_pos);
83    }
84
85    jz4740_dma_set_transfer_count(prtd->dma, count);
86
87    prtd->dma_pos += count;
88
89    jz4740_dma_enable(prtd->dma);
90}
91
92static void jz4740_pcm_dma_transfer_done(struct jz4740_dma_chan *dma, int err,
93    void *dev_id)
94{
95    struct snd_pcm_substream *substream = dev_id;
96    struct snd_pcm_runtime *runtime = substream->runtime;
97    struct jz4740_runtime_data *prtd = runtime->private_data;
98
99    snd_pcm_period_elapsed(substream);
100
101    jz4740_pcm_start_transfer(prtd, substream);
102}
103
104static int jz4740_pcm_hw_params(struct snd_pcm_substream *substream,
105    struct snd_pcm_hw_params *params)
106{
107    struct snd_pcm_runtime *runtime = substream->runtime;
108    struct jz4740_runtime_data *prtd = runtime->private_data;
109    struct snd_soc_pcm_runtime *rtd = substream->private_data;
110    struct jz4740_pcm_config *config;
111
112    config = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
113
114    if (!config)
115        return 0;
116
117    if (!prtd->dma) {
118        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
119            prtd->dma = jz4740_dma_request(substream, "PCM Capture");
120        else
121            prtd->dma = jz4740_dma_request(substream, "PCM Playback");
122    }
123
124    if (!prtd->dma)
125        return -EBUSY;
126
127    jz4740_dma_configure(prtd->dma, &config->dma_config);
128    prtd->fifo_addr = config->fifo_addr;
129
130    jz4740_dma_set_complete_cb(prtd->dma, jz4740_pcm_dma_transfer_done);
131
132    snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
133    runtime->dma_bytes = params_buffer_bytes(params);
134
135    prtd->dma_period = params_period_bytes(params);
136    prtd->dma_start = runtime->dma_addr;
137    prtd->dma_pos = prtd->dma_start;
138    prtd->dma_end = prtd->dma_start + runtime->dma_bytes;
139
140    return 0;
141}
142
143static int jz4740_pcm_hw_free(struct snd_pcm_substream *substream)
144{
145    struct jz4740_runtime_data *prtd = substream->runtime->private_data;
146
147    snd_pcm_set_runtime_buffer(substream, NULL);
148    if (prtd->dma) {
149        jz4740_dma_free(prtd->dma);
150        prtd->dma = NULL;
151    }
152
153    return 0;
154}
155
156static int jz4740_pcm_prepare(struct snd_pcm_substream *substream)
157{
158    struct jz4740_runtime_data *prtd = substream->runtime->private_data;
159
160    if (!prtd->dma)
161        return -EBUSY;
162
163    prtd->dma_pos = prtd->dma_start;
164
165    return 0;
166}
167
168static int jz4740_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
169{
170    struct snd_pcm_runtime *runtime = substream->runtime;
171    struct jz4740_runtime_data *prtd = runtime->private_data;
172
173    switch (cmd) {
174    case SNDRV_PCM_TRIGGER_START:
175    case SNDRV_PCM_TRIGGER_RESUME:
176    case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
177        jz4740_pcm_start_transfer(prtd, substream);
178        break;
179    case SNDRV_PCM_TRIGGER_STOP:
180    case SNDRV_PCM_TRIGGER_SUSPEND:
181    case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
182        jz4740_dma_disable(prtd->dma);
183        break;
184    default:
185        break;
186    }
187
188    return 0;
189}
190
191static snd_pcm_uframes_t jz4740_pcm_pointer(struct snd_pcm_substream *substream)
192{
193    struct snd_pcm_runtime *runtime = substream->runtime;
194    struct jz4740_runtime_data *prtd = runtime->private_data;
195    unsigned long byte_offset;
196    snd_pcm_uframes_t offset;
197    struct jz4740_dma_chan *dma = prtd->dma;
198
199    /* prtd->dma_pos points to the end of the current transfer. So by
200     * subtracting prdt->dma_start we get the offset to the end of the
201     * current period in bytes. By subtracting the residue of the transfer
202     * we get the current offset in bytes. */
203    byte_offset = prtd->dma_pos - prtd->dma_start;
204    byte_offset -= jz4740_dma_get_residue(dma);
205
206    offset = bytes_to_frames(runtime, byte_offset);
207    if (offset >= runtime->buffer_size)
208        offset = 0;
209
210    return offset;
211}
212
213static int jz4740_pcm_open(struct snd_pcm_substream *substream)
214{
215    struct snd_pcm_runtime *runtime = substream->runtime;
216    struct jz4740_runtime_data *prtd;
217
218    prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
219    if (prtd == NULL)
220        return -ENOMEM;
221
222    snd_soc_set_runtime_hwparams(substream, &jz4740_pcm_hardware);
223
224    runtime->private_data = prtd;
225
226    return 0;
227}
228
229static int jz4740_pcm_close(struct snd_pcm_substream *substream)
230{
231    struct snd_pcm_runtime *runtime = substream->runtime;
232    struct jz4740_runtime_data *prtd = runtime->private_data;
233
234    kfree(prtd);
235
236    return 0;
237}
238
239static int jz4740_pcm_mmap(struct snd_pcm_substream *substream,
240    struct vm_area_struct *vma)
241{
242    return remap_pfn_range(vma, vma->vm_start,
243            substream->dma_buffer.addr >> PAGE_SHIFT,
244            vma->vm_end - vma->vm_start, vma->vm_page_prot);
245}
246
247static struct snd_pcm_ops jz4740_pcm_ops = {
248    .open = jz4740_pcm_open,
249    .close = jz4740_pcm_close,
250    .ioctl = snd_pcm_lib_ioctl,
251    .hw_params = jz4740_pcm_hw_params,
252    .hw_free = jz4740_pcm_hw_free,
253    .prepare = jz4740_pcm_prepare,
254    .trigger = jz4740_pcm_trigger,
255    .pointer = jz4740_pcm_pointer,
256    .mmap = jz4740_pcm_mmap,
257};
258
259static int jz4740_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
260{
261    struct snd_pcm_substream *substream = pcm->streams[stream].substream;
262    struct snd_dma_buffer *buf = &substream->dma_buffer;
263    size_t size = jz4740_pcm_hardware.buffer_bytes_max;
264
265    buf->dev.type = SNDRV_DMA_TYPE_DEV;
266    buf->dev.dev = pcm->card->dev;
267    buf->private_data = NULL;
268
269    buf->area = dma_alloc_noncoherent(pcm->card->dev, size,
270                      &buf->addr, GFP_KERNEL);
271    if (!buf->area)
272        return -ENOMEM;
273
274    buf->bytes = size;
275
276    return 0;
277}
278
279static void jz4740_pcm_free(struct snd_pcm *pcm)
280{
281    struct snd_pcm_substream *substream;
282    struct snd_dma_buffer *buf;
283    int stream;
284
285    for (stream = 0; stream < SNDRV_PCM_STREAM_LAST; ++stream) {
286        substream = pcm->streams[stream].substream;
287        if (!substream)
288            continue;
289
290        buf = &substream->dma_buffer;
291        if (!buf->area)
292            continue;
293
294        dma_free_noncoherent(pcm->card->dev, buf->bytes, buf->area,
295                buf->addr);
296        buf->area = NULL;
297    }
298}
299
300static u64 jz4740_pcm_dmamask = DMA_BIT_MASK(32);
301
302static int jz4740_pcm_new(struct snd_soc_pcm_runtime *rtd)
303{
304    struct snd_card *card = rtd->card->snd_card;
305    struct snd_pcm *pcm = rtd->pcm;
306    int ret = 0;
307
308    if (!card->dev->dma_mask)
309        card->dev->dma_mask = &jz4740_pcm_dmamask;
310
311    if (!card->dev->coherent_dma_mask)
312        card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
313
314    if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
315        ret = jz4740_pcm_preallocate_dma_buffer(pcm,
316            SNDRV_PCM_STREAM_PLAYBACK);
317        if (ret)
318            goto err;
319    }
320
321    if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
322        ret = jz4740_pcm_preallocate_dma_buffer(pcm,
323            SNDRV_PCM_STREAM_CAPTURE);
324        if (ret)
325            goto err;
326    }
327
328err:
329    return ret;
330}
331
332static struct snd_soc_platform_driver jz4740_soc_platform = {
333        .ops = &jz4740_pcm_ops,
334        .pcm_new = jz4740_pcm_new,
335        .pcm_free = jz4740_pcm_free,
38static const struct snd_dmaengine_pcm_config jz4740_dmaengine_pcm_config = {
39    .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
40    .pcm_hardware = &jz4740_pcm_hardware,
41    .prealloc_buffer_size = 256 * PAGE_SIZE,
33642};
33743
33844static int jz4740_pcm_probe(struct platform_device *pdev)
33945{
340    return snd_soc_register_platform(&pdev->dev, &jz4740_soc_platform);
46    return snd_dmaengine_pcm_register(&pdev->dev, &jz4740_dmaengine_pcm_config,
47        SND_DMAENGINE_PCM_FLAG_COMPAT);
34148}
34249
34350static int jz4740_pcm_remove(struct platform_device *pdev)
34451{
345    snd_soc_unregister_platform(&pdev->dev);
52    snd_dmaengine_pcm_unregister(&pdev->dev);
34653    return 0;
34754}
34855

Archive Download the corresponding diff file



interactive