ASoC: SOF: Intel: hda: fix MSI issues by merging ipc and stream irq handlers

The existing code uses two handlers for a shared edge-based MSI interrupts.
In corner cases, interrupts are lost, leading to IPC timeouts. Those
timeouts do not appear in legacy mode.

This patch merges the two handlers into a single one. Follow-up patches
will extend this work to SoundWire interrupts which are also shared with
IPC and stream interrupts.

Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com>
This commit is contained in:
Bard Liao 2019-11-14 13:08:08 +08:00 committed by Pierre Bossart
parent 2824abb095
commit dbf48060e3
6 changed files with 45 additions and 41 deletions

View File

@ -42,7 +42,6 @@ const struct snd_sof_dsp_ops sof_apl_ops = {
.block_write = sof_block_write,
/* doorbell */
.irq_handler = hda_dsp_ipc_irq_handler,
.irq_thread = hda_dsp_ipc_irq_thread,
/* ipc */

View File

@ -232,7 +232,6 @@ const struct snd_sof_dsp_ops sof_cnl_ops = {
.block_write = sof_block_write,
/* doorbell */
.irq_handler = hda_dsp_ipc_irq_handler,
.irq_thread = cnl_ipc_irq_thread,
/* ipc */

View File

@ -237,14 +237,14 @@ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context)
return IRQ_HANDLED;
}
/* is this IRQ for ADSP ? - we only care about IPC here */
irqreturn_t hda_dsp_ipc_irq_handler(int irq, void *context)
/* Check if it is an IPC and disable IPC interrupt if yes */
bool check_ipc(struct snd_sof_dev *sdev)
{
struct snd_sof_dev *sdev = context;
int ret = IRQ_NONE;
bool ret = false;
u32 irq_status;
spin_lock(&sdev->hw_lock);
/* The function can be called at irq thread, so use spin_lock_irq */
spin_lock_irq(&sdev->hw_lock);
/* store status */
irq_status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIS);
@ -260,11 +260,11 @@ irqreturn_t hda_dsp_ipc_irq_handler(int irq, void *context)
snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR,
HDA_DSP_REG_ADSPIC,
HDA_DSP_ADSPIC_IPC, 0);
ret = IRQ_WAKE_THREAD;
ret = true;
}
out:
spin_unlock(&sdev->hw_lock);
spin_unlock_irq(&sdev->hw_lock);
return ret;
}

View File

@ -550,22 +550,23 @@ int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev,
return 0;
}
irqreturn_t hda_dsp_stream_interrupt(int irq, void *context)
bool is_stream_irq(struct snd_sof_dev *sdev)
{
struct hdac_bus *bus = context;
int ret = IRQ_WAKE_THREAD;
struct hdac_bus *bus = sof_to_bus(sdev);
bool ret = true;
u32 status;
spin_lock(&bus->reg_lock);
/* The function can be called at irq thread, so use spin_lock_irq */
spin_lock_irq(&bus->reg_lock);
status = snd_hdac_chip_readl(bus, INTSTS);
dev_vdbg(bus->dev, "stream irq, INTSTS status: 0x%x\n", status);
/* Register inaccessible, ignore it.*/
if (status == 0xffffffff)
ret = IRQ_NONE;
ret = false;
spin_unlock(&bus->reg_lock);
spin_unlock_irq(&bus->reg_lock);
return ret;
}
@ -603,7 +604,8 @@ static bool hda_dsp_stream_check(struct hdac_bus *bus, u32 status)
irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context)
{
struct hdac_bus *bus = context;
struct snd_sof_dev *sdev = context;
struct hdac_bus *bus = sof_to_bus(sdev);
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
u32 rirb_status;
#endif

View File

@ -402,6 +402,28 @@ static const struct sof_intel_dsp_desc
return chip_info;
}
irqreturn_t hda_dsp_interrupt_handler(int irq, void *context)
{
struct snd_sof_dev *sdev = context;
if (check_ipc(sdev) || is_stream_irq(sdev))
return IRQ_WAKE_THREAD;
return IRQ_NONE;
}
irqreturn_t hda_dsp_interrupt_thread(int irq, void *context)
{
struct snd_sof_dev *sdev = context;
if (check_ipc(sdev))
sof_ops(sdev)->irq_thread(irq, sdev);
if (is_stream_irq(sdev))
hda_dsp_stream_threaded_handler(irq, sdev);
return IRQ_HANDLED;
}
int hda_dsp_probe(struct snd_sof_dev *sdev)
{
struct pci_dev *pci = to_pci_dev(sdev->dev);
@ -506,9 +528,7 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
*/
if (hda_use_msi && pci_alloc_irq_vectors(pci, 1, 1, PCI_IRQ_MSI) > 0) {
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->ipc_irq = pci_irq_vector(pci, 0);
/* initialised to "false" by kzalloc() */
sdev->msi_enabled = true;
}
@ -519,28 +539,17 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
* 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;
}
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);
if (ret < 0) {
dev_err(sdev->dev, "error: failed to register HDA IRQ %d\n",
hdev->irq);
goto free_irq_vector;
}
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);
ret = request_threaded_irq(sdev->ipc_irq, hda_dsp_interrupt_handler,
hda_dsp_interrupt_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;
goto free_irq_vector;
}
pci_set_master(pci);
@ -571,8 +580,6 @@ int hda_dsp_probe(struct snd_sof_dev *sdev)
free_ipc_irq:
free_irq(sdev->ipc_irq, sdev);
free_hda_irq:
free_irq(hdev->irq, bus);
free_irq_vector:
if (sdev->msi_enabled)
pci_free_irq_vectors(pci);
@ -618,7 +625,6 @@ int hda_dsp_remove(struct snd_sof_dev *sdev)
SOF_HDA_PPCTL_GPROCEN, 0);
free_irq(sdev->ipc_irq, sdev);
free_irq(hda->irq, bus);
if (sdev->msi_enabled)
pci_free_irq_vectors(pci);

View File

@ -406,8 +406,6 @@ struct sof_intel_hda_dev {
/* the maximum number of streams (playback + capture) supported */
u32 stream_max;
int irq;
/* PM related */
bool l1_support_changed;/* during suspend, is L1SEN changed or not */
@ -511,11 +509,12 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev,
struct snd_pcm_hw_params *params);
int hda_dsp_stream_trigger(struct snd_sof_dev *sdev,
struct hdac_ext_stream *stream, int cmd);
irqreturn_t hda_dsp_stream_interrupt(int irq, void *context);
irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context);
int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev,
struct snd_dma_buffer *dmab,
struct hdac_stream *stream);
bool check_ipc(struct snd_sof_dev *sdev);
bool is_stream_irq(struct snd_sof_dev *sdev);
struct hdac_ext_stream *
hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction);
@ -540,7 +539,6 @@ void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev);
int hda_dsp_ipc_get_mailbox_offset(struct snd_sof_dev *sdev);
int hda_dsp_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id);
irqreturn_t hda_dsp_ipc_irq_handler(int irq, void *context);
irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context);
int hda_dsp_ipc_cmd_done(struct snd_sof_dev *sdev, int dir);