2 * Copyright (C) 2013, Analog Devices Inc.
3 * Author: Lars-Peter Clausen <lars@metafoo.de>
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
10 * You should have received a copy of the GNU General Public License along
11 * with this program; if not, write to the Free Software Foundation, Inc.,
12 * 675 Mass Ave, Cambridge, MA 02139, USA.
15 #include <linux/module.h>
16 #include <linux/init.h>
17 #include <linux/dmaengine.h>
18 #include <linux/slab.h>
19 #include <sound/pcm.h>
20 #include <sound/pcm_params.h>
21 #include <sound/soc.h>
22 #include <linux/dma-mapping.h>
25 #include <sound/dmaengine_pcm.h>
27 struct dmaengine_pcm
{
28 struct dma_chan
*chan
[SNDRV_PCM_STREAM_LAST
+ 1];
29 const struct snd_dmaengine_pcm_config
*config
;
30 struct snd_soc_platform platform
;
34 static struct dmaengine_pcm
*soc_platform_to_pcm(struct snd_soc_platform
*p
)
36 return container_of(p
, struct dmaengine_pcm
, platform
);
39 static struct device
*dmaengine_dma_dev(struct dmaengine_pcm
*pcm
,
40 struct snd_pcm_substream
*substream
)
42 if (!pcm
->chan
[substream
->stream
])
45 return pcm
->chan
[substream
->stream
]->device
->dev
;
49 * snd_dmaengine_pcm_prepare_slave_config() - Generic prepare_slave_config callback
50 * @substream: PCM substream
52 * @slave_config: DMA slave config to prepare
54 * This function can be used as a generic prepare_slave_config callback for
55 * platforms which make use of the snd_dmaengine_dai_dma_data struct for their
56 * DAI DMA data. Internally the function will first call
57 * snd_hwparams_to_dma_slave_config to fill in the slave config based on the
58 * hw_params, followed by snd_dmaengine_set_config_from_dai_data to fill in the
59 * remaining fields based on the DAI DMA data.
61 int snd_dmaengine_pcm_prepare_slave_config(struct snd_pcm_substream
*substream
,
62 struct snd_pcm_hw_params
*params
, struct dma_slave_config
*slave_config
)
64 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
65 struct snd_dmaengine_dai_dma_data
*dma_data
;
68 dma_data
= snd_soc_dai_get_dma_data(rtd
->cpu_dai
, substream
);
70 ret
= snd_hwparams_to_dma_slave_config(substream
, params
, slave_config
);
74 snd_dmaengine_pcm_set_config_from_dai_data(substream
, dma_data
,
79 EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_prepare_slave_config
);
81 static int dmaengine_pcm_hw_params(struct snd_pcm_substream
*substream
,
82 struct snd_pcm_hw_params
*params
)
84 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
85 struct dmaengine_pcm
*pcm
= soc_platform_to_pcm(rtd
->platform
);
86 struct dma_chan
*chan
= snd_dmaengine_pcm_get_chan(substream
);
87 int (*prepare_slave_config
)(struct snd_pcm_substream
*substream
,
88 struct snd_pcm_hw_params
*params
,
89 struct dma_slave_config
*slave_config
);
90 struct dma_slave_config slave_config
;
93 memset(&slave_config
, 0, sizeof(slave_config
));
96 prepare_slave_config
= snd_dmaengine_pcm_prepare_slave_config
;
98 prepare_slave_config
= pcm
->config
->prepare_slave_config
;
100 if (prepare_slave_config
) {
101 ret
= prepare_slave_config(substream
, params
, &slave_config
);
105 ret
= dmaengine_slave_config(chan
, &slave_config
);
110 return snd_pcm_lib_malloc_pages(substream
, params_buffer_bytes(params
));
113 static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream
*substream
)
115 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
116 struct dmaengine_pcm
*pcm
= soc_platform_to_pcm(rtd
->platform
);
117 struct device
*dma_dev
= dmaengine_dma_dev(pcm
, substream
);
118 struct dma_chan
*chan
= pcm
->chan
[substream
->stream
];
119 struct snd_dmaengine_dai_dma_data
*dma_data
;
120 struct dma_slave_caps dma_caps
;
121 struct snd_pcm_hardware hw
;
124 if (pcm
->config
&& pcm
->config
->pcm_hardware
)
125 return snd_soc_set_runtime_hwparams(substream
,
126 pcm
->config
->pcm_hardware
);
128 dma_data
= snd_soc_dai_get_dma_data(rtd
->cpu_dai
, substream
);
130 memset(&hw
, 0, sizeof(hw
));
131 hw
.info
= SNDRV_PCM_INFO_MMAP
| SNDRV_PCM_INFO_MMAP_VALID
|
132 SNDRV_PCM_INFO_INTERLEAVED
;
134 hw
.periods_max
= UINT_MAX
;
135 hw
.period_bytes_min
= 256;
136 hw
.period_bytes_max
= dma_get_max_seg_size(dma_dev
);
137 hw
.buffer_bytes_max
= SIZE_MAX
;
138 hw
.fifo_size
= dma_data
->fifo_size
;
140 if (pcm
->flags
& SND_DMAENGINE_PCM_FLAG_NO_RESIDUE
)
141 hw
.info
|= SNDRV_PCM_INFO_BATCH
;
143 ret
= dma_get_slave_caps(chan
, &dma_caps
);
145 if (dma_caps
.cmd_pause
)
146 hw
.info
|= SNDRV_PCM_INFO_PAUSE
| SNDRV_PCM_INFO_RESUME
;
149 return snd_soc_set_runtime_hwparams(substream
, &hw
);
152 static int dmaengine_pcm_open(struct snd_pcm_substream
*substream
)
154 struct snd_soc_pcm_runtime
*rtd
= substream
->private_data
;
155 struct dmaengine_pcm
*pcm
= soc_platform_to_pcm(rtd
->platform
);
156 struct dma_chan
*chan
= pcm
->chan
[substream
->stream
];
159 ret
= dmaengine_pcm_set_runtime_hwparams(substream
);
163 return snd_dmaengine_pcm_open(substream
, chan
);
166 static void dmaengine_pcm_free(struct snd_pcm
*pcm
)
168 snd_pcm_lib_preallocate_free_for_all(pcm
);
171 static struct dma_chan
*dmaengine_pcm_compat_request_channel(
172 struct snd_soc_pcm_runtime
*rtd
,
173 struct snd_pcm_substream
*substream
)
175 struct dmaengine_pcm
*pcm
= soc_platform_to_pcm(rtd
->platform
);
176 struct snd_dmaengine_dai_dma_data
*dma_data
;
178 dma_data
= snd_soc_dai_get_dma_data(rtd
->cpu_dai
, substream
);
180 if ((pcm
->flags
& SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX
) && pcm
->chan
[0])
183 if (pcm
->config
->compat_request_channel
)
184 return pcm
->config
->compat_request_channel(rtd
, substream
);
186 return snd_dmaengine_pcm_request_channel(pcm
->config
->compat_filter_fn
,
187 dma_data
->filter_data
);
190 static int dmaengine_pcm_new(struct snd_soc_pcm_runtime
*rtd
)
192 struct dmaengine_pcm
*pcm
= soc_platform_to_pcm(rtd
->platform
);
193 const struct snd_dmaengine_pcm_config
*config
= pcm
->config
;
194 struct device
*dev
= rtd
->platform
->dev
;
195 struct snd_dmaengine_dai_dma_data
*dma_data
;
196 struct snd_pcm_substream
*substream
;
197 size_t prealloc_buffer_size
;
198 size_t max_buffer_size
;
202 if (config
&& config
->prealloc_buffer_size
) {
203 prealloc_buffer_size
= config
->prealloc_buffer_size
;
204 max_buffer_size
= config
->pcm_hardware
->buffer_bytes_max
;
206 prealloc_buffer_size
= 512 * 1024;
207 max_buffer_size
= SIZE_MAX
;
211 for (i
= SNDRV_PCM_STREAM_PLAYBACK
; i
<= SNDRV_PCM_STREAM_CAPTURE
; i
++) {
212 substream
= rtd
->pcm
->streams
[i
].substream
;
216 dma_data
= snd_soc_dai_get_dma_data(rtd
->cpu_dai
, substream
);
219 (pcm
->flags
& SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME
))
220 pcm
->chan
[i
] = dma_request_slave_channel(dev
,
221 dma_data
->chan_name
);
223 if (!pcm
->chan
[i
] && (pcm
->flags
& SND_DMAENGINE_PCM_FLAG_COMPAT
)) {
224 pcm
->chan
[i
] = dmaengine_pcm_compat_request_channel(rtd
,
229 dev_err(rtd
->platform
->dev
,
230 "Missing dma channel for stream: %d\n", i
);
235 ret
= snd_pcm_lib_preallocate_pages(substream
,
236 SNDRV_DMA_TYPE_DEV_IRAM
,
237 dmaengine_dma_dev(pcm
, substream
),
238 prealloc_buffer_size
,
247 dmaengine_pcm_free(rtd
->pcm
);
251 static const struct snd_pcm_ops dmaengine_pcm_ops
= {
252 .open
= dmaengine_pcm_open
,
253 .close
= snd_dmaengine_pcm_close
,
254 .ioctl
= snd_pcm_lib_ioctl
,
255 .hw_params
= dmaengine_pcm_hw_params
,
256 .hw_free
= snd_pcm_lib_free_pages
,
257 .trigger
= snd_dmaengine_pcm_trigger
,
258 .pointer
= snd_dmaengine_pcm_pointer
,
261 static const struct snd_soc_platform_driver dmaengine_pcm_platform
= {
262 .ops
= &dmaengine_pcm_ops
,
263 .pcm_new
= dmaengine_pcm_new
,
264 .pcm_free
= dmaengine_pcm_free
,
265 .probe_order
= SND_SOC_COMP_ORDER_LATE
,
268 static const struct snd_pcm_ops dmaengine_no_residue_pcm_ops
= {
269 .open
= dmaengine_pcm_open
,
270 .close
= snd_dmaengine_pcm_close
,
271 .ioctl
= snd_pcm_lib_ioctl
,
272 .hw_params
= dmaengine_pcm_hw_params
,
273 .hw_free
= snd_pcm_lib_free_pages
,
274 .trigger
= snd_dmaengine_pcm_trigger
,
275 .pointer
= snd_dmaengine_pcm_pointer_no_residue
,
278 static const struct snd_soc_platform_driver dmaengine_no_residue_pcm_platform
= {
279 .ops
= &dmaengine_no_residue_pcm_ops
,
280 .pcm_new
= dmaengine_pcm_new
,
281 .pcm_free
= dmaengine_pcm_free
,
282 .probe_order
= SND_SOC_COMP_ORDER_LATE
,
285 static const char * const dmaengine_pcm_dma_channel_names
[] = {
286 [SNDRV_PCM_STREAM_PLAYBACK
] = "tx",
287 [SNDRV_PCM_STREAM_CAPTURE
] = "rx",
290 static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm
*pcm
,
291 struct device
*dev
, const struct snd_dmaengine_pcm_config
*config
)
295 struct dma_chan
*chan
;
297 if ((pcm
->flags
& (SND_DMAENGINE_PCM_FLAG_NO_DT
|
298 SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME
)) ||
302 if (config
->dma_dev
) {
304 * If this warning is seen, it probably means that your Linux
305 * device structure does not match your HW device structure.
306 * It would be best to refactor the Linux device structure to
307 * correctly match the HW structure.
309 dev_warn(dev
, "DMA channels sourced from device %s",
310 dev_name(config
->dma_dev
));
311 dev
= config
->dma_dev
;
314 for (i
= SNDRV_PCM_STREAM_PLAYBACK
; i
<= SNDRV_PCM_STREAM_CAPTURE
;
316 if (pcm
->flags
& SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX
)
319 name
= dmaengine_pcm_dma_channel_names
[i
];
320 if (config
->chan_names
[i
])
321 name
= config
->chan_names
[i
];
322 chan
= dma_request_slave_channel_reason(dev
, name
);
324 if (PTR_ERR(chan
) == -EPROBE_DEFER
)
325 return -EPROBE_DEFER
;
330 if (pcm
->flags
& SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX
)
334 if (pcm
->flags
& SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX
)
335 pcm
->chan
[1] = pcm
->chan
[0];
340 static void dmaengine_pcm_release_chan(struct dmaengine_pcm
*pcm
)
344 for (i
= SNDRV_PCM_STREAM_PLAYBACK
; i
<= SNDRV_PCM_STREAM_CAPTURE
;
348 dma_release_channel(pcm
->chan
[i
]);
349 if (pcm
->flags
& SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX
)
355 * snd_dmaengine_pcm_register - Register a dmaengine based PCM device
356 * @dev: The parent device for the PCM device
357 * @config: Platform specific PCM configuration
358 * @flags: Platform specific quirks
360 int snd_dmaengine_pcm_register(struct device
*dev
,
361 const struct snd_dmaengine_pcm_config
*config
, unsigned int flags
)
363 struct dmaengine_pcm
*pcm
;
366 pcm
= kzalloc(sizeof(*pcm
), GFP_KERNEL
);
370 pcm
->config
= config
;
373 ret
= dmaengine_pcm_request_chan_of(pcm
, dev
, config
);
377 if (flags
& SND_DMAENGINE_PCM_FLAG_NO_RESIDUE
)
378 ret
= snd_soc_add_platform(dev
, &pcm
->platform
,
379 &dmaengine_no_residue_pcm_platform
);
381 ret
= snd_soc_add_platform(dev
, &pcm
->platform
,
382 &dmaengine_pcm_platform
);
389 dmaengine_pcm_release_chan(pcm
);
393 EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_register
);
396 * snd_dmaengine_pcm_unregister - Removes a dmaengine based PCM device
397 * @dev: Parent device the PCM was register with
399 * Removes a dmaengine based PCM device previously registered with
400 * snd_dmaengine_pcm_register.
402 void snd_dmaengine_pcm_unregister(struct device
*dev
)
404 struct snd_soc_platform
*platform
;
405 struct dmaengine_pcm
*pcm
;
407 platform
= snd_soc_lookup_platform(dev
);
411 pcm
= soc_platform_to_pcm(platform
);
413 snd_soc_remove_platform(platform
);
414 dmaengine_pcm_release_chan(pcm
);
417 EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_unregister
);
419 MODULE_LICENSE("GPL");