ALSA: ctxfi - Fix deadlock with xfi-timer
[pandora-kernel.git] / sound / pci / ctxfi / cttimer.c
index 779c6c3..93b0aed 100644 (file)
@@ -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);
 }