1a912e80bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2b7ae6f31SDaniel Mack /* 3b7ae6f31SDaniel Mack * Copyright (C) 2012, Analog Devices Inc. 4b7ae6f31SDaniel Mack * Author: Lars-Peter Clausen <lars@metafoo.de> 5b7ae6f31SDaniel Mack * 6b7ae6f31SDaniel Mack * Based on: 7b7ae6f31SDaniel Mack * imx-pcm-dma-mx2.c, Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de> 8b7ae6f31SDaniel Mack * mxs-pcm.c, Copyright (C) 2011 Freescale Semiconductor, Inc. 9b7ae6f31SDaniel Mack * ep93xx-pcm.c, Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org> 10b7ae6f31SDaniel Mack * Copyright (C) 2006 Applied Data Systems 11b7ae6f31SDaniel Mack */ 12b7ae6f31SDaniel Mack #include <linux/module.h> 13b7ae6f31SDaniel Mack #include <linux/init.h> 14b7ae6f31SDaniel Mack #include <linux/dmaengine.h> 15b7ae6f31SDaniel Mack #include <linux/slab.h> 16b7ae6f31SDaniel Mack #include <sound/pcm.h> 17b7ae6f31SDaniel Mack #include <sound/pcm_params.h> 18b7ae6f31SDaniel Mack #include <sound/soc.h> 19b7ae6f31SDaniel Mack 20b7ae6f31SDaniel Mack #include <sound/dmaengine_pcm.h> 21b7ae6f31SDaniel Mack 22b7ae6f31SDaniel Mack struct dmaengine_pcm_runtime_data { 23b7ae6f31SDaniel Mack struct dma_chan *dma_chan; 24b7ae6f31SDaniel Mack dma_cookie_t cookie; 25b7ae6f31SDaniel Mack 26b7ae6f31SDaniel Mack unsigned int pos; 27b7ae6f31SDaniel Mack }; 28b7ae6f31SDaniel Mack 29b7ae6f31SDaniel Mack static inline struct dmaengine_pcm_runtime_data *substream_to_prtd( 30b7ae6f31SDaniel Mack const struct snd_pcm_substream *substream) 31b7ae6f31SDaniel Mack { 32b7ae6f31SDaniel Mack return substream->runtime->private_data; 33b7ae6f31SDaniel Mack } 34b7ae6f31SDaniel Mack 35b7ae6f31SDaniel Mack struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream) 36b7ae6f31SDaniel Mack { 37b7ae6f31SDaniel Mack struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); 38b7ae6f31SDaniel Mack 39b7ae6f31SDaniel Mack return prtd->dma_chan; 40b7ae6f31SDaniel Mack } 41b7ae6f31SDaniel Mack EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_get_chan); 42b7ae6f31SDaniel Mack 43b7ae6f31SDaniel Mack /** 44b7ae6f31SDaniel Mack * snd_hwparams_to_dma_slave_config - Convert hw_params to dma_slave_config 45b7ae6f31SDaniel Mack * @substream: PCM substream 46b7ae6f31SDaniel Mack * @params: hw_params 47b7ae6f31SDaniel Mack * @slave_config: DMA slave config 48b7ae6f31SDaniel Mack * 49b7ae6f31SDaniel Mack * This function can be used to initialize a dma_slave_config from a substream 50b7ae6f31SDaniel Mack * and hw_params in a dmaengine based PCM driver implementation. 51b7ae6f31SDaniel Mack */ 52b7ae6f31SDaniel Mack int snd_hwparams_to_dma_slave_config(const struct snd_pcm_substream *substream, 53b7ae6f31SDaniel Mack const struct snd_pcm_hw_params *params, 54b7ae6f31SDaniel Mack struct dma_slave_config *slave_config) 55b7ae6f31SDaniel Mack { 56b7ae6f31SDaniel Mack enum dma_slave_buswidth buswidth; 57a655f75cSTakashi Iwai int bits; 58b7ae6f31SDaniel Mack 59732814c8SPeter Ujfalusi bits = params_physical_width(params); 60a655f75cSTakashi Iwai if (bits < 8 || bits > 64) 61b7ae6f31SDaniel Mack return -EINVAL; 62a655f75cSTakashi Iwai else if (bits == 8) 63a655f75cSTakashi Iwai buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE; 64a655f75cSTakashi Iwai else if (bits == 16) 65a655f75cSTakashi Iwai buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; 6675f850fcSPeter Ujfalusi else if (bits == 24) 6775f850fcSPeter Ujfalusi buswidth = DMA_SLAVE_BUSWIDTH_3_BYTES; 68a655f75cSTakashi Iwai else if (bits <= 32) 69a655f75cSTakashi Iwai buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; 70a655f75cSTakashi Iwai else 71a655f75cSTakashi Iwai buswidth = DMA_SLAVE_BUSWIDTH_8_BYTES; 72b7ae6f31SDaniel Mack 73b7ae6f31SDaniel Mack if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 74b7ae6f31SDaniel Mack slave_config->direction = DMA_MEM_TO_DEV; 75b7ae6f31SDaniel Mack slave_config->dst_addr_width = buswidth; 76b7ae6f31SDaniel Mack } else { 77b7ae6f31SDaniel Mack slave_config->direction = DMA_DEV_TO_MEM; 78b7ae6f31SDaniel Mack slave_config->src_addr_width = buswidth; 79b7ae6f31SDaniel Mack } 80b7ae6f31SDaniel Mack 81b7ae6f31SDaniel Mack slave_config->device_fc = false; 82b7ae6f31SDaniel Mack 83b7ae6f31SDaniel Mack return 0; 84b7ae6f31SDaniel Mack } 85b7ae6f31SDaniel Mack EXPORT_SYMBOL_GPL(snd_hwparams_to_dma_slave_config); 86b7ae6f31SDaniel Mack 87b7ae6f31SDaniel Mack /** 88b7ae6f31SDaniel Mack * snd_dmaengine_pcm_set_config_from_dai_data() - Initializes a dma slave config 89b7ae6f31SDaniel Mack * using DAI DMA data. 90b7ae6f31SDaniel Mack * @substream: PCM substream 91b7ae6f31SDaniel Mack * @dma_data: DAI DMA data 92b7ae6f31SDaniel Mack * @slave_config: DMA slave configuration 93b7ae6f31SDaniel Mack * 94b7ae6f31SDaniel Mack * Initializes the {dst,src}_addr, {dst,src}_maxburst, {dst,src}_addr_width and 95b7ae6f31SDaniel Mack * slave_id fields of the DMA slave config from the same fields of the DAI DMA 96b7ae6f31SDaniel Mack * data struct. The src and dst fields will be initialized depending on the 97b7ae6f31SDaniel Mack * direction of the substream. If the substream is a playback stream the dst 98b7ae6f31SDaniel Mack * fields will be initialized, if it is a capture stream the src fields will be 99b7ae6f31SDaniel Mack * initialized. The {dst,src}_addr_width field will only be initialized if the 10073fe01cfSMatthias Reichl * SND_DMAENGINE_PCM_DAI_FLAG_PACK flag is set or if the addr_width field of 10173fe01cfSMatthias Reichl * the DAI DMA data struct is not equal to DMA_SLAVE_BUSWIDTH_UNDEFINED. If 10273fe01cfSMatthias Reichl * both conditions are met the latter takes priority. 103b7ae6f31SDaniel Mack */ 104b7ae6f31SDaniel Mack void snd_dmaengine_pcm_set_config_from_dai_data( 105b7ae6f31SDaniel Mack const struct snd_pcm_substream *substream, 106b7ae6f31SDaniel Mack const struct snd_dmaengine_dai_dma_data *dma_data, 107b7ae6f31SDaniel Mack struct dma_slave_config *slave_config) 108b7ae6f31SDaniel Mack { 109b7ae6f31SDaniel Mack if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 110b7ae6f31SDaniel Mack slave_config->dst_addr = dma_data->addr; 111b7ae6f31SDaniel Mack slave_config->dst_maxburst = dma_data->maxburst; 11273fe01cfSMatthias Reichl if (dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK) 11373fe01cfSMatthias Reichl slave_config->dst_addr_width = 11473fe01cfSMatthias Reichl DMA_SLAVE_BUSWIDTH_UNDEFINED; 115b7ae6f31SDaniel Mack if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED) 116b7ae6f31SDaniel Mack slave_config->dst_addr_width = dma_data->addr_width; 117b7ae6f31SDaniel Mack } else { 118b7ae6f31SDaniel Mack slave_config->src_addr = dma_data->addr; 119b7ae6f31SDaniel Mack slave_config->src_maxburst = dma_data->maxburst; 12073fe01cfSMatthias Reichl if (dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK) 12173fe01cfSMatthias Reichl slave_config->src_addr_width = 12273fe01cfSMatthias Reichl DMA_SLAVE_BUSWIDTH_UNDEFINED; 123b7ae6f31SDaniel Mack if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED) 124b7ae6f31SDaniel Mack slave_config->src_addr_width = dma_data->addr_width; 125b7ae6f31SDaniel Mack } 126b7ae6f31SDaniel Mack 127b7ae6f31SDaniel Mack slave_config->slave_id = dma_data->slave_id; 128b7ae6f31SDaniel Mack } 129b7ae6f31SDaniel Mack EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_set_config_from_dai_data); 130b7ae6f31SDaniel Mack 131b7ae6f31SDaniel Mack static void dmaengine_pcm_dma_complete(void *arg) 132b7ae6f31SDaniel Mack { 133b7ae6f31SDaniel Mack struct snd_pcm_substream *substream = arg; 134b7ae6f31SDaniel Mack struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); 135b7ae6f31SDaniel Mack 136b7ae6f31SDaniel Mack prtd->pos += snd_pcm_lib_period_bytes(substream); 137b7ae6f31SDaniel Mack if (prtd->pos >= snd_pcm_lib_buffer_bytes(substream)) 138b7ae6f31SDaniel Mack prtd->pos = 0; 139b7ae6f31SDaniel Mack 140b7ae6f31SDaniel Mack snd_pcm_period_elapsed(substream); 141b7ae6f31SDaniel Mack } 142b7ae6f31SDaniel Mack 143b7ae6f31SDaniel Mack static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream) 144b7ae6f31SDaniel Mack { 145b7ae6f31SDaniel Mack struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); 146b7ae6f31SDaniel Mack struct dma_chan *chan = prtd->dma_chan; 147b7ae6f31SDaniel Mack struct dma_async_tx_descriptor *desc; 148b7ae6f31SDaniel Mack enum dma_transfer_direction direction; 149b7ae6f31SDaniel Mack unsigned long flags = DMA_CTRL_ACK; 150b7ae6f31SDaniel Mack 151b7ae6f31SDaniel Mack direction = snd_pcm_substream_to_dma_direction(substream); 152b7ae6f31SDaniel Mack 153b7ae6f31SDaniel Mack if (!substream->runtime->no_period_wakeup) 154b7ae6f31SDaniel Mack flags |= DMA_PREP_INTERRUPT; 155b7ae6f31SDaniel Mack 156b7ae6f31SDaniel Mack prtd->pos = 0; 157b7ae6f31SDaniel Mack desc = dmaengine_prep_dma_cyclic(chan, 158b7ae6f31SDaniel Mack substream->runtime->dma_addr, 159b7ae6f31SDaniel Mack snd_pcm_lib_buffer_bytes(substream), 160b7ae6f31SDaniel Mack snd_pcm_lib_period_bytes(substream), direction, flags); 161b7ae6f31SDaniel Mack 162b7ae6f31SDaniel Mack if (!desc) 163b7ae6f31SDaniel Mack return -ENOMEM; 164b7ae6f31SDaniel Mack 165b7ae6f31SDaniel Mack desc->callback = dmaengine_pcm_dma_complete; 166b7ae6f31SDaniel Mack desc->callback_param = substream; 167b7ae6f31SDaniel Mack prtd->cookie = dmaengine_submit(desc); 168b7ae6f31SDaniel Mack 169b7ae6f31SDaniel Mack return 0; 170b7ae6f31SDaniel Mack } 171b7ae6f31SDaniel Mack 172b7ae6f31SDaniel Mack /** 173b7ae6f31SDaniel Mack * snd_dmaengine_pcm_trigger - dmaengine based PCM trigger implementation 174b7ae6f31SDaniel Mack * @substream: PCM substream 175b7ae6f31SDaniel Mack * @cmd: Trigger command 176b7ae6f31SDaniel Mack * 177b7ae6f31SDaniel Mack * Returns 0 on success, a negative error code otherwise. 178b7ae6f31SDaniel Mack * 179b7ae6f31SDaniel Mack * This function can be used as the PCM trigger callback for dmaengine based PCM 180b7ae6f31SDaniel Mack * driver implementations. 181b7ae6f31SDaniel Mack */ 182b7ae6f31SDaniel Mack int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 183b7ae6f31SDaniel Mack { 184b7ae6f31SDaniel Mack struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); 18502fb05a5STushar Behera struct snd_pcm_runtime *runtime = substream->runtime; 186b7ae6f31SDaniel Mack int ret; 187b7ae6f31SDaniel Mack 188b7ae6f31SDaniel Mack switch (cmd) { 189b7ae6f31SDaniel Mack case SNDRV_PCM_TRIGGER_START: 190b7ae6f31SDaniel Mack ret = dmaengine_pcm_prepare_and_submit(substream); 191b7ae6f31SDaniel Mack if (ret) 192b7ae6f31SDaniel Mack return ret; 193b7ae6f31SDaniel Mack dma_async_issue_pending(prtd->dma_chan); 194b7ae6f31SDaniel Mack break; 195b7ae6f31SDaniel Mack case SNDRV_PCM_TRIGGER_RESUME: 196b7ae6f31SDaniel Mack case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 197b7ae6f31SDaniel Mack dmaengine_resume(prtd->dma_chan); 198b7ae6f31SDaniel Mack break; 199b7ae6f31SDaniel Mack case SNDRV_PCM_TRIGGER_SUSPEND: 20002fb05a5STushar Behera if (runtime->info & SNDRV_PCM_INFO_PAUSE) 20102fb05a5STushar Behera dmaengine_pause(prtd->dma_chan); 20202fb05a5STushar Behera else 203bc0e7345SLars-Peter Clausen dmaengine_terminate_async(prtd->dma_chan); 20402fb05a5STushar Behera break; 205b7ae6f31SDaniel Mack case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 206b7ae6f31SDaniel Mack dmaengine_pause(prtd->dma_chan); 207b7ae6f31SDaniel Mack break; 208b7ae6f31SDaniel Mack case SNDRV_PCM_TRIGGER_STOP: 209bc0e7345SLars-Peter Clausen dmaengine_terminate_async(prtd->dma_chan); 210b7ae6f31SDaniel Mack break; 211b7ae6f31SDaniel Mack default: 212b7ae6f31SDaniel Mack return -EINVAL; 213b7ae6f31SDaniel Mack } 214b7ae6f31SDaniel Mack 215b7ae6f31SDaniel Mack return 0; 216b7ae6f31SDaniel Mack } 217b7ae6f31SDaniel Mack EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_trigger); 218b7ae6f31SDaniel Mack 219b7ae6f31SDaniel Mack /** 220b7ae6f31SDaniel Mack * snd_dmaengine_pcm_pointer_no_residue - dmaengine based PCM pointer implementation 221b7ae6f31SDaniel Mack * @substream: PCM substream 222b7ae6f31SDaniel Mack * 223b7ae6f31SDaniel Mack * This function is deprecated and should not be used by new drivers, as its 224b7ae6f31SDaniel Mack * results may be unreliable. 225b7ae6f31SDaniel Mack */ 226b7ae6f31SDaniel Mack snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream *substream) 227b7ae6f31SDaniel Mack { 228b7ae6f31SDaniel Mack struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); 229b7ae6f31SDaniel Mack return bytes_to_frames(substream->runtime, prtd->pos); 230b7ae6f31SDaniel Mack } 231b7ae6f31SDaniel Mack EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer_no_residue); 232b7ae6f31SDaniel Mack 233b7ae6f31SDaniel Mack /** 234b7ae6f31SDaniel Mack * snd_dmaengine_pcm_pointer - dmaengine based PCM pointer implementation 235b7ae6f31SDaniel Mack * @substream: PCM substream 236b7ae6f31SDaniel Mack * 237b7ae6f31SDaniel Mack * This function can be used as the PCM pointer callback for dmaengine based PCM 238b7ae6f31SDaniel Mack * driver implementations. 239b7ae6f31SDaniel Mack */ 240b7ae6f31SDaniel Mack snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream) 241b7ae6f31SDaniel Mack { 242b7ae6f31SDaniel Mack struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); 2439d789dc0SPeter Ujfalusi struct snd_pcm_runtime *runtime = substream->runtime; 244b7ae6f31SDaniel Mack struct dma_tx_state state; 245b7ae6f31SDaniel Mack enum dma_status status; 246b7ae6f31SDaniel Mack unsigned int buf_size; 247b7ae6f31SDaniel Mack unsigned int pos = 0; 248b7ae6f31SDaniel Mack 249b7ae6f31SDaniel Mack status = dmaengine_tx_status(prtd->dma_chan, prtd->cookie, &state); 250b7ae6f31SDaniel Mack if (status == DMA_IN_PROGRESS || status == DMA_PAUSED) { 251b7ae6f31SDaniel Mack buf_size = snd_pcm_lib_buffer_bytes(substream); 252b7ae6f31SDaniel Mack if (state.residue > 0 && state.residue <= buf_size) 253b7ae6f31SDaniel Mack pos = buf_size - state.residue; 254fa1f875cSPeter Ujfalusi 255fa1f875cSPeter Ujfalusi runtime->delay = bytes_to_frames(runtime, 256fa1f875cSPeter Ujfalusi state.in_flight_bytes); 257b7ae6f31SDaniel Mack } 258b7ae6f31SDaniel Mack 2599d789dc0SPeter Ujfalusi return bytes_to_frames(runtime, pos); 260b7ae6f31SDaniel Mack } 261b7ae6f31SDaniel Mack EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer); 262b7ae6f31SDaniel Mack 263b7ae6f31SDaniel Mack /** 264b7ae6f31SDaniel Mack * snd_dmaengine_pcm_request_channel - Request channel for the dmaengine PCM 265b7ae6f31SDaniel Mack * @filter_fn: Filter function used to request the DMA channel 266b7ae6f31SDaniel Mack * @filter_data: Data passed to the DMA filter function 267b7ae6f31SDaniel Mack * 268b7ae6f31SDaniel Mack * Returns NULL or the requested DMA channel. 269b7ae6f31SDaniel Mack * 270b7ae6f31SDaniel Mack * This function request a DMA channel for usage with dmaengine PCM. 271b7ae6f31SDaniel Mack */ 272b7ae6f31SDaniel Mack struct dma_chan *snd_dmaengine_pcm_request_channel(dma_filter_fn filter_fn, 273b7ae6f31SDaniel Mack void *filter_data) 274b7ae6f31SDaniel Mack { 275b7ae6f31SDaniel Mack dma_cap_mask_t mask; 276b7ae6f31SDaniel Mack 277b7ae6f31SDaniel Mack dma_cap_zero(mask); 278b7ae6f31SDaniel Mack dma_cap_set(DMA_SLAVE, mask); 279b7ae6f31SDaniel Mack dma_cap_set(DMA_CYCLIC, mask); 280b7ae6f31SDaniel Mack 281b7ae6f31SDaniel Mack return dma_request_channel(mask, filter_fn, filter_data); 282b7ae6f31SDaniel Mack } 283b7ae6f31SDaniel Mack EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_request_channel); 284b7ae6f31SDaniel Mack 285b7ae6f31SDaniel Mack /** 286b7ae6f31SDaniel Mack * snd_dmaengine_pcm_open - Open a dmaengine based PCM substream 287b7ae6f31SDaniel Mack * @substream: PCM substream 288b7ae6f31SDaniel Mack * @chan: DMA channel to use for data transfers 289b7ae6f31SDaniel Mack * 290b7ae6f31SDaniel Mack * Returns 0 on success, a negative error code otherwise. 291b7ae6f31SDaniel Mack * 292b7ae6f31SDaniel Mack * The function should usually be called from the pcm open callback. Note that 293b7ae6f31SDaniel Mack * this function will use private_data field of the substream's runtime. So it 2941a6ab46fSMasanari Iida * is not available to your pcm driver implementation. 295b7ae6f31SDaniel Mack */ 296b7ae6f31SDaniel Mack int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream, 297b7ae6f31SDaniel Mack struct dma_chan *chan) 298b7ae6f31SDaniel Mack { 299b7ae6f31SDaniel Mack struct dmaengine_pcm_runtime_data *prtd; 300b7ae6f31SDaniel Mack int ret; 301b7ae6f31SDaniel Mack 302b7ae6f31SDaniel Mack if (!chan) 303b7ae6f31SDaniel Mack return -ENXIO; 304b7ae6f31SDaniel Mack 305b7ae6f31SDaniel Mack ret = snd_pcm_hw_constraint_integer(substream->runtime, 306b7ae6f31SDaniel Mack SNDRV_PCM_HW_PARAM_PERIODS); 307b7ae6f31SDaniel Mack if (ret < 0) 308b7ae6f31SDaniel Mack return ret; 309b7ae6f31SDaniel Mack 310b7ae6f31SDaniel Mack prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); 311b7ae6f31SDaniel Mack if (!prtd) 312b7ae6f31SDaniel Mack return -ENOMEM; 313b7ae6f31SDaniel Mack 314b7ae6f31SDaniel Mack prtd->dma_chan = chan; 315b7ae6f31SDaniel Mack 316b7ae6f31SDaniel Mack substream->runtime->private_data = prtd; 317b7ae6f31SDaniel Mack 318b7ae6f31SDaniel Mack return 0; 319b7ae6f31SDaniel Mack } 320b7ae6f31SDaniel Mack EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open); 321b7ae6f31SDaniel Mack 322b7ae6f31SDaniel Mack /** 323b7ae6f31SDaniel Mack * snd_dmaengine_pcm_open_request_chan - Open a dmaengine based PCM substream and request channel 324b7ae6f31SDaniel Mack * @substream: PCM substream 325b7ae6f31SDaniel Mack * @filter_fn: Filter function used to request the DMA channel 326b7ae6f31SDaniel Mack * @filter_data: Data passed to the DMA filter function 327b7ae6f31SDaniel Mack * 328b7ae6f31SDaniel Mack * Returns 0 on success, a negative error code otherwise. 329b7ae6f31SDaniel Mack * 330b7ae6f31SDaniel Mack * This function will request a DMA channel using the passed filter function and 331b7ae6f31SDaniel Mack * data. The function should usually be called from the pcm open callback. Note 332b7ae6f31SDaniel Mack * that this function will use private_data field of the substream's runtime. So 3331a6ab46fSMasanari Iida * it is not available to your pcm driver implementation. 334b7ae6f31SDaniel Mack */ 335b7ae6f31SDaniel Mack int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream, 336b7ae6f31SDaniel Mack dma_filter_fn filter_fn, void *filter_data) 337b7ae6f31SDaniel Mack { 338b7ae6f31SDaniel Mack return snd_dmaengine_pcm_open(substream, 339b7ae6f31SDaniel Mack snd_dmaengine_pcm_request_channel(filter_fn, filter_data)); 340b7ae6f31SDaniel Mack } 341b7ae6f31SDaniel Mack EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open_request_chan); 342b7ae6f31SDaniel Mack 343b7ae6f31SDaniel Mack /** 344b7ae6f31SDaniel Mack * snd_dmaengine_pcm_close - Close a dmaengine based PCM substream 345b7ae6f31SDaniel Mack * @substream: PCM substream 346b7ae6f31SDaniel Mack */ 347b7ae6f31SDaniel Mack int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream) 348b7ae6f31SDaniel Mack { 349b7ae6f31SDaniel Mack struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); 350b7ae6f31SDaniel Mack 351bc0e7345SLars-Peter Clausen dmaengine_synchronize(prtd->dma_chan); 352b7ae6f31SDaniel Mack kfree(prtd); 353b7ae6f31SDaniel Mack 354b7ae6f31SDaniel Mack return 0; 355b7ae6f31SDaniel Mack } 356b7ae6f31SDaniel Mack EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close); 357b7ae6f31SDaniel Mack 358b7ae6f31SDaniel Mack /** 359*f7b6603cSMauro Carvalho Chehab * snd_dmaengine_pcm_close_release_chan - Close a dmaengine based PCM 360*f7b6603cSMauro Carvalho Chehab * substream and release channel 361b7ae6f31SDaniel Mack * @substream: PCM substream 362b7ae6f31SDaniel Mack * 363b7ae6f31SDaniel Mack * Releases the DMA channel associated with the PCM substream. 364b7ae6f31SDaniel Mack */ 365b7ae6f31SDaniel Mack int snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream *substream) 366b7ae6f31SDaniel Mack { 367b7ae6f31SDaniel Mack struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); 368b7ae6f31SDaniel Mack 369bc0e7345SLars-Peter Clausen dmaengine_synchronize(prtd->dma_chan); 370b7ae6f31SDaniel Mack dma_release_channel(prtd->dma_chan); 371bc0e7345SLars-Peter Clausen kfree(prtd); 372b7ae6f31SDaniel Mack 373bc0e7345SLars-Peter Clausen return 0; 374b7ae6f31SDaniel Mack } 375b7ae6f31SDaniel Mack EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close_release_chan); 376b7ae6f31SDaniel Mack 377e957204eSShengjiu Wang /** 378e957204eSShengjiu Wang * snd_dmaengine_pcm_refine_runtime_hwparams - Refine runtime hw params 379e957204eSShengjiu Wang * @substream: PCM substream 380e957204eSShengjiu Wang * @dma_data: DAI DMA data 381e957204eSShengjiu Wang * @hw: PCM hw params 382e957204eSShengjiu Wang * @chan: DMA channel to use for data transfers 383e957204eSShengjiu Wang * 384e957204eSShengjiu Wang * Returns 0 on success, a negative error code otherwise. 385e957204eSShengjiu Wang * 386e957204eSShengjiu Wang * This function will query DMA capability, then refine the pcm hardware 387e957204eSShengjiu Wang * parameters. 388e957204eSShengjiu Wang */ 389e957204eSShengjiu Wang int snd_dmaengine_pcm_refine_runtime_hwparams( 390e957204eSShengjiu Wang struct snd_pcm_substream *substream, 391e957204eSShengjiu Wang struct snd_dmaengine_dai_dma_data *dma_data, 392e957204eSShengjiu Wang struct snd_pcm_hardware *hw, 393e957204eSShengjiu Wang struct dma_chan *chan) 394e957204eSShengjiu Wang { 395e957204eSShengjiu Wang struct dma_slave_caps dma_caps; 396e957204eSShengjiu Wang u32 addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | 397e957204eSShengjiu Wang BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | 398e957204eSShengjiu Wang BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); 399e957204eSShengjiu Wang snd_pcm_format_t i; 400e957204eSShengjiu Wang int ret = 0; 401e957204eSShengjiu Wang 402e957204eSShengjiu Wang if (!hw || !chan || !dma_data) 403e957204eSShengjiu Wang return -EINVAL; 404e957204eSShengjiu Wang 405e957204eSShengjiu Wang ret = dma_get_slave_caps(chan, &dma_caps); 406e957204eSShengjiu Wang if (ret == 0) { 407e957204eSShengjiu Wang if (dma_caps.cmd_pause && dma_caps.cmd_resume) 408e957204eSShengjiu Wang hw->info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME; 409e957204eSShengjiu Wang if (dma_caps.residue_granularity <= DMA_RESIDUE_GRANULARITY_SEGMENT) 410e957204eSShengjiu Wang hw->info |= SNDRV_PCM_INFO_BATCH; 411e957204eSShengjiu Wang 412e957204eSShengjiu Wang if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 413e957204eSShengjiu Wang addr_widths = dma_caps.dst_addr_widths; 414e957204eSShengjiu Wang else 415e957204eSShengjiu Wang addr_widths = dma_caps.src_addr_widths; 416e957204eSShengjiu Wang } 417e957204eSShengjiu Wang 418e957204eSShengjiu Wang /* 419e957204eSShengjiu Wang * If SND_DMAENGINE_PCM_DAI_FLAG_PACK is set keep 420e957204eSShengjiu Wang * hw.formats set to 0, meaning no restrictions are in place. 421e957204eSShengjiu Wang * In this case it's the responsibility of the DAI driver to 422e957204eSShengjiu Wang * provide the supported format information. 423e957204eSShengjiu Wang */ 424e957204eSShengjiu Wang if (!(dma_data->flags & SND_DMAENGINE_PCM_DAI_FLAG_PACK)) 425e957204eSShengjiu Wang /* 426e957204eSShengjiu Wang * Prepare formats mask for valid/allowed sample types. If the 427e957204eSShengjiu Wang * dma does not have support for the given physical word size, 428e957204eSShengjiu Wang * it needs to be masked out so user space can not use the 429e957204eSShengjiu Wang * format which produces corrupted audio. 430e957204eSShengjiu Wang * In case the dma driver does not implement the slave_caps the 431e957204eSShengjiu Wang * default assumption is that it supports 1, 2 and 4 bytes 432e957204eSShengjiu Wang * widths. 433e957204eSShengjiu Wang */ 43489e0b9a0STakashi Iwai pcm_for_each_format(i) { 435e957204eSShengjiu Wang int bits = snd_pcm_format_physical_width(i); 436e957204eSShengjiu Wang 437e957204eSShengjiu Wang /* 438e957204eSShengjiu Wang * Enable only samples with DMA supported physical 439e957204eSShengjiu Wang * widths 440e957204eSShengjiu Wang */ 441e957204eSShengjiu Wang switch (bits) { 442e957204eSShengjiu Wang case 8: 443e957204eSShengjiu Wang case 16: 444e957204eSShengjiu Wang case 24: 445e957204eSShengjiu Wang case 32: 446e957204eSShengjiu Wang case 64: 447e957204eSShengjiu Wang if (addr_widths & (1 << (bits / 8))) 448e957204eSShengjiu Wang hw->formats |= pcm_format_to_bits(i); 449e957204eSShengjiu Wang break; 450e957204eSShengjiu Wang default: 451e957204eSShengjiu Wang /* Unsupported types */ 452e957204eSShengjiu Wang break; 453e957204eSShengjiu Wang } 454e957204eSShengjiu Wang } 455e957204eSShengjiu Wang 456e957204eSShengjiu Wang return ret; 457e957204eSShengjiu Wang } 458e957204eSShengjiu Wang EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_refine_runtime_hwparams); 459e957204eSShengjiu Wang 460b7ae6f31SDaniel Mack MODULE_LICENSE("GPL"); 461