ALSA: ctxfi - Optimize the native timer handling using wc counter
authorTakashi Iwai <tiwai@suse.de>
Mon, 8 Jun 2009 08:21:07 +0000 (10:21 +0200)
committerTakashi Iwai <tiwai@suse.de>
Mon, 8 Jun 2009 10:38:54 +0000 (12:38 +0200)
Optimize the timer update routine to look up wall clock once instead of
checking the position of each stream at each timer update.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/ctxfi/cthardware.h
sound/pci/ctxfi/cthw20k1.c
sound/pci/ctxfi/cttimer.c

index 35350cf..8f11644 100644 (file)
@@ -147,6 +147,7 @@ struct hw {
 
        int (*set_timer_irq)(struct hw *hw, int enable);
        int (*set_timer_tick)(struct hw *hw, unsigned int tick);
+       unsigned int (*get_wc)(struct hw *hw);
 
        void (*irq_callback)(void *data, unsigned int bit);
        void *irq_callback_data;
index df565c1..b165466 100644 (file)
@@ -1186,6 +1186,11 @@ static int set_timer_tick(struct hw *hw, unsigned int ticks)
        return 0;
 }
 
+static unsigned int get_wc(struct hw *hw)
+{
+       return hw_read_20kx(hw, WC);
+}
+
 /* Card hardware initialization block */
 struct dac_conf {
        unsigned int msr; /* master sample rate in rsrs */
@@ -2235,6 +2240,7 @@ static struct hw ct20k1_preset __devinitdata = {
 
        .set_timer_irq = set_timer_irq,
        .set_timer_tick = set_timer_tick,
+       .get_wc = get_wc,
 };
 
 int __devinit create_20k1_hw_obj(struct hw **rhw)
index ceda74e..ec869a4 100644 (file)
@@ -47,6 +47,7 @@ struct ct_timer {
        struct ct_timer_ops *ops;
        struct list_head instance_head;
        struct list_head running_head;
+       unsigned int wc;                /* current wallclock */
        unsigned int irq_handling:1;    /* in IRQ handling */
        unsigned int reprogram:1;       /* need to reprogram the internval */
        unsigned int running:1;         /* global timer running */
@@ -136,6 +137,7 @@ static struct ct_timer_ops ct_systimer_ops = {
  */
 
 #define CT_TIMER_FREQ  48000
+#define MIN_TICKS      1
 #define MAX_TICKS      ((1 << 13) - 1)
 
 static void ct_xfitimer_irq_rearm(struct ct_timer *atimer, int ticks)
@@ -159,6 +161,12 @@ static void ct_xfitimer_irq_stop(struct ct_timer *atimer)
        }
 }
 
+static inline unsigned int ct_xfitimer_get_wc(struct ct_timer *atimer)
+{
+       struct hw *hw = atimer->atc->hw;
+       return hw->get_wc(hw);
+}
+
 /*
  * reprogram the timer interval;
  * checks the running instance list and determines the next timer interval.
@@ -170,37 +178,46 @@ static void ct_xfitimer_irq_stop(struct ct_timer *atimer)
 static int ct_xfitimer_reprogram(struct ct_timer *atimer)
 {
        struct ct_timer_instance *ti;
-       int min_intr = -1;
+       unsigned int min_intr = (unsigned int)-1;
        int updates = 0;
+       unsigned int wc, diff;
 
+       if (list_empty(&atimer->running_head)) {
+               ct_xfitimer_irq_stop(atimer);
+               atimer->reprogram = 0; /* clear flag */
+               return 0;
+       }
+
+       wc = ct_xfitimer_get_wc(atimer);
+       diff = wc - atimer->wc;
+       atimer->wc = wc;
        list_for_each_entry(ti, &atimer->running_head, running_list) {
-               struct snd_pcm_runtime *runtime;
-               unsigned int pos, diff;
-               int intr;
-               runtime = ti->substream->runtime;
-               pos = ti->substream->ops->pointer(ti->substream);
-               if (pos < ti->position)
-                       diff = runtime->buffer_size - ti->position + pos;
-               else
-                       diff = pos - ti->position;
-               ti->position = pos;
-               while (diff >= ti->frag_count) {
-                       ti->frag_count += runtime->period_size;
-                       ti->need_update = 1;
-                       updates++;
+               if (ti->frag_count > diff)
+                       ti->frag_count -= diff;
+               else {
+                       unsigned int pos;
+                       unsigned int period_size, rate;
+
+                       period_size = ti->substream->runtime->period_size;
+                       rate = ti->substream->runtime->rate;
+                       pos = ti->substream->ops->pointer(ti->substream);
+                       if (pos / period_size != ti->position / period_size) {
+                               ti->need_update = 1;
+                               ti->position = pos;
+                               updates++;
+                       }
+                       pos %= period_size;
+                       pos = period_size - pos;
+                       ti->frag_count = div_u64((u64)pos * CT_TIMER_FREQ +
+                                                rate - 1, rate);
                }
-               ti->frag_count -= diff;
-               intr = div_u64((u64)ti->frag_count * CT_TIMER_FREQ,
-                              runtime->rate);
-               if (min_intr < 0 || intr < min_intr)
-                       min_intr = intr;
+               if (ti->frag_count < min_intr)
+                       min_intr = ti->frag_count;
        }
 
-       if (min_intr > 0)
-               ct_xfitimer_irq_rearm(atimer, min_intr);
-       else
-               ct_xfitimer_irq_stop(atimer);
-
+       if (min_intr < MIN_TICKS)
+               min_intr = MIN_TICKS;
+       ct_xfitimer_irq_rearm(atimer, min_intr);
        atimer->reprogram = 0; /* clear flag */
        return updates;
 }
@@ -253,13 +270,14 @@ static void ct_xfitimer_update(struct ct_timer *atimer)
        unsigned long flags;
        int update;
 
+       spin_lock_irqsave(&atimer->lock, flags);
        if (atimer->irq_handling) {
                /* reached from IRQ handler; let it handle later */
                atimer->reprogram = 1;
+               spin_unlock_irqrestore(&atimer->lock, flags);
                return;
        }
 
-       spin_lock_irqsave(&atimer->lock, flags);
        ct_xfitimer_irq_stop(atimer);
        update = ct_xfitimer_reprogram(atimer);
        spin_unlock_irqrestore(&atimer->lock, flags);
@@ -273,6 +291,8 @@ static void ct_xfitimer_start(struct ct_timer_instance *ti)
        unsigned long flags;
 
        spin_lock_irqsave(&atimer->lock, flags);
+       if (list_empty(&ti->running_list))
+               atimer->wc = ct_xfitimer_get_wc(atimer);
        list_add(&ti->running_list, &atimer->running_head);
        spin_unlock_irqrestore(&atimer->lock, flags);
        ct_xfitimer_update(atimer);
@@ -396,12 +416,12 @@ struct ct_timer *ct_timer_new(struct ct_atc *atc)
        atimer->atc = atc;
        hw = atc->hw;
        if (!USE_SYSTEM_TIMER && hw->set_timer_irq) {
-               printk(KERN_INFO "ctxfi: Use xfi-native timer\n");
+               snd_printd(KERN_INFO "ctxfi: Use xfi-native timer\n");
                atimer->ops = &ct_xfitimer_ops;
                hw->irq_callback_data = atimer;
                hw->irq_callback = ct_timer_interrupt;
        } else {
-               printk(KERN_INFO "ctxfi: Use system timer\n");
+               snd_printd(KERN_INFO "ctxfi: Use system timer\n");
                atimer->ops = &ct_systimer_ops;
        }
        return atimer;