diff options
-rw-r--r-- | include/sound/pcm.h | 3 | ||||
-rw-r--r-- | sound/core/pcm_lib.c | 47 |
2 files changed, 41 insertions, 9 deletions
diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 8904b1900d7f..c17296891617 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h | |||
@@ -268,7 +268,8 @@ struct snd_pcm_runtime { | |||
268 | int overrange; | 268 | int overrange; |
269 | snd_pcm_uframes_t avail_max; | 269 | snd_pcm_uframes_t avail_max; |
270 | snd_pcm_uframes_t hw_ptr_base; /* Position at buffer restart */ | 270 | snd_pcm_uframes_t hw_ptr_base; /* Position at buffer restart */ |
271 | snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time*/ | 271 | snd_pcm_uframes_t hw_ptr_interrupt; /* Position at interrupt time */ |
272 | unsigned long hw_ptr_jiffies; /* Time when hw_ptr is updated */ | ||
272 | 273 | ||
273 | /* -- HW params -- */ | 274 | /* -- HW params -- */ |
274 | snd_pcm_access_t access; /* access mode */ | 275 | snd_pcm_access_t access; /* access mode */ |
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index fbb2e391591e..63d088f2265f 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,30 @@ 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 | hdelta = new_hw_ptr - old_hw_ptr; |
253 | jdelta = jiffies - runtime->hw_ptr_jiffies; | ||
254 | if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) { | ||
255 | delta = jdelta / | ||
256 | (((runtime->period_size * HZ) / runtime->rate) | ||
257 | + HZ/100); | ||
258 | hw_ptr_error(substream, | ||
259 | "hw_ptr skipping! [Q] " | ||
260 | "(pos=%ld, delta=%ld, period=%ld, " | ||
261 | "jdelta=%lu/%lu/%lu)\n", | ||
262 | (long)pos, (long)hdelta, | ||
263 | (long)runtime->period_size, jdelta, | ||
264 | ((hdelta * HZ) / runtime->rate), delta); | ||
265 | hw_ptr_interrupt = runtime->hw_ptr_interrupt + | ||
266 | runtime->period_size * delta; | ||
267 | if (hw_ptr_interrupt >= runtime->boundary) | ||
268 | hw_ptr_interrupt -= runtime->boundary; | ||
269 | /* rebase to interrupt position */ | ||
270 | hw_base = new_hw_ptr = hw_ptr_interrupt; | ||
271 | /* align hw_base to buffer_size */ | ||
272 | hw_base -= hw_base % runtime->buffer_size; | ||
273 | delta = 0; | ||
274 | } | ||
275 | if (delta > runtime->period_size + runtime->period_size / 2) { | ||
251 | hw_ptr_error(substream, | 276 | hw_ptr_error(substream, |
252 | "Lost interrupts? " | 277 | "Lost interrupts? " |
253 | "(stream=%i, delta=%ld, intr_ptr=%ld)\n", | 278 | "(stream=%i, delta=%ld, intr_ptr=%ld)\n", |
@@ -263,6 +288,7 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) | |||
263 | 288 | ||
264 | runtime->hw_ptr_base = hw_base; | 289 | runtime->hw_ptr_base = hw_base; |
265 | runtime->status->hw_ptr = new_hw_ptr; | 290 | runtime->status->hw_ptr = new_hw_ptr; |
291 | runtime->hw_ptr_jiffies = jiffies; | ||
266 | runtime->hw_ptr_interrupt = hw_ptr_interrupt; | 292 | runtime->hw_ptr_interrupt = hw_ptr_interrupt; |
267 | 293 | ||
268 | return snd_pcm_update_hw_ptr_post(substream, runtime); | 294 | return snd_pcm_update_hw_ptr_post(substream, runtime); |
@@ -275,6 +301,7 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) | |||
275 | snd_pcm_uframes_t pos; | 301 | snd_pcm_uframes_t pos; |
276 | snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base; | 302 | snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base; |
277 | snd_pcm_sframes_t delta; | 303 | snd_pcm_sframes_t delta; |
304 | unsigned long jdelta; | ||
278 | 305 | ||
279 | old_hw_ptr = runtime->status->hw_ptr; | 306 | old_hw_ptr = runtime->status->hw_ptr; |
280 | pos = snd_pcm_update_hw_ptr_pos(substream, runtime); | 307 | pos = snd_pcm_update_hw_ptr_pos(substream, runtime); |
@@ -286,14 +313,15 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) | |||
286 | new_hw_ptr = hw_base + pos; | 313 | new_hw_ptr = hw_base + pos; |
287 | 314 | ||
288 | delta = new_hw_ptr - old_hw_ptr; | 315 | delta = new_hw_ptr - old_hw_ptr; |
316 | jdelta = jiffies - runtime->hw_ptr_jiffies; | ||
289 | if (delta < 0) { | 317 | if (delta < 0) { |
290 | delta += runtime->buffer_size; | 318 | delta += runtime->buffer_size; |
291 | if (delta < 0) { | 319 | if (delta < 0) { |
292 | hw_ptr_error(substream, | 320 | hw_ptr_error(substream, |
293 | "Unexpected hw_pointer value [2] " | 321 | "Unexpected hw_pointer value [2] " |
294 | "(stream=%i, pos=%ld, old_ptr=%ld)\n", | 322 | "(stream=%i, pos=%ld, old_ptr=%ld, jdelta=%li)\n", |
295 | substream->stream, (long)pos, | 323 | substream->stream, (long)pos, |
296 | (long)old_hw_ptr); | 324 | (long)old_hw_ptr, jdelta); |
297 | return 0; | 325 | return 0; |
298 | } | 326 | } |
299 | hw_base += runtime->buffer_size; | 327 | hw_base += runtime->buffer_size; |
@@ -301,12 +329,13 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) | |||
301 | hw_base = 0; | 329 | hw_base = 0; |
302 | new_hw_ptr = hw_base + pos; | 330 | new_hw_ptr = hw_base + pos; |
303 | } | 331 | } |
304 | if (delta > runtime->period_size && runtime->periods > 1) { | 332 | if (((delta * HZ) / runtime->rate) > jdelta + HZ/100) { |
305 | hw_ptr_error(substream, | 333 | hw_ptr_error(substream, |
306 | "hw_ptr skipping! " | 334 | "hw_ptr skipping! " |
307 | "(pos=%ld, delta=%ld, period=%ld)\n", | 335 | "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu)\n", |
308 | (long)pos, (long)delta, | 336 | (long)pos, (long)delta, |
309 | (long)runtime->period_size); | 337 | (long)runtime->period_size, jdelta, |
338 | ((delta * HZ) / runtime->rate)); | ||
310 | return 0; | 339 | return 0; |
311 | } | 340 | } |
312 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && | 341 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && |
@@ -315,6 +344,7 @@ int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) | |||
315 | 344 | ||
316 | runtime->hw_ptr_base = hw_base; | 345 | runtime->hw_ptr_base = hw_base; |
317 | runtime->status->hw_ptr = new_hw_ptr; | 346 | runtime->status->hw_ptr = new_hw_ptr; |
347 | runtime->hw_ptr_jiffies = jiffies; | ||
318 | 348 | ||
319 | return snd_pcm_update_hw_ptr_post(substream, runtime); | 349 | return snd_pcm_update_hw_ptr_post(substream, runtime); |
320 | } | 350 | } |
@@ -1441,6 +1471,7 @@ static int snd_pcm_lib_ioctl_reset(struct snd_pcm_substream *substream, | |||
1441 | runtime->status->hw_ptr %= runtime->buffer_size; | 1471 | runtime->status->hw_ptr %= runtime->buffer_size; |
1442 | else | 1472 | else |
1443 | runtime->status->hw_ptr = 0; | 1473 | runtime->status->hw_ptr = 0; |
1474 | runtime->hw_ptr_jiffies = jiffies; | ||
1444 | snd_pcm_stream_unlock_irqrestore(substream, flags); | 1475 | snd_pcm_stream_unlock_irqrestore(substream, flags); |
1445 | return 0; | 1476 | return 0; |
1446 | } | 1477 | } |