diff options
Diffstat (limited to 'sound/core/pcm_lib.c')
-rw-r--r-- | sound/core/pcm_lib.c | 54 |
1 files changed, 46 insertions, 8 deletions
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index fbb2e391591e..a2a792c18c40 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c | |||
@@ -209,9 +209,11 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) | |||
209 | { | 209 | { |
210 | struct snd_pcm_runtime *runtime = substream->runtime; | 210 | struct snd_pcm_runtime *runtime = substream->runtime; |
211 | snd_pcm_uframes_t pos; | 211 | snd_pcm_uframes_t pos; |
212 | snd_pcm_uframes_t new_hw_ptr, hw_ptr_interrupt, hw_base; | 212 | snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_ptr_interrupt, hw_base; |
213 | snd_pcm_sframes_t delta; | 213 | snd_pcm_sframes_t hdelta, delta; |
214 | unsigned long jdelta; | ||
214 | 215 | ||
216 | old_hw_ptr = runtime->status->hw_ptr; | ||
215 | pos = snd_pcm_update_hw_ptr_pos(substream, runtime); | 217 | pos = snd_pcm_update_hw_ptr_pos(substream, runtime); |
216 | if (pos == SNDRV_PCM_POS_XRUN) { | 218 | if (pos == SNDRV_PCM_POS_XRUN) { |
217 | xrun(substream); | 219 | xrun(substream); |
@@ -247,7 +249,37 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) | |||
247 | new_hw_ptr = hw_base + pos; | 249 | new_hw_ptr = hw_base + pos; |
248 | } | 250 | } |
249 | } | 251 | } |
250 | if (delta > runtime->period_size) { | 252 | /* Skip the jiffies check for hardwares with BATCH flag. |
253 | * Such hardware usually just increases the position at each IRQ, | ||
254 | * thus it can't give any strange position. | ||
255 | */ | ||
256 | if (runtime->hw.info & SNDRV_PCM_INFO_BATCH) | ||
257 | goto no_jiffies_check; | ||
258 | hdelta = new_hw_ptr - old_hw_ptr; | ||
259 | jdelta = jiffies - runtime->hw_ptr_jiffies; | ||
260 | if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) { | ||
261 | delta = jdelta / | ||
262 | (((runtime->period_size * HZ) / runtime->rate) | ||
263 | + HZ/100); | ||
264 | hw_ptr_error(substream, | ||
265 | "hw_ptr skipping! [Q] " | ||
266 | "(pos=%ld, delta=%ld, period=%ld, " | ||
267 | "jdelta=%lu/%lu/%lu)\n", | ||
268 | (long)pos, (long)hdelta, | ||
269 | (long)runtime->period_size, jdelta, | ||
270 | ((hdelta * HZ) / runtime->rate), delta); | ||
271 | hw_ptr_interrupt = runtime->hw_ptr_interrupt + | ||
272 | runtime->period_size * delta; | ||
273 | if (hw_ptr_interrupt >= runtime->boundary) | ||
274 | hw_ptr_interrupt -= runtime->boundary; | ||
275 | /* rebase to interrupt position */ | ||
276 | hw_base = new_hw_ptr = hw_ptr_interrupt; | ||
277 | /* align hw_base to buffer_size */ | ||
278 | hw_base -= hw_base % runtime->buffer_size; | ||
279 | delta = 0; | ||
280 | } | ||
281 | no_jiffies_check: | ||
282 | if (delta > runtime->period_size + runtime->period_size / 2) { | ||
251 | hw_ptr_error(substream, | 283 | hw_ptr_error(substream, |
252 | "Lost interrupts? " | 284 | "Lost interrupts? " |
253 | "(stream=%i, delta=%ld, intr_ptr=%ld)\n", | 285 | "(stream=%i, delta=%ld, intr_ptr=%ld)\n", |
@@ -263,6 +295,7 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) | |||
263 | 295 | ||
264 | runtime->hw_ptr_base = hw_base; | 296 | runtime->hw_ptr_base = hw_base; |
265 | runtime->status->hw_ptr = new_hw_ptr; | 297 | runtime->status->hw_ptr = new_hw_ptr; |
298 | runtime->hw_ptr_jiffies = jiffies; | ||
266 | runtime->hw_ptr_interrupt = hw_ptr_interrupt; | 299 | runtime->hw_ptr_interrupt = hw_ptr_interrupt; |
267 | 300 | ||
268 | return snd_pcm_update_hw_ptr_post(substream, runtime); | 301 | return snd_pcm_update_hw_ptr_post(substream, runtime); |
@@ -275,6 +308,7 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) | |||
275 | snd_pcm_uframes_t pos; | 308 | snd_pcm_uframes_t pos; |
276 | snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base; | 309 | snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base; |
277 | snd_pcm_sframes_t delta; | 310 | snd_pcm_sframes_t delta; |
311 | unsigned long jdelta; | ||
278 | 312 | ||
279 | old_hw_ptr = runtime->status->hw_ptr; | 313 | old_hw_ptr = runtime->status->hw_ptr; |
280 | pos = snd_pcm_update_hw_ptr_pos(substream, runtime); | 314 | pos = snd_pcm_update_hw_ptr_pos(substream, runtime); |
@@ -286,14 +320,15 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) | |||
286 | new_hw_ptr = hw_base + pos; | 320 | new_hw_ptr = hw_base + pos; |
287 | 321 | ||
288 | delta = new_hw_ptr - old_hw_ptr; | 322 | delta = new_hw_ptr - old_hw_ptr; |
323 | jdelta = jiffies - runtime->hw_ptr_jiffies; | ||
289 | if (delta < 0) { | 324 | if (delta < 0) { |
290 | delta += runtime->buffer_size; | 325 | delta += runtime->buffer_size; |
291 | if (delta < 0) { | 326 | if (delta < 0) { |
292 | hw_ptr_error(substream, | 327 | hw_ptr_error(substream, |
293 | "Unexpected hw_pointer value [2] " | 328 | "Unexpected hw_pointer value [2] " |
294 | "(stream=%i, pos=%ld, old_ptr=%ld)\n", | 329 | "(stream=%i, pos=%ld, old_ptr=%ld, jdelta=%li)\n", |
295 | substream->stream, (long)pos, | 330 | substream->stream, (long)pos, |
296 | (long)old_hw_ptr); | 331 | (long)old_hw_ptr, jdelta); |
297 | return 0; | 332 | return 0; |
298 | } | 333 | } |
299 | hw_base += runtime->buffer_size; | 334 | hw_base += runtime->buffer_size; |
@@ -301,12 +336,13 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) | |||
301 | hw_base = 0; | 336 | hw_base = 0; |
302 | new_hw_ptr = hw_base + pos; | 337 | new_hw_ptr = hw_base + pos; |
303 | } | 338 | } |
304 | if (delta > runtime->period_size && runtime->periods > 1) { | 339 | if (((delta * HZ) / runtime->rate) > jdelta + HZ/100) { |
305 | hw_ptr_error(substream, | 340 | hw_ptr_error(substream, |
306 | "hw_ptr skipping! " | 341 | "hw_ptr skipping! " |
307 | "(pos=%ld, delta=%ld, period=%ld)\n", | 342 | "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu)\n", |
308 | (long)pos, (long)delta, | 343 | (long)pos, (long)delta, |
309 | (long)runtime->period_size); | 344 | (long)runtime->period_size, jdelta, |
345 | ((delta * HZ) / runtime->rate)); | ||
310 | return 0; | 346 | return 0; |
311 | } | 347 | } |
312 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && | 348 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && |
@@ -315,6 +351,7 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) | |||
315 | 351 | ||
316 | runtime->hw_ptr_base = hw_base; | 352 | runtime->hw_ptr_base = hw_base; |
317 | runtime->status->hw_ptr = new_hw_ptr; | 353 | runtime->status->hw_ptr = new_hw_ptr; |
354 | runtime->hw_ptr_jiffies = jiffies; | ||
318 | 355 | ||
319 | return snd_pcm_update_hw_ptr_post(substream, runtime); | 356 | return snd_pcm_update_hw_ptr_post(substream, runtime); |
320 | } | 357 | } |
@@ -1441,6 +1478,7 @@ static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream, | |||
1441 | runtime->status->hw_ptr %= runtime->buffer_size; | 1478 | runtime->status->hw_ptr %= runtime->buffer_size; |
1442 | else | 1479 | else |
1443 | runtime->status->hw_ptr = 0; | 1480 | runtime->status->hw_ptr = 0; |
1481 | runtime->hw_ptr_jiffies = jiffies; | ||
1444 | snd_pcm_stream_unlock_irqrestore(substream, flags); | 1482 | snd_pcm_stream_unlock_irqrestore(substream, flags); |
1445 | return 0; | 1483 | return 0; |
1446 | } | 1484 | } |