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;
/* 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);
+ 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;
}
}
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;
- struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus);
- u32 stream_mask;
+ int ret = IRQ_WAKE_THREAD;
u32 status;
- if (!pm_runtime_active(bus->dev))
- return IRQ_NONE;
-
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);
- 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);
- u32 status = snd_hdac_chip_readl(bus, INTSTS);
struct hdac_stream *s;
+ bool active = false;
u32 sd_status;
- /* check streams */
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);
- 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)
/* 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;
if (!hda_stream)
return -ENOMEM;
+ hda_stream->sdev = sdev;
+
stream = &hda_stream->hda_stream;
stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] +
if (!hda_stream)
return -ENOMEM;
+ hda_stream->sdev = sdev;
+
stream = &hda_stream->hda_stream;
/* we always have DSP support */