aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/ctxfi/ctpcm.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2009-06-05 10:11:07 -0400
committerTakashi Iwai <tiwai@suse.de>2009-06-05 10:44:13 -0400
commitb7bbf876087e0e2c0ba723a8398083c9a9ac1dfd (patch)
tree69a3e70658fc751ffc99eef5a6f047b19f61a4a2 /sound/pci/ctxfi/ctpcm.c
parent6bc5874a1ddf98ac0fe6c4eab7d286c11cb1c748 (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.c106
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
185static 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
214static 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
234static 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
255static 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
269static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream) 190static 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 }