summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorManjula Gupta <magupta@nvidia.com>2010-12-28 15:09:10 +0530
committerBharat Nihalani <bnihalani@nvidia.com>2011-01-04 02:32:44 -0800
commit1e5e6f79dd4b73dea3ccd11ed3dc1a681944c076 (patch)
tree73da454aa8129bd395fe3e057c5bc912296a8916 /sound
parentd1502e140f4e96b34fc6c713004e681ce4e0942b (diff)
[tegra ALSA]: Fix for kernel panic in dma_isr
Fix to handle overrun and underrun cases and gracefully exit dma buffering threads during overrun and underrun conditions. For bug: 774397 Change-Id: Ie8e320981b5f01bf5283f1b1048d7859088a3b82 Reviewed-on: http://git-master/r/14456 Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com> Tested-by: Bharat Nihalani <bnihalani@nvidia.com>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/tegra/tegra_pcm.c98
-rw-r--r--sound/soc/tegra/tegra_soc.h1
2 files changed, 67 insertions, 32 deletions
diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c
index 6ec095d6a3a8..6aef41853ceb 100644
--- a/sound/soc/tegra/tegra_pcm.c
+++ b/sound/soc/tegra/tegra_pcm.c
@@ -47,17 +47,23 @@ static void tegra_pcm_play(struct tegra_runtime_data *prtd)
if (runtime->dma_addr) {
prtd->size = frames_to_bytes(runtime, runtime->period_size);
if (reqid == 0) {
- prtd->dma_req1.source_addr = buf->addr +
- + frames_to_bytes(runtime,prtd->dma_pos);
- prtd->dma_req1.size = prtd->size;
- tegra_dma_enqueue_req(prtd->dma_chan, &prtd->dma_req1);
- reqid = 1;
+ if (prtd->dma_state != STATE_ABORT) {
+ prtd->dma_req1.source_addr = buf->addr +
+ frames_to_bytes(runtime,prtd->dma_pos);
+ prtd->dma_req1.size = prtd->size;
+ tegra_dma_enqueue_req(prtd->dma_chan,
+ &prtd->dma_req1);
+ reqid = 1;
+ }
} else {
- prtd->dma_req2.source_addr = buf->addr +
- + frames_to_bytes(runtime,prtd->dma_pos);
- prtd->dma_req2.size = prtd->size;
- tegra_dma_enqueue_req(prtd->dma_chan, &prtd->dma_req2);
- reqid = 0;
+ if (prtd->dma_state != STATE_ABORT) {
+ prtd->dma_req2.source_addr = buf->addr +
+ frames_to_bytes(runtime,prtd->dma_pos);
+ prtd->dma_req2.size = prtd->size;
+ tegra_dma_enqueue_req(prtd->dma_chan,
+ &prtd->dma_req2);
+ reqid = 0;
+ }
}
}
@@ -78,8 +84,10 @@ static void dma_tx_complete_callback (struct tegra_dma_req *req)
prtd->period_index = 0;
}
- snd_pcm_period_elapsed(substream);
- tegra_pcm_play(prtd);
+ if (prtd->dma_state != STATE_ABORT) {
+ snd_pcm_period_elapsed(substream);
+ tegra_pcm_play(prtd);
+ }
}
static void setup_dma_tx_request(struct tegra_dma_req *req)
@@ -121,17 +129,23 @@ static void tegra_pcm_capture(struct tegra_runtime_data *prtd)
if (runtime->dma_addr) {
prtd->size = frames_to_bytes(runtime, runtime->period_size);
if (reqid == 0) {
- prtd->dma_req1.dest_addr = buf->addr +
- + frames_to_bytes(runtime,prtd->dma_pos);
- prtd->dma_req1.size = prtd->size;
- tegra_dma_enqueue_req(prtd->dma_chan, &prtd->dma_req1);
- reqid = 1;
+ if (prtd->dma_state != STATE_ABORT) {
+ prtd->dma_req1.dest_addr = buf->addr +
+ frames_to_bytes(runtime,prtd->dma_pos);
+ prtd->dma_req1.size = prtd->size;
+ tegra_dma_enqueue_req(prtd->dma_chan,
+ &prtd->dma_req1);
+ reqid = 1;
+ }
} else {
- prtd->dma_req2.dest_addr = buf->addr +
- + frames_to_bytes(runtime,prtd->dma_pos);
- prtd->dma_req2.size = prtd->size;
- tegra_dma_enqueue_req(prtd->dma_chan, &prtd->dma_req2);
- reqid = 0;
+ if (prtd->dma_state != STATE_ABORT) {
+ prtd->dma_req2.dest_addr = buf->addr +
+ frames_to_bytes(runtime,prtd->dma_pos);
+ prtd->dma_req2.size = prtd->size;
+ tegra_dma_enqueue_req(prtd->dma_chan,
+ &prtd->dma_req2);
+ reqid = 0;
+ }
}
}
@@ -152,8 +166,10 @@ static void dma_rx_complete_callback(struct tegra_dma_req *req)
prtd->period_index = 0;
}
- snd_pcm_period_elapsed(substream);
- tegra_pcm_capture(prtd);
+ if (prtd->dma_state != STATE_ABORT) {
+ snd_pcm_period_elapsed(substream);
+ tegra_pcm_capture(prtd);
+ }
}
static void setup_dma_rx_request(struct tegra_dma_req *req)
@@ -174,7 +190,7 @@ static const struct snd_pcm_hardware tegra_pcm_hardware = {
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | \
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID ,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
- .channels_min = 2,
+ .channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = (PAGE_SIZE * 8),
.period_bytes_min = 1024,
@@ -209,28 +225,45 @@ static int tegra_pcm_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:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
prtd->state = STATE_INIT;
+ prtd->dma_state = STATE_INIT;
tegra_pcm_play(prtd); /* dma enqueue req1 */
tegra_pcm_play(prtd); /* dma enqueue req2 */
start_i2s_playback();
} else if (prtd->state != STATE_INIT) {
/* start recording */
prtd->state = STATE_INIT;
+ prtd->dma_state = STATE_INIT;
tegra_pcm_capture(prtd); /* dma enqueue req1 */
tegra_pcm_capture(prtd); /* dma enqueue req2 */
start_i2s_capture();
}
break;
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- break;
case SNDRV_PCM_TRIGGER_STOP:
- prtd->state = STATE_ABORT;
- break;
case SNDRV_PCM_TRIGGER_SUSPEND:
- break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ prtd->state = STATE_ABORT;
+ prtd->dma_state = prtd->dma_state = STATE_ABORT;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (prtd->dma_chan) {
+ stop_i2s_playback();
+ tegra_dma_dequeue_req(prtd->dma_chan,
+ &prtd->dma_req1);
+ tegra_dma_dequeue_req(prtd->dma_chan,
+ &prtd->dma_req2);
+ }
+ } else {
+ if (prtd->dma_chan) {
+ stop_i2s_capture();
+ tegra_dma_dequeue_req(prtd->dma_chan,
+ &prtd->dma_req1);
+ tegra_dma_dequeue_req(prtd->dma_chan,
+ &prtd->dma_req2);
+ }
+ }
break;
default:
ret = -EINVAL;
@@ -310,7 +343,7 @@ static int tegra_pcm_open(struct snd_pcm_substream *substream)
prtd->dma_req1.dev = prtd;
prtd->dma_req2.dev = prtd;
- prtd->dma_chan = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT);
+ prtd->dma_chan = tegra_dma_allocate_channel(TEGRA_DMA_MODE_CONTINUOUS_DOUBLE);
if (IS_ERR(prtd->dma_chan)) {
pr_err("%s: could not allocate DMA channel for I2S: %ld\n",
__func__, PTR_ERR(prtd->dma_chan));
@@ -354,6 +387,7 @@ static int tegra_pcm_close(struct snd_pcm_substream *substream)
prtd->state = STATE_EXIT;
if (prtd->dma_chan) {
+ prtd->dma_state = STATE_EXIT;
tegra_dma_dequeue_req(prtd->dma_chan, &prtd->dma_req1);
tegra_dma_dequeue_req(prtd->dma_chan, &prtd->dma_req2);
stop_i2s_playback();
diff --git a/sound/soc/tegra/tegra_soc.h b/sound/soc/tegra/tegra_soc.h
index e570bcb39172..1c8e6cef4fa4 100644
--- a/sound/soc/tegra/tegra_soc.h
+++ b/sound/soc/tegra/tegra_soc.h
@@ -92,6 +92,7 @@ struct tegra_runtime_data {
volatile int state;
int period_index;
int i2s_shutdown;
+ int dma_state;
struct tegra_dma_channel *dma_chan;
struct clk *i2s_clk;
struct clk *dap_mclk;