diff options
-rw-r--r-- | include/sound/pcm.h | 1 | ||||
-rw-r--r-- | sound/core/pcm.c | 1 | ||||
-rw-r--r-- | sound/core/pcm_lib.c | 155 | ||||
-rw-r--r-- | sound/core/pcm_timer.c | 6 |
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 | |||
128 | static void xrun(struct snd_pcm_substream *substream) | 139 | static 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 | ||
143 | static inline snd_pcm_uframes_t snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream, | 151 | static snd_pcm_uframes_t |
144 | struct snd_pcm_runtime *runtime) | 152 | snd_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 | ||
162 | static inline int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream, | 175 | static 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 | ||
185 | static 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 | |||
208 | static 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 | ||
86 | static int snd_pcm_timer_start(struct snd_timer * timer) | 86 | static 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 | ||
98 | static int snd_pcm_timer_stop(struct snd_timer * timer) | 95 | static 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 | ||