Skip to content

Commit

Permalink
ALSA: ctxfi - Optimize the native timer handling using wc counter
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Takashi Iwai committed Jun 8, 2009
1 parent 28cd4aa commit 54de6bc
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 28 deletions.
1 change: 1 addition & 0 deletions sound/pci/ctxfi/cthardware.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 6 additions & 0 deletions sound/pci/ctxfi/cthw20k1.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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)
Expand Down
76 changes: 48 additions & 28 deletions sound/pci/ctxfi/cttimer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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)
Expand All @@ -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.
Expand All @@ -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;
}
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 54de6bc

Please sign in to comment.