Skip to content

Instantly share code, notes, and snippets.

@kangear
Last active August 29, 2015 14:09
Show Gist options
  • Save kangear/2ac8590da9f3c6d65a8d to your computer and use it in GitHub Desktop.
Save kangear/2ac8590da9f3c6d65a8d to your computer and use it in GitHub Desktop.
S3C2440 audio patch
diff --git a/sound/soc/samsung/dma.c b/sound/soc/samsung/dma.c
index 9338d11..96a1b01 100644
--- a/sound/soc/samsung/dma.c
+++ b/sound/soc/samsung/dma.c
@@ -34,7 +34,9 @@ static const struct snd_pcm_hardware dma_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID,
+ SNDRV_PCM_INFO_MMAP_VALID|
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_U16_LE |
SNDRV_PCM_FMTBIT_U8 |
@@ -57,10 +59,11 @@ struct runtime_data {
dma_addr_t dma_start;
dma_addr_t dma_pos;
dma_addr_t dma_end;
+ unsigned int dma_limit;
struct s3c_dma_params *params;
};
-static void audio_buffdone(void *data);
+/* static void audio_buffdone(void *data); */
/* dma_enqueue
*
@@ -72,74 +75,66 @@ 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;
+ int ret;
pr_debug("Entered %s\n", __func__);
- limit = (prtd->dma_end - prtd->dma_start) / prtd->dma_period;
+ if (samsung_dma_has_circular())
+ limit = (prtd->dma_end - prtd->dma_start) / prtd->dma_period;
+ else
+ limit = prtd->dma_limit;
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) {
+ unsigned long len = prtd->dma_period;
pr_debug("dma_loaded: %d\n", prtd->dma_loaded);
- if ((pos + dma_info.period) > prtd->dma_end) {
- dma_info.period = prtd->dma_end - pos;
+ if ((pos + len) > prtd->dma_end) {
+ len = prtd->dma_end - pos;
pr_debug("%s: corrected dma len %ld\n",
- __func__, dma_info.period);
+ __func__, len);
}
- dma_info.buf = pos;
- prtd->params->ops->prepare(prtd->params->ch, &dma_info);
+ ret = s3c2410_dma_enqueue(prtd->params->channel,
+ substream, pos, len);
- prtd->dma_loaded++;
- pos += prtd->dma_period;
- if (pos >= prtd->dma_end)
- pos = prtd->dma_start;
+ if (ret == 0) {
+ prtd->dma_loaded++;
+ pos += prtd->dma_period;
+ if (pos >= prtd->dma_end)
+ pos = prtd->dma_start;
+ } else
+ break;
}
prtd->dma_pos = pos;
}
-
-static void audio_buffdone(void *data)
+static void audio_buffdone(struct s3c2410_dma_chan *channel,
+ void *dev_id, int size,
+ enum s3c2410_dma_buffresult result)
{
- struct snd_pcm_substream *substream = data;
- struct runtime_data *prtd = substream->runtime->private_data;
+ struct snd_pcm_substream *substream = dev_id;
+ struct runtime_data *prtd;
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 (result == S3C2410_RES_ABORT || result == S3C2410_RES_ERR)
+ return;
- if (substream)
- snd_pcm_period_elapsed(substream);
+ prtd = substream->runtime->private_data;
- spin_lock(&prtd->lock);
- if (!samsung_dma_has_circular()) {
- prtd->dma_loaded--;
- dma_enqueue(substream);
- }
- spin_unlock(&prtd->lock);
+ if (substream)
+ snd_pcm_period_elapsed(substream);
+
+ spin_lock(&prtd->lock);
+ if (prtd->state & ST_RUNNING && !samsung_dma_has_circular()) {
+ prtd->dma_loaded--;
+ dma_enqueue(substream);
}
+
+ spin_unlock(&prtd->lock);
}
static int dma_hw_params(struct snd_pcm_substream *substream,
@@ -190,6 +185,8 @@ static int dma_hw_params(struct snd_pcm_substream *substream,
prtd->params->ops->config(prtd->params->ch, &config);
}
+ s3c2410_dma_set_buffdone_fn(prtd->params->channel, audio_buffdone);
+
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
runtime->dma_bytes = totbytes;
@@ -200,6 +197,8 @@ static int dma_hw_params(struct snd_pcm_substream *substream,
prtd->dma_start = runtime->dma_addr;
prtd->dma_pos = prtd->dma_start;
prtd->dma_end = prtd->dma_start + totbytes;
+
+ prtd->dma_limit = runtime->hw.periods_min;
spin_unlock_irq(&prtd->lock);
return 0;
@@ -227,6 +226,7 @@ static int dma_prepare(struct snd_pcm_substream *substream)
{
struct runtime_data *prtd = substream->runtime->private_data;
int ret = 0;
+ struct samsung_dma_config config;
pr_debug("Entered %s\n", __func__);
@@ -234,7 +234,12 @@ static int dma_prepare(struct snd_pcm_substream *substream)
* codec <--> BT codec or GSM modem -- lg FIXME */
if (!prtd->params)
return 0;
-
+ 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->ops->config(prtd->params->ch, &config);
/* flush the DMA channel */
prtd->params->ops->flush(prtd->params->ch);
@@ -258,11 +263,15 @@ static int dma_trigger(struct snd_pcm_substream *substream, int cmd)
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
prtd->state |= ST_RUNNING;
prtd->params->ops->trigger(prtd->params->ch);
break;
case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
prtd->state &= ~ST_RUNNING;
prtd->params->ops->stop(prtd->params->ch);
break;
@@ -284,9 +293,20 @@ dma_pointer(struct snd_pcm_substream *substream)
struct runtime_data *prtd = runtime->private_data;
unsigned long res;
+ /* res = prtd->dma_pos - prtd->dma_start; */
+ dma_addr_t src, dst;
+
pr_debug("Entered %s\n", __func__);
- res = prtd->dma_pos - prtd->dma_start;
+ spin_lock(&prtd->lock);
+ s3c2410_dma_getposition(prtd->params->channel, &src, &dst);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ res = dst - prtd->dma_start;
+ else
+ res = src - prtd->dma_start;
+
+ spin_unlock(&prtd->lock);
pr_debug("Pointer offset: %lu\n", res);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment