Unverified Commit 27a153e0 authored by Mark Brown's avatar Mark Brown
Browse files

ASoC: Intel: avs: Refactor IRQ handling

Merge series from Cezary Rojewski <cezary.rojewski@intel.com>:

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

With that done, switch to the new implementation and remove unused
members. While the change is non-trivial, from functional perspective
status quo is achieved.
parents ed37d240 84049e2d
Loading
Loading
Loading
Loading
+18 −2
Original line number Diff line number Diff line
@@ -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,8 +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,
	.irq_handler = avs_irq_handler,
	.irq_thread = avs_skl_irq_thread,
	.dsp_interrupt = avs_apl_dsp_interrupt,
	.int_control = avs_dsp_interrupt_control,
	.load_basefw = avs_hda_load_basefw,
	.load_lib = avs_hda_load_library,
+3 −5
Original line number Diff line number Diff line
@@ -46,8 +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 irq_handler)(struct avs_dev *);
	irqreturn_t (* const irq_thread)(struct avs_dev *);
	irqreturn_t (* const dsp_interrupt)(struct avs_dev *);
	void (* const int_control)(struct avs_dev *, bool);
	int (* const load_basefw)(struct avs_dev *, struct firmware *);
	int (* const load_lib)(struct avs_dev *, struct firmware *, u32);
@@ -245,7 +244,6 @@ struct avs_ipc {
#define AVS_IPC_RET(ret) \
	(((ret) <= 0) ? (ret) : -AVS_EIPC)

irqreturn_t avs_irq_handler(struct avs_dev *adev);
void avs_dsp_process_response(struct avs_dev *adev, u64 header);
int avs_dsp_send_msg_timeout(struct avs_dev *adev, struct avs_ipc_msg *request,
			     struct avs_ipc_msg *reply, int timeout, const char *name);
@@ -267,8 +265,8 @@ void avs_ipc_block(struct avs_ipc *ipc);
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);
irqreturn_t avs_cnl_irq_thread(struct avs_dev *adev);
void avs_skl_ipc_interrupt(struct avs_dev *adev);
irqreturn_t avs_cnl_dsp_interrupt(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);
int avs_icl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
+8 −34
Original line number Diff line number Diff line
@@ -248,32 +248,20 @@ void hda_cldma_setup(struct hda_cldma *cl)
	snd_hdac_stream_writel(cl, CL_SPBFCTL, 1);
}

static irqreturn_t cldma_irq_handler(int irq, void *dev_id)
void hda_cldma_interrupt(struct hda_cldma *cl)
{
	struct hda_cldma *cl = dev_id;
	u32 adspis;

	adspis = snd_hdac_adsp_readl(cl, AVS_ADSP_REG_ADSPIS);
	if (adspis == UINT_MAX)
		return IRQ_NONE;
	if (!(adspis & AVS_ADSP_ADSPIS_CLDMA))
		return IRQ_NONE;

	cl->sd_status = snd_hdac_stream_readb(cl, SD_STS);
	dev_warn(cl->dev, "%s sd_status: 0x%08x\n", __func__, cl->sd_status);

	/* disable CLDMA interrupt */
	snd_hdac_adsp_updatel(cl, AVS_ADSP_REG_ADSPIC, AVS_ADSP_ADSPIC_CLDMA, 0);

	complete(&cl->completion);
	cl->sd_status = snd_hdac_stream_readb(cl, SD_STS);
	dev_dbg(cl->dev, "%s sd_status: 0x%08x\n", __func__, cl->sd_status);

	return IRQ_HANDLED;
	complete(&cl->completion);
}

int hda_cldma_init(struct hda_cldma *cl, struct hdac_bus *bus, void __iomem *dsp_ba,
		   unsigned int buffer_size)
{
	struct pci_dev *pci = to_pci_dev(bus->dev);
	int ret;

	ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, bus->dev, buffer_size, &cl->dmab_data);
