forked from Minki/linux
ALSA: ctxfi - Fix deadlock with xfi-timer
The PCM x-fi native update routine can cause deadlocks when the trigger(START) is called while the stream is running. This patch fixes the deadlock by just postponing the pcm period update to the next possible wake-up. Also it adds the flip of ti->running flag (just to be sure as now). Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
635c265f32
commit
8dca419721
@ -180,7 +180,7 @@ static inline unsigned int ct_xfitimer_get_wc(struct ct_timer *atimer)
|
||||
*
|
||||
* call this inside the lock and irq disabled
|
||||
*/
|
||||
static int ct_xfitimer_reprogram(struct ct_timer *atimer)
|
||||
static int ct_xfitimer_reprogram(struct ct_timer *atimer, int can_update)
|
||||
{
|
||||
struct ct_timer_instance *ti;
|
||||
unsigned int min_intr = (unsigned int)-1;
|
||||
@ -216,6 +216,8 @@ static int ct_xfitimer_reprogram(struct ct_timer *atimer)
|
||||
ti->frag_count = div_u64((u64)pos * CT_TIMER_FREQ +
|
||||
rate - 1, rate);
|
||||
}
|
||||
if (ti->need_update && !can_update)
|
||||
min_intr = 0; /* pending to the next irq */
|
||||
if (ti->frag_count < min_intr)
|
||||
min_intr = ti->frag_count;
|
||||
}
|
||||
@ -235,7 +237,7 @@ static void ct_xfitimer_check_period(struct ct_timer *atimer)
|
||||
|
||||
spin_lock_irqsave(&atimer->list_lock, flags);
|
||||
list_for_each_entry(ti, &atimer->instance_head, instance_list) {
|
||||
if (ti->need_update) {
|
||||
if (ti->running && ti->need_update) {
|
||||
ti->need_update = 0;
|
||||
ti->apcm->interrupt(ti->apcm);
|
||||
}
|
||||
@ -252,7 +254,7 @@ static void ct_xfitimer_callback(struct ct_timer *atimer)
|
||||
spin_lock_irqsave(&atimer->lock, flags);
|
||||
atimer->irq_handling = 1;
|
||||
do {
|
||||
update = ct_xfitimer_reprogram(atimer);
|
||||
update = ct_xfitimer_reprogram(atimer, 1);
|
||||
spin_unlock(&atimer->lock);
|
||||
if (update)
|
||||
ct_xfitimer_check_period(atimer);
|
||||
@ -265,6 +267,7 @@ static void ct_xfitimer_callback(struct ct_timer *atimer)
|
||||
static void ct_xfitimer_prepare(struct ct_timer_instance *ti)
|
||||
{
|
||||
ti->frag_count = ti->substream->runtime->period_size;
|
||||
ti->running = 0;
|
||||
ti->need_update = 0;
|
||||
}
|
||||
|
||||
@ -273,7 +276,6 @@ static void ct_xfitimer_prepare(struct ct_timer_instance *ti)
|
||||
static void ct_xfitimer_update(struct ct_timer *atimer)
|
||||
{
|
||||
unsigned long flags;
|
||||
int update;
|
||||
|
||||
spin_lock_irqsave(&atimer->lock, flags);
|
||||
if (atimer->irq_handling) {
|
||||
@ -284,10 +286,8 @@ static void ct_xfitimer_update(struct ct_timer *atimer)
|
||||
}
|
||||
|
||||
ct_xfitimer_irq_stop(atimer);
|
||||
update = ct_xfitimer_reprogram(atimer);
|
||||
ct_xfitimer_reprogram(atimer, 0);
|
||||
spin_unlock_irqrestore(&atimer->lock, flags);
|
||||
if (update)
|
||||
ct_xfitimer_check_period(atimer);
|
||||
}
|
||||
|
||||
static void ct_xfitimer_start(struct ct_timer_instance *ti)
|
||||
@ -298,6 +298,8 @@ static void ct_xfitimer_start(struct ct_timer_instance *ti)
|
||||
spin_lock_irqsave(&atimer->lock, flags);
|
||||
if (list_empty(&ti->running_list))
|
||||
atimer->wc = ct_xfitimer_get_wc(atimer);
|
||||
ti->running = 1;
|
||||
ti->need_update = 0;
|
||||
list_add(&ti->running_list, &atimer->running_head);
|
||||
spin_unlock_irqrestore(&atimer->lock, flags);
|
||||
ct_xfitimer_update(atimer);
|
||||
@ -310,7 +312,7 @@ static void ct_xfitimer_stop(struct ct_timer_instance *ti)
|
||||
|
||||
spin_lock_irqsave(&atimer->lock, flags);
|
||||
list_del_init(&ti->running_list);
|
||||
ti->need_update = 0;
|
||||
ti->running = 0;
|
||||
spin_unlock_irqrestore(&atimer->lock, flags);
|
||||
ct_xfitimer_update(atimer);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user