| From a734d59da57210648d4e73e2893e8ac14ac9fc7b Mon Sep 17 00:00:00 2001 |
| From: Cezary Rojewski <cezary.rojewski@intel.com> |
| Date: Fri, 19 Apr 2024 10:48:56 +0200 |
| Subject: [PATCH] BACKPORT: FROMGIT: ASoC: Intel: avs: New IRQ handling |
| implementation |
| |
| The existing code can be both improved and simplified. To make this |
| change easier to manage, first add new implementation and then remove |
| deadcode in a separate patch. |
| |
| Simplification achieved with: |
| - reduce the amount of resources requested by the driver i.e.: IPC and |
| CLDMA request_irq() merged into one |
| - reduce the number of DSP ops from 2 to 1: |
| irq_handler/thread() vs dsp_interrupt() |
| - drop ambiguity around CLDMA interrupt, let skl.c handle that |
| explicitly as it is the only user |
| |
| Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com> |
| Link: https://lore.kernel.org/r/[email protected] |
| Signed-off-by: Mark Brown <broonie@kernel.org> |
| (cherry picked from commit 7ce6ceeb77bfd9fb0b22203190bd6f57fe917b51 |
| git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next) |
| |
| Conflicts: |
| sound/soc/intel/avs/apl.c |
| sound/soc/intel/avs/avs.h |
| sound/soc/intel/avs/cnl.c |
| sound/soc/intel/avs/icl.c |
| sound/soc/intel/avs/tgl.c |
| |
| BUG=b:329025955 |
| TEST=no 'snd_soc_avs 0000:00:1f.3: firmware boot failed: -110' in dmesg |
| |
| Change-Id: I7ab016e91614e8115f791a7f81d42c922a987451 |
| Signed-off-by: Lukasz Majczak <lmajczak@google.com> |
| Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/5471772 |
| Reviewed-by: Wojciech Macek <wmacek@google.com> |
| --- |
| sound/soc/intel/avs/apl.c | 18 +++++++++ |
| sound/soc/intel/avs/avs.h | 2 + |
| sound/soc/intel/avs/cldma.c | 11 +++++ |
| sound/soc/intel/avs/cldma.h | 1 + |
| sound/soc/intel/avs/core.c | 80 +++++++++++++++++++++++++++++++++++++ |
| sound/soc/intel/avs/skl.c | 62 ++++++++++++++++++++++++++++ |
| 6 files changed, 174 insertions(+) |
| |
| diff --git a/sound/soc/intel/avs/apl.c b/sound/soc/intel/avs/apl.c |
| index c21ecaef9ebaa7d3a3093881777575d4e0ebe7fb..a186d88430b95c8e920cc29c64e09ce30d18da98 100644 |
| --- a/sound/soc/intel/avs/apl.c |
| +++ b/sound/soc/intel/avs/apl.c |
| @@ -8,11 +8,28 @@ |
| |
| #include <linux/devcoredump.h> |
| #include <linux/slab.h> |
| +#include <sound/hdaudio_ext.h> |
| #include "avs.h" |
| #include "messages.h" |
| #include "path.h" |
| #include "topology.h" |
| |
| +static irqreturn_t avs_apl_dsp_interrupt(struct avs_dev *adev) |
| +{ |
| + u32 adspis = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPIS); |
| + irqreturn_t ret = IRQ_NONE; |
| + |
| + if (adspis == UINT_MAX) |
| + return ret; |
| + |
| + if (adspis & AVS_ADSP_ADSPIS_IPC) { |
| + avs_skl_ipc_interrupt(adev); |
| + ret = IRQ_HANDLED; |
| + } |
| + |
| + return ret; |
| +} |
| + |
| #ifdef CONFIG_DEBUG_FS |
| int avs_apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, |
| u32 fifo_full_period, unsigned long resource_mask, u32 *priorities) |
| @@ -237,6 +254,7 @@ const struct avs_dsp_ops avs_apl_dsp_ops = { |
| .power = avs_dsp_core_power, |
| .reset = avs_dsp_core_reset, |
| .stall = avs_dsp_core_stall, |
| + .dsp_interrupt = avs_apl_dsp_interrupt, |
| .irq_handler = avs_irq_handler, |
| .irq_thread = avs_skl_irq_thread, |
| .int_control = avs_dsp_interrupt_control, |
| diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h |
| index f80f79415344abde8784ce485ee7345fb8d61b30..51fb53e31b2e3730f2a7f6ea8f2e2972394f30a9 100644 |
| --- a/sound/soc/intel/avs/avs.h |
| +++ b/sound/soc/intel/avs/avs.h |
| @@ -46,6 +46,7 @@ struct avs_dsp_ops { |
| int (* const power)(struct avs_dev *, u32, bool); |
| int (* const reset)(struct avs_dev *, u32, bool); |
| int (* const stall)(struct avs_dev *, u32, bool); |
| + irqreturn_t (* const dsp_interrupt)(struct avs_dev *); |
| irqreturn_t (* const irq_handler)(struct avs_dev *); |
| irqreturn_t (* const irq_thread)(struct avs_dev *); |
| void (* const int_control)(struct avs_dev *, bool); |
| @@ -268,6 +269,7 @@ int avs_dsp_disable_d0ix(struct avs_dev *adev); |
| int avs_dsp_enable_d0ix(struct avs_dev *adev); |
| |
| irqreturn_t avs_skl_irq_thread(struct avs_dev *adev); |
| +void avs_skl_ipc_interrupt(struct avs_dev *adev); |
| irqreturn_t avs_cnl_irq_thread(struct avs_dev *adev); |
| int avs_apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, |
| u32 fifo_full_period, unsigned long resource_mask, u32 *priorities); |
| diff --git a/sound/soc/intel/avs/cldma.c b/sound/soc/intel/avs/cldma.c |
| index d7a9390b5e483c91dc0939da1fbdb1d1aeaaacff..d485ca11ad9a69bd0bdfa6db4168f5a78b9c24a1 100644 |
| --- a/sound/soc/intel/avs/cldma.c |
| +++ b/sound/soc/intel/avs/cldma.c |
| @@ -270,6 +270,17 @@ static irqreturn_t cldma_irq_handler(int irq, void *dev_id) |
| return IRQ_HANDLED; |
| } |
| |
| +void hda_cldma_interrupt(struct hda_cldma *cl) |
| +{ |
| + /* disable CLDMA interrupt */ |
| + snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA, 0); |
| + |
| + cl->sd_status = snd_hdac_stream_readb(cl, SD_STS); |
| + dev_dbg(cl->dev, "%s sd_status: 0x%08x\n", __func__, cl->sd_status); |
| + |
| + complete(&cl->completion); |
| +} |
| + |
| int hda_cldma_init(struct hda_cldma *cl, struct hdac_bus *bus, void __iomem *dsp_ba, |
| unsigned int buffer_size) |
| { |
| diff --git a/sound/soc/intel/avs/cldma.h b/sound/soc/intel/avs/cldma.h |
| index 223d3431ab811620fb82d7131cf036df683977a4..7d95e2747f5272a7ca26493c76436d026191f109 100644 |
| --- a/sound/soc/intel/avs/cldma.h |
| +++ b/sound/soc/intel/avs/cldma.h |
| @@ -24,6 +24,7 @@ int hda_cldma_reset(struct hda_cldma *cl); |
| |
| void hda_cldma_set_data(struct hda_cldma *cl, void *data, unsigned int size); |
| void hda_cldma_setup(struct hda_cldma *cl); |
| +void hda_cldma_interrupt(struct hda_cldma *cl); |
| int hda_cldma_init(struct hda_cldma *cl, struct hdac_bus *bus, void __iomem *dsp_ba, |
| unsigned int buffer_size); |
| void hda_cldma_free(struct hda_cldma *cl); |
| diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c |
| index d7f8940099cec6510c1179b7045134243767625d..09027c34c14c2e2552cb2012ace11f75592971d7 100644 |
| --- a/sound/soc/intel/avs/core.c |
| +++ b/sound/soc/intel/avs/core.c |
| @@ -336,6 +336,86 @@ static irqreturn_t avs_dsp_irq_thread(int irq, void *dev_id) |
| return avs_dsp_op(adev, irq_thread); |
| } |
| |
| +static irqreturn_t avs_hda_interrupt(struct hdac_bus *bus) |
| +{ |
| + irqreturn_t ret = IRQ_NONE; |
| + u32 status; |
| + |
| + status = snd_hdac_chip_readl(bus, INTSTS); |
| + if (snd_hdac_bus_handle_stream_irq(bus, status, hdac_update_stream)) |
| + ret = IRQ_HANDLED; |
| + |
| + spin_lock_irq(&bus->reg_lock); |
| + /* Clear RIRB interrupt. */ |
| + 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); |
| + ret = IRQ_HANDLED; |
| + } |
| + |
| + spin_unlock_irq(&bus->reg_lock); |
| + return ret; |
| +} |
| + |
| +__maybe_unused |
| +static irqreturn_t avs_hda_irq_handler(int irq, void *dev_id) |
| +{ |
| + struct hdac_bus *bus = dev_id; |
| + u32 intsts; |
| + |
| + intsts = snd_hdac_chip_readl(bus, INTSTS); |
| + if (intsts == UINT_MAX || !(intsts & AZX_INT_GLOBAL_EN)) |
| + return IRQ_NONE; |
| + |
| + /* Mask GIE, unmasked in irq_thread(). */ |
| + snd_hdac_chip_updatel(bus, INTCTL, AZX_INT_GLOBAL_EN, 0); |
| + |
| + return IRQ_WAKE_THREAD; |
| +} |
| + |
| +__maybe_unused |
| +static irqreturn_t avs_hda_irq_thread(int irq, void *dev_id) |
| +{ |
| + struct hdac_bus *bus = dev_id; |
| + u32 status; |
| + |
| + status = snd_hdac_chip_readl(bus, INTSTS); |
| + if (status & ~AZX_INT_GLOBAL_EN) |
| + avs_hda_interrupt(bus); |
| + |
| + /* Unmask GIE, masked in irq_handler(). */ |
| + snd_hdac_chip_updatel(bus, INTCTL, AZX_INT_GLOBAL_EN, AZX_INT_GLOBAL_EN); |
| + |
| + return IRQ_HANDLED; |
| +} |
| + |
| +__maybe_unused |
| +static irqreturn_t avs_dsp_irq_handler2(int irq, void *dev_id) |
| +{ |
| + struct avs_dev *adev = dev_id; |
| + |
| + return avs_hda_irq_handler(irq, &adev->base.core); |
| +} |
| + |
| +__maybe_unused |
| +static irqreturn_t avs_dsp_irq_thread2(int irq, void *dev_id) |
| +{ |
| + struct avs_dev *adev = dev_id; |
| + struct hdac_bus *bus = &adev->base.core; |
| + u32 status; |
| + |
| + status = readl(bus->ppcap + AZX_REG_PP_PPSTS); |
| + if (status & AZX_PPCTL_PIE) |
| + avs_dsp_op(adev, dsp_interrupt); |
| + |
| + /* Unmask GIE, masked in irq_handler(). */ |
| + snd_hdac_chip_updatel(bus, INTCTL, AZX_INT_GLOBAL_EN, AZX_INT_GLOBAL_EN); |
| + |
| + return IRQ_HANDLED; |
| +} |
| + |
| static int avs_hdac_acquire_irq(struct avs_dev *adev) |
| { |
| struct hdac_bus *bus = &adev->base.core; |
| diff --git a/sound/soc/intel/avs/skl.c b/sound/soc/intel/avs/skl.c |
| index d19f8953993f4365556bea2fb2b0f3b9a359d90e..25abfead9f63add424da3157d32fa5a10df370d8 100644 |
| --- a/sound/soc/intel/avs/skl.c |
| +++ b/sound/soc/intel/avs/skl.c |
| @@ -10,6 +10,7 @@ |
| #include <linux/slab.h> |
| #include <sound/hdaudio_ext.h> |
| #include "avs.h" |
| +#include "cldma.h" |
| #include "messages.h" |
| |
| irqreturn_t avs_skl_irq_thread(struct avs_dev *adev) |
| @@ -37,6 +38,66 @@ irqreturn_t avs_skl_irq_thread(struct avs_dev *adev) |
| return IRQ_HANDLED; |
| } |
| |
| +void avs_skl_ipc_interrupt(struct avs_dev *adev) |
| +{ |
| + const struct avs_spec *spec = adev->spec; |
| + u32 hipc_ack, hipc_rsp; |
| + |
| + snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset, |
| + AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY, 0); |
| + |
| + hipc_ack = snd_hdac_adsp_readl(adev, spec->hipc->ack_offset); |
| + hipc_rsp = snd_hdac_adsp_readl(adev, spec->hipc->rsp_offset); |
| + |
| + /* DSP acked host's request. */ |
| + if (hipc_ack & spec->hipc->ack_done_mask) { |
| + complete(&adev->ipc->done_completion); |
| + |
| + /* Tell DSP it has our attention. */ |
| + snd_hdac_adsp_updatel(adev, spec->hipc->ack_offset, spec->hipc->ack_done_mask, |
| + spec->hipc->ack_done_mask); |
| + } |
| + |
| + /* DSP sent new response to process */ |
| + if (hipc_rsp & spec->hipc->rsp_busy_mask) { |
| + union avs_reply_msg msg; |
| + |
| + msg.primary = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCT); |
| + msg.ext.val = snd_hdac_adsp_readl(adev, SKL_ADSP_REG_HIPCTE); |
| + |
| + avs_dsp_process_response(adev, msg.val); |
| + |
| + /* Tell DSP we accepted its message. */ |
| + snd_hdac_adsp_updatel(adev, SKL_ADSP_REG_HIPCT, SKL_ADSP_HIPCT_BUSY, |
| + SKL_ADSP_HIPCT_BUSY); |
| + } |
| + |
| + snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset, |
| + AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY, |
| + AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY); |
| +} |
| + |
| +static irqreturn_t avs_skl_dsp_interrupt(struct avs_dev *adev) |
| +{ |
| + u32 adspis = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPIS); |
| + irqreturn_t ret = IRQ_NONE; |
| + |
| + if (adspis == UINT_MAX) |
| + return ret; |
| + |
| + if (adspis & AVS_ADSP_ADSPIS_CLDMA) { |
| + hda_cldma_interrupt(&code_loader); |
| + ret = IRQ_HANDLED; |
| + } |
| + |
| + if (adspis & AVS_ADSP_ADSPIS_IPC) { |
| + avs_skl_ipc_interrupt(adev); |
| + ret = IRQ_HANDLED; |
| + } |
| + |
| + return ret; |
| +} |
| + |
| static int __maybe_unused |
| avs_skl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, |
| u32 fifo_full_period, unsigned long resource_mask, u32 *priorities) |
| @@ -128,6 +189,7 @@ const struct avs_dsp_ops avs_skl_dsp_ops = { |
| .power = avs_dsp_core_power, |
| .reset = avs_dsp_core_reset, |
| .stall = avs_dsp_core_stall, |
| + .dsp_interrupt = avs_skl_dsp_interrupt, |
| .irq_handler = avs_irq_handler, |
| .irq_thread = avs_skl_irq_thread, |
| .int_control = avs_dsp_interrupt_control, |
| -- |
| 2.45.0.rc1.225.g2a3ae87e7f-goog |
| |