| 
									
										
										
										
											2012-02-22 10:49:08 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  *  Copyright (C) 2012, Analog Devices Inc. | 
					
						
							|  |  |  |  *	Author: Lars-Peter Clausen <lars@metafoo.de> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  Based on: | 
					
						
							|  |  |  |  *	imx-pcm-dma-mx2.c, Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de> | 
					
						
							|  |  |  |  *	mxs-pcm.c, Copyright (C) 2011 Freescale Semiconductor, Inc. | 
					
						
							|  |  |  |  *	ep93xx-pcm.c, Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org> | 
					
						
							|  |  |  |  *		      Copyright (C) 2006 Applied Data Systems | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  This program is free software; you can redistribute it and/or modify it | 
					
						
							|  |  |  |  *  under  the terms of the GNU General  Public License as published by the | 
					
						
							|  |  |  |  *  Free Software Foundation;  either version 2 of the License, or (at your | 
					
						
							|  |  |  |  *  option) any later version. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  You should have received a copy of the GNU General Public License along | 
					
						
							|  |  |  |  *  with this program; if not, write to the Free Software Foundation, Inc., | 
					
						
							|  |  |  |  *  675 Mass Ave, Cambridge, MA 02139, USA. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | #include <linux/init.h>
 | 
					
						
							|  |  |  | #include <linux/dmaengine.h>
 | 
					
						
							|  |  |  | #include <linux/slab.h>
 | 
					
						
							|  |  |  | #include <sound/pcm.h>
 | 
					
						
							|  |  |  | #include <sound/pcm_params.h>
 | 
					
						
							|  |  |  | #include <sound/soc.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <sound/dmaengine_pcm.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct dmaengine_pcm_runtime_data { | 
					
						
							|  |  |  | 	struct dma_chan *dma_chan; | 
					
						
							| 
									
										
										
										
											2012-06-11 20:11:42 +02:00
										 |  |  | 	dma_cookie_t cookie; | 
					
						
							| 
									
										
										
										
											2012-02-22 10:49:08 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	unsigned int pos; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	void *data; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline struct dmaengine_pcm_runtime_data *substream_to_prtd( | 
					
						
							|  |  |  | 	const struct snd_pcm_substream *substream) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return substream->runtime->private_data; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * snd_dmaengine_pcm_set_data - Set dmaengine substream private data | 
					
						
							|  |  |  |  * @substream: PCM substream | 
					
						
							|  |  |  |  * @data: Data to set | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void snd_dmaengine_pcm_set_data(struct snd_pcm_substream *substream, void *data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	prtd->data = data; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_set_data); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * snd_dmaengine_pcm_get_data - Get dmaeinge substream private data | 
					
						
							|  |  |  |  * @substream: PCM substream | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Returns the data previously set with snd_dmaengine_pcm_set_data | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void *snd_dmaengine_pcm_get_data(struct snd_pcm_substream *substream) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return prtd->data; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_get_data); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct dma_chan *snd_dmaengine_pcm_get_chan(struct snd_pcm_substream *substream) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return prtd->dma_chan; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_get_chan); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * snd_hwparams_to_dma_slave_config - Convert hw_params to dma_slave_config | 
					
						
							|  |  |  |  * @substream: PCM substream | 
					
						
							|  |  |  |  * @params: hw_params | 
					
						
							|  |  |  |  * @slave_config: DMA slave config | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This function can be used to initialize a dma_slave_config from a substream | 
					
						
							|  |  |  |  * and hw_params in a dmaengine based PCM driver implementation. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int snd_hwparams_to_dma_slave_config(const struct snd_pcm_substream *substream, | 
					
						
							|  |  |  | 	const struct snd_pcm_hw_params *params, | 
					
						
							|  |  |  | 	struct dma_slave_config *slave_config) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	enum dma_slave_buswidth buswidth; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (params_format(params)) { | 
					
						
							|  |  |  | 	case SNDRV_PCM_FORMAT_S8: | 
					
						
							|  |  |  | 		buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case SNDRV_PCM_FORMAT_S16_LE: | 
					
						
							|  |  |  | 		buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case SNDRV_PCM_FORMAT_S18_3LE: | 
					
						
							|  |  |  | 	case SNDRV_PCM_FORMAT_S20_3LE: | 
					
						
							|  |  |  | 	case SNDRV_PCM_FORMAT_S24_LE: | 
					
						
							|  |  |  | 	case SNDRV_PCM_FORMAT_S32_LE: | 
					
						
							|  |  |  | 		buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 
					
						
							|  |  |  | 		slave_config->direction = DMA_MEM_TO_DEV; | 
					
						
							|  |  |  | 		slave_config->dst_addr_width = buswidth; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		slave_config->direction = DMA_DEV_TO_MEM; | 
					
						
							|  |  |  | 		slave_config->src_addr_width = buswidth; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(snd_hwparams_to_dma_slave_config); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void dmaengine_pcm_dma_complete(void *arg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct snd_pcm_substream *substream = arg; | 
					
						
							|  |  |  | 	struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	prtd->pos += snd_pcm_lib_period_bytes(substream); | 
					
						
							|  |  |  | 	if (prtd->pos >= snd_pcm_lib_buffer_bytes(substream)) | 
					
						
							|  |  |  | 		prtd->pos = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	snd_pcm_period_elapsed(substream); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); | 
					
						
							|  |  |  | 	struct dma_chan *chan = prtd->dma_chan; | 
					
						
							|  |  |  | 	struct dma_async_tx_descriptor *desc; | 
					
						
							|  |  |  | 	enum dma_transfer_direction direction; | 
					
						
							| 
									
										
										
										
											2012-09-24 10:58:04 +03:00
										 |  |  | 	unsigned long flags = DMA_CTRL_ACK; | 
					
						
							| 
									
										
										
										
											2012-02-22 10:49:08 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	direction = snd_pcm_substream_to_dma_direction(substream); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-24 10:58:04 +03:00
										 |  |  | 	if (!substream->runtime->no_period_wakeup) | 
					
						
							|  |  |  | 		flags |= DMA_PREP_INTERRUPT; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-03-05 14:02:14 +01:00
										 |  |  | 	prtd->pos = 0; | 
					
						
							| 
									
										
										
										
											2012-03-26 17:28:08 +05:30
										 |  |  | 	desc = dmaengine_prep_dma_cyclic(chan, | 
					
						
							| 
									
										
										
										
											2012-02-22 10:49:08 +01:00
										 |  |  | 		substream->runtime->dma_addr, | 
					
						
							|  |  |  | 		snd_pcm_lib_buffer_bytes(substream), | 
					
						
							| 
									
										
										
										
											2012-09-24 10:58:04 +03:00
										 |  |  | 		snd_pcm_lib_period_bytes(substream), direction, flags); | 
					
						
							| 
									
										
										
										
											2012-02-22 10:49:08 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!desc) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	desc->callback = dmaengine_pcm_dma_complete; | 
					
						
							|  |  |  | 	desc->callback_param = substream; | 
					
						
							| 
									
										
										
										
											2012-06-11 20:11:42 +02:00
										 |  |  | 	prtd->cookie = dmaengine_submit(desc); | 
					
						
							| 
									
										
										
										
											2012-02-22 10:49:08 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * snd_dmaengine_pcm_trigger - dmaengine based PCM trigger implementation | 
					
						
							|  |  |  |  * @substream: PCM substream | 
					
						
							|  |  |  |  * @cmd: Trigger command | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Returns 0 on success, a negative error code otherwise. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This function can be used as the PCM trigger callback for dmaengine based PCM | 
					
						
							|  |  |  |  * driver implementations. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (cmd) { | 
					
						
							|  |  |  | 	case SNDRV_PCM_TRIGGER_START: | 
					
						
							|  |  |  | 		ret = dmaengine_pcm_prepare_and_submit(substream); | 
					
						
							|  |  |  | 		if (ret) | 
					
						
							|  |  |  | 			return ret; | 
					
						
							|  |  |  | 		dma_async_issue_pending(prtd->dma_chan); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case SNDRV_PCM_TRIGGER_RESUME: | 
					
						
							|  |  |  | 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | 
					
						
							|  |  |  | 		dmaengine_resume(prtd->dma_chan); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case SNDRV_PCM_TRIGGER_SUSPEND: | 
					
						
							|  |  |  | 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | 
					
						
							|  |  |  | 		dmaengine_pause(prtd->dma_chan); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case SNDRV_PCM_TRIGGER_STOP: | 
					
						
							|  |  |  | 		dmaengine_terminate_all(prtd->dma_chan); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_trigger); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2012-06-11 20:11:41 +02:00
										 |  |  |  * snd_dmaengine_pcm_pointer_no_residue - dmaengine based PCM pointer implementation | 
					
						
							| 
									
										
										
										
											2012-02-22 10:49:08 +01:00
										 |  |  |  * @substream: PCM substream | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2012-06-11 20:11:41 +02:00
										 |  |  |  * This function is deprecated and should not be used by new drivers, as its | 
					
						
							|  |  |  |  * results may be unreliable. | 
					
						
							| 
									
										
										
										
											2012-02-22 10:49:08 +01:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-06-11 20:11:41 +02:00
										 |  |  | snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream *substream) | 
					
						
							| 
									
										
										
										
											2012-02-22 10:49:08 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); | 
					
						
							|  |  |  | 	return bytes_to_frames(substream->runtime, prtd->pos); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2012-06-11 20:11:41 +02:00
										 |  |  | EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer_no_residue); | 
					
						
							| 
									
										
										
										
											2012-02-22 10:49:08 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-11 20:11:42 +02:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * snd_dmaengine_pcm_pointer - dmaengine based PCM pointer implementation | 
					
						
							|  |  |  |  * @substream: PCM substream | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This function can be used as the PCM pointer callback for dmaengine based PCM | 
					
						
							|  |  |  |  * driver implementations. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); | 
					
						
							|  |  |  | 	struct dma_tx_state state; | 
					
						
							|  |  |  | 	enum dma_status status; | 
					
						
							|  |  |  | 	unsigned int buf_size; | 
					
						
							|  |  |  | 	unsigned int pos = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	status = dmaengine_tx_status(prtd->dma_chan, prtd->cookie, &state); | 
					
						
							|  |  |  | 	if (status == DMA_IN_PROGRESS || status == DMA_PAUSED) { | 
					
						
							|  |  |  | 		buf_size = snd_pcm_lib_buffer_bytes(substream); | 
					
						
							|  |  |  | 		if (state.residue > 0 && state.residue <= buf_size) | 
					
						
							|  |  |  | 			pos = buf_size - state.residue; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return bytes_to_frames(substream->runtime, pos); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-02-22 10:49:08 +01:00
										 |  |  | static int dmaengine_pcm_request_channel(struct dmaengine_pcm_runtime_data *prtd, | 
					
						
							|  |  |  | 	dma_filter_fn filter_fn, void *filter_data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	dma_cap_mask_t mask; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dma_cap_zero(mask); | 
					
						
							|  |  |  | 	dma_cap_set(DMA_SLAVE, mask); | 
					
						
							|  |  |  | 	dma_cap_set(DMA_CYCLIC, mask); | 
					
						
							|  |  |  | 	prtd->dma_chan = dma_request_channel(mask, filter_fn, filter_data); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!prtd->dma_chan) | 
					
						
							|  |  |  | 		return -ENXIO; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * snd_dmaengine_pcm_open - Open a dmaengine based PCM substream | 
					
						
							|  |  |  |  * @substream: PCM substream | 
					
						
							|  |  |  |  * @filter_fn: Filter function used to request the DMA channel | 
					
						
							|  |  |  |  * @filter_data: Data passed to the DMA filter function | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Returns 0 on success, a negative error code otherwise. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This function will request a DMA channel using the passed filter function and | 
					
						
							|  |  |  |  * data. The function should usually be called from the pcm open callback. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Note that this function will use private_data field of the substream's | 
					
						
							|  |  |  |  * runtime. So it is not availabe to your pcm driver implementation. If you need | 
					
						
							|  |  |  |  * to keep additional data attached to a substream use | 
					
						
							| 
									
										
										
										
											2012-06-25 09:28:46 +02:00
										 |  |  |  * snd_dmaengine_pcm_{set,get}_data. | 
					
						
							| 
									
										
										
										
											2012-02-22 10:49:08 +01:00
										 |  |  |  */ | 
					
						
							|  |  |  | int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream, | 
					
						
							|  |  |  | 	dma_filter_fn filter_fn, void *filter_data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct dmaengine_pcm_runtime_data *prtd; | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = snd_pcm_hw_constraint_integer(substream->runtime, | 
					
						
							|  |  |  | 					    SNDRV_PCM_HW_PARAM_PERIODS); | 
					
						
							|  |  |  | 	if (ret < 0) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); | 
					
						
							|  |  |  | 	if (!prtd) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = dmaengine_pcm_request_channel(prtd, filter_fn, filter_data); | 
					
						
							|  |  |  | 	if (ret < 0) { | 
					
						
							|  |  |  | 		kfree(prtd); | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	substream->runtime->private_data = prtd; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * snd_dmaengine_pcm_close - Close a dmaengine based PCM substream | 
					
						
							|  |  |  |  * @substream: PCM substream | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dma_release_channel(prtd->dma_chan); | 
					
						
							|  |  |  | 	kfree(prtd); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close); | 
					
						
							| 
									
										
										
										
											2012-11-22 13:31:10 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | MODULE_LICENSE("GPL"); |