diff options
-rw-r--r-- | sound/soc/samsung/dma.c | 307 |
1 files changed, 44 insertions, 263 deletions
diff --git a/sound/soc/samsung/dma.c b/sound/soc/samsung/dma.c index bf3d80234ed..b39820b1461 100644 --- a/sound/soc/samsung/dma.c +++ b/sound/soc/samsung/dma.c @@ -20,6 +20,9 @@ #include <sound/soc.h> #include <sound/pcm_params.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/dmaengine_pcm.h> #include <asm/dma.h> #include <mach/hardware.h> @@ -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, }; |