diff options
Diffstat (limited to 'sound/core/pcm_lib.c')
-rw-r--r-- | sound/core/pcm_lib.c | 445 |
1 files changed, 245 insertions, 200 deletions
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index a27545b23ee9..b546ac2660f9 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c | |||
@@ -126,17 +126,6 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram | |||
126 | } | 126 | } |
127 | } | 127 | } |
128 | 128 | ||
129 | #ifdef CONFIG_SND_PCM_XRUN_DEBUG | ||
130 | #define xrun_debug(substream, mask) ((substream)->pstr->xrun_debug & (mask)) | ||
131 | #else | ||
132 | #define xrun_debug(substream, mask) 0 | ||
133 | #endif | ||
134 | |||
135 | #define dump_stack_on_xrun(substream) do { \ | ||
136 | if (xrun_debug(substream, 2)) \ | ||
137 | dump_stack(); \ | ||
138 | } while (0) | ||
139 | |||
140 | static void pcm_debug_name(struct snd_pcm_substream *substream, | 129 | static void pcm_debug_name(struct snd_pcm_substream *substream, |
141 | char *name, size_t len) | 130 | char *name, size_t len) |
142 | { | 131 | { |
@@ -147,6 +136,24 @@ static void pcm_debug_name(struct snd_pcm_substream *substream, | |||
147 | substream->number); | 136 | substream->number); |
148 | } | 137 | } |
149 | 138 | ||
139 | #define XRUN_DEBUG_BASIC (1<<0) | ||
140 | #define XRUN_DEBUG_STACK (1<<1) /* dump also stack */ | ||
141 | #define XRUN_DEBUG_JIFFIESCHECK (1<<2) /* do jiffies check */ | ||
142 | #define XRUN_DEBUG_PERIODUPDATE (1<<3) /* full period update info */ | ||
143 | #define XRUN_DEBUG_HWPTRUPDATE (1<<4) /* full hwptr update info */ | ||
144 | #define XRUN_DEBUG_LOG (1<<5) /* show last 10 positions on err */ | ||
145 | #define XRUN_DEBUG_LOGONCE (1<<6) /* do above only once */ | ||
146 | |||
147 | #ifdef CONFIG_SND_PCM_XRUN_DEBUG | ||
148 | |||
149 | #define xrun_debug(substream, mask) \ | ||
150 | ((substream)->pstr->xrun_debug & (mask)) | ||
151 | |||
152 | #define dump_stack_on_xrun(substream) do { \ | ||
153 | if (xrun_debug(substream, XRUN_DEBUG_STACK)) \ | ||
154 | dump_stack(); \ | ||
155 | } while (0) | ||
156 | |||
150 | static void xrun(struct snd_pcm_substream *substream) | 157 | static void xrun(struct snd_pcm_substream *substream) |
151 | { | 158 | { |
152 | struct snd_pcm_runtime *runtime = substream->runtime; | 159 | struct snd_pcm_runtime *runtime = substream->runtime; |
@@ -154,7 +161,7 @@ static void xrun(struct snd_pcm_substream *substream) | |||
154 | if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) | 161 | if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) |
155 | snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); | 162 | snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); |
156 | snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); | 163 | snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); |
157 | if (xrun_debug(substream, 1)) { | 164 | if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { |
158 | char name[16]; | 165 | char name[16]; |
159 | pcm_debug_name(substream, name, sizeof(name)); | 166 | pcm_debug_name(substream, name, sizeof(name)); |
160 | snd_printd(KERN_DEBUG "XRUN: %s\n", name); | 167 | snd_printd(KERN_DEBUG "XRUN: %s\n", name); |
@@ -162,32 +169,102 @@ static void xrun(struct snd_pcm_substream *substream) | |||
162 | } | 169 | } |
163 | } | 170 | } |
164 | 171 | ||
165 | static snd_pcm_uframes_t | 172 | #define hw_ptr_error(substream, fmt, args...) \ |
166 | snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream, | 173 | do { \ |
167 | struct snd_pcm_runtime *runtime) | 174 | if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \ |
168 | { | 175 | xrun_log_show(substream); \ |
176 | if (printk_ratelimit()) { \ | ||
177 | snd_printd("PCM: " fmt, ##args); \ | ||
178 | } \ | ||
179 | dump_stack_on_xrun(substream); \ | ||
180 | } \ | ||
181 | } while (0) | ||
182 | |||
183 | #define XRUN_LOG_CNT 10 | ||
184 | |||
185 | struct hwptr_log_entry { | ||
186 | unsigned long jiffies; | ||
169 | snd_pcm_uframes_t pos; | 187 | snd_pcm_uframes_t pos; |
188 | snd_pcm_uframes_t period_size; | ||
189 | snd_pcm_uframes_t buffer_size; | ||
190 | snd_pcm_uframes_t old_hw_ptr; | ||
191 | snd_pcm_uframes_t hw_ptr_base; | ||
192 | }; | ||
170 | 193 | ||
171 | pos = substream->ops->pointer(substream); | 194 | struct snd_pcm_hwptr_log { |
172 | if (pos == SNDRV_PCM_POS_XRUN) | 195 | unsigned int idx; |
173 | return pos; /* XRUN */ | 196 | unsigned int hit: 1; |
174 | if (pos >= runtime->buffer_size) { | 197 | struct hwptr_log_entry entries[XRUN_LOG_CNT]; |
175 | if (printk_ratelimit()) { | 198 | }; |
176 | char name[16]; | 199 | |
177 | pcm_debug_name(substream, name, sizeof(name)); | 200 | static void xrun_log(struct snd_pcm_substream *substream, |
178 | snd_printd(KERN_ERR "BUG: %s, pos = 0x%lx, " | 201 | snd_pcm_uframes_t pos) |
179 | "buffer size = 0x%lx, period size = 0x%lx\n", | 202 | { |
180 | name, pos, runtime->buffer_size, | 203 | struct snd_pcm_runtime *runtime = substream->runtime; |
181 | runtime->period_size); | 204 | struct snd_pcm_hwptr_log *log = runtime->hwptr_log; |
182 | } | 205 | struct hwptr_log_entry *entry; |
183 | pos = 0; | 206 | |
207 | if (log == NULL) { | ||
208 | log = kzalloc(sizeof(*log), GFP_ATOMIC); | ||
209 | if (log == NULL) | ||
210 | return; | ||
211 | runtime->hwptr_log = log; | ||
212 | } else { | ||
213 | if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit) | ||
214 | return; | ||
184 | } | 215 | } |
185 | pos -= pos % runtime->min_align; | 216 | entry = &log->entries[log->idx]; |
186 | return pos; | 217 | entry->jiffies = jiffies; |
218 | entry->pos = pos; | ||
219 | entry->period_size = runtime->period_size; | ||
220 | entry->buffer_size = runtime->buffer_size;; | ||
221 | entry->old_hw_ptr = runtime->status->hw_ptr; | ||
222 | entry->hw_ptr_base = runtime->hw_ptr_base; | ||
223 | log->idx = (log->idx + 1) % XRUN_LOG_CNT; | ||
224 | } | ||
225 | |||
226 | static void xrun_log_show(struct snd_pcm_substream *substream) | ||
227 | { | ||
228 | struct snd_pcm_hwptr_log *log = substream->runtime->hwptr_log; | ||
229 | struct hwptr_log_entry *entry; | ||
230 | char name[16]; | ||
231 | unsigned int idx; | ||
232 | int cnt; | ||
233 | |||
234 | if (log == NULL) | ||
235 | return; | ||
236 | if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit) | ||
237 | return; | ||
238 | pcm_debug_name(substream, name, sizeof(name)); | ||
239 | for (cnt = 0, idx = log->idx; cnt < XRUN_LOG_CNT; cnt++) { | ||
240 | entry = &log->entries[idx]; | ||
241 | if (entry->period_size == 0) | ||
242 | break; | ||
243 | snd_printd("hwptr log: %s: j=%lu, pos=%ld/%ld/%ld, " | ||
244 | "hwptr=%ld/%ld\n", | ||
245 | name, entry->jiffies, (unsigned long)entry->pos, | ||
246 | (unsigned long)entry->period_size, | ||
247 | (unsigned long)entry->buffer_size, | ||
248 | (unsigned long)entry->old_hw_ptr, | ||
249 | (unsigned long)entry->hw_ptr_base); | ||
250 | idx++; | ||
251 | idx %= XRUN_LOG_CNT; | ||
252 | } | ||
253 | log->hit = 1; | ||
187 | } | 254 | } |
188 | 255 | ||
189 | static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream, | 256 | #else /* ! CONFIG_SND_PCM_XRUN_DEBUG */ |
190 | struct snd_pcm_runtime *runtime) | 257 | |
258 | #define xrun_debug(substream, mask) 0 | ||
259 | #define xrun(substream) do { } while (0) | ||
260 | #define hw_ptr_error(substream, fmt, args...) do { } while (0) | ||
261 | #define xrun_log(substream, pos) do { } while (0) | ||
262 | #define xrun_log_show(substream) do { } while (0) | ||
263 | |||
264 | #endif | ||
265 | |||
266 | int snd_pcm_update_state(struct snd_pcm_substream *substream, | ||
267 | struct snd_pcm_runtime *runtime) | ||
191 | { | 268 | { |
192 | snd_pcm_uframes_t avail; | 269 | snd_pcm_uframes_t avail; |
193 | 270 | ||
@@ -209,88 +286,94 @@ static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream, | |||
209 | } | 286 | } |
210 | } | 287 | } |
211 | if (avail >= runtime->control->avail_min) | 288 | if (avail >= runtime->control->avail_min) |
212 | wake_up(&runtime->sleep); | 289 | wake_up(runtime->twake ? &runtime->tsleep : &runtime->sleep); |
213 | return 0; | 290 | return 0; |
214 | } | 291 | } |
215 | 292 | ||
216 | #define hw_ptr_error(substream, fmt, args...) \ | 293 | static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, |
217 | do { \ | 294 | unsigned int in_interrupt) |
218 | if (xrun_debug(substream, 1)) { \ | ||
219 | if (printk_ratelimit()) { \ | ||
220 | snd_printd("PCM: " fmt, ##args); \ | ||
221 | } \ | ||
222 | dump_stack_on_xrun(substream); \ | ||
223 | } \ | ||
224 | } while (0) | ||
225 | |||
226 | static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) | ||
227 | { | 295 | { |
228 | struct snd_pcm_runtime *runtime = substream->runtime; | 296 | struct snd_pcm_runtime *runtime = substream->runtime; |
229 | snd_pcm_uframes_t pos; | 297 | snd_pcm_uframes_t pos; |
230 | snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_ptr_interrupt, hw_base; | 298 | snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base; |
231 | snd_pcm_sframes_t hdelta, delta; | 299 | snd_pcm_sframes_t hdelta, delta; |
232 | unsigned long jdelta; | 300 | unsigned long jdelta; |
233 | 301 | ||
234 | old_hw_ptr = runtime->status->hw_ptr; | 302 | old_hw_ptr = runtime->status->hw_ptr; |
235 | pos = snd_pcm_update_hw_ptr_pos(substream, runtime); | 303 | pos = substream->ops->pointer(substream); |
236 | if (pos == SNDRV_PCM_POS_XRUN) { | 304 | if (pos == SNDRV_PCM_POS_XRUN) { |
237 | xrun(substream); | 305 | xrun(substream); |
238 | return -EPIPE; | 306 | return -EPIPE; |
239 | } | 307 | } |
240 | if (xrun_debug(substream, 8)) { | 308 | if (pos >= runtime->buffer_size) { |
241 | char name[16]; | 309 | if (printk_ratelimit()) { |
242 | pcm_debug_name(substream, name, sizeof(name)); | 310 | char name[16]; |
243 | snd_printd("period_update: %s: pos=0x%x/0x%x/0x%x, " | 311 | pcm_debug_name(substream, name, sizeof(name)); |
244 | "hwptr=0x%lx, hw_base=0x%lx, hw_intr=0x%lx\n", | 312 | xrun_log_show(substream); |
245 | name, (unsigned int)pos, | 313 | snd_printd(KERN_ERR "BUG: %s, pos = %ld, " |
246 | (unsigned int)runtime->period_size, | 314 | "buffer size = %ld, period size = %ld\n", |
247 | (unsigned int)runtime->buffer_size, | 315 | name, pos, runtime->buffer_size, |
248 | (unsigned long)old_hw_ptr, | 316 | runtime->period_size); |
249 | (unsigned long)runtime->hw_ptr_base, | 317 | } |
250 | (unsigned long)runtime->hw_ptr_interrupt); | 318 | pos = 0; |
251 | } | 319 | } |
320 | pos -= pos % runtime->min_align; | ||
321 | if (xrun_debug(substream, XRUN_DEBUG_LOG)) | ||
322 | xrun_log(substream, pos); | ||
252 | hw_base = runtime->hw_ptr_base; | 323 | hw_base = runtime->hw_ptr_base; |
253 | new_hw_ptr = hw_base + pos; | 324 | new_hw_ptr = hw_base + pos; |
254 | hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size; | 325 | if (in_interrupt) { |
255 | delta = new_hw_ptr - hw_ptr_interrupt; | 326 | /* we know that one period was processed */ |
256 | if (hw_ptr_interrupt >= runtime->boundary) { | 327 | /* delta = "expected next hw_ptr" for in_interrupt != 0 */ |
257 | hw_ptr_interrupt -= runtime->boundary; | 328 | delta = runtime->hw_ptr_interrupt + runtime->period_size; |
258 | if (hw_base < runtime->boundary / 2) | 329 | if (delta > new_hw_ptr) { |
259 | /* hw_base was already lapped; recalc delta */ | ||
260 | delta = new_hw_ptr - hw_ptr_interrupt; | ||
261 | } | ||
262 | if (delta < 0) { | ||
263 | if (runtime->periods == 1 || new_hw_ptr < old_hw_ptr) | ||
264 | delta += runtime->buffer_size; | ||
265 | if (delta < 0) { | ||
266 | hw_ptr_error(substream, | ||
267 | "Unexpected hw_pointer value " | ||
268 | "(stream=%i, pos=%ld, intr_ptr=%ld)\n", | ||
269 | substream->stream, (long)pos, | ||
270 | (long)hw_ptr_interrupt); | ||
271 | #if 1 | ||
272 | /* simply skipping the hwptr update seems more | ||
273 | * robust in some cases, e.g. on VMware with | ||
274 | * inaccurate timer source | ||
275 | */ | ||
276 | return 0; /* skip this update */ | ||
277 | #else | ||
278 | /* rebase to interrupt position */ | ||
279 | hw_base = new_hw_ptr = hw_ptr_interrupt; | ||
280 | /* align hw_base to buffer_size */ | ||
281 | hw_base -= hw_base % runtime->buffer_size; | ||
282 | delta = 0; | ||
283 | #endif | ||
284 | } else { | ||
285 | hw_base += runtime->buffer_size; | 330 | hw_base += runtime->buffer_size; |
286 | if (hw_base >= runtime->boundary) | 331 | if (hw_base >= runtime->boundary) |
287 | hw_base = 0; | 332 | hw_base = 0; |
288 | new_hw_ptr = hw_base + pos; | 333 | new_hw_ptr = hw_base + pos; |
334 | goto __delta; | ||
289 | } | 335 | } |
290 | } | 336 | } |
337 | /* new_hw_ptr might be lower than old_hw_ptr in case when */ | ||
338 | /* pointer crosses the end of the ring buffer */ | ||
339 | if (new_hw_ptr < old_hw_ptr) { | ||
340 | hw_base += runtime->buffer_size; | ||
341 | if (hw_base >= runtime->boundary) | ||
342 | hw_base = 0; | ||
343 | new_hw_ptr = hw_base + pos; | ||
344 | } | ||
345 | __delta: | ||
346 | delta = (new_hw_ptr - old_hw_ptr) % runtime->boundary; | ||
347 | if (xrun_debug(substream, in_interrupt ? | ||
348 | XRUN_DEBUG_PERIODUPDATE : XRUN_DEBUG_HWPTRUPDATE)) { | ||
349 | char name[16]; | ||
350 | pcm_debug_name(substream, name, sizeof(name)); | ||
351 | snd_printd("%s_update: %s: pos=%u/%u/%u, " | ||
352 | "hwptr=%ld/%ld/%ld/%ld\n", | ||
353 | in_interrupt ? "period" : "hwptr", | ||
354 | name, | ||
355 | (unsigned int)pos, | ||
356 | (unsigned int)runtime->period_size, | ||
357 | (unsigned int)runtime->buffer_size, | ||
358 | (unsigned long)delta, | ||
359 | (unsigned long)old_hw_ptr, | ||
360 | (unsigned long)new_hw_ptr, | ||
361 | (unsigned long)runtime->hw_ptr_base); | ||
362 | } | ||
363 | /* something must be really wrong */ | ||
364 | if (delta >= runtime->buffer_size + runtime->period_size) { | ||
365 | hw_ptr_error(substream, | ||
366 | "Unexpected hw_pointer value %s" | ||
367 | "(stream=%i, pos=%ld, new_hw_ptr=%ld, " | ||
368 | "old_hw_ptr=%ld)\n", | ||
369 | in_interrupt ? "[Q] " : "[P]", | ||
370 | substream->stream, (long)pos, | ||
371 | (long)new_hw_ptr, (long)old_hw_ptr); | ||
372 | return 0; | ||
373 | } | ||
291 | 374 | ||
292 | /* Do jiffies check only in xrun_debug mode */ | 375 | /* Do jiffies check only in xrun_debug mode */ |
293 | if (!xrun_debug(substream, 4)) | 376 | if (!xrun_debug(substream, XRUN_DEBUG_JIFFIESCHECK)) |
294 | goto no_jiffies_check; | 377 | goto no_jiffies_check; |
295 | 378 | ||
296 | /* Skip the jiffies check for hardwares with BATCH flag. | 379 | /* Skip the jiffies check for hardwares with BATCH flag. |
@@ -299,7 +382,7 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) | |||
299 | */ | 382 | */ |
300 | if (runtime->hw.info & SNDRV_PCM_INFO_BATCH) | 383 | if (runtime->hw.info & SNDRV_PCM_INFO_BATCH) |
301 | goto no_jiffies_check; | 384 | goto no_jiffies_check; |
302 | hdelta = new_hw_ptr - old_hw_ptr; | 385 | hdelta = delta; |
303 | if (hdelta < runtime->delay) | 386 | if (hdelta < runtime->delay) |
304 | goto no_jiffies_check; | 387 | goto no_jiffies_check; |
305 | hdelta -= runtime->delay; | 388 | hdelta -= runtime->delay; |
@@ -308,130 +391,68 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream) | |||
308 | delta = jdelta / | 391 | delta = jdelta / |
309 | (((runtime->period_size * HZ) / runtime->rate) | 392 | (((runtime->period_size * HZ) / runtime->rate) |
310 | + HZ/100); | 393 | + HZ/100); |
394 | /* move new_hw_ptr according jiffies not pos variable */ | ||
395 | new_hw_ptr = old_hw_ptr; | ||
396 | hw_base = delta; | ||
397 | /* use loop to avoid checks for delta overflows */ | ||
398 | /* the delta value is small or zero in most cases */ | ||
399 | while (delta > 0) { | ||
400 | new_hw_ptr += runtime->period_size; | ||
401 | if (new_hw_ptr >= runtime->boundary) | ||
402 | new_hw_ptr -= runtime->boundary; | ||
403 | delta--; | ||
404 | } | ||
405 | /* align hw_base to buffer_size */ | ||
311 | hw_ptr_error(substream, | 406 | hw_ptr_error(substream, |
312 | "hw_ptr skipping! [Q] " | 407 | "hw_ptr skipping! %s" |
313 | "(pos=%ld, delta=%ld, period=%ld, " | 408 | "(pos=%ld, delta=%ld, period=%ld, " |
314 | "jdelta=%lu/%lu/%lu)\n", | 409 | "jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)\n", |
410 | in_interrupt ? "[Q] " : "", | ||
315 | (long)pos, (long)hdelta, | 411 | (long)pos, (long)hdelta, |
316 | (long)runtime->period_size, jdelta, | 412 | (long)runtime->period_size, jdelta, |
317 | ((hdelta * HZ) / runtime->rate), delta); | 413 | ((hdelta * HZ) / runtime->rate), hw_base, |
318 | hw_ptr_interrupt = runtime->hw_ptr_interrupt + | 414 | (unsigned long)old_hw_ptr, |
319 | runtime->period_size * delta; | 415 | (unsigned long)new_hw_ptr); |
320 | if (hw_ptr_interrupt >= runtime->boundary) | 416 | /* reset values to proper state */ |
321 | hw_ptr_interrupt -= runtime->boundary; | ||
322 | /* rebase to interrupt position */ | ||
323 | hw_base = new_hw_ptr = hw_ptr_interrupt; | ||
324 | /* align hw_base to buffer_size */ | ||
325 | hw_base -= hw_base % runtime->buffer_size; | ||
326 | delta = 0; | 417 | delta = 0; |
418 | hw_base = new_hw_ptr - (new_hw_ptr % runtime->buffer_size); | ||
327 | } | 419 | } |
328 | no_jiffies_check: | 420 | no_jiffies_check: |
329 | if (delta > runtime->period_size + runtime->period_size / 2) { | 421 | if (delta > runtime->period_size + runtime->period_size / 2) { |
330 | hw_ptr_error(substream, | 422 | hw_ptr_error(substream, |
331 | "Lost interrupts? " | 423 | "Lost interrupts? %s" |
332 | "(stream=%i, delta=%ld, intr_ptr=%ld)\n", | 424 | "(stream=%i, delta=%ld, new_hw_ptr=%ld, " |
425 | "old_hw_ptr=%ld)\n", | ||
426 | in_interrupt ? "[Q] " : "", | ||
333 | substream->stream, (long)delta, | 427 | substream->stream, (long)delta, |
334 | (long)hw_ptr_interrupt); | 428 | (long)new_hw_ptr, |
335 | /* rebase hw_ptr_interrupt */ | 429 | (long)old_hw_ptr); |
336 | hw_ptr_interrupt = | ||
337 | new_hw_ptr - new_hw_ptr % runtime->period_size; | ||
338 | } | 430 | } |
339 | runtime->hw_ptr_interrupt = hw_ptr_interrupt; | 431 | |
432 | if (runtime->status->hw_ptr == new_hw_ptr) | ||
433 | return 0; | ||
340 | 434 | ||
341 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && | 435 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && |
342 | runtime->silence_size > 0) | 436 | runtime->silence_size > 0) |
343 | snd_pcm_playback_silence(substream, new_hw_ptr); | 437 | snd_pcm_playback_silence(substream, new_hw_ptr); |
344 | 438 | ||
345 | if (runtime->status->hw_ptr == new_hw_ptr) | 439 | if (in_interrupt) { |
346 | return 0; | 440 | runtime->hw_ptr_interrupt = new_hw_ptr - |
347 | 441 | (new_hw_ptr % runtime->period_size); | |
442 | } | ||
348 | runtime->hw_ptr_base = hw_base; | 443 | runtime->hw_ptr_base = hw_base; |
349 | runtime->status->hw_ptr = new_hw_ptr; | 444 | runtime->status->hw_ptr = new_hw_ptr; |
350 | runtime->hw_ptr_jiffies = jiffies; | 445 | runtime->hw_ptr_jiffies = jiffies; |
351 | if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) | 446 | if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) |
352 | snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); | 447 | snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); |
353 | 448 | ||
354 | return snd_pcm_update_hw_ptr_post(substream, runtime); | 449 | return snd_pcm_update_state(substream, runtime); |
355 | } | 450 | } |
356 | 451 | ||
357 | /* CAUTION: call it with irq disabled */ | 452 | /* CAUTION: call it with irq disabled */ |
358 | int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) | 453 | int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream) |
359 | { | 454 | { |
360 | struct snd_pcm_runtime *runtime = substream->runtime; | 455 | return snd_pcm_update_hw_ptr0(substream, 0); |
361 | snd_pcm_uframes_t pos; | ||
362 | snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base; | ||
363 | snd_pcm_sframes_t delta; | ||
364 | unsigned long jdelta; | ||
365 | |||
366 | old_hw_ptr = runtime->status->hw_ptr; | ||
367 | pos = snd_pcm_update_hw_ptr_pos(substream, runtime); | ||
368 | if (pos == SNDRV_PCM_POS_XRUN) { | ||
369 | xrun(substream); | ||
370 | return -EPIPE; | ||
371 | } | ||
372 | if (xrun_debug(substream, 16)) { | ||
373 | char name[16]; | ||
374 | pcm_debug_name(substream, name, sizeof(name)); | ||
375 | snd_printd("hw_update: %s: pos=0x%x/0x%x/0x%x, " | ||
376 | "hwptr=0x%lx, hw_base=0x%lx, hw_intr=0x%lx\n", | ||
377 | name, (unsigned int)pos, | ||
378 | (unsigned int)runtime->period_size, | ||
379 | (unsigned int)runtime->buffer_size, | ||
380 | (unsigned long)old_hw_ptr, | ||
381 | (unsigned long)runtime->hw_ptr_base, | ||
382 | (unsigned long)runtime->hw_ptr_interrupt); | ||
383 | } | ||
384 | |||
385 | hw_base = runtime->hw_ptr_base; | ||
386 | new_hw_ptr = hw_base + pos; | ||
387 | |||
388 | delta = new_hw_ptr - old_hw_ptr; | ||
389 | jdelta = jiffies - runtime->hw_ptr_jiffies; | ||
390 | if (delta < 0) { | ||
391 | delta += runtime->buffer_size; | ||
392 | if (delta < 0) { | ||
393 | hw_ptr_error(substream, | ||
394 | "Unexpected hw_pointer value [2] " | ||
395 | "(stream=%i, pos=%ld, old_ptr=%ld, jdelta=%li)\n", | ||
396 | substream->stream, (long)pos, | ||
397 | (long)old_hw_ptr, jdelta); | ||
398 | return 0; | ||
399 | } | ||
400 | hw_base += runtime->buffer_size; | ||
401 | if (hw_base >= runtime->boundary) | ||
402 | hw_base = 0; | ||
403 | new_hw_ptr = hw_base + pos; | ||
404 | } | ||
405 | /* Do jiffies check only in xrun_debug mode */ | ||
406 | if (!xrun_debug(substream, 4)) | ||
407 | goto no_jiffies_check; | ||
408 | if (delta < runtime->delay) | ||
409 | goto no_jiffies_check; | ||
410 | delta -= runtime->delay; | ||
411 | if (((delta * HZ) / runtime->rate) > jdelta + HZ/100) { | ||
412 | hw_ptr_error(substream, | ||
413 | "hw_ptr skipping! " | ||
414 | "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu)\n", | ||
415 | (long)pos, (long)delta, | ||
416 | (long)runtime->period_size, jdelta, | ||
417 | ((delta * HZ) / runtime->rate)); | ||
418 | return 0; | ||
419 | } | ||
420 | no_jiffies_check: | ||
421 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && | ||
422 | runtime->silence_size > 0) | ||
423 | snd_pcm_playback_silence(substream, new_hw_ptr); | ||
424 | |||
425 | if (runtime->status->hw_ptr == new_hw_ptr) | ||
426 | return 0; | ||
427 | |||
428 | runtime->hw_ptr_base = hw_base; | ||
429 | runtime->status->hw_ptr = new_hw_ptr; | ||
430 | runtime->hw_ptr_jiffies = jiffies; | ||
431 | if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) | ||
432 | snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); | ||
433 | |||
434 | return snd_pcm_update_hw_ptr_post(substream, runtime); | ||
435 | } | 456 | } |
436 | 457 | ||
437 | /** | 458 | /** |
@@ -745,10 +766,13 @@ int snd_interval_ratnum(struct snd_interval *i, | |||
745 | unsigned int rats_count, struct snd_ratnum *rats, | 766 | unsigned int rats_count, struct snd_ratnum *rats, |
746 | unsigned int *nump, unsigned int *denp) | 767 | unsigned int *nump, unsigned int *denp) |
747 | { | 768 | { |
748 | unsigned int best_num, best_diff, best_den; | 769 | unsigned int best_num, best_den; |
770 | int best_diff; | ||
749 | unsigned int k; | 771 | unsigned int k; |
750 | struct snd_interval t; | 772 | struct snd_interval t; |
751 | int err; | 773 | int err; |
774 | unsigned int result_num, result_den; | ||
775 | int result_diff; | ||
752 | 776 | ||
753 | best_num = best_den = best_diff = 0; | 777 | best_num = best_den = best_diff = 0; |
754 | for (k = 0; k < rats_count; ++k) { | 778 | for (k = 0; k < rats_count; ++k) { |
@@ -770,6 +794,8 @@ int snd_interval_ratnum(struct snd_interval *i, | |||
770 | den -= r; | 794 | den -= r; |
771 | } | 795 | } |
772 | diff = num - q * den; | 796 | diff = num - q * den; |
797 | if (diff < 0) | ||
798 | diff = -diff; | ||
773 | if (best_num == 0 || | 799 | if (best_num == 0 || |
774 | diff * best_den < best_diff * den) { | 800 | diff * best_den < best_diff * den) { |
775 | best_diff = diff; | 801 | best_diff = diff; |
@@ -784,6 +810,9 @@ int snd_interval_ratnum(struct snd_interval *i, | |||
784 | t.min = div_down(best_num, best_den); | 810 | t.min = div_down(best_num, best_den); |
785 | t.openmin = !!(best_num % best_den); | 811 | t.openmin = !!(best_num % best_den); |
786 | 812 | ||
813 | result_num = best_num; | ||
814 | result_diff = best_diff; | ||
815 | result_den = best_den; | ||
787 | best_num = best_den = best_diff = 0; | 816 | best_num = best_den = best_diff = 0; |
788 | for (k = 0; k < rats_count; ++k) { | 817 | for (k = 0; k < rats_count; ++k) { |
789 | unsigned int num = rats[k].num; | 818 | unsigned int num = rats[k].num; |
@@ -806,6 +835,8 @@ int snd_interval_ratnum(struct snd_interval *i, | |||
806 | den += rats[k].den_step - r; | 835 | den += rats[k].den_step - r; |
807 | } | 836 | } |
808 | diff = q * den - num; | 837 | diff = q * den - num; |
838 | if (diff < 0) | ||
839 | diff = -diff; | ||
809 | if (best_num == 0 || | 840 | if (best_num == 0 || |
810 | diff * best_den < best_diff * den) { | 841 | diff * best_den < best_diff * den) { |
811 | best_diff = diff; | 842 | best_diff = diff; |
@@ -825,10 +856,14 @@ int snd_interval_ratnum(struct snd_interval *i, | |||
825 | return err; | 856 | return err; |
826 | 857 | ||
827 | if (snd_interval_single(i)) { | 858 | if (snd_interval_single(i)) { |
859 | if (best_diff * result_den < result_diff * best_den) { | ||
860 | result_num = best_num; | ||
861 | result_den = best_den; | ||
862 | } | ||
828 | if (nump) | 863 | if (nump) |
829 | *nump = best_num; | 864 | *nump = result_num; |
830 | if (denp) | 865 | if (denp) |
831 | *denp = best_den; | 866 | *denp = result_den; |
832 | } | 867 | } |
833 | return err; | 868 | return err; |
834 | } | 869 | } |
@@ -1643,7 +1678,7 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream) | |||
1643 | 1678 | ||
1644 | snd_pcm_stream_lock_irqsave(substream, flags); | 1679 | snd_pcm_stream_lock_irqsave(substream, flags); |
1645 | if (!snd_pcm_running(substream) || | 1680 | if (!snd_pcm_running(substream) || |
1646 | snd_pcm_update_hw_ptr_interrupt(substream) < 0) | 1681 | snd_pcm_update_hw_ptr0(substream, 1) < 0) |
1647 | goto _end; | 1682 | goto _end; |
1648 | 1683 | ||
1649 | if (substream->timer_running) | 1684 | if (substream->timer_running) |
@@ -1674,7 +1709,7 @@ static int wait_for_avail_min(struct snd_pcm_substream *substream, | |||
1674 | long tout; | 1709 | long tout; |
1675 | 1710 | ||
1676 | init_waitqueue_entry(&wait, current); | 1711 | init_waitqueue_entry(&wait, current); |
1677 | add_wait_queue(&runtime->sleep, &wait); | 1712 | add_wait_queue(&runtime->tsleep, &wait); |
1678 | for (;;) { | 1713 | for (;;) { |
1679 | if (signal_pending(current)) { | 1714 | if (signal_pending(current)) { |
1680 | err = -ERESTARTSYS; | 1715 | err = -ERESTARTSYS; |
@@ -1717,7 +1752,7 @@ static int wait_for_avail_min(struct snd_pcm_substream *substream, | |||
1717 | break; | 1752 | break; |
1718 | } | 1753 | } |
1719 | _endloop: | 1754 | _endloop: |
1720 | remove_wait_queue(&runtime->sleep, &wait); | 1755 | remove_wait_queue(&runtime->tsleep, &wait); |
1721 | *availp = avail; | 1756 | *availp = avail; |
1722 | return err; | 1757 | return err; |
1723 | } | 1758 | } |
@@ -1776,6 +1811,7 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, | |||
1776 | goto _end_unlock; | 1811 | goto _end_unlock; |
1777 | } | 1812 | } |
1778 | 1813 | ||
1814 | runtime->twake = 1; | ||
1779 | while (size > 0) { | 1815 | while (size > 0) { |
1780 | snd_pcm_uframes_t frames, appl_ptr, appl_ofs; | 1816 | snd_pcm_uframes_t frames, appl_ptr, appl_ofs; |
1781 | snd_pcm_uframes_t avail; | 1817 | snd_pcm_uframes_t avail; |
@@ -1797,15 +1833,17 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, | |||
1797 | if (frames > cont) | 1833 | if (frames > cont) |
1798 | frames = cont; | 1834 | frames = cont; |
1799 | if (snd_BUG_ON(!frames)) { | 1835 | if (snd_BUG_ON(!frames)) { |
1836 | runtime->twake = 0; | ||
1800 | snd_pcm_stream_unlock_irq(substream); | 1837 | snd_pcm_stream_unlock_irq(substream); |
1801 | return -EINVAL; | 1838 | return -EINVAL; |
1802 | } | 1839 | } |
1803 | appl_ptr = runtime->control->appl_ptr; | 1840 | appl_ptr = runtime->control->appl_ptr; |
1804 | appl_ofs = appl_ptr % runtime->buffer_size; | 1841 | appl_ofs = appl_ptr % runtime->buffer_size; |
1805 | snd_pcm_stream_unlock_irq(substream); | 1842 | snd_pcm_stream_unlock_irq(substream); |
1806 | if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0) | 1843 | err = transfer(substream, appl_ofs, data, offset, frames); |
1807 | goto _end; | ||
1808 | snd_pcm_stream_lock_irq(substream); | 1844 | snd_pcm_stream_lock_irq(substream); |
1845 | if (err < 0) | ||
1846 | goto _end_unlock; | ||
1809 | switch (runtime->status->state) { | 1847 | switch (runtime->status->state) { |
1810 | case SNDRV_PCM_STATE_XRUN: | 1848 | case SNDRV_PCM_STATE_XRUN: |
1811 | err = -EPIPE; | 1849 | err = -EPIPE; |
@@ -1834,8 +1872,10 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, | |||
1834 | } | 1872 | } |
1835 | } | 1873 | } |
1836 | _end_unlock: | 1874 | _end_unlock: |
1875 | runtime->twake = 0; | ||
1876 | if (xfer > 0 && err >= 0) | ||
1877 | snd_pcm_update_state(substream, runtime); | ||
1837 | snd_pcm_stream_unlock_irq(substream); | 1878 | snd_pcm_stream_unlock_irq(substream); |
1838 | _end: | ||
1839 | return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; | 1879 | return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; |
1840 | } | 1880 | } |
1841 | 1881 | ||
@@ -1993,6 +2033,7 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, | |||
1993 | goto _end_unlock; | 2033 | goto _end_unlock; |
1994 | } | 2034 | } |
1995 | 2035 | ||
2036 | runtime->twake = 1; | ||
1996 | while (size > 0) { | 2037 | while (size > 0) { |
1997 | snd_pcm_uframes_t frames, appl_ptr, appl_ofs; | 2038 | snd_pcm_uframes_t frames, appl_ptr, appl_ofs; |
1998 | snd_pcm_uframes_t avail; | 2039 | snd_pcm_uframes_t avail; |
@@ -2021,15 +2062,17 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, | |||
2021 | if (frames > cont) | 2062 | if (frames > cont) |
2022 | frames = cont; | 2063 | frames = cont; |
2023 | if (snd_BUG_ON(!frames)) { | 2064 | if (snd_BUG_ON(!frames)) { |
2065 | runtime->twake = 0; | ||
2024 | snd_pcm_stream_unlock_irq(substream); | 2066 | snd_pcm_stream_unlock_irq(substream); |
2025 | return -EINVAL; | 2067 | return -EINVAL; |
2026 | } | 2068 | } |
2027 | appl_ptr = runtime->control->appl_ptr; | 2069 | appl_ptr = runtime->control->appl_ptr; |
2028 | appl_ofs = appl_ptr % runtime->buffer_size; | 2070 | appl_ofs = appl_ptr % runtime->buffer_size; |
2029 | snd_pcm_stream_unlock_irq(substream); | 2071 | snd_pcm_stream_unlock_irq(substream); |
2030 | if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0) | 2072 | err = transfer(substream, appl_ofs, data, offset, frames); |
2031 | goto _end; | ||
2032 | snd_pcm_stream_lock_irq(substream); | 2073 | snd_pcm_stream_lock_irq(substream); |
2074 | if (err < 0) | ||
2075 | goto _end_unlock; | ||
2033 | switch (runtime->status->state) { | 2076 | switch (runtime->status->state) { |
2034 | case SNDRV_PCM_STATE_XRUN: | 2077 | case SNDRV_PCM_STATE_XRUN: |
2035 | err = -EPIPE; | 2078 | err = -EPIPE; |
@@ -2052,8 +2095,10 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream, | |||
2052 | xfer += frames; | 2095 | xfer += frames; |
2053 | } | 2096 | } |
2054 | _end_unlock: | 2097 | _end_unlock: |
2098 | runtime->twake = 0; | ||
2099 | if (xfer > 0 && err >= 0) | ||
2100 | snd_pcm_update_state(substream, runtime); | ||
2055 | snd_pcm_stream_unlock_irq(substream); | 2101 | snd_pcm_stream_unlock_irq(substream); |
2056 | _end: | ||
2057 | return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; | 2102 | return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; |
2058 | } | 2103 | } |
2059 | 2104 | ||