diff options
author | Takashi Iwai <tiwai@suse.de> | 2023-06-26 15:38:02 +0200 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2023-06-26 15:38:02 +0200 |
commit | d6048fdc870240e5020343f8af0c825829c232bd (patch) | |
tree | d83ca76eaac5f8bf1c4c16f003aad64baa8fae62 /sound/soc/sof | |
parent | a15b51375684c2bfa6017bb185139477e7a3b96c (diff) | |
parent | 2d0cad0473bd1ffbc5842be0b9f2546265acb011 (diff) |
Merge tag 'asoc-v6.5' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus
ASoC: Updates for v6.5
A fairly quiet release from a core and framework point of view, but a
very big one from the point of view of new drivers:
- More refectoring from Morimoto-san, this time mainly around DAI
links and how we control the ordering of trigger() callbacks.
- Convert a lot of drivers to use maple tree based caches.
- Lots of work on the x86 driver stack.
- Compressed audio support for Qualcomm.
- Support for AMD SoundWire, Analog Devices SSM3515, Google Chameleon,
Ingenic X1000, Intel systems with various CODECs, Longsoon platforms,
Maxim MAX98388, Mediatek MT8188, Nuvoton NAU8825C, NXP platforms with
NAU8822, Qualcomm WSA884x, StarFive JH7110, Texas Instruments TAS2781.
Diffstat (limited to 'sound/soc/sof')
45 files changed, 1334 insertions, 484 deletions
diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index a2725188f4ce..80361139a49a 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -236,6 +236,17 @@ config SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR Say Y if you want to enable the IPC message injector. If unsure, select "N". +config SND_SOC_SOF_DEBUG_IPC_KERNEL_INJECTOR + tristate "SOF enable IPC kernel injector" + depends on SND_SOC_SOF + select SND_SOC_SOF_CLIENT + help + This option enables the IPC kernel injector which can be used to send + crafted IPC messages to the kernel to test its robustness against + DSP messages. + Say Y if you want to enable the IPC kernel injector. + If unsure, select "N". + config SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT bool "SOF retain DSP context on any FW exceptions" help diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index 308d87639916..744d40bd8c8b 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -26,6 +26,7 @@ snd-sof-of-objs := sof-of-dev.o snd-sof-ipc-flood-test-objs := sof-client-ipc-flood-test.o snd-sof-ipc-msg-injector-objs := sof-client-ipc-msg-injector.o +snd-sof-ipc-kernel-injector-objs := sof-client-ipc-kernel-injector.o snd-sof-probes-objs := sof-client-probes.o ifneq ($(CONFIG_SND_SOC_SOF_IPC3),) snd-sof-probes-objs += sof-client-probes-ipc3.o @@ -49,6 +50,7 @@ obj-$(CONFIG_SND_SOC_SOF_PCI_DEV) += snd-sof-pci.o obj-$(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) += snd-sof-ipc-flood-test.o obj-$(CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR) += snd-sof-ipc-msg-injector.o +obj-$(CONFIG_SND_SOC_SOF_DEBUG_IPC_KERNEL_INJECTOR) += snd-sof-ipc-kernel-injector.o obj-$(CONFIG_SND_SOC_SOF_DEBUG_PROBES) += snd-sof-probes.o obj-$(CONFIG_SND_SOC_SOF_INTEL_TOPLEVEL) += intel/ diff --git a/sound/soc/sof/amd/acp-ipc.c b/sound/soc/sof/amd/acp-ipc.c index 749e856dc601..8a0fc635a997 100644 --- a/sound/soc/sof/amd/acp-ipc.c +++ b/sound/soc/sof/amd/acp-ipc.c @@ -130,6 +130,13 @@ static void acp_dsp_ipc_get_reply(struct snd_sof_dev *sdev) memcpy(msg->reply_data, &reply, sizeof(reply)); ret = reply.error; } else { + /* + * To support an IPC tx_message with a + * reply_size set to zero. + */ + if (!msg->reply_size) + goto out; + /* reply correct size ? */ if (reply.hdr.size != msg->reply_size && !(reply.hdr.cmd & SOF_IPC_GLB_PROBE)) { diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index 1c535cc6c3a9..dc624f727aa3 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -55,6 +55,9 @@ #define ACP_DSP_TO_HOST_IRQ 0x04 +#define ACP_RN_PCI_ID 0x01 +#define ACP_RMB_PCI_ID 0x6F + #define HOST_BRIDGE_CZN 0x1630 #define HOST_BRIDGE_RMB 0x14B5 #define ACP_SHA_STAT 0x8000 diff --git a/sound/soc/sof/amd/pci-rmb.c b/sound/soc/sof/amd/pci-rmb.c index eaf70ea6e556..58b3092425f1 100644 --- a/sound/soc/sof/amd/pci-rmb.c +++ b/sound/soc/sof/amd/pci-rmb.c @@ -65,6 +65,9 @@ static int acp_pci_rmb_probe(struct pci_dev *pci, const struct pci_device_id *pc { unsigned int flag; + if (pci->revision != ACP_RMB_PCI_ID) + return -ENODEV; + flag = snd_amd_acp_find_config(pci); if (flag != FLAG_AMD_SOF && flag != FLAG_AMD_SOF_ONLY_DMIC) return -ENODEV; diff --git a/sound/soc/sof/amd/pci-rn.c b/sound/soc/sof/amd/pci-rn.c index 4809cb644152..7409e21ce5aa 100644 --- a/sound/soc/sof/amd/pci-rn.c +++ b/sound/soc/sof/amd/pci-rn.c @@ -65,6 +65,9 @@ static int acp_pci_rn_probe(struct pci_dev *pci, const struct pci_device_id *pci { unsigned int flag; + if (pci->revision != ACP_RN_PCI_ID) + return -ENODEV; + flag = snd_amd_acp_find_config(pci); if (flag != FLAG_AMD_SOF && flag != FLAG_AMD_SOF_ONLY_DMIC) return -ENODEV; diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 9a9d82220fd0..30db685cc5f4 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -504,8 +504,10 @@ int snd_sof_device_shutdown(struct device *dev) if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) cancel_work_sync(&sdev->probe_work); - if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) + if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) { + sof_fw_trace_free(sdev); return snd_sof_shutdown(sdev); + } return 0; } diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index f4eeacf1f281..69c1a370d3b6 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -284,8 +284,6 @@ if SND_SOC_SOF_HDA_COMMON config SND_SOC_SOF_HDA_LINK bool "SOF support for HDA Links(HDA/HDMI)" - depends on SND_SOC_SOF_NOCODEC_SUPPORT=n - select SND_SOC_SOF_PROBE_WORK_QUEUE help This adds support for HDA links(HDA/HDMI) with Sound Open Firmware for Intel(R) platforms. @@ -295,6 +293,7 @@ config SND_SOC_SOF_HDA_LINK config SND_SOC_SOF_HDA_AUDIO_CODEC bool "SOF support for HDAudio codecs" depends on SND_SOC_SOF_HDA_LINK + select SND_SOC_SOF_PROBE_WORK_QUEUE help This adds support for HDAudio codecs with Sound Open Firmware for Intel(R) platforms. diff --git a/sound/soc/sof/intel/hda-bus.c b/sound/soc/sof/intel/hda-bus.c index acb4b85868d0..fc63085d2d74 100644 --- a/sound/soc/sof/intel/hda-bus.c +++ b/sound/soc/sof/intel/hda-bus.c @@ -70,9 +70,14 @@ void sof_hda_bus_init(struct snd_sof_dev *sdev, struct device *dev) { struct hdac_bus *bus = sof_to_bus(sdev); +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK) #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) snd_hdac_ext_bus_init(bus, dev, &bus_core_ops, sof_hda_ext_ops); -#else /* CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC */ +#else + snd_hdac_ext_bus_init(bus, dev, NULL, NULL); +#endif +#else + memset(bus, 0, sizeof(*bus)); bus->dev = dev; @@ -87,12 +92,12 @@ void sof_hda_bus_init(struct snd_sof_dev *sdev, struct device *dev) bus->idx = 0; spin_lock_init(&bus->reg_lock); -#endif /* CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC */ +#endif /* CONFIG_SND_SOC_SOF_HDA_LINK */ } void sof_hda_bus_exit(struct snd_sof_dev *sdev) { -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK) struct hdac_bus *bus = sof_to_bus(sdev); snd_hdac_ext_bus_exit(bus); diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index 4b39cecacd68..f3513796c189 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -16,7 +16,7 @@ #include "hda.h" /* These ops are only applicable for the HDA DAI's in their current form */ -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK) /* * This function checks if the host dma channel corresponding * to the link DMA stream_tag argument is assigned to one @@ -120,6 +120,26 @@ static struct hdac_ext_stream *hda_get_hext_stream(struct snd_sof_dev *sdev, return snd_soc_dai_get_dma_data(cpu_dai, substream); } +static struct hdac_ext_stream *hda_ipc4_get_hext_stream(struct snd_sof_dev *sdev, + struct snd_soc_dai *cpu_dai, + struct snd_pcm_substream *substream) +{ + struct snd_sof_widget *pipe_widget; + struct sof_ipc4_pipeline *pipeline; + struct snd_sof_widget *swidget; + struct snd_soc_dapm_widget *w; + + w = snd_soc_dai_get_widget(cpu_dai, substream->stream); + swidget = w->dobj.private; + pipe_widget = swidget->spipe->pipe_widget; + pipeline = pipe_widget->private; + + /* mark pipeline so that it can be skipped during FE trigger */ + pipeline->skip_during_fe_trigger = true; + + return snd_soc_dai_get_dma_data(cpu_dai, substream); +} + static struct hdac_ext_stream *hda_assign_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, struct snd_pcm_substream *substream) @@ -155,20 +175,67 @@ static void hda_reset_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stre snd_hdac_ext_stream_reset(hext_stream); } +static void hda_codec_dai_set_stream(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct hdac_stream *hstream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + + /* set the hdac_stream in the codec dai */ + snd_soc_dai_set_stream(codec_dai, hstream, substream->stream); +} + +static unsigned int hda_calc_stream_format(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + unsigned int link_bps; + unsigned int format_val; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + link_bps = codec_dai->driver->playback.sig_bits; + else + link_bps = codec_dai->driver->capture.sig_bits; + + format_val = snd_hdac_calc_stream_format(params_rate(params), params_channels(params), + params_format(params), link_bps, 0); + + dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val, + params_rate(params), params_channels(params), params_format(params)); + + return format_val; +} + +static struct hdac_ext_link *hda_get_hlink(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct hdac_bus *bus = sof_to_bus(sdev); + + return snd_hdac_ext_bus_get_hlink_by_name(bus, codec_dai->component->name); +} + static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, struct snd_pcm_substream *substream, int cmd) { + struct sof_ipc4_fw_data *ipc4_data = sdev->private; struct snd_sof_widget *pipe_widget; struct sof_ipc4_pipeline *pipeline; struct snd_sof_widget *swidget; struct snd_soc_dapm_widget *w; - int ret; + int ret = 0; w = snd_soc_dai_get_widget(cpu_dai, substream->stream); swidget = w->dobj.private; pipe_widget = swidget->spipe->pipe_widget; pipeline = pipe_widget->private; + mutex_lock(&ipc4_data->pipeline_state_mutex); + switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: @@ -179,16 +246,17 @@ static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cp ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, SOF_IPC4_PIPE_PAUSED); if (ret < 0) - return ret; + goto out; pipeline->state = SOF_IPC4_PIPE_PAUSED; break; default: dev_err(sdev->dev, "unknown trigger command %d\n", cmd); - return -EINVAL; + ret = -EINVAL; } - - return 0; +out: + mutex_unlock(&ipc4_data->pipeline_state_mutex); + return ret; } static int hda_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, @@ -217,64 +285,76 @@ static int hda_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, struct snd_pcm_substream *substream, int cmd) { + struct sof_ipc4_fw_data *ipc4_data = sdev->private; struct snd_sof_widget *pipe_widget; struct sof_ipc4_pipeline *pipeline; struct snd_sof_widget *swidget; struct snd_soc_dapm_widget *w; - int ret; + int ret = 0; w = snd_soc_dai_get_widget(cpu_dai, substream->stream); swidget = w->dobj.private; pipe_widget = swidget->spipe->pipe_widget; pipeline = pipe_widget->private; + mutex_lock(&ipc4_data->pipeline_state_mutex); + switch (cmd) { case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if (pipeline->state != SOF_IPC4_PIPE_PAUSED) { ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, SOF_IPC4_PIPE_PAUSED); if (ret < 0) - return ret; + goto out; pipeline->state = SOF_IPC4_PIPE_PAUSED; } ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, SOF_IPC4_PIPE_RUNNING); if (ret < 0) - return ret; + goto out; pipeline->state = SOF_IPC4_PIPE_RUNNING; + swidget->spipe->started_count++; break; - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_STOP: - { + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, - SOF_IPC4_PIPE_RESET); + SOF_IPC4_PIPE_RUNNING); if (ret < 0) - return ret; - - pipeline->state = SOF_IPC4_PIPE_RESET; + goto out; + pipeline->state = SOF_IPC4_PIPE_RUNNING; + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + /* + * STOP/SUSPEND trigger is invoked only once when all users of this pipeline have + * been stopped. So, clear the started_count so that the pipeline can be reset + */ + swidget->spipe->started_count = 0; break; - } case SNDRV_PCM_TRIGGER_PAUSE_PUSH: break; default: dev_err(sdev->dev, "unknown trigger command %d\n", cmd); - return -EINVAL; + ret = -EINVAL; + break; } - - return 0; +out: + mutex_unlock(&ipc4_data->pipeline_state_mutex); + return ret; } static const struct hda_dai_widget_dma_ops hda_ipc4_dma_ops = { - .get_hext_stream = hda_get_hext_stream, + .get_hext_stream = hda_ipc4_get_hext_stream, .assign_hext_stream = hda_assign_hext_stream, .release_hext_stream = hda_release_hext_stream, .setup_hext_stream = hda_setup_hext_stream, .reset_hext_stream = hda_reset_hext_stream, .pre_trigger = hda_ipc4_pre_trigger, .trigger = hda_trigger, - .post_trigger = hda_ipc4_post_trigger + .post_trigger = hda_ipc4_post_trigger, + .codec_dai_set_stream = hda_codec_dai_set_stream, + .calc_stream_format = hda_calc_stream_format, + .get_hlink = hda_get_hlink, }; static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = { @@ -284,6 +364,9 @@ static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = { .setup_hext_stream = hda_setup_hext_stream, .reset_hext_stream = hda_reset_hext_stream, .trigger = hda_trigger, + .codec_dai_set_stream = hda_codec_dai_set_stream, + .calc_stream_format = hda_calc_stream_format, + .get_hlink = hda_get_hlink, }; static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, @@ -317,6 +400,9 @@ static const struct hda_dai_widget_dma_ops hda_ipc3_dma_ops = { .reset_hext_stream = hda_reset_hext_stream, .trigger = hda_trigger, .post_trigger = hda_ipc3_post_trigger, + .codec_dai_set_stream = hda_codec_dai_set_stream, + .calc_stream_format = hda_calc_stream_format, + .get_hlink = hda_get_hlink, }; static struct hdac_ext_stream * @@ -343,6 +429,9 @@ static void hda_dspless_setup_hext_stream(struct snd_sof_dev *sdev, static const struct hda_dai_widget_dma_ops hda_dspless_dma_ops = { .get_hext_stream = hda_dspless_get_hext_stream, .setup_hext_stream = hda_dspless_setup_hext_stream, + .codec_dai_set_stream = hda_codec_dai_set_stream, + .calc_stream_format = hda_calc_stream_format, + .get_hlink = hda_get_hlink, }; #endif @@ -350,7 +439,7 @@ static const struct hda_dai_widget_dma_ops hda_dspless_dma_ops = { const struct hda_dai_widget_dma_ops * hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK) struct snd_sof_dai *sdai; if (sdev->dspless_mode_selected) diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 44a5d94c5050..3297dea493aa 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -27,20 +27,26 @@ static bool hda_use_tplg_nhlt; module_param_named(sof_use_tplg_nhlt, hda_use_tplg_nhlt, bool, 0444); MODULE_PARM_DESC(sof_use_tplg_nhlt, "SOF topology nhlt override"); +static struct snd_sof_dev *widget_to_sdev(struct snd_soc_dapm_widget *w) +{ + struct snd_sof_widget *swidget = w->dobj.private; + struct snd_soc_component *component = swidget->scomp; + + return snd_soc_component_get_drvdata(component); +} + int hda_dai_config(struct snd_soc_dapm_widget *w, unsigned int flags, struct snd_sof_dai_config_data *data) { struct snd_sof_widget *swidget = w->dobj.private; const struct sof_ipc_tplg_ops *tplg_ops; - struct snd_soc_component *component; struct snd_sof_dev *sdev; int ret; if (!swidget) return 0; - component = swidget->scomp; - sdev = snd_soc_component_get_drvdata(component); + sdev = widget_to_sdev(w); tplg_ops = sof_ipc_get_ops(sdev, tplg); if (tplg_ops && tplg_ops->dai_config) { @@ -55,16 +61,26 @@ int hda_dai_config(struct snd_soc_dapm_widget *w, unsigned int flags, return 0; } -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK) + +static struct snd_sof_dev *dai_to_sdev(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); + + return widget_to_sdev(w); +} static const struct hda_dai_widget_dma_ops * hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component); struct snd_sof_widget *swidget = w->dobj.private; + struct snd_sof_dev *sdev; struct snd_sof_dai *sdai; + sdev = widget_to_sdev(w); + /* * The swidget parameter of hda_select_dai_widget_ops() is ignored in * case of DSPless mode @@ -93,18 +109,22 @@ hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai static int hda_link_dma_cleanup(struct snd_pcm_substream *substream, struct hdac_ext_stream *hext_stream, - struct snd_soc_dai *cpu_dai, - struct snd_soc_dai *codec_dai) + struct snd_soc_dai *cpu_dai) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component); const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai); - struct hdac_stream *hstream = &hext_stream->hstream; - struct hdac_bus *bus = hstream->bus; struct sof_intel_hda_stream *hda_stream; struct hdac_ext_link *hlink; + struct snd_sof_dev *sdev; int stream_tag; - hlink = snd_hdac_ext_bus_get_hlink_by_name(bus, codec_dai->component->name); + if (!ops) { + dev_err(cpu_dai->dev, "DAI widget ops not set\n"); + return -EINVAL; + } + + sdev = dai_to_sdev(substream, cpu_dai); + + hlink = ops->get_hlink(sdev, substream); if (!hlink) return -EINVAL; @@ -129,21 +149,20 @@ static int hda_link_dma_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) { const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai); - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct hdac_ext_stream *hext_stream; struct hdac_stream *hstream; struct hdac_ext_link *hlink; struct snd_sof_dev *sdev; - struct hdac_bus *bus; - unsigned int format_val; - unsigned int link_bps; int stream_tag; - sdev = snd_soc_component_get_drvdata(cpu_dai->component); - bus = sof_to_bus(sdev); + if (!ops) { + dev_err(cpu_dai->dev, "DAI widget ops not set\n"); + return -EINVAL; + } - hlink = snd_hdac_ext_bus_get_hlink_by_name(bus, codec_dai->component->name); + sdev = dai_to_sdev(substream, cpu_dai); + + hlink = ops->get_hlink(sdev, substream); if (!hlink) return -EINVAL; @@ -164,48 +183,32 @@ static int hda_link_dma_hw_params(struct snd_pcm_substream *substream, snd_hdac_ext_bus_link_set_stream_id(hlink, stream_tag); /* set the hdac_stream in the codec dai */ - snd_soc_dai_set_stream(codec_dai, hdac_stream(hext_stream), substream->stream); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - link_bps = codec_dai->driver->playback.sig_bits; - else - link_bps = codec_dai->driver->capture.sig_bits; + if (ops->codec_dai_set_stream) + ops->codec_dai_set_stream(sdev, substream, hstream); if (ops->reset_hext_stream) ops->reset_hext_stream(sdev, hext_stream); - format_val = snd_hdac_calc_stream_format(params_rate(params), params_channels(params), - params_format(params), link_bps, 0); + if (ops->calc_stream_format && ops->setup_hext_stream) { + unsigned int format_val = ops->calc_stream_format(sdev, substream, params); - dev_dbg(bus->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val, - params_rate(params), params_channels(params), params_format(params)); - - if (ops->setup_hext_stream) ops->setup_hext_stream(sdev, hext_stream, format_val); + } hext_stream->link_prepared = 1; return 0; } -static int hda_link_dma_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) -{ - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - int stream = substream->stream; - - return hda_link_dma_hw_params(substream, &rtd->dpcm[stream].hw_params, cpu_dai); -} - -static int hda_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) +static int __maybe_unused hda_dai_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component); const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai); - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); struct hdac_ext_stream *hext_stream; + struct snd_sof_dev *sdev = dai_to_sdev(substream, cpu_dai); if (!ops) { - dev_err(sdev->dev, "DAI widget ops not set\n"); + dev_err(cpu_dai->dev, "DAI widget ops not set\n"); return -EINVAL; } @@ -213,19 +216,19 @@ static int hda_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_d if (!hext_stream) return 0; - return hda_link_dma_cleanup(substream, hext_stream, cpu_dai, codec_dai); + return hda_link_dma_cleanup(substream, hext_stream, cpu_dai); } -static int hda_dai_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) +static int __maybe_unused hda_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) { struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream); - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai); struct hdac_ext_stream *hext_stream; struct snd_sof_dai_config_data data = { 0 }; unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS; + struct snd_sof_dev *sdev = widget_to_sdev(w); int ret; if (!ops) { @@ -249,57 +252,32 @@ static int hda_dai_hw_params(struct snd_pcm_substream *substream, return hda_dai_config(w, flags, &data); } -static int hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) -{ - struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream); - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); - const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai); - struct hdac_ext_stream *hext_stream; - struct snd_sof_dai_config_data data = { 0 }; - unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS; - int ret; - - hext_stream = ops->get_hext_stream(sdev, dai, substream); - if (hext_stream && hext_stream->link_prepared) - return 0; - - dev_dbg(sdev->dev, "prepare stream dir %d\n", substream->stream); - - ret = hda_link_dma_prepare(substream, dai); - if (ret < 0) - return ret; - - hext_stream = ops->get_hext_stream(sdev, dai, substream); - - flags |= SOF_DAI_CONFIG_FLAGS_2_STEP_STOP << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT; - data.dai_data = hdac_stream(hext_stream)->stream_tag - 1; - - return hda_dai_config(w, flags, &data); -} - /* * In contrast to IPC3, the dai trigger in IPC4 mixes pipeline state changes * (over IPC channel) and DMA state change (direct host register changes). */ -static int hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) +static int __maybe_unused hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai); struct hdac_ext_stream *hext_stream; - struct snd_soc_pcm_runtime *rtd; - struct snd_soc_dai *codec_dai; + struct snd_sof_dev *sdev; int ret; + if (!ops) { + dev_err(dai->dev, "DAI widget ops not set\n"); + return -EINVAL; + } + dev_dbg(dai->dev, "cmd=%d dai %s direction %d\n", cmd, dai->name, substream->stream); + sdev = dai_to_sdev(substream, dai); + hext_stream = ops->get_hext_stream(sdev, dai, substream); if (!hext_stream) return -EINVAL; - rtd = asoc_substream_to_rtd(substream); - codec_dai = asoc_rtd_to_codec(rtd, 0); - if (ops->pre_trigger) { ret = ops->pre_trigger(sdev, dai, substream, cmd); if (ret < 0) @@ -320,7 +298,7 @@ static int hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct switch (cmd) { case SNDRV_PCM_TRIGGER_SUSPEND: - ret = hda_link_dma_cleanup(substream, hext_stream, dai, codec_dai); + ret = hda_link_dma_cleanup(substream, hext_stream, dai); if (ret < 0) { dev_err(sdev->dev, "%s: failed to clean up link DMA\n", __func__); return ret; @@ -333,6 +311,16 @@ static int hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct return 0; } +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) + +static int hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + int stream = substream->stream; + + return hda_dai_hw_params(substream, &rtd->dpcm[stream].hw_params, dai); +} + static const struct snd_soc_dai_ops hda_dai_ops = { .hw_params = hda_dai_hw_params, .hw_free = hda_dai_hw_free, @@ -340,6 +328,8 @@ static const struct snd_soc_dai_ops hda_dai_ops = { .prepare = hda_dai_prepare, }; +#endif + static int hda_dai_suspend(struct hdac_bus *bus) { struct snd_soc_pcm_runtime *rtd; @@ -362,23 +352,21 @@ static int hda_dai_suspend(struct hdac_bus *bus) const struct hda_dai_widget_dma_ops *ops; struct snd_sof_widget *swidget; struct snd_soc_dapm_widget *w; - struct snd_soc_dai *codec_dai; struct snd_soc_dai *cpu_dai; struct snd_sof_dev *sdev; struct snd_sof_dai *sdai; rtd = asoc_substream_to_rtd(hext_stream->link_substream); cpu_dai = asoc_rtd_to_cpu(rtd, 0); - codec_dai = asoc_rtd_to_codec(rtd, 0); w = snd_soc_dai_get_widget(cpu_dai, hdac_stream(hext_stream)->direction); swidget = w->dobj.private; - sdev = snd_soc_component_get_drvdata(swidget->scomp); + sdev = widget_to_sdev(w); sdai = swidget->private; ops = sdai->platform_private; ret = hda_link_dma_cleanup(hext_stream->link_substream, hext_stream, - cpu_dai, codec_dai); + cpu_dai); if (ret < 0) return ret; @@ -588,7 +576,7 @@ int hda_dsp_dais_suspend(struct snd_sof_dev *sdev) * Since the component suspend is called last, we can trap this corner case * and force the DAIs to release their resources. */ -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK) int ret; ret = hda_dai_suspend(sof_to_bus(sdev)); diff --git a/sound/soc/sof/intel/hda-loader-skl.c b/sound/soc/sof/intel/hda-loader-skl.c index 69fdef8f89ae..1e77ca936f80 100644 --- a/sound/soc/sof/intel/hda-loader-skl.c +++ b/sound/soc/sof/intel/hda-loader-skl.c @@ -15,7 +15,6 @@ #include <linux/mm.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/pm_runtime.h> #include <linux/slab.h> #include <sound/hdaudio_ext.h> #include <sound/sof.h> diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index 981e7b699bdb..f23c72cdff48 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -33,7 +33,7 @@ static bool hda_always_enable_dmi_l1; module_param_named(always_enable_dmi_l1, hda_always_enable_dmi_l1, bool, 0444); MODULE_PARM_DESC(always_enable_dmi_l1, "SOF HDA always enable DMI l1"); -static bool hda_disable_rewinds = IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_DISABLE_REWINDS); +static bool hda_disable_rewinds; module_param_named(disable_rewinds, hda_disable_rewinds, bool, 0444); MODULE_PARM_DESC(disable_rewinds, "SOF HDA disable rewinds"); diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 8de422604ad5..b13acb959653 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -15,7 +15,6 @@ * Hardware interface for generic Intel audio DSP HDA IP */ -#include <linux/pm_runtime.h> #include <sound/hdaudio_ext.h> #include <sound/hda_register.h> #include <sound/sof.h> diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 3153e21f100a..ec31a5762ddf 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -222,6 +222,31 @@ int hda_sdw_check_lcount_common(struct snd_sof_dev *sdev) return 0; } +int hda_sdw_check_lcount_ext(struct snd_sof_dev *sdev) +{ + struct sof_intel_hda_dev *hdev; + struct sdw_intel_ctx *ctx; + struct hdac_bus *bus; + u32 slcount; + + bus = sof_to_bus(sdev); + + hdev = sdev->pdata->hw_pdata; + ctx = hdev->sdw; + + slcount = hdac_bus_eml_get_count(bus, true, AZX_REG_ML_LEPTR_ID_SDW); + + /* Check HW supported vs property value */ + if (slcount < ctx->count) { + dev_err(sdev->dev, + "%s: BIOS master count %d is larger than hardware capabilities %d\n", + __func__, ctx->count, slcount); + return -EINVAL; + } + + return 0; +} + static int hda_sdw_check_lcount(struct snd_sof_dev *sdev) { const struct sof_intel_dsp_desc *chip; @@ -1343,12 +1368,22 @@ static void hda_generic_machine_select(struct snd_sof_dev *sdev, hda_mach->mach_params.dmic_num = dmic_num; pdata->tplg_filename = tplg_filename; - if (codec_num == 2) { + if (codec_num == 2 || + (codec_num == 1 && !HDA_IDISP_CODEC(bus->codec_mask))) { /* * Prevent SoundWire links from starting when an external * HDaudio codec is used */ hda_mach->mach_params.link_mask = 0; + } else { + /* + * Allow SoundWire links to start when no external HDaudio codec + * was detected. This will not create a SoundWire card but + * will help detect if any SoundWire codec reports as ATTACHED. + */ + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; + + hda_mach->mach_params.link_mask = hdev->info.link_mask; } *mach = hda_mach; @@ -1562,7 +1597,11 @@ void hda_set_mach_params(struct snd_soc_acpi_mach *mach, mach_params = &mach->mach_params; mach_params->platform = dev_name(sdev->dev); - mach_params->num_dai_drivers = desc->ops->num_drv; + if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) && + sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC)) + mach_params->num_dai_drivers = SOF_SKL_NUM_DAIS_NOCODEC; + else + mach_params->num_dai_drivers = desc->ops->num_drv; mach_params->dai_drivers = desc->ops->drv; } diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index c4befacde23e..3f7c6fb05e5d 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -414,10 +414,12 @@ (HDA_DSP_BDL_SIZE / sizeof(struct sof_intel_dsp_bdl)) /* Number of DAIs */ -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) +#define SOF_SKL_NUM_DAIS_NOCODEC 8 + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) #define SOF_SKL_NUM_DAIS 15 #else -#define SOF_SKL_NUM_DAIS 8 +#define SOF_SKL_NUM_DAIS SOF_SKL_NUM_DAIS_NOCODEC #endif /* Intel HD Audio SRAM Window 0*/ @@ -779,6 +781,7 @@ int hda_dsp_trace_trigger(struct snd_sof_dev *sdev, int cmd); #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) int hda_sdw_check_lcount_common(struct snd_sof_dev *sdev); +int hda_sdw_check_lcount_ext(struct snd_sof_dev *sdev); int hda_sdw_startup(struct snd_sof_dev *sdev); void hda_common_enable_sdw_irq(struct snd_sof_dev *sdev, bool enable); void hda_sdw_int_enable(struct snd_sof_dev *sdev, bool enable); @@ -792,6 +795,11 @@ static inline int hda_sdw_check_lcount_common(struct snd_sof_dev *sdev) return 0; } +static inline int hda_sdw_check_lcount_ext(struct snd_sof_dev *sdev) +{ + return 0; +} + static inline int hda_sdw_startup(struct snd_sof_dev *sdev) { return 0; @@ -917,6 +925,11 @@ int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev, * @pre_trigger: Function pointer for DAI DMA pre-trigger actions * @trigger: Function pointer for DAI DMA trigger actions * @post_trigger: Function pointer for DAI DMA post-trigger actions + * @codec_dai_set_stream: Function pointer to set codec-side stream information + * @calc_stream_format: Function pointer to determine stream format from hw_params and + * for HDaudio codec DAI from the .sig bits + * @get_hlink: Mandatory function pointer to retrieve hlink, mainly to program LOSIDV + * for legacy HDaudio links or program HDaudio Extended Link registers. */ struct hda_dai_widget_dma_ops { struct hdac_ext_stream *(*get_hext_stream)(struct snd_sof_dev *sdev, @@ -936,6 +949,14 @@ struct hda_dai_widget_dma_ops { struct snd_pcm_substream *substream, int cmd); int (*post_trigger)(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, struct snd_pcm_substream *substream, int cmd); + void (*codec_dai_set_stream)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct hdac_stream *hstream); + unsigned int (*calc_stream_format)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params); + struct hdac_ext_link * (*get_hlink)(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream); }; const struct hda_dai_widget_dma_ops * diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c index 46caf3ccde66..30fe77fd87bf 100644 --- a/sound/soc/sof/intel/mtl.c +++ b/sound/soc/sof/intel/mtl.c @@ -55,7 +55,7 @@ static void mtl_ipc_dsp_done(struct snd_sof_dev *sdev) } /* Check if an IPC IRQ occurred */ -static bool mtl_dsp_check_ipc_irq(struct snd_sof_dev *sdev) +bool mtl_dsp_check_ipc_irq(struct snd_sof_dev *sdev) { u32 irq_status; u32 hfintipptr; @@ -118,7 +118,7 @@ static int mtl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *ms return 0; } -static void mtl_enable_ipc_interrupts(struct snd_sof_dev *sdev) +void mtl_enable_ipc_interrupts(struct snd_sof_dev *sdev) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; const struct sof_intel_dsp_desc *chip = hda->desc; @@ -132,7 +132,7 @@ static void mtl_enable_ipc_interrupts(struct snd_sof_dev *sdev) MTL_DSP_REG_HFIPCXCTL_BUSY | MTL_DSP_REG_HFIPCXCTL_DONE); } -static void mtl_disable_ipc_interrupts(struct snd_sof_dev *sdev) +void mtl_disable_ipc_interrupts(struct snd_sof_dev *sdev) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; const struct sof_intel_dsp_desc *chip = hda->desc; @@ -173,7 +173,7 @@ static void mtl_enable_sdw_irq(struct snd_sof_dev *sdev, bool enable) enable ? "enable" : "disable"); } -static int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable) +int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable) { u32 hfintipptr; u32 irqinten; @@ -361,11 +361,17 @@ static int mtl_dsp_core_power_up(struct snd_sof_dev *sdev, int core) ret = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, MTL_DSP2CXCTL_PRIMARY_CORE, dspcxctl, (dspcxctl & cpa) == cpa, HDA_DSP_REG_POLL_INTERVAL_US, HDA_DSP_RESET_TIMEOUT_US); - if (ret < 0) + if (ret < 0) { dev_err(sdev->dev, "%s: timeout on MTL_DSP2CXCTL_PRIMARY_CORE read\n", __func__); + return ret; + } - return ret; + /* set primary core mask and refcount to 1 */ + sdev->enabled_cores_mask = BIT(SOF_DSP_PRIMARY_CORE); + sdev->dsp_core_ref_count[SOF_DSP_PRIMARY_CORE] = 1; + + return 0; } static int mtl_dsp_core_power_down(struct snd_sof_dev *sdev, int core) @@ -388,13 +394,18 @@ static int mtl_dsp_core_power_down(struct snd_sof_dev *sdev, int core) !(dspcxctl & MTL_DSP2CXCTL_PRIMARY_CORE_CPA_MASK), HDA_DSP_REG_POLL_INTERVAL_US, HDA_DSP_PD_TIMEOUT * USEC_PER_MSEC); - if (ret < 0) + if (ret < 0) { dev_err(sdev->dev, "failed to power down primary core\n"); + return ret; + } - return ret; + sdev->enabled_cores_mask = 0; + sdev->dsp_core_ref_count[SOF_DSP_PRIMARY_CORE] = 0; + + return 0; } -static int mtl_power_down_dsp(struct snd_sof_dev *sdev) +int mtl_power_down_dsp(struct snd_sof_dev *sdev) { u32 dsphfdsscs, cpa; int ret; @@ -421,7 +432,7 @@ static int mtl_power_down_dsp(struct snd_sof_dev *sdev) HDA_DSP_RESET_TIMEOUT_US); } -static int mtl_dsp_cl_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot) +int mtl_dsp_cl_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; const struct sof_intel_dsp_desc *chip = hda->desc; @@ -613,6 +624,36 @@ static u64 mtl_dsp_get_stream_hda_link_position(struct snd_sof_dev *sdev, return ((u64)llp_u << 32) | llp_l; } +static int mtl_dsp_core_get(struct snd_sof_dev *sdev, int core) +{ + const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm; + + if (core == SOF_DSP_PRIMARY_CORE) + return mtl_dsp_core_power_up(sdev, SOF_DSP_PRIMARY_CORE); + + if (pm_ops->set_core_state) + return pm_ops->set_core_state(sdev, core, true); + + return 0; +} + +static int mtl_dsp_core_put(struct snd_sof_dev *sdev, int core) +{ + const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm; + int ret; + + if (pm_ops->set_core_state) { + ret = pm_ops->set_core_state(sdev, core, false); + if (ret < 0) + return ret; + } + + if (core == SOF_DSP_PRIMARY_CORE) + return mtl_dsp_core_power_down(sdev, SOF_DSP_PRIMARY_CORE); + + return 0; +} + /* Meteorlake ops */ struct snd_sof_dsp_ops sof_mtl_ops; EXPORT_SYMBOL_NS(sof_mtl_ops, SND_SOC_SOF_INTEL_HDA_COMMON); @@ -649,7 +690,8 @@ int sof_mtl_ops_init(struct snd_sof_dev *sdev) sof_mtl_ops.parse_platform_ext_manifest = NULL; /* dsp core get/put */ - /* TODO: add core_get and core_put */ + sof_mtl_ops.core_get = mtl_dsp_core_get; + sof_mtl_ops.core_put = mtl_dsp_core_put; sof_mtl_ops.get_stream_position = mtl_dsp_get_stream_hda_link_position; diff --git a/sound/soc/sof/intel/mtl.h b/sound/soc/sof/intel/mtl.h index 26418fb08807..2794fe6e8139 100644 --- a/sound/soc/sof/intel/mtl.h +++ b/sound/soc/sof/intel/mtl.h @@ -82,3 +82,10 @@ #define MTL_DSP_REG_HfIMRIS1 0x162088 #define MTL_DSP_REG_HfIMRIS1_IU_MASK BIT(0) +void mtl_enable_ipc_interrupts(struct snd_sof_dev *sdev); +void mtl_disable_ipc_interrupts(struct snd_sof_dev *sdev); +bool mtl_dsp_check_ipc_irq(struct snd_sof_dev *sdev); + +int mtl_enable_interrupts(struct snd_sof_dev *sdev, bool enable); +int mtl_dsp_cl_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot); +int mtl_power_down_dsp(struct snd_sof_dev *sdev); diff --git a/sound/soc/sof/intel/skl.c b/sound/soc/sof/intel/skl.c index 13efdb94d071..d24e64e71b58 100644 --- a/sound/soc/sof/intel/skl.c +++ b/sound/soc/sof/intel/skl.c @@ -19,7 +19,6 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/pci.h> -#include <linux/pm_runtime.h> #include <sound/hdaudio_ext.h> #include <sound/pcm_params.h> #include <sound/sof.h> diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c index 2713b7dc7931..8e2b07e1612b 100644 --- a/sound/soc/sof/intel/tgl.c +++ b/sound/soc/sof/intel/tgl.c @@ -39,14 +39,18 @@ static int tgl_dsp_core_get(struct snd_sof_dev *sdev, int core) static int tgl_dsp_core_put(struct snd_sof_dev *sdev, int core) { const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm; + int ret; + + if (pm_ops->set_core_state) { + ret = pm_ops->set_core_state(sdev, core, false); + if (ret < 0) + return ret; + } /* power down primary core and return */ if (core == SOF_DSP_PRIMARY_CORE) return hda_dsp_core_reset_power_down(sdev, BIT(core)); - if (pm_ops->set_core_state) - return pm_ops->set_core_state(sdev, core, false); - return 0; } diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index ad040e7bb850..a8deec7dc021 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -96,6 +96,26 @@ static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, cdata->elems_remaining = 0; ret = iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set); + if (!set) + goto unlock; + + /* It is a set-data operation, and we have a backup that we can restore */ + if (ret < 0) { + if (!scontrol->old_ipc_control_data) + goto unlock; + /* + * Current ipc_control_data is not valid, we use the last known good + * configuration + */ + memcpy(scontrol->ipc_control_data, scontrol->old_ipc_control_data, + scontrol->max_size); + kfree(scontrol->old_ipc_control_data); + scontrol->old_ipc_control_data = NULL; + /* Send the last known good configuration to firmware */ + ret = iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set); + if (ret < 0) + goto unlock; + } unlock: if (lock) @@ -351,6 +371,7 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol, struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; struct snd_soc_component *scomp = scontrol->scomp; struct snd_ctl_tlv header; + int ret = -EINVAL; /* * The beginning of bytes data contains a header from where @@ -381,31 +402,52 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol, return -EINVAL; } - if (copy_from_user(cdata->data, tlvd->tlv, header.length)) - return -EFAULT; + if (!scontrol->old_ipc_control_data) { + /* Create a backup of the current, valid bytes control */ + scontrol->old_ipc_control_data = kmemdup(scontrol->ipc_control_data, + scontrol->max_size, GFP_KERNEL); + if (!scontrol->old_ipc_control_data) + return -ENOMEM; + } + + if (copy_from_user(cdata->data, tlvd->tlv, header.length)) { + ret = -EFAULT; + goto err_restore; + } if (cdata->data->magic != SOF_ABI_MAGIC) { dev_err_ratelimited(scomp->dev, "Wrong ABI magic 0x%08x\n", cdata->data->magic); - return -EINVAL; + goto err_restore; } if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) { dev_err_ratelimited(scomp->dev, "Incompatible ABI version 0x%08x\n", cdata->data->abi); - return -EINVAL; + goto err_restore; } /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) { dev_err_ratelimited(scomp->dev, "Mismatch in ABI data size (truncated?)\n"); - return -EINVAL; + goto err_restore; } /* notify DSP of byte control updates */ - if (pm_runtime_active(scomp->dev)) + if (pm_runtime_active(scomp->dev)) { + /* Actually send the data to the DSP; this is an opportunity to validate the data */ return sof_ipc3_set_get_kcontrol_data(scontrol, true, true); + } return 0; + +err_restore: + /* If we have an issue, we restore the old, valid bytes control data */ + if (scontrol->old_ipc_control_data) { + memcpy(cdata->data, scontrol->old_ipc_control_data, scontrol->max_size); + kfree(scontrol->old_ipc_control_data); + scontrol->old_ipc_control_data = NULL; + } + return ret; } static int _sof_ipc3_bytes_ext_get(struct snd_sof_control *scontrol, diff --git a/sound/soc/sof/ipc3-priv.h b/sound/soc/sof/ipc3-priv.h index f5044202f3c5..0bbca418e67e 100644 --- a/sound/soc/sof/ipc3-priv.h +++ b/sound/soc/sof/ipc3-priv.h @@ -28,6 +28,8 @@ int sof_ipc3_validate_fw_version(struct snd_sof_dev *sdev); /* dtrace position update */ int ipc3_dtrace_posn_update(struct snd_sof_dev *sdev, struct sof_ipc_dma_trace_posn *posn); +/* RX handler backend */ +void sof_ipc3_do_rx_work(struct snd_sof_dev *sdev, struct sof_ipc_cmd_hdr *hdr, void *msg_buf); /* dtrace platform callback wrappers */ static inline int sof_dtrace_host_init(struct snd_sof_dev *sdev, diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c index c67767742093..2c5aac31e8b0 100644 --- a/sound/soc/sof/ipc3.c +++ b/sound/soc/sof/ipc3.c @@ -223,6 +223,14 @@ static inline void ipc3_log_header(struct device *dev, u8 *text, u32 cmd) } #endif +static void sof_ipc3_dump_payload(struct snd_sof_dev *sdev, + void *ipc_data, size_t size) +{ + printk(KERN_DEBUG "Size of payload following the header: %zu\n", size); + print_hex_dump_debug("Message payload: ", DUMP_PREFIX_OFFSET, + 16, 4, ipc_data, size, false); +} + static int sof_ipc3_get_reply(struct snd_sof_dev *sdev) { struct snd_sof_ipc_msg *msg = sdev->msg; @@ -374,6 +382,29 @@ static int sof_ipc3_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_ ret = ipc3_tx_msg_unlocked(ipc, msg_data, msg_bytes, reply_data, reply_bytes); + if (sof_debug_check_flag(SOF_DBG_DUMP_IPC_MESSAGE_PAYLOAD)) { + size_t payload_bytes, header_bytes; + char *payload = NULL; + + /* payload is indicated by non zero msg/reply_bytes */ + if (msg_bytes > sizeof(struct sof_ipc_cmd_hdr)) { + payload = msg_data; + + header_bytes = sizeof(struct sof_ipc_cmd_hdr); + payload_bytes = msg_bytes - header_bytes; + } else if (reply_bytes > sizeof(struct sof_ipc_reply)) { + payload = reply_data; + + header_bytes = sizeof(struct sof_ipc_reply); + payload_bytes = reply_bytes - header_bytes; + } + + if (payload) { + payload += header_bytes; + sof_ipc3_dump_payload(sdev, payload, payload_bytes); + } + } + mutex_unlock(&ipc->tx_mutex); return ret; @@ -472,6 +503,14 @@ static int sof_ipc3_set_get_data(struct snd_sof_dev *sdev, void *data, size_t da offset += payload_size; } + if (sof_debug_check_flag(SOF_DBG_DUMP_IPC_MESSAGE_PAYLOAD)) { + size_t header_bytes = sizeof(struct sof_ipc_reply); + char *payload = (char *)cdata; + + payload += header_bytes; + sof_ipc3_dump_payload(sdev, payload, data_bytes - header_bytes); + } + mutex_unlock(&sdev->ipc->tx_mutex); kfree(cdata_chunk); @@ -954,31 +993,21 @@ static void ipc3_trace_message(struct snd_sof_dev *sdev, void *msg_buf) } } -/* DSP firmware has sent host a message */ -static void sof_ipc3_rx_msg(struct snd_sof_dev *sdev) +void sof_ipc3_do_rx_work(struct snd_sof_dev *sdev, struct sof_ipc_cmd_hdr *hdr, void *msg_buf) { ipc3_rx_callback rx_callback = NULL; - struct sof_ipc_cmd_hdr hdr; - void *msg_buf; u32 cmd; int err; - /* read back header */ - err = snd_sof_ipc_msg_data(sdev, NULL, &hdr, sizeof(hdr)); - if (err < 0) { - dev_warn(sdev->dev, "failed to read IPC header: %d\n", err); - return; - } + ipc3_log_header(sdev->dev, "ipc rx", hdr->cmd); - if (hdr.size < sizeof(hdr) || hdr.size > SOF_IPC_MSG_MAX_SIZE) { + if (hdr->size < sizeof(hdr) || hdr->size > SOF_IPC_MSG_MAX_SIZE) { dev_err(sdev->dev, "The received message size is invalid: %u\n", - hdr.size); + hdr->size); return; } - ipc3_log_header(sdev->dev, "ipc rx", hdr.cmd); - - cmd = hdr.cmd & SOF_GLB_TYPE_MASK; + cmd = hdr->cmd & SOF_GLB_TYPE_MASK; /* check message type */ switch (cmd) { @@ -1016,6 +1045,36 @@ static void sof_ipc3_rx_msg(struct snd_sof_dev *sdev) break; } + /* Call local handler for the message */ + if (rx_callback) + rx_callback(sdev, msg_buf); + + /* Notify registered clients */ + sof_client_ipc_rx_dispatcher(sdev, msg_buf); + + ipc3_log_header(sdev->dev, "ipc rx done", hdr->cmd); +} +EXPORT_SYMBOL(sof_ipc3_do_rx_work); + +/* DSP firmware has sent host a message */ +static void sof_ipc3_rx_msg(struct snd_sof_dev *sdev) +{ + struct sof_ipc_cmd_hdr hdr; + void *msg_buf; + int err; + + /* read back header */ + err = snd_sof_ipc_msg_data(sdev, NULL, &hdr, sizeof(hdr)); + if (err < 0) { + dev_warn(sdev->dev, "failed to read IPC header: %d\n", err); + return; + } + + if (hdr.size < sizeof(hdr)) { + dev_err(sdev->dev, "The received message size is invalid\n"); + return; + } + /* read the full message */ msg_buf = kmalloc(hdr.size, GFP_KERNEL); if (!msg_buf) @@ -1024,18 +1083,13 @@ static void sof_ipc3_rx_msg(struct snd_sof_dev *sdev) err = snd_sof_ipc_msg_data(sdev, NULL, msg_buf, hdr.size); if (err < 0) { dev_err(sdev->dev, "%s: Failed to read message: %d\n", __func__, err); - } else { - /* Call local handler for the message */ - if (rx_callback) - rx_callback(sdev, msg_buf); - - /* Notify registered clients */ - sof_client_ipc_rx_dispatcher(sdev, msg_buf); + kfree(msg_buf); + return; } - kfree(msg_buf); + sof_ipc3_do_rx_work(sdev, &hdr, msg_buf); - ipc3_log_header(sdev->dev, "ipc rx done", hdr.cmd); + kfree(msg_buf); } static int sof_ipc3_set_core_state(struct snd_sof_dev *sdev, int core_idx, bool on) diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c index 6f0698be9451..c6d404d44097 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -54,6 +54,26 @@ static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, msg->primary |= SOF_IPC4_MOD_INSTANCE(swidget->instance_id); ret = iops->set_get_data(sdev, msg, msg->data_size, set); + if (!set) + goto unlock; + + /* It is a set-data operation, and we have a valid backup that we can restore */ + if (ret < 0) { + if (!scontrol->old_ipc_control_data) + goto unlock; + /* + * Current ipc_control_data is not valid, we use the last known good + * configuration + */ + memcpy(scontrol->ipc_control_data, scontrol->old_ipc_control_data, + scontrol->max_size); + kfree(scontrol->old_ipc_control_data); + scontrol->old_ipc_control_data = NULL; + /* Send the last known good configuration to firmware */ + ret = iops->set_get_data(sdev, msg, msg->data_size, set); + if (ret < 0) + goto unlock; + } unlock: if (lock) @@ -327,13 +347,24 @@ static int sof_ipc4_bytes_ext_put(struct snd_sof_control *scontrol, return -EINVAL; } + if (!scontrol->old_ipc_control_data) { + /* Create a backup of the current, valid bytes control */ + scontrol->old_ipc_control_data = kmemdup(scontrol->ipc_control_data, + scontrol->max_size, GFP_KERNEL); + if (!scontrol->old_ipc_control_data) + return -ENOMEM; + } + /* Copy the whole binary data which includes the ABI header and the payload */ - if (copy_from_user(data, tlvd->tlv, header.length)) + if (copy_from_user(data, tlvd->tlv, header.length)) { + memcpy(scontrol->ipc_control_data, scontrol->old_ipc_control_data, + scontrol->max_size); + kfree(scontrol->old_ipc_control_data); + scontrol->old_ipc_control_data = NULL; return -EFAULT; + } - sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true); - - return 0; + return sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true); } static int _sof_ipc4_bytes_ext_get(struct snd_sof_control *scontrol, diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c index 1321acc402fd..eaa04762eb11 100644 --- a/sound/soc/sof/ipc4-loader.c +++ b/sound/soc/sof/ipc4-loader.c @@ -112,16 +112,13 @@ static ssize_t sof_ipc4_fw_parse_ext_man(struct snd_sof_dev *sdev, return -EINVAL; } - /* a module's config is always the same size */ - fw_module->bss_size = fm_config[fm_entry->cfg_offset].is_bytes; + fw_module->fw_mod_cfg = &fm_config[fm_entry->cfg_offset]; dev_dbg(sdev->dev, "module %s: UUID %pUL cfg_count: %u, bss_size: %#x\n", fm_entry->name, &fm_entry->uuid, fm_entry->cfg_count, - fw_module->bss_size); + fm_config[fm_entry->cfg_offset].is_bytes); } else { - fw_module->bss_size = 0; - dev_dbg(sdev->dev, "module %s: UUID %pUL\n", fm_entry->name, &fm_entry->uuid); } @@ -426,6 +423,71 @@ int sof_ipc4_reload_fw_libraries(struct snd_sof_dev *sdev) return ret; } +/** + * sof_ipc4_update_cpc_from_manifest - Update the cpc in base config from manifest + * @sdev: SOF device + * @fw_module: pointer struct sof_ipc4_fw_module to parse + * @basecfg: Pointer to the base_config to update + */ +void sof_ipc4_update_cpc_from_manifest(struct snd_sof_dev *sdev, + struct sof_ipc4_fw_module *fw_module, + struct sof_ipc4_base_module_cfg *basecfg) +{ + const struct sof_man4_module_config *fw_mod_cfg; + u32 cpc_pick = 0; + u32 max_cpc = 0; + const char *msg; + int i; + + if (!fw_module->fw_mod_cfg) { + msg = "No mod_cfg available for CPC lookup in the firmware file's manifest"; + goto no_cpc; + } + + /* + * Find the best matching (highest) CPC value based on the module's + * IBS/OBS configuration inferred from the audio format selection. + * + * The CPC value in each module config entry has been measured and + * recorded as a IBS/OBS/CPC triplet and stored in the firmware file's + * manifest + */ + fw_mod_cfg = fw_module->fw_mod_cfg; + for (i = 0; i < fw_module->man4_module_entry.cfg_count; i++) { + if (basecfg->obs == fw_mod_cfg[i].obs && + basecfg->ibs == fw_mod_cfg[i].ibs && + cpc_pick < fw_mod_cfg[i].cpc) + cpc_pick = fw_mod_cfg[i].cpc; + + if (max_cpc < fw_mod_cfg[i].cpc) + max_cpc = fw_mod_cfg[i].cpc; + } + + basecfg->cpc = cpc_pick; + + /* We have a matching configuration for CPC */ + if (basecfg->cpc) + return; + + /* + * No matching IBS/OBS found, the firmware manifest is missing + * information in the module's module configuration table. + */ + if (!max_cpc) + msg = "No CPC value available in the firmware file's manifest"; + else if (!cpc_pick) + msg = "No CPC match in the firmware file's manifest"; + +no_cpc: + dev_warn(sdev->dev, "%s (UUID: %pUL): %s (ibs/obs: %u/%u)\n", + fw_module->man4_module_entry.name, + &fw_module->man4_module_entry.uuid, msg, basecfg->ibs, + basecfg->obs); + dev_warn_once(sdev->dev, "Please try to update the firmware.\n"); + dev_warn_once(sdev->dev, "If the issue persists, file a bug at\n"); + dev_warn_once(sdev->dev, "https://github.com/thesofproject/sof/issues/\n"); +} + const struct sof_ipc_fw_loader_ops ipc4_loader_ops = { .validate = sof_ipc4_validate_firmware, .parse_ext_manifest = sof_ipc4_fw_parse_basefw_ext_man, diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 9e2b6c45080d..0c905bd0fab4 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -69,7 +69,7 @@ sof_ipc4_add_pipeline_to_trigger_list(struct snd_sof_dev *sdev, int state, struct snd_sof_widget *pipe_widget = spipe->pipe_widget; struct sof_ipc4_pipeline *pipeline = pipe_widget->private; - if (pipeline->skip_during_fe_trigger) + if (pipeline->skip_during_fe_trigger && state != SOF_IPC4_PIPE_RESET) return; switch (state) { @@ -108,7 +108,7 @@ sof_ipc4_update_pipeline_state(struct snd_sof_dev *sdev, int state, int cmd, struct sof_ipc4_pipeline *pipeline = pipe_widget->private; int i; - if (pipeline->skip_during_fe_trigger) + if (pipeline->skip_during_fe_trigger && state != SOF_IPC4_PIPE_RESET) return; /* set state for pipeline if it was just triggered */ diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index f461b8c70df3..a5d0b2eae464 100644 --- a/sound/soc/sof/ipc4-priv.h +++ b/sound/soc/sof/ipc4-priv.h @@ -28,14 +28,14 @@ enum sof_ipc4_mtrace_type { /** * struct sof_ipc4_fw_module - IPC4 module info * @sof_man4_module: Module info + * @fw_mod_cfg: Pointer to the module config start of the module * @m_ida: Module instance identifier - * @bss_size: Module object size * @private: Module private data */ struct sof_ipc4_fw_module { struct sof_man4_module man4_module_entry; + const struct sof_man4_module_config *fw_mod_cfg; struct ida m_ida; - u32 bss_size; void *private; }; @@ -114,4 +114,10 @@ int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev); int sof_ipc4_reload_fw_libraries(struct snd_sof_dev *sdev); struct sof_ipc4_fw_module *sof_ipc4_find_module_by_uuid(struct snd_sof_dev *sdev, const guid_t *uuid); + +struct sof_ipc4_base_module_cfg; +void sof_ipc4_update_cpc_from_manifest(struct snd_sof_dev *sdev, + struct sof_ipc4_fw_module *fw_module, + struct sof_ipc4_base_module_cfg *basecfg); + #endif diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 5abe616a2054..a4e1a70b607d 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -39,8 +39,6 @@ static const struct sof_topology_token pipeline_tokens[] = { }; static const struct sof_topology_token ipc4_comp_tokens[] = { - {SOF_TKN_COMP_CPC, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, - offsetof(struct sof_ipc4_base_module_cfg, cpc)}, {SOF_TKN_COMP_IS_PAGES, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, offsetof(struct sof_ipc4_base_module_cfg, is_pages)}, }; @@ -235,7 +233,7 @@ static int sof_ipc4_get_audio_fmt(struct snd_soc_component *scomp, "Number of input audio formats: %d. Number of output audio formats: %d\n", available_fmt->num_input_formats, available_fmt->num_output_formats); - /* set cpc and is_pages in the module's base_config */ + /* set is_pages in the module's base_config */ ret = sof_update_ipc_object(scomp, module_base_cfg, SOF_COMP_TOKENS, swidget->tuples, swidget->num_tuples, sizeof(*module_base_cfg), 1); if (ret) { @@ -244,8 +242,8 @@ static int sof_ipc4_get_audio_fmt(struct snd_soc_component *scomp, return ret; } - dev_dbg(scomp->dev, "widget %s cpc: %d is_pages: %d\n", - swidget->widget->name, module_base_cfg->cpc, module_base_cfg->is_pages); + dev_dbg(scomp->dev, "widget %s: is_pages: %d\n", swidget->widget->name, + module_base_cfg->is_pages); if (available_fmt->num_input_formats) { in_format = kcalloc(available_fmt->num_input_formats, @@ -561,7 +559,7 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) strcmp(w->widget->sname, swidget->widget->sname)) continue; - blob->alh_cfg.count++; + blob->alh_cfg.device_count++; } ipc4_copier->copier_config = (uint32_t *)blob; @@ -723,9 +721,9 @@ static int sof_ipc4_widget_setup_comp_pga(struct snd_sof_widget *swidget) } dev_dbg(scomp->dev, - "pga widget %s: ramp type: %d, ramp duration %d, initial gain value: %#x, cpc %d\n", + "pga widget %s: ramp type: %d, ramp duration %d, initial gain value: %#x\n", swidget->widget->name, gain->data.curve_type, gain->data.curve_duration_l, - gain->data.init_val, gain->base_config.cpc); + gain->data.init_val); ret = sof_ipc4_widget_setup_msg(swidget, &gain->msg); if (ret) @@ -936,8 +934,8 @@ static void sof_ipc4_widget_free_comp_process(struct snd_sof_widget *swidget) } static void -sof_ipc4_update_pipeline_mem_usage(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, - struct sof_ipc4_base_module_cfg *base_config) +sof_ipc4_update_resource_usage(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, + struct sof_ipc4_base_module_cfg *base_config) { struct sof_ipc4_fw_module *fw_module = swidget->module_info; struct snd_sof_widget *pipe_widget; @@ -968,6 +966,13 @@ sof_ipc4_update_pipeline_mem_usage(struct snd_sof_dev *sdev, struct snd_sof_widg pipe_widget = swidget->spipe->pipe_widget; pipeline = pipe_widget->private; pipeline->mem_usage += total; + + /* Update base_config->cpc from the module manifest */ + sof_ipc4_update_cpc_from_manifest(sdev, fw_module, base_config); + + dev_dbg(sdev->dev, "%s: ibs / obs / cpc: %u / %u / %u\n", + swidget->widget->name, base_config->ibs, base_config->obs, + base_config->cpc); } static int sof_ipc4_widget_assign_instance_id(struct snd_sof_dev *sdev, @@ -1028,47 +1033,125 @@ static int sof_ipc4_update_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw return 0; } -static int sof_ipc4_init_audio_fmt(struct snd_sof_dev *sdev, - struct snd_sof_widget *swidget, - struct sof_ipc4_base_module_cfg *base_config, - struct snd_pcm_hw_params *params, - struct sof_ipc4_available_audio_format *available_fmt, - struct sof_ipc4_pin_format *pin_fmts, u32 pin_fmts_size) +static bool sof_ipc4_is_single_format(struct snd_sof_dev *sdev, + struct sof_ipc4_pin_format *pin_fmts, u32 pin_fmts_size) { - u32 valid_bits; - u32 channels; - u32 rate; - int sample_valid_bits; + struct sof_ipc4_audio_format *fmt; + u32 rate, channels, valid_bits; + int i; + + fmt = &pin_fmts[0].audio_fmt; + rate = fmt->sampling_frequency; + channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg); + valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg); + + /* check if all output formats in topology are the same */ + for (i = 1; i < pin_fmts_size; i++) { + u32 _rate, _channels, _valid_bits; + + fmt = &pin_fmts[i].audio_fmt; + _rate = fmt->sampling_frequency; + _channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(fmt->fmt_cfg); + _valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(fmt->fmt_cfg); + + if (_rate != rate || _channels != channels || _valid_bits != valid_bits) + return false; + } + + return true; +} + +static int sof_ipc4_init_output_audio_fmt(struct snd_sof_dev *sdev, + struct sof_ipc4_base_module_cfg *base_config, + struct sof_ipc4_available_audio_format *available_fmt, + u32 out_ref_rate, u32 out_ref_channels, + u32 out_ref_valid_bits) +{ + struct sof_ipc4_audio_format *out_fmt; + bool single_format; int i; - if (!pin_fmts) { - dev_err(sdev->dev, "no reference formats for %s\n", swidget->widget->name); + if (!available_fmt->num_output_formats) return -EINVAL; + + single_format = sof_ipc4_is_single_format(sdev, available_fmt->output_pin_fmts, + available_fmt->num_output_formats); + + /* pick the first format if there's only one available or if all formats are the same */ + if (single_format) { + base_config->obs = available_fmt->output_pin_fmts[0].buffer_size; + return 0; } + /* + * if there are multiple output formats, then choose the output format that matches + * the reference params + */ + for (i = 0; i < available_fmt->num_output_formats; i++) { + u32 _out_rate, _out_channels, _out_valid_bits; + + out_fmt = &available_fmt->output_pin_fmts[i].audio_fmt; + _out_rate = out_fmt->sampling_frequency; + _out_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(out_fmt->fmt_cfg); + _out_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(out_fmt->fmt_cfg); + + if (_out_rate == out_ref_rate && _out_channels == out_ref_channels && + _out_valid_bits == out_ref_valid_bits) { + base_config->obs = available_fmt->output_pin_fmts[i].buffer_size; + return i; + } + } + + return -EINVAL; +} + +static int sof_ipc4_get_valid_bits(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params) +{ switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: - sample_valid_bits = 16; - break; + return 16; case SNDRV_PCM_FORMAT_S24_LE: - sample_valid_bits = 24; - break; + return 24; case SNDRV_PCM_FORMAT_S32_LE: - sample_valid_bits = 32; - break; + return 32; default: dev_err(sdev->dev, "invalid pcm frame format %d\n", params_format(params)); return -EINVAL; } +} + +static int sof_ipc4_init_input_audio_fmt(struct snd_sof_dev *sdev, + struct snd_sof_widget *swidget, + struct sof_ipc4_base_module_cfg *base_config, + struct snd_pcm_hw_params *params, + struct sof_ipc4_available_audio_format *available_fmt) +{ + struct sof_ipc4_pin_format *pin_fmts = available_fmt->input_pin_fmts; + u32 pin_fmts_size = available_fmt->num_input_formats; + u32 valid_bits; + u32 channels; + u32 rate; + bool single_format; + int sample_valid_bits; + int i = 0; - if (!pin_fmts_size) { - dev_err(sdev->dev, "no formats available for %s\n", swidget->widget->name); + if (!available_fmt->num_input_formats) { + dev_err(sdev->dev, "no input formats for %s\n", swidget->widget->name); return -EINVAL; } + single_format = sof_ipc4_is_single_format(sdev, available_fmt->input_pin_fmts, + available_fmt->num_input_formats); + if (single_format) + goto in_fmt; + + sample_valid_bits = sof_ipc4_get_valid_bits(sdev, params); + if (sample_valid_bits < 0) + return sample_valid_bits; + /* - * Search supported audio formats with pin index 0 to match rate, channels ,and - * sample_valid_bytes from runtime params + * Search supported input audio formats with pin index 0 to match rate, channels and + * sample_valid_bits from reference params */ for (i = 0; i < pin_fmts_size; i++) { struct sof_ipc4_audio_format *fmt = &pin_fmts[i].audio_fmt; @@ -1093,6 +1176,7 @@ static int sof_ipc4_init_audio_fmt(struct snd_sof_dev *sdev, return -EINVAL; } +in_fmt: /* copy input format */ if (available_fmt->num_input_formats && i < available_fmt->num_input_formats) { memcpy(&base_config->audio_fmt, &available_fmt->input_pin_fmts[i].audio_fmt, @@ -1105,10 +1189,6 @@ static int sof_ipc4_init_audio_fmt(struct snd_sof_dev *sdev, sof_ipc4_dbg_audio_format(sdev->dev, &available_fmt->input_pin_fmts[i], 1); } - if (available_fmt->num_output_formats && i < available_fmt->num_output_formats) - base_config->obs = available_fmt->output_pin_fmts[i].buffer_size; - - /* Return the index of the matched format */ return i; } @@ -1145,7 +1225,7 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget) unsigned int group_id; blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config; - if (blob->alh_cfg.count > 1) { + if (blob->alh_cfg.device_count > 1) { group_id = SOF_IPC4_NODE_INDEX(ipc4_copier->data.gtw_cfg.node_id) - ALH_MULTI_GTW_BASE; ida_free(&alh_group_ida, group_id); @@ -1288,50 +1368,6 @@ static int ipc4_set_fmt_mask(struct snd_mask *fmt, unsigned int bit_depth) return 0; } -static int ipc4_copier_set_capture_fmt(struct snd_sof_dev *sdev, - struct snd_pcm_hw_params *pipeline_params, - struct snd_pcm_hw_params *fe_params, - struct sof_ipc4_available_audio_format *available_fmt) -{ - struct sof_ipc4_audio_format *audio_fmt; - unsigned int sample_valid_bits; - bool multiple_formats = false; - bool fe_format_match = false; - struct snd_mask *fmt; - int i; - - for (i = 0; i < available_fmt->num_output_formats; i++) { - unsigned int val; - - audio_fmt = &available_fmt->output_pin_fmts[i].audio_fmt; - val = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(audio_fmt->fmt_cfg); - - if (i == 0) - sample_valid_bits = val; - else if (sample_valid_bits != val) - multiple_formats = true; - - if (snd_pcm_format_width(params_format(fe_params)) == val) - fe_format_match = true; - } - - fmt = hw_param_mask(pipeline_params, SNDRV_PCM_HW_PARAM_FORMAT); - snd_mask_none(fmt); - - if (multiple_formats) { - if (fe_format_match) { - /* multiple formats defined and one matches FE */ - snd_mask_set_format(fmt, params_format(fe_params)); - return 0; - } - - dev_err(sdev->dev, "Multiple audio formats for single dai_out not supported\n"); - return -EINVAL; - } - - return ipc4_set_fmt_mask(fmt, sample_valid_bits); -} - static int sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, struct snd_pcm_hw_params *fe_params, @@ -1341,19 +1377,21 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, struct sof_ipc4_available_audio_format *available_fmt; struct snd_soc_component *scomp = swidget->scomp; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct sof_ipc4_pin_format *format_list_to_search; struct sof_ipc4_copier_data *copier_data; struct snd_pcm_hw_params *ref_params; struct sof_ipc4_copier *ipc4_copier; struct snd_sof_dai *dai; struct snd_mask *fmt; int out_sample_valid_bits; + u32 gtw_cfg_config_length; + u32 dma_config_tlv_size = 0; void **ipc_config_data; int *ipc_config_size; u32 **data; - int ipc_size, ret; + int ipc_size, ret, out_ref_valid_bits; + u32 out_ref_rate, out_ref_channels; u32 deep_buffer_dma_ms = 0; - u32 format_list_count; + int output_fmt_index; dev_dbg(sdev->dev, "copier %s, type %d", swidget->widget->name, swidget->id); @@ -1417,13 +1455,10 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, * Use the input_pin_fmts to match pcm params for playback and the output_pin_fmts * for capture. */ - if (dir == SNDRV_PCM_STREAM_PLAYBACK) { - format_list_to_search = available_fmt->input_pin_fmts; - format_list_count = available_fmt->num_input_formats; - } else { - format_list_to_search = available_fmt->output_pin_fmts; - format_list_count = available_fmt->num_output_formats; - } + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + ref_params = fe_params; + else + ref_params = pipeline_params; copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK; copier_data->gtw_cfg.node_id |= @@ -1431,7 +1466,6 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, /* set gateway attributes */ gtw_attr->lp_buffer_alloc = pipeline->lp_mode; - ref_params = fe_params; break; } case snd_soc_dapm_dai_in: @@ -1448,20 +1482,17 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, ipc4_copier = (struct sof_ipc4_copier *)dai->private; copier_data = &ipc4_copier->data; available_fmt = &ipc4_copier->available_fmt; - if (dir == SNDRV_PCM_STREAM_CAPTURE) { - format_list_to_search = available_fmt->output_pin_fmts; - format_list_count = available_fmt->num_output_formats; - - ret = ipc4_copier_set_capture_fmt(sdev, pipeline_params, fe_params, - available_fmt); - if (ret < 0) - return ret; - } else { - format_list_to_search = available_fmt->input_pin_fmts; - format_list_count = available_fmt->num_input_formats; - } - ref_params = pipeline_params; + /* + * When there is format conversion within a pipeline, the number of supported + * output formats is typically limited to just 1 for the DAI copiers. But when there + * is no format conversion, the DAI copiers input format must match that of the + * FE hw_params for capture and the pipeline params for playback. + */ + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + ref_params = pipeline_params; + else + ref_params = fe_params; ret = snd_sof_get_nhlt_endpoint_data(sdev, dai, fe_params, ipc4_copier->dai_index, ipc4_copier->dai_type, dir, @@ -1477,10 +1508,6 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, ipc4_copier = (struct sof_ipc4_copier *)swidget->private; copier_data = &ipc4_copier->data; available_fmt = &ipc4_copier->available_fmt; - - /* Use the input formats to match pcm params */ - format_list_to_search = available_fmt->input_pin_fmts; - format_list_count = available_fmt->num_input_formats; ref_params = pipeline_params; break; @@ -1492,11 +1519,51 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, } /* set input and output audio formats */ - ret = sof_ipc4_init_audio_fmt(sdev, swidget, &copier_data->base_config, ref_params, - available_fmt, format_list_to_search, format_list_count); + ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &copier_data->base_config, ref_params, + available_fmt); if (ret < 0) return ret; + /* set the reference params for output format selection */ + switch (swidget->id) { + case snd_soc_dapm_aif_in: + case snd_soc_dapm_dai_out: + case snd_soc_dapm_buffer: + { + struct sof_ipc4_audio_format *in_fmt; + + in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt; + out_ref_rate = in_fmt->sampling_frequency; + out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg); + out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg); + break; + } + case snd_soc_dapm_aif_out: + case snd_soc_dapm_dai_in: + out_ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); + if (out_ref_valid_bits < 0) + return out_ref_valid_bits; + + out_ref_rate = params_rate(fe_params); + out_ref_channels = params_channels(fe_params); + break; + default: + /* + * Unsupported type should be caught by the former switch default + * case, this should never happen in reality. + */ + return -EINVAL; + } + + output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, &copier_data->base_config, + available_fmt, out_ref_rate, + out_ref_channels, out_ref_valid_bits); + if (output_fmt_index < 0) { + dev_err(sdev->dev, "Failed to initialize output format for %s", + swidget->widget->name); + return output_fmt_index; + } + /* * Set the output format. Current topology defines pin 0 input and output formats in pairs. * This assumes that the pin 0 formats are defined before all other pins. @@ -1504,10 +1571,11 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, * input format. This logic will need to be updated when the format definitions * in topology change. */ - memcpy(&copier_data->out_format, &available_fmt->output_pin_fmts[ret].audio_fmt, + memcpy(&copier_data->out_format, + &available_fmt->output_pin_fmts[output_fmt_index].audio_fmt, sizeof(struct sof_ipc4_audio_format)); dev_dbg(sdev->dev, "Output audio format for %s\n", swidget->widget->name); - sof_ipc4_dbg_audio_format(sdev->dev, &available_fmt->output_pin_fmts[ret], 1); + sof_ipc4_dbg_audio_format(sdev->dev, &available_fmt->output_pin_fmts[output_fmt_index], 1); switch (swidget->id) { case snd_soc_dapm_dai_in: @@ -1515,7 +1583,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, { /* * Only SOF_DAI_INTEL_ALH needs copier_data to set blob. - * That's why only ALH dai's blob is set after sof_ipc4_init_audio_fmt + * That's why only ALH dai's blob is set after sof_ipc4_init_input_audio_fmt */ if (ipc4_copier->dai_type == SOF_DAI_INTEL_ALH) { struct sof_ipc4_alh_configuration_blob *blob; @@ -1543,7 +1611,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, ch_map >>= 4; } - step = ch_count / blob->alh_cfg.count; + step = ch_count / blob->alh_cfg.device_count; mask = GENMASK(step - 1, 0); /* * Set each gtw_cfg.node_id to blob->alh_cfg.mapping[] @@ -1558,7 +1626,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, dai = w->private; alh_copier = (struct sof_ipc4_copier *)dai->private; alh_data = &alh_copier->data; - blob->alh_cfg.mapping[i].alh_id = alh_data->gtw_cfg.node_id; + blob->alh_cfg.mapping[i].device = alh_data->gtw_cfg.node_id; /* * Set the same channel mask for playback as the audio data is * duplicated for all speakers. For capture, split the channels @@ -1577,7 +1645,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, i++; } - if (blob->alh_cfg.count > 1) { + if (blob->alh_cfg.device_count > 1) { int group_id; group_id = ida_alloc_max(&alh_group_ida, ALH_MULTI_GTW_COUNT - 1, @@ -1633,7 +1701,27 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, ipc_config_data = &ipc4_copier->ipc_config_data; /* config_length is DWORD based */ - ipc_size = sizeof(*copier_data) + copier_data->gtw_cfg.config_length * 4; + gtw_cfg_config_length = copier_data->gtw_cfg.config_length * 4; + ipc_size = sizeof(*copier_data) + gtw_cfg_config_length; + + if (ipc4_copier->dma_config_tlv.type == SOF_IPC4_GTW_DMA_CONFIG_ID && + ipc4_copier->dma_config_tlv.length) { + dma_config_tlv_size = sizeof(ipc4_copier->dma_config_tlv) + + ipc4_copier->dma_config_tlv.dma_config.dma_priv_config_size; + + /* paranoia check on TLV size/length */ + if (dma_config_tlv_size != ipc4_copier->dma_config_tlv.length + + sizeof(uint32_t) * 2) { + dev_err(sdev->dev, "Invalid configuration, TLV size %d length %d\n", + dma_config_tlv_size, ipc4_copier->dma_config_tlv.length); + return -EINVAL; + } + + ipc_size += dma_config_tlv_size; + + /* we also need to increase the size at the gtw level */ + copier_data->gtw_cfg.config_length += dma_config_tlv_size / 4; + } dev_dbg(sdev->dev, "copier %s, IPC size is %d", swidget->widget->name, ipc_size); @@ -1645,12 +1733,18 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, /* copy IPC data */ memcpy(*ipc_config_data, (void *)copier_data, sizeof(*copier_data)); - if (copier_data->gtw_cfg.config_length) + if (gtw_cfg_config_length) memcpy(*ipc_config_data + sizeof(*copier_data), - *data, copier_data->gtw_cfg.config_length * 4); + *data, gtw_cfg_config_length); + + /* add DMA Config TLV, if configured */ + if (dma_config_tlv_size) + memcpy(*ipc_config_data + sizeof(*copier_data) + + gtw_cfg_config_length, + &ipc4_copier->dma_config_tlv, dma_config_tlv_size); /* update pipeline memory usage */ - sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &copier_data->base_config); + sof_ipc4_update_resource_usage(sdev, swidget, &copier_data->base_config); return 0; } @@ -1664,17 +1758,30 @@ static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_ipc4_gain *gain = swidget->private; struct sof_ipc4_available_audio_format *available_fmt = &gain->available_fmt; + struct sof_ipc4_audio_format *in_fmt; + u32 out_ref_rate, out_ref_channels, out_ref_valid_bits; int ret; - ret = sof_ipc4_init_audio_fmt(sdev, swidget, &gain->base_config, - pipeline_params, available_fmt, - available_fmt->input_pin_fmts, - available_fmt->num_input_formats); + ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &gain->base_config, + pipeline_params, available_fmt); if (ret < 0) return ret; + in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt; + out_ref_rate = in_fmt->sampling_frequency; + out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg); + out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg); + + ret = sof_ipc4_init_output_audio_fmt(sdev, &gain->base_config, available_fmt, + out_ref_rate, out_ref_channels, out_ref_valid_bits); + if (ret < 0) { + dev_err(sdev->dev, "Failed to initialize output format for %s", + swidget->widget->name); + return ret; + } + /* update pipeline memory usage */ - sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &gain->base_config); + sof_ipc4_update_resource_usage(sdev, swidget, &gain->base_config); return 0; } @@ -1688,17 +1795,30 @@ static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_ipc4_mixer *mixer = swidget->private; struct sof_ipc4_available_audio_format *available_fmt = &mixer->available_fmt; + struct sof_ipc4_audio_format *in_fmt; + u32 out_ref_rate, out_ref_channels, out_ref_valid_bits; int ret; - ret = sof_ipc4_init_audio_fmt(sdev, swidget, &mixer->base_config, - pipeline_params, available_fmt, - available_fmt->input_pin_fmts, - available_fmt->num_input_formats); + ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &mixer->base_config, + pipeline_params, available_fmt); if (ret < 0) return ret; + in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt; + out_ref_rate = in_fmt->sampling_frequency; + out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg); + out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg); + + ret = sof_ipc4_init_output_audio_fmt(sdev, &mixer->base_config, available_fmt, + out_ref_rate, out_ref_channels, out_ref_valid_bits); + if (ret < 0) { + dev_err(sdev->dev, "Failed to initialize output format for %s", + swidget->widget->name); + return ret; + } + /* update pipeline memory usage */ - sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &mixer->base_config); + sof_ipc4_update_resource_usage(sdev, swidget, &mixer->base_config); return 0; } @@ -1712,18 +1832,30 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_ipc4_src *src = swidget->private; struct sof_ipc4_available_audio_format *available_fmt = &src->available_fmt; + struct sof_ipc4_audio_format *in_fmt; + u32 out_ref_rate, out_ref_channels, out_ref_valid_bits; struct snd_interval *rate; int ret; - ret = sof_ipc4_init_audio_fmt(sdev, swidget, &src->base_config, - pipeline_params, available_fmt, - available_fmt->input_pin_fmts, - available_fmt->num_input_formats); + ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &src->base_config, + pipeline_params, available_fmt); if (ret < 0) return ret; + in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt; + out_ref_rate = in_fmt->sampling_frequency; + out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg); + out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg); + + ret = sof_ipc4_init_output_audio_fmt(sdev, &src->base_config, available_fmt, + out_ref_rate, out_ref_channels, out_ref_valid_bits); + if (ret < 0) { + dev_err(sdev->dev, "Failed to initialize output format for %s", + swidget->widget->name); + } + /* update pipeline memory usage */ - sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &src->base_config); + sof_ipc4_update_resource_usage(sdev, swidget, &src->base_config); /* update pipeline_params for sink widgets */ rate = hw_param_interval(pipeline_params, SNDRV_PCM_HW_PARAM_RATE); @@ -1820,20 +1952,37 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_ipc4_process *process = swidget->private; struct sof_ipc4_available_audio_format *available_fmt = &process->available_fmt; + struct sof_ipc4_audio_format *in_fmt; + u32 out_ref_rate, out_ref_channels, out_ref_valid_bits; void *cfg = process->ipc_config_data; + int output_fmt_index; int ret; - ret = sof_ipc4_init_audio_fmt(sdev, swidget, &process->base_config, - pipeline_params, available_fmt, - available_fmt->input_pin_fmts, - available_fmt->num_input_formats); + ret = sof_ipc4_init_input_audio_fmt(sdev, swidget, &process->base_config, + pipeline_params, available_fmt); if (ret < 0) return ret; + in_fmt = &available_fmt->input_pin_fmts[ret].audio_fmt; + out_ref_rate = in_fmt->sampling_frequency; + out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg); + out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg); + + output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, &process->base_config, + available_fmt, out_ref_rate, + out_ref_channels, out_ref_valid_bits); + if (output_fmt_index < 0 && available_fmt->num_output_formats) { + dev_err(sdev->dev, "Failed to initialize output format for %s", + swidget->widget->name); + return output_fmt_index; + } + /* copy Pin 0 output format */ - if (available_fmt->num_output_formats && ret < available_fmt->num_output_formats && - !available_fmt->output_pin_fmts[ret].pin_index) { - memcpy(&process->output_format, &available_fmt->output_pin_fmts[ret].audio_fmt, + if (available_fmt->num_output_formats && + output_fmt_index < available_fmt->num_output_formats && + !available_fmt->output_pin_fmts[output_fmt_index].pin_index) { + memcpy(&process->output_format, + &available_fmt->output_pin_fmts[output_fmt_index].audio_fmt, sizeof(struct sof_ipc4_audio_format)); /* modify the pipeline params with the pin 0 output format */ @@ -1843,7 +1992,7 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, } /* update pipeline memory usage */ - sof_ipc4_update_pipeline_mem_usage(sdev, swidget, &process->base_config); + sof_ipc4_update_resource_usage(sdev, swidget, &process->base_config); /* ipc_config_data is composed of the base_config followed by an optional extension */ memcpy(cfg, &process->base_config, sizeof(struct sof_ipc4_base_module_cfg)); @@ -2271,11 +2420,11 @@ static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev, int sink_id) { struct sof_ipc4_copier_config_set_sink_format format; + const struct sof_ipc_ops *iops = sdev->ipc->ops; struct sof_ipc4_base_module_cfg *src_config; const struct sof_ipc4_audio_format *pin_fmt; struct sof_ipc4_fw_module *fw_module; struct sof_ipc4_msg msg = {{ 0 }}; - u32 header, extension; dev_dbg(sdev->dev, "%s set copier sink %d format\n", src_widget->widget->name, sink_id); @@ -2305,22 +2454,15 @@ static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev, msg.data_size = sizeof(format); msg.data_ptr = &format; - header = fw_module->man4_module_entry.id; - header |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id); - header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_SET); - header |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); - header |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG); + msg.primary = fw_module->man4_module_entry.id; + msg.primary |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id); + msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); + msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG); - extension = SOF_IPC4_MOD_EXT_MSG_SIZE(msg.data_size); - extension |= + msg.extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_COPIER_MODULE_CFG_PARAM_SET_SINK_FORMAT); - extension |= SOF_IPC4_MOD_EXT_MSG_LAST_BLOCK(1); - extension |= SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK(1); - - msg.primary = header; - msg.extension = extension; - return sof_ipc_tx_message_no_reply(sdev->ipc, &msg, msg.data_size); + return iops->set_get_data(sdev, &msg, msg.data_size, true); } static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *sroute) @@ -2507,7 +2649,6 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget * } gtw_attr = ipc4_copier->gtw_attr; gtw_attr->lp_buffer_alloc = pipeline->lp_mode; - pipeline->skip_during_fe_trigger = true; fallthrough; case SOF_DAI_INTEL_ALH: /* diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h index cf007282867b..6dcf14886e85 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -55,7 +55,7 @@ #define SOF_IPC4_GAIN_ALL_CHANNELS_MASK 0xffffffff #define SOF_IPC4_VOL_ZERO_DB 0x7fffffff -#define ALH_MAX_NUMBER_OF_GTW 16 +#define SOF_IPC4_DMA_DEVICE_MAX_COUNT 16 #define SOF_IPC4_INVALID_NODE_ID 0xffffffff @@ -220,18 +220,64 @@ struct sof_ipc4_gtw_attributes { uint32_t rsvd : 30; }; -/** struct sof_ipc4_alh_multi_gtw_cfg: ALH gateway cfg data - * @count: Number of streams (valid items in mapping array) - * @alh_id: ALH stream id of a single ALH stream aggregated - * @channel_mask: Channel mask - * @mapping: ALH streams +/** + * struct sof_ipc4_dma_device_stream_ch_map: abstract representation of + * channel mapping to DMAs + * @device: representation of hardware device address or FIFO + * @channel_mask: channels handled by @device. Channels are expected to be + * contiguous + */ +struct sof_ipc4_dma_device_stream_ch_map { + uint32_t device; + uint32_t channel_mask; +}; + +/** + * struct sof_ipc4_dma_stream_ch_map: DMA configuration data + * @device_count: Number valid items in mapping array + * @mapping: device address and channel mask + */ +struct sof_ipc4_dma_stream_ch_map { + uint32_t device_count; + struct sof_ipc4_dma_device_stream_ch_map mapping[SOF_IPC4_DMA_DEVICE_MAX_COUNT]; +} __packed; + +#define SOF_IPC4_DMA_METHOD_HDA 1 +#define SOF_IPC4_DMA_METHOD_GPDMA 2 /* defined for consistency but not used */ + +/** + * struct sof_ipc4_dma_config: DMA configuration + * @dma_method: HDAudio or GPDMA + * @pre_allocated_by_host: 1 if host driver allocates DMA channels, 0 otherwise + * @dma_channel_id: for HDaudio defined as @stream_id - 1 + * @stream_id: HDaudio stream tag + * @dma_stream_channel_map: array of device/channel mappings + * @dma_priv_config_size: currently not used + * @dma_priv_config: currently not used + */ +struct sof_ipc4_dma_config { + uint8_t dma_method; + uint8_t pre_allocated_by_host; + uint16_t rsvd; + uint32_t dma_channel_id; + uint32_t stream_id; + struct sof_ipc4_dma_stream_ch_map dma_stream_channel_map; + uint32_t dma_priv_config_size; + uint8_t dma_priv_config[]; +} __packed; + +#define SOF_IPC4_GTW_DMA_CONFIG_ID 0x1000 + +/** + * struct sof_ipc4_dma_config: DMA configuration + * @type: set to SOF_IPC4_GTW_DMA_CONFIG_ID + * @length: sizeof(struct sof_ipc4_dma_config) + dma_config.dma_priv_config_size + * @dma_config: actual DMA configuration */ -struct sof_ipc4_alh_multi_gtw_cfg { - uint32_t count; - struct { - uint32_t alh_id; - uint32_t channel_mask; - } mapping[ALH_MAX_NUMBER_OF_GTW]; +struct sof_ipc4_dma_config_tlv { + uint32_t type; + uint32_t length; + struct sof_ipc4_dma_config dma_config; } __packed; /** struct sof_ipc4_alh_configuration_blob: ALH blob @@ -240,7 +286,7 @@ struct sof_ipc4_alh_multi_gtw_cfg { */ struct sof_ipc4_alh_configuration_blob { struct sof_ipc4_gtw_attributes gw_attr; - struct sof_ipc4_alh_multi_gtw_cfg alh_cfg; + struct sof_ipc4_dma_stream_ch_map alh_cfg; }; /** @@ -254,6 +300,7 @@ struct sof_ipc4_alh_configuration_blob { * @gtw_attr: Gateway attributes for copier blob * @dai_type: DAI type * @dai_index: DAI index + * @dma_config_tlv: DMA configuration */ struct sof_ipc4_copier { struct sof_ipc4_copier_data data; @@ -266,6 +313,7 @@ struct sof_ipc4_copier { struct sof_ipc4_gtw_attributes *gtw_attr; u32 dai_type; int dai_index; + struct sof_ipc4_dma_config_tlv dma_config_tlv; }; /** diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c index 246b56d24a6f..ab6eddd91bb7 100644 --- a/sound/soc/sof/ipc4.c +++ b/sound/soc/sof/ipc4.c @@ -17,15 +17,6 @@ #include "ipc4-priv.h" #include "ops.h" -#ifdef DEBUG_VERBOSE -#define sof_ipc4_dump_payload(sdev, ipc_data, size) \ - print_hex_dump_debug("Message payload: ", \ - DUMP_PREFIX_OFFSET, \ - 16, 4, ipc_data, size, false) -#else -#define sof_ipc4_dump_payload(sdev, ipc_data, size) do { } while (0) -#endif - static const struct sof_ipc4_fw_status { int status; char *msg; @@ -256,6 +247,13 @@ static void sof_ipc4_log_header(struct device *dev, u8 *text, struct sof_ipc4_ms } #endif +static void sof_ipc4_dump_payload(struct snd_sof_dev *sdev, + void *ipc_data, size_t size) +{ + print_hex_dump_debug("Message payload: ", DUMP_PREFIX_OFFSET, + 16, 4, ipc_data, size, false); +} + static int sof_ipc4_get_reply(struct snd_sof_dev *sdev) { struct snd_sof_ipc_msg *msg = sdev->msg; @@ -362,9 +360,6 @@ static int sof_ipc4_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_ void *reply_data, size_t reply_bytes, bool no_pm) { struct snd_sof_ipc *ipc = sdev->ipc; -#ifdef DEBUG_VERBOSE - struct sof_ipc4_msg *msg = NULL; -#endif int ret; if (!msg_data) @@ -386,18 +381,20 @@ static int sof_ipc4_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_ ret = ipc4_tx_msg_unlocked(ipc, msg_data, msg_bytes, reply_data, reply_bytes); - mutex_unlock(&ipc->tx_mutex); + if (sof_debug_check_flag(SOF_DBG_DUMP_IPC_MESSAGE_PAYLOAD)) { + struct sof_ipc4_msg *msg = NULL; -#ifdef DEBUG_VERBOSE - /* payload is indicated by non zero msg/reply_bytes */ - if (msg_bytes) - msg = msg_data; - else if (reply_bytes) - msg = reply_data; + /* payload is indicated by non zero msg/reply_bytes */ + if (msg_bytes) + msg = msg_data; + else if (reply_bytes) + msg = reply_data; - if (msg) - sof_ipc4_dump_payload(sdev, msg->data_ptr, msg->data_size); -#endif + if (msg) + sof_ipc4_dump_payload(sdev, msg->data_ptr, msg->data_size); + } + + mutex_unlock(&ipc->tx_mutex); return ret; } @@ -516,7 +513,8 @@ static int sof_ipc4_set_get_data(struct snd_sof_dev *sdev, void *data, if (!set && payload_bytes != offset) ipc4_msg->data_size = offset; - sof_ipc4_dump_payload(sdev, ipc4_msg->data_ptr, ipc4_msg->data_size); + if (sof_debug_check_flag(SOF_DBG_DUMP_IPC_MESSAGE_PAYLOAD)) + sof_ipc4_dump_payload(sdev, ipc4_msg->data_ptr, ipc4_msg->data_size); out: mutex_unlock(&sdev->ipc->tx_mutex); diff --git a/sound/soc/sof/mediatek/mt8186/mt8186-clk.c b/sound/soc/sof/mediatek/mt8186/mt8186-clk.c index 2df3b7ae1c6f..cb2ab5884b8c 100644 --- a/sound/soc/sof/mediatek/mt8186/mt8186-clk.c +++ b/sound/soc/sof/mediatek/mt8186/mt8186-clk.c @@ -8,7 +8,6 @@ // Hardware interface for mt8186 DSP clock #include <linux/clk.h> -#include <linux/pm_runtime.h> #include <linux/io.h> #include "../../sof-audio.h" diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.c b/sound/soc/sof/mediatek/mt8186/mt8186.c index 597cb4476acb..3e0ea0e109e2 100644 --- a/sound/soc/sof/mediatek/mt8186/mt8186.c +++ b/sound/soc/sof/mediatek/mt8186/mt8186.c @@ -24,6 +24,7 @@ #include "../../sof-of-dev.h" #include "../../sof-audio.h" #include "../adsp_helper.h" +#include "../mtk-adsp-common.h" #include "mt8186.h" #include "mt8186-clk.h" @@ -48,47 +49,13 @@ static int mt8186_send_msg(struct snd_sof_dev *sdev, return mtk_adsp_ipc_send(priv->dsp_ipc, MTK_ADSP_IPC_REQ, MTK_ADSP_IPC_OP_REQ); } -static void mt8186_get_reply(struct snd_sof_dev *sdev) -{ - struct snd_sof_ipc_msg *msg = sdev->msg; - struct sof_ipc_reply reply; - int ret = 0; - - if (!msg) { - dev_warn(sdev->dev, "unexpected ipc interrupt\n"); - return; - } - - /* get reply */ - sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); - if (reply.error < 0) { - memcpy(msg->reply_data, &reply, sizeof(reply)); - ret = reply.error; - } else { - /* reply has correct size? */ - if (reply.hdr.size != msg->reply_size) { - dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n", - msg->reply_size, reply.hdr.size); - ret = -EINVAL; - } - - /* read the message */ - if (msg->reply_size > 0) - sof_mailbox_read(sdev, sdev->host_box.offset, - msg->reply_data, msg->reply_size); - } - - msg->reply_error = ret; -} - static void mt8186_dsp_handle_reply(struct mtk_adsp_ipc *ipc) { struct adsp_priv *priv = mtk_adsp_ipc_get_data(ipc); unsigned long flags; spin_lock_irqsave(&priv->sdev->ipc_lock, flags); - mt8186_get_reply(priv->sdev); - snd_sof_ipc_reply(priv->sdev, 0); + snd_sof_ipc_process_reply(priv->sdev, 0); spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags); } @@ -507,6 +474,26 @@ static snd_pcm_uframes_t mt8186_pcm_pointer(struct snd_sof_dev *sdev, return pos; } +static void mt8186_adsp_dump(struct snd_sof_dev *sdev, u32 flags) +{ + u32 dbg_pc, dbg_data, dbg_inst, dbg_ls0stat, dbg_status, faultinfo; + + /* dump debug registers */ + dbg_pc = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGPC); + dbg_data = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGDATA); + dbg_inst = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGINST); + dbg_ls0stat = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGLS0STAT); + dbg_status = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PDEBUGSTATUS); + faultinfo = snd_sof_dsp_read(sdev, DSP_REG_BAR, DSP_PFAULTINFO); + + dev_info(sdev->dev, "adsp dump : pc %#x, data %#x, dbg_inst %#x,", + dbg_pc, dbg_data, dbg_inst); + dev_info(sdev->dev, "ls0stat %#x, status %#x, faultinfo %#x", + dbg_ls0stat, dbg_status, faultinfo); + + mtk_adsp_dump(sdev, flags); +} + static struct snd_soc_dai_driver mt8186_dai[] = { { .name = "SOF_DL1", @@ -589,6 +576,7 @@ static struct snd_sof_dsp_ops sof_mt8186_ops = { .num_drv = ARRAY_SIZE(mt8186_dai), /* Debug information */ + .dbg_dump = mt8186_adsp_dump, .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, /* PM */ @@ -628,7 +616,65 @@ static const struct sof_dev_desc sof_of_mt8186_desc = { .ops = &sof_mt8186_ops, }; +/* + * DL2, DL3, UL4, UL5 are registered as SOF FE, so creating the corresponding + * SOF BE to complete the pipeline. + */ +static struct snd_soc_dai_driver mt8188_dai[] = { +{ + .name = "SOF_DL2", + .playback = { + .channels_min = 1, + .channels_max = 2, + }, +}, +{ + .name = "SOF_DL3", + .playback = { + .channels_min = 1, + .channels_max = 2, + }, +}, +{ + .name = "SOF_UL4", + .capture = { + .channels_min = 1, + .channels_max = 2, + }, +}, +{ + .name = "SOF_UL5", + .capture = { + .channels_min = 1, + .channels_max = 2, + }, +}, +}; + +/* mt8188 ops */ +static struct snd_sof_dsp_ops sof_mt8188_ops; + +static int sof_mt8188_ops_init(struct snd_sof_dev *sdev) +{ + /* common defaults */ + memcpy(&sof_mt8188_ops, &sof_mt8186_ops, sizeof(sof_mt8188_ops)); + + sof_mt8188_ops.drv = mt8188_dai; + sof_mt8188_ops.num_drv = ARRAY_SIZE(mt8188_dai); + + return 0; +} + +static struct snd_sof_of_mach sof_mt8188_machs[] = { + { + .compatible = "mediatek,mt8188", + .sof_tplg_filename = "sof-mt8188.tplg", + }, + {} +}; + static const struct sof_dev_desc sof_of_mt8188_desc = { + .of_machines = sof_mt8188_machs, .ipc_supported_mask = BIT(SOF_IPC), .ipc_default = SOF_IPC, .default_fw_path = { @@ -641,7 +687,8 @@ static const struct sof_dev_desc sof_of_mt8188_desc = { [SOF_IPC] = "sof-mt8188.ri", }, .nocodec_tplg_filename = "sof-mt8188-nocodec.tplg", - .ops = &sof_mt8186_ops, + .ops = &sof_mt8188_ops, + .ops_init = sof_mt8188_ops_init, }; static const struct of_device_id sof_of_mt8186_ids[] = { diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.h b/sound/soc/sof/mediatek/mt8186/mt8186.h index 5b521c60b4e3..91323f492a1e 100644 --- a/sound/soc/sof/mediatek/mt8186/mt8186.h +++ b/sound/soc/sof/mediatek/mt8186/mt8186.h @@ -38,6 +38,11 @@ struct snd_sof_dev; #define DSP_MBOX3_IRQ_EN BIT(3) #define DSP_MBOX4_IRQ_EN BIT(4) #define DSP_PDEBUGPC 0x013C +#define DSP_PDEBUGDATA 0x0140 +#define DSP_PDEBUGINST 0x0144 +#define DSP_PDEBUGLS0STAT 0x0148 +#define DSP_PDEBUGSTATUS 0x014C +#define DSP_PFAULTINFO 0x0150 #define ADSP_CK_EN 0x1000 #define CORE_CLK_EN BIT(0) #define COREDBG_EN BIT(1) diff --git a/sound/soc/sof/mediatek/mt8195/mt8195-clk.c b/sound/soc/sof/mediatek/mt8195/mt8195-clk.c index 9ef08e43aa38..7cffcad00f9b 100644 --- a/sound/soc/sof/mediatek/mt8195/mt8195-clk.c +++ b/sound/soc/sof/mediatek/mt8195/mt8195-clk.c @@ -7,7 +7,6 @@ // Hardware interface for mt8195 DSP clock #include <linux/clk.h> -#include <linux/pm_runtime.h> #include <linux/io.h> #include "mt8195.h" #include "mt8195-clk.h" diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c index 42bae574c87a..7d6a568556ea 100644 --- a/sound/soc/sof/mediatek/mt8195/mt8195.c +++ b/sound/soc/sof/mediatek/mt8195/mt8195.c @@ -49,47 +49,13 @@ static int mt8195_send_msg(struct snd_sof_dev *sdev, return mtk_adsp_ipc_send(priv->dsp_ipc, MTK_ADSP_IPC_REQ, MTK_ADSP_IPC_OP_REQ); } -static void mt8195_get_reply(struct snd_sof_dev *sdev) -{ - struct snd_sof_ipc_msg *msg = sdev->msg; - struct sof_ipc_reply reply; - int ret = 0; - - if (!msg) { - dev_warn(sdev->dev, "unexpected ipc interrupt\n"); - return; - } - - /* get reply */ - sof_mailbox_read(sdev, sdev->host_box.offset, &reply, sizeof(reply)); - if (reply.error < 0) { - memcpy(msg->reply_data, &reply, sizeof(reply)); - ret = reply.error; - } else { - /* reply has correct size? */ - if (reply.hdr.size != msg->reply_size) { - dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n", - msg->reply_size, reply.hdr.size); - ret = -EINVAL; - } - - /* read the message */ - if (msg->reply_size > 0) - sof_mailbox_read(sdev, sdev->host_box.offset, - msg->reply_data, msg->reply_size); - } - - msg->reply_error = ret; -} - static void mt8195_dsp_handle_reply(struct mtk_adsp_ipc *ipc) { struct adsp_priv *priv = mtk_adsp_ipc_get_data(ipc); unsigned long flags; spin_lock_irqsave(&priv->sdev->ipc_lock, flags); - mt8195_get_reply(priv->sdev); - snd_sof_ipc_reply(priv->sdev, 0); + snd_sof_ipc_process_reply(priv->sdev, 0); spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags); } diff --git a/sound/soc/sof/nocodec.c b/sound/soc/sof/nocodec.c index b13bfdeb2b70..7c5bb9badb6c 100644 --- a/sound/soc/sof/nocodec.c +++ b/sound/soc/sof/nocodec.c @@ -32,7 +32,7 @@ static int sof_nocodec_bes_setup(struct device *dev, /* set up BE dai_links */ for (i = 0; i < link_num; i++) { - dlc = devm_kcalloc(dev, 3, sizeof(*dlc), GFP_KERNEL); + dlc = devm_kcalloc(dev, 2, sizeof(*dlc), GFP_KERNEL); if (!dlc) return -ENOMEM; @@ -44,8 +44,8 @@ static int sof_nocodec_bes_setup(struct device *dev, links[i].stream_name = links[i].name; links[i].cpus = &dlc[0]; - links[i].codecs = &dlc[1]; - links[i].platforms = &dlc[2]; + links[i].codecs = &asoc_dummy_dlc; + links[i].platforms = &dlc[1]; links[i].num_cpus = 1; links[i].num_codecs = 1; @@ -55,8 +55,6 @@ static int sof_nocodec_bes_setup(struct device *dev, links[i].no_pcm = 1; links[i].cpus->dai_name = drv[i].name; links[i].platforms->name = dev_name(dev->parent); - links[i].codecs->dai_name = "snd-soc-dummy-dai"; - links[i].codecs->name = "snd-soc-dummy"; if (drv[i].playback.channels_min) links[i].dpcm_playback = 1; if (drv[i].capture.channels_min) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index d0ab6f390734..d778717cab10 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -369,7 +369,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component, case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_STOP: /* invoke platform trigger to stop DMA even if pcm_ops isn't set or if it failed */ - if (!pcm_ops || (pcm_ops && !pcm_ops->platform_stop_during_hw_free)) + if (!pcm_ops || !pcm_ops->platform_stop_during_hw_free) snd_sof_pcm_platform_trigger(sdev, substream, cmd); break; default: diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 2b232442e84b..704b21413c71 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -234,20 +234,16 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) pm_state.event = target_state; - /* Skip to platform-specific suspend if DSP is entering D0 */ - if (target_state == SOF_DSP_PM_D0) { - sof_fw_trace_suspend(sdev, pm_state); - /* Notify clients not managed by pm framework about core suspend */ - sof_suspend_clients(sdev, pm_state); - goto suspend; - } - /* suspend DMA trace */ sof_fw_trace_suspend(sdev, pm_state); /* Notify clients not managed by pm framework about core suspend */ sof_suspend_clients(sdev, pm_state); + /* Skip to platform-specific suspend if DSP is entering D0 */ + if (target_state == SOF_DSP_PM_D0) + goto suspend; + #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) /* cache debugfs contents during runtime suspend */ if (runtime_suspend) diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 1cbda595c518..e7ef77012c35 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -14,6 +14,20 @@ #include "sof-of-dev.h" #include "ops.h" +static bool is_virtual_widget(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget, + const char *func) +{ + switch (widget->id) { + case snd_soc_dapm_out_drv: + case snd_soc_dapm_output: + case snd_soc_dapm_input: + dev_dbg(sdev->dev, "%s: %s is a virtual widget\n", func, widget->name); + return true; + default: + return false; + } +} + static void sof_reset_route_setup_status(struct snd_sof_dev *sdev, struct snd_sof_widget *widget) { const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); @@ -231,23 +245,9 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc bool route_found = false; /* ignore routes involving virtual widgets in topology */ - switch (src_widget->id) { - case snd_soc_dapm_out_drv: - case snd_soc_dapm_output: - case snd_soc_dapm_input: + if (is_virtual_widget(sdev, src_widget->widget, __func__) || + is_virtual_widget(sdev, sink_widget->widget, __func__)) return 0; - default: - break; - } - - switch (sink_widget->id) { - case snd_soc_dapm_out_drv: - case snd_soc_dapm_output: - case snd_soc_dapm_input: - return 0; - default: - break; - } /* find route matching source and sink widgets */ list_for_each_entry(sroute, &sdev->route_list, list) @@ -396,6 +396,9 @@ sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widg const struct sof_ipc_tplg_widget_ops *widget_ops; struct snd_soc_dapm_path *p; + if (is_virtual_widget(sdev, widget, __func__)) + return; + /* skip if the widget is in use or if it is already unprepared */ if (!swidget || !swidget->prepared || swidget->use_count > 0) goto sink_unprepare; @@ -433,6 +436,9 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget struct snd_soc_dapm_path *p; int ret; + if (is_virtual_widget(sdev, widget, __func__)) + return 0; + widget_ops = tplg_ops ? tplg_ops->widget : NULL; if (!widget_ops) return 0; @@ -488,6 +494,9 @@ static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dap int err; int ret = 0; + if (is_virtual_widget(sdev, widget, __func__)) + return 0; + if (widget->dobj.private) { err = sof_widget_free(sdev, widget->dobj.private); if (err < 0) @@ -527,6 +536,9 @@ static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_d struct snd_soc_dapm_path *p; int ret; + if (is_virtual_widget(sdev, widget, __func__)) + return 0; + if (swidget) { int i; @@ -592,6 +604,9 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, return 0; for_each_dapm_widgets(list, i, widget) { + if (is_virtual_widget(sdev, widget, __func__)) + continue; + /* starting widget for playback is AIF type */ if (dir == SNDRV_PCM_STREAM_PLAYBACK && widget->id != snd_soc_dapm_aif_in) continue; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index a090a9eb4828..5d5eeb1a1a6f 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -362,6 +362,7 @@ struct snd_sof_control { size_t priv_size; /* size of private data */ size_t max_size; void *ipc_control_data; + void *old_ipc_control_data; int max; /* applicable to volume controls */ u32 size; /* cdata size */ u32 *volume_table; /* volume table computed from tlv data*/ diff --git a/sound/soc/sof/sof-client-ipc-kernel-injector.c b/sound/soc/sof/sof-client-ipc-kernel-injector.c new file mode 100644 index 000000000000..ad0ed2d570a9 --- /dev/null +++ b/sound/soc/sof/sof-client-ipc-kernel-injector.c @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2023 Google Inc. All rights reserved. +// +// Author: Curtis Malainey <cujomalainey@chromium.org> +// + +#include <linux/auxiliary_bus.h> +#include <linux/debugfs.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <sound/sof/header.h> + +#include "sof-client.h" + +#define SOF_IPC_CLIENT_SUSPEND_DELAY_MS 3000 + +struct sof_msg_inject_priv { + struct dentry *kernel_dfs_file; + size_t max_msg_size; + + void *kernel_buffer; +}; + +static int sof_msg_inject_dfs_open(struct inode *inode, struct file *file) +{ + int ret = debugfs_file_get(file->f_path.dentry); + + if (unlikely(ret)) + return ret; + + ret = simple_open(inode, file); + if (ret) + debugfs_file_put(file->f_path.dentry); + + return ret; +} + +static ssize_t sof_kernel_msg_inject_dfs_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct sof_client_dev *cdev = file->private_data; + struct sof_msg_inject_priv *priv = cdev->data; + struct sof_ipc_cmd_hdr *hdr = priv->kernel_buffer; + struct device *dev = &cdev->auxdev.dev; + ssize_t size; + int ret; + + if (*ppos) + return 0; + + size = simple_write_to_buffer(priv->kernel_buffer, priv->max_msg_size, + ppos, buffer, count); + if (size < 0) + return size; + if (size != count) + return -EFAULT; + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0 && ret != -EACCES) { + dev_err_ratelimited(dev, "debugfs write failed to resume %d\n", ret); + return ret; + } + + sof_client_ipc_rx_message(cdev, hdr, priv->kernel_buffer); + + pm_runtime_mark_last_busy(dev); + ret = pm_runtime_put_autosuspend(dev); + if (ret < 0) + dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", ret); + + return count; +}; + +static int sof_msg_inject_dfs_release(struct inode *inode, struct file *file) +{ + debugfs_file_put(file->f_path.dentry); + + return 0; +} + +static const struct file_operations sof_kernel_msg_inject_fops = { + .open = sof_msg_inject_dfs_open, + .write = sof_kernel_msg_inject_dfs_write, + .release = sof_msg_inject_dfs_release, + + .owner = THIS_MODULE, +}; + +static int sof_msg_inject_probe(struct auxiliary_device *auxdev, + const struct auxiliary_device_id *id) +{ + struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); + struct dentry *debugfs_root = sof_client_get_debugfs_root(cdev); + struct device *dev = &auxdev->dev; + struct sof_msg_inject_priv *priv; + size_t alloc_size; + + /* allocate memory for client data */ + priv = devm_kzalloc(&auxdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->max_msg_size = sof_client_get_ipc_max_payload_size(cdev); + alloc_size = priv->max_msg_size; + priv->kernel_buffer = devm_kmalloc(dev, alloc_size, GFP_KERNEL); + + if (!priv->kernel_buffer) + return -ENOMEM; + + cdev->data = priv; + + priv->kernel_dfs_file = debugfs_create_file("kernel_ipc_msg_inject", 0644, + debugfs_root, cdev, + &sof_kernel_msg_inject_fops); + + /* enable runtime PM */ + pm_runtime_set_autosuspend_delay(dev, SOF_IPC_CLIENT_SUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_idle(dev); + + return 0; +} + +static void sof_msg_inject_remove(struct auxiliary_device *auxdev) +{ + struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); + struct sof_msg_inject_priv *priv = cdev->data; + + pm_runtime_disable(&auxdev->dev); + + debugfs_remove(priv->kernel_dfs_file); +} + +static const struct auxiliary_device_id sof_msg_inject_client_id_table[] = { + { .name = "snd_sof.kernel_injector" }, + {}, +}; +MODULE_DEVICE_TABLE(auxiliary, sof_msg_inject_client_id_table); + +/* + * No need for driver pm_ops as the generic pm callbacks in the auxiliary bus + * type are enough to ensure that the parent SOF device resumes to bring the DSP + * back to D0. + * Driver name will be set based on KBUILD_MODNAME. + */ +static struct auxiliary_driver sof_msg_inject_client_drv = { + .probe = sof_msg_inject_probe, + .remove = sof_msg_inject_remove, + + .id_table = sof_msg_inject_client_id_table, +}; + +module_auxiliary_driver(sof_msg_inject_client_drv); + +MODULE_DESCRIPTION("SOF IPC Kernel Injector Client Driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(SND_SOC_SOF_CLIENT); diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index d6b7caa0cf03..284de96e779c 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -16,6 +16,7 @@ #include "ops.h" #include "sof-client.h" #include "sof-priv.h" +#include "ipc3-priv.h" #include "ipc4-priv.h" /** @@ -126,6 +127,29 @@ static inline int sof_register_ipc_msg_injector(struct snd_sof_dev *sdev) static inline void sof_unregister_ipc_msg_injector(struct snd_sof_dev *sdev) {} #endif /* CONFIG_SND_SOC_SOF_DEBUG_IPC_MSG_INJECTOR */ +#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_KERNEL_INJECTOR) +static int sof_register_ipc_kernel_injector(struct snd_sof_dev *sdev) +{ + /* Only IPC3 supported right now */ + if (sdev->pdata->ipc_type != SOF_IPC) + return 0; + + return sof_client_dev_register(sdev, "kernel_injector", 0, NULL, 0); +} + +static void sof_unregister_ipc_kernel_injector(struct snd_sof_dev *sdev) +{ + sof_client_dev_unregister(sdev, "kernel_injector", 0); +} +#else +static inline int sof_register_ipc_kernel_injector(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline void sof_unregister_ipc_kernel_injector(struct snd_sof_dev *sdev) {} +#endif /* CONFIG_SND_SOC_SOF_DEBUG_IPC_KERNEL_INJECTOR */ + int sof_register_clients(struct snd_sof_dev *sdev) { int ret; @@ -146,6 +170,12 @@ int sof_register_clients(struct snd_sof_dev *sdev) goto err_msg_injector; } + ret = sof_register_ipc_kernel_injector(sdev); + if (ret) { + dev_err(sdev->dev, "IPC kernel injector client registration failed\n"); + goto err_kernel_injector; + } + /* Platform depndent client device registration */ if (sof_ops(sdev) && sof_ops(sdev)->register_ipc_clients) @@ -154,6 +184,9 @@ int sof_register_clients(struct snd_sof_dev *sdev) if (!ret) return 0; + sof_unregister_ipc_kernel_injector(sdev); + +err_kernel_injector: sof_unregister_ipc_msg_injector(sdev); err_msg_injector: @@ -167,6 +200,7 @@ void sof_unregister_clients(struct snd_sof_dev *sdev) if (sof_ops(sdev) && sof_ops(sdev)->unregister_ipc_clients) sof_ops(sdev)->unregister_ipc_clients(sdev); + sof_unregister_ipc_kernel_injector(sdev); sof_unregister_ipc_msg_injector(sdev); sof_unregister_ipc_flood_test(sdev); } @@ -269,6 +303,24 @@ int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg, } EXPORT_SYMBOL_NS_GPL(sof_client_ipc_tx_message, SND_SOC_SOF_CLIENT); +int sof_client_ipc_rx_message(struct sof_client_dev *cdev, void *ipc_msg, void *msg_buf) +{ + if (cdev->sdev->pdata->ipc_type == SOF_IPC) { + struct sof_ipc_cmd_hdr *hdr = ipc_msg; + + if (hdr->size < sizeof(hdr)) { + dev_err(cdev->sdev->dev, "The received message size is invalid\n"); + return -EINVAL; + } + + sof_ipc3_do_rx_work(cdev->sdev, ipc_msg, msg_buf); + return 0; + } + + return -EOPNOTSUPP; +} +EXPORT_SYMBOL_NS_GPL(sof_client_ipc_rx_message, SND_SOC_SOF_CLIENT); + int sof_client_ipc_set_get_data(struct sof_client_dev *cdev, void *ipc_msg, bool set) { diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index 10571d1ea9a7..b6ccc2cd69e5 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -75,5 +75,6 @@ int sof_client_register_fw_state_handler(struct sof_client_dev *cdev, sof_client_fw_state_callback callback); void sof_client_unregister_fw_state_handler(struct sof_client_dev *cdev); enum sof_fw_state sof_client_get_fw_state(struct sof_client_dev *cdev); +int sof_client_ipc_rx_message(struct sof_client_dev *cdev, void *ipc_msg, void *msg_buf); #endif /* __SOC_SOF_CLIENT_H */ diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index cd4f6ac126ec..d4f6702e93dc 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -48,6 +48,9 @@ struct snd_sof_pcm_stream; #define SOF_DBG_FORCE_NOCODEC BIT(10) /* ignore all codec-related * configurations */ +#define SOF_DBG_DUMP_IPC_MESSAGE_PAYLOAD BIT(11) /* On top of the IPC message header + * dump the message payload also + */ #define SOF_DBG_DSPLESS_MODE BIT(15) /* Do not initialize and use the DSP */ /* Flag definitions used for controlling the DSP dump behavior */ diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index f160dc454b44..698129dccc7d 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1077,7 +1077,7 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp, list_for_each_entry(rtd, &card->rtd_list, list) { /* does stream match DAI link ? */ if (!rtd->dai_link->stream_name || - strcmp(w->sname, rtd->dai_link->stream_name)) + !strstr(rtd->dai_link->stream_name, w->sname)) continue; for_each_rtd_cpu_dais(rtd, i, cpu_dai) { |