diff options
Diffstat (limited to 'sound/soc/sof/intel')
-rw-r--r-- | sound/soc/sof/intel/Kconfig | 33 | ||||
-rw-r--r-- | sound/soc/sof/intel/apl.c | 2 | ||||
-rw-r--r-- | sound/soc/sof/intel/byt.c | 5 | ||||
-rw-r--r-- | sound/soc/sof/intel/cnl.c | 79 | ||||
-rw-r--r-- | sound/soc/sof/intel/hda-ctrl.c | 77 | ||||
-rw-r--r-- | sound/soc/sof/intel/hda-dai.c | 293 | ||||
-rw-r--r-- | sound/soc/sof/intel/hda-dsp.c | 63 | ||||
-rw-r--r-- | sound/soc/sof/intel/hda-ipc.c | 43 | ||||
-rw-r--r-- | sound/soc/sof/intel/hda-stream.c | 157 | ||||
-rw-r--r-- | sound/soc/sof/intel/hda.c | 30 | ||||
-rw-r--r-- | sound/soc/sof/intel/hda.h | 22 |
11 files changed, 590 insertions, 214 deletions
diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index 603e0db4f012..dd14ce92fe10 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config SND_SOC_SOF_INTEL_TOPLEVEL bool "SOF support for Intel audio DSPs" depends on X86 || COMPILE_TEST @@ -24,6 +25,8 @@ config SND_SOC_SOF_INTEL_PCI select SND_SOC_SOF_CANNONLAKE if SND_SOC_SOF_CANNONLAKE_SUPPORT select SND_SOC_SOF_COFFEELAKE if SND_SOC_SOF_COFFEELAKE_SUPPORT select SND_SOC_SOF_ICELAKE if SND_SOC_SOF_ICELAKE_SUPPORT + select SND_SOC_SOF_COMETLAKE_LP if SND_SOC_SOF_COMETLAKE_LP_SUPPORT + select SND_SOC_SOF_COMETLAKE_H if SND_SOC_SOF_COMETLAKE_H_SUPPORT help This option is not user-selectable but automagically handled by 'select' statements at a higher level @@ -179,6 +182,36 @@ config SND_SOC_SOF_ICELAKE This option is not user-selectable but automagically handled by 'select' statements at a higher level +config SND_SOC_SOF_COMETLAKE_LP + tristate + select SND_SOC_SOF_HDA_COMMON + help + This option is not user-selectable but automagically handled by + 'select' statements at a higher level + +config SND_SOC_SOF_COMETLAKE_LP_SUPPORT + bool "SOF support for CometLake-LP" + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the Cometlake-LP processors. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_SOF_COMETLAKE_H + tristate + select SND_SOC_SOF_HDA_COMMON + help + This option is not user-selectable but automagically handled by + 'select' statements at a higher level + +config SND_SOC_SOF_COMETLAKE_H_SUPPORT + bool "SOF support for CometLake-H" + help + This adds support for Sound Open Firmware for Intel(R) platforms + using the Cometlake-H processors. + Say Y if you have such a device. + If unsure select "N". + config SND_SOC_SOF_HDA_COMMON tristate select SND_SOC_SOF_INTEL_COMMON diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index f215d80dce2c..fd2e26d79796 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -61,6 +61,7 @@ const struct snd_sof_dsp_ops sof_apl_ops = { .pcm_open = hda_dsp_pcm_open, .pcm_close = hda_dsp_pcm_close, .pcm_hw_params = hda_dsp_pcm_hw_params, + .pcm_hw_free = hda_dsp_stream_hw_free, .pcm_trigger = hda_dsp_pcm_trigger, .pcm_pointer = hda_dsp_pcm_pointer, @@ -92,6 +93,7 @@ const struct snd_sof_dsp_ops sof_apl_ops = { .resume = hda_dsp_resume, .runtime_suspend = hda_dsp_runtime_suspend, .runtime_resume = hda_dsp_runtime_resume, + .runtime_idle = hda_dsp_runtime_idle, .set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume, }; EXPORT_SYMBOL(sof_apl_ops); diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c index 39d1ae01c45d..107d711efc3f 100644 --- a/sound/soc/sof/intel/byt.c +++ b/sound/soc/sof/intel/byt.c @@ -376,13 +376,10 @@ static irqreturn_t byt_irq_thread(int irq, void *context) static int byt_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) { - u64 cmd = msg->header; - /* send the message */ sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, msg->msg_size); - snd_sof_dsp_write64(sdev, BYT_DSP_BAR, SHIM_IPCX, - cmd | SHIM_BYT_IPCX_BUSY); + snd_sof_dsp_write64(sdev, BYT_DSP_BAR, SHIM_IPCX, SHIM_BYT_IPCX_BUSY); return 0; } diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index b2eba7adcad8..f2b392998f20 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -31,27 +31,20 @@ static irqreturn_t cnl_ipc_irq_thread(int irq, void *context) { 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; + bool ipc_irq = false; 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); + hipctdd = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCTDD); + hipci = snd_sof_dsp_read(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR); /* reply message from DSP */ - 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); + if (hipcida & CNL_DSP_REG_HIPCIDA_DONE) { msg_ext = hipci & CNL_DSP_REG_HIPCIDR_MSG_MASK; msg = hipcida & CNL_DSP_REG_HIPCIDA_MSG_MASK; @@ -79,13 +72,11 @@ static irqreturn_t cnl_ipc_irq_thread(int irq, void *context) spin_unlock_irq(&sdev->ipc_lock); - ret = IRQ_HANDLED; + ipc_irq = true; } /* 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; @@ -101,26 +92,37 @@ static irqreturn_t cnl_ipc_irq_thread(int irq, void *context) 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(sdev, HDA_DSP_BAR, - CNL_DSP_REG_HIPCTDR, - CNL_DSP_REG_HIPCTDR_BUSY, - CNL_DSP_REG_HIPCTDR_BUSY); - cnl_ipc_host_done(sdev); - ret = IRQ_HANDLED; + ipc_irq = true; + } + + if (!ipc_irq) { + /* + * This interrupt is not shared so no need to return IRQ_NONE. + */ + dev_err_ratelimited(sdev->dev, + "error: nothing to do in IRQ thread\n"); } - return ret; + /* re-enable 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_HANDLED; } static void cnl_ipc_host_done(struct snd_sof_dev *sdev) { /* + * 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); + /* * set done bit to ack dsp the msg has been * processed and send reply msg to dsp */ @@ -151,13 +153,11 @@ static void cnl_ipc_dsp_done(struct snd_sof_dev *sdev) static int cnl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) { - u32 cmd = msg->header; - /* send the message */ sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, msg->msg_size); snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR, - cmd | CNL_DSP_REG_HIPCIDR_BUSY); + CNL_DSP_REG_HIPCIDR_BUSY); return 0; } @@ -168,6 +168,8 @@ static void cnl_ipc_dump(struct snd_sof_dev *sdev) u32 hipcida; u32 hipctdr; + hda_ipc_irq_dump(sdev); + /* read IPC status */ 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); @@ -217,6 +219,7 @@ const struct snd_sof_dsp_ops sof_cnl_ops = { .pcm_open = hda_dsp_pcm_open, .pcm_close = hda_dsp_pcm_close, .pcm_hw_params = hda_dsp_pcm_hw_params, + .pcm_hw_free = hda_dsp_stream_hw_free, .pcm_trigger = hda_dsp_pcm_trigger, .pcm_pointer = hda_dsp_pcm_pointer, @@ -248,6 +251,7 @@ const struct snd_sof_dsp_ops sof_cnl_ops = { .resume = hda_dsp_resume, .runtime_suspend = hda_dsp_runtime_suspend, .runtime_resume = hda_dsp_runtime_resume, + .runtime_idle = hda_dsp_runtime_idle, .set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume, }; EXPORT_SYMBOL(sof_cnl_ops); @@ -270,3 +274,22 @@ const struct sof_intel_dsp_desc cnl_chip_info = { .ssp_base_offset = CNL_SSP_BASE_OFFSET, }; EXPORT_SYMBOL(cnl_chip_info); + +const struct sof_intel_dsp_desc icl_chip_info = { + /* Icelake */ + .cores_num = 4, + .init_core_mask = 1, + .cores_mask = HDA_DSP_CORE_MASK(0) | + HDA_DSP_CORE_MASK(1) | + HDA_DSP_CORE_MASK(2) | + HDA_DSP_CORE_MASK(3), + .ipc_req = CNL_DSP_REG_HIPCIDR, + .ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY, + .ipc_ack = CNL_DSP_REG_HIPCIDA, + .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE, + .ipc_ctl = CNL_DSP_REG_HIPCCTL, + .rom_init_timeout = 300, + .ssp_count = ICL_SSP_COUNT, + .ssp_base_offset = CNL_SSP_BASE_OFFSET, +}; +EXPORT_SYMBOL(icl_chip_info); diff --git a/sound/soc/sof/intel/hda-ctrl.c b/sound/soc/sof/intel/hda-ctrl.c index 07bc123112c9..ea63f83a509b 100644 --- a/sound/soc/sof/intel/hda-ctrl.c +++ b/sound/soc/sof/intel/hda-ctrl.c @@ -217,17 +217,14 @@ int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool full_reset) /* clear stream status */ list_for_each_entry(stream, &bus->stream_list, list) { sd_offset = SOF_STREAM_SD_OFFSET(stream); - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, - sd_offset + - SOF_HDA_ADSP_REG_CL_SD_STS, - SOF_HDA_CL_DMA_SD_INT_MASK, - SOF_HDA_CL_DMA_SD_INT_MASK); + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS, + SOF_HDA_CL_DMA_SD_INT_MASK); } /* clear WAKESTS */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_WAKESTS, - SOF_HDA_WAKESTS_INT_MASK, - SOF_HDA_WAKESTS_INT_MASK); + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_WAKESTS, + SOF_HDA_WAKESTS_INT_MASK); #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) /* clear rirb status */ @@ -263,3 +260,67 @@ int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool full_reset) return ret; } + +void hda_dsp_ctrl_stop_chip(struct snd_sof_dev *sdev) +{ + struct hdac_bus *bus = sof_to_bus(sdev); + struct hdac_stream *stream; + int sd_offset; + + if (!bus->chip_init) + return; + + /* disable interrupts in stream descriptor */ + list_for_each_entry(stream, &bus->stream_list, list) { + sd_offset = SOF_STREAM_SD_OFFSET(stream); + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + sd_offset + + SOF_HDA_ADSP_REG_CL_SD_CTL, + SOF_HDA_CL_DMA_SD_INT_MASK, + 0); + } + + /* disable SIE for all streams */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, + SOF_HDA_INT_ALL_STREAM, 0); + + /* disable controller CIE and GIE */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, + SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN, + 0); + + /* clear stream status */ + list_for_each_entry(stream, &bus->stream_list, list) { + sd_offset = SOF_STREAM_SD_OFFSET(stream); + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS, + SOF_HDA_CL_DMA_SD_INT_MASK); + } + + /* clear WAKESTS */ + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_WAKESTS, + SOF_HDA_WAKESTS_INT_MASK); + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) + /* clear rirb status */ + snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK); +#endif + + /* clear interrupt status register */ + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS, + SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_ALL_STREAM); + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) + /* disable CORB/RIRB */ + snd_hdac_bus_stop_cmd_io(bus); +#endif + /* disable position buffer */ + if (bus->posbuf.addr) { + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + SOF_HDA_ADSP_DPLBASE, 0); + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + SOF_HDA_ADSP_DPUBASE, 0); + } + + bus->chip_init = false; +} diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index e1decf25aeac..a514f9cf5c9a 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -30,62 +30,90 @@ struct hda_pipe_params { }; /* - * Unlike GP dma, there is a set of stream registers in hda controller - * to control the link dma channels. Each register controls one link - * dma channel and the relation is fixed. To make sure FW uses correct - * link dma channels, host allocates stream registers and sends the - * corresponding link dma channels to FW to allocate link dma channel - * - * FIXME: this API is abused in the sense that tx_num and rx_num are - * passed as arguments, not returned. We need to find a better way to - * retrieve the stream tag allocated for the link DMA + * This function checks if the host dma channel corresponding + * to the link DMA stream_tag argument is assigned to one + * of the FEs connected to the BE DAI. */ -static int hda_link_dma_get_channels(struct snd_soc_dai *dai, - unsigned int *tx_num, - unsigned int *tx_slot, - unsigned int *rx_num, - unsigned int *rx_slot) +static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd, + int dir, int stream_tag) { - struct hdac_bus *bus; - struct hdac_ext_stream *stream; - struct snd_pcm_substream substream; - struct snd_sof_dev *sdev = - snd_soc_component_get_drvdata(dai->component); - - bus = sof_to_bus(sdev); - - memset(&substream, 0, sizeof(substream)); - if (*tx_num == 1) { - substream.stream = SNDRV_PCM_STREAM_PLAYBACK; - stream = snd_hdac_ext_stream_assign(bus, &substream, - HDAC_EXT_STREAM_TYPE_LINK); - if (!stream) { - dev_err(bus->dev, "error: failed to find a free hda ext stream for playback"); - return -EBUSY; - } + struct snd_pcm_substream *fe_substream; + struct hdac_stream *fe_hstream; + struct snd_soc_dpcm *dpcm; + + for_each_dpcm_fe(rtd, dir, dpcm) { + fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, dir); + fe_hstream = fe_substream->runtime->private_data; + if (fe_hstream->stream_tag == stream_tag) + return true; + } - snd_soc_dai_set_dma_data(dai, &substream, stream); - *tx_slot = hdac_stream(stream)->stream_tag - 1; + return false; +} + +static struct hdac_ext_stream * + hda_link_stream_assign(struct hdac_bus *bus, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct sof_intel_hda_stream *hda_stream; + struct hdac_ext_stream *res = NULL; + struct hdac_stream *stream = NULL; - dev_dbg(bus->dev, "link dma channel %d for playback", *tx_slot); + int stream_dir = substream->stream; + + if (!bus->ppcap) { + dev_err(bus->dev, "stream type not supported\n"); + return NULL; } - if (*rx_num == 1) { - substream.stream = SNDRV_PCM_STREAM_CAPTURE; - stream = snd_hdac_ext_stream_assign(bus, &substream, - HDAC_EXT_STREAM_TYPE_LINK); - if (!stream) { - dev_err(bus->dev, "error: failed to find a free hda ext stream for capture"); - return -EBUSY; + list_for_each_entry(stream, &bus->stream_list, list) { + struct hdac_ext_stream *hstream = + stream_to_hdac_ext_stream(stream); + if (stream->direction != substream->stream) + continue; + + hda_stream = hstream_to_sof_hda_stream(hstream); + + /* check if link is available */ + if (!hstream->link_locked) { + if (stream->opened) { + /* + * check if the stream tag matches the stream + * tag of one of the connected FEs + */ + if (hda_check_fes(rtd, stream_dir, + stream->stream_tag)) { + res = hstream; + break; + } + } else { + res = hstream; + + /* + * This must be a hostless stream. + * So reserve the host DMA channel. + */ + hda_stream->host_reserved = 1; + break; + } } + } - snd_soc_dai_set_dma_data(dai, &substream, stream); - *rx_slot = hdac_stream(stream)->stream_tag - 1; - - dev_dbg(bus->dev, "link dma channel %d for capture", *rx_slot); + if (res) { + /* + * Decouple host and link DMA. The decoupled flag + * is updated in snd_hdac_ext_stream_decouple(). + */ + if (!res->decoupled) + snd_hdac_ext_stream_decouple(bus, res, true); + spin_lock_irq(&bus->reg_lock); + res->link_locked = 1; + res->link_substream = substream; + spin_unlock_irq(&bus->reg_lock); } - return 0; + return res; } static int hda_link_dma_params(struct hdac_ext_stream *stream, @@ -122,6 +150,51 @@ static int hda_link_dma_params(struct hdac_ext_stream *stream, return 0; } +/* Send DAI_CONFIG IPC to the DAI that matches the dai_name and direction */ +static int hda_link_config_ipc(struct sof_intel_hda_stream *hda_stream, + const char *dai_name, int channel, int dir) +{ + struct sof_ipc_dai_config *config; + struct snd_sof_dai *sof_dai; + struct sof_ipc_reply reply; + int ret = 0; + + list_for_each_entry(sof_dai, &hda_stream->sdev->dai_list, list) { + if (!sof_dai->cpu_dai_name) + continue; + + if (!strcmp(dai_name, sof_dai->cpu_dai_name) && + dir == sof_dai->comp_dai.direction) { + config = sof_dai->dai_config; + + if (!config) { + dev_err(hda_stream->sdev->dev, + "error: no config for DAI %s\n", + sof_dai->name); + return -EINVAL; + } + + /* update config with stream tag */ + config->hda.link_dma_ch = channel; + + /* send IPC */ + ret = sof_ipc_tx_message(hda_stream->sdev->ipc, + config->hdr.cmd, + config, + config->hdr.size, + &reply, sizeof(reply)); + + if (ret < 0) + dev_err(hda_stream->sdev->dev, + "error: failed to set dai config for %s\n", + sof_dai->name); + return ret; + } + } + + return -EINVAL; +} + static int hda_link_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -135,20 +208,31 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream, struct hda_pipe_params p_params = {0}; struct hdac_ext_link *link; int stream_tag; + int ret; - link_dev = snd_soc_dai_get_dma_data(dai, substream); + link_dev = hda_link_stream_assign(bus, substream); + if (!link_dev) + return -EBUSY; + + stream_tag = hdac_stream(link_dev)->stream_tag; + + hda_stream = hstream_to_sof_hda_stream(link_dev); + + /* update the DSP with the new tag */ + ret = hda_link_config_ipc(hda_stream, dai->name, stream_tag - 1, + substream->stream); + if (ret < 0) + return ret; + + snd_soc_dai_set_dma_data(dai, substream, (void *)link_dev); - hda_stream = container_of(link_dev, struct sof_intel_hda_stream, - hda_stream); hda_stream->hw_params_upon_resume = 0; link = snd_hdac_ext_bus_get_link(bus, codec_dai->component->name); if (!link) return -EINVAL; - stream_tag = hdac_stream(link_dev)->stream_tag; - - /* set the stream tag in the codec dai dma params */ + /* set the stream tag in the codec dai dma params */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) snd_soc_dai_set_tdm_slot(codec_dai, stream_tag, 0, 0, 0); else @@ -181,8 +265,7 @@ static int hda_link_pcm_prepare(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); int stream = substream->stream; - hda_stream = container_of(link_dev, struct sof_intel_hda_stream, - hda_stream); + hda_stream = hstream_to_sof_hda_stream(link_dev); /* setup hw_params again only if resuming from system suspend */ if (!hda_stream->hw_params_upon_resume) @@ -199,8 +282,24 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, { struct hdac_ext_stream *link_dev = snd_soc_dai_get_dma_data(dai, substream); + struct sof_intel_hda_stream *hda_stream; + struct snd_soc_pcm_runtime *rtd; + struct hdac_ext_link *link; + struct hdac_stream *hstream; + struct hdac_bus *bus; + int stream_tag; int ret; + hstream = substream->runtime->private_data; + bus = hstream->bus; + rtd = snd_pcm_substream_chip(substream); + + link = snd_hdac_ext_bus_get_link(bus, rtd->codec_dai->component->name); + if (!link) + return -EINVAL; + + hda_stream = hstream_to_sof_hda_stream(link_dev); + dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd); switch (cmd) { case SNDRV_PCM_TRIGGER_RESUME: @@ -217,8 +316,22 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: snd_hdac_ext_link_stream_start(link_dev); break; - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: + /* + * clear and release link DMA channel. It will be assigned when + * hw_params is set up again after resume. + */ + ret = hda_link_config_ipc(hda_stream, dai->name, + DMA_CHAN_INVALID, substream->stream); + if (ret < 0) + return ret; + stream_tag = hdac_stream(link_dev)->stream_tag; + snd_hdac_ext_link_clear_stream_id(link, stream_tag); + snd_hdac_ext_stream_release(link_dev, + HDAC_EXT_STREAM_TYPE_LINK); + + /* fallthrough */ + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_STOP: snd_hdac_ext_link_stream_clear(link_dev); break; @@ -228,62 +341,41 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, return 0; } -/* - * FIXME: This API is also abused since it's used for two purposes. - * when the substream argument is NULL this function is used for cleanups - * that aren't necessarily required, and called explicitly by handling - * ASoC core structures, which is not recommended. - * This part will be reworked in follow-up patches. - */ static int hda_link_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - const char *name; unsigned int stream_tag; + struct sof_intel_hda_stream *hda_stream; struct hdac_bus *bus; struct hdac_ext_link *link; struct hdac_stream *hstream; - struct hdac_ext_stream *stream; struct snd_soc_pcm_runtime *rtd; struct hdac_ext_stream *link_dev; - struct snd_pcm_substream pcm_substream; - - memset(&pcm_substream, 0, sizeof(pcm_substream)); - if (substream) { - hstream = substream->runtime->private_data; - bus = hstream->bus; - rtd = snd_pcm_substream_chip(substream); - link_dev = snd_soc_dai_get_dma_data(dai, substream); - snd_hdac_ext_stream_decouple(bus, link_dev, false); - name = rtd->codec_dai->component->name; - link = snd_hdac_ext_bus_get_link(bus, name); - if (!link) - return -EINVAL; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - stream_tag = hdac_stream(link_dev)->stream_tag; - snd_hdac_ext_link_clear_stream_id(link, stream_tag); - } + int ret; - link_dev->link_prepared = 0; - } else { - /* release all hda streams when dai link is unloaded */ - pcm_substream.stream = SNDRV_PCM_STREAM_PLAYBACK; - stream = snd_soc_dai_get_dma_data(dai, &pcm_substream); - if (stream) { - snd_soc_dai_set_dma_data(dai, &pcm_substream, NULL); - snd_hdac_ext_stream_release(stream, - HDAC_EXT_STREAM_TYPE_LINK); - } + hstream = substream->runtime->private_data; + bus = hstream->bus; + rtd = snd_pcm_substream_chip(substream); + link_dev = snd_soc_dai_get_dma_data(dai, substream); + hda_stream = hstream_to_sof_hda_stream(link_dev); - pcm_substream.stream = SNDRV_PCM_STREAM_CAPTURE; - stream = snd_soc_dai_get_dma_data(dai, &pcm_substream); - if (stream) { - snd_soc_dai_set_dma_data(dai, &pcm_substream, NULL); - snd_hdac_ext_stream_release(stream, - HDAC_EXT_STREAM_TYPE_LINK); - } - } + /* free the link DMA channel in the FW */ + ret = hda_link_config_ipc(hda_stream, dai->name, DMA_CHAN_INVALID, + substream->stream); + if (ret < 0) + return ret; + + link = snd_hdac_ext_bus_get_link(bus, rtd->codec_dai->component->name); + if (!link) + return -EINVAL; + + stream_tag = hdac_stream(link_dev)->stream_tag; + snd_hdac_ext_link_clear_stream_id(link, stream_tag); + snd_hdac_ext_stream_release(link_dev, HDAC_EXT_STREAM_TYPE_LINK); + link_dev->link_prepared = 0; + + /* free the host DMA channel reserved by hostless streams */ + hda_stream->host_reserved = 0; return 0; } @@ -293,7 +385,6 @@ static const struct snd_soc_dai_ops hda_link_dai_ops = { .hw_free = hda_link_hw_free, .trigger = hda_link_pcm_trigger, .prepare = hda_link_pcm_prepare, - .get_channel_map = hda_link_dma_get_channels, }; #endif diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 5b73115a0b78..91de4785b6a3 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -307,23 +307,12 @@ static int hda_suspend(struct snd_sof_dev *sdev, int state) return ret; } -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) - /* disable ppcap interrupt */ - snd_hdac_ext_bus_ppcap_int_enable(bus, false); - snd_hdac_ext_bus_ppcap_enable(bus, false); - - /* disable hda bus irq and i/o */ - snd_hdac_bus_stop_chip(bus); -#else /* disable ppcap interrupt */ hda_dsp_ctrl_ppcap_enable(sdev, false); hda_dsp_ctrl_ppcap_int_enable(sdev, false); - /* disable hda bus irq */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, - SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN, - 0); -#endif + /* disable hda bus irq and streams */ + hda_dsp_ctrl_stop_chip(sdev); /* disable LP retention mode */ snd_sof_pci_update_bits(sdev, PCI_PGCTL, @@ -370,10 +359,6 @@ static int hda_resume(struct snd_sof_dev *sdev) bus->io_ops->reg_writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV); hda_dsp_ctrl_misc_clock_gating(sdev, true); - - /* enable ppcap interrupt */ - snd_hdac_ext_bus_ppcap_enable(bus, true); - snd_hdac_ext_bus_ppcap_int_enable(bus, true); #else hda_dsp_ctrl_misc_clock_gating(sdev, false); @@ -400,11 +385,11 @@ static int hda_resume(struct snd_sof_dev *sdev) SOF_HDA_INT_CTRL_EN | SOF_HDA_INT_GLOBAL_EN); hda_dsp_ctrl_misc_clock_gating(sdev, true); +#endif /* enable ppcap interrupt */ hda_dsp_ctrl_ppcap_enable(sdev, true); hda_dsp_ctrl_ppcap_int_enable(sdev, true); -#endif #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) /* turn off the links that were off before suspend */ @@ -433,6 +418,19 @@ int hda_dsp_runtime_resume(struct snd_sof_dev *sdev) return hda_resume(sdev); } +int hda_dsp_runtime_idle(struct snd_sof_dev *sdev) +{ + struct hdac_bus *hbus = sof_to_bus(sdev); + + if (hbus->codec_powered) { + dev_dbg(sdev->dev, "some codecs still powered (%08X), not idle\n", + (unsigned int)hbus->codec_powered); + return -EBUSY; + } + + return 0; +} + int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev, int state) { /* stop hda controller and power dsp off */ @@ -454,18 +452,45 @@ int hda_dsp_suspend(struct snd_sof_dev *sdev, int state) return 0; } -void hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev) +int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev) { struct hdac_bus *bus = sof_to_bus(sdev); struct sof_intel_hda_stream *hda_stream; struct hdac_ext_stream *stream; struct hdac_stream *s; +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) + struct snd_soc_pcm_runtime *rtd; + struct hdac_ext_link *link; + const char *name; + int stream_tag; +#endif + /* set internal flag for BE */ list_for_each_entry(s, &bus->stream_list, list) { stream = stream_to_hdac_ext_stream(s); hda_stream = container_of(stream, struct sof_intel_hda_stream, hda_stream); hda_stream->hw_params_upon_resume = 1; +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) + /* + * clear and release stream. This should already be taken care + * for running streams when the SUSPEND trigger is called. + * But paused streams do not get suspended, so this needs to be + * done explicitly during suspend. + */ + if (stream->link_substream) { + rtd = snd_pcm_substream_chip(stream->link_substream); + name = rtd->codec_dai->component->name; + link = snd_hdac_ext_bus_get_link(bus, name); + if (!link) + return -EINVAL; + stream_tag = hdac_stream(stream)->stream_tag; + snd_hdac_ext_link_clear_stream_id(link, stream_tag); + snd_hdac_ext_stream_release(stream, + HDAC_EXT_STREAM_TYPE_LINK); + } +#endif } + return 0; } diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c index 51b285103394..50244b82600c 100644 --- a/sound/soc/sof/intel/hda-ipc.c +++ b/sound/soc/sof/intel/hda-ipc.c @@ -56,13 +56,11 @@ static void hda_dsp_ipc_dsp_done(struct snd_sof_dev *sdev) int hda_dsp_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) { - u32 cmd = msg->header; - /* send IPC message to DSP */ sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, msg->msg_size); snd_sof_dsp_write(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI, - cmd | HDA_DSP_REG_HIPCI_BUSY); + HDA_DSP_REG_HIPCI_BUSY); return 0; } @@ -133,30 +131,23 @@ static bool hda_dsp_ipc_is_sof(uint32_t msg) irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context) { struct snd_sof_dev *sdev = context; - irqreturn_t ret = IRQ_NONE; u32 hipci; u32 hipcie; u32 hipct; u32 hipcte; - u32 hipcctl; u32 msg; u32 msg_ext; + bool ipc_irq = false; /* read IPC status */ hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCIE); hipct = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT); - hipcctl = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCCTL); - - /* reenable IPC interrupt */ - snd_sof_dsp_update_bits(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIC, - HDA_DSP_ADSPIC_IPC, HDA_DSP_ADSPIC_IPC); + hipci = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI); + hipcte = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCTE); /* is this a reply message from the DSP */ - if (hipcie & HDA_DSP_REG_HIPCIE_DONE && - hipcctl & HDA_DSP_REG_HIPCCTL_DONE) { - hipci = snd_sof_dsp_read(sdev, HDA_DSP_BAR, - HDA_DSP_REG_HIPCI); + if (hipcie & HDA_DSP_REG_HIPCIE_DONE) { msg = hipci & HDA_DSP_REG_HIPCI_MSG_MASK; msg_ext = hipcie & HDA_DSP_REG_HIPCIE_MSG_MASK; @@ -198,15 +189,11 @@ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context) spin_unlock_irq(&sdev->ipc_lock); - ret = IRQ_HANDLED; + ipc_irq = true; } /* is this a new message from DSP */ - if (hipct & HDA_DSP_REG_HIPCT_BUSY && - hipcctl & HDA_DSP_REG_HIPCCTL_BUSY) { - - hipcte = snd_sof_dsp_read(sdev, HDA_DSP_BAR, - HDA_DSP_REG_HIPCTE); + if (hipct & HDA_DSP_REG_HIPCT_BUSY) { msg = hipct & HDA_DSP_REG_HIPCT_MSG_MASK; msg_ext = hipcte & HDA_DSP_REG_HIPCTE_MSG_MASK; @@ -230,10 +217,22 @@ irqreturn_t hda_dsp_ipc_irq_thread(int irq, void *context) hda_dsp_ipc_host_done(sdev); - ret = IRQ_HANDLED; + ipc_irq = true; } - return ret; + if (!ipc_irq) { + /* + * This interrupt is not shared so no need to return IRQ_NONE. + */ + dev_err_ratelimited(sdev->dev, + "error: nothing to do in IRQ thread\n"); + } + + /* re-enable 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_HANDLED; } /* is this IRQ for ADSP ? - we only care about IPC here */ diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index c92006f89499..ad8d41f22e92 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -155,6 +155,7 @@ struct hdac_ext_stream * hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction) { struct hdac_bus *bus = sof_to_bus(sdev); + struct sof_intel_hda_stream *hda_stream; struct hdac_ext_stream *stream = NULL; struct hdac_stream *s; @@ -163,8 +164,15 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction) /* get an unused stream */ list_for_each_entry(s, &bus->stream_list, list) { if (s->direction == direction && !s->opened) { - s->opened = true; stream = stream_to_hdac_ext_stream(s); + hda_stream = container_of(stream, + struct sof_intel_hda_stream, + hda_stream); + /* check if the host DMA channel is reserved */ + if (hda_stream->host_reserved) + continue; + + s->opened = true; break; } } @@ -209,6 +217,9 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, { struct hdac_stream *hstream = &stream->hstream; int sd_offset = SOF_STREAM_SD_OFFSET(hstream); + u32 dma_start = SOF_HDA_SD_CTL_DMA_START; + int ret; + u32 run; /* cmd must be for audio stream */ switch (cmd) { @@ -226,6 +237,16 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, SOF_HDA_SD_CTL_DMA_START | SOF_HDA_CL_DMA_SD_INT_MASK); + ret = snd_sof_dsp_read_poll_timeout(sdev, + HDA_DSP_HDA_BAR, + sd_offset, run, + ((run & dma_start) == dma_start), + HDA_DSP_REG_POLL_INTERVAL_US, + HDA_DSP_STREAM_RUN_TIMEOUT); + + if (ret) + return ret; + hstream->running = true; break; case SNDRV_PCM_TRIGGER_SUSPEND: @@ -236,6 +257,15 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, SOF_HDA_SD_CTL_DMA_START | SOF_HDA_CL_DMA_SD_INT_MASK, 0x0); + ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_HDA_BAR, + sd_offset, run, + !(run & dma_start), + HDA_DSP_REG_POLL_INTERVAL_US, + HDA_DSP_STREAM_RUN_TIMEOUT); + + if (ret) + return ret; + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS, SOF_HDA_CL_DMA_SD_INT_MASK); @@ -265,7 +295,9 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, struct hdac_stream *hstream = &stream->hstream; int sd_offset = SOF_STREAM_SD_OFFSET(hstream); int ret, timeout = HDA_DSP_STREAM_RESET_TIMEOUT; + u32 dma_start = SOF_HDA_SD_CTL_DMA_START; u32 val, mask; + u32 run; if (!stream) { dev_err(sdev->dev, "error: no stream available\n"); @@ -286,6 +318,16 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, SOF_HDA_CL_DMA_SD_INT_MASK | SOF_HDA_SD_CTL_DMA_START, 0); + + ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_HDA_BAR, + sd_offset, run, + !(run & dma_start), + HDA_DSP_REG_POLL_INTERVAL_US, + HDA_DSP_STREAM_RUN_TIMEOUT); + + if (ret) + return ret; + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS, SOF_HDA_CL_DMA_SD_INT_MASK, @@ -338,6 +380,16 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, SOF_HDA_CL_DMA_SD_INT_MASK | SOF_HDA_SD_CTL_DMA_START, 0); + + ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_HDA_BAR, + sd_offset, run, + !(run & dma_start), + HDA_DSP_REG_POLL_INTERVAL_US, + HDA_DSP_STREAM_RUN_TIMEOUT); + + if (ret) + return ret; + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset + SOF_HDA_ADSP_REG_CL_SD_STS, SOF_HDA_CL_DMA_SD_INT_MASK, @@ -430,60 +482,63 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, return ret; } +int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) +{ + struct hdac_stream *stream = substream->runtime->private_data; + struct hdac_ext_stream *link_dev = container_of(stream, + struct hdac_ext_stream, + hstream); + struct hdac_bus *bus = sof_to_bus(sdev); + u32 mask = 0x1 << stream->index; + + spin_lock_irq(&bus->reg_lock); + /* couple host and link DMA if link DMA channel is idle */ + if (!link_dev->link_locked) + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, + SOF_HDA_REG_PP_PPCTL, mask, 0); + spin_unlock_irq(&bus->reg_lock); + + return 0; +} + 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; + int ret = IRQ_WAKE_THREAD; u32 status; - if (!pm_runtime_active(bus->dev)) - return IRQ_NONE; - spin_lock(&bus->reg_lock); status = snd_hdac_chip_readl(bus, INTSTS); - stream_mask = GENMASK(sof_hda->stream_max - 1, 0) | AZX_INT_CTRL_EN; + dev_vdbg(bus->dev, "stream irq, INTSTS status: 0x%x\n", status); - /* Not stream interrupt or register inaccessible, ignore it.*/ - if (!(status & stream_mask) || status == 0xffffffff) { - spin_unlock(&bus->reg_lock); - return IRQ_NONE; - } - -#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); - } -#endif + /* Register inaccessible, ignore it.*/ + if (status == 0xffffffff) + ret = IRQ_NONE; spin_unlock(&bus->reg_lock); - return snd_hdac_chip_readl(bus, INTSTS) ? IRQ_WAKE_THREAD : IRQ_HANDLED; + return ret; } -irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context) +static bool hda_dsp_stream_check(struct hdac_bus *bus, u32 status) { - struct hdac_bus *bus = context; struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); - u32 status = snd_hdac_chip_readl(bus, INTSTS); struct hdac_stream *s; + bool active = false; u32 sd_status; - /* check streams */ list_for_each_entry(s, &bus->stream_list, list) { - if (status & (1 << s->index) && s->opened) { + if (status & BIT(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); + snd_hdac_stream_writeb(s, SD_STS, sd_status); + active = true; if (!s->substream || !s->running || (sd_status & SOF_HDA_CL_DMA_SD_INT_COMPLETE) == 0) @@ -492,8 +547,48 @@ 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_sof_pcm_period_elapsed(s->substream); + } + } + + return active; +} + +irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context) +{ + struct hdac_bus *bus = context; +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) + u32 rirb_status; +#endif + bool active; + u32 status; + int i; + + /* + * Loop 10 times to handle missed interrupts caused by + * unsolicited responses from the codec + */ + for (i = 0, active = true; i < 10 && active; i++) { + spin_lock_irq(&bus->reg_lock); + + status = snd_hdac_chip_readl(bus, INTSTS); + /* check streams */ + active = hda_dsp_stream_check(bus, status); + + /* check and clear RIRB interrupt */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) + if (status & AZX_INT_CTRL_EN) { + rirb_status = snd_hdac_chip_readb(bus, RIRBSTS); + if (rirb_status & RIRB_INT_MASK) { + active = true; + if (rirb_status & RIRB_INT_RESPONSE) + snd_hdac_bus_update_rirb(bus); + snd_hdac_chip_writeb(bus, RIRBSTS, + RIRB_INT_MASK); + } } +#endif + spin_unlock_irq(&bus->reg_lock); } return IRQ_HANDLED; @@ -564,6 +659,8 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) if (!hda_stream) return -ENOMEM; + hda_stream->sdev = sdev; + stream = &hda_stream->hda_stream; stream->pphc_addr = sdev->bar[HDA_DSP_PP_BAR] + @@ -617,6 +714,8 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) if (!hda_stream) return -ENOMEM; + hda_stream->sdev = sdev; + stream = &hda_stream->hda_stream; /* we always have DSP support */ diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 6811a477d0fb..7f665392618f 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -15,8 +15,10 @@ * Hardware interface for generic Intel audio DSP HDA IP */ -#include <linux/module.h> #include <sound/hdaudio_ext.h> +#include <sound/hda_register.h> + +#include <linux/module.h> #include <sound/sof.h> #include <sound/sof/xtensa.h> #include "../ops.h" @@ -186,12 +188,38 @@ void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags) } } +void hda_ipc_irq_dump(struct snd_sof_dev *sdev) +{ + struct hdac_bus *bus = sof_to_bus(sdev); + u32 adspis; + u32 intsts; + u32 intctl; + u32 ppsts; + u8 rirbsts; + + /* read key IRQ stats and config registers */ + adspis = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_ADSPIS); + intsts = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS); + intctl = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL); + ppsts = snd_sof_dsp_read(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPSTS); + rirbsts = snd_hdac_chip_readb(bus, RIRBSTS); + + dev_err(sdev->dev, + "error: hda irq intsts 0x%8.8x intlctl 0x%8.8x rirb %2.2x\n", + intsts, intctl, rirbsts); + dev_err(sdev->dev, + "error: dsp irq ppsts 0x%8.8x adspis 0x%8.8x\n", + ppsts, adspis); +} + void hda_ipc_dump(struct snd_sof_dev *sdev) { u32 hipcie; u32 hipct; u32 hipcctl; + hda_ipc_irq_dump(sdev); + /* read IPC status */ hipcie = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCIE); hipct = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCT); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 92d45c43b4b1..d9c17146200b 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -61,6 +61,7 @@ #define SOF_HDA_PP_CAP_ID 0x3 #define SOF_HDA_REG_PP_PPCH 0x10 #define SOF_HDA_REG_PP_PPCTL 0x04 +#define SOF_HDA_REG_PP_PPSTS 0x08 #define SOF_HDA_PPCTL_PIE BIT(31) #define SOF_HDA_PPCTL_GPROCEN BIT(30) @@ -158,6 +159,12 @@ #define HDA_DSP_MBOX_UPLINK_OFFSET 0x81000 #define HDA_DSP_STREAM_RESET_TIMEOUT 300 +/* + * Timeout in us, for setting the stream RUN bit, during + * start/stop the stream. The timeout expires if new RUN bit + * value cannot be read back within the specified time. + */ +#define HDA_DSP_STREAM_RUN_TIMEOUT 300 #define HDA_DSP_CL_TRIGGER_TIMEOUT 300 #define HDA_DSP_SPIB_ENABLE 1 @@ -348,6 +355,7 @@ /* SSP Count of the Platform */ #define APL_SSP_COUNT 6 #define CNL_SSP_COUNT 3 +#define ICL_SSP_COUNT 6 /* SSP Registers */ #define SSP_SSC1_OFFSET 0x4 @@ -407,11 +415,16 @@ static inline struct hda_bus *sof_to_hbus(struct snd_sof_dev *s) } struct sof_intel_hda_stream { + struct snd_sof_dev *sdev; struct hdac_ext_stream hda_stream; struct sof_intel_stream stream; int hw_params_upon_resume; /* set up hw_params upon resume */ + int host_reserved; /* reserve host DMA channel */ }; +#define hstream_to_sof_hda_stream(hstream) \ + container_of(hstream, struct sof_intel_hda_stream, hda_stream) + #define bus_to_sof_hda(bus) \ container_of(bus, struct sof_intel_hda_dev, hbus.core) @@ -444,10 +457,12 @@ int hda_dsp_suspend(struct snd_sof_dev *sdev, int state); int hda_dsp_resume(struct snd_sof_dev *sdev); int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev, int state); int hda_dsp_runtime_resume(struct snd_sof_dev *sdev); -void hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev); +int hda_dsp_runtime_idle(struct snd_sof_dev *sdev); +int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev); void hda_dsp_dump_skl(struct snd_sof_dev *sdev, u32 flags); void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags); void hda_ipc_dump(struct snd_sof_dev *sdev); +void hda_ipc_irq_dump(struct snd_sof_dev *sdev); /* * DSP PCM Operations. @@ -460,6 +475,8 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct sof_ipc_stream_params *ipc_params); +int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream); int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, int cmd); snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev, @@ -528,7 +545,7 @@ int hda_dsp_ctrl_link_reset(struct snd_sof_dev *sdev, bool reset); void hda_dsp_ctrl_misc_clock_gating(struct snd_sof_dev *sdev, bool enable); int hda_dsp_ctrl_clock_power_gating(struct snd_sof_dev *sdev, bool enable); int hda_dsp_ctrl_init_chip(struct snd_sof_dev *sdev, bool full_reset); - +void hda_dsp_ctrl_stop_chip(struct snd_sof_dev *sdev); /* * HDA bus operations. */ @@ -579,5 +596,6 @@ extern const struct snd_sof_dsp_ops sof_skl_ops; extern const struct sof_intel_dsp_desc apl_chip_info; extern const struct sof_intel_dsp_desc cnl_chip_info; extern const struct sof_intel_dsp_desc skl_chip_info; +extern const struct sof_intel_dsp_desc icl_chip_info; #endif |