2 * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
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.
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.
15 #include <linux/init.h>
16 #include <linux/interrupt.h>
17 #include <linux/kernel.h>
18 #include <linux/module.h>
19 #include <linux/platform_device.h>
20 #include <linux/slab.h>
22 #include <linux/dma-mapping.h>
24 #include <sound/core.h>
25 #include <sound/pcm.h>
26 #include <sound/pcm_params.h>
27 #include <sound/soc.h>
29 #include <asm/mach-jz4740/dma.h>
30 #include "jz4740-pcm.h"
32 struct jz4740_runtime_data
{
33 unsigned long dma_period
;
38 struct jz4740_dma_chan
*dma
;
43 /* identify hardware playback capabilities */
44 static const struct snd_pcm_hardware jz4740_pcm_hardware
= {
45 .info
= SNDRV_PCM_INFO_MMAP
|
46 SNDRV_PCM_INFO_MMAP_VALID
|
47 SNDRV_PCM_INFO_INTERLEAVED
|
48 SNDRV_PCM_INFO_BLOCK_TRANSFER
,
49 .formats
= SNDRV_PCM_FMTBIT_S16_LE
| SNDRV_PCM_FMTBIT_S8
,
51 .rates
= SNDRV_PCM_RATE_8000_48000
,
54 .period_bytes_min
= 16,
55 .period_bytes_max
= 2 * PAGE_SIZE
,
58 .buffer_bytes_max
= 128 * 2 * PAGE_SIZE
,
62 static void jz4740_pcm_start_transfer(struct jz4740_runtime_data
*prtd
,
63 struct snd_pcm_substream
*substream
)
67 if (prtd
->dma_pos
== prtd
->dma_end
)
68 prtd
->dma_pos
= prtd
->dma_start
;
70 if (prtd
->dma_pos
+ prtd
->dma_period
> prtd
->dma_end
)
71 count
= prtd
->dma_end
- prtd
->dma_pos
;
73 count
= prtd
->dma_period
;
75 jz4740_dma_disable(prtd
->dma
);
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
);
81 jz4740_dma_set_src_addr(prtd
->dma
, prtd
->fifo_addr
);
82 jz4740_dma_set_dst_addr(prtd
->dma
, prtd
->dma_pos
);
85 jz4740_dma_set_transfer_count(prtd
->dma
, count
);
87 prtd
->dma_pos
+= count
;
89 jz4740_dma_enable(prtd
->dma
);
92 static void jz4740_pcm_dma_transfer_done(struct jz4740_dma_chan
*dma
, int err
,
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
;
99 snd_pcm_period_elapsed(substream
);
101 jz4740_pcm_start_transfer(prtd
, substream
);
104 static int jz4740_pcm_hw_params(struct snd_pcm_substream
*substream
,
105 struct snd_pcm_hw_params
*params
)
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
;
112 config
= snd_soc_dai_get_dma_data(rtd
->cpu_dai
, substream
);
118 if (substream
->stream
== SNDRV_PCM_STREAM_CAPTURE
)
119 prtd
->dma
= jz4740_dma_request(substream
, "PCM Capture");
121 prtd
->dma
= jz4740_dma_request(substream
, "PCM Playback");
127 jz4740_dma_configure(prtd
->dma
, &config
->dma_config
);
128 prtd
->fifo_addr
= config
->fifo_addr
;
130 jz4740_dma_set_complete_cb(prtd
->dma
, jz4740_pcm_dma_transfer_done
);
132 snd_pcm_set_runtime_buffer(substream
, &substream
->dma_buffer
);
133 runtime
->dma_bytes
= params_buffer_bytes(params
);
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
;
143 static int jz4740_pcm_hw_free(struct snd_pcm_substream
*substream
)
145 struct jz4740_runtime_data
*prtd
= substream
->runtime
->private_data
;
147 snd_pcm_set_runtime_buffer(substream
, NULL
);
149 jz4740_dma_free(prtd
->dma
);
156 static int jz4740_pcm_prepare(struct snd_pcm_substream
*substream
)
158 struct jz4740_runtime_data
*prtd
= substream
->runtime
->private_data
;
163 prtd
->dma_pos
= prtd
->dma_start
;
168 static int jz4740_pcm_trigger(struct snd_pcm_substream
*substream
, int cmd
)
170 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
171 struct jz4740_runtime_data
*prtd
= runtime
->private_data
;
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
);
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
);
191 static snd_pcm_uframes_t
jz4740_pcm_pointer(struct snd_pcm_substream
*substream
)
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
;
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
);
206 offset
= bytes_to_frames(runtime
, byte_offset
);
207 if (offset
>= runtime
->buffer_size
)
213 static int jz4740_pcm_open(struct snd_pcm_substream
*substream
)
215 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
216 struct jz4740_runtime_data
*prtd
;
218 prtd
= kzalloc(sizeof(*prtd
), GFP_KERNEL
);
222 snd_soc_set_runtime_hwparams(substream
, &jz4740_pcm_hardware
);
224 runtime
->private_data
= prtd
;
229 static int jz4740_pcm_close(struct snd_pcm_substream
*substream
)
231 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
232 struct jz4740_runtime_data
*prtd
= runtime
->private_data
;
239 static int jz4740_pcm_mmap(struct snd_pcm_substream
*substream
,
240 struct vm_area_struct
*vma
)
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
);
247 static 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
,
259 static int jz4740_pcm_preallocate_dma_buffer(struct snd_pcm
*pcm
, int stream
)
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
;
265 buf
->dev
.type
= SNDRV_DMA_TYPE_DEV
;
266 buf
->dev
.dev
= pcm
->card
->dev
;
267 buf
->private_data
= NULL
;
269 buf
->area
= dma_alloc_noncoherent(pcm
->card
->dev
, size
,
270 &buf
->addr
, GFP_KERNEL
);
279 static void jz4740_pcm_free(struct snd_pcm
*pcm
)
281 struct snd_pcm_substream
*substream
;
282 struct snd_dma_buffer
*buf
;
285 for (stream
= 0; stream
< SNDRV_PCM_STREAM_LAST
; ++stream
) {
286 substream
= pcm
->streams
[stream
].substream
;
290 buf
= &substream
->dma_buffer
;
294 dma_free_noncoherent(pcm
->card
->dev
, buf
->bytes
, buf
->area
,
300 static u64 jz4740_pcm_dmamask
= DMA_BIT_MASK(32);
302 static int jz4740_pcm_new(struct snd_soc_pcm_runtime
*rtd
)
304 struct snd_card
*card
= rtd
->card
->snd_card
;
305 struct snd_pcm
*pcm
= rtd
->pcm
;
308 if (!card
->dev
->dma_mask
)
309 card
->dev
->dma_mask
= &jz4740_pcm_dmamask
;
311 if (!card
->dev
->coherent_dma_mask
)
312 card
->dev
->coherent_dma_mask
= DMA_BIT_MASK(32);
314 if (pcm
->streams
[SNDRV_PCM_STREAM_PLAYBACK
].substream
) {
315 ret
= jz4740_pcm_preallocate_dma_buffer(pcm
,
316 SNDRV_PCM_STREAM_PLAYBACK
);
321 if (pcm
->streams
[SNDRV_PCM_STREAM_CAPTURE
].substream
) {
322 ret
= jz4740_pcm_preallocate_dma_buffer(pcm
,
323 SNDRV_PCM_STREAM_CAPTURE
);
332 static 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
,
338 static int jz4740_pcm_probe(struct platform_device
*pdev
)
340 return snd_soc_register_platform(&pdev
->dev
, &jz4740_soc_platform
);
343 static int jz4740_pcm_remove(struct platform_device
*pdev
)
345 snd_soc_unregister_platform(&pdev
->dev
);
349 static struct platform_driver jz4740_pcm_driver
= {
350 .probe
= jz4740_pcm_probe
,
351 .remove
= jz4740_pcm_remove
,
353 .name
= "jz4740-pcm-audio",
354 .owner
= THIS_MODULE
,
358 module_platform_driver(jz4740_pcm_driver
);
360 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
361 MODULE_DESCRIPTION("Ingenic SoC JZ4740 PCM driver");
362 MODULE_LICENSE("GPL");