From 2a0d7115a463f3313fd242764e43e512a60c4b75 Mon Sep 17 00:00:00 2001 From: Huang Chao Date: Tue, 1 Jul 2014 09:41:12 +0800 Subject: [PATCH] ASoC: samsung: dma: Replace the private pcm operations with the generic ones This patch reshapes the whole samsung platform driver which is composed by samsung private dmaengine pcm operations, replacing them with the generic asoc dmaengine pcm operations implemented by alsa soc subsystem. The dma channel is requested in open callback, and will be released when the pcm stream is closed. And the pcm trigger operations are also changed to the asoc generic dmaengine based pcm trigger implementation. Change-Id: Ib70b973720e5d57a40c7d583b74c11b971b4776c Signed-off-by: Huang Chao --- sound/soc/samsung/dma.c | 307 +++++++----------------------------------------- 1 file changed, 44 insertions(+), 263 deletions(-) diff --git a/sound/soc/samsung/dma.c b/sound/soc/samsung/dma.c index bf3d802..b39820b 100644 --- a/sound/soc/samsung/dma.c +++ b/sound/soc/samsung/dma.c @@ -20,6 +20,9 @@ #include #include +#include +#include +#include #include #include @@ -27,9 +30,6 @@ #include "dma.h" -#define ST_RUNNING (1<<0) -#define ST_OPENED (1<<1) - static const struct snd_pcm_hardware dma_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | @@ -51,300 +51,82 @@ static const struct snd_pcm_hardware dma_hardware = { .fifo_size = 32, }; -struct runtime_data { - spinlock_t lock; - int state; - unsigned int dma_loaded; - unsigned int dma_period; - dma_addr_t dma_start; - dma_addr_t dma_pos; - dma_addr_t dma_end; - struct s3c_dma_params *params; -}; - -static void audio_buffdone(void *data); - -/* dma_enqueue - * - * place a dma buffer onto the queue for the dma system - * to handle. - */ -static void dma_enqueue(struct snd_pcm_substream *substream) -{ - struct runtime_data *prtd = substream->runtime->private_data; - dma_addr_t pos = prtd->dma_pos; - unsigned int limit; - struct samsung_dma_prep dma_info; - - pr_debug("Entered %s\n", __func__); - - limit = (prtd->dma_end - prtd->dma_start) / prtd->dma_period; - - pr_debug("%s: loaded %d, limit %d\n", - __func__, prtd->dma_loaded, limit); - - dma_info.cap = (samsung_dma_has_circular() ? DMA_CYCLIC : DMA_SLAVE); - dma_info.direction = - (substream->stream == SNDRV_PCM_STREAM_PLAYBACK - ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM); - dma_info.fp = audio_buffdone; - dma_info.fp_param = substream; - dma_info.period = prtd->dma_period; - dma_info.len = prtd->dma_period*limit; - - if (dma_info.cap == DMA_CYCLIC) { - dma_info.buf = pos; - prtd->params->ops->prepare(prtd->params->ch, &dma_info); - prtd->dma_loaded += limit; - return; - } - - while (prtd->dma_loaded < limit) { - pr_debug("dma_loaded: %d\n", prtd->dma_loaded); - - if ((pos + dma_info.period) > prtd->dma_end) { - dma_info.period = prtd->dma_end - pos; - pr_debug("%s: corrected dma len %ld\n", - __func__, dma_info.period); - } - - dma_info.buf = pos; - prtd->params->ops->prepare(prtd->params->ch, &dma_info); - - prtd->dma_loaded++; - pos += prtd->dma_period; - if (pos >= prtd->dma_end) - pos = prtd->dma_start; - } - - prtd->dma_pos = pos; -} - -static void audio_buffdone(void *data) -{ - struct snd_pcm_substream *substream = data; - struct runtime_data *prtd = substream->runtime->private_data; - - pr_debug("Entered %s\n", __func__); - - if (prtd->state & ST_RUNNING) { - prtd->dma_pos += prtd->dma_period; - if (prtd->dma_pos >= prtd->dma_end) - prtd->dma_pos = prtd->dma_start; - - if (substream) - snd_pcm_period_elapsed(substream); - - spin_lock(&prtd->lock); - if (!samsung_dma_has_circular()) { - prtd->dma_loaded--; - dma_enqueue(substream); - } - spin_unlock(&prtd->lock); - } -} - +/* This might get called for several times by oss emulation */ static int dma_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct runtime_data *prtd = runtime->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data; - unsigned long totbytes = params_buffer_bytes(params); - struct s3c_dma_params *dma = - snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - struct samsung_dma_req req; - struct samsung_dma_config config; + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_dmaengine_dai_dma_data *dma; + struct dma_slave_config slave_config; + struct dma_chan *chan; + int ret; pr_debug("Entered %s\n", __func__); + dma = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + /* return if this is a bufferless transfer e.g. * codec <--> BT codec or GSM modem -- lg FIXME */ if (!dma) return 0; - /* this may get called several times by oss emulation - * with different params -HW */ - if (prtd->params == NULL) { - /* prepare DMA */ - prtd->params = dma; - - pr_debug("params %p, client %p, channel %d\n", prtd->params, - prtd->params->client, prtd->params->channel); - - prtd->params->ops = samsung_dma_get_ops(); - - req.cap = (samsung_dma_has_circular() ? - DMA_CYCLIC : DMA_SLAVE); - req.client = prtd->params->client; - config.direction = - (substream->stream == SNDRV_PCM_STREAM_PLAYBACK - ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM); - config.width = prtd->params->dma_size; - config.fifo = prtd->params->dma_addr; - prtd->params->ch = prtd->params->ops->request( - prtd->params->channel, &req, rtd->cpu_dai->dev, - prtd->params->ch_name); - prtd->params->ops->config(prtd->params->ch, &config); - } - snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + runtime->dma_bytes = params_buffer_bytes(params); - runtime->dma_bytes = totbytes; + chan = snd_dmaengine_pcm_get_chan(substream); + if (!chan) + return -ENODEV; - spin_lock_irq(&prtd->lock); - prtd->dma_loaded = 0; - prtd->dma_period = params_period_bytes(params); - prtd->dma_start = runtime->dma_addr; - prtd->dma_pos = prtd->dma_start; - prtd->dma_end = prtd->dma_start + totbytes; - spin_unlock_irq(&prtd->lock); + ret = snd_dmaengine_pcm_prepare_slave_config(substream, params, + &slave_config); + if (ret) + return ret; - return 0; + return dmaengine_slave_config(chan, &slave_config); } static int dma_hw_free(struct snd_pcm_substream *substream) { - struct runtime_data *prtd = substream->runtime->private_data; - pr_debug("Entered %s\n", __func__); snd_pcm_set_runtime_buffer(substream, NULL); - if (prtd->params) { - prtd->params->ops->flush(prtd->params->ch); - prtd->params->ops->release(prtd->params->ch, - prtd->params->client); - prtd->params = NULL; - } - return 0; } -static int dma_prepare(struct snd_pcm_substream *substream) -{ - struct runtime_data *prtd = substream->runtime->private_data; - int ret = 0; - - pr_debug("Entered %s\n", __func__); - - /* return if this is a bufferless transfer e.g. - * codec <--> BT codec or GSM modem -- lg FIXME */ - if (!prtd->params) - return 0; - - /* flush the DMA channel */ - prtd->params->ops->flush(prtd->params->ch); - - prtd->dma_loaded = 0; - prtd->dma_pos = prtd->dma_start; - - /* enqueue dma buffers */ - dma_enqueue(substream); - - return ret; -} - -static int dma_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct runtime_data *prtd = substream->runtime->private_data; - int ret = 0; - - pr_debug("Entered %s\n", __func__); - - spin_lock(&prtd->lock); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - prtd->state |= ST_RUNNING; - prtd->params->ops->trigger(prtd->params->ch); - break; - - case SNDRV_PCM_TRIGGER_STOP: - prtd->state &= ~ST_RUNNING; - prtd->params->ops->stop(prtd->params->ch); - break; - - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - prtd->state &= ~ST_RUNNING; - prtd->params->ops->pause(prtd->params->ch); - break; - - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - prtd->state |= ST_RUNNING; - prtd->params->ops->resume(prtd->params->ch); - break; - - default: - ret = -EINVAL; - break; - } - - spin_unlock(&prtd->lock); - - return ret; -} - -static snd_pcm_uframes_t -dma_pointer(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct runtime_data *prtd = runtime->private_data; - unsigned long res; - - pr_debug("Entered %s\n", __func__); - - res = prtd->dma_pos - prtd->dma_start; - - pr_debug("Pointer offset: %lu\n", res); - - /* we seem to be getting the odd error from the pcm library due - * to out-of-bounds pointers. this is maybe due to the dma engine - * not having loaded the new values for the channel before being - * called... (todo - fix ) - */ - - if (res >= snd_pcm_lib_buffer_bytes(substream)) { - if (res == snd_pcm_lib_buffer_bytes(substream)) - res = 0; - } - - return bytes_to_frames(substream->runtime, res); -} +static const char * const dma_chan_names[] = { + [SNDRV_PCM_STREAM_PLAYBACK] = "tx", + [SNDRV_PCM_STREAM_CAPTURE] = "rx", +}; static int dma_open(struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct runtime_data *prtd; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct dma_chan *chan; pr_debug("Entered %s\n", __func__); - snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); snd_soc_set_runtime_hwparams(substream, &dma_hardware); - prtd = kzalloc(sizeof(struct runtime_data), GFP_KERNEL); - if (prtd == NULL) + chan = kzalloc(sizeof(struct dma_chan), GFP_KERNEL); + if (!chan) return -ENOMEM; - spin_lock_init(&prtd->lock); - - runtime->private_data = prtd; - return 0; -} - -static int dma_close(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct runtime_data *prtd = runtime->private_data; - - pr_debug("Entered %s\n", __func__); - - if (!prtd) - pr_debug("dma_close called with prtd == NULL\n"); - - kfree(prtd); + /* Request slave dma channel according to playback/capture stream */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + chan = dma_request_slave_channel(rtd->cpu_dai->dev, + dma_chan_names[0]); + else + chan = dma_request_slave_channel(rtd->cpu_dai->dev, + dma_chan_names[1]); + if (!chan) { + pr_err("%s: failed to request dma chan\n", __func__); + kfree(chan); + return -EFAULT; + } - return 0; + return snd_dmaengine_pcm_open(substream, chan); } static int dma_mmap(struct snd_pcm_substream *substream, @@ -362,13 +144,12 @@ static int dma_mmap(struct snd_pcm_substream *substream, static struct snd_pcm_ops dma_ops = { .open = dma_open, - .close = dma_close, + .close = snd_dmaengine_pcm_close_release_chan, .ioctl = snd_pcm_lib_ioctl, .hw_params = dma_hw_params, .hw_free = dma_hw_free, - .prepare = dma_prepare, - .trigger = dma_trigger, - .pointer = dma_pointer, + .trigger = snd_dmaengine_pcm_trigger, + .pointer = snd_dmaengine_pcm_pointer_no_residue, .mmap = dma_mmap, }; -- 2.7.4