1 // SPDX-License-Identifier: GPL-2.0+
3 // Copyright (C) 2013, Analog Devices Inc.
4 // Author: Lars-Peter Clausen <lars@metafoo.de>
6 #include <linux/module.h>
7 #include <linux/init.h>
8 #include <linux/dmaengine.h>
9 #include <linux/slab.h>
10 #include <sound/pcm.h>
11 #include <sound/pcm_params.h>
12 #include <sound/soc.h>
13 #include <linux/dma-mapping.h>
16 #include <sound/dmaengine_pcm.h>
18 static unsigned int prealloc_buffer_size_kbytes
= 512;
19 module_param(prealloc_buffer_size_kbytes
, uint
, 0444);
20 MODULE_PARM_DESC(prealloc_buffer_size_kbytes
, "Preallocate DMA buffer size (KB).");
23 * The platforms dmaengine driver does not support reporting the amount of
24 * bytes that are still left to transfer.
26 #define SND_DMAENGINE_PCM_FLAG_NO_RESIDUE BIT(31)
28 static struct device
*dmaengine_dma_dev(struct dmaengine_pcm
*pcm
,
29 struct snd_pcm_substream
*substream
)
31 if (!pcm
->chan
[substream
->stream
])
34 return pcm
->chan
[substream
->stream
]->device
->dev
;
38 * snd_dmaengine_pcm_prepare_slave_config() - Generic prepare_slave_config callback
39 * @substream: PCM substream
41 * @slave_config: DMA slave config to prepare
43 * This function can be used as a generic prepare_slave_config callback for
44 * platforms which make use of the snd_dmaengine_dai_dma_data struct for their
45 * DAI DMA data. Internally the function will first call
46 * snd_hwparams_to_dma_slave_config to fill in the slave config based on the
47 * hw_params, followed by snd_dmaengine_pcm_set_config_from_dai_data to fill in
48 * the remaining fields based on the DAI DMA data.
50 int snd_dmaengine_pcm_prepare_slave_config(struct snd_pcm_substream
*substream
,
51 struct snd_pcm_hw_params
*params
, struct dma_slave_config
*slave_config
)
53 struct snd_soc_pcm_runtime
*rtd
= snd_soc_substream_to_rtd(substream
);
54 struct snd_dmaengine_dai_dma_data
*dma_data
;
57 if (rtd
->dai_link
->num_cpus
> 1) {
59 "%s doesn't support Multi CPU yet\n", __func__
);
63 dma_data
= snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd
, 0), substream
);
65 ret
= snd_hwparams_to_dma_slave_config(substream
, params
, slave_config
);
69 snd_dmaengine_pcm_set_config_from_dai_data(substream
, dma_data
,
74 EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_prepare_slave_config
);
76 static int dmaengine_pcm_hw_params(struct snd_soc_component
*component
,
77 struct snd_pcm_substream
*substream
,
78 struct snd_pcm_hw_params
*params
)
80 struct dmaengine_pcm
*pcm
= soc_component_to_pcm(component
);
81 struct dma_chan
*chan
= snd_dmaengine_pcm_get_chan(substream
);
82 struct dma_slave_config slave_config
;
85 if (!pcm
->config
->prepare_slave_config
)
88 memset(&slave_config
, 0, sizeof(slave_config
));
90 ret
= pcm
->config
->prepare_slave_config(substream
, params
, &slave_config
);
94 return dmaengine_slave_config(chan
, &slave_config
);
98 dmaengine_pcm_set_runtime_hwparams(struct snd_soc_component
*component
,
99 struct snd_pcm_substream
*substream
)
101 struct snd_soc_pcm_runtime
*rtd
= snd_soc_substream_to_rtd(substream
);
102 struct dmaengine_pcm
*pcm
= soc_component_to_pcm(component
);
103 struct device
*dma_dev
= dmaengine_dma_dev(pcm
, substream
);
104 struct dma_chan
*chan
= pcm
->chan
[substream
->stream
];
105 struct snd_dmaengine_dai_dma_data
*dma_data
;
106 struct snd_pcm_hardware hw
;
108 if (rtd
->dai_link
->num_cpus
> 1) {
110 "%s doesn't support Multi CPU yet\n", __func__
);
114 if (pcm
->config
->pcm_hardware
)
115 return snd_soc_set_runtime_hwparams(substream
,
116 pcm
->config
->pcm_hardware
);
118 dma_data
= snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd
, 0), substream
);
120 memset(&hw
, 0, sizeof(hw
));
121 hw
.info
= SNDRV_PCM_INFO_MMAP
| SNDRV_PCM_INFO_MMAP_VALID
|
122 SNDRV_PCM_INFO_INTERLEAVED
;
124 hw
.periods_max
= UINT_MAX
;
125 hw
.period_bytes_min
= dma_data
->maxburst
* DMA_SLAVE_BUSWIDTH_8_BYTES
;
126 if (!hw
.period_bytes_min
)
127 hw
.period_bytes_min
= 256;
128 hw
.period_bytes_max
= dma_get_max_seg_size(dma_dev
);
129 hw
.buffer_bytes_max
= SIZE_MAX
;
130 hw
.fifo_size
= dma_data
->fifo_size
;
132 if (pcm
->flags
& SND_DMAENGINE_PCM_FLAG_NO_RESIDUE
)
133 hw
.info
|= SNDRV_PCM_INFO_BATCH
;
136 * FIXME: Remove the return value check to align with the code
137 * before adding snd_dmaengine_pcm_refine_runtime_hwparams
140 snd_dmaengine_pcm_refine_runtime_hwparams(substream
,
145 return snd_soc_set_runtime_hwparams(substream
, &hw
);
148 static int dmaengine_pcm_open(struct snd_soc_component
*component
,
149 struct snd_pcm_substream
*substream
)
151 struct dmaengine_pcm
*pcm
= soc_component_to_pcm(component
);
152 struct dma_chan
*chan
= pcm
->chan
[substream
->stream
];
155 ret
= dmaengine_pcm_set_runtime_hwparams(component
, substream
);
159 return snd_dmaengine_pcm_open(substream
, chan
);
162 static int dmaengine_pcm_close(struct snd_soc_component
*component
,
163 struct snd_pcm_substream
*substream
)
165 return snd_dmaengine_pcm_close(substream
);
168 static int dmaengine_pcm_trigger(struct snd_soc_component
*component
,
169 struct snd_pcm_substream
*substream
, int cmd
)
171 return snd_dmaengine_pcm_trigger(substream
, cmd
);
174 static struct dma_chan
*dmaengine_pcm_compat_request_channel(
175 struct snd_soc_component
*component
,
176 struct snd_soc_pcm_runtime
*rtd
,
177 struct snd_pcm_substream
*substream
)
179 struct dmaengine_pcm
*pcm
= soc_component_to_pcm(component
);
180 struct snd_dmaengine_dai_dma_data
*dma_data
;
182 if (rtd
->dai_link
->num_cpus
> 1) {
184 "%s doesn't support Multi CPU yet\n", __func__
);
188 dma_data
= snd_soc_dai_get_dma_data(snd_soc_rtd_to_cpu(rtd
, 0), substream
);
190 if ((pcm
->flags
& SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX
) && pcm
->chan
[0])
193 if (pcm
->config
->compat_request_channel
)
194 return pcm
->config
->compat_request_channel(rtd
, substream
);
196 return snd_dmaengine_pcm_request_channel(pcm
->config
->compat_filter_fn
,
197 dma_data
->filter_data
);
200 static bool dmaengine_pcm_can_report_residue(struct device
*dev
,
201 struct dma_chan
*chan
)
203 struct dma_slave_caps dma_caps
;
206 ret
= dma_get_slave_caps(chan
, &dma_caps
);
208 dev_warn(dev
, "Failed to get DMA channel capabilities, falling back to period counting: %d\n",
213 if (dma_caps
.residue_granularity
== DMA_RESIDUE_GRANULARITY_DESCRIPTOR
)
219 static int dmaengine_pcm_new(struct snd_soc_component
*component
,
220 struct snd_soc_pcm_runtime
*rtd
)
222 struct dmaengine_pcm
*pcm
= soc_component_to_pcm(component
);
223 const struct snd_dmaengine_pcm_config
*config
= pcm
->config
;
224 struct device
*dev
= component
->dev
;
225 size_t prealloc_buffer_size
;
226 size_t max_buffer_size
;
229 if (config
->prealloc_buffer_size
)
230 prealloc_buffer_size
= config
->prealloc_buffer_size
;
232 prealloc_buffer_size
= prealloc_buffer_size_kbytes
* 1024;
234 if (config
->pcm_hardware
&& config
->pcm_hardware
->buffer_bytes_max
)
235 max_buffer_size
= config
->pcm_hardware
->buffer_bytes_max
;
237 max_buffer_size
= SIZE_MAX
;
239 for_each_pcm_streams(i
) {
240 struct snd_pcm_substream
*substream
= rtd
->pcm
->streams
[i
].substream
;
244 if (!pcm
->chan
[i
] && config
->chan_names
[i
])
245 pcm
->chan
[i
] = dma_request_slave_channel(dev
,
246 config
->chan_names
[i
]);
248 if (!pcm
->chan
[i
] && (pcm
->flags
& SND_DMAENGINE_PCM_FLAG_COMPAT
)) {
249 pcm
->chan
[i
] = dmaengine_pcm_compat_request_channel(
250 component
, rtd
, substream
);
254 dev_err(component
->dev
,
255 "Missing dma channel for stream: %d\n", i
);
259 snd_pcm_set_managed_buffer(substream
,
260 SNDRV_DMA_TYPE_DEV_IRAM
,
261 dmaengine_dma_dev(pcm
, substream
),
262 prealloc_buffer_size
,
265 if (!dmaengine_pcm_can_report_residue(dev
, pcm
->chan
[i
]))
266 pcm
->flags
|= SND_DMAENGINE_PCM_FLAG_NO_RESIDUE
;
268 if (rtd
->pcm
->streams
[i
].pcm
->name
[0] == '\0') {
269 strscpy_pad(rtd
->pcm
->streams
[i
].pcm
->name
,
270 rtd
->pcm
->streams
[i
].pcm
->id
,
271 sizeof(rtd
->pcm
->streams
[i
].pcm
->name
));
278 static snd_pcm_uframes_t
dmaengine_pcm_pointer(
279 struct snd_soc_component
*component
,
280 struct snd_pcm_substream
*substream
)
282 struct dmaengine_pcm
*pcm
= soc_component_to_pcm(component
);
284 if (pcm
->flags
& SND_DMAENGINE_PCM_FLAG_NO_RESIDUE
)
285 return snd_dmaengine_pcm_pointer_no_residue(substream
);
287 return snd_dmaengine_pcm_pointer(substream
);
290 static int dmaengine_copy(struct snd_soc_component
*component
,
291 struct snd_pcm_substream
*substream
,
292 int channel
, unsigned long hwoff
,
293 struct iov_iter
*iter
, unsigned long bytes
)
295 struct snd_pcm_runtime
*runtime
= substream
->runtime
;
296 struct dmaengine_pcm
*pcm
= soc_component_to_pcm(component
);
297 int (*process
)(struct snd_pcm_substream
*substream
,
298 int channel
, unsigned long hwoff
,
299 unsigned long bytes
) = pcm
->config
->process
;
300 bool is_playback
= substream
->stream
== SNDRV_PCM_STREAM_PLAYBACK
;
301 void *dma_ptr
= runtime
->dma_area
+ hwoff
+
302 channel
* (runtime
->dma_bytes
/ runtime
->channels
);
305 if (copy_from_iter(dma_ptr
, bytes
, iter
) != bytes
)
309 int ret
= process(substream
, channel
, hwoff
, bytes
);
315 if (copy_to_iter(dma_ptr
, bytes
, iter
) != bytes
)
321 static int dmaengine_pcm_sync_stop(struct snd_soc_component
*component
,
322 struct snd_pcm_substream
*substream
)
324 return snd_dmaengine_pcm_sync_stop(substream
);
327 static const struct snd_soc_component_driver dmaengine_pcm_component
= {
328 .name
= SND_DMAENGINE_PCM_DRV_NAME
,
329 .probe_order
= SND_SOC_COMP_ORDER_LATE
,
330 .open
= dmaengine_pcm_open
,
331 .close
= dmaengine_pcm_close
,
332 .hw_params
= dmaengine_pcm_hw_params
,
333 .trigger
= dmaengine_pcm_trigger
,
334 .pointer
= dmaengine_pcm_pointer
,
335 .pcm_construct
= dmaengine_pcm_new
,
336 .sync_stop
= dmaengine_pcm_sync_stop
,
339 static const struct snd_soc_component_driver dmaengine_pcm_component_process
= {
340 .name
= SND_DMAENGINE_PCM_DRV_NAME
,
341 .probe_order
= SND_SOC_COMP_ORDER_LATE
,
342 .open
= dmaengine_pcm_open
,
343 .close
= dmaengine_pcm_close
,
344 .hw_params
= dmaengine_pcm_hw_params
,
345 .trigger
= dmaengine_pcm_trigger
,
346 .pointer
= dmaengine_pcm_pointer
,
347 .copy
= dmaengine_copy
,
348 .pcm_construct
= dmaengine_pcm_new
,
349 .sync_stop
= dmaengine_pcm_sync_stop
,
352 static const char * const dmaengine_pcm_dma_channel_names
[] = {
353 [SNDRV_PCM_STREAM_PLAYBACK
] = "tx",
354 [SNDRV_PCM_STREAM_CAPTURE
] = "rx",
357 static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm
*pcm
,
358 struct device
*dev
, const struct snd_dmaengine_pcm_config
*config
)
362 struct dma_chan
*chan
;
364 if ((pcm
->flags
& SND_DMAENGINE_PCM_FLAG_NO_DT
) || (!dev
->of_node
&&
365 !(config
->dma_dev
&& config
->dma_dev
->of_node
)))
368 if (config
->dma_dev
) {
370 * If this warning is seen, it probably means that your Linux
371 * device structure does not match your HW device structure.
372 * It would be best to refactor the Linux device structure to
373 * correctly match the HW structure.
375 dev_warn(dev
, "DMA channels sourced from device %s",
376 dev_name(config
->dma_dev
));
377 dev
= config
->dma_dev
;
380 for_each_pcm_streams(i
) {
381 if (pcm
->flags
& SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX
)
384 name
= dmaengine_pcm_dma_channel_names
[i
];
385 if (config
->chan_names
[i
])
386 name
= config
->chan_names
[i
];
387 chan
= dma_request_chan(dev
, name
);
390 * Only report probe deferral errors, channels
391 * might not be present for devices that
392 * support only TX or only RX.
394 if (PTR_ERR(chan
) == -EPROBE_DEFER
)
395 return -EPROBE_DEFER
;
400 if (pcm
->flags
& SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX
)
404 if (pcm
->flags
& SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX
)
405 pcm
->chan
[1] = pcm
->chan
[0];
410 static void dmaengine_pcm_release_chan(struct dmaengine_pcm
*pcm
)
414 for_each_pcm_streams(i
) {
417 dma_release_channel(pcm
->chan
[i
]);
418 if (pcm
->flags
& SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX
)
423 static const struct snd_dmaengine_pcm_config snd_dmaengine_pcm_default_config
= {
424 .prepare_slave_config
= snd_dmaengine_pcm_prepare_slave_config
,
428 * snd_dmaengine_pcm_register - Register a dmaengine based PCM device
429 * @dev: The parent device for the PCM device
430 * @config: Platform specific PCM configuration
431 * @flags: Platform specific quirks
433 int snd_dmaengine_pcm_register(struct device
*dev
,
434 const struct snd_dmaengine_pcm_config
*config
, unsigned int flags
)
436 const struct snd_soc_component_driver
*driver
;
437 struct dmaengine_pcm
*pcm
;
440 pcm
= kzalloc(sizeof(*pcm
), GFP_KERNEL
);
444 #ifdef CONFIG_DEBUG_FS
445 pcm
->component
.debugfs_prefix
= "dma";
448 config
= &snd_dmaengine_pcm_default_config
;
449 pcm
->config
= config
;
453 pcm
->component
.name
= config
->name
;
455 ret
= dmaengine_pcm_request_chan_of(pcm
, dev
, config
);
460 driver
= &dmaengine_pcm_component_process
;
462 driver
= &dmaengine_pcm_component
;
464 ret
= snd_soc_component_initialize(&pcm
->component
, driver
, dev
);
468 ret
= snd_soc_add_component(&pcm
->component
, NULL
, 0);
475 dmaengine_pcm_release_chan(pcm
);
479 EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_register
);
482 * snd_dmaengine_pcm_unregister - Removes a dmaengine based PCM device
483 * @dev: Parent device the PCM was register with
485 * Removes a dmaengine based PCM device previously registered with
486 * snd_dmaengine_pcm_register.
488 void snd_dmaengine_pcm_unregister(struct device
*dev
)
490 struct snd_soc_component
*component
;
491 struct dmaengine_pcm
*pcm
;
493 component
= snd_soc_lookup_component(dev
, SND_DMAENGINE_PCM_DRV_NAME
);
497 pcm
= soc_component_to_pcm(component
);
499 snd_soc_unregister_component_by_driver(dev
, component
->driver
);
500 dmaengine_pcm_release_chan(pcm
);
503 EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_unregister
);
505 MODULE_DESCRIPTION("ASoC helpers for generic PCM dmaengine API");
506 MODULE_LICENSE("GPL");