summaryrefslogtreecommitdiff
path: root/sound/soc/ux500/ux500_pcm.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/ux500/ux500_pcm.c')
-rw-r--r--sound/soc/ux500/ux500_pcm.c238
1 files changed, 188 insertions, 50 deletions
diff --git a/sound/soc/ux500/ux500_pcm.c b/sound/soc/ux500/ux500_pcm.c
index 07e18135451..7616bbb09af 100644
--- a/sound/soc/ux500/ux500_pcm.c
+++ b/sound/soc/ux500/ux500_pcm.c
@@ -19,13 +19,15 @@
#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#include <plat/ste_dma40.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "ux500_pcm.h"
-#include "ux500_msp_dai.h"
static struct snd_pcm_hardware ux500_pcm_hw_playback = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
@@ -77,28 +79,8 @@ static const char *stream_str(struct snd_pcm_substream *substream)
return "Capture";
}
-static void ux500_pcm_dma_hw_free(struct device *dev,
- struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_dma_buffer *buf = runtime->dma_buffer_p;
-
- if (runtime->dma_area == NULL)
- return;
-
- if (buf != &substream->dma_buffer) {
- dma_free_coherent(
- buf->dev.dev,
- buf->bytes,
- buf->area,
- buf->addr);
- kfree(runtime->dma_buffer_p);
- }
-
- snd_pcm_set_runtime_buffer(substream, NULL);
-}
-
-void ux500_pcm_dma_eot_handler(void *data)
+static void
+ux500_pcm_dma_eot_handler(void *data)
{
struct snd_pcm_substream *substream = data;
struct snd_pcm_runtime *runtime;
@@ -106,18 +88,14 @@ void ux500_pcm_dma_eot_handler(void *data)
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *dai = rtd->cpu_dai;
- pr_debug("%s: MSP %d (%s): Enter.\n", __func__, dai->id, stream_str(substream));
+ pr_debug("%s: MSP %d (%s): Enter.\n", __func__,
+ dai->id,
+ stream_str(substream));
if (substream) {
runtime = substream->runtime;
private = substream->runtime->private_data;
- if (ux500_msp_dai_i2s_get_underrun_status(private->msp_id)) {
- private->no_of_underruns++;
- pr_debug("%s: Nr of underruns (%d)\n", __func__,
- private->no_of_underruns);
- }
-
/* calc the offset in the circular buffer */
private->offset += frames_to_bytes(runtime,
runtime->period_size);
@@ -127,7 +105,98 @@ void ux500_pcm_dma_eot_handler(void *data)
snd_pcm_period_elapsed(substream);
}
}
-EXPORT_SYMBOL(ux500_pcm_dma_eot_handler);
+
+static int
+ux500_pcm_dma_start(
+ struct snd_pcm_substream *substream,
+ dma_addr_t dma_addr,
+ int period_cnt,
+ size_t period_len,
+ int dai_idx,
+ int stream_id)
+{
+ dma_cookie_t status_submit;
+ int direction;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ struct ux500_pcm_private *private = runtime->private_data;
+ struct dma_async_tx_descriptor *cdesc;
+ struct ux500_pcm_dma_params *dma_params;
+
+ pr_debug("%s: Enter\n", __func__);
+
+ dma_params = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ direction = DMA_TO_DEVICE;
+ else
+ direction = DMA_FROM_DEVICE;
+
+ cdesc = private->pipeid->device->device_prep_dma_cyclic(
+ private->pipeid,
+ dma_addr,
+ period_cnt * period_len,
+ period_len,
+ direction);
+
+ if (IS_ERR(cdesc)) {
+ pr_err("%s: ERROR: device_prep_dma_cyclic failed (%ld)!\n",
+ __func__,
+ PTR_ERR(cdesc));
+ return -EINVAL;
+ }
+
+ cdesc->callback = ux500_pcm_dma_eot_handler;
+ cdesc->callback_param = substream;
+
+ status_submit = dmaengine_submit(cdesc);
+
+ if (dma_submit_error(status_submit)) {
+ pr_err("%s: ERROR: dmaengine_submit failed!\n", __func__);
+ return -EINVAL;
+ }
+
+ dma_async_issue_pending(private->pipeid);
+
+ return 0;
+}
+
+static void
+ux500_pcm_dma_stop(struct work_struct *work)
+{
+ struct ux500_pcm_private *private = container_of(work,
+ struct ux500_pcm_private, ws_stop);
+
+ if (private->pipeid != NULL) {
+ dmaengine_terminate_all(private->pipeid);
+ dma_release_channel(private->pipeid);
+ private->pipeid = NULL;
+ }
+}
+
+static void
+ux500_pcm_dma_hw_free(
+ struct device *dev,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_dma_buffer *buf = runtime->dma_buffer_p;
+
+ if (runtime->dma_area == NULL)
+ return;
+
+ if (buf != &substream->dma_buffer) {
+ dma_free_coherent(
+ buf->dev.dev,
+ buf->bytes,
+ buf->area,
+ buf->addr);
+ kfree(runtime->dma_buffer_p);
+ }
+
+ snd_pcm_set_runtime_buffer(substream, NULL);
+}
static int ux500_pcm_open(struct snd_pcm_substream *substream)
{
@@ -138,7 +207,9 @@ static int ux500_pcm_open(struct snd_pcm_substream *substream)
struct snd_soc_dai *dai = rtd->cpu_dai;
int ret;
- pr_debug("%s: MSP %d (%s): Enter.\n", __func__, dai->id, stream_str(substream));
+ pr_debug("%s: MSP %d (%s): Enter.\n", __func__,
+ dai->id,
+ stream_str(substream));
pr_debug("%s: Set runtime hwparams.\n", __func__);
if (stream_id == SNDRV_PCM_STREAM_PLAYBACK)
@@ -147,7 +218,10 @@ static int ux500_pcm_open(struct snd_pcm_substream *substream)
snd_soc_set_runtime_hwparams(substream, &ux500_pcm_hw_capture);
/* ensure that buffer size is a multiple of period size */
- ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ ret = snd_pcm_hw_constraint_integer(
+ runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+
if (ret < 0) {
pr_err("%s: Error: snd_pcm_hw_constraints failed (%d)\n",
__func__,
@@ -160,9 +234,15 @@ static int ux500_pcm_open(struct snd_pcm_substream *substream)
if (private == NULL)
return -ENOMEM;
private->msp_id = dai->id;
+ private->wq = alloc_ordered_workqueue("ux500/pcm", 0);
+ INIT_WORK(&private->ws_stop, ux500_pcm_dma_stop);
+
runtime->private_data = private;
- pr_debug("%s: Set hw-struct for %s.\n", __func__, stream_str(substream));
+ pr_debug("%s: Set hw-struct for %s.\n",
+ __func__,
+ stream_str(substream));
+
runtime->hw = (stream_id == SNDRV_PCM_STREAM_PLAYBACK) ?
ux500_pcm_hw_playback : ux500_pcm_hw_capture;
@@ -175,6 +255,12 @@ static int ux500_pcm_close(struct snd_pcm_substream *substream)
pr_debug("%s: Enter\n", __func__);
+ if (private->pipeid != NULL) {
+ dma_release_channel(private->pipeid);
+ private->pipeid = NULL;
+ }
+
+ destroy_workqueue(private->wq);
kfree(private);
return 0;
@@ -231,7 +317,8 @@ static int ux500_pcm_hw_params(struct snd_pcm_substream *substream,
return -ENOMEM;
}
-static int ux500_pcm_hw_free(struct snd_pcm_substream *substream)
+static int
+ux500_pcm_hw_free(struct snd_pcm_substream *substream)
{
pr_debug("%s: Enter\n", __func__);
@@ -240,15 +327,67 @@ static int ux500_pcm_hw_free(struct snd_pcm_substream *substream)
return 0;
}
-static int ux500_pcm_prepare(struct snd_pcm_substream *substream)
+static int
+ux500_pcm_prepare(struct snd_pcm_substream *substream)
{
+ struct stedma40_chan_cfg *dma_cfg;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ struct ux500_pcm_private *private = runtime->private_data;
+ struct ux500_pcm_dma_params *dma_params;
+ dma_cap_mask_t mask;
+ u16 per_data_width, mem_data_width;
+
pr_debug("%s: Enter\n", __func__);
+
+ if (private->pipeid != NULL) {
+ dma_release_channel(private->pipeid);
+ private->pipeid = NULL;
+ }
+
+ dma_params = snd_soc_dai_get_dma_data(dai, substream);
+
+ mem_data_width = STEDMA40_HALFWORD_WIDTH;
+
+ switch (dma_params->data_size) {
+ case 32:
+ per_data_width = STEDMA40_WORD_WIDTH;
+ break;
+ case 16:
+ per_data_width = STEDMA40_HALFWORD_WIDTH;
+ break;
+ case 8:
+ per_data_width = STEDMA40_BYTE_WIDTH;
+ break;
+ default:
+ per_data_width = STEDMA40_WORD_WIDTH;
+ pr_warn("%s: Unknown data-size (%d)! Assuming 32 bits.\n",
+ __func__, dma_params->data_size);
+ }
+
+ dma_cfg = dma_params->dma_cfg;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ dma_cfg->src_info.data_width = mem_data_width;
+ dma_cfg->dst_info.data_width = per_data_width;
+ } else {
+ dma_cfg->src_info.data_width = per_data_width;
+ dma_cfg->dst_info.data_width = mem_data_width;
+ }
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ private->pipeid = dma_request_channel(mask, stedma40_filter, dma_cfg);
+
return 0;
}
-static int ux500_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+static int
+ux500_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
- int ret;
+ int ret = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
struct ux500_pcm_private *private = runtime->private_data;
int stream_id = substream->pstr->stream;
@@ -264,9 +403,10 @@ static int ux500_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
return 0;
}
- private->no_of_underruns = 0;
private->offset = 0;
- ret = ux500_msp_dai_i2s_configure_sg(runtime->dma_addr,
+ ret = ux500_pcm_dma_start(
+ substream,
+ runtime->dma_addr,
runtime->periods,
frames_to_bytes(runtime, runtime->period_size),
private->msp_id,
@@ -277,16 +417,11 @@ static int ux500_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
}
break;
case SNDRV_PCM_TRIGGER_RESUME:
- break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- break;
-
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
pr_debug("%s: SNDRV_PCM_TRIGGER_STOP\n", __func__);
- pr_debug("%s: no_of_underruns = %u\n",
- __func__,
- private->no_of_underruns);
+ queue_work(private->wq, &private->ws_stop);
break;
default:
@@ -295,10 +430,11 @@ static int ux500_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
return -EINVAL;
}
- return 0;
+ return ret;
}
-static snd_pcm_uframes_t ux500_pcm_pointer(struct snd_pcm_substream *substream)
+static snd_pcm_uframes_t
+ux500_pcm_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct ux500_pcm_private *private = runtime->private_data;
@@ -309,8 +445,10 @@ static snd_pcm_uframes_t ux500_pcm_pointer(struct snd_pcm_substream *substream)
return bytes_to_frames(substream->runtime, private->offset);
}
-static int ux500_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
+static int
+ux500_pcm_mmap(
+ struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
{
struct snd_pcm_runtime *runtime = substream->runtime;
pr_debug("%s: Enter.\n", __func__);