From patchwork Mon Aug 12 08:42:37 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Mack X-Patchwork-Id: 2842890 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 73A1DBF546 for ; Mon, 12 Aug 2013 08:45:26 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id C7D1620284 for ; Mon, 12 Aug 2013 08:45:20 +0000 (UTC) Received: from casper.infradead.org (casper.infradead.org [85.118.1.10]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id A66F5201B5 for ; Mon, 12 Aug 2013 08:45:14 +0000 (UTC) Received: from merlin.infradead.org ([2001:4978:20e::2]) by casper.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1V8njm-0001kN-UV; Mon, 12 Aug 2013 08:44:28 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1V8njS-0002Ae-9c; Mon, 12 Aug 2013 08:44:06 +0000 Received: from svenfoo.org ([82.94.215.22] helo=mail.zonque.de) by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1V8nic-00024x-SG for linux-arm-kernel@lists.infradead.org; Mon, 12 Aug 2013 08:43:38 +0000 Received: from localhost (localhost [127.0.0.1]) by mail.zonque.de (Postfix) with ESMTP id D32D7C555E; Mon, 12 Aug 2013 10:42:48 +0200 (CEST) Received: from mail.zonque.de ([127.0.0.1]) by localhost (rambrand.bugwerft.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id OGsIqdnATqF1; Mon, 12 Aug 2013 10:42:48 +0200 (CEST) Received: from tamtam.fritz.box (p5DDC63EC.dip0.t-ipconnect.de [93.220.99.236]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.zonque.de (Postfix) with ESMTPSA id E08EEC555C; Mon, 12 Aug 2013 10:42:47 +0200 (CEST) From: Daniel Mack To: broonie@kernel.org, linux-arm-kernel@lists.infradead.org, alsa-devel@alsa-project.org Subject: [PATCH 1/5] ALSA: move dmaengine implementation from ASoC to ALSA core Date: Mon, 12 Aug 2013 10:42:37 +0200 Message-Id: <1376296961-20564-2-git-send-email-zonque@gmail.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1376296961-20564-1-git-send-email-zonque@gmail.com> References: <1376296961-20564-1-git-send-email-zonque@gmail.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20130812_044315_470025_B30374E7 X-CRM114-Status: GOOD ( 29.58 ) X-Spam-Score: -0.2 (/) Cc: devicetree@vger.kernel.org, eric.y.miao@gmail.com, arnd@arndb.de, s.neumann@raumfeld.com, Daniel Mack , haojian.zhuang@linaro.org, rmk+kernel@arm.linux.org.uk X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-7.0 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, FREEMAIL_FROM,RCVD_IN_DNSWL_MED,RP_MATCHES_RCVD,UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP For the PXA DMA rework, we need the generic dmaengine implementation that currently lives in sound/soc for standalone (non-ASoC) AC'97 support. Move it to sound/core, and rename the Kconfig symbol. Signed-off-by: Daniel Mack --- sound/core/Kconfig | 3 + sound/core/Makefile | 3 + sound/core/pcm_dmaengine.c | 367 ++++++++++++++++++++++++++++++++++++++++++ sound/soc/Kconfig | 5 +- sound/soc/Makefile | 4 - sound/soc/omap/Kconfig | 2 +- sound/soc/pxa/Kconfig | 2 +- sound/soc/soc-dmaengine-pcm.c | 367 ------------------------------------------ sound/soc/spear/Kconfig | 2 +- 9 files changed, 377 insertions(+), 378 deletions(-) create mode 100644 sound/core/pcm_dmaengine.c delete mode 100644 sound/soc/soc-dmaengine-pcm.c diff --git a/sound/core/Kconfig b/sound/core/Kconfig index c0c2f57..94ce1c4 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -6,6 +6,9 @@ config SND_PCM tristate select SND_TIMER +config SND_DMAENGINE_PCM + bool + config SND_HWDEP tristate diff --git a/sound/core/Makefile b/sound/core/Makefile index 43d4117..5e890cf 100644 --- a/sound/core/Makefile +++ b/sound/core/Makefile @@ -13,6 +13,8 @@ snd-$(CONFIG_SND_JACK) += jack.o snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \ pcm_memory.o +snd-pcm-dmaengine-objs := pcm_dmaengine.o + snd-page-alloc-y := memalloc.o snd-page-alloc-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o @@ -30,6 +32,7 @@ obj-$(CONFIG_SND_TIMER) += snd-timer.o obj-$(CONFIG_SND_HRTIMER) += snd-hrtimer.o obj-$(CONFIG_SND_RTCTIMER) += snd-rtctimer.o obj-$(CONFIG_SND_PCM) += snd-pcm.o snd-page-alloc.o +obj-$(CONFIG_SND_DMAENGINE_PCM) += snd-pcm-dmaengine.o obj-$(CONFIG_SND_RAWMIDI) += snd-rawmidi.o obj-$(CONFIG_SND_OSSEMUL) += oss/ diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c new file mode 100644 index 0000000..aa924d9 --- /dev/null +++ b/sound/core/pcm_dmaengine.c @@ -0,0 +1,367 @@ +/* + * Copyright (C) 2012, Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Based on: + * imx-pcm-dma-mx2.c, Copyright 2009 Sascha Hauer + * mxs-pcm.c, Copyright (C) 2011 Freescale Semiconductor, Inc. + * ep93xx-pcm.c, Copyright (C) 2006 Lennert Buytenhek + * 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 +#include +#include +#include +#include +#include +#include + +#include + +struct dmaengine_pcm_runtime_data { + struct dma_chan *dma_chan; + dma_cookie_t cookie; + + unsigned int pos; +}; + +static inline struct dmaengine_pcm_runtime_data *substream_to_prtd( + const struct snd_pcm_substream *substream) +{ + return substream->runtime->private_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; + } + + slave_config->device_fc = false; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_hwparams_to_dma_slave_config); + +/** + * snd_dmaengine_pcm_set_config_from_dai_data() - Initializes a dma slave config + * using DAI DMA data. + * @substream: PCM substream + * @dma_data: DAI DMA data + * @slave_config: DMA slave configuration + * + * Initializes the {dst,src}_addr, {dst,src}_maxburst, {dst,src}_addr_width and + * slave_id fields of the DMA slave config from the same fields of the DAI DMA + * data struct. The src and dst fields will be initialized depending on the + * direction of the substream. If the substream is a playback stream the dst + * fields will be initialized, if it is a capture stream the src fields will be + * initialized. The {dst,src}_addr_width field will only be initialized if the + * addr_width field of the DAI DMA data struct is not equal to + * DMA_SLAVE_BUSWIDTH_UNDEFINED. + */ +void snd_dmaengine_pcm_set_config_from_dai_data( + const struct snd_pcm_substream *substream, + const struct snd_dmaengine_dai_dma_data *dma_data, + struct dma_slave_config *slave_config) +{ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + slave_config->dst_addr = dma_data->addr; + slave_config->dst_maxburst = dma_data->maxburst; + if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED) + slave_config->dst_addr_width = dma_data->addr_width; + } else { + slave_config->src_addr = dma_data->addr; + slave_config->src_maxburst = dma_data->maxburst; + if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED) + slave_config->src_addr_width = dma_data->addr_width; + } + + slave_config->slave_id = dma_data->slave_id; +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_set_config_from_dai_data); + +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; + unsigned long flags = DMA_CTRL_ACK; + + direction = snd_pcm_substream_to_dma_direction(substream); + + if (!substream->runtime->no_period_wakeup) + flags |= DMA_PREP_INTERRUPT; + + prtd->pos = 0; + desc = dmaengine_prep_dma_cyclic(chan, + substream->runtime->dma_addr, + snd_pcm_lib_buffer_bytes(substream), + snd_pcm_lib_period_bytes(substream), direction, flags); + + if (!desc) + return -ENOMEM; + + desc->callback = dmaengine_pcm_dma_complete; + desc->callback_param = substream; + prtd->cookie = dmaengine_submit(desc); + + 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); + +/** + * snd_dmaengine_pcm_pointer_no_residue - dmaengine based PCM pointer implementation + * @substream: PCM substream + * + * This function is deprecated and should not be used by new drivers, as its + * results may be unreliable. + */ +snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream *substream) +{ + struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + return bytes_to_frames(substream->runtime, prtd->pos); +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer_no_residue); + +/** + * 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); + +/** + * snd_dmaengine_pcm_request_channel - Request channel for the dmaengine PCM + * @filter_fn: Filter function used to request the DMA channel + * @filter_data: Data passed to the DMA filter function + * + * Returns NULL or the requested DMA channel. + * + * This function request a DMA channel for usage with dmaengine PCM. + */ +struct dma_chan *snd_dmaengine_pcm_request_channel(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); + + return dma_request_channel(mask, filter_fn, filter_data); +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_request_channel); + +/** + * snd_dmaengine_pcm_open - Open a dmaengine based PCM substream + * @substream: PCM substream + * @chan: DMA channel to use for data transfers + * + * Returns 0 on success, a negative error code otherwise. + * + * 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. + */ +int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream, + struct dma_chan *chan) +{ + struct dmaengine_pcm_runtime_data *prtd; + int ret; + + if (!chan) + return -ENXIO; + + 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; + + prtd->dma_chan = chan; + + substream->runtime->private_data = prtd; + + return 0; +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open); + +/** + * snd_dmaengine_pcm_open_request_chan - Open a dmaengine based PCM substream and request channel + * @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. + */ +int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream, + dma_filter_fn filter_fn, void *filter_data) +{ + return snd_dmaengine_pcm_open(substream, + snd_dmaengine_pcm_request_channel(filter_fn, filter_data)); +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open_request_chan); + +/** + * 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); + + kfree(prtd); + + return 0; +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close); + +/** + * snd_dmaengine_pcm_release_chan_close - Close a dmaengine based PCM substream and release channel + * @substream: PCM substream + * + * Releases the DMA channel associated with the PCM substream. + */ +int snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream *substream) +{ + struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); + + dma_release_channel(prtd->dma_chan); + + return snd_dmaengine_pcm_close(substream); +} +EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close_release_chan); + +MODULE_LICENSE("GPL"); diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 45eeaa9..5138b84 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -26,12 +26,9 @@ if SND_SOC config SND_SOC_AC97_BUS bool -config SND_SOC_DMAENGINE_PCM - bool - config SND_SOC_GENERIC_DMAENGINE_PCM bool - select SND_SOC_DMAENGINE_PCM + select SND_DMAENGINE_PCM # All the supported SoCs source "sound/soc/atmel/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index bc02614..61a64d2 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -1,10 +1,6 @@ snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o -ifneq ($(CONFIG_SND_SOC_DMAENGINE_PCM),) -snd-soc-core-objs += soc-dmaengine-pcm.o -endif - ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),) snd-soc-core-objs += soc-generic-dmaengine-pcm.o endif diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index 9f5d55e..accd0ff 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -1,7 +1,7 @@ config SND_OMAP_SOC tristate "SoC Audio for the Texas Instruments OMAP chips" depends on ARCH_OMAP && DMA_OMAP - select SND_SOC_DMAENGINE_PCM + select SND_DMAENGINE_PCM config SND_OMAP_SOC_DMIC tristate diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index b358094..4db74a0 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -11,7 +11,7 @@ config SND_PXA2XX_SOC config SND_MMP_SOC bool "Soc Audio for Marvell MMP chips" depends on ARCH_MMP - select SND_SOC_DMAENGINE_PCM + select SND_DMAENGINE_PCM select SND_ARM help Say Y if you want to add support for codecs attached to diff --git a/sound/soc/soc-dmaengine-pcm.c b/sound/soc/soc-dmaengine-pcm.c deleted file mode 100644 index aa924d9..0000000 --- a/sound/soc/soc-dmaengine-pcm.c +++ /dev/null @@ -1,367 +0,0 @@ -/* - * Copyright (C) 2012, Analog Devices Inc. - * Author: Lars-Peter Clausen - * - * Based on: - * imx-pcm-dma-mx2.c, Copyright 2009 Sascha Hauer - * mxs-pcm.c, Copyright (C) 2011 Freescale Semiconductor, Inc. - * ep93xx-pcm.c, Copyright (C) 2006 Lennert Buytenhek - * 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 -#include -#include -#include -#include -#include -#include - -#include - -struct dmaengine_pcm_runtime_data { - struct dma_chan *dma_chan; - dma_cookie_t cookie; - - unsigned int pos; -}; - -static inline struct dmaengine_pcm_runtime_data *substream_to_prtd( - const struct snd_pcm_substream *substream) -{ - return substream->runtime->private_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; - } - - slave_config->device_fc = false; - - return 0; -} -EXPORT_SYMBOL_GPL(snd_hwparams_to_dma_slave_config); - -/** - * snd_dmaengine_pcm_set_config_from_dai_data() - Initializes a dma slave config - * using DAI DMA data. - * @substream: PCM substream - * @dma_data: DAI DMA data - * @slave_config: DMA slave configuration - * - * Initializes the {dst,src}_addr, {dst,src}_maxburst, {dst,src}_addr_width and - * slave_id fields of the DMA slave config from the same fields of the DAI DMA - * data struct. The src and dst fields will be initialized depending on the - * direction of the substream. If the substream is a playback stream the dst - * fields will be initialized, if it is a capture stream the src fields will be - * initialized. The {dst,src}_addr_width field will only be initialized if the - * addr_width field of the DAI DMA data struct is not equal to - * DMA_SLAVE_BUSWIDTH_UNDEFINED. - */ -void snd_dmaengine_pcm_set_config_from_dai_data( - const struct snd_pcm_substream *substream, - const struct snd_dmaengine_dai_dma_data *dma_data, - struct dma_slave_config *slave_config) -{ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - slave_config->dst_addr = dma_data->addr; - slave_config->dst_maxburst = dma_data->maxburst; - if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED) - slave_config->dst_addr_width = dma_data->addr_width; - } else { - slave_config->src_addr = dma_data->addr; - slave_config->src_maxburst = dma_data->maxburst; - if (dma_data->addr_width != DMA_SLAVE_BUSWIDTH_UNDEFINED) - slave_config->src_addr_width = dma_data->addr_width; - } - - slave_config->slave_id = dma_data->slave_id; -} -EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_set_config_from_dai_data); - -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; - unsigned long flags = DMA_CTRL_ACK; - - direction = snd_pcm_substream_to_dma_direction(substream); - - if (!substream->runtime->no_period_wakeup) - flags |= DMA_PREP_INTERRUPT; - - prtd->pos = 0; - desc = dmaengine_prep_dma_cyclic(chan, - substream->runtime->dma_addr, - snd_pcm_lib_buffer_bytes(substream), - snd_pcm_lib_period_bytes(substream), direction, flags); - - if (!desc) - return -ENOMEM; - - desc->callback = dmaengine_pcm_dma_complete; - desc->callback_param = substream; - prtd->cookie = dmaengine_submit(desc); - - 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); - -/** - * snd_dmaengine_pcm_pointer_no_residue - dmaengine based PCM pointer implementation - * @substream: PCM substream - * - * This function is deprecated and should not be used by new drivers, as its - * results may be unreliable. - */ -snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream *substream) -{ - struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); - return bytes_to_frames(substream->runtime, prtd->pos); -} -EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer_no_residue); - -/** - * 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); - -/** - * snd_dmaengine_pcm_request_channel - Request channel for the dmaengine PCM - * @filter_fn: Filter function used to request the DMA channel - * @filter_data: Data passed to the DMA filter function - * - * Returns NULL or the requested DMA channel. - * - * This function request a DMA channel for usage with dmaengine PCM. - */ -struct dma_chan *snd_dmaengine_pcm_request_channel(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); - - return dma_request_channel(mask, filter_fn, filter_data); -} -EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_request_channel); - -/** - * snd_dmaengine_pcm_open - Open a dmaengine based PCM substream - * @substream: PCM substream - * @chan: DMA channel to use for data transfers - * - * Returns 0 on success, a negative error code otherwise. - * - * 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. - */ -int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream, - struct dma_chan *chan) -{ - struct dmaengine_pcm_runtime_data *prtd; - int ret; - - if (!chan) - return -ENXIO; - - 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; - - prtd->dma_chan = chan; - - substream->runtime->private_data = prtd; - - return 0; -} -EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open); - -/** - * snd_dmaengine_pcm_open_request_chan - Open a dmaengine based PCM substream and request channel - * @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. - */ -int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream, - dma_filter_fn filter_fn, void *filter_data) -{ - return snd_dmaengine_pcm_open(substream, - snd_dmaengine_pcm_request_channel(filter_fn, filter_data)); -} -EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open_request_chan); - -/** - * 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); - - kfree(prtd); - - return 0; -} -EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close); - -/** - * snd_dmaengine_pcm_release_chan_close - Close a dmaengine based PCM substream and release channel - * @substream: PCM substream - * - * Releases the DMA channel associated with the PCM substream. - */ -int snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream *substream) -{ - struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); - - dma_release_channel(prtd->dma_chan); - - return snd_dmaengine_pcm_close(substream); -} -EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close_release_chan); - -MODULE_LICENSE("GPL"); diff --git a/sound/soc/spear/Kconfig b/sound/soc/spear/Kconfig index 3567d73..0a53053 100644 --- a/sound/soc/spear/Kconfig +++ b/sound/soc/spear/Kconfig @@ -1,6 +1,6 @@ config SND_SPEAR_SOC tristate - select SND_SOC_DMAENGINE_PCM + select SND_DMAENGINE_PCM config SND_SPEAR_SPDIF_OUT tristate