diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 7812f3232a8072..8c87f3e7cb956f 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -14,7 +14,8 @@ /* * Hardware interface for audio DSP on Cannonlake. */ - +#include +#include #include "../ops.h" #include "hda.h" @@ -29,95 +30,61 @@ static void cnl_ipc_dsp_done(struct snd_sof_dev *sdev); static irqreturn_t cnl_ipc_irq_thread(int irq, void *context) { + //irqreturn_t ret = IRQ_NONE; struct snd_sof_dev *sdev = context; - u32 hipci; - u32 hipcctl; - u32 hipcida; - u32 hipctdr; - u32 hipctdd; - u32 msg; - u32 msg_ext; - irqreturn_t ret = IRQ_NONE; - - hipcida = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDA); - hipcctl = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCCTL); - hipctdr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCTDR); - - /* reenable IPC interrupt */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, - HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC); - - /* reply message from DSP */ - if (hipcida & CNL_DSP_REG_HIPCIDA_DONE && - hipcctl & CNL_DSP_REG_HIPCCTL_DONE) { - unsigned long flags; - - hipci = snd_sof_dsp_read(sdev, HDA_DSP_BAR, - CNL_DSP_REG_HIPCIDR); - msg_ext = hipci & CNL_DSP_REG_HIPCIDR_MSG_MASK; - msg = hipcida & CNL_DSP_REG_HIPCIDA_MSG_MASK; - - dev_vdbg(sdev->dev, - "ipc: firmware response, msg:0x%x, msg_ext:0x%x\n", - msg, msg_ext); - - /* mask Done interrupt */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, - CNL_DSP_REG_HIPCCTL, - CNL_DSP_REG_HIPCCTL_DONE, 0); - - spin_lock_irqsave(&sdev->ipc_lock, flags); - - /* handle immediate reply from DSP core */ - hda_dsp_ipc_get_reply(sdev); - snd_sof_ipc_reply(sdev, msg); - - if (sdev->code_loading) { - sdev->code_loading = 0; - wake_up(&sdev->waitq); - } - - cnl_ipc_dsp_done(sdev); - - spin_unlock_irqrestore(&sdev->ipc_lock, flags); - - ret = IRQ_HANDLED; + struct hdac_bus *bus = sof_to_bus(sdev); + struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); + struct hdac_stream *s; + u32 status; + u32 sd_status; + + //status = sdev->intsts; + status = snd_hdac_chip_readl(bus, INTSTS); +#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); } - - /* new message from DSP */ - if (hipctdr & CNL_DSP_REG_HIPCTDR_BUSY) { - hipctdd = snd_sof_dsp_read(sdev, HDA_DSP_BAR, - CNL_DSP_REG_HIPCTDD); - msg = hipctdr & CNL_DSP_REG_HIPCTDR_MSG_MASK; - msg_ext = hipctdd & CNL_DSP_REG_HIPCTDD_MSG_MASK; - - dev_vdbg(sdev->dev, - "ipc: firmware initiated, msg:0x%x, msg_ext:0x%x\n", - msg, msg_ext); - - /* handle messages from DSP */ - if ((hipctdr & SOF_IPC_PANIC_MAGIC_MASK) == - SOF_IPC_PANIC_MAGIC) { - snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext)); - } else { - snd_sof_ipc_msgs_rx(sdev); +#endif + dev_err(sdev->dev, "in %s %d ylb, status: %#x\n", __func__, __LINE__, status); + status = snd_hdac_chip_readl(bus, INTSTS); + /* check streams */ + list_for_each_entry(s, &bus->stream_list, list) { + if (status & (1 << 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); + + if (!s->substream || + !s->running || + (sd_status & SOF_HDA_CL_DMA_SD_INT_COMPLETE) == 0) + continue; + + /* Inform ALSA only in case not do that with IPC */ + if (sof_hda->no_ipc_position) + snd_pcm_period_elapsed(s->substream); + } + } - /* - * clear busy interrupt to tell dsp controller this - * interrupt has been accepted, not trigger it again - */ - snd_sof_dsp_update_bits_forced(sdev, HDA_DSP_BAR, - CNL_DSP_REG_HIPCTDR, - CNL_DSP_REG_HIPCTDR_BUSY, - CNL_DSP_REG_HIPCTDR_BUSY); - - cnl_ipc_host_done(sdev); + if (!sdev->wakeup) + return IRQ_HANDLED; + dev_err(sdev->dev, "in %s %d ylb\n", __func__, __LINE__); + snd_sof_ipc_reply(sdev, 0); - ret = IRQ_HANDLED; + if (sdev->code_loading) { + sdev->code_loading = 0; + wake_up(&sdev->waitq); } - - return ret; + sdev->wakeup = 0; + sdev->intsts = 0; + return IRQ_HANDLED; } static void cnl_ipc_host_done(struct snd_sof_dev *sdev) diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c index fc1a1d978b5afe..a68ff2cce9cef0 100644 --- a/sound/soc/sof/intel/hda-ipc.c +++ b/sound/soc/sof/intel/hda-ipc.c @@ -238,15 +238,56 @@ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context) return ret; } +static void cnl_ipc_dsp_done_1(struct snd_sof_dev *sdev) +{ + /* + * set DONE bit - tell DSP we have received the reply msg + * from DSP, and processed it, don't send more reply to host + */ + snd_sof_dsp_update_bits_forced_unlocked(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCIDA, + CNL_DSP_REG_HIPCIDA_DONE, + CNL_DSP_REG_HIPCIDA_DONE); + + /* unmask Done interrupt */ + snd_sof_dsp_update_bits_unlock(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCCTL, + CNL_DSP_REG_HIPCCTL_DONE, + CNL_DSP_REG_HIPCCTL_DONE); +} + +static void cnl_ipc_host_done_1(struct snd_sof_dev *sdev) +{ + /* + * set done bit to ack dsp the msg has been + * processed and send reply msg to dsp + */ + snd_sof_dsp_update_bits_forced_unlocked(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCTDA, + CNL_DSP_REG_HIPCTDA_DONE, + CNL_DSP_REG_HIPCTDA_DONE); +} + /* is this IRQ for ADSP ? - we only care about IPC here */ irqreturn_t hda_dsp_ipc_irq_handler(int irq, void *context) { struct snd_sof_dev *sdev = context; int ret = IRQ_NONE; + u32 hipci; + u32 hipcctl; + u32 hipcida; + u32 hipctdr; + u32 hipctdd; + u32 msg; + u32 msg_ext; u32 irq_status = 0; spin_lock(&sdev->hw_lock); + hipcida = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDA); + hipcctl = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCCTL); + hipctdr = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCTDR); + sdev->irq_handled = 0; /* store status */ irq_status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIS); dev_vdbg(sdev->dev, "irq handler: irq_status:0x%x\n", irq_status); @@ -256,14 +297,78 @@ irqreturn_t hda_dsp_ipc_irq_handler(int irq, void *context) goto out; /* IPC message ? */ - if (irq_status & HDA_DSP_ADSPIS_IPC) { - /* disable IPC interrupt */ - snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, - HDA_DSP_REG_ADSPIC, - HDA_DSP_ADSPIC_IPC, 0); + if (!(irq_status & HDA_DSP_ADSPIS_IPC)) + goto out; + + if (hipcida & CNL_DSP_REG_HIPCIDA_DONE && + hipcctl & CNL_DSP_REG_HIPCCTL_DONE) { + hipci = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCIDR); + msg_ext = hipci & CNL_DSP_REG_HIPCIDR_MSG_MASK; + msg = hipcida & CNL_DSP_REG_HIPCIDA_MSG_MASK; + + dev_vdbg(sdev->dev, + "ipc: firmware response, msg:0x%x, msg_ext:0x%x\n", + msg, msg_ext); + + /* mask Done interrupt */ + snd_sof_dsp_update_bits_unlock(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCCTL, + CNL_DSP_REG_HIPCCTL_DONE, 0); + + /* handle immediate reply from DSP core */ + hda_dsp_ipc_get_reply(sdev); + sdev->wakeup = 1; + /* snd_sof_ipc_reply(sdev, msg); */ + + /* if (sdev->code_loading) { */ + /* sdev->code_loading = 0; */ + /* wake_up(&sdev->waitq); */ + /* } */ + + cnl_ipc_dsp_done_1(sdev); + sdev->irq_handled = 1; + ret = IRQ_WAKE_THREAD; } + /* new message from DSP */ + if (hipctdr & CNL_DSP_REG_HIPCTDR_BUSY) { + hipctdd = snd_sof_dsp_read(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCTDD); + msg = hipctdr & CNL_DSP_REG_HIPCTDR_MSG_MASK; + msg_ext = hipctdd & CNL_DSP_REG_HIPCTDD_MSG_MASK; + + dev_vdbg(sdev->dev, + "ipc: firmware initiated, msg:0x%x, msg_ext:0x%x\n", + msg, msg_ext); + + /* handle messages from DSP */ + if ((hipctdr & SOF_IPC_PANIC_MAGIC_MASK) == + SOF_IPC_PANIC_MAGIC) { + snd_sof_dsp_panic(sdev, HDA_DSP_PANIC_OFFSET(msg_ext)); + } else { + snd_sof_ipc_msgs_rx(sdev); + } + + /* + * clear busy interrupt to tell dsp controller this + * interrupt has been accepted, not trigger it again + */ + snd_sof_dsp_update_bits_forced_unlocked(sdev, HDA_DSP_BAR, + CNL_DSP_REG_HIPCTDR, + CNL_DSP_REG_HIPCTDR_BUSY, + CNL_DSP_REG_HIPCTDR_BUSY); + + cnl_ipc_host_done_1(sdev); + sdev->irq_handled = 1; + + if (ret == IRQ_NONE) + ret = IRQ_HANDLED; + } + /* /\* reenable IPC interrupt *\/ */ + /* snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, */ + /* HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC); */ out: spin_unlock(&sdev->hw_lock); return ret; diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 3beb1dc99be0e1..fbfff308160b24 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -460,14 +460,18 @@ int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev, irqreturn_t hda_dsp_stream_interrupt(int irq, void *context) { - struct hdac_bus *bus = context; + struct snd_sof_dev *sdev = context; + struct hdac_bus *bus = sof_to_bus(sdev); struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); u32 stream_mask; u32 status; + struct hdac_stream *s; + u32 sd_status; dev_vdbg(bus->dev, "stream irq, INTSTS status: 0x%x\n", snd_hdac_chip_readl(bus, INTSTS)); + sdev->irq_handled = 0; if (!pm_runtime_active(bus->dev)) return IRQ_NONE; @@ -475,7 +479,7 @@ irqreturn_t hda_dsp_stream_interrupt(int irq, void *context) status = snd_hdac_chip_readl(bus, INTSTS); stream_mask = GENMASK(sof_hda->stream_max - 1, 0) | AZX_INT_CTRL_EN; - + dev_err(sdev->dev, "in %s %d ylb status: %#x, intsts: %#x\n", __func__, __LINE__, status, sdev->intsts); /* Not stream interrupt or register inaccessible, ignore it.*/ if (!(status & stream_mask) || status == 0xffffffff) { spin_unlock(&bus->reg_lock); @@ -491,10 +495,40 @@ irqreturn_t hda_dsp_stream_interrupt(int irq, void *context) snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK); } #endif + dev_err(sdev->dev, "in %s %d ylb\n", __func__, __LINE__); + status = snd_hdac_chip_readl(bus, INTSTS); + dev_err(sdev->dev, "in %s %d ylb status: %#x, intsts: %#x\n", __func__, __LINE__, status, sdev->intsts); + sdev->irq_handled = 1; + if (!sdev->intsts) + sdev->intsts = status; - spin_unlock(&bus->reg_lock); + + /* /\* check streams *\/ */ + /* list_for_each_entry(s, &bus->stream_list, list) { */ + /* if (status & (1 << 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); */ - return snd_hdac_chip_readl(bus, INTSTS) ? IRQ_WAKE_THREAD : IRQ_HANDLED; + /* snd_hdac_stream_writeb(s, SD_STS, SD_INT_MASK); */ + + /* if (!s->substream || */ + /* !s->running || */ + /* (sd_status & SOF_HDA_CL_DMA_SD_INT_COMPLETE) == 0) */ + /* continue; */ + + /* /\* Inform ALSA only in case not do that with IPC *\/ */ + /* if (sof_hda->no_ipc_position) */ + /* snd_pcm_period_elapsed(s->substream); */ + + /* } */ + /* } */ + + + spin_unlock(&bus->reg_lock); + + return IRQ_HANDLED; } irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index d72c6d0bfa70c0..7a37a09e699fa4 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -15,6 +15,7 @@ * Hardware interface for generic Intel audio DSP HDA IP */ +#include #include #include #include @@ -416,6 +417,45 @@ static const struct sof_intel_dsp_desc return chip_info; } +irqreturn_t my_irq_handler(int irq, void *context) +{ + struct snd_sof_dev *sdev = context; + struct hdac_bus *bus = sof_to_bus(sdev); + //struct hdac_bus *bus; + irqreturn_t ret = IRQ_NONE; + u32 status; + int again = 0; + + //snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, + // HDA_DSP_REG_ADSPIC, + // HDA_DSP_ADSPIC_IPC, 0); +again: + again = 0; + //bus = sof_to_bus(sdev); + ret = hda_dsp_ipc_irq_handler(irq, context); + if (sdev->irq_handled) { + again = 1; + } + dev_err(sdev->dev, "in %s %d ylb, ret: %d\n", __func__, __LINE__, ret); + hda_dsp_stream_interrupt(irq, context); + //if (sdev->irq_handled) { + // if (ret == IRQ_NONE) + // ret = IRQ_HANDLED; + // again = 1; + //} + //if (again) + // goto again; + status = snd_hdac_chip_readl(bus, INTSTS); + if (status & AZX_INT_CTRL_EN) + goto again; + dev_err(sdev->dev, "in %s %d ylb, ret: %d\n", __func__, __LINE__, ret); + /* reenable IPC interrupt */ + //snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, + // HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC); + //return IRQ_THREAD; + return IRQ_WAKE_THREAD; +} + int hda_dsp_probe(struct snd_sof_dev *sdev) { struct pci_dev *pci = to_pci_dev(sdev->dev); @@ -513,37 +553,33 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) /* * register our IRQ - * looks msi has some interrupt missing issues for us, - * let's use legacy interrupt mode before we root casue that. - */ - dev_info(sdev->dev, "use legacy interrupt mode\n"); - /* - * use IO-APIC mode, hda->irq and ipc_irq are using the same - * irq number of pci->irq + * let's try to enable msi firstly + * if it fails, use legacy interrupt mode + * TODO: support interrupt mode selection with kernel parameter + * support msi multiple vectors */ - hdev->irq = pci->irq; - sdev->ipc_irq = pci->irq; - sdev->msi_enabled = 0; - - dev_dbg(sdev->dev, "using HDA IRQ %d\n", hdev->irq); - ret = request_threaded_irq(hdev->irq, hda_dsp_stream_interrupt, - hda_dsp_stream_threaded_handler, - IRQF_SHARED, "AudioHDA", bus); + ret = pci_alloc_irq_vectors(pci, 1, 1, PCI_IRQ_MSI); if (ret < 0) { - dev_err(sdev->dev, "error: failed to register HDA IRQ %d\n", - hdev->irq); - goto free_irq_vector; + dev_info(sdev->dev, "use legacy interrupt mode\n"); + /* + * in IO-APIC mode, hda->irq and ipc_irq are using the same + * irq number of pci->irq + */ + hdev->irq = pci->irq; + sdev->ipc_irq = pci->irq; + sdev->msi_enabled = 0; + } else { + dev_info(sdev->dev, "use msi interrupt mode\n"); + hdev->irq = pci_irq_vector(pci, 0); + /* ipc irq number is the same of hda irq */ + sdev->ipc_irq = hdev->irq; + sdev->msi_enabled = 1; } - dev_dbg(sdev->dev, "using IPC IRQ %d\n", sdev->ipc_irq); - ret = request_threaded_irq(sdev->ipc_irq, hda_dsp_ipc_irq_handler, - sof_ops(sdev)->irq_thread, IRQF_SHARED, - "AudioDSP", sdev); - if (ret < 0) { - dev_err(sdev->dev, "error: failed to register IPC IRQ %d\n", - sdev->ipc_irq); - goto free_hda_irq; - } + dev_dbg(sdev->dev, "using HDA IRQ %d\n", hdev->irq); + ret = request_threaded_irq(hdev->irq, my_irq_handler, + sof_ops(sdev)->irq_thread, + IRQF_SHARED, "AudioHDA", sdev); pci_set_master(pci); synchronize_irq(pci->irq); diff --git a/sound/soc/sof/ops.c b/sound/soc/sof/ops.c index 80f907740b82ce..5b3190d54a1ff6 100644 --- a/sound/soc/sof/ops.c +++ b/sound/soc/sof/ops.c @@ -86,6 +86,17 @@ bool snd_sof_dsp_update_bits64_unlocked(struct snd_sof_dev *sdev, u32 bar, } EXPORT_SYMBOL(snd_sof_dsp_update_bits64_unlocked); +bool snd_sof_dsp_update_bits_unlock(struct snd_sof_dev *sdev, u32 bar, u32 offset, + u32 mask, u32 value) +{ + bool change; + + change = snd_sof_dsp_update_bits_unlocked(sdev, bar, offset, mask, + value); + return change; +} +EXPORT_SYMBOL(snd_sof_dsp_update_bits_unlock); + /* This is for registers bits with attribute RWC */ bool snd_sof_dsp_update_bits(struct snd_sof_dev *sdev, u32 bar, u32 offset, u32 mask, u32 value) @@ -115,7 +126,6 @@ bool snd_sof_dsp_update_bits64(struct snd_sof_dev *sdev, u32 bar, u32 offset, } EXPORT_SYMBOL(snd_sof_dsp_update_bits64); -static void snd_sof_dsp_update_bits_forced_unlocked(struct snd_sof_dev *sdev, u32 bar, u32 offset, u32 mask, u32 value) { @@ -129,6 +139,7 @@ void snd_sof_dsp_update_bits_forced_unlocked(struct snd_sof_dev *sdev, u32 bar, snd_sof_dsp_write(sdev, bar, offset, new); } +EXPORT_SYMBOL(snd_sof_dsp_update_bits_forced_unlocked); /* This is for registers bits with attribute RWC */ void snd_sof_dsp_update_bits_forced(struct snd_sof_dev *sdev, u32 bar, diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 45a3d10911634a..7a2465094f237f 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -405,7 +405,8 @@ bool snd_sof_dsp_update_bits_unlocked(struct snd_sof_dev *sdev, u32 bar, bool snd_sof_dsp_update_bits64_unlocked(struct snd_sof_dev *sdev, u32 bar, u32 offset, u64 mask, u64 value); - +bool snd_sof_dsp_update_bits_unlock(struct snd_sof_dev *sdev, u32 bar, u32 offset, + u32 mask, u32 value); bool snd_sof_dsp_update_bits(struct snd_sof_dev *sdev, u32 bar, u32 offset, u32 mask, u32 value); @@ -414,7 +415,8 @@ bool snd_sof_dsp_update_bits64(struct snd_sof_dev *sdev, u32 bar, void snd_sof_dsp_update_bits_forced(struct snd_sof_dev *sdev, u32 bar, u32 offset, u32 mask, u32 value); - +void snd_sof_dsp_update_bits_forced_unlocked(struct snd_sof_dev *sdev, u32 bar, + u32 offset, u32 mask, u32 value); int snd_sof_dsp_register_poll(struct snd_sof_dev *sdev, u32 bar, u32 offset, u32 mask, u32 target, u32 timeout_ms, u32 interval_us); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 81eeacb84d2506..273d6e1b6e5002 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -352,6 +352,9 @@ struct snd_sof_dev { spinlock_t ipc_lock; /* lock for IPC users */ spinlock_t hw_lock; /* lock for HW IO access */ + int irq_handled; + int wakeup; + u32 intsts; /* * ASoC components. plat_drv fields are set dynamically so * can't use const