diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 36ae9b88d0dc3d..0fd7207610ba46 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -83,7 +83,8 @@ static irqreturn_t cnl_ipc_irq_thread(int irq, void *context) } /* new message from DSP */ - if (hipctdr & CNL_DSP_REG_HIPCTDR_BUSY) { + if (hipctdr & CNL_DSP_REG_HIPCTDR_BUSY && + hipcctl & CNL_DSP_REG_HIPCCTL_BUSY) { hipctdd = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCTDD); msg = hipctdr & CNL_DSP_REG_HIPCTDR_MSG_MASK; @@ -101,6 +102,11 @@ static irqreturn_t cnl_ipc_irq_thread(int irq, void *context) snd_sof_ipc_msgs_rx(sdev); } + /* mask BUSY interrupt */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, + HDA_DSP_REG_HIPCCTL, + HDA_DSP_REG_HIPCCTL_BUSY, 0); + /* * clear busy interrupt to tell dsp controller this * interrupt has been accepted, not trigger it again diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c index a938f568dbb1c3..0b4ba8da1b12d2 100644 --- a/sound/soc/sof/intel/hda-ipc.c +++ b/sound/soc/sof/intel/hda-ipc.c @@ -234,19 +234,21 @@ irqreturn_t hda_dsp_ipc_irq_handler(int irq, void *context) { struct snd_sof_dev *sdev = context; int ret = IRQ_NONE; + u32 irq_status = 0; spin_lock(&sdev->hw_lock); /* store status */ - sdev->irq_status = snd_sof_dsp_read(sdev, HDA_DSP_BAR, - HDA_DSP_REG_ADSPIS); + 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); /* invalid message ? */ - if (sdev->irq_status == 0xffffffff) + if (irq_status == 0xffffffff) goto out; /* IPC message ? */ - if (sdev->irq_status & HDA_DSP_ADSPIS_IPC) { + if (irq_status & HDA_DSP_ADSPIS_IPC) { + sdev->irq_status = irq_status; /* disable IPC interrupt */ snd_sof_dsp_update_bits_unlocked(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 6290b2df5e621d..e72385a0f7bbcb 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -433,15 +433,23 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, 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; u32 status; + dev_vdbg(bus->dev, "stream irq, INTSTS status: 0x%x\n", + snd_hdac_chip_readl(bus, INTSTS)); + if (!pm_runtime_active(bus->dev)) return IRQ_NONE; spin_lock(&bus->reg_lock); status = snd_hdac_chip_readl(bus, INTSTS); - if (status == 0 || status == 0xffffffff) { + stream_mask = GENMASK(sof_hda->stream_total - 1, 0) | AZX_INT_CTRL_EN; + + /* Not stream interrupt or register inaccessible, ignore it.*/ + if (!(status & stream_mask) || status == 0xffffffff) { spin_unlock(&bus->reg_lock); return IRQ_NONE; } @@ -465,8 +473,8 @@ irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context) { struct hdac_bus *bus = context; struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); - struct hdac_stream *s; u32 status = snd_hdac_chip_readl(bus, INTSTS); + struct hdac_stream *s; u32 sd_status; /* check streams */ @@ -486,7 +494,7 @@ irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context) /* Inform ALSA only in case not do that with IPC */ if (sof_hda->no_ipc_position) - snd_pcm_period_elapsed(s->substream); + snd_sof_pcm_elapsed(s->substream); } } @@ -500,6 +508,7 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) struct hdac_ext_stream *stream; struct hdac_stream *hstream; struct pci_dev *pci = to_pci_dev(sdev->dev); + struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); int sd_offset; int i, num_playback, num_capture, num_total, ret; u32 gcap; @@ -657,6 +666,9 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) list_add_tail(&hstream->list, &bus->stream_list); } + /* store the total playback + capture stream number */ + sof_hda->stream_total = num_total; + return 0; } diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index e49d9c92dd1c8b..b6129e029b898d 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -367,6 +367,9 @@ struct sof_intel_hda_dev { /* if position update IPC needed */ u32 no_ipc_position; + /* the total playback + capture stream number */ + u32 stream_total; + int irq; /* DMIC device */ diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index bf58d58e737964..3c42eb51bc092d 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -445,7 +445,7 @@ static void ipc_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) /* only inform ALSA for period_wakeup mode */ if (!stream->substream->runtime->no_period_wakeup) - snd_pcm_period_elapsed(stream->substream); + snd_sof_pcm_elapsed(stream->substream); } /* DSP notifies host of an XRUN within FW */ diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index be4984c4da4eb8..b406bb4b307e5c 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -52,6 +52,39 @@ static int sof_pcm_dsp_params(struct snd_sof_pcm *spcm, struct snd_pcm_substream return ret; } +/* + * sof pcm period elapse work + */ +static void sof_elapsed_work(struct work_struct *work) +{ + struct snd_sof_pcm_stream *sps = + container_of(work, struct snd_sof_pcm_stream, elapse_work); + + snd_pcm_period_elapsed(sps->substream); +} + +/* + * sof pcm period elapse, this might be called at irq context. + */ +void snd_sof_pcm_elapsed(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component = + snd_soc_rtdcom_lookup(rtd, DRV_NAME); + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_sof_pcm *spcm; + + spcm = snd_sof_find_spcm_dai(sdev, rtd); + if (!spcm) { + dev_err(sdev->dev, + "error: period elapsed for unknown stream!\n"); + return; + } + + schedule_work(&spcm->stream[substream->stream].elapse_work); +} +EXPORT_SYMBOL(snd_sof_pcm_elapsed); + /* this may get called several times by oss emulation */ static int sof_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) @@ -169,6 +202,9 @@ static int sof_pcm_hw_params(struct snd_pcm_substream *substream, /* save pcm hw_params */ memcpy(&spcm->params[substream->stream], params, sizeof(*params)); + INIT_WORK(&spcm->stream[substream->stream].elapse_work, + sof_elapsed_work); + return ret; } @@ -203,6 +239,9 @@ static int sof_pcm_hw_free(struct snd_pcm_substream *substream) sizeof(stream), &reply, sizeof(reply)); snd_pcm_lib_free_pages(substream); + + cancel_work_sync(&spcm->stream[substream->stream].elapse_work); + return ret; } diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 35e78ffecce2fe..e03ef6999fb640 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -274,6 +274,7 @@ struct snd_sof_pcm_stream { struct snd_dma_buffer page_table; struct sof_ipc_stream_posn posn; struct snd_pcm_substream *substream; + struct work_struct elapse_work; }; /* ALSA SOF PCM device */ @@ -495,6 +496,7 @@ struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_sof_dev *sdev, int *direction); struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_sof_dev *sdev, unsigned int pcm_id); +void snd_sof_pcm_elapsed(struct snd_pcm_substream *substream); /* * Stream IPC