diff options
| author | Guennadi Liakhovetski <g.liakhovetski@gmx.de> | 2012-10-03 08:33:50 -0400 |
|---|---|---|
| committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2012-10-09 02:47:33 -0400 |
| commit | 57451e437796548d658d03c2c4aab659eafcd799 (patch) | |
| tree | f4e8c18605a3cc3591b93c8bcf4ce52b8548cd5f | |
| parent | a92b078eab17d09ac600446954d8b0d7998c6168 (diff) | |
ASoC: fsi: don't reschedule DMA from an atomic context
shdma doesn't support transfer re-scheduling or triggering from callbacks
or from atomic context. The fsi driver issues DMA transfers from a tasklet
context, which is a bug. To fix it convert tasklet to a work.
Reported-by: Do Q.Thang <dq-thang@jinso.co.jp>
Tested-by: Do Q.Thang <dq-thang@jinso.co.jp>
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Cc: stable@vger.kernel.org
| -rw-r--r-- | sound/soc/sh/fsi.c | 15 |
1 files changed, 8 insertions, 7 deletions
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 5328ae5539f1..9d7f30774a44 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c | |||
| @@ -20,6 +20,7 @@ | |||
| 20 | #include <linux/sh_dma.h> | 20 | #include <linux/sh_dma.h> |
| 21 | #include <linux/slab.h> | 21 | #include <linux/slab.h> |
| 22 | #include <linux/module.h> | 22 | #include <linux/module.h> |
| 23 | #include <linux/workqueue.h> | ||
| 23 | #include <sound/soc.h> | 24 | #include <sound/soc.h> |
| 24 | #include <sound/sh_fsi.h> | 25 | #include <sound/sh_fsi.h> |
| 25 | 26 | ||
| @@ -223,7 +224,7 @@ struct fsi_stream { | |||
| 223 | */ | 224 | */ |
| 224 | struct dma_chan *chan; | 225 | struct dma_chan *chan; |
| 225 | struct sh_dmae_slave slave; /* see fsi_handler_init() */ | 226 | struct sh_dmae_slave slave; /* see fsi_handler_init() */ |
| 226 | struct tasklet_struct tasklet; | 227 | struct work_struct work; |
| 227 | dma_addr_t dma; | 228 | dma_addr_t dma; |
| 228 | }; | 229 | }; |
| 229 | 230 | ||
| @@ -1085,9 +1086,9 @@ static void fsi_dma_complete(void *data) | |||
| 1085 | snd_pcm_period_elapsed(io->substream); | 1086 | snd_pcm_period_elapsed(io->substream); |
| 1086 | } | 1087 | } |
| 1087 | 1088 | ||
| 1088 | static void fsi_dma_do_tasklet(unsigned long data) | 1089 | static void fsi_dma_do_work(struct work_struct *work) |
| 1089 | { | 1090 | { |
| 1090 | struct fsi_stream *io = (struct fsi_stream *)data; | 1091 | struct fsi_stream *io = container_of(work, struct fsi_stream, work); |
| 1091 | struct fsi_priv *fsi = fsi_stream_to_priv(io); | 1092 | struct fsi_priv *fsi = fsi_stream_to_priv(io); |
| 1092 | struct snd_soc_dai *dai; | 1093 | struct snd_soc_dai *dai; |
| 1093 | struct dma_async_tx_descriptor *desc; | 1094 | struct dma_async_tx_descriptor *desc; |
| @@ -1129,7 +1130,7 @@ static void fsi_dma_do_tasklet(unsigned long data) | |||
| 1129 | * FIXME | 1130 | * FIXME |
| 1130 | * | 1131 | * |
| 1131 | * In DMAEngine case, codec and FSI cannot be started simultaneously | 1132 | * In DMAEngine case, codec and FSI cannot be started simultaneously |
| 1132 | * since FSI is using tasklet. | 1133 | * since FSI is using the scheduler work queue. |
| 1133 | * Therefore, in capture case, probably FSI FIFO will have got | 1134 | * Therefore, in capture case, probably FSI FIFO will have got |
| 1134 | * overflow error in this point. | 1135 | * overflow error in this point. |
| 1135 | * in that case, DMA cannot start transfer until error was cleared. | 1136 | * in that case, DMA cannot start transfer until error was cleared. |
| @@ -1153,7 +1154,7 @@ static bool fsi_dma_filter(struct dma_chan *chan, void *param) | |||
| 1153 | 1154 | ||
| 1154 | static int fsi_dma_transfer(struct fsi_priv *fsi, struct fsi_stream *io) | 1155 | static int fsi_dma_transfer(struct fsi_priv *fsi, struct fsi_stream *io) |
| 1155 | { | 1156 | { |
| 1156 | tasklet_schedule(&io->tasklet); | 1157 | schedule_work(&io->work); |
| 1157 | 1158 | ||
| 1158 | return 0; | 1159 | return 0; |
| 1159 | } | 1160 | } |
| @@ -1195,14 +1196,14 @@ static int fsi_dma_probe(struct fsi_priv *fsi, struct fsi_stream *io, struct dev | |||
| 1195 | return fsi_stream_probe(fsi, dev); | 1196 | return fsi_stream_probe(fsi, dev); |
| 1196 | } | 1197 | } |
| 1197 | 1198 | ||
| 1198 | tasklet_init(&io->tasklet, fsi_dma_do_tasklet, (unsigned long)io); | 1199 | INIT_WORK(&io->work, fsi_dma_do_work); |
| 1199 | 1200 | ||
| 1200 | return 0; | 1201 | return 0; |
| 1201 | } | 1202 | } |
| 1202 | 1203 | ||
| 1203 | static int fsi_dma_remove(struct fsi_priv *fsi, struct fsi_stream *io) | 1204 | static int fsi_dma_remove(struct fsi_priv *fsi, struct fsi_stream *io) |
| 1204 | { | 1205 | { |
| 1205 | tasklet_kill(&io->tasklet); | 1206 | cancel_work_sync(&io->work); |
| 1206 | 1207 | ||
| 1207 | fsi_stream_stop(fsi, io); | 1208 | fsi_stream_stop(fsi, io); |
| 1208 | 1209 | ||
