ASoC: SOF: Intel: hda: modify stream interrupt handler
[platform/kernel/linux-rpi.git] / sound / soc / sof / intel / hda-stream.c
index c92006f..d443180 100644 (file)
@@ -155,6 +155,7 @@ struct hdac_ext_stream *
 hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction)
 {
        struct hdac_bus *bus = sof_to_bus(sdev);
 hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction)
 {
        struct hdac_bus *bus = sof_to_bus(sdev);
+       struct sof_intel_hda_stream *hda_stream;
        struct hdac_ext_stream *stream = NULL;
        struct hdac_stream *s;
 
        struct hdac_ext_stream *stream = NULL;
        struct hdac_stream *s;
 
@@ -163,8 +164,15 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction)
        /* get an unused stream */
        list_for_each_entry(s, &bus->stream_list, list) {
                if (s->direction == direction && !s->opened) {
        /* get an unused stream */
        list_for_each_entry(s, &bus->stream_list, list) {
                if (s->direction == direction && !s->opened) {
-                       s->opened = true;
                        stream = stream_to_hdac_ext_stream(s);
                        stream = stream_to_hdac_ext_stream(s);
+                       hda_stream = container_of(stream,
+                                                 struct sof_intel_hda_stream,
+                                                 hda_stream);
+                       /* check if the host DMA channel is reserved */
+                       if (hda_stream->host_reserved)
+                               continue;
+
+                       s->opened = true;
                        break;
                }
        }
                        break;
                }
        }
@@ -430,60 +438,63 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
        return ret;
 }
 
        return ret;
 }
 
+int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev,
+                          struct snd_pcm_substream *substream)
+{
+       struct hdac_stream *stream = substream->runtime->private_data;
+       struct hdac_ext_stream *link_dev = container_of(stream,
+                                                       struct hdac_ext_stream,
+                                                       hstream);
+       struct hdac_bus *bus = sof_to_bus(sdev);
+       u32 mask = 0x1 << stream->index;
+
+       spin_lock(&bus->reg_lock);
+       /* couple host and link DMA if link DMA channel is idle */
+       if (!link_dev->link_locked)
+               snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR,
+                                       SOF_HDA_REG_PP_PPCTL, mask, 0);
+       spin_unlock(&bus->reg_lock);
+
+       return 0;
+}
+
 irqreturn_t hda_dsp_stream_interrupt(int irq, void *context)
 {
        struct hdac_bus *bus = context;
 irqreturn_t hda_dsp_stream_interrupt(int irq, void *context)
 {
        struct hdac_bus *bus = context;
-       struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus);
-       u32 stream_mask;
+       int ret = IRQ_WAKE_THREAD;
        u32 status;
 
        u32 status;
 
-       if (!pm_runtime_active(bus->dev))
-               return IRQ_NONE;
-
        spin_lock(&bus->reg_lock);
 
        status = snd_hdac_chip_readl(bus, INTSTS);
        spin_lock(&bus->reg_lock);
 
        status = snd_hdac_chip_readl(bus, INTSTS);
-       stream_mask = GENMASK(sof_hda->stream_max - 1, 0) | AZX_INT_CTRL_EN;
+       dev_vdbg(bus->dev, "stream irq, INTSTS status: 0x%x\n", status);
 
 
-       /* Not stream interrupt or register inaccessible, ignore it.*/
-       if (!(status & stream_mask) || status == 0xffffffff) {
-               spin_unlock(&bus->reg_lock);
-               return IRQ_NONE;
-       }
-
-#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
-       /* clear rirb int */
-       status = snd_hdac_chip_readb(bus, RIRBSTS);
-       if (status & RIRB_INT_MASK) {
-               if (status & RIRB_INT_RESPONSE)
-                       snd_hdac_bus_update_rirb(bus);
-               snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK);
-       }
-#endif
+       /* Register inaccessible, ignore it.*/
+       if (status == 0xffffffff)
+               ret = IRQ_NONE;
 
        spin_unlock(&bus->reg_lock);
 
 
        spin_unlock(&bus->reg_lock);
 
