]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - sound/hda/hdac_controller.c
ALSA: hda: Reset stream if DMA RUN bit not cleared
[linux.git] / sound / hda / hdac_controller.c
index 7e7be8e4dcf9c3d7dd170ad2cbea0b949ec7f9e1..bc4a8b606020a1e5f715377f172d3348dd77a87e 100644 (file)
@@ -181,6 +181,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_send_cmd);
  * @bus: HD-audio core bus
  *
  * Usually called from interrupt handler.
+ * The caller needs bus->reg_lock spinlock before calling this.
  */
 void snd_hdac_bus_update_rirb(struct hdac_bus *bus)
 {
@@ -216,6 +217,9 @@ void snd_hdac_bus_update_rirb(struct hdac_bus *bus)
                else if (bus->rirb.cmds[addr]) {
                        bus->rirb.res[addr] = res;
                        bus->rirb.cmds[addr]--;
+                       if (!bus->rirb.cmds[addr] &&
+                           waitqueue_active(&bus->rirb_wq))
+                               wake_up(&bus->rirb_wq);
                } else {
                        dev_err_ratelimited(bus->dev,
                                "spurious response %#x:%#x, last cmd=%#08x\n",
@@ -238,30 +242,51 @@ int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr,
 {
        unsigned long timeout;
        unsigned long loopcounter;
+       wait_queue_entry_t wait;
+       bool warned = false;
 
+       init_wait_entry(&wait, 0);
        timeout = jiffies + msecs_to_jiffies(1000);
 
        for (loopcounter = 0;; loopcounter++) {
                spin_lock_irq(&bus->reg_lock);
+               if (!bus->polling_mode)
+                       prepare_to_wait(&bus->rirb_wq, &wait,
+                                       TASK_UNINTERRUPTIBLE);
                if (bus->polling_mode)
                        snd_hdac_bus_update_rirb(bus);
                if (!bus->rirb.cmds[addr]) {
                        if (res)
                                *res = bus->rirb.res[addr]; /* the last value */
+                       if (!bus->polling_mode)
+                               finish_wait(&bus->rirb_wq, &wait);
                        spin_unlock_irq(&bus->reg_lock);
                        return 0;
                }
                spin_unlock_irq(&bus->reg_lock);
                if (time_after(jiffies, timeout))
                        break;
-               if (loopcounter > 3000)
+#define LOOP_COUNT_MAX 3000
+               if (!bus->polling_mode) {
+                       schedule_timeout(msecs_to_jiffies(2));
+               } else if (bus->needs_damn_long_delay ||
+                          loopcounter > LOOP_COUNT_MAX) {
+                       if (loopcounter > LOOP_COUNT_MAX && !warned) {
+                               dev_dbg_ratelimited(bus->dev,
+                                                   "too slow response, last cmd=%#08x\n",
+                                                   bus->last_cmd[addr]);
+                               warned = true;
+                       }
                        msleep(2); /* temporary workaround */
-               else {
+               else {
                        udelay(10);
                        cond_resched();
                }
        }
 
+       if (!bus->polling_mode)
+               finish_wait(&bus->rirb_wq, &wait);
+
        return -EIO;
 }
 EXPORT_SYMBOL_GPL(snd_hdac_bus_get_response);
@@ -536,7 +561,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_stop_chip);
  * snd_hdac_bus_handle_stream_irq - interrupt handler for streams
  * @bus: HD-audio core bus
  * @status: INTSTS register value
- * @ask: callback to be called for woken streams
+ * @ack: callback to be called for woken streams
  *
  * Returns the bits of handled streams, or zero if no stream is handled.
  */