@@ -281,8 +269,10 @@ int hda_cldma_init(struct hda_cldma *cl, struct hdac_bus *bus, void __iomem *dsp
		return ret;

	ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, bus->dev, BDL_SIZE, &cl->dmab_bdl);
	if (ret < 0)
		goto alloc_err;
	if (ret < 0) {
		snd_dma_free_pages(&cl->dmab_data);
		return ret;
	}

	cl->dev = bus->dev;
	cl->bus = bus;
@@ -290,27 +280,11 @@ int hda_cldma_init(struct hda_cldma *cl, struct hdac_bus *bus, void __iomem *dsp
	cl->buffer_size = buffer_size;
	cl->sd_addr = dsp_ba + AZX_CL_SD_BASE;

	ret = pci_request_irq(pci, 0, cldma_irq_handler, NULL, cl, "CLDMA");
	if (ret < 0) {
		dev_err(cl->dev, "Failed to request CLDMA IRQ handler: %d\n", ret);
		goto req_err;
	}

	return 0;

req_err:
	snd_dma_free_pages(&cl->dmab_bdl);
alloc_err:
	snd_dma_free_pages(&cl->dmab_data);

	return ret;
}

void hda_cldma_free(struct hda_cldma *cl)
{
	struct pci_dev *pci = to_pci_dev(cl->dev);

	pci_free_irq(pci, 0, cl);
	snd_dma_free_pages(&cl->dmab_data);
	snd_dma_free_pages(&cl->dmab_bdl);
}
+1 −0
Original line number Diff line number Diff line
@@ -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);
+60 −31
Original line number Diff line number Diff line
@@ -10,20 +10,34 @@
#include "avs.h"
#include "messages.h"

irqreturn_t avs_cnl_irq_thread(struct avs_dev *adev)
static void avs_cnl_ipc_interrupt(struct avs_dev *adev)
{
	union avs_reply_msg msg;
	u32 hipctdr, hipctdd, hipctda;
	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);

	hipctdr = snd_hdac_adsp_readl(adev, CNL_ADSP_REG_HIPCTDR);
	hipctdd = snd_hdac_adsp_readl(adev, CNL_ADSP_REG_HIPCTDD);
		/* 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;
		u32 hipctda;

	/* Ensure DSP sent new response to process. */
	if (!(hipctdr & CNL_ADSP_HIPCTDR_BUSY))
		return IRQ_NONE;
		msg.primary = snd_hdac_adsp_readl(adev, CNL_ADSP_REG_HIPCTDR);
		msg.ext.val = snd_hdac_adsp_readl(adev, CNL_ADSP_REG_HIPCTDD);

	msg.primary = hipctdr;
	msg.ext.val = hipctdd;
		avs_dsp_process_response(adev, msg.val);

		/* Tell DSP we accepted its message. */
@@ -35,19 +49,34 @@ irqreturn_t avs_cnl_irq_thread(struct avs_dev *adev)
		/* HW might have been clock gated, give some time for change to propagate. */
		snd_hdac_adsp_readl_poll(adev, CNL_ADSP_REG_HIPCTDA, hipctda,
					 !(hipctda & CNL_ADSP_HIPCTDA_DONE), 10, 1000);
	/* Unmask busy interrupt. */
	snd_hdac_adsp_updatel(adev, CNL_ADSP_REG_HIPCCTL,
			      AVS_ADSP_HIPCCTL_BUSY, AVS_ADSP_HIPCCTL_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);
}

irqreturn_t avs_cnl_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_cnl_ipc_interrupt(adev);
		ret = IRQ_HANDLED;
	}

	return IRQ_HANDLED;
	return ret;
}

const struct avs_dsp_ops avs_cnl_dsp_ops = {
	.power = avs_dsp_core_power,
	.reset = avs_dsp_core_reset,
	.stall = avs_dsp_core_stall,
	.irq_handler = avs_irq_handler,
	.irq_thread = avs_cnl_irq_thread,
	.dsp_interrupt = avs_cnl_dsp_interrupt,
	.int_control = avs_dsp_interrupt_control,
	.load_basefw = avs_hda_load_basefw,
	.load_lib = avs_hda_load_library,
Loading