-       return snd_hdac_chip_readl(bus, INTSTS) ? IRQ_WAKE_THREAD : IRQ_HANDLED;
+       return ret;
 }
 
 }
 
-irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context)
+static bool hda_dsp_stream_check(struct hdac_bus *bus, u32 status)
 {
 {
-       struct hdac_bus *bus = context;
        struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus);
        struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus);
-       u32 status = snd_hdac_chip_readl(bus, INTSTS);
        struct hdac_stream *s;
        struct hdac_stream *s;
+       bool active = false;
        u32 sd_status;
 
        u32 sd_status;
 
-       /* check streams */
        list_for_each_entry(s, &bus->stream_list, list) {
        list_for_each_entry(s, &bus->stream_list, list) {
-               if (status & (1 << s->index) && s->opened) {
+               if (status & BIT(s->index) && s->opened) {
                        sd_status = snd_hdac_stream_readb(s, SD_STS);
 
                        dev_vdbg(bus->dev, "stream %d status 0x%x\n",
                                 s->index, sd_status);
 
                        sd_status = snd_hdac_stream_readb(s, SD_STS);
 
                        dev_vdbg(bus->dev, "stream %d status 0x%x\n",
                                 s->index, sd_status);
 
-                       snd_hdac_stream_writeb(s, SD_STS, SD_INT_MASK);
+                       snd_hdac_stream_writeb(s, SD_STS, sd_status);
 
 
+                       active = true;
                        if (!s->substream ||
                            !s->running ||
                            (sd_status & SOF_HDA_CL_DMA_SD_INT_COMPLETE) == 0)
                        if (!s->substream ||
                            !s->running ||
                            (sd_status & SOF_HDA_CL_DMA_SD_INT_COMPLETE) == 0)
@@ -492,8 +503,48 @@ irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context)
                        /* Inform ALSA only in case not do that with IPC */
                        if (sof_hda->no_ipc_position)
                                snd_sof_pcm_period_elapsed(s->substream);
                        /* Inform ALSA only in case not do that with IPC */
                        if (sof_hda->no_ipc_position)
                                snd_sof_pcm_period_elapsed(s->substream);
+               }
+       }
+
+       return active;
+}
+
+irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context)
+{
+       struct hdac_bus *bus = context;
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
+       u32 rirb_status;
+#endif
+       bool active;
+       u32 status;
+       int i;
+
+       /*
+        * Loop 10 times to handle missed interrupts caused by
+        * unsolicited responses from the codec
+        */
+       for (i = 0, active = true; i < 10 && active; i++) {
+               spin_lock_irq(&bus->reg_lock);
+
+               status = snd_hdac_chip_readl(bus, INTSTS);
 
 
+               /* check streams */
+               active = hda_dsp_stream_check(bus, status);
+
+               /* check and clear RIRB interrupt */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
+               if (status & AZX_INT_CTRL_EN) {
+                       rirb_status = snd_hdac_chip_readb(bus, RIRBSTS);
+                       if (rirb_status & RIRB_INT_MASK) {
+                               active = true;
+                               if (rirb_status & RIRB_INT_RESPONSE)
+                                       snd_hdac_bus_update_rirb(bus);
+                               snd_hdac_chip_writeb(bus, RIRBSTS,
+                                                    RIRB_INT_MASK);
+                       }
                }
                }
+#endif
+               spin_unlock_irq(&bus->reg_lock);
        }
 
        return IRQ_HANDLED;
        }
 
        return IRQ_HANDLED;
@@ -564,6 +615,8 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev)
                if (!hda_stream)
                        return -ENOMEM;
 
                if (!hda_stream)
                        return -ENOMEM;
 
+               hda_stream->sdev = sdev;
+
                stream = &hda_stream->hda_stream;
 
                stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] +
                stream = &hda_stream->hda_stream;
 
                stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] +
@@ -617,6 +670,8 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev)
                if (!hda_stream)
                        return -ENOMEM;
 
                if (!hda_stream)
                        return -ENOMEM;
 
+               hda_stream->sdev = sdev;
+
                stream = &hda_stream->hda_stream;
 
                /* we always have DSP support */
                stream = &hda_stream->hda_stream;
 
                /* we always have DSP support */