aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/sound/pcm.h1
-rw-r--r--sound/core/pcm.c1
-rw-r--r--sound/core/pcm_lib.c155
-rw-r--r--sound/core/pcm_timer.c6
4 files changed, 102 insertions, 61 deletions
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 40c5a6fa6bc..e4f60076e6c 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -364,7 +364,6 @@ struct snd_pcm_substream {
364 /* -- timer section -- */ 364 /* -- timer section -- */
365 struct snd_timer *timer; /* timer */ 365 struct snd_timer *timer; /* timer */
366 unsigned timer_running: 1; /* time is running */ 366 unsigned timer_running: 1; /* time is running */
367 spinlock_t timer_lock;
368 /* -- next substream -- */ 367 /* -- next substream -- */
369 struct snd_pcm_substream *next; 368 struct snd_pcm_substream *next;
370 /* -- linked substreams -- */ 369 /* -- linked substreams -- */
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 192a433a240..37f567a68ef 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -667,7 +667,6 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
667 spin_lock_init(&substream->self_group.lock); 667 spin_lock_init(&substream->self_group.lock);
668 INIT_LIST_HEAD(&substream->self_group.substreams); 668 INIT_LIST_HEAD(&substream->self_group.substreams);
669 list_add_tail(&substream->link_list, &substream->self_group.substreams); 669 list_add_tail(&substream->link_list, &substream->self_group.substreams);
670 spin_lock_init(&substream->timer_lock);
671 atomic_set(&substream->mmap_count, 0); 670 atomic_set(&substream->mmap_count, 0);
672 prev = substream; 671 prev = substream;
673 } 672 }
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 921691080f3..fbb2e391591 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -125,23 +125,32 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
125 } 125 }
126} 126}
127 127
128#ifdef CONFIG_SND_PCM_XRUN_DEBUG
129#define xrun_debug(substream) ((substream)->pstr->xrun_debug)
130#else
131#define xrun_debug(substream) 0
132#endif
133
134#define dump_stack_on_xrun(substream) do { \
135 if (xrun_debug(substream) > 1) \
136 dump_stack(); \
137 } while (0)
138
128static void xrun(struct snd_pcm_substream *substream) 139static void xrun(struct snd_pcm_substream *substream)
129{ 140{
130 snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); 141 snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
131#ifdef CONFIG_SND_PCM_XRUN_DEBUG 142 if (xrun_debug(substream)) {
132 if (substream->pstr->xrun_debug) {
133 snd_printd(KERN_DEBUG "XRUN: pcmC%dD%d%c\n", 143 snd_printd(KERN_DEBUG "XRUN: pcmC%dD%d%c\n",
134 substream->pcm->card->number, 144 substream->pcm->card->number,
135 substream->pcm->device, 145 substream->pcm->device,
136 substream->stream ? 'c' : 'p'); 146 substream->stream ? 'c' : 'p');
137 if (substream->pstr->xrun_debug > 1) 147 dump_stack_on_xrun(substream);
138 dump_stack();
139 } 148 }
140#endif
141} 149}
142 150
143static inline snd_pcm_uframes_t snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream, 151static snd_pcm_uframes_t
144 struct snd_pcm_runtime *runtime) 152snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream,
153 struct snd_pcm_runtime *runtime)
145{ 154{
146 snd_pcm_uframes_t pos; 155 snd_pcm_uframes_t pos;
147 156
@@ -150,17 +159,21 @@ static inline snd_pcm_uframes_t snd_pcm_update_hw_ptr_pos(struct snd_pcm_substre
150 pos = substream->ops->pointer(substream); 159 pos = substream->ops->pointer(substream);
151 if (pos == SNDRV_PCM_POS_XRUN) 160 if (pos == SNDRV_PCM_POS_XRUN)
152 return pos; /* XRUN */ 161 return pos; /* XRUN */
153#ifdef CONFIG_SND_DEBUG
154 if (pos >= runtime->buffer_size) { 162 if (pos >= runtime->buffer_size) {
155 snd_printk(KERN_ERR "BUG: stream = %i, pos = 0x%lx, buffer size = 0x%lx, period size = 0x%lx\n", substream->stream, pos, runtime->buffer_size, runtime->period_size); 163 if (printk_ratelimit()) {
164 snd_printd(KERN_ERR "BUG: stream = %i, pos = 0x%lx, "
165 "buffer size = 0x%lx, period size = 0x%lx\n",
166 substream->stream, pos, runtime->buffer_size,
167 runtime->period_size);
168 }
169 pos = 0;
156 } 170 }
157#endif
158 pos -= pos % runtime->min_align; 171 pos -= pos % runtime->min_align;
159 return pos; 172 return pos;
160} 173}
161 174
162static inline int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream, 175static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream,
163 struct snd_pcm_runtime *runtime) 176 struct snd_pcm_runtime *runtime)
164{ 177{
165 snd_pcm_uframes_t avail; 178 snd_pcm_uframes_t avail;
166 179
@@ -182,11 +195,21 @@ static inline int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream
182 return 0; 195 return 0;
183} 196}
184 197
185static inline int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) 198#define hw_ptr_error(substream, fmt, args...) \
199 do { \
200 if (xrun_debug(substream)) { \
201 if (printk_ratelimit()) { \
202 snd_printd("PCM: " fmt, ##args); \
203 } \
204 dump_stack_on_xrun(substream); \
205 } \
206 } while (0)
207
208static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
186{ 209{
187 struct snd_pcm_runtime *runtime = substream->runtime; 210 struct snd_pcm_runtime *runtime = substream->runtime;
188 snd_pcm_uframes_t pos; 211 snd_pcm_uframes_t pos;
189 snd_pcm_uframes_t new_hw_ptr, hw_ptr_interrupt; 212 snd_pcm_uframes_t new_hw_ptr, hw_ptr_interrupt, hw_base;
190 snd_pcm_sframes_t delta; 213 snd_pcm_sframes_t delta;
191 214
192 pos = snd_pcm_update_hw_ptr_pos(substream, runtime); 215 pos = snd_pcm_update_hw_ptr_pos(substream, runtime);
@@ -194,36 +217,53 @@ static inline int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *subs
194 xrun(substream); 217 xrun(substream);
195 return -EPIPE; 218 return -EPIPE;
196 } 219 }
197 if (runtime->period_size == runtime->buffer_size) 220 hw_base = runtime->hw_ptr_base;
198 goto __next_buf; 221 new_hw_ptr = hw_base + pos;
199 new_hw_ptr = runtime->hw_ptr_base + pos;
200 hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size; 222 hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size;
201 223 delta = new_hw_ptr - hw_ptr_interrupt;
202 delta = hw_ptr_interrupt - new_hw_ptr; 224 if (hw_ptr_interrupt >= runtime->boundary) {
203 if (delta > 0) { 225 hw_ptr_interrupt -= runtime->boundary;
204 if ((snd_pcm_uframes_t)delta < runtime->buffer_size / 2) { 226 if (hw_base < runtime->boundary / 2)
205#ifdef CONFIG_SND_PCM_XRUN_DEBUG 227 /* hw_base was already lapped; recalc delta */
206 if (runtime->periods > 1 && substream->pstr->xrun_debug) { 228 delta = new_hw_ptr - hw_ptr_interrupt;
207 snd_printd(KERN_ERR "Unexpected hw_pointer value [1] (stream = %i, delta: -%ld, max jitter = %ld): wrong interrupt acknowledge?\n", substream->stream, (long) delta, runtime->buffer_size / 2); 229 }
208 if (substream->pstr->xrun_debug > 1) 230 if (delta < 0) {
209 dump_stack(); 231 delta += runtime->buffer_size;
210 } 232 if (delta < 0) {
211#endif 233 hw_ptr_error(substream,
212 return 0; 234 "Unexpected hw_pointer value "
235 "(stream=%i, pos=%ld, intr_ptr=%ld)\n",
236 substream->stream, (long)pos,
237 (long)hw_ptr_interrupt);
238 /* rebase to interrupt position */
239 hw_base = new_hw_ptr = hw_ptr_interrupt;
240 /* align hw_base to buffer_size */
241 hw_base -= hw_base % runtime->buffer_size;
242 delta = 0;
243 } else {
244 hw_base += runtime->buffer_size;
245 if (hw_base >= runtime->boundary)
246 hw_base = 0;
247 new_hw_ptr = hw_base + pos;
213 } 248 }
214 __next_buf:
215 runtime->hw_ptr_base += runtime->buffer_size;
216 if (runtime->hw_ptr_base == runtime->boundary)
217 runtime->hw_ptr_base = 0;
218 new_hw_ptr = runtime->hw_ptr_base + pos;
219 } 249 }
220 250 if (delta > runtime->period_size) {
251 hw_ptr_error(substream,
252 "Lost interrupts? "
253 "(stream=%i, delta=%ld, intr_ptr=%ld)\n",
254 substream->stream, (long)delta,
255 (long)hw_ptr_interrupt);
256 /* rebase hw_ptr_interrupt */
257 hw_ptr_interrupt =
258 new_hw_ptr - new_hw_ptr % runtime->period_size;
259 }
221 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && 260 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
222 runtime->silence_size > 0) 261 runtime->silence_size > 0)
223 snd_pcm_playback_silence(substream, new_hw_ptr); 262 snd_pcm_playback_silence(substream, new_hw_ptr);
224 263
264 runtime->hw_ptr_base = hw_base;
225 runtime->status->hw_ptr = new_hw_ptr; 265 runtime->status->hw_ptr = new_hw_ptr;
226 runtime->hw_ptr_interrupt = new_hw_ptr - new_hw_ptr % runtime->period_size; 266 runtime->hw_ptr_interrupt = hw_ptr_interrupt;
227 267
228 return snd_pcm_update_hw_ptr_post(substream, runtime); 268 return snd_pcm_update_hw_ptr_post(substream, runtime);
229} 269}
@@ -233,7 +273,7 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
233{ 273{
234 struct snd_pcm_runtime *runtime = substream->runtime; 274 struct snd_pcm_runtime *runtime = substream->runtime;
235 snd_pcm_uframes_t pos; 275 snd_pcm_uframes_t pos;
236 snd_pcm_uframes_t old_hw_ptr, new_hw_ptr; 276 snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base;
237 snd_pcm_sframes_t delta; 277 snd_pcm_sframes_t delta;
238 278
239 old_hw_ptr = runtime->status->hw_ptr; 279 old_hw_ptr = runtime->status->hw_ptr;
@@ -242,29 +282,38 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
242 xrun(substream); 282 xrun(substream);
243 return -EPIPE; 283 return -EPIPE;
244 } 284 }
245 new_hw_ptr = runtime->hw_ptr_base + pos; 285 hw_base = runtime->hw_ptr_base;
246 286 new_hw_ptr = hw_base + pos;
247 delta = old_hw_ptr - new_hw_ptr; 287
248 if (delta > 0) { 288 delta = new_hw_ptr - old_hw_ptr;
249 if ((snd_pcm_uframes_t)delta < runtime->buffer_size / 2) { 289 if (delta < 0) {
250#ifdef CONFIG_SND_PCM_XRUN_DEBUG 290 delta += runtime->buffer_size;
251 if (runtime->periods > 2 && substream->pstr->xrun_debug) { 291 if (delta < 0) {
252 snd_printd(KERN_ERR "Unexpected hw_pointer value [2] (stream = %i, delta: -%ld, max jitter = %ld): wrong interrupt acknowledge?\n", substream->stream, (long) delta, runtime->buffer_size / 2); 292 hw_ptr_error(substream,
253 if (substream->pstr->xrun_debug > 1) 293 "Unexpected hw_pointer value [2] "
254 dump_stack(); 294 "(stream=%i, pos=%ld, old_ptr=%ld)\n",
255 } 295 substream->stream, (long)pos,
256#endif 296 (long)old_hw_ptr);
257 return 0; 297 return 0;
258 } 298 }
259 runtime->hw_ptr_base += runtime->buffer_size; 299 hw_base += runtime->buffer_size;
260 if (runtime->hw_ptr_base == runtime->boundary) 300 if (hw_base >= runtime->boundary)
261 runtime->hw_ptr_base = 0; 301 hw_base = 0;
262 new_hw_ptr = runtime->hw_ptr_base + pos; 302 new_hw_ptr = hw_base + pos;
303 }
304 if (delta > runtime->period_size && runtime->periods > 1) {
305 hw_ptr_error(substream,
306 "hw_ptr skipping! "
307 "(pos=%ld, delta=%ld, period=%ld)\n",
308 (long)pos, (long)delta,
309 (long)runtime->period_size);
310 return 0;
263 } 311 }
264 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && 312 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
265 runtime->silence_size > 0) 313 runtime->silence_size > 0)
266 snd_pcm_playback_silence(substream, new_hw_ptr); 314 snd_pcm_playback_silence(substream, new_hw_ptr);
267 315
316 runtime->hw_ptr_base = hw_base;
268 runtime->status->hw_ptr = new_hw_ptr; 317 runtime->status->hw_ptr = new_hw_ptr;
269 318
270 return snd_pcm_update_hw_ptr_post(substream, runtime); 319 return snd_pcm_update_hw_ptr_post(substream, runtime);
diff --git a/sound/core/pcm_timer.c b/sound/core/pcm_timer.c
index 2c89c04f291..ca8068b63d6 100644
--- a/sound/core/pcm_timer.c
+++ b/sound/core/pcm_timer.c
@@ -85,25 +85,19 @@ static unsigned long snd_pcm_timer_resolution(struct snd_timer * timer)
85 85
86static int snd_pcm_timer_start(struct snd_timer * timer) 86static int snd_pcm_timer_start(struct snd_timer * timer)
87{ 87{
88 unsigned long flags;
89 struct snd_pcm_substream *substream; 88 struct snd_pcm_substream *substream;
90 89
91 substream = snd_timer_chip(timer); 90 substream = snd_timer_chip(timer);
92 spin_lock_irqsave(&substream->timer_lock, flags);
93 substream->timer_running = 1; 91 substream->timer_running = 1;
94 spin_unlock_irqrestore(&substream->timer_lock, flags);
95 return 0; 92 return 0;
96} 93}
97 94
98static int snd_pcm_timer_stop(struct snd_timer * timer) 95static int snd_pcm_timer_stop(struct snd_timer * timer)
99{ 96{
100 unsigned long flags;
101 struct snd_pcm_substream *substream; 97 struct snd_pcm_substream *substream;
102 98
103 substream = snd_timer_chip(timer); 99 substream = snd_timer_chip(timer);
104 spin_lock_irqsave(&substream->timer_lock, flags);
105 substream->timer_running = 0; 100 substream->timer_running = 0;
106 spin_unlock_irqrestore(&substream->timer_lock, flags);
107 return 0; 101 return 0;
108} 102}
109 103