Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.5 288 lines 8.0 kB view raw
1/* 2 * Copyright (C) 2012, Analog Devices Inc. 3 * Author: Lars-Peter Clausen <lars@metafoo.de> 4 * 5 * Based on: 6 * imx-pcm-dma-mx2.c, Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de> 7 * mxs-pcm.c, Copyright (C) 2011 Freescale Semiconductor, Inc. 8 * ep93xx-pcm.c, Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org> 9 * Copyright (C) 2006 Applied Data Systems 10 * 11 * This program is free software; you can redistribute it and/or modify it 12 * under the terms of the GNU General Public License as published by the 13 * Free Software Foundation; either version 2 of the License, or (at your 14 * option) any later version. 15 * 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 * 675 Mass Ave, Cambridge, MA 02139, USA. 19 * 20 */ 21#include <linux/module.h> 22#include <linux/init.h> 23#include <linux/dmaengine.h> 24#include <linux/slab.h> 25#include <sound/pcm.h> 26#include <sound/pcm_params.h> 27#include <sound/soc.h> 28 29#include <sound/dmaengine_pcm.h> 30 31struct dmaengine_pcm_runtime_data { 32 struct dma_chan *dma_chan; 33 34 unsigned int pos; 35 36 void *data; 37}; 38 39static inline struct dmaengine_pcm_runtime_data *substream_to_prtd( 40 const struct snd_pcm_substream *substream) 41{ 42 return substream->runtime->private_data; 43} 44 45/** 46 * snd_dmaengine_pcm_set_data - Set dmaengine substream private data 47 * @substream: PCM substream 48 * @data: Data to set 49 */ 50void snd_dmaengine_pcm_set_data(struct snd_pcm_substream *substream, void *data) 51{ 52 struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); 53 54 prtd->data = data; 55} 56EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_set_data); 57 58/** 59 * snd_dmaengine_pcm_get_data - Get dmaeinge substream private data 60 * @substream: PCM substream 61 * 62 * Returns the data previously set with snd_dmaengine_pcm_set_data 63 */ 64void *snd_dmaengine_pcm_get_data(struct snd_pcm_substream *substream) 65{ 66 struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); 67 68 return prtd->data; 69} 70EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_get_data); 71 72struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream) 73{ 74 struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); 75 76 return prtd->dma_chan; 77} 78EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_get_chan); 79 80/** 81 * snd_hwparams_to_dma_slave_config - Convert hw_params to dma_slave_config 82 * @substream: PCM substream 83 * @params: hw_params 84 * @slave_config: DMA slave config 85 * 86 * This function can be used to initialize a dma_slave_config from a substream 87 * and hw_params in a dmaengine based PCM driver implementation. 88 */ 89int snd_hwparams_to_dma_slave_config(const struct snd_pcm_substream *substream, 90 const struct snd_pcm_hw_params *params, 91 struct dma_slave_config *slave_config) 92{ 93 enum dma_slave_buswidth buswidth; 94 95 switch (params_format(params)) { 96 case SNDRV_PCM_FORMAT_S8: 97 buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE; 98 break; 99 case SNDRV_PCM_FORMAT_S16_LE: 100 buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; 101 break; 102 case SNDRV_PCM_FORMAT_S18_3LE: 103 case SNDRV_PCM_FORMAT_S20_3LE: 104 case SNDRV_PCM_FORMAT_S24_LE: 105 case SNDRV_PCM_FORMAT_S32_LE: 106 buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; 107 break; 108 default: 109 return -EINVAL; 110 } 111 112 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 113 slave_config->direction = DMA_MEM_TO_DEV; 114 slave_config->dst_addr_width = buswidth; 115 } else { 116 slave_config->direction = DMA_DEV_TO_MEM; 117 slave_config->src_addr_width = buswidth; 118 } 119 120 return 0; 121} 122EXPORT_SYMBOL_GPL(snd_hwparams_to_dma_slave_config); 123 124static void dmaengine_pcm_dma_complete(void *arg) 125{ 126 struct snd_pcm_substream *substream = arg; 127 struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); 128 129 prtd->pos += snd_pcm_lib_period_bytes(substream); 130 if (prtd->pos >= snd_pcm_lib_buffer_bytes(substream)) 131 prtd->pos = 0; 132 133 snd_pcm_period_elapsed(substream); 134} 135 136static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream) 137{ 138 struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); 139 struct dma_chan *chan = prtd->dma_chan; 140 struct dma_async_tx_descriptor *desc; 141 enum dma_transfer_direction direction; 142 143 direction = snd_pcm_substream_to_dma_direction(substream); 144 145 prtd->pos = 0; 146 desc = dmaengine_prep_dma_cyclic(chan, 147 substream->runtime->dma_addr, 148 snd_pcm_lib_buffer_bytes(substream), 149 snd_pcm_lib_period_bytes(substream), direction); 150 151 if (!desc) 152 return -ENOMEM; 153 154 desc->callback = dmaengine_pcm_dma_complete; 155 desc->callback_param = substream; 156 dmaengine_submit(desc); 157 158 return 0; 159} 160 161/** 162 * snd_dmaengine_pcm_trigger - dmaengine based PCM trigger implementation 163 * @substream: PCM substream 164 * @cmd: Trigger command 165 * 166 * Returns 0 on success, a negative error code otherwise. 167 * 168 * This function can be used as the PCM trigger callback for dmaengine based PCM 169 * driver implementations. 170 */ 171int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 172{ 173 struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); 174 int ret; 175 176 switch (cmd) { 177 case SNDRV_PCM_TRIGGER_START: 178 ret = dmaengine_pcm_prepare_and_submit(substream); 179 if (ret) 180 return ret; 181 dma_async_issue_pending(prtd->dma_chan); 182 break; 183 case SNDRV_PCM_TRIGGER_RESUME: 184 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 185 dmaengine_resume(prtd->dma_chan); 186 break; 187 case SNDRV_PCM_TRIGGER_SUSPEND: 188 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 189 dmaengine_pause(prtd->dma_chan); 190 break; 191 case SNDRV_PCM_TRIGGER_STOP: 192 dmaengine_terminate_all(prtd->dma_chan); 193 break; 194 default: 195 return -EINVAL; 196 } 197 198 return 0; 199} 200EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_trigger); 201 202/** 203 * snd_dmaengine_pcm_pointer - dmaengine based PCM pointer implementation 204 * @substream: PCM substream 205 * 206 * This function can be used as the PCM pointer callback for dmaengine based PCM 207 * driver implementations. 208 */ 209snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream) 210{ 211 struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); 212 return bytes_to_frames(substream->runtime, prtd->pos); 213} 214EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer); 215 216static int dmaengine_pcm_request_channel(struct dmaengine_pcm_runtime_data *prtd, 217 dma_filter_fn filter_fn, void *filter_data) 218{ 219 dma_cap_mask_t mask; 220 221 dma_cap_zero(mask); 222 dma_cap_set(DMA_SLAVE, mask); 223 dma_cap_set(DMA_CYCLIC, mask); 224 prtd->dma_chan = dma_request_channel(mask, filter_fn, filter_data); 225 226 if (!prtd->dma_chan) 227 return -ENXIO; 228 229 return 0; 230} 231 232/** 233 * snd_dmaengine_pcm_open - Open a dmaengine based PCM substream 234 * @substream: PCM substream 235 * @filter_fn: Filter function used to request the DMA channel 236 * @filter_data: Data passed to the DMA filter function 237 * 238 * Returns 0 on success, a negative error code otherwise. 239 * 240 * This function will request a DMA channel using the passed filter function and 241 * data. The function should usually be called from the pcm open callback. 242 * 243 * Note that this function will use private_data field of the substream's 244 * runtime. So it is not availabe to your pcm driver implementation. If you need 245 * to keep additional data attached to a substream use 246 * snd_dmaeinge_pcm_{set,get}_data. 247 */ 248int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream, 249 dma_filter_fn filter_fn, void *filter_data) 250{ 251 struct dmaengine_pcm_runtime_data *prtd; 252 int ret; 253 254 ret = snd_pcm_hw_constraint_integer(substream->runtime, 255 SNDRV_PCM_HW_PARAM_PERIODS); 256 if (ret < 0) 257 return ret; 258 259 prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); 260 if (!prtd) 261 return -ENOMEM; 262 263 ret = dmaengine_pcm_request_channel(prtd, filter_fn, filter_data); 264 if (ret < 0) { 265 kfree(prtd); 266 return ret; 267 } 268 269 substream->runtime->private_data = prtd; 270 271 return 0; 272} 273EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open); 274 275/** 276 * snd_dmaengine_pcm_close - Close a dmaengine based PCM substream 277 * @substream: PCM substream 278 */ 279int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream) 280{ 281 struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); 282 283 dma_release_channel(prtd->dma_chan); 284 kfree(prtd); 285 286 return 0; 287} 288EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close);