diff options
author | Takashi Iwai <tiwai@suse.de> | 2009-06-05 10:11:07 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2009-06-05 10:44:13 -0400 |
commit | b7bbf876087e0e2c0ba723a8398083c9a9ac1dfd (patch) | |
tree | 69a3e70658fc751ffc99eef5a6f047b19f61a4a2 /sound/pci/ctxfi/ctpcm.c | |
parent | 6bc5874a1ddf98ac0fe6c4eab7d286c11cb1c748 (diff) |
ALSA: ctxfi - Use native timer interrupt on emu20k1
emu20k1 has a native timer interrupt based on the audio clock, which
is more accurate than the system timer (from the synchronization POV).
This patch adds the code to handle this with multiple streams.
The system timer is still used on emu20k2, and can be used also for
emu20k1 easily by changing USE_SYSTEM_TIMER to 1 in cttimer.c.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/ctxfi/ctpcm.c')
-rw-r--r-- | sound/pci/ctxfi/ctpcm.c | 106 |
1 files changed, 10 insertions, 96 deletions
diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c index 52ddf19d83bb..32b742dcd538 100644 --- a/sound/pci/ctxfi/ctpcm.c +++ b/sound/pci/ctxfi/ctpcm.c | |||
@@ -16,6 +16,7 @@ | |||
16 | */ | 16 | */ |
17 | 17 | ||
18 | #include "ctpcm.h" | 18 | #include "ctpcm.h" |
19 | #include "cttimer.h" | ||
19 | #include <sound/pcm.h> | 20 | #include <sound/pcm.h> |
20 | 21 | ||
21 | /* Hardware descriptions for playback */ | 22 | /* Hardware descriptions for playback */ |
@@ -108,6 +109,7 @@ static void ct_atc_pcm_free_substream(struct snd_pcm_runtime *runtime) | |||
108 | struct ct_atc *atc = snd_pcm_substream_chip(apcm->substream); | 109 | struct ct_atc *atc = snd_pcm_substream_chip(apcm->substream); |
109 | 110 | ||
110 | atc->pcm_release_resources(atc, apcm); | 111 | atc->pcm_release_resources(atc, apcm); |
112 | ct_timer_instance_free(apcm->timer); | ||
111 | kfree(apcm); | 113 | kfree(apcm); |
112 | runtime->private_data = NULL; | 114 | runtime->private_data = NULL; |
113 | } | 115 | } |
@@ -124,8 +126,6 @@ static int ct_pcm_playback_open(struct snd_pcm_substream *substream) | |||
124 | if (NULL == apcm) | 126 | if (NULL == apcm) |
125 | return -ENOMEM; | 127 | return -ENOMEM; |
126 | 128 | ||
127 | spin_lock_init(&apcm->timer_lock); | ||
128 | apcm->stop_timer = 0; | ||
129 | apcm->substream = substream; | 129 | apcm->substream = substream; |
130 | apcm->interrupt = ct_atc_pcm_interrupt; | 130 | apcm->interrupt = ct_atc_pcm_interrupt; |
131 | runtime->private_data = apcm; | 131 | runtime->private_data = apcm; |
@@ -153,6 +153,10 @@ static int ct_pcm_playback_open(struct snd_pcm_substream *substream) | |||
153 | return err; | 153 | return err; |
154 | } | 154 | } |
155 | 155 | ||
156 | apcm->timer = ct_timer_instance_new(atc->timer, apcm); | ||
157 | if (!apcm->timer) | ||
158 | return -ENOMEM; | ||
159 | |||
156 | return 0; | 160 | return 0; |
157 | } | 161 | } |
158 | 162 | ||
@@ -182,89 +186,6 @@ static int ct_pcm_hw_free(struct snd_pcm_substream *substream) | |||
182 | return snd_pcm_lib_free_pages(substream); | 186 | return snd_pcm_lib_free_pages(substream); |
183 | } | 187 | } |
184 | 188 | ||
185 | static void ct_pcm_timer_callback(unsigned long data) | ||
186 | { | ||
187 | struct ct_atc_pcm *apcm = (struct ct_atc_pcm *)data; | ||
188 | struct snd_pcm_substream *substream = apcm->substream; | ||
189 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
190 | unsigned int period_size = runtime->period_size; | ||
191 | unsigned int buffer_size = runtime->buffer_size; | ||
192 | unsigned long flags; | ||
193 | unsigned int position = 0, dist = 0, interval = 0; | ||
194 | |||
195 | position = substream->ops->pointer(substream); | ||
196 | dist = (position + buffer_size - apcm->position) % buffer_size; | ||
197 | if ((dist >= period_size) || | ||
198 | (position/period_size != apcm->position/period_size)) { | ||
199 | apcm->interrupt(apcm); | ||
200 | apcm->position = position; | ||
201 | } | ||
202 | /* Add extra HZ*5/1000 to avoid overrun issue when recording | ||
203 | * at 8kHz in 8-bit format or at 88kHz in 24-bit format. */ | ||
204 | interval = ((period_size - (position % period_size)) | ||
205 | * HZ + (runtime->rate - 1)) / runtime->rate + HZ * 5 / 1000; | ||
206 | spin_lock_irqsave(&apcm->timer_lock, flags); | ||
207 | apcm->timer.expires = jiffies + interval; | ||
208 | if (!apcm->stop_timer) | ||
209 | add_timer(&apcm->timer); | ||
210 | |||
211 | spin_unlock_irqrestore(&apcm->timer_lock, flags); | ||
212 | } | ||
213 | |||
214 | static int ct_pcm_timer_prepare(struct ct_atc_pcm *apcm) | ||
215 | { | ||
216 | unsigned long flags; | ||
217 | |||
218 | spin_lock_irqsave(&apcm->timer_lock, flags); | ||
219 | if (timer_pending(&apcm->timer)) { | ||
220 | /* The timer has already been started. */ | ||
221 | spin_unlock_irqrestore(&apcm->timer_lock, flags); | ||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | init_timer(&apcm->timer); | ||
226 | apcm->timer.data = (unsigned long)apcm; | ||
227 | apcm->timer.function = ct_pcm_timer_callback; | ||
228 | spin_unlock_irqrestore(&apcm->timer_lock, flags); | ||
229 | apcm->position = 0; | ||
230 | |||
231 | return 0; | ||
232 | } | ||
233 | |||
234 | static int ct_pcm_timer_start(struct ct_atc_pcm *apcm) | ||
235 | { | ||
236 | struct snd_pcm_runtime *runtime = apcm->substream->runtime; | ||
237 | unsigned long flags; | ||
238 | |||
239 | spin_lock_irqsave(&apcm->timer_lock, flags); | ||
240 | if (timer_pending(&apcm->timer)) { | ||
241 | /* The timer has already been started. */ | ||
242 | spin_unlock_irqrestore(&apcm->timer_lock, flags); | ||
243 | return 0; | ||
244 | } | ||
245 | |||
246 | apcm->timer.expires = jiffies + (runtime->period_size * HZ + | ||
247 | (runtime->rate - 1)) / runtime->rate; | ||
248 | apcm->stop_timer = 0; | ||
249 | add_timer(&apcm->timer); | ||
250 | spin_unlock_irqrestore(&apcm->timer_lock, flags); | ||
251 | |||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | static int ct_pcm_timer_stop(struct ct_atc_pcm *apcm) | ||
256 | { | ||
257 | unsigned long flags; | ||
258 | |||
259 | spin_lock_irqsave(&apcm->timer_lock, flags); | ||
260 | apcm->stop_timer = 1; | ||
261 | del_timer(&apcm->timer); | ||
262 | spin_unlock_irqrestore(&apcm->timer_lock, flags); | ||
263 | |||
264 | try_to_del_timer_sync(&apcm->timer); | ||
265 | |||
266 | return 0; | ||
267 | } | ||
268 | 189 | ||
269 | static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream) | 190 | static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream) |
270 | { | 191 | { |
@@ -283,8 +204,6 @@ static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream) | |||
283 | return err; | 204 | return err; |
284 | } | 205 | } |
285 | 206 | ||
286 | ct_pcm_timer_prepare(apcm); | ||
287 | |||
288 | return 0; | 207 | return 0; |
289 | } | 208 | } |
290 | 209 | ||
@@ -300,12 +219,10 @@ ct_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd) | |||
300 | case SNDRV_PCM_TRIGGER_RESUME: | 219 | case SNDRV_PCM_TRIGGER_RESUME: |
301 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | 220 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
302 | atc->pcm_playback_start(atc, apcm); | 221 | atc->pcm_playback_start(atc, apcm); |
303 | ct_pcm_timer_start(apcm); | ||
304 | break; | 222 | break; |
305 | case SNDRV_PCM_TRIGGER_STOP: | 223 | case SNDRV_PCM_TRIGGER_STOP: |
306 | case SNDRV_PCM_TRIGGER_SUSPEND: | 224 | case SNDRV_PCM_TRIGGER_SUSPEND: |
307 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | 225 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
308 | ct_pcm_timer_stop(apcm); | ||
309 | atc->pcm_playback_stop(atc, apcm); | 226 | atc->pcm_playback_stop(atc, apcm); |
310 | break; | 227 | break; |
311 | default: | 228 | default: |
@@ -341,9 +258,7 @@ static int ct_pcm_capture_open(struct snd_pcm_substream *substream) | |||
341 | if (NULL == apcm) | 258 | if (NULL == apcm) |
342 | return -ENOMEM; | 259 | return -ENOMEM; |
343 | 260 | ||
344 | spin_lock_init(&apcm->timer_lock); | ||
345 | apcm->started = 0; | 261 | apcm->started = 0; |
346 | apcm->stop_timer = 0; | ||
347 | apcm->substream = substream; | 262 | apcm->substream = substream; |
348 | apcm->interrupt = ct_atc_pcm_interrupt; | 263 | apcm->interrupt = ct_atc_pcm_interrupt; |
349 | runtime->private_data = apcm; | 264 | runtime->private_data = apcm; |
@@ -365,6 +280,10 @@ static int ct_pcm_capture_open(struct snd_pcm_substream *substream) | |||
365 | return err; | 280 | return err; |
366 | } | 281 | } |
367 | 282 | ||
283 | apcm->timer = ct_timer_instance_new(atc->timer, apcm); | ||
284 | if (!apcm->timer) | ||
285 | return -ENOMEM; | ||
286 | |||
368 | return 0; | 287 | return 0; |
369 | } | 288 | } |
370 | 289 | ||
@@ -388,8 +307,6 @@ static int ct_pcm_capture_prepare(struct snd_pcm_substream *substream) | |||
388 | return err; | 307 | return err; |
389 | } | 308 | } |
390 | 309 | ||
391 | ct_pcm_timer_prepare(apcm); | ||
392 | |||
393 | return 0; | 310 | return 0; |
394 | } | 311 | } |
395 | 312 | ||
@@ -403,14 +320,11 @@ ct_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd) | |||
403 | switch (cmd) { | 320 | switch (cmd) { |
404 | case SNDRV_PCM_TRIGGER_START: | 321 | case SNDRV_PCM_TRIGGER_START: |
405 | atc->pcm_capture_start(atc, apcm); | 322 | atc->pcm_capture_start(atc, apcm); |
406 | ct_pcm_timer_start(apcm); | ||
407 | break; | 323 | break; |
408 | case SNDRV_PCM_TRIGGER_STOP: | 324 | case SNDRV_PCM_TRIGGER_STOP: |
409 | ct_pcm_timer_stop(apcm); | ||
410 | atc->pcm_capture_stop(atc, apcm); | 325 | atc->pcm_capture_stop(atc, apcm); |
411 | break; | 326 | break; |
412 | default: | 327 | default: |
413 | ct_pcm_timer_stop(apcm); | ||
414 | atc->pcm_capture_stop(atc, apcm); | 328 | atc->pcm_capture_stop(atc, apcm); |
415 | break; | 329 | break; |
416 | } | 330 | } |