blob: f974e61fa68c2196a97ba6e8692177de3cd00516 [file] [log] [blame]
Sascha Hauer83802222009-11-25 16:41:04 +01001/*
2 * imx-pcm-dma-mx2.c -- ALSA Soc Audio Layer
3 *
4 * Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>
5 *
6 * This code is based on code copyrighted by Freescale,
7 * Liam Girdwood, Javier Martin and probably others.
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version.
13 */
14#include <linux/clk.h>
15#include <linux/delay.h>
16#include <linux/device.h>
17#include <linux/dma-mapping.h>
18#include <linux/init.h>
19#include <linux/interrupt.h>
20#include <linux/module.h>
21#include <linux/platform_device.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090022#include <linux/slab.h>
Sascha Hauerbf974a02010-11-04 17:05:43 +010023#include <linux/dmaengine.h>
Sascha Hauer83802222009-11-25 16:41:04 +010024
25#include <sound/core.h>
26#include <sound/initval.h>
27#include <sound/pcm.h>
28#include <sound/pcm_params.h>
29#include <sound/soc.h>
30
Sascha Hauerbf974a02010-11-04 17:05:43 +010031#include <mach/dma.h>
Sascha Hauer83802222009-11-25 16:41:04 +010032
33#include "imx-ssi.h"
34
35struct imx_pcm_runtime_data {
Sascha Hauerbf974a02010-11-04 17:05:43 +010036 int period_bytes;
Sascha Hauer83802222009-11-25 16:41:04 +010037 int periods;
Sascha Hauer83802222009-11-25 16:41:04 +010038 unsigned long offset;
Sascha Hauerbf974a02010-11-04 17:05:43 +010039 struct dma_async_tx_descriptor *desc;
40 struct dma_chan *dma_chan;
41 struct imx_dma_data dma_data;
Sascha Hauer83802222009-11-25 16:41:04 +010042};
43
Sascha Hauerbf974a02010-11-04 17:05:43 +010044static void audio_dma_irq(void *data)
Sascha Hauer83802222009-11-25 16:41:04 +010045{
Sascha Hauerbf974a02010-11-04 17:05:43 +010046 struct snd_pcm_substream *substream = (struct snd_pcm_substream *)data;
Sascha Hauer83802222009-11-25 16:41:04 +010047 struct snd_pcm_runtime *runtime = substream->runtime;
48 struct imx_pcm_runtime_data *iprtd = runtime->private_data;
49
Sascha Hauerbf974a02010-11-04 17:05:43 +010050 iprtd->offset += iprtd->period_bytes;
51 iprtd->offset %= iprtd->period_bytes * iprtd->periods;
Sascha Hauer83802222009-11-25 16:41:04 +010052
Sascha Hauerbf974a02010-11-04 17:05:43 +010053 snd_pcm_period_elapsed(substream);
Sascha Hauer83802222009-11-25 16:41:04 +010054}
55
Sascha Hauerbf974a02010-11-04 17:05:43 +010056static bool filter(struct dma_chan *chan, void *param)
Sascha Hauer83802222009-11-25 16:41:04 +010057{
Sascha Hauerbf974a02010-11-04 17:05:43 +010058 struct imx_pcm_runtime_data *iprtd = param;
59
60 if (!imx_dma_is_general_purpose(chan))
61 return false;
62
63 chan->private = &iprtd->dma_data;
64
65 return true;
Sascha Hauer83802222009-11-25 16:41:04 +010066}
67
Lars-Peter Clausen4564d102012-02-22 10:49:06 +010068static int imx_ssi_dma_alloc(struct snd_pcm_substream *substream)
Sascha Hauer83802222009-11-25 16:41:04 +010069{
70 struct snd_soc_pcm_runtime *rtd = substream->private_data;
Daniel Mack5f712b22010-03-22 10:11:15 +010071 struct imx_pcm_dma_params *dma_params;
Sascha Hauer83802222009-11-25 16:41:04 +010072 struct snd_pcm_runtime *runtime = substream->runtime;
73 struct imx_pcm_runtime_data *iprtd = runtime->private_data;
Sascha Hauerbf974a02010-11-04 17:05:43 +010074 dma_cap_mask_t mask;
Sascha Hauer83802222009-11-25 16:41:04 +010075
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +000076 dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
Daniel Mack5f712b22010-03-22 10:11:15 +010077
Sascha Hauerbf974a02010-11-04 17:05:43 +010078 iprtd->dma_data.peripheral_type = IMX_DMATYPE_SSI;
79 iprtd->dma_data.priority = DMA_PRIO_HIGH;
80 iprtd->dma_data.dma_request = dma_params->dma;
81
82 /* Try to grab a DMA channel */
Lars-Peter Clausen4564d102012-02-22 10:49:06 +010083 dma_cap_zero(mask);
84 dma_cap_set(DMA_SLAVE, mask);
85 iprtd->dma_chan = dma_request_channel(mask, filter, iprtd);
86 if (!iprtd->dma_chan)
87 return -EINVAL;
88
89 return 0;
90}
91
92static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream,
93 struct snd_pcm_hw_params *params)
94{
95 struct snd_soc_pcm_runtime *rtd = substream->private_data;
96 struct snd_pcm_runtime *runtime = substream->runtime;
97 struct imx_pcm_runtime_data *iprtd = runtime->private_data;
98 struct dma_chan *chan = iprtd->dma_chan;
99 struct imx_pcm_dma_params *dma_params;
100 struct dma_slave_config slave_config;
101 enum dma_slave_buswidth buswidth;
102 unsigned long dma_addr;
103 int ret;
104
105 dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
Sascha Hauerbf974a02010-11-04 17:05:43 +0100106
107 switch (params_format(params)) {
108 case SNDRV_PCM_FORMAT_S16_LE:
109 buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
110 break;
111 case SNDRV_PCM_FORMAT_S20_3LE:
112 case SNDRV_PCM_FORMAT_S24_LE:
113 buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
114 break;
115 default:
116 return 0;
Sascha Hauer83802222009-11-25 16:41:04 +0100117 }
118
Sascha Hauerbf974a02010-11-04 17:05:43 +0100119 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
Vinod Koul35e16582011-10-14 10:49:30 +0530120 slave_config.direction = DMA_MEM_TO_DEV;
Sascha Hauerbf974a02010-11-04 17:05:43 +0100121 slave_config.dst_addr = dma_params->dma_addr;
122 slave_config.dst_addr_width = buswidth;
Sascha Hauer6584cb82011-07-06 11:18:33 +0200123 slave_config.dst_maxburst = dma_params->burstsize;
Sascha Hauerbf974a02010-11-04 17:05:43 +0100124 } else {
Vinod Koul35e16582011-10-14 10:49:30 +0530125 slave_config.direction = DMA_DEV_TO_MEM;
Sascha Hauerbf974a02010-11-04 17:05:43 +0100126 slave_config.src_addr = dma_params->dma_addr;
127 slave_config.src_addr_width = buswidth;
Sascha Hauer6584cb82011-07-06 11:18:33 +0200128 slave_config.src_maxburst = dma_params->burstsize;
Sascha Hauerbf974a02010-11-04 17:05:43 +0100129 }
130
Lars-Peter Clausen4564d102012-02-22 10:49:06 +0100131 ret = dmaengine_slave_config(chan, &slave_config);
Sascha Hauer83802222009-11-25 16:41:04 +0100132 if (ret)
Sascha Hauerbf974a02010-11-04 17:05:43 +0100133 return ret;
Sascha Hauer83802222009-11-25 16:41:04 +0100134
Sascha Hauer83802222009-11-25 16:41:04 +0100135
Sascha Hauer83802222009-11-25 16:41:04 +0100136 iprtd->periods = params_periods(params);
Sascha Hauerbf974a02010-11-04 17:05:43 +0100137 iprtd->period_bytes = params_period_bytes(params);
Sascha Hauer83802222009-11-25 16:41:04 +0100138 iprtd->offset = 0;
Sascha Hauer83802222009-11-25 16:41:04 +0100139
140 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
141
Sascha Hauer83802222009-11-25 16:41:04 +0100142 dma_addr = runtime->dma_addr;
143
Sascha Hauerbf974a02010-11-04 17:05:43 +0100144 iprtd->desc = chan->device->device_prep_dma_cyclic(chan, dma_addr,
145 iprtd->period_bytes * iprtd->periods,
146 iprtd->period_bytes,
147 substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
Vinod Koul35e16582011-10-14 10:49:30 +0530148 DMA_MEM_TO_DEV : DMA_DEV_TO_MEM);
Sascha Hauerbf974a02010-11-04 17:05:43 +0100149 if (!iprtd->desc) {
150 dev_err(&chan->dev->device, "cannot prepare slave dma\n");
151 return -EINVAL;
Sascha Hauer83802222009-11-25 16:41:04 +0100152 }
153
Sascha Hauerbf974a02010-11-04 17:05:43 +0100154 iprtd->desc->callback = audio_dma_irq;
155 iprtd->desc->callback_param = substream;
156
Sascha Hauer83802222009-11-25 16:41:04 +0100157 return 0;
158}
159
Sascha Hauer83802222009-11-25 16:41:04 +0100160static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
161{
162 struct snd_pcm_runtime *runtime = substream->runtime;
163 struct imx_pcm_runtime_data *iprtd = runtime->private_data;
164
165 switch (cmd) {
166 case SNDRV_PCM_TRIGGER_START:
167 case SNDRV_PCM_TRIGGER_RESUME:
168 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
Sascha Hauerbf974a02010-11-04 17:05:43 +0100169 dmaengine_submit(iprtd->desc);
Sascha Hauer83802222009-11-25 16:41:04 +0100170
171 break;
172
173 case SNDRV_PCM_TRIGGER_STOP:
174 case SNDRV_PCM_TRIGGER_SUSPEND:
175 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
Sascha Hauerbf974a02010-11-04 17:05:43 +0100176 dmaengine_terminate_all(iprtd->dma_chan);
Sascha Hauer83802222009-11-25 16:41:04 +0100177
178 break;
179 default:
180 return -EINVAL;
181 }
182
183 return 0;
184}
185
186static snd_pcm_uframes_t snd_imx_pcm_pointer(struct snd_pcm_substream *substream)
187{
188 struct snd_pcm_runtime *runtime = substream->runtime;
189 struct imx_pcm_runtime_data *iprtd = runtime->private_data;
190
Sascha Hauerbf974a02010-11-04 17:05:43 +0100191 pr_debug("%s: %ld %ld\n", __func__, iprtd->offset,
192 bytes_to_frames(substream->runtime, iprtd->offset));
193
Sascha Hauer83802222009-11-25 16:41:04 +0100194 return bytes_to_frames(substream->runtime, iprtd->offset);
195}
196
197static struct snd_pcm_hardware snd_imx_hardware = {
198 .info = SNDRV_PCM_INFO_INTERLEAVED |
199 SNDRV_PCM_INFO_BLOCK_TRANSFER |
200 SNDRV_PCM_INFO_MMAP |
201 SNDRV_PCM_INFO_MMAP_VALID |
202 SNDRV_PCM_INFO_PAUSE |
203 SNDRV_PCM_INFO_RESUME,
204 .formats = SNDRV_PCM_FMTBIT_S16_LE,
205 .rate_min = 8000,
206 .channels_min = 2,
207 .channels_max = 2,
208 .buffer_bytes_max = IMX_SSI_DMABUF_SIZE,
209 .period_bytes_min = 128,
Sascha Hauerbf974a02010-11-04 17:05:43 +0100210 .period_bytes_max = 65535, /* Limited by SDMA engine */
Sascha Hauer83802222009-11-25 16:41:04 +0100211 .periods_min = 2,
212 .periods_max = 255,
213 .fifo_size = 0,
214};
215
216static int snd_imx_open(struct snd_pcm_substream *substream)
217{
218 struct snd_pcm_runtime *runtime = substream->runtime;
219 struct imx_pcm_runtime_data *iprtd;
220 int ret;
221
222 iprtd = kzalloc(sizeof(*iprtd), GFP_KERNEL);
Kulikov Vasiliy51b6dfb2010-07-16 20:16:34 +0400223 if (iprtd == NULL)
224 return -ENOMEM;
Sascha Hauer83802222009-11-25 16:41:04 +0100225 runtime->private_data = iprtd;
226
227 ret = snd_pcm_hw_constraint_integer(substream->runtime,
228 SNDRV_PCM_HW_PARAM_PERIODS);
Kulikov Vasiliy51b6dfb2010-07-16 20:16:34 +0400229 if (ret < 0) {
230 kfree(iprtd);
Sascha Hauer83802222009-11-25 16:41:04 +0100231 return ret;
Kulikov Vasiliy51b6dfb2010-07-16 20:16:34 +0400232 }
Sascha Hauer83802222009-11-25 16:41:04 +0100233
Lars-Peter Clausen4564d102012-02-22 10:49:06 +0100234 ret = imx_ssi_dma_alloc(substream);
235 if (ret < 0) {
236 kfree(iprtd);
237 return ret;
238 }
239
Sascha Hauer83802222009-11-25 16:41:04 +0100240 snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);
Sascha Hauerbf974a02010-11-04 17:05:43 +0100241
242 return 0;
243}
244
245static int snd_imx_close(struct snd_pcm_substream *substream)
246{
247 struct snd_pcm_runtime *runtime = substream->runtime;
248 struct imx_pcm_runtime_data *iprtd = runtime->private_data;
249
Lars-Peter Clausen4564d102012-02-22 10:49:06 +0100250 dma_release_channel(iprtd->dma_chan);
Sascha Hauerbf974a02010-11-04 17:05:43 +0100251 kfree(iprtd);
252
Sascha Hauer83802222009-11-25 16:41:04 +0100253 return 0;
254}
255
256static struct snd_pcm_ops imx_pcm_ops = {
257 .open = snd_imx_open,
Sascha Hauerbf974a02010-11-04 17:05:43 +0100258 .close = snd_imx_close,
Sascha Hauer83802222009-11-25 16:41:04 +0100259 .ioctl = snd_pcm_lib_ioctl,
260 .hw_params = snd_imx_pcm_hw_params,
Sascha Hauer83802222009-11-25 16:41:04 +0100261 .trigger = snd_imx_pcm_trigger,
262 .pointer = snd_imx_pcm_pointer,
263 .mmap = snd_imx_pcm_mmap,
264};
265
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000266static struct snd_soc_platform_driver imx_soc_platform_mx2 = {
267 .ops = &imx_pcm_ops,
Sascha Hauer83802222009-11-25 16:41:04 +0100268 .pcm_new = imx_pcm_new,
269 .pcm_free = imx_pcm_free,
270};
271
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000272static int __devinit imx_soc_platform_probe(struct platform_device *pdev)
Sascha Hauer83802222009-11-25 16:41:04 +0100273{
Wolfram Sang2c4cf172011-03-25 16:51:44 +0100274 struct imx_ssi *ssi = platform_get_drvdata(pdev);
275
276 ssi->dma_params_tx.burstsize = 6;
277 ssi->dma_params_rx.burstsize = 4;
278
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000279 return snd_soc_register_platform(&pdev->dev, &imx_soc_platform_mx2);
Sascha Hauer83802222009-11-25 16:41:04 +0100280}
281
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000282static int __devexit imx_soc_platform_remove(struct platform_device *pdev)
283{
284 snd_soc_unregister_platform(&pdev->dev);
285 return 0;
286}
287
288static struct platform_driver imx_pcm_driver = {
289 .driver = {
290 .name = "imx-pcm-audio",
291 .owner = THIS_MODULE,
292 },
Liam Girdwoodf0fba2a2010-03-17 20:15:21 +0000293 .probe = imx_soc_platform_probe,
294 .remove = __devexit_p(imx_soc_platform_remove),
295};
296
Axel Lin7a24b2b2011-11-24 15:03:50 +0800297module_platform_driver(imx_pcm_driver);
Arnaud Patard (Rtp)96dcabb2011-06-22 22:21:49 +0200298MODULE_LICENSE("GPL");
299MODULE_ALIAS("platform:imx-pcm-audio");