2 * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
4 * Based on sound/soc/imx/imx-pcm-dma-mx2.c
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include <linux/clk.h>
22 #include <linux/delay.h>
23 #include <linux/device.h>
24 #include <linux/dma-mapping.h>
25 #include <linux/init.h>
26 #include <linux/interrupt.h>
27 #include <linux/module.h>
28 #include <linux/platform_device.h>
29 #include <linux/slab.h>
30 #include <linux/dmaengine.h>
31 #include <linux/fsl/mxs-dma.h>
33 #include <sound/core.h>
34 #include <sound/initval.h>
35 #include <sound/pcm.h>
36 #include <sound/pcm_params.h>
37 #include <sound/soc.h>
38 #include <sound/dmaengine_pcm.h>
42 struct mxs_pcm_dma_data
{
43 struct mxs_dma_data dma_data
;
44 struct mxs_pcm_dma_params
*dma_params
;
47 static struct snd_pcm_hardware snd_mxs_hardware
= {
48 .info
= SNDRV_PCM_INFO_MMAP
|
49 SNDRV_PCM_INFO_MMAP_VALID
|
50 SNDRV_PCM_INFO_PAUSE
|
51 SNDRV_PCM_INFO_RESUME
|
52 SNDRV_PCM_INFO_INTERLEAVED
,
53 .formats
= SNDRV_PCM_FMTBIT_S16_LE
|
54 SNDRV_PCM_FMTBIT_S20_3LE
|
55 SNDRV_PCM_FMTBIT_S24_LE
,
58 .period_bytes_min
= 32,
59 .period_bytes_max
= 8192,
62 .buffer_bytes_max
= 64 * 1024,
67 static bool filter(struct dma_chan
*chan
, void *param
)
69 struct mxs_pcm_dma_data
*pcm_dma_data
= param
;
70 struct mxs_pcm_dma_params
*dma_params
= pcm_dma_data
->dma_params
;
72 if (!mxs_dma_is_apbx(chan
))
75 if (chan
->chan_id
!= dma_params
->chan_num
)
78 chan
->private = &pcm_dma_data
->dma_data
;
83 static int snd_mxs_pcm_hw_params(struct snd_pcm_substream
*substream
,
84 struct snd_pcm_hw_params
*params
)
86 snd_pcm_set_runtime_buffer(substream
, &substream
->dma_buffer
);
91 static int snd_mxs_open(struct snd_pcm_substream
*substream
)
93 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
94 struct mxs_pcm_dma_data
*pcm_dma_data
;
97 pcm_dma_data
= kzalloc(sizeof(*pcm_dma_data
), GFP_KERNEL
);
98 if (pcm_dma_data
== NULL
)
101 pcm_dma_data
->dma_params
= snd_soc_dai_get_dma_data(rtd
->cpu_dai
, substream
);
102 pcm_dma_data
->dma_data
.chan_irq
= pcm_dma_data
->dma_params
->chan_irq
;
104 ret
= snd_dmaengine_pcm_open(substream
, filter
, pcm_dma_data
);
110 snd_soc_set_runtime_hwparams(substream
, &snd_mxs_hardware
);
112 snd_dmaengine_pcm_set_data(substream
, pcm_dma_data
);
117 static int snd_mxs_close(struct snd_pcm_substream
*substream
)
119 struct mxs_pcm_dma_data
*pcm_dma_data
= snd_dmaengine_pcm_get_data(substream
);
121 snd_dmaengine_pcm_close(substream
);
127 static int snd_mxs_pcm_mmap(struct snd_pcm_substream
*substream
,
128 struct vm_area_struct
*vma
)
130 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
132 return dma_mmap_writecombine(substream
->pcm
->card
->dev
, vma
,
138 static struct snd_pcm_ops mxs_pcm_ops
= {
139 .open
= snd_mxs_open
,
140 .close
= snd_mxs_close
,
141 .ioctl
= snd_pcm_lib_ioctl
,
142 .hw_params
= snd_mxs_pcm_hw_params
,
143 .trigger
= snd_dmaengine_pcm_trigger
,
144 .pointer
= snd_dmaengine_pcm_pointer_no_residue
,
145 .mmap
= snd_mxs_pcm_mmap
,
148 static int mxs_pcm_preallocate_dma_buffer(struct snd_pcm
*pcm
, int stream
)
150 struct snd_pcm_substream
*substream
= pcm
->streams
[stream
].substream
;
151 struct snd_dma_buffer
*buf
= &substream
->dma_buffer
;
152 size_t size
= snd_mxs_hardware
.buffer_bytes_max
;
154 buf
->dev
.type
= SNDRV_DMA_TYPE_DEV
;
155 buf
->dev
.dev
= pcm
->card
->dev
;
156 buf
->private_data
= NULL
;
157 buf
->area
= dma_alloc_writecombine(pcm
->card
->dev
, size
,
158 &buf
->addr
, GFP_KERNEL
);
166 static u64 mxs_pcm_dmamask
= DMA_BIT_MASK(32);
167 static int mxs_pcm_new(struct snd_soc_pcm_runtime
*rtd
)
169 struct snd_card
*card
= rtd
->card
->snd_card
;
170 struct snd_pcm
*pcm
= rtd
->pcm
;
173 if (!card
->dev
->dma_mask
)
174 card
->dev
->dma_mask
= &mxs_pcm_dmamask
;
175 if (!card
->dev
->coherent_dma_mask
)
176 card
->dev
->coherent_dma_mask
= DMA_BIT_MASK(32);
178 if (pcm
->streams
[SNDRV_PCM_STREAM_PLAYBACK
].substream
) {
179 ret
= mxs_pcm_preallocate_dma_buffer(pcm
,
180 SNDRV_PCM_STREAM_PLAYBACK
);
185 if (pcm
->streams
[SNDRV_PCM_STREAM_CAPTURE
].substream
) {
186 ret
= mxs_pcm_preallocate_dma_buffer(pcm
,
187 SNDRV_PCM_STREAM_CAPTURE
);
196 static void mxs_pcm_free(struct snd_pcm
*pcm
)
198 struct snd_pcm_substream
*substream
;
199 struct snd_dma_buffer
*buf
;
202 for (stream
= 0; stream
< 2; stream
++) {
203 substream
= pcm
->streams
[stream
].substream
;
207 buf
= &substream
->dma_buffer
;
211 dma_free_writecombine(pcm
->card
->dev
, buf
->bytes
,
212 buf
->area
, buf
->addr
);
217 static struct snd_soc_platform_driver mxs_soc_platform
= {
219 .pcm_new
= mxs_pcm_new
,
220 .pcm_free
= mxs_pcm_free
,
223 int mxs_pcm_platform_register(struct device
*dev
)
225 return snd_soc_register_platform(dev
, &mxs_soc_platform
);
227 EXPORT_SYMBOL_GPL(mxs_pcm_platform_register
);
229 void mxs_pcm_platform_unregister(struct device
*dev
)
231 snd_soc_unregister_platform(dev
);
233 EXPORT_SYMBOL_GPL(mxs_pcm_platform_unregister
);
235 MODULE_LICENSE("GPL");