aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/sound/pcm.h3
-rw-r--r--sound/core/pcm_lib.c47
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}