Commit 54de6bc8 authored by Takashi Iwai's avatar Takashi Iwai

ALSA: ctxfi - Optimize the native timer handling using wc counter

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: default avatarTakashi Iwai <tiwai@suse.de>
parent 28cd4aa4
...@@ -147,6 +147,7 @@ struct hw { ...@@ -147,6 +147,7 @@ struct hw {
int (*set_timer_irq)(struct hw *hw, int enable); int (*set_timer_irq)(struct hw *hw, int enable);
int (*set_timer_tick)(struct hw *hw, unsigned int tick); 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)(void *data, unsigned int bit);
void *irq_callback_data; void *irq_callback_data;
......
...@@ -1186,6 +1186,11 @@ static int set_timer_tick(struct hw *hw, unsigned int ticks) ...@@ -1186,6 +1186,11 @@ static int set_timer_tick(struct hw *hw, unsigned int ticks)
return 0; return 0;
} }
static unsigned int get_wc(struct hw *hw)
{
return hw_read_20kx(hw, WC);
}
/* Card hardware initialization block */ /* Card hardware initialization block */
struct dac_conf { struct dac_conf {
unsigned int msr; /* master sample rate in rsrs */ unsigned int msr; /* master sample rate in rsrs */
...@@ -2235,6 +2240,7 @@ static struct hw ct20k1_preset __devinitdata = { ...@@ -2235,6 +2240,7 @@ static struct hw ct20k1_preset __devinitdata = {
.set_timer_irq = set_timer_irq, .set_timer_irq = set_timer_irq,
.set_timer_tick = set_timer_tick, .set_timer_tick = set_timer_tick,
.get_wc = get_wc,
}; };
int __devinit create_20k1_hw_obj(struct hw **rhw) int __devinit create_20k1_hw_obj(struct hw **rhw)
......
...@@ -47,6 +47,7 @@ struct ct_timer { ...@@ -47,6 +47,7 @@ struct ct_timer {
struct ct_timer_ops *ops; struct ct_timer_ops *ops;
struct list_head instance_head; struct list_head instance_head;
struct list_head running_head; struct list_head running_head;
unsigned int wc; /* current wallclock */
unsigned int irq_handling:1; /* in IRQ handling */ unsigned int irq_handling:1; /* in IRQ handling */
unsigned int reprogram:1; /* need to reprogram the internval */ unsigned int reprogram:1; /* need to reprogram the internval */
unsigned int running:1; /* global timer running */ unsigned int running:1; /* global timer running */
...@@ -136,6 +137,7 @@ static struct ct_timer_ops ct_systimer_ops = { ...@@ -136,6 +137,7 @@ static struct ct_timer_ops ct_systimer_ops = {
*/ */
#define CT_TIMER_FREQ 48000 #define CT_TIMER_FREQ 48000
#define MIN_TICKS 1
#define MAX_TICKS ((1 << 13) - 1) #define MAX_TICKS ((1 << 13) - 1)
static void ct_xfitimer_irq_rearm(struct ct_timer *atimer, int ticks) 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) ...@@ -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; * reprogram the timer interval;
* checks the running instance list and determines the next 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) ...@@ -170,37 +178,46 @@ static void ct_xfitimer_irq_stop(struct ct_timer *atimer)
static int ct_xfitimer_reprogram(struct ct_timer *atimer) static int ct_xfitimer_reprogram(struct ct_timer *atimer)
{ {
struct ct_timer_instance *ti; struct ct_timer_instance *ti;
int min_intr = -1; unsigned int min_intr = (unsigned int)-1;
int updates = 0; 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) { list_for_each_entry(ti, &atimer->running_head, running_list) {
struct snd_pcm_runtime *runtime; if (ti->frag_count > diff)
unsigned int pos, diff; ti->frag_count -= diff;
int intr; else {
runtime = ti->substream->runtime; unsigned int pos;
pos = ti->substream->ops->pointer(ti->substream); unsigned int period_size, rate;
if (pos < ti->position)
diff = runtime->buffer_size - ti->position + pos; period_size = ti->substream->runtime->period_size;
else rate = ti->substream->runtime->rate;
diff = pos - ti->position; pos = ti->substream->ops->pointer(ti->substream);
ti->position = pos; if (pos / period_size != ti->position / period_size) {
while (diff >= ti->frag_count) { ti->need_update = 1;
ti->frag_count += runtime->period_size; ti->position = pos;
ti->need_update = 1; updates++;
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; if (ti->frag_count < min_intr)
intr = div_u64((u64)ti->frag_count * CT_TIMER_FREQ, min_intr = ti->frag_count;
runtime->rate);
if (min_intr < 0 || intr < min_intr)
min_intr = intr;
} }
if (min_intr > 0) if (min_intr < MIN_TICKS)
ct_xfitimer_irq_rearm(atimer, min_intr); min_intr = MIN_TICKS;
else ct_xfitimer_irq_rearm(atimer, min_intr);
ct_xfitimer_irq_stop(atimer);
atimer->reprogram = 0; /* clear flag */ atimer->reprogram = 0; /* clear flag */
return updates; return updates;
} }
...@@ -253,13 +270,14 @@ static void ct_xfitimer_update(struct ct_timer *atimer) ...@@ -253,13 +270,14 @@ static void ct_xfitimer_update(struct ct_timer *atimer)
unsigned long flags; unsigned long flags;
int update; int update;
spin_lock_irqsave(&atimer->lock, flags);
if (atimer->irq_handling) { if (atimer->irq_handling) {
/* reached from IRQ handler; let it handle later */ /* reached from IRQ handler; let it handle later */
atimer->reprogram = 1; atimer->reprogram = 1;
spin_unlock_irqrestore(&atimer->lock, flags);
return; return;
} }
spin_lock_irqsave(&atimer->lock, flags);
ct_xfitimer_irq_stop(atimer); ct_xfitimer_irq_stop(atimer);
update = ct_xfitimer_reprogram(atimer); update = ct_xfitimer_reprogram(atimer);
spin_unlock_irqrestore(&atimer->lock, flags); spin_unlock_irqrestore(&atimer->lock, flags);
...@@ -273,6 +291,8 @@ static void ct_xfitimer_start(struct ct_timer_instance *ti) ...@@ -273,6 +291,8 @@ static void ct_xfitimer_start(struct ct_timer_instance *ti)
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&atimer->lock, 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); list_add(&ti->running_list, &atimer->running_head);
spin_unlock_irqrestore(&atimer->lock, flags); spin_unlock_irqrestore(&atimer->lock, flags);
ct_xfitimer_update(atimer); ct_xfitimer_update(atimer);
...@@ -396,12 +416,12 @@ struct ct_timer *ct_timer_new(struct ct_atc *atc) ...@@ -396,12 +416,12 @@ struct ct_timer *ct_timer_new(struct ct_atc *atc)
atimer->atc = atc; atimer->atc = atc;
hw = atc->hw; hw = atc->hw;
if (!USE_SYSTEM_TIMER && hw->set_timer_irq) { 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; atimer->ops = &ct_xfitimer_ops;
hw->irq_callback_data = atimer; hw->irq_callback_data = atimer;
hw->irq_callback = ct_timer_interrupt; hw->irq_callback = ct_timer_interrupt;
} else { } else {
printk(KERN_INFO "ctxfi: Use system timer\n"); snd_printd(KERN_INFO "ctxfi: Use system timer\n");
atimer->ops = &ct_systimer_ops; atimer->ops = &ct_systimer_ops;
} }
return atimer; return atimer;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment