lkml.org 
[lkml]   [2016]   [Mar]   [7]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.2.y-ckt 210/273] ALSA: hda - Loop interrupt handling until really cleared
    Date
    4.2.8-ckt5 -stable review patch.  If anyone has any objections, please let me know.

    ---8<------------------------------------------------------------

    From: Takashi Iwai <tiwai@suse.de>

    commit 473f414564528a819f0c2bb6b4bf26366b64c9ab upstream.

    Currently the interrupt handler of HD-audio driver assumes that no irq
    update is needed while processing the irq. But in reality, it has
    been confirmed that the HW irq is issued even during the irq
    handling. Since we clear the irq status at the beginning, process the
    interrupt, then exits from the handler, the lately issued interrupt is
    left untouched without being properly processed.

    This patch changes the interrupt handler code to loop over the
    check-and-process. The handler tries repeatedly as long as the IRQ
    status are turned on, and either stream or CORB/RIRB is handled.

    For checking the stream handling, snd_hdac_bus_handle_stream_irq()
    returns a value indicating the stream indices bits. Other than that,
    the change is only in the irq handler itself.

    Reported-by: Libin Yang <libin.yang@linux.intel.com>
    Signed-off-by: Takashi Iwai <tiwai@suse.de>
    [ kamal: backport to 4.2-stable: context ]
    Signed-off-by: Kamal Mostafa <kamal@canonical.com>
    ---
    include/sound/hdaudio.h | 2 +-
    sound/hda/hdac_controller.c | 7 ++++++-
    sound/pci/hda/hda_controller.c | 47 +++++++++++++++++++++++-------------------
    3 files changed, 33 insertions(+), 23 deletions(-)

    diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h
    index 4caf1fd..772d137 100644
    --- a/include/sound/hdaudio.h
    +++ b/include/sound/hdaudio.h
    @@ -343,7 +343,7 @@ void snd_hdac_bus_enter_link_reset(struct hdac_bus *bus);
    void snd_hdac_bus_exit_link_reset(struct hdac_bus *bus);

    void snd_hdac_bus_update_rirb(struct hdac_bus *bus);
    -void snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status,
    +int snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status,
    void (*ack)(struct hdac_bus *,
    struct hdac_stream *));

    diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c
    index b5a17cb..8c48623 100644
    --- a/sound/hda/hdac_controller.c
    +++ b/sound/hda/hdac_controller.c
    @@ -426,18 +426,22 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_stop_chip);
    * @bus: HD-audio core bus
    * @status: INTSTS register value
    * @ask: callback to be called for woken streams
    + *
    + * Returns the bits of handled streams, or zero if no stream is handled.
    */
    -void snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status,
    +int snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status,
    void (*ack)(struct hdac_bus *,
    struct hdac_stream *))
    {
    struct hdac_stream *azx_dev;
    u8 sd_status;
    + int handled = 0;

    list_for_each_entry(azx_dev, &bus->stream_list, list) {
    if (status & azx_dev->sd_int_sta_mask) {
    sd_status = snd_hdac_stream_readb(azx_dev, SD_STS);
    snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK);
    + handled |= 1 << azx_dev->index;
    if (!azx_dev->substream || !azx_dev->running ||
    !(sd_status & SD_INT_COMPLETE))
    continue;
    @@ -445,6 +449,7 @@ void snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status,
    ack(bus, azx_dev);
    }
    }
    + return handled;
    }
    EXPORT_SYMBOL_GPL(snd_hdac_bus_handle_stream_irq);

    diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c
    index 4013af3..1091b7a 100644
    --- a/sound/pci/hda/hda_controller.c
    +++ b/sound/pci/hda/hda_controller.c
    @@ -930,6 +930,8 @@ irqreturn_t azx_interrupt(int irq, void *dev_id)
    struct azx *chip = dev_id;
    struct hdac_bus *bus = azx_bus(chip);
    u32 status;
    + bool active, handled = false;
    + int repeat = 0; /* count for avoiding endless loop */

    #ifdef CONFIG_PM
    if (azx_has_pm_runtime(chip))
    @@ -939,33 +941,36 @@ irqreturn_t azx_interrupt(int irq, void *dev_id)

    spin_lock(&bus->reg_lock);

    - if (chip->disabled) {
    - spin_unlock(&bus->reg_lock);
    - return IRQ_NONE;
    - }
    -
    - status = azx_readl(chip, INTSTS);
    - if (status == 0 || status == 0xffffffff) {
    - spin_unlock(&bus->reg_lock);
    - return IRQ_NONE;
    - }
    + if (chip->disabled)
    + goto unlock;

    - snd_hdac_bus_handle_stream_irq(bus, status, stream_update);
    + do {
    + status = azx_readl(chip, INTSTS);
    + if (status == 0 || status == 0xffffffff)
    + break;

    - /* clear rirb int */
    - status = azx_readb(chip, RIRBSTS);
    - if (status & RIRB_INT_MASK) {
    - if (status & RIRB_INT_RESPONSE) {
    - if (chip->driver_caps & AZX_DCAPS_RIRB_PRE_DELAY)
    - udelay(80);
    - snd_hdac_bus_update_rirb(bus);
    + handled = true;
    + active = false;
    + if (snd_hdac_bus_handle_stream_irq(bus, status, stream_update))
    + active = true;
    +
    + /* clear rirb int */
    + status = azx_readb(chip, RIRBSTS);
    + if (status & RIRB_INT_MASK) {
    + active = true;
    + if (status & RIRB_INT_RESPONSE) {
    + if (chip->driver_caps & AZX_DCAPS_RIRB_PRE_DELAY)
    + udelay(80);
    + snd_hdac_bus_update_rirb(bus);
    + }
    + azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
    }
    - azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
    - }
    + } while (active && ++repeat < 10);

    + unlock:
    spin_unlock(&bus->reg_lock);

    - return IRQ_HANDLED;
    + return IRQ_RETVAL(handled);
    }
    EXPORT_SYMBOL_GPL(azx_interrupt);

    --
    2.7.0
    \
     
     \ /
      Last update: 2016-03-08 01:01    [W:8.989 / U:0.100 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site