aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2010-02-25 07:52:10 -0500
committerMark Brown <broonie@opensource.wolfsonmicro.com>2010-02-25 10:25:07 -0500
commitb4e82b5b785670b68136765059d1afc65c0ae023 (patch)
treeb37d6abcda23e818ec0d3016b4305f55341450a2 /sound
parent9e4a10d27e89f780539e08abd2b051cb83635dfa (diff)
ASoC: Check progress when reporting periods from i.MX FIQ handler
Currently the i.MX FIQ handler is reporting periods as elapsed based purely on a timer running in the CPU. This means that any clock mismatch between the CPU and the audio subsystem can result in the status reported to applications drifting away from the actual status of the hardware. This is particularly likely at present since the SSI driver is only capable of operating in slave mode so it's very likely that the interface will be clocked from a different source. Instead check the offset reported by the FIQ and only notify when we have transferred at least one period, re-firing the timer if we didn't do so. Also factor out the calculation of the timer expiry time for make it a bit easier to experiment with. Note that this only improves the situation, problems can still be triggered. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Acked-by: Sascha Hauer <s.hauer@pengutronix.de> Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/imx/imx-pcm-fiq.c36
1 files changed, 30 insertions, 6 deletions
diff --git a/sound/soc/imx/imx-pcm-fiq.c b/sound/soc/imx/imx-pcm-fiq.c
index a1c4ce6ad40..d9cb9849b03 100644
--- a/sound/soc/imx/imx-pcm-fiq.c
+++ b/sound/soc/imx/imx-pcm-fiq.c
@@ -36,17 +36,24 @@ struct imx_pcm_runtime_data {
36 int period; 36 int period;
37 int periods; 37 int periods;
38 unsigned long offset; 38 unsigned long offset;
39 unsigned long last_offset;
39 unsigned long size; 40 unsigned long size;
40 struct timer_list timer; 41 struct timer_list timer;
41 int period_time; 42 int poll_time;
42}; 43};
43 44
45static inline void imx_ssi_set_next_poll(struct imx_pcm_runtime_data *iprtd)
46{
47 iprtd->timer.expires = jiffies + iprtd->poll_time;
48}
49
44static void imx_ssi_timer_callback(unsigned long data) 50static void imx_ssi_timer_callback(unsigned long data)
45{ 51{
46 struct snd_pcm_substream *substream = (void *)data; 52 struct snd_pcm_substream *substream = (void *)data;
47 struct snd_pcm_runtime *runtime = substream->runtime; 53 struct snd_pcm_runtime *runtime = substream->runtime;
48 struct imx_pcm_runtime_data *iprtd = runtime->private_data; 54 struct imx_pcm_runtime_data *iprtd = runtime->private_data;
49 struct pt_regs regs; 55 struct pt_regs regs;
56 unsigned long delta;
50 57
51 get_fiq_regs(&regs); 58 get_fiq_regs(&regs);
52 59
@@ -55,9 +62,25 @@ static void imx_ssi_timer_callback(unsigned long data)
55 else 62 else
56 iprtd->offset = regs.ARM_r9 & 0xffff; 63 iprtd->offset = regs.ARM_r9 & 0xffff;
57 64
58 iprtd->timer.expires = jiffies + iprtd->period_time; 65 /* How much data have we transferred since the last period report? */
66 if (iprtd->offset >= iprtd->last_offset)
67 delta = iprtd->offset - iprtd->last_offset;
68 else
69 delta = runtime->buffer_size + iprtd->offset
70 - iprtd->last_offset;
71
72 /* If we've transferred at least a period then report it and
73 * reset our poll time */
74 if (delta >= runtime->period_size) {
75 snd_pcm_period_elapsed(substream);
76 iprtd->last_offset = iprtd->offset;
77
78 imx_ssi_set_next_poll(iprtd);
79 }
80
81 /* Restart the timer; if we didn't report we'll run on the next tick */
59 add_timer(&iprtd->timer); 82 add_timer(&iprtd->timer);
60 snd_pcm_period_elapsed(substream); 83
61} 84}
62 85
63static struct fiq_handler fh = { 86static struct fiq_handler fh = {
@@ -72,9 +95,10 @@ static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream,
72 95
73 iprtd->size = params_buffer_bytes(params); 96 iprtd->size = params_buffer_bytes(params);
74 iprtd->periods = params_periods(params); 97 iprtd->periods = params_periods(params);
75 iprtd->period = params_period_bytes(params); 98 iprtd->period = params_period_bytes(params) ;
76 iprtd->offset = 0; 99 iprtd->offset = 0;
77 iprtd->period_time = HZ / (params_rate(params) / params_period_size(params)); 100 iprtd->last_offset = 0;
101 iprtd->poll_time = HZ / (params_rate(params) / params_period_size(params));
78 102
79 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 103 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
80 104
@@ -110,7 +134,7 @@ static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
110 case SNDRV_PCM_TRIGGER_START: 134 case SNDRV_PCM_TRIGGER_START:
111 case SNDRV_PCM_TRIGGER_RESUME: 135 case SNDRV_PCM_TRIGGER_RESUME:
112 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 136 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
113 iprtd->timer.expires = jiffies + iprtd->period_time; 137 imx_ssi_set_next_poll(iprtd);
114 add_timer(&iprtd->timer); 138 add_timer(&iprtd->timer);
115 if (++fiq_enable == 1) 139 if (++fiq_enable == 1)
116 enable_fiq(imx_pcm_fiq); 140 enable_fiq(imx_pcm_fiq);