diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h index 0519afd7217f13..10389154c20cad 100644 --- a/include/sound/soc-acpi.h +++ b/include/sound/soc-acpi.h @@ -74,6 +74,9 @@ static inline struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg) * @subsystem_id_set: true if a value has been written to * subsystem_vendor and subsystem_device. * @bt_link_mask: BT offload link enabled on the board + * @card_name: optional card name to be used by the machine driver + * @dai_type_mask: optional mask of the DAI types the machine driver is + * restricted to, all DAI types are used when it is not set */ struct snd_soc_acpi_mach_params { u32 acpi_ipc_irq_index; @@ -90,6 +93,8 @@ struct snd_soc_acpi_mach_params { unsigned short subsystem_rev; bool subsystem_id_set; u32 bt_link_mask; + const char *card_name; + u32 dai_type_mask; }; /** diff --git a/include/sound/sof.h b/include/sound/sof.h index 38d6c8cb5e832b..20c39a49e68888 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -17,6 +17,7 @@ struct snd_sof_dsp_ops; struct snd_sof_dev; +struct sof_audio_ops; /** * enum sof_fw_state - DSP firmware state definitions @@ -57,6 +58,22 @@ enum sof_ipc_type { SOF_IPC_TYPE_COUNT }; +/* mailbox descriptor, used for host <-> DSP IPC */ +struct snd_sof_mailbox { + size_t size; + u32 offset; +}; + +/* DSP mailbox types */ +enum snd_sof_mailbox_type { + SOF_MAILBOX_FW_INFO, + SOF_MAILBOX_DSP, + SOF_MAILBOX_HOST, + SOF_MAILBOX_STREAM, + SOF_MAILBOX_DEBUG, + SOF_MAILBOX_COUNT +}; + struct sof_loadable_file_profile { enum sof_ipc_type ipc_type; @@ -171,6 +188,7 @@ struct sof_dev_desc { const char *default_fw_filename[SOF_IPC_TYPE_COUNT]; const struct snd_sof_dsp_ops *ops; + const struct sof_audio_ops *audio_ops; int (*ops_init)(struct snd_sof_dev *sdev); void (*ops_free)(struct snd_sof_dev *sdev); }; diff --git a/include/trace/events/sof.h b/include/trace/events/sof.h index 3061423c066701..62eeccdce9de65 100644 --- a/include/trace/events/sof.h +++ b/include/trace/events/sof.h @@ -12,79 +12,7 @@ #define _TRACE_SOF_H #include #include -#include -#include "../../../sound/soc/sof/sof-audio.h" - -DECLARE_EVENT_CLASS(sof_widget_template, - TP_PROTO(struct snd_sof_widget *swidget), - TP_ARGS(swidget), - TP_STRUCT__entry( - __string(name, swidget->widget->name) - __field(int, use_count) - ), - TP_fast_assign( - __assign_str(name); - __entry->use_count = swidget->use_count; - ), - TP_printk("name=%s use_count=%d", __get_str(name), __entry->use_count) -); - -DEFINE_EVENT(sof_widget_template, sof_widget_setup, - TP_PROTO(struct snd_sof_widget *swidget), - TP_ARGS(swidget) -); - -DEFINE_EVENT(sof_widget_template, sof_widget_free, - TP_PROTO(struct snd_sof_widget *swidget), - TP_ARGS(swidget) -); - -TRACE_EVENT(sof_ipc3_period_elapsed_position, - TP_PROTO(struct snd_sof_dev *sdev, struct sof_ipc_stream_posn *posn), - TP_ARGS(sdev, posn), - TP_STRUCT__entry( - __string(device_name, dev_name(sdev->dev)) - __field(u64, host_posn) - __field(u64, dai_posn) - __field(u64, wallclock) - ), - TP_fast_assign( - __assign_str(device_name); - __entry->host_posn = posn->host_posn; - __entry->dai_posn = posn->dai_posn; - __entry->wallclock = posn->wallclock; - ), - TP_printk("device_name=%s host_posn=%#llx dai_posn=%#llx wallclock=%#llx", - __get_str(device_name), __entry->host_posn, __entry->dai_posn, - __entry->wallclock) -); - -TRACE_EVENT(sof_pcm_pointer_position, - TP_PROTO(struct snd_sof_dev *sdev, - struct snd_sof_pcm *spcm, - struct snd_pcm_substream *substream, - snd_pcm_uframes_t dma_posn, - snd_pcm_uframes_t dai_posn - ), - TP_ARGS(sdev, spcm, substream, dma_posn, dai_posn), - TP_STRUCT__entry( - __string(device_name, dev_name(sdev->dev)) - __field(u32, pcm_id) - __field(int, stream) - __field(unsigned long, dma_posn) - __field(unsigned long, dai_posn) - ), - TP_fast_assign( - __assign_str(device_name); - __entry->pcm_id = le32_to_cpu(spcm->pcm.pcm_id); - __entry->stream = substream->stream; - __entry->dma_posn = dma_posn; - __entry->dai_posn = dai_posn; - ), - TP_printk("device_name=%s pcm_id=%d stream=%d dma_posn=%lu dai_posn=%lu", - __get_str(device_name), __entry->pcm_id, __entry->stream, - __entry->dma_posn, __entry->dai_posn) -); +#include "../../../sound/soc/sof/sof-priv.h" TRACE_EVENT(sof_stream_position_ipc_rx, TP_PROTO(struct device *dev), diff --git a/include/trace/events/sof_audio.h b/include/trace/events/sof_audio.h new file mode 100644 index 00000000000000..8c2b50b7024a7f --- /dev/null +++ b/include/trace/events/sof_audio.h @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright(c) 2022 Intel Corporation + * + * Author: Noah Klayman + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM sof + +#if !defined(_TRACE_SOF_AUDIO_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_SOF_AUDIO_H +#include +#include +#include +#include "../../../sound/soc/sof/sof-audio.h" + +DECLARE_EVENT_CLASS(sof_widget_template, + TP_PROTO(struct snd_sof_widget *swidget), + TP_ARGS(swidget), + TP_STRUCT__entry( + __string(name, swidget->widget->name) + __field(int, use_count) + ), + TP_fast_assign( + __assign_str(name); + __entry->use_count = swidget->use_count; + ), + TP_printk("name=%s use_count=%d", __get_str(name), __entry->use_count) +); + +DEFINE_EVENT(sof_widget_template, sof_widget_setup, + TP_PROTO(struct snd_sof_widget *swidget), + TP_ARGS(swidget) +); + +DEFINE_EVENT(sof_widget_template, sof_widget_free, + TP_PROTO(struct snd_sof_widget *swidget), + TP_ARGS(swidget) +); + +TRACE_EVENT(sof_ipc3_period_elapsed_position, + TP_PROTO(struct snd_sof_dev *sdev, struct sof_ipc_stream_posn *posn), + TP_ARGS(sdev, posn), + TP_STRUCT__entry( + __string(device_name, dev_name(sdev->dev)) + __field(u64, host_posn) + __field(u64, dai_posn) + __field(u64, wallclock) + ), + TP_fast_assign( + __assign_str(device_name); + __entry->host_posn = posn->host_posn; + __entry->dai_posn = posn->dai_posn; + __entry->wallclock = posn->wallclock; + ), + TP_printk("device_name=%s host_posn=%#llx dai_posn=%#llx wallclock=%#llx", + __get_str(device_name), __entry->host_posn, __entry->dai_posn, + __entry->wallclock) +); + +TRACE_EVENT(sof_pcm_pointer_position, + TP_PROTO(struct snd_sof_dev *sdev, + struct snd_sof_pcm *spcm, + struct snd_pcm_substream *substream, + snd_pcm_uframes_t dma_posn, + snd_pcm_uframes_t dai_posn + ), + TP_ARGS(sdev, spcm, substream, dma_posn, dai_posn), + TP_STRUCT__entry( + __string(device_name, dev_name(sdev->dev)) + __field(u32, pcm_id) + __field(int, stream) + __field(unsigned long, dma_posn) + __field(unsigned long, dai_posn) + ), + TP_fast_assign( + __assign_str(device_name); + __entry->pcm_id = le32_to_cpu(spcm->pcm.pcm_id); + __entry->stream = substream->stream; + __entry->dma_posn = dma_posn; + __entry->dai_posn = dai_posn; + ), + TP_printk("device_name=%s pcm_id=%d stream=%d dma_posn=%lu dai_posn=%lu", + __get_str(device_name), __entry->pcm_id, __entry->stream, + __entry->dma_posn, __entry->dai_posn) +); + +#endif /* _TRACE_SOF_AUDIO_H */ + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE sof_audio +#include diff --git a/sound/soc/intel/boards/skl_hda_dsp_generic.c b/sound/soc/intel/boards/skl_hda_dsp_generic.c index 519218385fdf74..7d75f9a4cdb27f 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_generic.c +++ b/sound/soc/intel/boards/skl_hda_dsp_generic.c @@ -109,7 +109,8 @@ static int skl_hda_audio_probe(struct platform_device *pdev) if (!card) return -ENOMEM; - card->name = "hda-dsp"; + card->name = mach->mach_params.card_name ? + mach->mach_params.card_name : "hda-dsp"; card->owner = THIS_MODULE; card->fully_routed = true; card->late_probe = skl_hda_card_late_probe; @@ -128,6 +129,8 @@ static int skl_hda_audio_probe(struct platform_device *pdev) if (mach->mach_params.codec_mask & IDISP_CODEC_MASK) ctx->hdmi.idisp_codec = true; + ctx->dmic_be_num = mach->mach_params.dmic_num; + ctx->link_order_overwrite = HDA_LINK_ORDER; ctx->link_id_overwrite = HDA_LINK_IDS; diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 25688780d7ef3f..4ba529c570bcf3 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -1485,7 +1485,8 @@ static int mc_probe(struct platform_device *pdev) ctx->codec_info_list_count = asoc_sdw_get_codec_info_list_count(); card = &ctx->card; card->dev = &pdev->dev; - card->name = "soundwire"; + card->name = mach->mach_params.card_name ? + mach->mach_params.card_name : "soundwire"; card->owner = THIS_MODULE; card->late_probe = sof_sdw_card_late_probe; card->add_dai_link = sof_sdw_add_dai_link; diff --git a/sound/soc/intel/common/sof-function-topology-lib.c b/sound/soc/intel/common/sof-function-topology-lib.c index 1935a537cdefec..2da0d217bf613d 100644 --- a/sound/soc/intel/common/sof-function-topology-lib.c +++ b/sound/soc/intel/common/sof-function-topology-lib.c @@ -183,6 +183,11 @@ int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_ int ret; int i; + if (!mach || !mach->sof_tplg_filename) { + dev_err(card->dev, "Missing base topology filename for function topology\n"); + return -EINVAL; + } + ret = get_platform_name(card, mach, platform); if (ret < 0) return ret; @@ -228,10 +233,7 @@ int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_ dev_dbg(card->dev, "dai_link %s is not supported by separated tplg yet\n", dai_link->name); - if (best_effort) - continue; - - return 0; + continue; } if (tplg_mask & BIT(tplg_dev)) continue; diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c index d4074309fa81c2..7d5e07176b82d8 100644 --- a/sound/soc/sdw_utils/soc_sdw_utils.c +++ b/sound/soc/sdw_utils/soc_sdw_utils.c @@ -82,7 +82,10 @@ struct asoc_sdw_codec_info codec_info_list[] = { .direction = {true, false}, .dai_name = "tac5xx2-aif1", .dai_type = SOC_SDW_DAI_TYPE_AMP, - .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID}, + .dailink = { + SOC_SDW_AMP_OUT_DAI_ID, + SOC_SDW_UNUSED_DAI_ID, + }, .init = asoc_sdw_ti_amp_init, .rtd_init = asoc_sdw_ti_tac5xx2_spk_rtd_init, .controls = lr_spk_controls, @@ -103,7 +106,10 @@ struct asoc_sdw_codec_info codec_info_list[] = { .direction = {true, true}, .dai_name = "tac5xx2-aif3", .dai_type = SOC_SDW_DAI_TYPE_JACK, - .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID}, + .dailink = { + SOC_SDW_JACK_OUT_DAI_ID, + SOC_SDW_JACK_IN_DAI_ID, + }, .controls = generic_jack_controls, .num_controls = ARRAY_SIZE(generic_jack_controls), .widgets = generic_jack_widgets, @@ -1860,6 +1866,29 @@ static int is_sdca_aux_dev_present(struct device *dev, return ret; } +/* + * Check if a codec device has any endpoints matching the dai_type_mask. + * Returns true if no filtering (mask == 0) or at least one endpoint matches. + */ +static bool adr_dev_matches_dai_type_mask(const struct snd_soc_acpi_adr_device *adr_dev, + struct asoc_sdw_codec_info *codec_info, + u32 dai_type_mask) +{ + int j; + + if (!dai_type_mask) + return true; + + for (j = 0; j < adr_dev->num_endpoints; j++) { + int ep_num = adr_dev->endpoints[j].num; + + if (ep_num < codec_info->dai_num && + (dai_type_mask & BIT(codec_info->dais[ep_num].dai_type))) + return true; + } + return false; +} + int asoc_sdw_count_sdw_endpoints(struct snd_soc_card *card, int *num_devs, int *num_ends, int *num_aux) { @@ -1882,6 +1911,11 @@ int asoc_sdw_count_sdw_endpoints(struct snd_soc_card *card, if (!codec_info) return -EINVAL; + /* Skip aux devices for codecs with no matching endpoints */ + if (!adr_dev_matches_dai_type_mask(adr_dev, codec_info, + mach_params->dai_type_mask)) + continue; + for (j = 0; j < codec_info->aux_num; j++) { ret = is_sdca_aux_dev_present(dev, codec_info->auxs[j].codec_name, adr_link, i); @@ -2045,29 +2079,34 @@ int asoc_sdw_parse_sdw_endpoints(struct device *dev, if (!codec_info) return -EINVAL; - for (j = 0; j < codec_info->aux_num; j++) { - struct snd_soc_component *component; + /* Skip aux devices for codecs with no matching endpoints */ + if (adr_dev_matches_dai_type_mask(adr_dev, codec_info, + mach_params->dai_type_mask)) { + for (j = 0; j < codec_info->aux_num; j++) { + struct snd_soc_component *component; + const char *aux_name = codec_info->auxs[j].codec_name; - ret = is_sdca_aux_dev_present(dev, codec_info->auxs[j].codec_name, - adr_link, i); - if (ret < 0) - return ret; + ret = is_sdca_aux_dev_present(dev, aux_name, adr_link, i); + if (ret < 0) + return ret; - if (ret == 0) - continue; + if (ret == 0) + continue; - component = snd_soc_lookup_component_by_name(codec_info->auxs[j].codec_name); - if (component) { - dev_dbg(dev, "%s found component %s for aux name %s\n", - __func__, component->name, - codec_info->auxs[j].codec_name); - soc_aux->dlc.name = component->name; - } else { - dev_dbg(dev, "%s the aux component %s is not registered yet\n", - __func__, codec_info->auxs[j].codec_name); - return -EPROBE_DEFER; + component = snd_soc_lookup_component_by_name(aux_name); + if (component) { + dev_dbg(dev, + "found component %s for aux name %s\n", + component->name, aux_name); + soc_aux->dlc.name = component->name; + } else { + dev_dbg(dev, + "aux component %s is not registered yet\n", + aux_name); + return -EPROBE_DEFER; + } + soc_aux++; } - soc_aux++; } ctx->ignore_internal_dmic |= codec_info->ignore_internal_dmic; @@ -2091,6 +2130,14 @@ int asoc_sdw_parse_sdw_endpoints(struct device *dev, adr_end = &adr_dev->endpoints[j]; dai_info = &codec_info->dais[adr_end->num]; + + /* Filter by DAI type if mask is set */ + if (mach_params->dai_type_mask && + !(mach_params->dai_type_mask & BIT(dai_info->dai_type))) { + (*num_devs)--; + continue; + } + soc_dai = asoc_sdw_find_dailink(soc_dais, adr_end); /* diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index fb8e2c0244128e..63c4129db8f34e 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1833,6 +1833,7 @@ static void soc_check_tplg_fes(struct snd_soc_card *card) { struct snd_soc_component *component; struct snd_soc_dai_link *dai_link; + struct snd_soc_dai_link_component *cpu; int i; for_each_component(component) { @@ -1867,11 +1868,35 @@ static void soc_check_tplg_fes(struct snd_soc_card *card) continue; } - if (component->dev->of_node) + if (component->dev->of_node) { dai_link->platforms->of_node = component->dev->of_node; - else + } else { + struct snd_soc_dai *dai; + int j; + dai_link->platforms->name = component->name; + /* + * Override CPU component name to ensure the + * correct component is matched when multiple + * components provide identically-named DAIs. + * Only override when the DAI is actually + * provided by this component. + */ + for_each_link_cpus(dai_link, j, cpu) { + if (cpu->of_node) + continue; + + for_each_component_dais(component, dai) { + if (cpu->dai_name && + !strcmp(dai->name, cpu->dai_name)) { + cpu->name = component->name; + break; + } + } + } + } + /* convert non BE into BE */ dai_link->no_pcm = 1; diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index f73a0e534851c8..3f5b541a592ba6 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -286,6 +286,7 @@ endif ## SND_SOC_SOF_DEVELOPER_SUPPORT config SND_SOC_SOF tristate select SND_SOC_TOPOLOGY + select SND_SOC_SOF_CLIENT select SND_SOC_SOF_NOCODEC if SND_SOC_SOF_NOCODEC_SUPPORT select SND_SOC_SOF_NOCODEC if SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT help diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index 887abf70da3ba1..f23a5f7b3ea61a 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -1,19 +1,14 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) -snd-sof-y := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\ - control.o trace.o iomem-utils.o sof-audio.o stream-ipc.o\ - fw-file-profile.o debug-dsp-ops.o +snd-sof-y := core.o ops.o loader.o ipc.o pm.o debug.o trace.o iomem-utils.o\ + stream-ipc.o fw-file-profile.o debug-dsp-ops.o # IPC implementations ifneq ($(CONFIG_SND_SOC_SOF_IPC3),) -snd-sof-y += ipc3.o ipc3-loader.o ipc3-topology.o ipc3-control.o ipc3-pcm.o\ - ipc3-dtrace.o -snd-sof-$(CONFIG_SND_SOC_SOF_COMPRESS) += ipc3-compress.o +snd-sof-y += ipc3.o ipc3-loader.o ipc3-dtrace.o endif ifneq ($(CONFIG_SND_SOC_SOF_IPC4),) -snd-sof-y += ipc4.o ipc4-loader.o ipc4-topology.o ipc4-control.o ipc4-pcm.o\ - ipc4-mtrace.o ipc4-telemetry.o -snd-sof-$(CONFIG_SND_SOC_SOF_COMPRESS) += ipc4-compress.o +snd-sof-y += ipc4.o ipc4-loader.o ipc4-mtrace.o ipc4-telemetry.o endif # SOF client support @@ -28,6 +23,17 @@ snd-sof-of-y := sof-of-dev.o snd-sof-ipc-flood-test-y := sof-client-ipc-flood-test.o snd-sof-ipc-msg-injector-y := sof-client-ipc-msg-injector.o snd-sof-ipc-kernel-injector-y := sof-client-ipc-kernel-injector.o +snd-sof-audio-y := sof-client-audio.o pcm.o topology.o control.o sof-audio.o +ifneq ($(CONFIG_SND_SOC_SOF_IPC3),) +snd-sof-audio-y += sof-client-audio-ipc3.o ipc3-topology.o ipc3-control.o\ + ipc3-pcm.o +snd-sof-audio-$(CONFIG_SND_SOC_SOF_COMPRESS) += ipc3-compress.o +endif +ifneq ($(CONFIG_SND_SOC_SOF_IPC4),) +snd-sof-audio-y += sof-client-audio-ipc4.o ipc4-topology.o ipc4-control.o\ + ipc4-pcm.o +snd-sof-audio-$(CONFIG_SND_SOC_SOF_COMPRESS) += ipc4-compress.o +endif snd-sof-probes-y := sof-client-probes.o ifneq ($(CONFIG_SND_SOC_SOF_IPC3),) snd-sof-probes-y += sof-client-probes-ipc3.o @@ -42,6 +48,7 @@ snd-sof-nocodec-y := nocodec.o snd-sof-utils-y := sof-utils.o obj-$(CONFIG_SND_SOC_SOF) += snd-sof.o +obj-$(CONFIG_SND_SOC_SOF) += snd-sof-audio.o obj-$(CONFIG_SND_SOC_SOF_NOCODEC) += snd-sof-nocodec.o obj-$(CONFIG_SND_SOC_SOF) += snd-sof-utils.o diff --git a/sound/soc/sof/amd/acp-common.c b/sound/soc/sof/amd/acp-common.c index df656cdc152773..2ce4ac45a25b1a 100644 --- a/sound/soc/sof/amd/acp-common.c +++ b/sound/soc/sof/amd/acp-common.c @@ -12,6 +12,7 @@ #include "../sof-priv.h" #include "../sof-audio.h" +#include "../sof-client.h" #include "../ops.h" #include "acp.h" #include "acp-dsp-offset.h" @@ -225,23 +226,15 @@ const struct snd_sof_dsp_ops sof_acp_common_ops = { .get_window_offset = acp_sof_ipc_get_window_offset, .irq_thread = acp_sof_ipc_irq_thread, - /* stream callbacks */ - .pcm_open = acp_pcm_open, - .pcm_close = acp_pcm_close, - .pcm_hw_params = acp_pcm_hw_params, - .pcm_pointer = acp_pcm_pointer, - - .hw_info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, - /* Machine driver callbacks */ .machine_select = amd_sof_machine_select, .machine_register = sof_machine_register, .machine_unregister = sof_machine_unregister, + /* audio client */ + .register_audio_client = sof_register_audio_client, + .unregister_audio_client = sof_unregister_audio_client, + /* Trace Logger */ .trace_init = acp_sof_trace_init, .trace_release = acp_sof_trace_release, @@ -264,5 +257,6 @@ EXPORT_SYMBOL_NS(sof_acp_common_ops, "SND_SOC_SOF_AMD_COMMON"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("ACP SOF COMMON Driver"); MODULE_IMPORT_NS("SND_SOC_SOF_AMD_COMMON"); +MODULE_IMPORT_NS("SND_SOC_SOF_CLIENT"); MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA"); MODULE_IMPORT_NS("SOUNDWIRE_AMD_INIT"); diff --git a/sound/soc/sof/amd/acp-pcm.c b/sound/soc/sof/amd/acp-pcm.c index 2802684f26de17..a16a40c6b59c3d 100644 --- a/sound/soc/sof/amd/acp-pcm.c +++ b/sound/soc/sof/amd/acp-pcm.c @@ -16,10 +16,12 @@ #include "acp.h" #include "acp-dsp-offset.h" -int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, +int acp_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_sof_platform_stream_params *platform_params) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_pcm_runtime *runtime = substream->runtime; struct acp_dsp_stream *stream = runtime->private_data; unsigned int buf_offset, index; @@ -54,8 +56,10 @@ int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substr } EXPORT_SYMBOL_NS(acp_pcm_hw_params, "SND_SOC_SOF_AMD_COMMON"); -int acp_pcm_open(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream) +int acp_pcm_open(struct snd_soc_component *component, struct snd_sof_pcm *spcm, + struct snd_pcm_substream *substream) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct acp_dsp_stream *stream; stream = acp_dsp_stream_get(sdev, 0); @@ -69,8 +73,10 @@ int acp_pcm_open(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream) } EXPORT_SYMBOL_NS(acp_pcm_open, "SND_SOC_SOF_AMD_COMMON"); -int acp_pcm_close(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream) +int acp_pcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct acp_dsp_stream *stream; stream = substream->runtime->private_data; @@ -86,24 +92,16 @@ int acp_pcm_close(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream) } EXPORT_SYMBOL_NS(acp_pcm_close, "SND_SOC_SOF_AMD_COMMON"); -snd_pcm_uframes_t acp_pcm_pointer(struct snd_sof_dev *sdev, +snd_pcm_uframes_t acp_pcm_pointer(struct snd_soc_component *component, + struct snd_sof_pcm *spcm, struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_soc_component *scomp = sdev->component; + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_sof_pcm_stream *stream; struct sof_ipc_stream_posn posn; - struct snd_sof_pcm *spcm; snd_pcm_uframes_t pos; int ret; - spcm = snd_sof_find_spcm_dai(scomp, rtd); - if (!spcm) { - dev_warn_ratelimited(sdev->dev, "warn: can't find PCM with DAI ID %d\n", - rtd->dai_link->id); - return 0; - } - stream = &spcm->stream[substream->stream]; ret = snd_sof_ipc_msg_data(sdev, stream, &posn, sizeof(posn)); if (ret < 0) { diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index 1cd9904c2908f2..eaffb947aef22d 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -332,12 +332,14 @@ int acp_dsp_stream_put(struct snd_sof_dev *sdev, struct acp_dsp_stream *acp_stre /* * DSP PCM Operations. */ -int acp_pcm_open(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); -int acp_pcm_close(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); -int acp_pcm_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, +int acp_pcm_open(struct snd_soc_component *component, struct snd_sof_pcm *spcm, + struct snd_pcm_substream *substream); +int acp_pcm_close(struct snd_soc_component *component, struct snd_pcm_substream *substream); +int acp_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_sof_platform_stream_params *platform_params); -snd_pcm_uframes_t acp_pcm_pointer(struct snd_sof_dev *sdev, +snd_pcm_uframes_t acp_pcm_pointer(struct snd_soc_component *component, + struct snd_sof_pcm *spcm, struct snd_pcm_substream *substream); extern const struct snd_sof_dsp_ops sof_acp_common_ops; diff --git a/sound/soc/sof/amd/acp63.c b/sound/soc/sof/amd/acp63.c index a686620b13588b..10dc2113f45546 100644 --- a/sound/soc/sof/amd/acp63.c +++ b/sound/soc/sof/amd/acp63.c @@ -130,13 +130,28 @@ static struct snd_soc_dai_driver acp63_sof_dai[] = { struct snd_sof_dsp_ops sof_acp63_ops; EXPORT_SYMBOL_NS(sof_acp63_ops, "SND_SOC_SOF_AMD_COMMON"); +static const struct sof_audio_ops sof_acp63_audio_ops = { + .pcm_open = acp_pcm_open, + .pcm_close = acp_pcm_close, + .pcm_hw_params = acp_pcm_hw_params, + .pcm_pointer = acp_pcm_pointer, + + .drv = acp63_sof_dai, + .num_drv = ARRAY_SIZE(acp63_sof_dai), + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, +}; + int sof_acp63_ops_init(struct snd_sof_dev *sdev) { /* common defaults */ memcpy(&sof_acp63_ops, &sof_acp_common_ops, sizeof(struct snd_sof_dsp_ops)); - sof_acp63_ops.drv = acp63_sof_dai; - sof_acp63_ops.num_drv = ARRAY_SIZE(acp63_sof_dai); + sdev->audio_ops = &sof_acp63_audio_ops; return 0; } diff --git a/sound/soc/sof/amd/acp70.c b/sound/soc/sof/amd/acp70.c index 8314ac4008dae1..ab9f57048fde82 100644 --- a/sound/soc/sof/amd/acp70.c +++ b/sound/soc/sof/amd/acp70.c @@ -130,13 +130,28 @@ static struct snd_soc_dai_driver acp70_sof_dai[] = { struct snd_sof_dsp_ops sof_acp70_ops; EXPORT_SYMBOL_NS(sof_acp70_ops, "SND_SOC_SOF_AMD_COMMON"); +static const struct sof_audio_ops sof_acp70_audio_ops = { + .pcm_open = acp_pcm_open, + .pcm_close = acp_pcm_close, + .pcm_hw_params = acp_pcm_hw_params, + .pcm_pointer = acp_pcm_pointer, + + .drv = acp70_sof_dai, + .num_drv = ARRAY_SIZE(acp70_sof_dai), + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, +}; + int sof_acp70_ops_init(struct snd_sof_dev *sdev) { /* common defaults */ memcpy(&sof_acp70_ops, &sof_acp_common_ops, sizeof(struct snd_sof_dsp_ops)); - sof_acp70_ops.drv = acp70_sof_dai; - sof_acp70_ops.num_drv = ARRAY_SIZE(acp70_sof_dai); + sdev->audio_ops = &sof_acp70_audio_ops; return 0; } diff --git a/sound/soc/sof/amd/rembrandt.c b/sound/soc/sof/amd/rembrandt.c index 86ef59743fc875..50351b4ddf6949 100644 --- a/sound/soc/sof/amd/rembrandt.c +++ b/sound/soc/sof/amd/rembrandt.c @@ -130,13 +130,28 @@ static struct snd_soc_dai_driver rembrandt_sof_dai[] = { struct snd_sof_dsp_ops sof_rembrandt_ops; EXPORT_SYMBOL_NS(sof_rembrandt_ops, "SND_SOC_SOF_AMD_COMMON"); +static const struct sof_audio_ops sof_rembrandt_audio_ops = { + .pcm_open = acp_pcm_open, + .pcm_close = acp_pcm_close, + .pcm_hw_params = acp_pcm_hw_params, + .pcm_pointer = acp_pcm_pointer, + + .drv = rembrandt_sof_dai, + .num_drv = ARRAY_SIZE(rembrandt_sof_dai), + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, +}; + int sof_rembrandt_ops_init(struct snd_sof_dev *sdev) { /* common defaults */ memcpy(&sof_rembrandt_ops, &sof_acp_common_ops, sizeof(struct snd_sof_dsp_ops)); - sof_rembrandt_ops.drv = rembrandt_sof_dai; - sof_rembrandt_ops.num_drv = ARRAY_SIZE(rembrandt_sof_dai); + sdev->audio_ops = &sof_rembrandt_audio_ops; return 0; } diff --git a/sound/soc/sof/amd/renoir.c b/sound/soc/sof/amd/renoir.c index b3b4639abf5065..203bcb3610bdf1 100644 --- a/sound/soc/sof/amd/renoir.c +++ b/sound/soc/sof/amd/renoir.c @@ -105,13 +105,28 @@ static struct snd_soc_dai_driver renoir_sof_dai[] = { struct snd_sof_dsp_ops sof_renoir_ops; EXPORT_SYMBOL_NS(sof_renoir_ops, "SND_SOC_SOF_AMD_COMMON"); +static const struct sof_audio_ops sof_renoir_audio_ops = { + .pcm_open = acp_pcm_open, + .pcm_close = acp_pcm_close, + .pcm_hw_params = acp_pcm_hw_params, + .pcm_pointer = acp_pcm_pointer, + + .drv = renoir_sof_dai, + .num_drv = ARRAY_SIZE(renoir_sof_dai), + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, +}; + int sof_renoir_ops_init(struct snd_sof_dev *sdev) { /* common defaults */ memcpy(&sof_renoir_ops, &sof_acp_common_ops, sizeof(struct snd_sof_dsp_ops)); - sof_renoir_ops.drv = renoir_sof_dai; - sof_renoir_ops.num_drv = ARRAY_SIZE(renoir_sof_dai); + sdev->audio_ops = &sof_renoir_audio_ops; return 0; } diff --git a/sound/soc/sof/amd/vangogh.c b/sound/soc/sof/amd/vangogh.c index 6ed5f9aaa414e6..9057ed9b39e91c 100644 --- a/sound/soc/sof/amd/vangogh.c +++ b/sound/soc/sof/amd/vangogh.c @@ -151,6 +151,22 @@ static int sof_vangogh_post_fw_run_delay(struct snd_sof_dev *sdev) struct snd_sof_dsp_ops sof_vangogh_ops; EXPORT_SYMBOL_NS(sof_vangogh_ops, "SND_SOC_SOF_AMD_COMMON"); +static const struct sof_audio_ops sof_vangogh_audio_ops = { + .pcm_open = acp_pcm_open, + .pcm_close = acp_pcm_close, + .pcm_hw_params = acp_pcm_hw_params, + .pcm_pointer = acp_pcm_pointer, + + .drv = vangogh_sof_dai, + .num_drv = ARRAY_SIZE(vangogh_sof_dai), + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, +}; + int sof_vangogh_ops_init(struct snd_sof_dev *sdev) { const struct dmi_system_id *dmi_id; @@ -159,8 +175,7 @@ int sof_vangogh_ops_init(struct snd_sof_dev *sdev) /* common defaults */ memcpy(&sof_vangogh_ops, &sof_acp_common_ops, sizeof(struct snd_sof_dsp_ops)); - sof_vangogh_ops.drv = vangogh_sof_dai; - sof_vangogh_ops.num_drv = ARRAY_SIZE(vangogh_sof_dai); + sdev->audio_ops = &sof_vangogh_audio_ops; dmi_id = dmi_first_match(acp_sof_quirk_table); if (dmi_id) { diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 74d997a4f6203c..d2780ce45819c6 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -21,8 +21,7 @@ int snd_sof_volume_get(struct snd_kcontrol *kcontrol, struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value; struct snd_sof_control *scontrol = sm->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_component_get_tplg_ops(scomp); if (tplg_ops && tplg_ops->control && tplg_ops->control->volume_get) return tplg_ops->control->volume_get(scontrol, ucontrol); @@ -36,8 +35,7 @@ int snd_sof_volume_put(struct snd_kcontrol *kcontrol, struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value; struct snd_sof_control *scontrol = sm->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_component_get_tplg_ops(scomp); if (tplg_ops && tplg_ops->control && tplg_ops->control->volume_put) return tplg_ops->control->volume_put(scontrol, ucontrol); @@ -73,8 +71,7 @@ int snd_sof_switch_get(struct snd_kcontrol *kcontrol, struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value; struct snd_sof_control *scontrol = sm->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_component_get_tplg_ops(scomp); if (tplg_ops && tplg_ops->control && tplg_ops->control->switch_get) return tplg_ops->control->switch_get(scontrol, ucontrol); @@ -88,8 +85,7 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol, struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value; struct snd_sof_control *scontrol = sm->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_component_get_tplg_ops(scomp); if (tplg_ops && tplg_ops->control && tplg_ops->control->switch_put) return tplg_ops->control->switch_put(scontrol, ucontrol); @@ -103,8 +99,7 @@ int snd_sof_enum_get(struct snd_kcontrol *kcontrol, struct soc_enum *se = (struct soc_enum *)kcontrol->private_value; struct snd_sof_control *scontrol = se->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_component_get_tplg_ops(scomp); if (tplg_ops && tplg_ops->control && tplg_ops->control->enum_get) return tplg_ops->control->enum_get(scontrol, ucontrol); @@ -118,8 +113,7 @@ int snd_sof_enum_put(struct snd_kcontrol *kcontrol, struct soc_enum *se = (struct soc_enum *)kcontrol->private_value; struct snd_sof_control *scontrol = se->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_component_get_tplg_ops(scomp); if (tplg_ops && tplg_ops->control && tplg_ops->control->enum_put) return tplg_ops->control->enum_put(scontrol, ucontrol); @@ -133,8 +127,7 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_component_get_tplg_ops(scomp); if (tplg_ops && tplg_ops->control && tplg_ops->control->bytes_get) return tplg_ops->control->bytes_get(scontrol, ucontrol); @@ -148,8 +141,7 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_component_get_tplg_ops(scomp); if (tplg_ops && tplg_ops->control && tplg_ops->control->bytes_put) return tplg_ops->control->bytes_put(scontrol, ucontrol); @@ -164,8 +156,7 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_component_get_tplg_ops(scomp); /* make sure we have at least a header */ if (size < sizeof(struct snd_ctl_tlv)) @@ -183,8 +174,8 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _ struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_component_get_tplg_ops(scomp); int ret, err; /* ignore the ext_volatile_get call if the callbacks are not provided */ @@ -219,8 +210,7 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_component_get_tplg_ops(scomp); if (tplg_ops && tplg_ops->control && tplg_ops->control->bytes_ext_get) return tplg_ops->control->bytes_ext_get(scontrol, binary_data, size); diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 1f38fe03e49395..4398d544e34dc9 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -472,9 +472,6 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) if (sdev->dspless_mode_selected) { sof_set_fw_state(sdev, SOF_DSPLESS_MODE); - /* set up platform component driver */ - snd_sof_new_platform_drv(sdev); - goto skip_dsp_init; } @@ -499,9 +496,6 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) goto ipc_err; } - /* set up platform component driver after initializing the IPC ops */ - snd_sof_new_platform_drv(sdev); - /* * skip loading/booting firmware and registering the machine driver when DSP OPS testing * is enabled with IPC4. Normal audio operations will be unavailable in this mode. @@ -554,27 +548,10 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) /* hereafter all FW boot flows are for PM reasons */ sdev->first_boot = false; - /* now register audio DSP platform driver and dai */ - ret = devm_snd_soc_register_component(sdev->dev, &sdev->plat_drv, - sof_ops(sdev)->drv, - sof_ops(sdev)->num_drv); - if (ret < 0) { - dev_err(sdev->dev, - "error: failed to register DSP DAI driver %d\n", ret); - goto fw_trace_err; - } - - ret = snd_sof_machine_register(sdev, plat_data); - if (ret < 0) { - dev_err(sdev->dev, - "error: failed to register machine driver %d\n", ret); - goto fw_trace_err; - } - ret = sof_register_clients(sdev); if (ret < 0) { dev_err(sdev->dev, "failed to register clients %d\n", ret); - goto sof_machine_err; + goto fw_trace_err; } /* @@ -592,8 +569,6 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) return 0; -sof_machine_err: - snd_sof_machine_unregister(sdev, plat_data); fw_trace_err: sof_fw_trace_free(sdev); fw_run_err: @@ -648,6 +623,13 @@ sof_apply_profile_override(struct sof_loadable_file_profile *path_override, } } +static void sof_cleanup_client_ops_srcu(void *data) +{ + struct snd_sof_dev *sdev = data; + + cleanup_srcu_struct(&sdev->client_ops_srcu); +} + int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) { struct snd_sof_dev *sdev; @@ -678,26 +660,29 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) sof_apply_profile_override(&plat_data->ipc_file_profile_base, plat_data); + sdev->audio_ops = plat_data->desc->audio_ops; + /* Initialize sof_ops based on the initial selected IPC version */ ret = sof_init_sof_ops(sdev); if (ret) return ret; - INIT_LIST_HEAD(&sdev->pcm_list); - INIT_LIST_HEAD(&sdev->kcontrol_list); - INIT_LIST_HEAD(&sdev->widget_list); - INIT_LIST_HEAD(&sdev->pipeline_list); - INIT_LIST_HEAD(&sdev->dai_list); - INIT_LIST_HEAD(&sdev->dai_link_list); - INIT_LIST_HEAD(&sdev->route_list); INIT_LIST_HEAD(&sdev->ipc_client_list); - INIT_LIST_HEAD(&sdev->ipc_rx_handler_list); - INIT_LIST_HEAD(&sdev->fw_state_handler_list); + INIT_LIST_HEAD(&sdev->client_ops_list); + + ret = init_srcu_struct(&sdev->client_ops_srcu); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, sof_cleanup_client_ops_srcu, sdev); + if (ret) + return ret; + spin_lock_init(&sdev->ipc_lock); spin_lock_init(&sdev->hw_lock); mutex_init(&sdev->power_state_access); mutex_init(&sdev->ipc_client_mutex); - mutex_init(&sdev->client_event_handler_mutex); + mutex_init(&sdev->client_ops_mutex); mutex_init(&sdev->dsp_fw_boot_mutex); /* set default timeouts if none provided */ @@ -750,7 +735,6 @@ EXPORT_SYMBOL(snd_sof_device_probe_completed); int snd_sof_device_remove(struct device *dev) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); - struct snd_sof_pdata *pdata = sdev->pdata; int ret; bool aborted = false; @@ -763,13 +747,6 @@ int snd_sof_device_remove(struct device *dev) */ sof_unregister_clients(sdev); - /* - * Unregister machine driver. This will unbind the snd_card which - * will remove the component driver and unload the topology - * before freeing the snd_card. - */ - snd_sof_machine_unregister(sdev, pdata); - /* * Balance the runtime pm usage count in case we are faced with an * exception and we forcably prevented D3 power state to preserve diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index 9801c875dd7018..908462fec20c05 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -355,8 +355,6 @@ int snd_sof_dbg_init(struct snd_sof_dev *sdev) (char **)&plat_data->tplg_filename_prefix); debugfs_create_str("fw_name", 0444, fw_profile, (char **)&plat_data->fw_filename); - debugfs_create_str("tplg_name", 0444, fw_profile, - (char **)&plat_data->tplg_filename); debugfs_create_u32("ipc_type", 0444, fw_profile, (u32 *)&plat_data->ipc_type); diff --git a/sound/soc/sof/imx/imx-common.c b/sound/soc/sof/imx/imx-common.c index 7a03c8cc5dd473..65daa45fda5fb6 100644 --- a/sound/soc/sof/imx/imx-common.c +++ b/sound/soc/sof/imx/imx-common.c @@ -12,6 +12,7 @@ #include #include "../ops.h" +#include "../sof-client.h" #include "imx-common.h" @@ -455,10 +456,11 @@ const struct snd_sof_dsp_ops sof_imx_ops = { .get_bar_index = imx_get_bar_index, .load_firmware = snd_sof_load_firmware_memcpy, - .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, + /* audio client */ + .register_audio_client = sof_register_audio_client, + .unregister_audio_client = sof_unregister_audio_client, - .pcm_open = sof_stream_pcm_open, - .pcm_close = sof_stream_pcm_close, + .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, .runtime_suspend = imx_runtime_suspend, .runtime_resume = imx_runtime_resume, @@ -466,15 +468,9 @@ const struct snd_sof_dsp_ops sof_imx_ops = { .resume = imx_resume, .set_power_state = imx_set_power_state, - - .hw_info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_BATCH | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, }; EXPORT_SYMBOL(sof_imx_ops); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("SOF helpers for IMX platforms"); +MODULE_IMPORT_NS("SND_SOC_SOF_CLIENT"); diff --git a/sound/soc/sof/imx/imx-common.h b/sound/soc/sof/imx/imx-common.h index 9bd711dbb5d03f..8c21b3a652d068 100644 --- a/sound/soc/sof/imx/imx-common.h +++ b/sound/soc/sof/imx/imx-common.h @@ -100,8 +100,7 @@ struct imx_chip_info { /* does the chip have a reserved memory region for DMA? */ bool has_dma_reserved; struct imx_memory_info *memory; - struct snd_soc_dai_driver *drv; - int num_drv; + const struct sof_audio_ops *audio_ops; /* optional */ const struct imx_chip_ops *ops; }; diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c index 7e9eab2e303472..12baa41c52413b 100644 --- a/sound/soc/sof/imx/imx8.c +++ b/sound/soc/sof/imx/imx8.c @@ -270,6 +270,37 @@ static struct snd_soc_dai_driver imx8ulp_dai[] = { IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai6", 1, 32), }; +#define SOF_IMX_HW_INFO (SNDRV_PCM_INFO_MMAP | \ + SNDRV_PCM_INFO_MMAP_VALID | \ + SNDRV_PCM_INFO_INTERLEAVED | \ + SNDRV_PCM_INFO_PAUSE | \ + SNDRV_PCM_INFO_BATCH | \ + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP) + +static const struct sof_audio_ops sof_imx8_audio_ops = { + .pcm_open = sof_stream_pcm_open, + .pcm_close = sof_stream_pcm_close, + .drv = imx8_dai, + .num_drv = ARRAY_SIZE(imx8_dai), + .hw_info = SOF_IMX_HW_INFO, +}; + +static const struct sof_audio_ops sof_imx8m_audio_ops = { + .pcm_open = sof_stream_pcm_open, + .pcm_close = sof_stream_pcm_close, + .drv = imx8m_dai, + .num_drv = ARRAY_SIZE(imx8m_dai), + .hw_info = SOF_IMX_HW_INFO, +}; + +static const struct sof_audio_ops sof_imx8ulp_audio_ops = { + .pcm_open = sof_stream_pcm_open, + .pcm_close = sof_stream_pcm_close, + .drv = imx8ulp_dai, + .num_drv = ARRAY_SIZE(imx8ulp_dai), + .hw_info = SOF_IMX_HW_INFO, +}; + static struct snd_sof_dsp_ops sof_imx8_ops; static int imx8_ops_init(struct snd_sof_dev *sdev) @@ -283,9 +314,7 @@ static int imx8_ops_init(struct snd_sof_dev *sdev) sof_imx8_ops.debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem; - /* ... and finally set DAI driver */ - sof_imx8_ops.drv = get_chip_info(sdev)->drv; - sof_imx8_ops.num_drv = get_chip_info(sdev)->num_drv; + sdev->audio_ops = get_chip_info(sdev)->audio_ops; return 0; } @@ -339,8 +368,7 @@ static const struct imx_chip_info imx8_chip_info = { .window_offset = 0x800000, }, .memory = imx8_memory_regions, - .drv = imx8_dai, - .num_drv = ARRAY_SIZE(imx8_dai), + .audio_ops = &sof_imx8_audio_ops, .ops = &imx8_chip_ops, }; @@ -351,8 +379,7 @@ static const struct imx_chip_info imx8x_chip_info = { .window_offset = 0x800000, }, .memory = imx8_memory_regions, - .drv = imx8_dai, - .num_drv = ARRAY_SIZE(imx8_dai), + .audio_ops = &sof_imx8_audio_ops, .ops = &imx8x_chip_ops, }; @@ -363,8 +390,7 @@ static const struct imx_chip_info imx8m_chip_info = { .window_offset = 0x800000, }, .memory = imx8m_memory_regions, - .drv = imx8m_dai, - .num_drv = ARRAY_SIZE(imx8m_dai), + .audio_ops = &sof_imx8m_audio_ops, .ops = &imx8m_chip_ops, }; @@ -376,8 +402,7 @@ static const struct imx_chip_info imx8ulp_chip_info = { }, .has_dma_reserved = true, .memory = imx8ulp_memory_regions, - .drv = imx8ulp_dai, - .num_drv = ARRAY_SIZE(imx8ulp_dai), + .audio_ops = &sof_imx8ulp_audio_ops, .ops = &imx8ulp_chip_ops, }; diff --git a/sound/soc/sof/imx/imx9.c b/sound/soc/sof/imx/imx9.c index e56e8a1c80222e..117cbd71bc4bc7 100644 --- a/sound/soc/sof/imx/imx9.c +++ b/sound/soc/sof/imx/imx9.c @@ -14,6 +14,19 @@ static struct snd_soc_dai_driver imx95_dai[] = { IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai3", 1, 32), }; +static const struct sof_audio_ops sof_imx95_audio_ops = { + .pcm_open = sof_stream_pcm_open, + .pcm_close = sof_stream_pcm_close, + .drv = imx95_dai, + .num_drv = ARRAY_SIZE(imx95_dai), + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, +}; + static struct snd_sof_dsp_ops sof_imx9_ops; static int imx95_ops_init(struct snd_sof_dev *sdev) @@ -21,9 +34,7 @@ static int imx95_ops_init(struct snd_sof_dev *sdev) /* first copy from template */ memcpy(&sof_imx9_ops, &sof_imx_ops, sizeof(sof_imx_ops)); - /* ... and finally set DAI driver */ - sof_imx9_ops.drv = get_chip_info(sdev)->drv; - sof_imx9_ops.num_drv = get_chip_info(sdev)->num_drv; + sdev->audio_ops = get_chip_info(sdev)->audio_ops; return 0; } @@ -74,8 +85,7 @@ static const struct imx_chip_info imx95_chip_info = { }, .has_dma_reserved = true, .memory = imx95_memory_regions, - .drv = imx95_dai, - .num_drv = ARRAY_SIZE(imx95_dai), + .audio_ops = &sof_imx95_audio_ops, .ops = &imx95_chip_ops, }; diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index 753da09301fa17..1e9f01c4ae4b62 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -80,7 +80,7 @@ int sof_apl_ops_init(struct snd_sof_dev *sdev) } /* set DAI driver ops */ - hda_set_dai_drv_ops(sdev, &sof_apl_ops); + hda_set_dai_drv_ops(sdev); /* debug */ sof_apl_ops.debug_map = apl_dsp_debugfs; diff --git a/sound/soc/sof/intel/atom.c b/sound/soc/sof/intel/atom.c index 32bf5e5e597881..7962c5abbe5c34 100644 --- a/sound/soc/sof/intel/atom.c +++ b/sound/soc/sof/intel/atom.c @@ -407,8 +407,8 @@ void atom_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; - mach_params->dai_drivers = desc->ops->drv; + mach_params->num_dai_drivers = desc->audio_ops->num_drv; + mach_params->dai_drivers = desc->audio_ops->drv; } EXPORT_SYMBOL_NS(atom_set_mach_params, "SND_SOC_SOF_INTEL_ATOM_HIFI_EP"); diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c index 9534d18be97dfe..bc78bd6858b4e3 100644 --- a/sound/soc/sof/intel/bdw.c +++ b/sound/soc/sof/intel/bdw.c @@ -19,6 +19,7 @@ #include #include #include "../ops.h" +#include "../sof-client.h" #include "shim.h" #include "../sof-acpi-dev.h" #include "../sof-audio.h" @@ -530,8 +531,8 @@ static void bdw_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; - mach_params->dai_drivers = desc->ops->drv; + mach_params->num_dai_drivers = desc->audio_ops->num_drv; + mach_params->dai_drivers = desc->audio_ops->drv; } /* Broadwell DAIs */ @@ -560,6 +561,20 @@ static struct snd_soc_dai_driver bdw_dai[] = { }, }; +static const struct sof_audio_ops sof_bdw_audio_ops = { + .pcm_open = sof_stream_pcm_open, + .pcm_close = sof_stream_pcm_close, + + .drv = bdw_dai, + .num_drv = ARRAY_SIZE(bdw_dai), + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_BATCH, +}; + /* broadwell ops */ static const struct snd_sof_dsp_ops sof_bdw_ops = { /*Device init */ @@ -593,30 +608,19 @@ static const struct snd_sof_dsp_ops sof_bdw_ops = { .machine_unregister = sof_machine_unregister, .set_mach_params = bdw_set_mach_params, + /* audio client */ + .register_audio_client = sof_register_audio_client, + .unregister_audio_client = sof_unregister_audio_client, + /* debug */ .debug_map = bdw_debugfs, .debug_map_count = ARRAY_SIZE(bdw_debugfs), .dbg_dump = bdw_dump, .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, - /* stream callbacks */ - .pcm_open = sof_stream_pcm_open, - .pcm_close = sof_stream_pcm_close, - /*Firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, - /* DAI drivers */ - .drv = bdw_dai, - .num_drv = ARRAY_SIZE(bdw_dai), - - /* ALSA HW info flags */ - .hw_info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_BATCH, - .dsp_arch_ops = &sof_xtensa_arch_ops, }; @@ -646,6 +650,7 @@ static const struct sof_dev_desc sof_acpi_broadwell_desc = { }, .nocodec_tplg_filename = "sof-bdw-nocodec.tplg", .ops = &sof_bdw_ops, + .audio_ops = &sof_bdw_audio_ops, }; static const struct acpi_device_id sof_broadwell_match[] = { @@ -689,5 +694,6 @@ module_platform_driver(snd_sof_acpi_intel_bdw_driver); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("SOF support for Broadwell platforms"); +MODULE_IMPORT_NS("SND_SOC_SOF_CLIENT"); MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA"); MODULE_IMPORT_NS("SND_SOC_SOF_ACPI_DEV"); diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c index 18208f77b84dc6..a946266420b5cc 100644 --- a/sound/soc/sof/intel/byt.c +++ b/sound/soc/sof/intel/byt.c @@ -19,6 +19,7 @@ #include #include #include "../ops.h" +#include "../sof-client.h" #include "atom.h" #include "shim.h" #include "../sof-acpi-dev.h" @@ -213,6 +214,20 @@ static int byt_acpi_probe(struct snd_sof_dev *sdev) } /* baytrail ops */ +static const struct sof_audio_ops sof_byt_audio_ops = { + .pcm_open = sof_stream_pcm_open, + .pcm_close = sof_stream_pcm_close, + + .drv = atom_dai, + .num_drv = 3, + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_BATCH, +}; + static const struct snd_sof_dsp_ops sof_byt_ops = { /* device init */ .probe = byt_acpi_probe, @@ -250,16 +265,16 @@ static const struct snd_sof_dsp_ops sof_byt_ops = { .machine_unregister = sof_machine_unregister, .set_mach_params = atom_set_mach_params, + /* audio client */ + .register_audio_client = sof_register_audio_client, + .unregister_audio_client = sof_unregister_audio_client, + /* debug */ .debug_map = byt_debugfs, .debug_map_count = ARRAY_SIZE(byt_debugfs), .dbg_dump = atom_dump, .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, - /* stream callbacks */ - .pcm_open = sof_stream_pcm_open, - .pcm_close = sof_stream_pcm_close, - /*Firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, @@ -267,17 +282,6 @@ static const struct snd_sof_dsp_ops sof_byt_ops = { .suspend = byt_suspend, .resume = byt_resume, - /* DAI drivers */ - .drv = atom_dai, - .num_drv = 3, /* we have only 3 SSPs on byt*/ - - /* ALSA HW info flags */ - .hw_info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_BATCH, - .dsp_arch_ops = &sof_xtensa_arch_ops, }; @@ -288,6 +292,20 @@ static const struct sof_intel_dsp_desc byt_chip_info = { }; /* cherrytrail and braswell ops */ +static const struct sof_audio_ops sof_cht_audio_ops = { + .pcm_open = sof_stream_pcm_open, + .pcm_close = sof_stream_pcm_close, + + .drv = atom_dai, + .num_drv = 6, + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_BATCH, +}; + static const struct snd_sof_dsp_ops sof_cht_ops = { /* device init */ .probe = byt_acpi_probe, @@ -325,16 +343,16 @@ static const struct snd_sof_dsp_ops sof_cht_ops = { .machine_unregister = sof_machine_unregister, .set_mach_params = atom_set_mach_params, + /* audio client */ + .register_audio_client = sof_register_audio_client, + .unregister_audio_client = sof_unregister_audio_client, + /* debug */ .debug_map = cht_debugfs, .debug_map_count = ARRAY_SIZE(cht_debugfs), .dbg_dump = atom_dump, .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, - /* stream callbacks */ - .pcm_open = sof_stream_pcm_open, - .pcm_close = sof_stream_pcm_close, - /*Firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, @@ -342,18 +360,6 @@ static const struct snd_sof_dsp_ops sof_cht_ops = { .suspend = byt_suspend, .resume = byt_resume, - /* DAI drivers */ - .drv = atom_dai, - /* all 6 SSPs may be available for cherrytrail */ - .num_drv = 6, - - /* ALSA HW info flags */ - .hw_info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_BATCH, - .dsp_arch_ops = &sof_xtensa_arch_ops, }; @@ -384,6 +390,7 @@ static const struct sof_dev_desc sof_acpi_baytrailcr_desc = { }, .nocodec_tplg_filename = "sof-byt-nocodec.tplg", .ops = &sof_byt_ops, + .audio_ops = &sof_byt_audio_ops, }; static const struct sof_dev_desc sof_acpi_baytrail_desc = { @@ -406,6 +413,7 @@ static const struct sof_dev_desc sof_acpi_baytrail_desc = { }, .nocodec_tplg_filename = "sof-byt-nocodec.tplg", .ops = &sof_byt_ops, + .audio_ops = &sof_byt_audio_ops, }; static const struct sof_dev_desc sof_acpi_cherrytrail_desc = { @@ -428,6 +436,7 @@ static const struct sof_dev_desc sof_acpi_cherrytrail_desc = { }, .nocodec_tplg_filename = "sof-cht-nocodec.tplg", .ops = &sof_cht_ops, + .audio_ops = &sof_cht_audio_ops, }; static const struct acpi_device_id sof_baytrail_match[] = { @@ -475,6 +484,7 @@ module_platform_driver(snd_sof_acpi_intel_byt_driver); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("SOF support for Baytrail/Cherrytrail"); +MODULE_IMPORT_NS("SND_SOC_SOF_CLIENT"); MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA"); MODULE_IMPORT_NS("SND_SOC_SOF_ACPI_DEV"); MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_ATOM_HIFI_EP"); diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index ec33cd18e669e2..d6886242492e79 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -427,7 +427,7 @@ int sof_cnl_ops_init(struct snd_sof_dev *sdev) } /* set DAI driver ops */ - hda_set_dai_drv_ops(sdev, &sof_cnl_ops); + hda_set_dai_drv_ops(sdev); /* debug */ sof_cnl_ops.debug_map = cnl_dsp_debugfs; diff --git a/sound/soc/sof/intel/hda-common-ops.c b/sound/soc/sof/intel/hda-common-ops.c index aa91f2b6fd657b..610d05425020fc 100644 --- a/sound/soc/sof/intel/hda-common-ops.c +++ b/sound/soc/sof/intel/hda-common-ops.c @@ -11,9 +11,41 @@ */ #include "../sof-priv.h" +#include "../sof-client.h" #include "hda.h" #include "../sof-audio.h" +const struct sof_audio_ops sof_hda_audio_ops = { + .pcm_open = hda_dsp_pcm_open, + .pcm_close = hda_dsp_pcm_close, + .pcm_hw_params = hda_dsp_pcm_hw_params, + .pcm_hw_free = hda_dsp_stream_hw_free, + .pcm_trigger = hda_dsp_pcm_trigger, + .pcm_pointer = hda_dsp_pcm_pointer, + .pcm_ack = hda_dsp_pcm_ack, + + .compr_open = hda_dsp_compr_open, + .compr_hw_params = hda_dsp_compr_hw_params, + .compr_hw_free = hda_dsp_stream_compr_hw_free, + .compr_close = hda_dsp_compr_close, + .compr_trigger = hda_dsp_compr_trigger, + .compr_pointer = hda_dsp_compr_pointer, + .compr_get_dai_frame_counter = hda_dsp_compr_get_stream_llp, + + .get_dai_frame_counter = hda_dsp_get_stream_llp, + .get_host_byte_counter = hda_dsp_get_stream_ldp, + + .drv = skl_dai, + .num_drv = SOF_SKL_NUM_DAIS, + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, +}; +EXPORT_SYMBOL_NS(sof_hda_audio_ops, "SND_SOC_SOF_INTEL_HDA_GENERIC"); + const struct snd_sof_dsp_ops sof_hda_common_ops = { /* probe/remove/shutdown */ .probe_early = hda_dsp_probe_early, @@ -48,26 +80,6 @@ const struct snd_sof_dsp_ops sof_hda_common_ops = { .dbg_dump = hda_dsp_dump, .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, - /* stream callbacks */ - .pcm_open = hda_dsp_pcm_open, - .pcm_close = hda_dsp_pcm_close, - .pcm_hw_params = hda_dsp_pcm_hw_params, - .pcm_hw_free = hda_dsp_stream_hw_free, - .pcm_trigger = hda_dsp_pcm_trigger, - .pcm_pointer = hda_dsp_pcm_pointer, - .pcm_ack = hda_dsp_pcm_ack, - - .compr_open = hda_dsp_compr_open, - .compr_hw_params = hda_dsp_compr_hw_params, - .compr_hw_free = hda_dsp_stream_compr_hw_free, - .compr_close = hda_dsp_compr_close, - .compr_trigger = hda_dsp_compr_trigger, - .compr_pointer = hda_dsp_compr_pointer, - .compr_get_dai_frame_counter = hda_dsp_compr_get_stream_llp, - - .get_dai_frame_counter = hda_dsp_get_stream_llp, - .get_host_byte_counter = hda_dsp_get_stream_ldp, - /* firmware loading */ .load_firmware = snd_sof_load_firmware_raw, @@ -91,9 +103,11 @@ const struct snd_sof_dsp_ops sof_hda_common_ops = { .register_ipc_clients = hda_register_clients, .unregister_ipc_clients = hda_unregister_clients, + /* audio client */ + .register_audio_client = hda_register_audio_client, + .unregister_audio_client = hda_unregister_audio_client, + /* DAI drivers */ - .drv = skl_dai, - .num_drv = SOF_SKL_NUM_DAIS, .is_chain_dma_supported = hda_is_chain_dma_supported, /* PM */ @@ -104,13 +118,6 @@ const struct snd_sof_dsp_ops sof_hda_common_ops = { .runtime_idle = hda_dsp_runtime_idle, .set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume, - /* ALSA HW info flags */ - .hw_info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, - .dsp_arch_ops = &sof_xtensa_arch_ops, }; EXPORT_SYMBOL_NS(sof_hda_common_ops, "SND_SOC_SOF_INTEL_HDA_GENERIC"); diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index f0be42048db33c..e760b3506e0710 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -356,16 +356,57 @@ static struct hdac_ext_link *sdw_get_hlink(struct snd_sof_dev *sdev, return hdac_bus_eml_sdw_get_hlink(bus); } +/* + * Check if all CPU DAIs sharing the same pipeline (spipe) have their link + * DMA streams running. Used to gate pipeline state transitions for aggregate + * DAIs: the RUNNING IPC is deferred until the last DAI's link DMA has started. + */ +static bool hda_ipc4_all_spipe_dmas_running(struct snd_pcm_substream *substream, + struct snd_sof_pipeline *target_spipe) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *dai; + int i; + + for_each_rtd_cpu_dais(rtd, i, dai) { + struct snd_soc_dapm_widget *w; + struct snd_sof_widget *sw; + struct hdac_ext_stream *hext_stream; + + w = snd_soc_dai_get_widget(dai, substream->stream); + if (!w) + continue; + + sw = w->dobj.private; + if (!sw || sw->spipe != target_spipe) + continue; + + hext_stream = snd_soc_dai_get_dma_data(dai, substream); + if (!hext_stream || !hext_stream->hstream.running) + return false; + } + + return true; +} + 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 snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); 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; + struct sof_client_dev *cdev; int ret = 0; + if (!component) + return -ENODEV; + + cdev = snd_sof_component_get_cdev(component); + w = snd_soc_dai_get_widget(cpu_dai, substream->stream); swidget = w->dobj.private; pipe_widget = swidget->spipe->pipe_widget; @@ -383,13 +424,22 @@ static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cp case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: - ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, + /* + * For aggregate DAIs with shared pipelines, the state check + * deduplicates: the first DAI sends the IPC, subsequent DAIs + * sharing the same pipeline see it already paused and skip. + * For aggregate DAIs with different pipelines, each DAI pauses + * its own pipeline independently. + */ + if (pipeline->state == SOF_IPC4_PIPE_PAUSED) + break; + + ret = sof_ipc4_set_pipeline_state(cdev, pipe_widget->instance_id, SOF_IPC4_PIPE_PAUSED); if (ret < 0) return ret; pipeline->state = SOF_IPC4_PIPE_PAUSED; - break; default: dev_err(sdev->dev, "unknown trigger command %d\n", cmd); @@ -436,13 +486,22 @@ 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 snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); 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; + struct sof_client_dev *cdev; + int num_cpus = rtd->dai_link->num_cpus; int ret = 0; + if (!component) + return -ENODEV; + + cdev = snd_sof_component_get_cdev(component); + w = snd_soc_dai_get_widget(cpu_dai, substream->stream); swidget = w->dobj.private; pipe_widget = swidget->spipe->pipe_widget; @@ -455,8 +514,18 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c switch (cmd) { case SNDRV_PCM_TRIGGER_START: + /* + * For aggregated DAIs (num_cpus > 1), defer pipeline RUNNING + * IPC until all CPU DAIs sharing this pipeline have started + * their link DMAs via hda_trigger(). The running state of each + * DAI's HDA stream naturally tracks completion. + */ + if (num_cpus > 1 && + !hda_ipc4_all_spipe_dmas_running(substream, swidget->spipe)) + break; + if (pipeline->state != SOF_IPC4_PIPE_PAUSED) { - ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, + ret = sof_ipc4_set_pipeline_state(cdev, pipe_widget->instance_id, SOF_IPC4_PIPE_PAUSED); if (ret < 0) return ret; @@ -464,7 +533,7 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c pipeline->state = SOF_IPC4_PIPE_PAUSED; } - ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, + ret = sof_ipc4_set_pipeline_state(cdev, pipe_widget->instance_id, SOF_IPC4_PIPE_RUNNING); if (ret < 0) return ret; @@ -473,7 +542,11 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c swidget->spipe->started_count++; break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, + if (num_cpus > 1 && + !hda_ipc4_all_spipe_dmas_running(substream, swidget->spipe)) + break; + + ret = sof_ipc4_set_pipeline_state(cdev, pipe_widget->instance_id, SOF_IPC4_PIPE_RUNNING); if (ret < 0) return ret; diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index c8729b6d82bd4b..da8d4a9a532291 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -41,7 +41,7 @@ int hda_dai_config(struct snd_soc_dapm_widget *w, unsigned int flags, return 0; sdev = widget_to_sdev(w); - tplg_ops = sof_ipc_get_ops(sdev, tplg); + tplg_ops = snd_sof_component_get_tplg_ops(swidget->scomp); if (tplg_ops && tplg_ops->dai_config) { ret = tplg_ops->dai_config(sdev, swidget, flags, data); @@ -705,7 +705,7 @@ static int hda_dai_suspend(struct hdac_bus *bus) return 0; } -static void ssp_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) +static void ssp_set_dai_drv_ops(struct snd_sof_dev *sdev) { const struct sof_intel_dsp_desc *chip; int i; @@ -713,14 +713,14 @@ static void ssp_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops chip = get_chip_info(sdev->pdata); if (chip->hw_ip_version >= SOF_INTEL_ACE_2_0) { - for (i = 0; i < ops->num_drv; i++) { - if (strstr(ops->drv[i].name, "SSP")) - ops->drv[i].ops = &ssp_dai_ops; + for (i = 0; i < sdev->audio_ops->num_drv; i++) { + if (strstr(sdev->audio_ops->drv[i].name, "SSP")) + sdev->audio_ops->drv[i].ops = &ssp_dai_ops; } } } -static void dmic_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) +static void dmic_set_dai_drv_ops(struct snd_sof_dev *sdev) { const struct sof_intel_dsp_desc *chip; int i; @@ -728,35 +728,35 @@ static void dmic_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_op chip = get_chip_info(sdev->pdata); if (chip->hw_ip_version >= SOF_INTEL_ACE_2_0) { - for (i = 0; i < ops->num_drv; i++) { - if (strstr(ops->drv[i].name, "DMIC")) - ops->drv[i].ops = &dmic_dai_ops; + for (i = 0; i < sdev->audio_ops->num_drv; i++) { + if (strstr(sdev->audio_ops->drv[i].name, "DMIC")) + sdev->audio_ops->drv[i].ops = &dmic_dai_ops; } } } #else -static inline void ssp_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) {} -static inline void dmic_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) {} +static inline void ssp_set_dai_drv_ops(struct snd_sof_dev *sdev) {} +static inline void dmic_set_dai_drv_ops(struct snd_sof_dev *sdev) {} #endif /* CONFIG_SND_SOC_SOF_HDA_LINK */ -void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) +void hda_set_dai_drv_ops(struct snd_sof_dev *sdev) { int i; - for (i = 0; i < ops->num_drv; i++) { + for (i = 0; i < sdev->audio_ops->num_drv; i++) { #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) - if (strstr(ops->drv[i].name, "iDisp") || - strstr(ops->drv[i].name, "Analog") || - strstr(ops->drv[i].name, "Digital")) - ops->drv[i].ops = &hda_dai_ops; + if (strstr(sdev->audio_ops->drv[i].name, "iDisp") || + strstr(sdev->audio_ops->drv[i].name, "Analog") || + strstr(sdev->audio_ops->drv[i].name, "Digital")) + sdev->audio_ops->drv[i].ops = &hda_dai_ops; #endif } - ssp_set_dai_drv_ops(sdev, ops); - dmic_set_dai_drv_ops(sdev, ops); + ssp_set_dai_drv_ops(sdev); + dmic_set_dai_drv_ops(sdev); if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4 && !hda_use_tplg_nhlt) { struct sof_ipc4_fw_data *ipc4_data = sdev->private; diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 9343102c4b1a9f..a04c830c6a659e 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -523,34 +523,6 @@ static int hda_dsp_update_d0i3c_register(struct snd_sof_dev *sdev, u8 value) return 0; } -/* - * d0i3 streaming is enabled if all the active streams can - * work in d0i3 state and playback is enabled - */ -static bool hda_dsp_d0i3_streaming_applicable(struct snd_sof_dev *sdev) -{ - struct snd_pcm_substream *substream; - struct snd_sof_pcm *spcm; - bool playback_active = false; - int dir; - - list_for_each_entry(spcm, &sdev->pcm_list, list) { - for_each_pcm_streams(dir) { - substream = spcm->stream[dir].substream; - if (!substream || !substream->runtime) - continue; - - if (!spcm->stream[dir].d0i3_compatible) - return false; - - if (dir == SNDRV_PCM_STREAM_PLAYBACK) - playback_active = true; - } - } - - return playback_active; -} - static int hda_dsp_set_D0_state(struct snd_sof_dev *sdev, const struct sof_dsp_power_state *target_state) { @@ -593,7 +565,8 @@ static int hda_dsp_set_D0_state(struct snd_sof_dev *sdev, sdev->system_suspend_target != SOF_SUSPEND_NONE) flags = HDA_PM_NO_DMA_TRACE; - if (hda_dsp_d0i3_streaming_applicable(sdev)) + /* power gated streaming needs an active D0i3 capable playback */ + if (snd_sof_dsp_state_is_d0i3_streaming(sdev)) flags |= HDA_PM_PG_STREAMING; } else { /* prevent power gating in D0I0 */ @@ -1180,7 +1153,7 @@ void hda_dsp_d0i3_work(struct work_struct *work) int ret; /* DSP can enter D0I3 iff only D0I3-compatible streams are active */ - if (!snd_sof_dsp_only_d0i3_compatible_stream_active(sdev)) + if (!snd_sof_dsp_state_is_d0i3_compatible(sdev)) /* remain in D0I0 */ return; diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index 83efc9bdf418af..4e5bbbb0bb4504 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -98,11 +98,12 @@ u32 hda_dsp_get_bits(struct snd_sof_dev *sdev, int sample_bits) } }; -int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, +int hda_dsp_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_sof_platform_stream_params *platform_params) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct hdac_stream *hstream = substream->runtime->private_data; struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream); struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; @@ -151,11 +152,12 @@ int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, } EXPORT_SYMBOL_NS(hda_dsp_pcm_hw_params, "SND_SOC_SOF_INTEL_HDA_COMMON"); -int hda_dsp_compr_hw_params(struct snd_sof_dev *sdev, +int hda_dsp_compr_hw_params(struct snd_soc_component *component, struct snd_compr_stream *cstream, struct snd_compr_params *params, struct snd_sof_platform_stream_params *platform_params) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct hdac_stream *hstream = cstream->runtime->private_data; struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream); struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; @@ -217,8 +219,10 @@ int hda_dsp_compr_hw_params(struct snd_sof_dev *sdev, EXPORT_SYMBOL_NS(hda_dsp_compr_hw_params, "SND_SOC_SOF_INTEL_HDA_COMMON"); /* update SPIB register with appl position */ -int hda_dsp_pcm_ack(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream) +int hda_dsp_pcm_ack(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct hdac_stream *hstream = substream->runtime->private_data; struct snd_pcm_runtime *runtime = substream->runtime; ssize_t appl_pos, buf_size; @@ -239,9 +243,10 @@ int hda_dsp_pcm_ack(struct snd_sof_dev *sdev, struct snd_pcm_substream *substrea } EXPORT_SYMBOL_NS(hda_dsp_pcm_ack, "SND_SOC_SOF_INTEL_HDA_COMMON"); -int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, +int hda_dsp_pcm_trigger(struct snd_soc_component *component, struct snd_pcm_substream *substream, int cmd) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct hdac_stream *hstream = substream->runtime->private_data; struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream); @@ -249,9 +254,10 @@ int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, } EXPORT_SYMBOL_NS(hda_dsp_pcm_trigger, "SND_SOC_SOF_INTEL_HDA_COMMON"); -int hda_dsp_compr_trigger(struct snd_sof_dev *sdev, +int hda_dsp_compr_trigger(struct snd_soc_component *component, struct snd_compr_stream *cstream, int cmd) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct hdac_stream *hstream = cstream->runtime->private_data; struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream); @@ -259,23 +265,15 @@ int hda_dsp_compr_trigger(struct snd_sof_dev *sdev, } EXPORT_SYMBOL_NS(hda_dsp_compr_trigger, "SND_SOC_SOF_INTEL_HDA_COMMON"); -snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev, +snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_soc_component *component, + struct snd_sof_pcm *spcm, struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_soc_component *scomp = sdev->component; + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct hdac_stream *hstream = substream->runtime->private_data; struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; - struct snd_sof_pcm *spcm; snd_pcm_uframes_t pos; - spcm = snd_sof_find_spcm_dai(scomp, rtd); - if (!spcm) { - dev_warn_ratelimited(sdev->dev, "warn: can't find PCM with DAI ID %d\n", - rtd->dai_link->id); - return 0; - } - if (hda && !hda->no_ipc_position) { /* read position from IPC position */ pos = spcm->stream[substream->stream].posn.host_posn; @@ -291,7 +289,8 @@ snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev, } EXPORT_SYMBOL_NS(hda_dsp_pcm_pointer, "SND_SOC_SOF_INTEL_HDA_COMMON"); -int hda_dsp_compr_pointer(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream, +int hda_dsp_compr_pointer(struct snd_soc_component *component, + struct snd_compr_stream *cstream, struct snd_compr_tstamp64 *tstamp) { struct hdac_stream *hstream = cstream->runtime->private_data; @@ -305,24 +304,17 @@ int hda_dsp_compr_pointer(struct snd_sof_dev *sdev, struct snd_compr_stream *cst } EXPORT_SYMBOL_NS(hda_dsp_compr_pointer, "SND_SOC_SOF_INTEL_HDA_COMMON"); -int hda_dsp_pcm_open(struct snd_sof_dev *sdev, +int hda_dsp_pcm_open(struct snd_soc_component *component, + struct snd_sof_pcm *spcm, struct snd_pcm_substream *substream) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); const struct sof_intel_dsp_desc *chip_info = get_chip_info(sdev->pdata); - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_component *scomp = sdev->component; struct hdac_ext_stream *dsp_stream; - struct snd_sof_pcm *spcm; int direction = substream->stream; u32 flags = 0; - spcm = snd_sof_find_spcm_dai(scomp, rtd); - if (!spcm) { - dev_err(sdev->dev, "error: can't find PCM with DAI ID %d\n", rtd->dai_link->id); - return -EINVAL; - } - /* * if we want the .ack to work, we need to prevent the control from being mapped. * The status can still be mapped. @@ -431,21 +423,13 @@ int hda_dsp_pcm_open(struct snd_sof_dev *sdev, } EXPORT_SYMBOL_NS(hda_dsp_pcm_open, "SND_SOC_SOF_INTEL_HDA_COMMON"); -int hda_dsp_compr_open(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream) +int hda_dsp_compr_open(struct snd_soc_component *component, + struct snd_compr_stream *cstream) { - struct snd_soc_pcm_runtime *rtd = cstream->private_data; - struct snd_soc_component *scomp = sdev->component; + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct hdac_ext_stream *dsp_stream; - struct snd_sof_pcm *spcm; int direction = cstream->direction; - spcm = snd_sof_find_spcm_dai(scomp, rtd); - if (!spcm) { - dev_err(sdev->dev, "%s: can't find PCM with DAI ID %d\n", - __func__, rtd->dai_link->id); - return -EINVAL; - } - dsp_stream = hda_dsp_stream_get(sdev, direction, 0); if (!dsp_stream) { dev_err(sdev->dev, "%s: no stream available\n", __func__); @@ -466,9 +450,10 @@ int hda_dsp_compr_open(struct snd_sof_dev *sdev, struct snd_compr_stream *cstrea } EXPORT_SYMBOL_NS(hda_dsp_compr_open, "SND_SOC_SOF_INTEL_HDA_COMMON"); -int hda_dsp_pcm_close(struct snd_sof_dev *sdev, +int hda_dsp_pcm_close(struct snd_soc_component *component, struct snd_pcm_substream *substream) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct hdac_stream *hstream = substream->runtime->private_data; int direction = substream->stream; int ret; @@ -486,8 +471,10 @@ int hda_dsp_pcm_close(struct snd_sof_dev *sdev, } EXPORT_SYMBOL_NS(hda_dsp_pcm_close, "SND_SOC_SOF_INTEL_HDA_COMMON"); -int hda_dsp_compr_close(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream) +int hda_dsp_compr_close(struct snd_soc_component *component, + struct snd_compr_stream *cstream) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct hdac_stream *hstream = cstream->runtime->private_data; int direction = cstream->direction; int ret; diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index bdd4831e050ce1..0cad6eaf95fa4d 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -803,16 +803,20 @@ static int _hda_dsp_stream_hw_free(struct snd_sof_dev *sdev, return 0; } -int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev, +int hda_dsp_stream_hw_free(struct snd_soc_component *component, struct snd_pcm_substream *substream) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + return _hda_dsp_stream_hw_free(sdev, substream->runtime->private_data); } EXPORT_SYMBOL_NS(hda_dsp_stream_hw_free, "SND_SOC_SOF_INTEL_HDA_COMMON"); -int hda_dsp_stream_compr_hw_free(struct snd_sof_dev *sdev, +int hda_dsp_stream_compr_hw_free(struct snd_soc_component *component, struct snd_compr_stream *cstream) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + return _hda_dsp_stream_hw_free(sdev, cstream->runtime->private_data); } EXPORT_SYMBOL_NS(hda_dsp_stream_compr_hw_free, "SND_SOC_SOF_INTEL_HDA_COMMON"); @@ -855,8 +859,9 @@ hda_dsp_compr_bytes_transferred(struct hdac_stream *hstream, int direction) hstream->curr_pos += num_bytes; } -static bool hda_dsp_stream_check(struct hdac_bus *bus, u32 status) +static bool hda_dsp_stream_check(struct snd_sof_dev *sdev, u32 status) { + struct hdac_bus *bus = sof_to_bus(sdev); struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); struct hdac_stream *s; bool active = false; @@ -893,7 +898,7 @@ static bool hda_dsp_stream_check(struct hdac_bus *bus, u32 status) /* Inform ALSA only if the IPC position is not used */ if (s->substream && sof_hda->no_ipc_position) { - snd_sof_pcm_period_elapsed(s->substream); + sof_client_period_elapsed(sdev, s->substream); } else if (s->cstream) { hda_dsp_compr_bytes_transferred(s, s->cstream->direction); snd_compr_fragment_elapsed(s->cstream); @@ -922,7 +927,7 @@ irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context) status = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTSTS); /* check streams */ - active = hda_dsp_stream_check(bus, status); + active = hda_dsp_stream_check(sdev, status); /* check and clear RIRB interrupt */ if (status & AZX_INT_CTRL_EN) { @@ -1191,8 +1196,7 @@ EXPORT_SYMBOL_NS(hda_dsp_stream_get_position, "SND_SOC_SOF_INTEL_HDA_COMMON"); #define merge_u64(u32_u, u32_l) (((u64)(u32_u) << 32) | (u32_l)) -static u64 hda_dsp_get_llp(struct snd_sof_dev *sdev, - struct snd_soc_pcm_runtime *rtd, int dir) +static u64 hda_dsp_get_llp(struct snd_soc_pcm_runtime *rtd, int dir) { struct snd_soc_pcm_runtime *be_rtd = NULL; struct hdac_ext_stream *hext_stream; @@ -1245,44 +1249,41 @@ static u64 hda_dsp_get_llp(struct snd_sof_dev *sdev, /** * hda_dsp_get_stream_llp - Retrieve the LLP (Linear Link Position) of the stream - * @sdev: SOF device * @component: ASoC component * @substream: PCM substream * * Returns the raw Linear Link Position value */ -u64 hda_dsp_get_stream_llp(struct snd_sof_dev *sdev, - struct snd_soc_component *component, +u64 hda_dsp_get_stream_llp(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - return hda_dsp_get_llp(sdev, snd_soc_substream_to_rtd(substream), + return hda_dsp_get_llp(snd_soc_substream_to_rtd(substream), substream->stream); } EXPORT_SYMBOL_NS(hda_dsp_get_stream_llp, "SND_SOC_SOF_INTEL_HDA_COMMON"); /** * hda_dsp_compr_get_stream_llp - Retrieve the LLP (Linear Link Position) of the stream - * @sdev: SOF device + * @component: ASoC component * @cstream: Compress stream * * Returns the raw Linear Link Position value */ -u64 hda_dsp_compr_get_stream_llp(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream) +u64 hda_dsp_compr_get_stream_llp(struct snd_soc_component *component, + struct snd_compr_stream *cstream) { - return hda_dsp_get_llp(sdev, cstream->private_data, cstream->direction); + return hda_dsp_get_llp(cstream->private_data, cstream->direction); } EXPORT_SYMBOL_NS(hda_dsp_compr_get_stream_llp, "SND_SOC_SOF_INTEL_HDA_COMMON"); /** * hda_dsp_get_stream_ldp - Retrieve the LDP (Linear DMA Position) of the stream - * @sdev: SOF device * @component: ASoC component * @substream: PCM substream * * Returns the raw Linear Link Position value */ -u64 hda_dsp_get_stream_ldp(struct snd_sof_dev *sdev, - struct snd_soc_component *component, +u64 hda_dsp_get_stream_ldp(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct hdac_stream *hstream = substream->runtime->private_data; diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 4dbba9186b29d4..1b861bd58f8604 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -31,6 +31,8 @@ #include #include #include "../sof-audio.h" +#include "../sof-client.h" +#include "../sof-client-audio.h" #include "../sof-pci-dev.h" #include "../ops.h" #include "../ipc4-topology.h" @@ -822,7 +824,7 @@ int hda_dsp_probe(struct snd_sof_dev *sdev) #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_FORCE_IPC_POSITION) hdev->no_ipc_position = 0; #else - hdev->no_ipc_position = sof_ops(sdev)->pcm_pointer ? 1 : 0; + hdev->no_ipc_position = sdev->audio_ops && sdev->audio_ops->pcm_pointer ? 1 : 0; #endif if (sdev->dspless_mode_selected) @@ -1460,8 +1462,8 @@ void hda_set_mach_params(struct snd_soc_acpi_mach *mach, 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; + mach_params->num_dai_drivers = desc->audio_ops->num_drv; + mach_params->dai_drivers = desc->audio_ops->drv; } static int check_tplg_quirk_mask(struct snd_soc_acpi_mach *mach) @@ -1767,6 +1769,345 @@ int hda_pci_intel_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) } EXPORT_SYMBOL_NS(hda_pci_intel_probe, "SND_SOC_SOF_INTEL_HDA_GENERIC"); +static bool multi_card; +module_param(multi_card, bool, 0444); +MODULE_PARM_DESC(multi_card, "Split audio into per-function cards. Experimental."); + +static bool split_hdmi; +module_param(split_hdmi, bool, 0444); +MODULE_PARM_DESC(split_hdmi, "Split monolithic audio into analog/HDMI cards."); + +struct hda_sdw_func_card { + u32 dai_type_bit; + const char *card_name; +}; + +static const struct hda_sdw_func_card sdw_func_cards[] = { + { BIT(SOC_SDW_DAI_TYPE_JACK), "jack" }, + { BIT(SOC_SDW_DAI_TYPE_AMP), "speaker" }, + { BIT(SOC_SDW_DAI_TYPE_MIC), "mic" }, +}; + +static int hda_register_hdmi_audio_client(struct snd_sof_dev *sdev, + const struct snd_soc_acpi_mach *mach, + int card_idx) +{ + static const struct snd_soc_acpi_link_adr empty_links[] = { {} }; + struct sof_audio_client_pdata pdata; + + sof_audio_client_init_pdata(sdev, &pdata); + memcpy(&pdata.machine, mach, sizeof(pdata.machine)); + pdata.machine.mach_params.links = empty_links; + pdata.machine.mach_params.link_mask = 0; + pdata.machine.mach_params.codec_mask = BIT(HDA_IDISP_ADDR); + pdata.machine.mach_params.dmic_num = 0; + pdata.machine.mach_params.card_name = "hdmi"; + /* Always use a dedicated HDA generic HDMI card. */ + pdata.machine.get_function_tplg_files = NULL; + pdata.machine.drv_name = "skl_hda_dsp_generic"; + pdata.machine.sof_tplg_filename = "sof-hda-generic-idisp.tplg"; + + return sof_client_dev_register(sdev, "audio", card_idx, &pdata, sizeof(pdata)); +} + +/* + * Group SDW DAI types that share physical codec devices. Types on the + * same device must be on the same card because ASoC components can only + * be bound to one card. + * + * Returns the number of groups. Each group's merged DAI type bitmask + * is stored in groups[] and the representative codec name in names[]. + */ +static int hda_sdw_group_dai_types(const struct snd_soc_acpi_mach *mach, + u32 *groups, const char **names, + int max_groups) +{ + const struct snd_soc_acpi_link_adr *adr_link; + int num_groups = 0; + int i, j, k; + + for (adr_link = mach->mach_params.links; + adr_link && adr_link->num_adr; adr_link++) { + for (i = 0; i < adr_link->num_adr; i++) { + const struct snd_soc_acpi_adr_device *adr_dev = + &adr_link->adr_d[i]; + struct asoc_sdw_codec_info *codec_info; + const char *dev_name = NULL; + u32 dev_types = 0; + int merged = -1; + + codec_info = asoc_sdw_find_codec_info_part(adr_dev->adr); + if (!codec_info) + continue; + + for (j = 0; j < adr_dev->num_endpoints; j++) { + int ep_num = adr_dev->endpoints[j].num; + + if (ep_num < codec_info->dai_num) + dev_types |= + BIT(codec_info->dais[ep_num].dai_type); + } + + if (!dev_types) + continue; + + /* + * Pick a representative codec name: prefer + * name_prefix for non-amp codecs, fall back to + * the first DAI's component_name for amps. + */ + if (!codec_info->is_amp) + dev_name = codec_info->name_prefix; + else if (codec_info->dai_num && + codec_info->dais[0].component_name) + dev_name = codec_info->dais[0].component_name; + + /* Merge with any existing group that overlaps */ + for (k = 0; k < num_groups; k++) { + if (!(groups[k] & dev_types)) + continue; + + if (merged < 0) { + groups[k] |= dev_types; + if (dev_name) + names[k] = dev_name; + merged = k; + } else { + groups[merged] |= groups[k]; + groups[k] = groups[--num_groups]; + names[k] = names[num_groups]; + k--; + } + } + + if (merged < 0 && num_groups < max_groups) { + groups[num_groups] = dev_types; + names[num_groups] = dev_name; + num_groups++; + } + } + } + + return num_groups; +} + +static int hda_register_audio_client_multi(struct snd_sof_dev *sdev) +{ + static const struct snd_soc_acpi_link_adr empty_links[] = { {} }; + const struct snd_soc_acpi_mach *mach = sdev->pdata->machine; + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; + struct sof_audio_client_pdata pdata; + u32 dai_groups[ARRAY_SIZE(sdw_func_cards)]; + const char *group_names[ARRAY_SIZE(sdw_func_cards)]; + int num_groups; + int card_idx = 0; + int ret, i, j; + + if (!mach) + return -ENODEV; + + num_groups = hda_sdw_group_dai_types(mach, dai_groups, group_names, + ARRAY_SIZE(dai_groups)); + + dev_dbg(sdev->dev, "multi_card: %d SDW groups, dmic: %d, idisp: %s\n", + num_groups, mach->mach_params.dmic_num, + HDA_IDISP_CODEC(mach->mach_params.codec_mask) ? "yes" : "no"); + + sof_audio_client_init_pdata(sdev, &pdata); + + /* Create one card per SDW DAI type group */ + for (i = 0; i < num_groups; i++) { + const char *card_name; + + /* + * Prefer codec device name (e.g. "cs42l43") over the + * generic function type name (e.g. "jack"). + */ + card_name = group_names[i]; + if (!card_name) { + for (j = 0; j < ARRAY_SIZE(sdw_func_cards); j++) { + if (dai_groups[i] & sdw_func_cards[j].dai_type_bit) { + card_name = sdw_func_cards[j].card_name; + break; + } + } + } + if (!card_name) + continue; + + dev_dbg(sdev->dev, "multi_card: group %d mask %#x card %s\n", + i, dai_groups[i], card_name); + + memcpy(&pdata.machine, mach, sizeof(pdata.machine)); + pdata.machine.mach_params.dai_type_mask = dai_groups[i]; + pdata.machine.mach_params.codec_mask &= ~BIT(HDA_IDISP_ADDR); + pdata.machine.mach_params.dmic_num = 0; + pdata.machine.mach_params.card_name = card_name; + ret = sof_client_dev_register(sdev, "audio", card_idx, + &pdata, sizeof(pdata)); + if (ret) { + dev_warn(sdev->dev, + "multi_card: failed to register card %s: %d\n", + card_name, ret); + continue; + } + card_idx++; + } + + /* DMIC card */ + if (mach->mach_params.dmic_num) { + memcpy(&pdata.machine, mach, sizeof(pdata.machine)); + pdata.machine.mach_params.links = empty_links; + pdata.machine.mach_params.link_mask = 0; + pdata.machine.mach_params.codec_mask &= ~BIT(HDA_IDISP_ADDR); + pdata.machine.mach_params.card_name = "dmic"; + ret = sof_client_dev_register(sdev, "audio", card_idx, + &pdata, sizeof(pdata)); + if (ret) + dev_warn(sdev->dev, + "multi_card: failed to register card dmic: %d\n", + ret); + else + card_idx++; + } + + /* HDMI card */ + if (HDA_IDISP_CODEC(mach->mach_params.codec_mask)) { + ret = hda_register_hdmi_audio_client(sdev, mach, card_idx); + if (ret) + dev_warn(sdev->dev, + "multi_card: failed to register card hdmi: %d\n", + ret); + else + card_idx++; + } + + hdev->num_audio_clients = card_idx; + + return 0; +} + +static int hda_register_audio_client_split_hdmi(struct snd_sof_dev *sdev) +{ + const struct snd_soc_acpi_mach *mach = sdev->pdata->machine; + struct hdac_bus *bus = sof_to_bus(sdev); + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; + bool function_tplg_enabled; + bool has_analog_card; + struct sof_audio_client_pdata pdata; + int ret; + + hdev->num_audio_clients = 0; + + if (!mach) + return -ENODEV; + + function_tplg_enabled = !sdev->pdata->disable_function_topology && + mach->get_function_tplg_files; + + if (!HDA_IDISP_CODEC(mach->mach_params.codec_mask)) + return sof_register_audio_client(sdev); + + has_analog_card = HDA_EXT_CODEC(bus->codec_mask) || + mach->mach_params.link_mask || + mach->mach_params.dmic_num; + + if (!function_tplg_enabled && !has_analog_card) { + dev_info(sdev->dev, + "split_hdmi is not supported on this machine, using single card\n"); + return sof_register_audio_client(sdev); + } + + sof_audio_client_init_pdata(sdev, &pdata); + + /* Analog card: remove iDisp codec from the base machine configuration */ + memcpy(&pdata.machine, mach, sizeof(pdata.machine)); + pdata.machine.mach_params.codec_mask &= ~BIT(HDA_IDISP_ADDR); + pdata.machine.mach_params.card_name = NULL; + if (function_tplg_enabled) { + pdata.machine.sof_tplg_filename = mach->sof_tplg_filename ? + mach->sof_tplg_filename : sdev->pdata->tplg_filename; + } else { + pdata.machine.get_function_tplg_files = NULL; + pdata.machine.sof_tplg_filename = NULL; + } + ret = sof_client_dev_register(sdev, "audio", 0, &pdata, sizeof(pdata)); + if (ret) { + dev_warn(sdev->dev, + "split_hdmi: failed to register analog card: %d\n", ret); + return sof_register_audio_client(sdev); + } + + ret = hda_register_hdmi_audio_client(sdev, mach, 1); + if (ret) { + dev_warn(sdev->dev, + "split_hdmi: failed to register HDMI card: %d, using single card\n", ret); + sof_client_dev_unregister(sdev, "audio", 0); + return sof_register_audio_client(sdev); + } + + hdev->num_audio_clients = 2; + + return 0; +} + +int hda_register_audio_client(struct snd_sof_dev *sdev) +{ + const struct snd_soc_acpi_mach *mach = sdev->pdata->machine; + + if (multi_card) { + if (mach && !sdev->pdata->disable_function_topology && + mach->get_function_tplg_files) + return hda_register_audio_client_multi(sdev); + + dev_warn(sdev->dev, + "multi_card is only supported with function topologies, using single card\n"); + } + + if (split_hdmi) + return hda_register_audio_client_split_hdmi(sdev); + + return sof_register_audio_client(sdev); +} + +void hda_unregister_audio_client(struct snd_sof_dev *sdev) +{ + const struct snd_soc_acpi_mach *mach = sdev->pdata->machine; + + if (multi_card) { + if (mach && !sdev->pdata->disable_function_topology && + mach->get_function_tplg_files) { + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; + int i; + + for (i = hdev->num_audio_clients - 1; i >= 0; i--) + sof_client_dev_unregister(sdev, "audio", i); + hdev->num_audio_clients = 0; + return; + } + } + + if (split_hdmi && mach && HDA_IDISP_CODEC(mach->mach_params.codec_mask)) { + struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; + int i; + + /* + * split_hdmi may fall back to single-card registration. In that case, + * use the legacy single-client unregister path. + */ + if (hdev->num_audio_clients <= 1) + goto single_client_unregister; + + for (i = hdev->num_audio_clients - 1; i >= 0; i--) + sof_client_dev_unregister(sdev, "audio", i); + hdev->num_audio_clients = 0; + return; + } + +single_client_unregister: + sof_unregister_audio_client(sdev); +} + int hda_register_clients(struct snd_sof_dev *sdev) { return hda_probes_register(sdev); @@ -1783,6 +2124,7 @@ MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV"); MODULE_IMPORT_NS("SND_SOC_SOF_HDA_AUDIO_CODEC"); MODULE_IMPORT_NS("SND_SOC_SOF_HDA_AUDIO_CODEC_I915"); MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA"); +MODULE_IMPORT_NS("SND_SOC_SOF_CLIENT"); MODULE_IMPORT_NS("SND_INTEL_SOUNDWIRE_ACPI"); MODULE_IMPORT_NS("SOUNDWIRE_INTEL_INIT"); MODULE_IMPORT_NS("SOUNDWIRE_INTEL"); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 701578ec35cf4d..79be085e0dbdda 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -580,6 +580,9 @@ struct sof_intel_hda_dev { * is received from the DSP for the previous message) */ struct snd_sof_ipc_msg *delayed_ipc_tx_msg; + + /* number of audio client devices registered in multi-card mode */ + int num_audio_clients; }; static inline struct hdac_bus *sof_to_bus(struct snd_sof_dev *s) @@ -667,35 +670,41 @@ u32 hda_get_interface_mask(struct snd_sof_dev *sdev); */ u32 hda_dsp_get_mult_div(struct snd_sof_dev *sdev, int rate); u32 hda_dsp_get_bits(struct snd_sof_dev *sdev, int sample_bits); -int hda_dsp_pcm_open(struct snd_sof_dev *sdev, +int hda_dsp_pcm_open(struct snd_soc_component *component, + struct snd_sof_pcm *spcm, struct snd_pcm_substream *substream); -int hda_dsp_pcm_close(struct snd_sof_dev *sdev, +int hda_dsp_pcm_close(struct snd_soc_component *component, struct snd_pcm_substream *substream); -int hda_dsp_pcm_hw_params(struct snd_sof_dev *sdev, +int hda_dsp_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_sof_platform_stream_params *platform_params); -int hda_dsp_stream_hw_free(struct snd_sof_dev *sdev, +int hda_dsp_stream_hw_free(struct snd_soc_component *component, struct snd_pcm_substream *substream); -int hda_dsp_pcm_trigger(struct snd_sof_dev *sdev, +int hda_dsp_pcm_trigger(struct snd_soc_component *component, struct snd_pcm_substream *substream, int cmd); -snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_sof_dev *sdev, +snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_soc_component *component, + struct snd_sof_pcm *spcm, struct snd_pcm_substream *substream); -int hda_dsp_pcm_ack(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); - -int hda_dsp_compr_open(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream); -int hda_dsp_compr_close(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream); -int hda_dsp_compr_hw_params(struct snd_sof_dev *sdev, +int hda_dsp_pcm_ack(struct snd_soc_component *component, + struct snd_pcm_substream *substream); + +int hda_dsp_compr_open(struct snd_soc_component *component, + struct snd_compr_stream *cstream); +int hda_dsp_compr_close(struct snd_soc_component *component, + struct snd_compr_stream *cstream); +int hda_dsp_compr_hw_params(struct snd_soc_component *component, struct snd_compr_stream *cstream, struct snd_compr_params *params, struct snd_sof_platform_stream_params *platform_params); -int hda_dsp_stream_compr_hw_free(struct snd_sof_dev *sdev, +int hda_dsp_stream_compr_hw_free(struct snd_soc_component *component, struct snd_compr_stream *cstream); -int hda_dsp_compr_trigger(struct snd_sof_dev *sdev, +int hda_dsp_compr_trigger(struct snd_soc_component *component, struct snd_compr_stream *cstream, int cmd); -int hda_dsp_compr_pointer(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream, +int hda_dsp_compr_pointer(struct snd_soc_component *component, + struct snd_compr_stream *cstream, struct snd_compr_tstamp64 *tstamp); -u64 hda_dsp_compr_get_stream_llp(struct snd_sof_dev *sdev, +u64 hda_dsp_compr_get_stream_llp(struct snd_soc_component *component, struct snd_compr_stream *cstream); /* @@ -723,11 +732,9 @@ bool hda_dsp_check_stream_irq(struct snd_sof_dev *sdev); snd_pcm_uframes_t hda_dsp_stream_get_position(struct hdac_stream *hstream, int direction, bool can_sleep); -u64 hda_dsp_get_stream_llp(struct snd_sof_dev *sdev, - struct snd_soc_component *component, +u64 hda_dsp_get_stream_llp(struct snd_soc_component *component, struct snd_pcm_substream *substream); -u64 hda_dsp_get_stream_ldp(struct snd_sof_dev *sdev, - struct snd_soc_component *component, +u64 hda_dsp_get_stream_ldp(struct snd_soc_component *component, struct snd_pcm_substream *substream); struct hdac_ext_stream * @@ -959,6 +966,7 @@ int hda_dsp_dais_suspend(struct snd_sof_dev *sdev); /* * Platform Specific HW abstraction Ops. */ +extern const struct sof_audio_ops sof_hda_audio_ops; extern const struct snd_sof_dsp_ops sof_hda_common_ops; extern struct snd_sof_dsp_ops sof_skl_ops; @@ -1008,6 +1016,10 @@ static inline void hda_probes_unregister(struct snd_sof_dev *sdev) int hda_register_clients(struct snd_sof_dev *sdev); void hda_unregister_clients(struct snd_sof_dev *sdev); +/* SOF audio client registration for HDA platforms (multi-card) */ +int hda_register_audio_client(struct snd_sof_dev *sdev); +void hda_unregister_audio_client(struct snd_sof_dev *sdev); + /* machine driver select */ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev); void hda_set_mach_params(struct snd_soc_acpi_mach *mach, @@ -1025,7 +1037,7 @@ struct sof_ipc_dai_config; extern int sof_hda_position_quirk; -void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops); +void hda_set_dai_drv_ops(struct snd_sof_dev *sdev); void hda_ops_free(struct snd_sof_dev *sdev); /* SKL/KBL */ @@ -1102,7 +1114,7 @@ static inline 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); + return snd_sof_component_get_sdev(component); } #endif diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c index 196caba14be0ee..d49b28feb04c54 100644 --- a/sound/soc/sof/intel/icl.c +++ b/sound/soc/sof/intel/icl.c @@ -166,7 +166,7 @@ int sof_icl_ops_init(struct snd_sof_dev *sdev) sof_icl_ops.core_get = hda_dsp_core_get; /* set DAI driver ops */ - hda_set_dai_drv_ops(sdev, &sof_icl_ops); + hda_set_dai_drv_ops(sdev); return 0; }; diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c index 2ba1855f8311dd..252e3ef2f23b37 100644 --- a/sound/soc/sof/intel/mtl.c +++ b/sound/soc/sof/intel/mtl.c @@ -778,7 +778,7 @@ int sof_mtl_set_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *dsp_ops) dsp_ops->set_power_state = hda_dsp_set_power_state_ipc4; /* set DAI ops */ - hda_set_dai_drv_ops(sdev, dsp_ops); + hda_set_dai_drv_ops(sdev); return 0; } diff --git a/sound/soc/sof/intel/pci-apl.c b/sound/soc/sof/intel/pci-apl.c index 3241403efa60a0..69ed5492a8d5f4 100644 --- a/sound/soc/sof/intel/pci-apl.c +++ b/sound/soc/sof/intel/pci-apl.c @@ -47,6 +47,7 @@ static const struct sof_dev_desc bxt_desc = { }, .nocodec_tplg_filename = "sof-apl-nocodec.tplg", .ops = &sof_apl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_apl_ops_init, .ops_free = hda_ops_free, }; @@ -79,6 +80,7 @@ static const struct sof_dev_desc glk_desc = { }, .nocodec_tplg_filename = "sof-glk-nocodec.tplg", .ops = &sof_apl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_apl_ops_init, .ops_free = hda_ops_free, }; diff --git a/sound/soc/sof/intel/pci-cnl.c b/sound/soc/sof/intel/pci-cnl.c index de48640024e493..ccd7ea7b5a822c 100644 --- a/sound/soc/sof/intel/pci-cnl.c +++ b/sound/soc/sof/intel/pci-cnl.c @@ -48,6 +48,7 @@ static const struct sof_dev_desc cnl_desc = { }, .nocodec_tplg_filename = "sof-cnl-nocodec.tplg", .ops = &sof_cnl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_cnl_ops_init, .ops_free = hda_ops_free, }; @@ -81,6 +82,7 @@ static const struct sof_dev_desc cfl_desc = { }, .nocodec_tplg_filename = "sof-cnl-nocodec.tplg", .ops = &sof_cnl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_cnl_ops_init, .ops_free = hda_ops_free, }; @@ -114,6 +116,7 @@ static const struct sof_dev_desc cml_desc = { }, .nocodec_tplg_filename = "sof-cnl-nocodec.tplg", .ops = &sof_cnl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_cnl_ops_init, .ops_free = hda_ops_free, }; diff --git a/sound/soc/sof/intel/pci-icl.c b/sound/soc/sof/intel/pci-icl.c index fd219e6548445c..87b059997edadf 100644 --- a/sound/soc/sof/intel/pci-icl.c +++ b/sound/soc/sof/intel/pci-icl.c @@ -48,6 +48,7 @@ static const struct sof_dev_desc icl_desc = { }, .nocodec_tplg_filename = "sof-icl-nocodec.tplg", .ops = &sof_icl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_icl_ops_init, .ops_free = hda_ops_free, }; @@ -80,6 +81,7 @@ static const struct sof_dev_desc jsl_desc = { }, .nocodec_tplg_filename = "sof-jsl-nocodec.tplg", .ops = &sof_cnl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_cnl_ops_init, .ops_free = hda_ops_free, }; diff --git a/sound/soc/sof/intel/pci-lnl.c b/sound/soc/sof/intel/pci-lnl.c index acb4429df9ecf6..3694fccf505fa9 100644 --- a/sound/soc/sof/intel/pci-lnl.c +++ b/sound/soc/sof/intel/pci-lnl.c @@ -55,6 +55,7 @@ static const struct sof_dev_desc lnl_desc = { }, .nocodec_tplg_filename = "sof-lnl-nocodec.tplg", .ops = &sof_lnl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_lnl_ops_init, }; diff --git a/sound/soc/sof/intel/pci-mtl.c b/sound/soc/sof/intel/pci-mtl.c index 23adc5d765b475..874cf2e8267e09 100644 --- a/sound/soc/sof/intel/pci-mtl.c +++ b/sound/soc/sof/intel/pci-mtl.c @@ -54,6 +54,7 @@ static const struct sof_dev_desc mtl_desc = { }, .nocodec_tplg_filename = "sof-mtl-nocodec.tplg", .ops = &sof_mtl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_mtl_ops_init, .ops_free = hda_ops_free, }; @@ -84,6 +85,7 @@ static const struct sof_dev_desc arl_desc = { }, .nocodec_tplg_filename = "sof-arl-nocodec.tplg", .ops = &sof_mtl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_mtl_ops_init, .ops_free = hda_ops_free, }; @@ -114,6 +116,7 @@ static const struct sof_dev_desc arl_s_desc = { }, .nocodec_tplg_filename = "sof-arl-nocodec.tplg", .ops = &sof_mtl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_mtl_ops_init, .ops_free = hda_ops_free, }; diff --git a/sound/soc/sof/intel/pci-nvl.c b/sound/soc/sof/intel/pci-nvl.c index bb3c29ef547773..8f370b0729a7d0 100644 --- a/sound/soc/sof/intel/pci-nvl.c +++ b/sound/soc/sof/intel/pci-nvl.c @@ -53,6 +53,7 @@ static const struct sof_dev_desc nvl_desc = { }, .nocodec_tplg_filename = "sof-nvl-nocodec.tplg", .ops = &sof_nvl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_nvl_ops_init, }; @@ -83,6 +84,7 @@ static const struct sof_dev_desc nvl_s_desc = { }, .nocodec_tplg_filename = "sof-nvl-nocodec.tplg", .ops = &sof_nvl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_nvl_ops_init, }; diff --git a/sound/soc/sof/intel/pci-ptl.c b/sound/soc/sof/intel/pci-ptl.c index 9cb785ef763f63..61b1095503d6bb 100644 --- a/sound/soc/sof/intel/pci-ptl.c +++ b/sound/soc/sof/intel/pci-ptl.c @@ -53,6 +53,7 @@ static const struct sof_dev_desc ptl_desc = { }, .nocodec_tplg_filename = "sof-ptl-nocodec.tplg", .ops = &sof_ptl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_ptl_ops_init, }; @@ -83,6 +84,7 @@ static const struct sof_dev_desc wcl_desc = { }, .nocodec_tplg_filename = "sof-ptl-nocodec.tplg", .ops = &sof_ptl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_ptl_ops_init, }; diff --git a/sound/soc/sof/intel/pci-skl.c b/sound/soc/sof/intel/pci-skl.c index a16945dc35f7dc..6848f4170d4cb8 100644 --- a/sound/soc/sof/intel/pci-skl.c +++ b/sound/soc/sof/intel/pci-skl.c @@ -38,6 +38,7 @@ static struct sof_dev_desc skl_desc = { }, .nocodec_tplg_filename = "sof-skl-nocodec.tplg", .ops = &sof_skl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_skl_ops_init, .ops_free = hda_ops_free, }; @@ -63,6 +64,7 @@ static struct sof_dev_desc kbl_desc = { }, .nocodec_tplg_filename = "sof-kbl-nocodec.tplg", .ops = &sof_skl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_skl_ops_init, .ops_free = hda_ops_free, }; diff --git a/sound/soc/sof/intel/pci-tgl.c b/sound/soc/sof/intel/pci-tgl.c index 437c4381982555..28a2b11582a9e2 100644 --- a/sound/soc/sof/intel/pci-tgl.c +++ b/sound/soc/sof/intel/pci-tgl.c @@ -48,6 +48,7 @@ static const struct sof_dev_desc tgl_desc = { }, .nocodec_tplg_filename = "sof-tgl-nocodec.tplg", .ops = &sof_tgl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_tgl_ops_init, .ops_free = hda_ops_free, }; @@ -81,6 +82,7 @@ static const struct sof_dev_desc tglh_desc = { }, .nocodec_tplg_filename = "sof-tgl-nocodec.tplg", .ops = &sof_tgl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_tgl_ops_init, .ops_free = hda_ops_free, }; @@ -113,6 +115,7 @@ static const struct sof_dev_desc ehl_desc = { }, .nocodec_tplg_filename = "sof-ehl-nocodec.tplg", .ops = &sof_tgl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_tgl_ops_init, .ops_free = hda_ops_free, }; @@ -146,6 +149,7 @@ static const struct sof_dev_desc adls_desc = { }, .nocodec_tplg_filename = "sof-adl-nocodec.tplg", .ops = &sof_tgl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_tgl_ops_init, .ops_free = hda_ops_free, }; @@ -179,6 +183,7 @@ static const struct sof_dev_desc adl_desc = { }, .nocodec_tplg_filename = "sof-adl-nocodec.tplg", .ops = &sof_tgl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_tgl_ops_init, .ops_free = hda_ops_free, }; @@ -212,6 +217,7 @@ static const struct sof_dev_desc adln_desc = { }, .nocodec_tplg_filename = "sof-adl-nocodec.tplg", .ops = &sof_tgl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_tgl_ops_init, .ops_free = hda_ops_free, }; @@ -245,6 +251,7 @@ static const struct sof_dev_desc rpls_desc = { }, .nocodec_tplg_filename = "sof-rpl-nocodec.tplg", .ops = &sof_tgl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_tgl_ops_init, .ops_free = hda_ops_free, }; @@ -278,6 +285,7 @@ static const struct sof_dev_desc rpl_desc = { }, .nocodec_tplg_filename = "sof-rpl-nocodec.tplg", .ops = &sof_tgl_ops, + .audio_ops = &sof_hda_audio_ops, .ops_init = sof_tgl_ops_init, .ops_free = hda_ops_free, }; diff --git a/sound/soc/sof/intel/pci-tng.c b/sound/soc/sof/intel/pci-tng.c index 0c11cc1fd820eb..59411585e68ef3 100644 --- a/sound/soc/sof/intel/pci-tng.c +++ b/sound/soc/sof/intel/pci-tng.c @@ -14,6 +14,7 @@ #include #include #include "../ops.h" +#include "../sof-client.h" #include "atom.h" #include "../sof-pci-dev.h" #include "../sof-audio.h" @@ -132,6 +133,20 @@ static int tangier_pci_probe(struct snd_sof_dev *sdev) return ret; } +static const struct sof_audio_ops sof_tng_audio_ops = { + .pcm_open = sof_stream_pcm_open, + .pcm_close = sof_stream_pcm_close, + + .drv = atom_dai, + .num_drv = 3, + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_BATCH, +}; + const struct snd_sof_dsp_ops sof_tng_ops = { /* device init */ .probe = tangier_pci_probe, @@ -168,30 +183,19 @@ const struct snd_sof_dsp_ops sof_tng_ops = { .machine_unregister = sof_machine_unregister, .set_mach_params = atom_set_mach_params, + /* audio client */ + .register_audio_client = sof_register_audio_client, + .unregister_audio_client = sof_unregister_audio_client, + /* debug */ .debug_map = tng_debugfs, .debug_map_count = ARRAY_SIZE(tng_debugfs), .dbg_dump = atom_dump, .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, - /* stream callbacks */ - .pcm_open = sof_stream_pcm_open, - .pcm_close = sof_stream_pcm_close, - /*Firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, - /* DAI drivers */ - .drv = atom_dai, - .num_drv = 3, /* we have only 3 SSPs on byt*/ - - /* ALSA HW info flags */ - .hw_info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_BATCH, - .dsp_arch_ops = &sof_xtensa_arch_ops, }; @@ -221,6 +225,7 @@ static const struct sof_dev_desc tng_desc = { }, .nocodec_tplg_filename = "sof-byt.tplg", .ops = &sof_tng_ops, + .audio_ops = &sof_tng_audio_ops, }; /* PCI IDs */ @@ -245,6 +250,7 @@ module_pci_driver(snd_sof_pci_intel_tng_driver); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("SOF support for Tangier platforms"); +MODULE_IMPORT_NS("SND_SOC_SOF_CLIENT"); MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA"); MODULE_IMPORT_NS("SND_SOC_SOF_PCI_DEV"); MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_ATOM_HIFI_EP"); diff --git a/sound/soc/sof/intel/skl.c b/sound/soc/sof/intel/skl.c index f831143a7afc28..3ed936dde62c30 100644 --- a/sound/soc/sof/intel/skl.c +++ b/sound/soc/sof/intel/skl.c @@ -82,7 +82,7 @@ int sof_skl_ops_init(struct snd_sof_dev *sdev) sof_skl_ops.send_msg = hda_dsp_ipc4_send_msg; /* set DAI driver ops */ - hda_set_dai_drv_ops(sdev, &sof_skl_ops); + hda_set_dai_drv_ops(sdev); /* debug */ sof_skl_ops.debug_map = skl_dsp_debugfs; diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c index 09a8a4a2b017aa..ac5f10c240d13b 100644 --- a/sound/soc/sof/intel/tgl.c +++ b/sound/soc/sof/intel/tgl.c @@ -121,7 +121,7 @@ int sof_tgl_ops_init(struct snd_sof_dev *sdev) } /* set DAI driver ops */ - hda_set_dai_drv_ops(sdev, &sof_tgl_ops); + hda_set_dai_drv_ops(sdev); /* pre/post fw run */ sof_tgl_ops.post_fw_run = hda_dsp_post_fw_run; diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index e6d8894b8ef60c..3d0b1f0769c267 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -190,16 +190,6 @@ struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev) return NULL; } - if (!ops->pcm) { - dev_err(sdev->dev, "Missing IPC PCM ops\n"); - return NULL; - } - - if (!ops->tplg || !ops->tplg->widget || !ops->tplg->control) { - dev_err(sdev->dev, "Missing IPC topology ops\n"); - return NULL; - } - if (ops->fw_tracing && (!ops->fw_tracing->init || !ops->fw_tracing->suspend || !ops->fw_tracing->resume)) { dev_err(sdev->dev, "Missing firmware tracing ops\n"); diff --git a/sound/soc/sof/ipc3-compress.c b/sound/soc/sof/ipc3-compress.c index 115d454bcaf5b7..fb24decc1a6dd9 100644 --- a/sound/soc/sof/ipc3-compress.c +++ b/sound/soc/sof/ipc3-compress.c @@ -8,6 +8,7 @@ #include #include #include "sof-audio.h" +#include "sof-client.h" #include "sof-priv.h" #include "sof-utils.h" #include "ops.h" @@ -51,7 +52,7 @@ static int sof_ipc3_compr_open(struct snd_soc_component *component, static int sof_ipc3_compr_free(struct snd_soc_component *component, struct snd_compr_stream *cstream) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct sof_client_dev *cdev = snd_sof_component_get_cdev(component); struct sof_compr_stream *sstream = cstream->runtime->private_data; struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct sof_ipc_stream stream; @@ -67,7 +68,7 @@ static int sof_ipc3_compr_free(struct snd_soc_component *component, stream.comp_id = spcm->stream[cstream->direction].comp_id; if (spcm->prepared[cstream->direction]) { - ret = sof_ipc_tx_message_no_reply(sdev->ipc, &stream, sizeof(stream)); + ret = sof_client_ipc_tx_message_no_reply(cdev, &stream); if (!ret) spcm->prepared[cstream->direction] = false; } @@ -83,7 +84,8 @@ static int sof_ipc3_compr_set_params(struct snd_soc_component *component, struct snd_compr_stream *cstream, struct snd_compr_params *params) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct sof_client_dev *cdev = snd_sof_component_get_cdev(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_compr_runtime *crtd = cstream->runtime; struct sof_ipc_pcm_params_reply ipc_params_reply; @@ -158,8 +160,8 @@ static int sof_ipc3_compr_set_params(struct snd_soc_component *component, memcpy((u8 *)pcm->params.ext_data, ¶ms->codec, ext_data_size); - ret = sof_ipc_tx_message(sdev->ipc, pcm, sizeof(*pcm) + ext_data_size, - &ipc_params_reply, sizeof(ipc_params_reply)); + ret = sof_client_ipc_tx_message(cdev, pcm, &ipc_params_reply, + sizeof(ipc_params_reply)); if (ret < 0) { dev_err(component->dev, "error ipc failed\n"); goto out; @@ -200,7 +202,7 @@ static int sof_ipc3_compr_get_params(struct snd_soc_component *component, static int sof_ipc3_compr_trigger(struct snd_soc_component *component, struct snd_compr_stream *cstream, int cmd) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct sof_client_dev *cdev = snd_sof_component_get_cdev(component); struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct sof_ipc_stream stream; struct snd_sof_pcm *spcm; @@ -231,7 +233,7 @@ static int sof_ipc3_compr_trigger(struct snd_soc_component *component, break; } - return sof_ipc_tx_message_no_reply(sdev->ipc, &stream, sizeof(stream)); + return sof_client_ipc_tx_message_no_reply(cdev, &stream); } static int sof_ipc3_compr_copy_playback(struct snd_compr_runtime *rtd, diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index d1697401b1da0d..0cd9a6067e86a3 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -15,7 +15,9 @@ static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool set, bool lock) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scontrol->scomp); + struct snd_sof_audio_instance *instance = + snd_sof_component_get_audio_instance(scontrol->scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scontrol->scomp); struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; const struct sof_ipc_ops *iops = sdev->ipc->ops; enum sof_ipc_ctrl_type ctrl_type; @@ -24,7 +26,7 @@ static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, u32 ipc_cmd, msg_bytes; int ret = 0; - list_for_each_entry(swidget, &sdev->widget_list, list) { + list_for_each_entry(swidget, &instance->widget_list, list) { if (swidget->comp_id == scontrol->comp_id) { widget_found = true; break; @@ -32,7 +34,7 @@ static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, } if (!widget_found) { - dev_err(sdev->dev, "%s: can't find widget with id %d\n", __func__, + dev_err(scontrol->scomp->dev, "%s: can't find widget with id %d\n", __func__, scontrol->comp_id); return -EINVAL; } @@ -380,6 +382,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; + unsigned int payload; int ret = -EINVAL; /* @@ -449,6 +452,15 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol, goto err_restore; } + /* header.length has been verified to be >= sizeof(struct sof_abi_hdr) */ + payload = header.length - sizeof(struct sof_abi_hdr); + if (cdata->data->size > payload) { + dev_err_ratelimited(scomp->dev, + "ABI header claims %u bytes of data, TLV carries %u\n", + cdata->data->size, payload); + goto err_restore; + } + /* notify DSP of byte control updates */ if (pm_runtime_active(scomp->dev)) { /* Actually send the data to the DSP; this is an opportunity to validate the data */ @@ -576,8 +588,9 @@ static void snd_sof_update_control(struct snd_sof_control *scontrol, } } -static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_message) +static void sof_ipc3_control_update(struct snd_soc_component *scomp, void *ipc_control_message) { + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct sof_ipc_ctrl_data *cdata = ipc_control_message; struct snd_soc_dapm_widget *widget; struct snd_sof_control *scontrol; @@ -590,14 +603,21 @@ static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_ bool found = false; int i, type; + if (!instance) + return; + if (cdata->type == SOF_CTRL_TYPE_VALUE_COMP_GET || cdata->type == SOF_CTRL_TYPE_VALUE_COMP_SET) { - dev_err(sdev->dev, "Component data is not supported in control notification\n"); + dev_err(scomp->dev, "Component data is not supported in control notification\n"); return; } - /* Find the swidget first */ - list_for_each_entry(swidget, &sdev->widget_list, list) { + /* + * Find the swidget first. The notification is broadcast to all audio + * clients of the DSP, a miss here only means that the component is not + * owned by this client. + */ + list_for_each_entry(swidget, &instance->widget_list, list) { if (swidget->comp_id == cdata->comp_id) { found = true; break; @@ -620,7 +640,7 @@ static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_ type = SND_SOC_TPLG_TYPE_ENUM; break; default: - dev_err(sdev->dev, "Unknown cmd %u in %s\n", cdata->cmd, __func__); + dev_err(swidget->scomp->dev, "Unknown cmd %u in %s\n", cdata->cmd, __func__); return; } @@ -683,7 +703,7 @@ static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_ } if (cdata->rhdr.hdr.size != expected_size) { - dev_err(sdev->dev, "Component notification size mismatch\n"); + dev_err(swidget->scomp->dev, "Component notification size mismatch\n"); return; } @@ -703,16 +723,18 @@ static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_ static int sof_ipc3_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { + struct snd_sof_audio_instance *instance = + snd_sof_component_get_audio_instance(swidget->scomp); struct snd_sof_control *scontrol; int ret; /* set up all controls for the widget */ - list_for_each_entry(scontrol, &sdev->kcontrol_list, list) + list_for_each_entry(scontrol, &instance->kcontrol_list, list) if (scontrol->comp_id == swidget->comp_id) { /* set kcontrol data in DSP */ ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, false); if (ret < 0) { - dev_err(sdev->dev, + dev_err(swidget->scomp->dev, "kcontrol %d set up failed for widget %s\n", scontrol->comp_id, swidget->widget->name); return ret; @@ -729,7 +751,7 @@ static int sof_ipc3_widget_kcontrol_setup(struct snd_sof_dev *sdev, ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, false); if (ret < 0) - dev_warn(sdev->dev, + dev_warn(swidget->scomp->dev, "kcontrol %d read failed for widget %s\n", scontrol->comp_id, swidget->widget->name); } diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c index 42f93e596598fd..1dec34adc4ed4a 100644 --- a/sound/soc/sof/ipc3-pcm.c +++ b/sound/soc/sof/ipc3-pcm.c @@ -12,12 +12,13 @@ #include "ops.h" #include "sof-priv.h" #include "sof-audio.h" +#include "sof-client.h" static int sof_ipc3_pcm_hw_free(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_sof_pcm *spcm, int dir) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct sof_client_dev *cdev = snd_sof_component_get_cdev(component); struct sof_ipc_stream stream; if (!spcm->prepared[dir]) @@ -28,7 +29,7 @@ static int sof_ipc3_pcm_hw_free(struct snd_soc_component *component, stream.comp_id = spcm->stream[dir].comp_id; /* send IPC to the DSP */ - return sof_ipc_tx_message_no_reply(sdev->ipc, &stream, sizeof(stream)); + return sof_client_ipc_tx_message_no_reply(cdev, &stream); } static int sof_ipc3_pcm_hw_params(struct snd_soc_component *component, @@ -36,7 +37,8 @@ static int sof_ipc3_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_hw_params *params, struct snd_sof_platform_stream_params *platform_params) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct sof_client_dev *cdev = snd_sof_component_get_cdev(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct sof_ipc_fw_version *v = &sdev->fw_ready.version; struct snd_pcm_runtime *runtime = substream->runtime; @@ -116,8 +118,8 @@ static int sof_ipc3_pcm_hw_params(struct snd_soc_component *component, pcm.params.stream_tag); /* send hw_params IPC to the DSP */ - ret = sof_ipc_tx_message(sdev->ipc, &pcm, sizeof(pcm), - &ipc_params_reply, sizeof(ipc_params_reply)); + ret = sof_client_ipc_tx_message(cdev, &pcm, &ipc_params_reply, + sizeof(ipc_params_reply)); if (ret < 0) { spcm_err(spcm, substream->stream, "STREAM_PCM_PARAMS ipc failed for stream_tag %d\n", @@ -139,7 +141,7 @@ static int sof_ipc3_pcm_trigger(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_sof_pcm *spcm, int cmd, int dir) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct sof_client_dev *cdev = snd_sof_component_get_cdev(component); struct sof_ipc_stream stream; stream.hdr.size = sizeof(stream); @@ -167,12 +169,15 @@ static int sof_ipc3_pcm_trigger(struct snd_soc_component *component, } /* send IPC to the DSP */ - return sof_ipc_tx_message_no_reply(sdev->ipc, &stream, sizeof(stream)); + return sof_client_ipc_tx_message_no_reply(cdev, &stream); } -static void ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char *link_name, +static void ssp_dai_config_pcm_params_match(struct snd_soc_component *component, + const char *link_name, struct snd_pcm_hw_params *params) { + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct sof_ipc_dai_config *config; struct snd_sof_dai *dai; int i; @@ -181,7 +186,7 @@ static void ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char * Search for all matching DAIs as we can have both playback and capture DAI * associated with the same link. */ - list_for_each_entry(dai, &sdev->dai_list, list) { + list_for_each_entry(dai, &instance->dai_list, list) { if (!dai->name || strcmp(link_name, dai->name)) continue; for (i = 0; i < dai->number_configs; i++) { @@ -205,7 +210,6 @@ static int sof_ipc3_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_sof_dai *dai = snd_sof_find_dai(component, (char *)rtd->dai_link->name); struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct sof_dai_private_data *private; struct snd_soc_dpcm *dpcm; @@ -244,7 +248,7 @@ static int sof_ipc3_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, switch (private->dai_config->type) { case SOF_DAI_INTEL_SSP: /* search for config to pcm params match, if not found use default */ - ssp_dai_config_pcm_params_match(sdev, (char *)rtd->dai_link->name, params); + ssp_dai_config_pcm_params_match(component, (char *)rtd->dai_link->name, params); rate->min = private->dai_config[dai->current_config].ssp.fsync_rate; rate->max = private->dai_config[dai->current_config].ssp.fsync_rate; diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index 26d85ca9be26a1..ad36348e13f979 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -11,6 +11,7 @@ #include #include "sof-priv.h" #include "sof-audio.h" +#include "sof-client.h" #include "ipc3-priv.h" #include "ops.h" @@ -524,7 +525,7 @@ static int sof_ipc3_widget_setup_comp_mixer(struct snd_sof_widget *swidget) static int sof_ipc3_widget_setup_comp_pipeline(struct snd_sof_widget *swidget) { struct snd_soc_component *scomp = swidget->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct snd_sof_pipeline *spipe = swidget->spipe; struct sof_ipc_pipe_new *pipeline; struct snd_sof_widget *comp_swidget; @@ -757,7 +758,7 @@ static int sof_ipc3_widget_setup_comp_mux(struct snd_sof_widget *swidget) static int sof_ipc3_widget_setup_comp_pga(struct snd_sof_widget *swidget) { struct snd_soc_component *scomp = swidget->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct sof_ipc_comp_volume *volume; struct snd_sof_control *scontrol; size_t ipc_size = sizeof(*volume); @@ -790,7 +791,7 @@ static int sof_ipc3_widget_setup_comp_pga(struct snd_sof_widget *swidget) dev_dbg(scomp->dev, "loaded PGA %s\n", swidget->widget->name); sof_dbg_comp_config(scomp, &volume->config); - list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { + list_for_each_entry(scontrol, &instance->kcontrol_list, list) { if (scontrol->comp_id == swidget->comp_id && scontrol->volume_table) { min_step = scontrol->min_volume_step; @@ -1429,11 +1430,12 @@ static int sof_link_afe_load(struct snd_soc_component *scomp, struct snd_sof_dai static int sof_link_ssp_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct sof_client_dev *cdev = snd_sof_component_get_cdev(scomp); struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs; struct sof_dai_private_data *private = dai->private; u32 size = sizeof(*config); int current_config = 0; + u16 mclk_id; int i, ret; /* @@ -1454,10 +1456,10 @@ static int sof_link_ssp_load(struct snd_soc_component *scomp, struct snd_sof_dai config[i].hdr.size = size; - if (sdev->mclk_id_override) { + if (sof_client_get_ssp_mclk_id_quirk(cdev, &mclk_id)) { dev_dbg(scomp->dev, "tplg: overriding topology mclk_id %d by quirk %d\n", - config[i].ssp.mclk_id, sdev->mclk_id_quirk); - config[i].ssp.mclk_id = sdev->mclk_id_quirk; + config[i].ssp.mclk_id, mclk_id); + config[i].ssp.mclk_id = mclk_id; } /* copy differentiating hw configs to ipc structs */ @@ -1502,7 +1504,7 @@ static int sof_link_ssp_load(struct snd_soc_component *scomp, struct snd_sof_dai static int sof_link_dmic_load(struct snd_soc_component *scomp, struct snd_sof_dai_link *slink, struct sof_ipc_dai_config *config, struct snd_sof_dai *dai) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct sof_dai_private_data *private = dai->private; struct sof_ipc_fw_ready *ready = &sdev->fw_ready; struct sof_ipc_fw_version *v = &ready->version; @@ -1600,7 +1602,7 @@ static int sof_link_alh_load(struct snd_soc_component *scomp, struct snd_sof_dai static int sof_ipc3_widget_setup_comp_dai(struct snd_sof_widget *swidget) { struct snd_soc_component *scomp = swidget->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct snd_sof_dai *dai = swidget->private; struct sof_dai_private_data *private; struct sof_ipc_comp_dai *comp_dai; @@ -1642,7 +1644,7 @@ static int sof_ipc3_widget_setup_comp_dai(struct snd_sof_widget *swidget) /* Subtract the base to match the FW dai index. */ if (comp_dai->type == SOF_DAI_INTEL_ALH) { if (comp_dai->dai_index < INTEL_ALH_DAI_INDEX_BASE) { - dev_err(sdev->dev, + dev_err(scomp->dev, "Invalid ALH dai index %d, only Pin numbers >= %d can be used\n", comp_dai->dai_index, INTEL_ALH_DAI_INDEX_BASE); ret = -EINVAL; @@ -1656,7 +1658,7 @@ static int sof_ipc3_widget_setup_comp_dai(struct snd_sof_widget *swidget) sof_dbg_comp_config(scomp, &comp_dai->config); /* now update DAI config */ - list_for_each_entry(slink, &sdev->dai_link_list, list) { + list_for_each_entry(slink, &instance->dai_link_list, list) { struct sof_ipc_dai_config common_config; int i; @@ -1779,6 +1781,8 @@ static void sof_ipc3_widget_free_comp_dai(struct snd_sof_widget *swidget) static int sof_ipc3_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *sroute) { + struct snd_soc_component *scomp = sroute->scomp; + struct sof_client_dev *cdev = snd_sof_component_get_cdev(scomp); struct sof_ipc_pipe_comp_connect connect; int ret; @@ -1787,33 +1791,34 @@ static int sof_ipc3_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * connect.source_id = sroute->src_widget->comp_id; connect.sink_id = sroute->sink_widget->comp_id; - dev_dbg(sdev->dev, "setting up route %s -> %s\n", + dev_dbg(scomp->dev, "setting up route %s -> %s\n", sroute->src_widget->widget->name, sroute->sink_widget->widget->name); /* send ipc */ - ret = sof_ipc_tx_message_no_reply(sdev->ipc, &connect, sizeof(connect)); + ret = sof_client_ipc_tx_message_no_reply(cdev, &connect); if (ret < 0) - dev_err(sdev->dev, "%s: route %s -> %s failed\n", __func__, + dev_err(scomp->dev, "%s: route %s -> %s failed\n", __func__, sroute->src_widget->widget->name, sroute->sink_widget->widget->name); return ret; } -static int sof_ipc3_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) +static int sof_ipc3_control_load_bytes(struct snd_sof_control *scontrol) { + struct snd_soc_component *scomp = scontrol->scomp; struct sof_ipc_ctrl_data *cdata; size_t priv_size_check; int ret; if (scontrol->max_size < (sizeof(*cdata) + sizeof(struct sof_abi_hdr))) { - dev_err(sdev->dev, "%s: insufficient size for a bytes control: %zu.\n", + dev_err(scomp->dev, "%s: insufficient size for a bytes control: %zu.\n", __func__, scontrol->max_size); return -EINVAL; } if (scontrol->priv_size > scontrol->max_size - sizeof(*cdata)) { - dev_err(sdev->dev, + dev_err(scomp->dev, "%s: bytes data size %zu exceeds max %zu.\n", __func__, scontrol->priv_size, scontrol->max_size - sizeof(*cdata)); return -EINVAL; @@ -1835,13 +1840,13 @@ static int sof_ipc3_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_ scontrol->priv = NULL; if (cdata->data->magic != SOF_ABI_MAGIC) { - dev_err(sdev->dev, "Wrong ABI magic 0x%08x.\n", cdata->data->magic); + dev_err(scomp->dev, "Wrong ABI magic 0x%08x.\n", cdata->data->magic); ret = -EINVAL; goto err; } if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) { - dev_err(sdev->dev, "Incompatible ABI version 0x%08x.\n", + dev_err(scomp->dev, "Incompatible ABI version 0x%08x.\n", cdata->data->abi); ret = -EINVAL; goto err; @@ -1849,7 +1854,7 @@ static int sof_ipc3_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_ priv_size_check = cdata->data->size + sizeof(struct sof_abi_hdr); if (priv_size_check != scontrol->priv_size) { - dev_err(sdev->dev, "Conflict in bytes (%zu) vs. priv size (%zu).\n", + dev_err(scomp->dev, "Conflict in bytes (%zu) vs. priv size (%zu).\n", priv_size_check, scontrol->priv_size); ret = -EINVAL; goto err; @@ -1921,7 +1926,7 @@ static int sof_ipc3_control_setup(struct snd_sof_dev *sdev, struct snd_sof_contr case SND_SOC_TPLG_CTL_VOLSW_XR_SX: return sof_ipc3_control_load_volume(sdev, scontrol); case SND_SOC_TPLG_CTL_BYTES: - return sof_ipc3_control_load_bytes(sdev, scontrol); + return sof_ipc3_control_load_bytes(scontrol); case SND_SOC_TPLG_CTL_ENUM: case SND_SOC_TPLG_CTL_ENUM_VALUE: return sof_ipc3_control_load_enum(sdev, scontrol); @@ -1934,6 +1939,7 @@ static int sof_ipc3_control_setup(struct snd_sof_dev *sdev, struct snd_sof_contr static int sof_ipc3_control_free(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) { + struct sof_client_dev *cdev = snd_sof_component_get_cdev(scontrol->scomp); struct sof_ipc_free fcomp; fcomp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_FREE; @@ -1941,14 +1947,14 @@ static int sof_ipc3_control_free(struct snd_sof_dev *sdev, struct snd_sof_contro fcomp.id = scontrol->comp_id; /* send IPC to the DSP */ - return sof_ipc_tx_message_no_reply(sdev->ipc, &fcomp, sizeof(fcomp)); + return sof_client_ipc_tx_message_no_reply(cdev, &fcomp); } /* send pcm params ipc */ static int sof_ipc3_keyword_detect_pcm_params(struct snd_sof_widget *swidget, int dir) { struct snd_soc_component *scomp = swidget->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct sof_client_dev *cdev = snd_sof_component_get_cdev(scomp); struct snd_pcm_hw_params *params; struct sof_ipc_pcm_params pcm; struct snd_sof_pcm *spcm; @@ -1992,7 +1998,7 @@ static int sof_ipc3_keyword_detect_pcm_params(struct snd_sof_widget *swidget, in } /* send IPC to the DSP */ - ret = sof_ipc_tx_message_no_reply(sdev->ipc, &pcm, sizeof(pcm)); + ret = sof_client_ipc_tx_message_no_reply(cdev, &pcm); if (ret < 0) dev_err(scomp->dev, "%s: PCM params failed for %s\n", __func__, swidget->widget->name); @@ -2004,7 +2010,7 @@ static int sof_ipc3_keyword_detect_pcm_params(struct snd_sof_widget *swidget, in static int sof_ipc3_keyword_detect_trigger(struct snd_sof_widget *swidget, int cmd) { struct snd_soc_component *scomp = swidget->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct sof_client_dev *cdev = snd_sof_component_get_cdev(scomp); struct sof_ipc_stream stream; int ret; @@ -2014,7 +2020,7 @@ static int sof_ipc3_keyword_detect_trigger(struct snd_sof_widget *swidget, int c stream.comp_id = swidget->comp_id; /* send IPC to the DSP */ - ret = sof_ipc_tx_message_no_reply(sdev->ipc, &stream, sizeof(stream)); + ret = sof_client_ipc_tx_message_no_reply(cdev, &stream); if (ret < 0) dev_err(scomp->dev, "%s: Failed to trigger %s\n", __func__, swidget->widget->name); @@ -2130,10 +2136,12 @@ static int sof_ipc3_widget_bind_event(struct snd_soc_component *scomp, static int sof_ipc3_complete_pipeline(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { + struct snd_soc_component *scomp = swidget->scomp; + struct sof_client_dev *cdev = snd_sof_component_get_cdev(scomp); struct sof_ipc_pipe_ready ready; int ret; - dev_dbg(sdev->dev, "tplg: complete pipeline %s id %d\n", + dev_dbg(scomp->dev, "tplg: complete pipeline %s id %d\n", swidget->widget->name, swidget->comp_id); memset(&ready, 0, sizeof(ready)); @@ -2141,7 +2149,7 @@ static int sof_ipc3_complete_pipeline(struct snd_sof_dev *sdev, struct snd_sof_w ready.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_PIPE_COMPLETE; ready.comp_id = swidget->comp_id; - ret = sof_ipc_tx_message_no_reply(sdev->ipc, &ready, sizeof(ready)); + ret = sof_client_ipc_tx_message_no_reply(cdev, &ready); if (ret < 0) return ret; @@ -2150,6 +2158,7 @@ static int sof_ipc3_complete_pipeline(struct snd_sof_dev *sdev, struct snd_sof_w static int sof_ipc3_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { + struct sof_client_dev *cdev = snd_sof_component_get_cdev(swidget->scomp); struct sof_ipc_free ipc_free = { .hdr = { .size = sizeof(ipc_free), @@ -2176,9 +2185,10 @@ static int sof_ipc3_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget break; } - ret = sof_ipc_tx_message_no_reply(sdev->ipc, &ipc_free, sizeof(ipc_free)); + ret = sof_client_ipc_tx_message_no_reply(cdev, &ipc_free); if (ret < 0) - dev_err(sdev->dev, "failed to free widget %s\n", swidget->widget->name); + dev_err(swidget->scomp->dev, "failed to free widget %s\n", + swidget->widget->name); return ret; } @@ -2193,19 +2203,21 @@ static int sof_ipc3_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget * int ret = 0; if (!dai || !dai->private) { - dev_err(sdev->dev, "No private data for DAI %s\n", swidget->widget->name); + dev_err(swidget->scomp->dev, "No private data for DAI %s\n", + swidget->widget->name); return -EINVAL; } private = dai->private; if (!private->dai_config) { - dev_err(sdev->dev, "No config for DAI %s\n", dai->name); + dev_err(swidget->scomp->dev, "No config for DAI %s\n", dai->name); return -EINVAL; } config = &private->dai_config[dai->current_config]; if (!config) { - dev_err(sdev->dev, "Invalid current config for DAI %s\n", dai->name); + dev_err(swidget->scomp->dev, "Invalid current config for DAI %s\n", + dai->name); return -EINVAL; } @@ -2230,7 +2242,7 @@ static int sof_ipc3_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget * if (flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) { /* Subtract the base to match the FW dai index. */ if (data->dai_index < INTEL_ALH_DAI_INDEX_BASE) { - dev_err(sdev->dev, + dev_err(swidget->scomp->dev, "Invalid ALH dai index %d, only Pin numbers >= %d can be used\n", config->dai_index, INTEL_ALH_DAI_INDEX_BASE); return -EINVAL; @@ -2274,9 +2286,11 @@ static int sof_ipc3_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget * /* only send the IPC if the widget is set up in the DSP */ if (swidget->use_count > 0) { - ret = sof_ipc_tx_message_no_reply(sdev->ipc, config, config->hdr.size); + ret = sof_client_ipc_tx_message_no_reply(snd_sof_component_get_cdev(swidget->scomp), + config); if (ret < 0) - dev_err(sdev->dev, "Failed to set dai config for %s\n", dai->name); + dev_err(swidget->scomp->dev, "Failed to set dai config for %s\n", + dai->name); /* clear the flags once the IPC has been sent even if it fails */ config->flags = SOF_DAI_CONFIG_FLAGS_NONE; @@ -2287,6 +2301,7 @@ static int sof_ipc3_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget * static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { + struct sof_client_dev *cdev = snd_sof_component_get_cdev(swidget->scomp); int ret; if (!swidget->private) @@ -2298,9 +2313,8 @@ static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget { struct snd_sof_dai *dai = swidget->private; struct sof_dai_private_data *dai_data = dai->private; - struct sof_ipc_comp *comp = &dai_data->comp_dai->comp; - ret = sof_ipc_tx_message_no_reply(sdev->ipc, dai_data->comp_dai, comp->hdr.size); + ret = sof_client_ipc_tx_message_no_reply(cdev, dai_data->comp_dai); break; } case snd_soc_dapm_scheduler: @@ -2308,33 +2322,34 @@ static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget struct sof_ipc_pipe_new *pipeline; pipeline = swidget->private; - ret = sof_ipc_tx_message_no_reply(sdev->ipc, pipeline, sizeof(*pipeline)); + ret = sof_client_ipc_tx_message_no_reply(cdev, pipeline); break; } default: - { - struct sof_ipc_cmd_hdr *hdr; - - hdr = swidget->private; - ret = sof_ipc_tx_message_no_reply(sdev->ipc, swidget->private, hdr->size); + ret = sof_client_ipc_tx_message_no_reply(cdev, swidget->private); break; } - } if (ret < 0) - dev_err(sdev->dev, "Failed to setup widget %s\n", swidget->widget->name); + dev_err(swidget->scomp->dev, "Failed to setup widget %s\n", + swidget->widget->name); return ret; } -static int sof_ipc3_set_up_all_pipelines(struct snd_sof_dev *sdev, bool verify) +static int sof_ipc3_set_up_all_pipelines(struct snd_soc_component *component, bool verify) { + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct sof_ipc_fw_version *v = &sdev->fw_ready.version; struct snd_sof_widget *swidget; struct snd_sof_route *sroute; int ret; + if (!instance) + return 0; + /* restore pipeline components */ - list_for_each_entry(swidget, &sdev->widget_list, list) { + list_for_each_entry(swidget, &instance->widget_list, list) { /* only set up the widgets belonging to static pipelines */ if (!verify && swidget->dynamic_pipeline_widget) continue; @@ -2376,7 +2391,7 @@ static int sof_ipc3_set_up_all_pipelines(struct snd_sof_dev *sdev, bool verify) } /* restore pipeline connections */ - list_for_each_entry(sroute, &sdev->route_list, list) { + list_for_each_entry(sroute, &instance->route_list, list) { /* only set up routes belonging to static pipelines */ if (!verify && (sroute->src_widget->dynamic_pipeline_widget || sroute->sink_widget->dynamic_pipeline_widget)) @@ -2393,13 +2408,13 @@ static int sof_ipc3_set_up_all_pipelines(struct snd_sof_dev *sdev, bool verify) ret = sof_route_setup(sdev, sroute->src_widget->widget, sroute->sink_widget->widget); if (ret < 0) { - dev_err(sdev->dev, "%s: route set up failed\n", __func__); + dev_err(sroute->scomp->dev, "%s: route set up failed\n", __func__); return ret; } } /* complete pipeline */ - list_for_each_entry(swidget, &sdev->widget_list, list) { + list_for_each_entry(swidget, &instance->widget_list, list) { switch (swidget->id) { case snd_soc_dapm_scheduler: /* only complete static pipelines */ @@ -2428,8 +2443,10 @@ static int sof_ipc3_set_up_all_pipelines(struct snd_sof_dev *sdev, bool verify) * Free the PCM, its associated widgets and set the prepared flag to false for all PCMs that * did not get suspended(ex: paused streams) so the widgets can be set up again during resume. */ -static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev) +static int sof_tear_down_left_over_pipelines(struct snd_soc_component *component) { + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_sof_widget *swidget; int ret; @@ -2438,7 +2455,7 @@ static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev) * list is not NULL. This should only be true for paused streams at this point. * This is equivalent to the handling of FE DAI suspend trigger for running streams. */ - ret = sof_pcm_free_all_streams(sdev); + ret = sof_pcm_free_all_streams(component); if (ret) return ret; @@ -2446,7 +2463,7 @@ static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev) * free any left over DAI widgets. This is equivalent to the handling of suspend trigger * for the BE DAI for running streams. */ - list_for_each_entry(swidget, &sdev->widget_list, list) + list_for_each_entry(swidget, &instance->widget_list, list) if (WIDGET_IS_DAI(swidget->id) && swidget->use_count == 1) { ret = sof_widget_free(sdev, swidget); if (ret < 0) @@ -2456,14 +2473,17 @@ static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev) return 0; } -static int sof_ipc3_free_widgets_in_list(struct snd_sof_dev *sdev, bool include_scheduler, +static int sof_ipc3_free_widgets_in_list(struct snd_soc_component *component, + bool include_scheduler, bool *dyn_widgets, bool verify) { + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct sof_ipc_fw_version *v = &sdev->fw_ready.version; struct snd_sof_widget *swidget; int ret; - list_for_each_entry(swidget, &sdev->widget_list, list) { + list_for_each_entry(swidget, &instance->widget_list, list) { if (swidget->dynamic_pipeline_widget) { *dyn_widgets = true; continue; @@ -2498,14 +2518,19 @@ static int sof_ipc3_free_widgets_in_list(struct snd_sof_dev *sdev, bool include_ * For older firmware, this function doesn't free widgets for static pipelines during suspend. * It only resets use_count for all widgets. */ -static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verify) +static int sof_ipc3_tear_down_all_pipelines(struct snd_soc_component *component, bool verify) { + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct sof_ipc_fw_version *v = &sdev->fw_ready.version; struct snd_sof_widget *swidget; struct snd_sof_route *sroute; bool dyn_widgets = false; int ret; + if (!instance) + return 0; + /* * This function is called during suspend and for one-time topology verification during * first boot. In both cases, there is no need to protect swidget->use_count and @@ -2514,7 +2539,7 @@ static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif * widgets yet so that the secondary cores do not get powered down before all the widgets * associated with the scheduler are freed. */ - ret = sof_ipc3_free_widgets_in_list(sdev, false, &dyn_widgets, verify); + ret = sof_ipc3_free_widgets_in_list(component, false, &dyn_widgets, verify); if (ret < 0) return ret; @@ -2526,19 +2551,19 @@ static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif */ if (!verify && (dyn_widgets || SOF_FW_VER(v->major, v->minor, v->micro) >= SOF_FW_VER(2, 2, 0))) { - ret = sof_tear_down_left_over_pipelines(sdev); + ret = sof_tear_down_left_over_pipelines(component); if (ret < 0) { - dev_err(sdev->dev, "failed to tear down paused pipelines\n"); + dev_err(component->dev, "failed to tear down paused pipelines\n"); return ret; } } /* free all the scheduler widgets now. This will also power down the secondary cores */ - ret = sof_ipc3_free_widgets_in_list(sdev, true, &dyn_widgets, verify); + ret = sof_ipc3_free_widgets_in_list(component, true, &dyn_widgets, verify); if (ret < 0) return ret; - list_for_each_entry(sroute, &sdev->route_list, list) + list_for_each_entry(sroute, &instance->route_list, list) sroute->setup = false; /* @@ -2546,9 +2571,10 @@ static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif * to recover at this point but this will help root cause bad sequences leading to * more issues on resume */ - list_for_each_entry(swidget, &sdev->widget_list, list) { + list_for_each_entry(swidget, &instance->widget_list, list) { if (swidget->use_count != 0) { - dev_err(sdev->dev, "%s: widget %s is still in use: count %d\n", + dev_err(swidget->scomp->dev, + "%s: widget %s is still in use: count %d\n", __func__, swidget->widget->name, swidget->use_count); } } diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c index 85bb22bbe18d08..0a8cc3b8c8efcb 100644 --- a/sound/soc/sof/ipc3.c +++ b/sound/soc/sof/ipc3.c @@ -858,115 +858,7 @@ static int ipc3_fw_ready(struct snd_sof_dev *sdev, u32 cmd) return ipc3_init_reply_data_buffer(sdev); } -/* IPC stream position. */ -static void ipc3_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) -{ - struct snd_soc_component *scomp = sdev->component; - struct snd_sof_pcm_stream *stream; - struct sof_ipc_stream_posn posn; - struct snd_sof_pcm *spcm; - int direction, ret; - - spcm = snd_sof_find_spcm_comp(scomp, msg_id, &direction); - if (!spcm) { - dev_err(sdev->dev, "period elapsed for unknown stream, msg_id %d\n", - msg_id); - return; - } - - stream = &spcm->stream[direction]; - ret = snd_sof_ipc_msg_data(sdev, stream, &posn, sizeof(posn)); - if (ret < 0) { - dev_warn(sdev->dev, "failed to read stream position: %d\n", ret); - return; - } - - trace_sof_ipc3_period_elapsed_position(sdev, &posn); - - memcpy(&stream->posn, &posn, sizeof(posn)); - - if (spcm->pcm.compress) - snd_sof_compr_fragment_elapsed(stream->cstream); - else if (stream->substream->runtime && - !stream->substream->runtime->no_period_wakeup) - /* only inform ALSA for period_wakeup mode */ - snd_sof_pcm_period_elapsed(stream->substream); -} - -/* DSP notifies host of an XRUN within FW */ -static void ipc3_xrun(struct snd_sof_dev *sdev, u32 msg_id) -{ - struct snd_soc_component *scomp = sdev->component; - struct snd_sof_pcm_stream *stream; - struct sof_ipc_stream_posn posn; - struct snd_sof_pcm *spcm; - int direction, ret; - - spcm = snd_sof_find_spcm_comp(scomp, msg_id, &direction); - if (!spcm) { - dev_err(sdev->dev, "XRUN for unknown stream, msg_id %d\n", - msg_id); - return; - } - - stream = &spcm->stream[direction]; - ret = snd_sof_ipc_msg_data(sdev, stream, &posn, sizeof(posn)); - if (ret < 0) { - dev_warn(sdev->dev, "failed to read overrun position: %d\n", ret); - return; - } - - dev_dbg(sdev->dev, "posn XRUN: host %llx comp %d size %d\n", - posn.host_posn, posn.xrun_comp_id, posn.xrun_size); - -#if defined(CONFIG_SND_SOC_SOF_DEBUG_XRUN_STOP) - /* stop PCM on XRUN - used for pipeline debug */ - memcpy(&stream->posn, &posn, sizeof(posn)); - snd_pcm_stop_xrun(stream->substream); -#endif -} - -/* stream notifications from firmware */ -static void ipc3_stream_message(struct snd_sof_dev *sdev, void *msg_buf) -{ - struct sof_ipc_cmd_hdr *hdr = msg_buf; - u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; - u32 msg_id = SOF_IPC_MESSAGE_ID(hdr->cmd); - - switch (msg_type) { - case SOF_IPC_STREAM_POSITION: - ipc3_period_elapsed(sdev, msg_id); - break; - case SOF_IPC_STREAM_TRIG_XRUN: - ipc3_xrun(sdev, msg_id); - break; - default: - dev_err(sdev->dev, "unhandled stream message %#x\n", - msg_id); - break; - } -} - /* component notifications from firmware */ -static void ipc3_comp_notification(struct snd_sof_dev *sdev, void *msg_buf) -{ - const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; - struct sof_ipc_cmd_hdr *hdr = msg_buf; - u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; - - switch (msg_type) { - case SOF_IPC_COMP_GET_VALUE: - case SOF_IPC_COMP_GET_DATA: - break; - default: - dev_err(sdev->dev, "unhandled component message %#x\n", msg_type); - return; - } - - if (tplg_ops->control->update) - tplg_ops->control->update(sdev, msg_buf); -} - static void ipc3_trace_message(struct snd_sof_dev *sdev, void *msg_buf) { struct sof_ipc_cmd_hdr *hdr = msg_buf; @@ -1019,12 +911,8 @@ void sof_ipc3_do_rx_work(struct snd_sof_dev *sdev, struct sof_ipc_cmd_hdr *hdr, case SOF_IPC_GLB_COMPOUND: case SOF_IPC_GLB_TPLG_MSG: case SOF_IPC_GLB_PM_MSG: - break; case SOF_IPC_GLB_COMP_MSG: - rx_callback = ipc3_comp_notification; - break; case SOF_IPC_GLB_STREAM_MSG: - rx_callback = ipc3_stream_message; break; case SOF_IPC_GLB_TRACE_MSG: rx_callback = ipc3_trace_message; @@ -1140,9 +1028,7 @@ static const struct sof_ipc_pm_ops ipc3_pm_ops = { }; const struct sof_ipc_ops ipc3_ops = { - .tplg = &ipc3_tplg_ops, .pm = &ipc3_pm_ops, - .pcm = &ipc3_pcm_ops, .fw_loader = &ipc3_loader_ops, .fw_tracing = &ipc3_dtrace_ops, diff --git a/sound/soc/sof/ipc4-compress.c b/sound/soc/sof/ipc4-compress.c index 05cebb780032e7..6c74f4d0ba18bc 100644 --- a/sound/soc/sof/ipc4-compress.c +++ b/sound/soc/sof/ipc4-compress.c @@ -67,7 +67,6 @@ static u32 sof_ipc4_compr_calc_min_fragment_size(struct snd_sof_pcm_stream *sps) static int sof_ipc4_compr_open(struct snd_soc_component *component, struct snd_compr_stream *cstream) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_sof_pcm *spcm; int dir, ret; @@ -83,7 +82,7 @@ static int sof_ipc4_compr_open(struct snd_soc_component *component, spcm_dbg(spcm, dir, "Entry: open\n"); - ret = snd_sof_compr_platform_open(sdev, cstream); + ret = snd_sof_compr_platform_open(component, cstream); if (ret < 0) { spcm_err(spcm, dir, "platform compress open failed %d\n", ret); return ret; @@ -102,20 +101,20 @@ static int sof_ipc4_compr_stream_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, struct snd_compr_stream *cstream) { - const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); + const struct sof_ipc_pcm_ops *pcm_ops = snd_sof_component_get_pcm_ops(spcm->scomp); int dir = cstream->direction; int ret = 0; int err = 0; if (spcm->prepared[dir]) { if (spcm->pending_stop[dir]) - pcm_ops->trigger(sdev->component, NULL, spcm, + pcm_ops->trigger(spcm->scomp, NULL, spcm, SNDRV_PCM_TRIGGER_STOP, dir); - snd_sof_compr_platform_trigger(sdev, cstream, + snd_sof_compr_platform_trigger(spcm->scomp, cstream, SNDRV_PCM_TRIGGER_STOP); - err = pcm_ops->hw_free(sdev->component, NULL, spcm, dir); + err = pcm_ops->hw_free(spcm->scomp, NULL, spcm, dir); if (err < 0) spcm_err(spcm, dir, "pcm_ops->hw_free failed %d\n", err); } @@ -125,7 +124,7 @@ static int sof_ipc4_compr_stream_free(struct snd_sof_dev *sdev, spcm->stream[dir].cstream = NULL; /* reset the DMA */ - ret = snd_sof_compr_platform_hw_free(sdev, cstream); + ret = snd_sof_compr_platform_hw_free(spcm->scomp, cstream); if (ret < 0) { spcm_err(spcm, dir, "platform hw free failed %d\n", ret); if (!err) @@ -133,7 +132,7 @@ static int sof_ipc4_compr_stream_free(struct snd_sof_dev *sdev, } /* free widget list */ - ret = sof_widget_list_free(sdev, spcm, dir); + ret = sof_widget_list_free(spcm->scomp, spcm, dir); if (ret < 0 && err == 0) { spcm_err(spcm, dir, "sof_widget_list_free failed %d\n", ret); err = ret; @@ -145,7 +144,7 @@ static int sof_ipc4_compr_stream_free(struct snd_sof_dev *sdev, static int sof_ipc4_compr_free(struct snd_soc_component *component, struct snd_compr_stream *cstream) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_sof_pcm *spcm; int ret, err; @@ -159,13 +158,13 @@ static int sof_ipc4_compr_free(struct snd_soc_component *component, ret = sof_ipc4_compr_stream_free(sdev, spcm, cstream); /* unprepare and free the list of DAPM widgets */ - sof_widget_list_unprepare(sdev, spcm, cstream->direction); + sof_widget_list_unprepare(component, spcm, cstream->direction); cancel_work_sync(&spcm->stream[cstream->direction].period_elapsed_work); snd_compr_free_pages(cstream); - err = snd_sof_compr_platform_close(sdev, cstream); + err = snd_sof_compr_platform_close(component, cstream); if (err < 0) { spcm_err(spcm, cstream->direction, "platform compress close failed %d\n", err); @@ -188,7 +187,7 @@ static int sof_ipc4_compr_get_caps(struct snd_soc_component *component, struct snd_compr_stream *cstream, struct snd_compr_caps *caps) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct sof_ipc4_fw_data *ipc4_data = sdev->private; struct sof_ipc4_codec_info_data *codec_info = ipc4_data->codec_info; @@ -316,8 +315,8 @@ static int sof_ipc4_compr_set_params(struct snd_soc_component *component, struct snd_compr_stream *cstream, struct snd_compr_params *params) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_component_get_tplg_ops(component); struct sof_ipc4_compr_init_data *compr_data __free(kfree) = NULL; struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_sof_platform_stream_params *platform_params; @@ -337,7 +336,7 @@ static int sof_ipc4_compr_set_params(struct snd_soc_component *component, if (!spcm) return -EINVAL; - host_swidget = snd_sof_find_swidget_by_comp_id(sdev, spcm->stream[dir].comp_id); + host_swidget = snd_sof_find_swidget_by_comp_id(component, spcm->stream[dir].comp_id); if (!host_swidget) { spcm_err(spcm, dir, "failed to find host widget with comp_id %d\n", spcm->stream[dir].comp_id); @@ -423,7 +422,7 @@ static int sof_ipc4_compr_set_params(struct snd_soc_component *component, interval->max = cstream->runtime->buffer_size; platform_params = &spcm->platform_params[dir]; - ret = snd_sof_compr_platform_hw_params(sdev, cstream, compr_params, + ret = snd_sof_compr_platform_hw_params(component, cstream, compr_params, platform_params); if (ret < 0) { spcm_err(spcm, dir, "platform compress hw params failed\n"); @@ -469,7 +468,7 @@ static int sof_ipc4_compr_set_params(struct snd_soc_component *component, tplg_ops->host_config(sdev, host_swidget, platform_params); /* set up the widgets and pipelines in the DSP */ - ret = sof_widget_list_setup(sdev, spcm, &p, platform_params, dir); + ret = sof_widget_list_setup(component, spcm, &p, platform_params, dir); if (ret < 0) { spcm_err(spcm, dir, "widget list set up failed\n"); goto clear_init_ext; @@ -511,7 +510,6 @@ static int sof_ipc4_compr_get_params(struct snd_soc_component *component, struct snd_compr_stream *cstream, struct snd_codec *params) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_sof_pcm *spcm; /* TODO: we don't query the supported codecs for now, if the @@ -519,7 +517,7 @@ static int sof_ipc4_compr_get_params(struct snd_soc_component *component, */ spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) { - dev_err(sdev->dev, "%s: can't find spcm\n", __func__); + dev_err(component->dev, "%s: can't find spcm\n", __func__); return -EINVAL; } @@ -534,9 +532,8 @@ static int sof_ipc4_compr_get_params(struct snd_soc_component *component, static int sof_ipc4_compr_trigger(struct snd_soc_component *component, struct snd_compr_stream *cstream, int cmd) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); struct snd_soc_pcm_runtime *rtd = cstream->private_data; - const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); + const struct sof_ipc_pcm_ops *pcm_ops = snd_sof_component_get_pcm_ops(component); struct snd_sof_pcm *spcm; int dir = cstream->direction; bool trigger_platform = false; @@ -544,7 +541,7 @@ static int sof_ipc4_compr_trigger(struct snd_soc_component *component, spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) { - dev_err(sdev->dev, "%s: can't find spcm\n", __func__); + dev_err(component->dev, "%s: can't find spcm\n", __func__); return -EINVAL; } @@ -580,7 +577,7 @@ static int sof_ipc4_compr_trigger(struct snd_soc_component *component, } if (!ret && trigger_platform) { - ret = snd_sof_compr_platform_trigger(sdev, cstream, cmd); + ret = snd_sof_compr_platform_trigger(component, cstream, cmd); if (ret < 0) spcm_err(spcm, dir, "platform compress trigger start failed %d\n", @@ -653,7 +650,7 @@ static int sof_ipc4_compr_pointer(struct snd_soc_component *component, struct snd_compr_stream *cstream, struct snd_compr_tstamp64 *tstamp) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct sof_ipc4_timestamp_info *time_info; struct snd_pcm_hw_params *params; @@ -668,7 +665,7 @@ static int sof_ipc4_compr_pointer(struct snd_soc_component *component, params = &spcm->params[cstream->direction]; - ret = snd_sof_compr_platform_pointer(sdev, cstream, tstamp); + ret = snd_sof_compr_platform_pointer(component, cstream, tstamp); if (ret < 0) { spcm_err(spcm, cstream->direction, "platform compress pointer failed %d\n", ret); @@ -693,7 +690,7 @@ static int sof_ipc4_compr_pointer(struct snd_soc_component *component, } if (!time_info->llp_offset) { - dai_cnt = snd_sof_compr_get_dai_frame_counter(sdev, cstream); + dai_cnt = snd_sof_compr_get_dai_frame_counter(component, cstream); } else { struct sof_ipc4_llp_reading_slot llp; @@ -717,20 +714,22 @@ static int sof_ipc4_compr_pointer(struct snd_soc_component *component, return 0; } -void sof_ipc4_compr_drain_done(struct snd_sof_dev *sdev, void *ipc_message) +void sof_ipc4_compr_drain_done(struct snd_soc_component *scomp, void *ipc_message) { struct sof_ipc4_msg *ipc4_msg = ipc_message; struct sof_ipc4_notify_module_data *ndata = ipc4_msg->data_ptr; struct snd_sof_widget *swidget, *host_swidget; + struct snd_sof_audio_instance *instance; bool widget_found = false; struct snd_sof_pcm *spcm; int dir; /* Find the swidget based on ndata->module_id and ndata->instance_id */ - swidget = sof_ipc4_find_swidget_by_ids(sdev, ndata->module_id, + swidget = sof_ipc4_find_swidget_by_ids(scomp, ndata->module_id, ndata->instance_id); if (!swidget) { - dev_err(sdev->dev, "%s: Failed to find widget for module %u.%u\n", + /* The module is not owned by this component */ + dev_dbg(scomp->dev, "%s: No widget for module %u.%u\n", __func__, ndata->module_id, ndata->instance_id); return; } @@ -739,7 +738,8 @@ void sof_ipc4_compr_drain_done(struct snd_sof_dev *sdev, void *ipc_message) return; /* Find the swidget of the host copier on the same pipeline */ - list_for_each_entry(host_swidget, &sdev->widget_list, list) { + instance = snd_sof_component_get_audio_instance(swidget->scomp); + list_for_each_entry(host_swidget, &instance->widget_list, list) { if (WIDGET_IS_AIF(host_swidget->id) && host_swidget->pipeline_id == swidget->pipeline_id) { widget_found = true; @@ -748,15 +748,15 @@ void sof_ipc4_compr_drain_done(struct snd_sof_dev *sdev, void *ipc_message) } if (!widget_found) { - dev_err(sdev->dev, "%s: Host widget not found for pipeline: %s\n", + dev_err(swidget->scomp->dev, "%s: Host widget not found for pipeline: %s\n", __func__, swidget->spipe->pipe_widget->widget->name); return; } /* Look up the spcm of the host copier */ - spcm = snd_sof_find_spcm_comp(sdev->component, host_swidget->comp_id, &dir); + spcm = snd_sof_find_spcm_comp(host_swidget->scomp, host_swidget->comp_id, &dir); if (!spcm) { - dev_err(sdev->dev, "%s: Stream cannot be found for %s\n", __func__, + dev_err(host_swidget->scomp->dev, "%s: Stream cannot be found for %s\n", __func__, host_swidget->widget->name); return; } diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c index 8d86d32a16caea..e8d85a5d4b1c52 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -17,14 +17,15 @@ static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool set, bool lock) { struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_ops *iops = sdev->ipc->ops; struct snd_sof_widget *swidget; bool widget_found = false; int ret = 0; /* find widget associated with the control */ - list_for_each_entry(swidget, &sdev->widget_list, list) { + list_for_each_entry(swidget, &instance->widget_list, list) { if (swidget->comp_id == scontrol->comp_id) { widget_found = true; break; @@ -81,9 +82,9 @@ static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, return ret; } -static int -sof_ipc4_set_volume_data(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, - struct snd_sof_control *scontrol, bool lock) +static int sof_ipc4_set_volume_data(struct snd_sof_widget *swidget, + struct snd_sof_control *scontrol, + bool lock) { struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; struct sof_ipc4_gain *gain = swidget->private; @@ -126,7 +127,7 @@ sof_ipc4_set_volume_data(struct snd_sof_dev *sdev, struct snd_sof_widget *swidge ret = sof_ipc4_set_get_kcontrol_data(scontrol, &msg, true, lock); if (ret < 0) { - dev_err(sdev->dev, "Failed to set volume update for %s\n", + dev_err(swidget->scomp->dev, "Failed to set volume update for %s\n", scontrol->name); return ret; } @@ -143,7 +144,7 @@ static bool sof_ipc4_volume_put(struct snd_sof_control *scontrol, { struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); unsigned int channels = scontrol->num_channels; struct snd_sof_widget *swidget; bool widget_found = false; @@ -165,7 +166,7 @@ static bool sof_ipc4_volume_put(struct snd_sof_control *scontrol, return change; /* find widget associated with the control */ - list_for_each_entry(swidget, &sdev->widget_list, list) { + list_for_each_entry(swidget, &instance->widget_list, list) { if (swidget->comp_id == scontrol->comp_id) { widget_found = true; break; @@ -177,7 +178,7 @@ static bool sof_ipc4_volume_put(struct snd_sof_control *scontrol, return false; } - ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol, true); + ret = sof_ipc4_set_volume_data(swidget, scontrol, true); if (ret < 0) return false; @@ -199,10 +200,9 @@ static int sof_ipc4_volume_get(struct snd_sof_control *scontrol, return 0; } -static int -sof_ipc4_set_generic_control_data(struct snd_sof_dev *sdev, - struct snd_sof_widget *swidget, - struct snd_sof_control *scontrol, bool lock) +static int sof_ipc4_set_generic_control_data(struct snd_sof_widget *swidget, + struct snd_sof_control *scontrol, + bool lock) { struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; struct sof_ipc4_control_msg_payload *data; @@ -229,7 +229,7 @@ sof_ipc4_set_generic_control_data(struct snd_sof_dev *sdev, ret = sof_ipc4_set_get_kcontrol_data(scontrol, &msg, true, lock); if (ret < 0) - dev_err(sdev->dev, "Failed to set control update for %s\n", + dev_err(scontrol->scomp->dev, "Failed to set control update for %s\n", scontrol->name); kfree(data); @@ -383,7 +383,7 @@ static bool sof_ipc4_switch_put(struct snd_sof_control *scontrol, { struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct snd_sof_widget *swidget; bool widget_found = false; bool change = false; @@ -403,7 +403,7 @@ static bool sof_ipc4_switch_put(struct snd_sof_control *scontrol, return change; /* find widget associated with the control */ - list_for_each_entry(swidget, &sdev->widget_list, list) { + list_for_each_entry(swidget, &instance->widget_list, list) { if (swidget->comp_id == scontrol->comp_id) { widget_found = true; break; @@ -415,7 +415,7 @@ static bool sof_ipc4_switch_put(struct snd_sof_control *scontrol, return false; } - ret = sof_ipc4_set_generic_control_data(sdev, swidget, scontrol, true); + ret = sof_ipc4_set_generic_control_data(swidget, scontrol, true); if (ret < 0) return false; @@ -442,7 +442,7 @@ static bool sof_ipc4_enum_put(struct snd_sof_control *scontrol, { struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct snd_sof_widget *swidget; bool widget_found = false; bool change = false; @@ -462,7 +462,7 @@ static bool sof_ipc4_enum_put(struct snd_sof_control *scontrol, return change; /* find widget associated with the control */ - list_for_each_entry(swidget, &sdev->widget_list, list) { + list_for_each_entry(swidget, &instance->widget_list, list) { if (swidget->comp_id == scontrol->comp_id) { widget_found = true; break; @@ -474,7 +474,7 @@ static bool sof_ipc4_enum_put(struct snd_sof_control *scontrol, return false; } - ret = sof_ipc4_set_generic_control_data(sdev, swidget, scontrol, true); + ret = sof_ipc4_set_generic_control_data(swidget, scontrol, true); if (ret < 0) return false; @@ -496,22 +496,22 @@ static int sof_ipc4_enum_get(struct snd_sof_control *scontrol, return 0; } -static int sof_ipc4_set_get_bytes_data(struct snd_sof_dev *sdev, - struct snd_sof_control *scontrol, +static int sof_ipc4_set_get_bytes_data(struct snd_sof_control *scontrol, bool set, bool lock) { struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; + struct snd_soc_component *scomp = scontrol->scomp; struct sof_abi_hdr *data = cdata->data; struct sof_ipc4_msg msg; int ret = 0; /* Send the new data to the firmware only if it is powered up */ if (set) { - if (!pm_runtime_active(sdev->dev)) + if (!pm_runtime_active(scomp->dev)) return 0; if (!data->size) { - dev_dbg(sdev->dev, "%s: No data to be sent.\n", + dev_dbg(scomp->dev, "%s: No data to be sent.\n", scontrol->name); return 0; } @@ -535,7 +535,7 @@ static int sof_ipc4_set_get_bytes_data(struct snd_sof_dev *sdev, ret = sof_ipc4_set_get_kcontrol_data(scontrol, &msg, set, lock); if (ret < 0) { - dev_err(sdev->dev, "Failed to %s for %s\n", + dev_err(scomp->dev, "Failed to %s for %s\n", set ? "set bytes update" : "get bytes", scontrol->name); } else if (!set) { @@ -552,7 +552,6 @@ static int sof_ipc4_bytes_put(struct snd_sof_control *scontrol, { struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_abi_hdr *data = cdata->data; const struct sof_abi_hdr *new_hdr = (const struct sof_abi_hdr *)ucontrol->value.bytes.data; @@ -580,7 +579,7 @@ static int sof_ipc4_bytes_put(struct snd_sof_control *scontrol, /* copy from kcontrol */ memcpy(data, ucontrol->value.bytes.data, size); - ret = sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true); + ret = sof_ipc4_set_get_bytes_data(scontrol, true, true); if (!ret) /* Update the cdata size */ scontrol->size = sizeof(*cdata) + size; @@ -626,7 +625,6 @@ static int sof_ipc4_bytes_ext_put(struct snd_sof_control *scontrol, struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data; struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_abi_hdr *data = cdata->data; struct sof_abi_hdr abi_hdr; struct snd_ctl_tlv header; @@ -700,7 +698,7 @@ static int sof_ipc4_bytes_ext_put(struct snd_sof_control *scontrol, /* Update the cdata size */ scontrol->size = sizeof(*cdata) + header.length; - return sof_ipc4_set_get_bytes_data(sdev, scontrol, true, true); + return sof_ipc4_set_get_bytes_data(scontrol, true, true); } static int _sof_ipc4_bytes_ext_get(struct snd_sof_control *scontrol, @@ -725,8 +723,7 @@ static int _sof_ipc4_bytes_ext_get(struct snd_sof_control *scontrol, /* get all the component data from DSP */ if (from_dsp) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - int ret = sof_ipc4_set_get_bytes_data(sdev, scontrol, false, true); + int ret = sof_ipc4_set_get_bytes_data(scontrol, false, true); if (ret < 0) return ret; @@ -777,24 +774,25 @@ static int sof_ipc4_bytes_ext_volatile_get(struct snd_sof_control *scontrol, return _sof_ipc4_bytes_ext_get(scontrol, binary_data, size, true); } -static int -sof_ipc4_volsw_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, - struct snd_sof_control *scontrol) +static int sof_ipc4_volsw_setup(struct snd_sof_widget *swidget, + struct snd_sof_control *scontrol) + { if (scontrol->max == 1) - return sof_ipc4_set_generic_control_data(sdev, swidget, scontrol, false); + return sof_ipc4_set_generic_control_data(swidget, scontrol, false); - return sof_ipc4_set_volume_data(sdev, swidget, scontrol, false); + return sof_ipc4_set_volume_data(swidget, scontrol, false); } #define PARAM_ID_FROM_EXTENSION(_ext) (((_ext) & SOF_IPC4_MOD_EXT_MSG_PARAM_ID_MASK) \ >> SOF_IPC4_MOD_EXT_MSG_PARAM_ID_SHIFT) -static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) +static void sof_ipc4_control_update(struct snd_soc_component *scomp, void *ipc_message) { struct sof_ipc4_msg *ipc4_msg = ipc_message; struct sof_ipc4_notify_module_data *ndata = ipc4_msg->data_ptr; struct sof_ipc4_control_msg_payload *msg_data; + struct snd_sof_audio_instance *instance; struct sof_ipc4_control_data *cdata; struct snd_soc_dapm_widget *widget; struct snd_sof_control *scontrol; @@ -805,7 +803,7 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) int i, type; if (ndata->event_data_size < sizeof(*msg_data)) { - dev_err(sdev->dev, + dev_err(scomp->dev, "%s: Invalid event data size for module %u.%u: %u\n", __func__, ndata->module_id, ndata->instance_id, ndata->event_data_size); @@ -824,7 +822,7 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) type = SND_SOC_TPLG_TYPE_BYTES; break; default: - dev_err(sdev->dev, + dev_err(scomp->dev, "%s: Invalid control type for module %u.%u: %u\n", __func__, ndata->module_id, ndata->instance_id, event_param_id); @@ -832,17 +830,19 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) } /* Find the swidget based on ndata->module_id and ndata->instance_id */ - swidget = sof_ipc4_find_swidget_by_ids(sdev, ndata->module_id, + swidget = sof_ipc4_find_swidget_by_ids(scomp, ndata->module_id, ndata->instance_id); if (!swidget) { - dev_err(sdev->dev, "%s: Failed to find widget for module %u.%u\n", + /* The module is not owned by this component */ + dev_dbg(scomp->dev, "%s: No widget for module %u.%u\n", __func__, ndata->module_id, ndata->instance_id); return; } /* Find the scontrol which is the source of the notification */ msg_data = (struct sof_ipc4_control_msg_payload *)ndata->event_data; - list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { + instance = snd_sof_component_get_audio_instance(swidget->scomp); + list_for_each_entry(scontrol, &instance->kcontrol_list, list) { if (scontrol->comp_id == swidget->comp_id) { u32 local_param_id; @@ -861,7 +861,7 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) } if (!scontrol_found) { - dev_err(sdev->dev, + dev_err(swidget->scomp->dev, "%s: Failed to find control on widget %s: %u:%u\n", __func__, swidget->widget->name, ndata->event_id & 0xffff, msg_data->id); @@ -878,7 +878,7 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) size_t source_size = struct_size(msg_data, data, msg_data->num_elems); if (source_size > ndata->event_data_size) { - dev_warn(sdev->dev, + dev_warn(swidget->scomp->dev, "%s: invalid bytes notification size for %s (%zu, %u)\n", __func__, scontrol->name, source_size, ndata->event_data_size); @@ -887,7 +887,7 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) } if (msg_data->num_elems > scontrol->max_size - sizeof(*data)) { - dev_warn(sdev->dev, + dev_warn(swidget->scomp->dev, "%s: no space for data in %s (%u, %zu)\n", __func__, scontrol->name, msg_data->num_elems, scontrol->max_size - sizeof(*data)); @@ -900,7 +900,7 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) size_t source_size = struct_size(msg_data, chanv, msg_data->num_elems); if (source_size > ndata->event_data_size) { - dev_warn(sdev->dev, + dev_warn(swidget->scomp->dev, "%s: invalid channel notification size for %s (%zu, %u)\n", __func__, scontrol->name, source_size, ndata->event_data_size); @@ -912,7 +912,7 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) u32 channel = msg_data->chanv[i].channel; if (channel >= scontrol->num_channels) { - dev_warn(sdev->dev, + dev_warn(swidget->scomp->dev, "Invalid channel index for %s: %u\n", scontrol->name, i); @@ -961,32 +961,32 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) /* set up all controls for the widget */ static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { + struct snd_sof_audio_instance *instance = + snd_sof_component_get_audio_instance(swidget->scomp); struct snd_sof_control *scontrol; int ret = 0; - list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { + list_for_each_entry(scontrol, &instance->kcontrol_list, list) { if (scontrol->comp_id == swidget->comp_id) { switch (scontrol->info_type) { case SND_SOC_TPLG_CTL_VOLSW: case SND_SOC_TPLG_CTL_VOLSW_SX: case SND_SOC_TPLG_CTL_VOLSW_XR_SX: - ret = sof_ipc4_volsw_setup(sdev, swidget, scontrol); + ret = sof_ipc4_volsw_setup(swidget, scontrol); break; case SND_SOC_TPLG_CTL_BYTES: - ret = sof_ipc4_set_get_bytes_data(sdev, scontrol, - true, false); + ret = sof_ipc4_set_get_bytes_data(scontrol, true, false); break; case SND_SOC_TPLG_CTL_ENUM: case SND_SOC_TPLG_CTL_ENUM_VALUE: - ret = sof_ipc4_set_generic_control_data(sdev, swidget, - scontrol, false); + ret = sof_ipc4_set_generic_control_data(swidget, scontrol, false); break; default: break; } if (ret < 0) { - dev_err(sdev->dev, + dev_err(swidget->scomp->dev, "kcontrol %d set up failed for widget %s\n", scontrol->comp_id, swidget->widget->name); return ret; diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c index 8778cac0d52520..4791c3ff13c734 100644 --- a/sound/soc/sof/ipc4-loader.c +++ b/sound/soc/sof/ipc4-loader.c @@ -395,6 +395,7 @@ struct sof_ipc4_fw_module *sof_ipc4_find_module_by_uuid(struct snd_sof_dev *sdev return NULL; } +EXPORT_SYMBOL(sof_ipc4_find_module_by_uuid); static int sof_ipc4_validate_firmware(struct snd_sof_dev *sdev) { @@ -669,6 +670,7 @@ void sof_ipc4_update_cpc_from_manifest(struct snd_sof_dev *sdev, &fw_module->man4_module_entry.uuid, msg, basecfg->ibs, basecfg->obs); } +EXPORT_SYMBOL(sof_ipc4_update_cpc_from_manifest); const struct sof_ipc_fw_loader_ops ipc4_loader_ops = { .validate = sof_ipc4_validate_firmware, diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 5d8fc9b25bcc82..b8c0fc9c9a53a6 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -14,6 +14,7 @@ #include "ipc4-priv.h" #include "ipc4-topology.h" #include "ipc4-fw-reg.h" +#include "sof-client.h" /** * struct sof_ipc4_pcm_stream_priv - IPC4 specific private data @@ -64,16 +65,19 @@ char *sof_ipc4_set_multi_pipeline_state_debug(struct snd_sof_dev *sdev, char *bu return buf; } -static int sof_ipc4_set_multi_pipeline_state(struct snd_sof_dev *sdev, u32 state, +static int sof_ipc4_set_multi_pipeline_state(struct snd_soc_component *component, u32 state, struct ipc4_pipeline_set_state_data *trigger_list) { + struct sof_client_dev *cdev = snd_sof_component_get_cdev(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct sof_ipc4_msg msg = {{ 0 }}; u32 primary, ipc_size; char debug_buf[32]; /* trigger a single pipeline */ if (trigger_list->count == 1) - return sof_ipc4_set_pipeline_state(sdev, trigger_list->pipeline_instance_ids[0], + return sof_ipc4_set_pipeline_state(cdev, + trigger_list->pipeline_instance_ids[0], state); dev_dbg(sdev->dev, "Set pipelines %s to state %d%s", @@ -95,29 +99,9 @@ static int sof_ipc4_set_multi_pipeline_state(struct snd_sof_dev *sdev, u32 state msg.data_size = ipc_size; msg.data_ptr = trigger_list; - return sof_ipc_tx_message_no_reply(sdev->ipc, &msg, ipc_size); + return sof_client_ipc_tx_message_no_reply(cdev, &msg); } -int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 instance_id, u32 state) -{ - struct sof_ipc4_msg msg = {{ 0 }}; - u32 primary; - - dev_dbg(sdev->dev, "Set pipeline %d to state %d%s", instance_id, state, - sof_ipc4_pipeline_state_str(state)); - - primary = state; - primary |= SOF_IPC4_GLB_PIPE_STATE_ID(instance_id); - primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_SET_PIPELINE_STATE); - primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); - primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG); - - msg.primary = primary; - - return sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0); -} -EXPORT_SYMBOL(sof_ipc4_set_pipeline_state); - static void sof_ipc4_add_pipeline_by_priority(struct ipc4_pipeline_set_state_data *trigger_list, struct snd_sof_widget *pipe_widget, s8 *pipe_priority, bool ascend) @@ -293,6 +277,7 @@ sof_ipc4_update_pipeline_state(struct snd_sof_dev *sdev, int state, int cmd, */ static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev, + struct sof_client_dev *cdev, struct snd_sof_pcm *spcm, int direction, struct snd_sof_pcm_stream_pipeline_list *pipeline_list, int state, int cmd) @@ -386,7 +371,7 @@ static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev, if (enable) msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_ENABLE_MASK; - ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0); + ret = sof_client_ipc_tx_message_no_reply(cdev, &msg); /* Update the ChainDMA allocation state */ if (!ret) stream_priv->chain_dma_allocated = allocate; @@ -398,7 +383,8 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, struct snd_pcm_substream *substream, int state, int cmd, struct snd_sof_pcm *spcm, int dir) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + struct sof_client_dev *cdev = snd_sof_component_get_cdev(component); struct snd_sof_pcm_stream_pipeline_list *pipeline_list; struct sof_ipc4_fw_data *ipc4_data = sdev->private; struct ipc4_pipeline_set_state_data *trigger_list; @@ -434,7 +420,7 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, time_info = sof_ipc4_sps_to_time_info(&spcm->stream[dir]); - ret = sof_ipc4_chain_dma_trigger(sdev, spcm, dir, + ret = sof_ipc4_chain_dma_trigger(sdev, cdev, spcm, dir, pipeline_list, state, cmd); if (ret || !time_info) return ret; @@ -448,8 +434,8 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, * never get here with compress. */ if (substream) { - u64 pos = snd_sof_pcm_get_dai_frame_counter(sdev, component, - substream); + u64 pos = snd_sof_pcm_get_dai_frame_counter(component, + substream); time_info->stream_end_offset += pos; } @@ -514,7 +500,7 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, * set paused state for pipelines if the final state is PAUSED or when the pipeline * is set to RUNNING for the first time after the PCM is started. */ - ret = sof_ipc4_set_multi_pipeline_state(sdev, SOF_IPC4_PIPE_PAUSED, trigger_list); + ret = sof_ipc4_set_multi_pipeline_state(component, SOF_IPC4_PIPE_PAUSED, trigger_list); if (ret < 0) { spcm_err(spcm, dir, "failed to pause all pipelines\n"); /* @@ -557,7 +543,7 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, } skip_pause_transition: /* else set the RUNNING/RESET state in the DSP */ - ret = sof_ipc4_set_multi_pipeline_state(sdev, state, trigger_list); + ret = sof_ipc4_set_multi_pipeline_state(component, state, trigger_list); if (ret < 0) { spcm_err(spcm, dir, "failed to set final state %d for all pipelines\n", @@ -630,10 +616,11 @@ static int sof_ipc4_pcm_hw_free(struct snd_soc_component *component, return sof_ipc4_trigger_pipelines(component, substream, SOF_IPC4_PIPE_RESET, 0, spcm, dir); } -static int ipc4_ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, +static int ipc4_ssp_dai_config_pcm_params_match(struct snd_soc_component *component, const char *link_name, struct snd_pcm_hw_params *params) { + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(component); struct snd_sof_dai_link *slink; struct snd_sof_dai *dai; bool dai_link_found = false; @@ -641,7 +628,7 @@ static int ipc4_ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, bool partial_match; int i; - list_for_each_entry(slink, &sdev->dai_link_list, list) { + list_for_each_entry(slink, &instance->dai_link_list, list) { if (!strcmp(slink->link->name, link_name)) { dai_link_found = true; break; @@ -678,17 +665,17 @@ static int ipc4_ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, } if (current_config < 0) { - dev_err(sdev->dev, + dev_err(component->dev, "%s: No suitable hw_config found for %s (num_hw_configs: %d)\n", __func__, slink->link->name, slink->num_hw_configs); return -EINVAL; } - dev_dbg(sdev->dev, + dev_dbg(component->dev, "hw_config for %s: %d (num_hw_configs: %d) with %s match\n", slink->link->name, current_config, slink->num_hw_configs, partial_match ? "partial" : "full"); - list_for_each_entry(dai, &sdev->dai_list, list) + list_for_each_entry(dai, &instance->dai_list, list) if (!strcmp(slink->link->name, dai->name)) dai->current_config = current_config; @@ -699,7 +686,7 @@ static int ipc4_ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, * Fixup DAI link parameters for sampling rate based on * DAI copier configuration. */ -static int sof_ipc4_pcm_dai_link_fixup_rate(struct snd_sof_dev *sdev, +static int sof_ipc4_pcm_dai_link_fixup_rate(struct snd_soc_component *component, struct snd_pcm_hw_params *params, struct sof_ipc4_copier *ipc4_copier) { @@ -740,7 +727,7 @@ static int sof_ipc4_pcm_dai_link_fixup_rate(struct snd_sof_dev *sdev, */ if (!fe_be_rate_match) { if (!single_be_rate) { - dev_err(sdev->dev, "Unable to select sampling rate for DAI link\n"); + dev_err(component->dev, "Unable to select sampling rate for DAI link\n"); return -EINVAL; } @@ -751,7 +738,7 @@ static int sof_ipc4_pcm_dai_link_fixup_rate(struct snd_sof_dev *sdev, return 0; } -static int sof_ipc4_pcm_dai_link_fixup_channels(struct snd_sof_dev *sdev, +static int sof_ipc4_pcm_dai_link_fixup_channels(struct snd_soc_component *component, struct snd_pcm_hw_params *params, struct sof_ipc4_copier *ipc4_copier) { @@ -792,7 +779,7 @@ static int sof_ipc4_pcm_dai_link_fixup_channels(struct snd_sof_dev *sdev, */ if (!fe_be_match) { if (!single_be_channels) { - dev_err(sdev->dev, "Unable to select channels for DAI link\n"); + dev_err(component->dev, "Unable to select channels for DAI link\n"); return -EINVAL; } @@ -809,7 +796,7 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); struct snd_sof_dai *dai = snd_sof_find_dai(component, rtd->dai_link->name); struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); struct sof_ipc4_audio_format *ipc4_fmt; struct sof_ipc4_copier *ipc4_copier; @@ -862,11 +849,11 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, } } - ret = sof_ipc4_pcm_dai_link_fixup_rate(sdev, params, ipc4_copier); + ret = sof_ipc4_pcm_dai_link_fixup_rate(component, params, ipc4_copier); if (ret) return ret; - ret = sof_ipc4_pcm_dai_link_fixup_channels(sdev, params, ipc4_copier); + ret = sof_ipc4_pcm_dai_link_fixup_channels(component, params, ipc4_copier); if (ret) return ret; @@ -892,7 +879,7 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, } if (ipc4_copier->dai_type == SOF_DAI_INTEL_SSP) - return ipc4_ssp_dai_config_pcm_params_match(sdev, + return ipc4_ssp_dai_config_pcm_params_match(component, (char *)rtd->dai_link->name, params); @@ -923,6 +910,8 @@ static void sof_ipc4_pcm_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm) { + struct snd_sof_audio_instance *instance = + snd_sof_component_get_audio_instance(spcm->scomp); struct snd_sof_pcm_stream_pipeline_list *pipeline_list; struct sof_ipc4_fw_data *ipc4_data = sdev->private; struct sof_ipc4_pcm_stream_priv *stream_priv; @@ -940,7 +929,7 @@ static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm support_info = false; /* For delay reporting the get_host_byte_counter callback is needed */ - if (!sof_ops(sdev) || !sof_ops(sdev)->get_host_byte_counter) + if (!instance || !instance->audio_ops->get_host_byte_counter) support_info = false; for_each_pcm_streams(stream) { @@ -1054,7 +1043,7 @@ static int sof_ipc4_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_hw_params *params, struct snd_sof_platform_stream_params *platform_params) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct sof_ipc4_timestamp_info *time_info; struct snd_sof_pcm *spcm; @@ -1179,7 +1168,7 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream, snd_pcm_uframes_t *pointer) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct sof_ipc4_timestamp_info *time_info; struct sof_ipc4_llp_reading_slot llp; @@ -1210,7 +1199,7 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component, } /* For delay calculation we need the host counter */ - host_cnt = snd_sof_pcm_get_host_byte_counter(sdev, component, substream); + host_cnt = snd_sof_pcm_get_host_byte_counter(component, substream); /* Store the original value to host_ptr */ host_ptr = host_cnt; @@ -1224,7 +1213,7 @@ static int sof_ipc4_pcm_pointer(struct snd_soc_component *component, * available. */ if (!time_info->llp_offset) { - dai_cnt = snd_sof_pcm_get_dai_frame_counter(sdev, component, substream); + dai_cnt = snd_sof_pcm_get_dai_frame_counter(component, substream); if (!dai_cnt) return -EOPNOTSUPP; } else { diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index 31f7d68029ce71..34af4463da388c 100644 --- a/sound/soc/sof/ipc4-priv.h +++ b/sound/soc/sof/ipc4-priv.h @@ -140,7 +140,7 @@ extern const struct sof_ipc_fw_tracing_ops ipc4_mtrace_ops; extern const struct snd_compress_ops sof_ipc4_compressed_ops; #endif -int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 instance_id, u32 state); +int sof_ipc4_set_pipeline_state(struct sof_client_dev *cdev, u32 instance_id, u32 state); int sof_ipc4_mtrace_update_pos(struct snd_sof_dev *sdev, int core); int sof_ipc4_complete_split_release(struct snd_sof_dev *sdev); @@ -149,7 +149,7 @@ 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 snd_sof_widget *sof_ipc4_find_swidget_by_ids(struct snd_sof_dev *sdev, +struct snd_sof_widget *sof_ipc4_find_swidget_by_ids(struct snd_soc_component *scomp, u32 module_id, int instance_id); struct sof_ipc4_base_module_cfg; @@ -174,9 +174,9 @@ int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev, u64 sof_ipc4_frames_dai_to_host(struct sof_ipc4_timestamp_info *time_info, u64 value); #if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS) -void sof_ipc4_compr_drain_done(struct snd_sof_dev *sdev, void *ipc_message); +void sof_ipc4_compr_drain_done(struct snd_soc_component *scomp, void *ipc_message); #else -static inline void sof_ipc4_compr_drain_done(struct snd_sof_dev *sdev, +static inline void sof_ipc4_compr_drain_done(struct snd_soc_component *scomp, void *ipc_message) { } #endif diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 09246e4c378097..6bcefe45653451 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -14,6 +14,7 @@ #include #include "sof-priv.h" #include "sof-audio.h" +#include "sof-client.h" #include "ipc4-priv.h" #include "ipc4-topology.h" #include "ops.h" @@ -218,12 +219,16 @@ static const struct sof_token_info ipc4_token_list[SOF_TOKEN_COUNT] = { [SOF_ASRC_TOKENS] = {"ASRC tokens", asrc_tokens, ARRAY_SIZE(asrc_tokens)}, }; -struct snd_sof_widget *sof_ipc4_find_swidget_by_ids(struct snd_sof_dev *sdev, +struct snd_sof_widget *sof_ipc4_find_swidget_by_ids(struct snd_soc_component *scomp, u32 module_id, int instance_id) { + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct snd_sof_widget *swidget; - list_for_each_entry(swidget, &sdev->widget_list, list) { + if (!instance) + return NULL; + + list_for_each_entry(swidget, &instance->widget_list, list) { struct sof_ipc4_fw_module *fw_module = swidget->module_info; /* Only active module instances have valid instance_id */ @@ -560,14 +565,14 @@ static void sof_ipc4_widget_free_comp_pipeline(struct snd_sof_widget *swidget) static int sof_ipc4_widget_set_module_info(struct snd_sof_widget *swidget) { struct snd_soc_component *scomp = swidget->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); swidget->module_info = sof_ipc4_find_module_by_uuid(sdev, &swidget->uuid); if (swidget->module_info) return 0; - dev_err(sdev->dev, "failed to find module info for widget %s with UUID %pUL\n", + dev_err(scomp->dev, "failed to find module info for widget %s with UUID %pUL\n", swidget->widget->name, &swidget->uuid); return -EINVAL; } @@ -610,12 +615,12 @@ static int sof_ipc4_widget_setup_msg(struct snd_sof_widget *swidget, struct sof_ static void sof_ipc4_widget_update_kcontrol_module_id(struct snd_sof_widget *swidget) { struct snd_soc_component *scomp = swidget->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct sof_ipc4_fw_module *fw_module = swidget->module_info; struct snd_sof_control *scontrol; /* update module ID for all kcontrols for this widget */ - list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { + list_for_each_entry(scontrol, &instance->kcontrol_list, list) { if (scontrol->comp_id == swidget->comp_id) { struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; struct sof_ipc4_msg *msg = &cdata->msg; @@ -809,7 +814,8 @@ static int sof_ipc4_widget_setup_comp_dai(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 snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct snd_sof_dai *dai = swidget->private; struct sof_ipc4_copier *ipc4_copier; struct snd_sof_widget *pipe_widget; @@ -893,7 +899,7 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) goto free_available_fmt; } - list_for_each_entry(w, &sdev->widget_list, list) { + list_for_each_entry(w, &instance->widget_list, list) { struct snd_sof_dai *alh_dai; if (!WIDGET_IS_DAI(w->id) || !w->widget->sname || @@ -989,7 +995,7 @@ static void sof_ipc4_widget_free_comp_dai(struct snd_sof_widget *swidget) static int sof_ipc4_widget_setup_comp_pipeline(struct snd_sof_widget *swidget) { struct snd_soc_component *scomp = swidget->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct sof_ipc4_pipeline *pipeline; struct snd_sof_pipeline *spipe = swidget->spipe; int ret; @@ -1382,9 +1388,11 @@ static void sof_ipc4_widget_free_comp_process(struct snd_sof_widget *swidget) } static void -sof_ipc4_update_resource_usage(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, +sof_ipc4_update_resource_usage(struct snd_sof_widget *swidget, struct sof_ipc4_base_module_cfg *base_config) { + struct snd_soc_component *scomp = swidget->scomp; + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct sof_ipc4_fw_module *fw_module = swidget->module_info; struct snd_sof_widget *pipe_widget; struct sof_ipc4_pipeline *pipeline; @@ -1426,7 +1434,7 @@ sof_ipc4_update_resource_usage(struct snd_sof_dev *sdev, struct snd_sof_widget * if (pipe_widget->stack_bytes < swidget->stack_bytes) pipe_widget->stack_bytes = swidget->stack_bytes; - dev_dbg(sdev->dev, "%s mem reqs to %s heap %u stack %u", + dev_dbg(scomp->dev, "%s mem reqs to %s heap %u stack %u", swidget->widget->name, pipe_widget->widget->name, pipe_widget->heap_bytes, pipe_widget->stack_bytes); } @@ -1435,26 +1443,26 @@ sof_ipc4_update_resource_usage(struct snd_sof_dev *sdev, struct snd_sof_widget * sof_ipc4_update_cpc_from_manifest(sdev, fw_module, base_config); if (ignore_cpc) { - dev_dbg(sdev->dev, "%s: ibs / obs: %u / %u, forcing cpc to 0 from %u\n", + dev_dbg(scomp->dev, "%s: ibs / obs: %u / %u, forcing cpc to 0 from %u\n", swidget->widget->name, base_config->ibs, base_config->obs, base_config->cpc); base_config->cpc = 0; } else { - dev_dbg(sdev->dev, "%s: ibs / obs / cpc: %u / %u / %u\n", + dev_dbg(scomp->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, - struct snd_sof_widget *swidget) +static int sof_ipc4_widget_assign_instance_id(struct snd_sof_widget *swidget) { + struct snd_soc_component *scomp = swidget->scomp; struct sof_ipc4_fw_module *fw_module = swidget->module_info; int max_instances = fw_module->man4_module_entry.instance_max_count; swidget->instance_id = ida_alloc_max(&fw_module->m_ida, max_instances, GFP_KERNEL); if (swidget->instance_id < 0) { - dev_err(sdev->dev, "failed to assign instance id for widget %s", + dev_err(scomp->dev, "failed to assign instance id for widget %s", swidget->widget->name); return swidget->instance_id; } @@ -1463,7 +1471,8 @@ static int sof_ipc4_widget_assign_instance_id(struct snd_sof_dev *sdev, } /* update hw_params based on the audio stream format */ -static int sof_ipc4_update_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params, +static int sof_ipc4_update_hw_params(struct snd_soc_component *scomp, + struct snd_pcm_hw_params *params, struct sof_ipc4_audio_format *fmt, u32 param_to_update) { struct snd_interval *i; @@ -1487,7 +1496,7 @@ static int sof_ipc4_update_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw snd_fmt = SNDRV_PCM_FORMAT_U8; break; default: - dev_err(sdev->dev, "Unsupported PCM 8-bit IPC4 type %d\n", type); + dev_err(scomp->dev, "Unsupported PCM 8-bit IPC4 type %d\n", type); return -EINVAL; } break; @@ -1506,12 +1515,12 @@ static int sof_ipc4_update_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw snd_fmt = SNDRV_PCM_FORMAT_FLOAT_LE; break; default: - dev_err(sdev->dev, "Unsupported PCM 32-bit IPC4 type %d\n", type); + dev_err(scomp->dev, "Unsupported PCM 32-bit IPC4 type %d\n", type); return -EINVAL; } break; default: - dev_err(sdev->dev, "invalid PCM valid_bits %d\n", valid_bits); + dev_err(scomp->dev, "invalid PCM valid_bits %d\n", valid_bits); return -EINVAL; } @@ -1539,8 +1548,8 @@ static int sof_ipc4_update_hw_params(struct snd_sof_dev *sdev, struct snd_pcm_hw return 0; } -static bool sof_ipc4_is_single_format(struct snd_sof_dev *sdev, - struct sof_ipc4_pin_format *pin_fmts, u32 pin_fmts_size) +static bool sof_ipc4_is_single_format(struct sof_ipc4_pin_format *pin_fmts, + u32 pin_fmts_size) { struct sof_ipc4_audio_format *fmt; u32 rate, channels, valid_bits; @@ -1567,25 +1576,25 @@ static bool sof_ipc4_is_single_format(struct snd_sof_dev *sdev, return true; } -static int sof_ipc4_init_output_audio_fmt(struct snd_sof_dev *sdev, - struct snd_sof_widget *swidget, +static int sof_ipc4_init_output_audio_fmt(struct snd_sof_widget *swidget, 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, u32 out_ref_type) { + struct snd_soc_component *scomp = swidget->scomp; struct sof_ipc4_pin_format *pin_fmts = available_fmt->output_pin_fmts; u32 pin_fmts_size = available_fmt->num_output_formats; bool single_format; int i = 0; if (!pin_fmts_size) { - dev_err(sdev->dev, "no output formats for %s\n", + dev_err(scomp->dev, "no output formats for %s\n", swidget->widget->name); return -EINVAL; } - single_format = sof_ipc4_is_single_format(sdev, pin_fmts, pin_fmts_size); + single_format = sof_ipc4_is_single_format(pin_fmts, pin_fmts_size); /* pick the first format if there's only one available or if all formats are the same */ if (single_format) @@ -1610,7 +1619,7 @@ static int sof_ipc4_init_output_audio_fmt(struct snd_sof_dev *sdev, goto out_fmt; } - dev_err(sdev->dev, + dev_err(scomp->dev, "%s: Unsupported audio format: %uHz, %ubit, %u channels, type: %d\n", __func__, out_ref_rate, out_ref_valid_bits, out_ref_channels, out_ref_type); @@ -1623,7 +1632,8 @@ static int sof_ipc4_init_output_audio_fmt(struct snd_sof_dev *sdev, return i; } -static int sof_ipc4_get_valid_bits(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params) +static int sof_ipc4_get_valid_bits(struct snd_soc_component *scomp, + struct snd_pcm_hw_params *params) { switch (params_format(params)) { case SNDRV_PCM_FORMAT_U8: @@ -1639,12 +1649,13 @@ static int sof_ipc4_get_valid_bits(struct snd_sof_dev *sdev, struct snd_pcm_hw_p case SNDRV_PCM_FORMAT_FLOAT_LE: return 32; default: - dev_err(sdev->dev, "invalid pcm frame format %d\n", params_format(params)); + dev_err(scomp->dev, "invalid pcm frame format %d\n", params_format(params)); return -EINVAL; } } -static int sof_ipc4_get_sample_type(struct snd_sof_dev *sdev, struct snd_pcm_hw_params *params) +static int sof_ipc4_get_sample_type(struct snd_soc_component *scomp, + struct snd_pcm_hw_params *params) { switch (params_format(params)) { case SNDRV_PCM_FORMAT_A_LAW: @@ -1661,17 +1672,17 @@ static int sof_ipc4_get_sample_type(struct snd_sof_dev *sdev, struct snd_pcm_hw_ case SNDRV_PCM_FORMAT_FLOAT_LE: return SOF_IPC4_TYPE_FLOAT; default: - dev_err(sdev->dev, "invalid pcm sample type %d\n", params_format(params)); + dev_err(scomp->dev, "invalid pcm sample type %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, +static int sof_ipc4_init_input_audio_fmt(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 snd_soc_component *scomp = swidget->scomp; struct sof_ipc4_pin_format *pin_fmts = available_fmt->input_pin_fmts; u32 pin_fmts_size = available_fmt->num_input_formats; u32 valid_bits; @@ -1684,19 +1695,19 @@ static int sof_ipc4_init_input_audio_fmt(struct snd_sof_dev *sdev, int i = 0; if (!pin_fmts_size) { - dev_err(sdev->dev, "no input formats for %s\n", swidget->widget->name); + dev_err(scomp->dev, "no input formats for %s\n", swidget->widget->name); return -EINVAL; } - single_format = sof_ipc4_is_single_format(sdev, pin_fmts, pin_fmts_size); + single_format = sof_ipc4_is_single_format(pin_fmts, pin_fmts_size); if (single_format) goto in_fmt; - sample_valid_bits = sof_ipc4_get_valid_bits(sdev, params); + sample_valid_bits = sof_ipc4_get_valid_bits(scomp, params); if (sample_valid_bits < 0) return sample_valid_bits; - sample_type = sof_ipc4_get_sample_type(sdev, params); + sample_type = sof_ipc4_get_sample_type(scomp, params); if (sample_type < 0) return sample_type; @@ -1720,7 +1731,7 @@ static int sof_ipc4_init_input_audio_fmt(struct snd_sof_dev *sdev, } if (i == pin_fmts_size) { - dev_err(sdev->dev, + dev_err(scomp->dev, "%s: Unsupported audio format: %uHz, %ubit, %u channels, type: %d\n", __func__, params_rate(params), sample_valid_bits, params_channels(params), sample_type); @@ -1794,9 +1805,11 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget) } #if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SND_INTEL_NHLT) -static int snd_sof_get_hw_config_params(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, +static int snd_sof_get_hw_config_params(struct snd_sof_dai *dai, int *sample_rate, int *channel_count, int *bit_depth) { + struct snd_soc_component *scomp = dai->scomp; + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct snd_soc_tplg_hw_config *hw_config; struct snd_sof_dai_link *slink; bool dai_link_found = false; @@ -1804,7 +1817,7 @@ static int snd_sof_get_hw_config_params(struct snd_sof_dev *sdev, struct snd_sof int i; /* get current hw_config from link */ - list_for_each_entry(slink, &sdev->dai_link_list, list) { + list_for_each_entry(slink, &instance->dai_link_list, list) { if (!strcmp(slink->link->name, dai->name)) { dai_link_found = true; break; @@ -1812,7 +1825,7 @@ static int snd_sof_get_hw_config_params(struct snd_sof_dev *sdev, struct snd_sof } if (!dai_link_found) { - dev_err(sdev->dev, "%s: no DAI link found for DAI %s\n", __func__, dai->name); + dev_err(scomp->dev, "%s: no DAI link found for DAI %s\n", __func__, dai->name); return -EINVAL; } @@ -1825,7 +1838,7 @@ static int snd_sof_get_hw_config_params(struct snd_sof_dev *sdev, struct snd_sof } if (!hw_cfg_found) { - dev_err(sdev->dev, "%s: no matching hw_config found for DAI %s\n", __func__, + dev_err(scomp->dev, "%s: no matching hw_config found for DAI %s\n", __func__, dai->name); return -EINVAL; } @@ -1834,18 +1847,20 @@ static int snd_sof_get_hw_config_params(struct snd_sof_dev *sdev, struct snd_sof *channel_count = le32_to_cpu(hw_config->tdm_slots); *sample_rate = le32_to_cpu(hw_config->fsync_rate); - dev_dbg(sdev->dev, "sample rate: %d sample width: %d channels: %d\n", + dev_dbg(scomp->dev, "sample rate: %d sample width: %d channels: %d\n", *sample_rate, *bit_depth, *channel_count); return 0; } static int -snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, +snd_sof_get_nhlt_endpoint_data(struct snd_sof_dai *dai, bool single_bitdepth, struct snd_pcm_hw_params *params, u32 dai_index, u32 linktype, u8 dir, u32 **dst, u32 *len) { + struct snd_soc_component *scomp = dai->scomp; + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct sof_ipc4_fw_data *ipc4_data = sdev->private; struct nhlt_specific_cfg *cfg = NULL; struct snd_ipc4_nhlt *entry = NULL; @@ -1865,14 +1880,14 @@ snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai /* Prefer 32-bit blob if copier supports multiple formats */ if (bit_depth <= 16 && !single_bitdepth) { - dev_dbg(sdev->dev, "Looking for 32-bit blob first for DMIC\n"); + dev_dbg(scomp->dev, "Looking for 32-bit blob first for DMIC\n"); format_change = true; bit_depth = 32; } break; case SOF_DAI_INTEL_SSP: nhlt_type = NHLT_LINK_SSP; - ret = snd_sof_get_hw_config_params(sdev, dai, &sample_rate, &channel_count, + ret = snd_sof_get_hw_config_params(dai, &sample_rate, &channel_count, &bit_depth); if (ret < 0) return ret; @@ -1891,16 +1906,22 @@ snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai break; } if (dev_type < 0) { - dev_err(sdev->dev, "%s: No match for SSP%d in NHLT table\n", + dev_err(scomp->dev, "%s: No match for SSP%d in NHLT table\n", __func__, dai_index); return dev_type; } + + if (params_width(params) != bit_depth) { + format_change = true; + dev_dbg(scomp->dev, "SSP sample width change from %d to %d\n", + params_width(params), bit_depth); + } break; default: return 0; } - dev_dbg(sdev->dev, "dai index %d nhlt type %d direction %d dev type %d\n", + dev_dbg(scomp->dev, "dai index %d nhlt type %d direction %d dev type %d\n", dai_index, nhlt_type, dir, dev_type); /* find NHLT blob with matching params */ @@ -1956,7 +1977,7 @@ snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai } } - dev_err(sdev->dev, + dev_err(scomp->dev, "no matching blob for sample rate: %d sample width: %d channels: %d\n", sample_rate, bit_depth, channel_count); return -EINVAL; @@ -1989,7 +2010,7 @@ snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai } #else static int -snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, +snd_sof_get_nhlt_endpoint_data(struct snd_sof_dai *dai, bool single_bitdepth, struct snd_pcm_hw_params *params, u32 dai_index, u32 linktype, u8 dir, u32 **dst, u32 *len) @@ -2024,7 +2045,7 @@ bool sof_ipc4_copier_is_single_bitdepth(struct snd_sof_dev *sdev, } static int -sof_ipc4_adjust_params_to_dai_format(struct snd_sof_dev *sdev, +sof_ipc4_adjust_params_to_dai_format(struct snd_soc_component *scomp, struct snd_pcm_hw_params *params, struct sof_ipc4_pin_format *pin_fmts, u32 pin_fmts_size) @@ -2065,7 +2086,7 @@ sof_ipc4_adjust_params_to_dai_format(struct snd_sof_dev *sdev, } if (params_mask) - return sof_ipc4_update_hw_params(sdev, params, + return sof_ipc4_update_hw_params(scomp, params, &pin_fmts[0].audio_fmt, params_mask); @@ -2073,9 +2094,11 @@ sof_ipc4_adjust_params_to_dai_format(struct snd_sof_dev *sdev, } static int -sof_ipc4_prepare_dai_copier(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, +sof_ipc4_prepare_dai_copier(struct snd_sof_dai *dai, struct snd_pcm_hw_params *params, int dir) { + struct snd_soc_component *scomp = dai->scomp; + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct sof_ipc4_available_audio_format *available_fmt; struct snd_pcm_hw_params dai_params = *params; struct sof_ipc4_copier_data *copier_data; @@ -2102,14 +2125,14 @@ sof_ipc4_prepare_dai_copier(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, num_pin_fmts = available_fmt->num_input_formats; } - ret = sof_ipc4_adjust_params_to_dai_format(sdev, &dai_params, pin_fmts, + ret = sof_ipc4_adjust_params_to_dai_format(dai->scomp, &dai_params, pin_fmts, num_pin_fmts); if (ret) return ret; single_bitdepth = sof_ipc4_copier_is_single_bitdepth(sdev, pin_fmts, num_pin_fmts); - ret = snd_sof_get_nhlt_endpoint_data(sdev, dai, single_bitdepth, + ret = snd_sof_get_nhlt_endpoint_data(dai, single_bitdepth, &dai_params, ipc4_copier->dai_index, ipc4_copier->dai_type, dir, @@ -2122,15 +2145,21 @@ sof_ipc4_prepare_dai_copier(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, return ret; } -static void sof_ipc4_host_config(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, +static void sof_ipc4_host_config(struct snd_sof_dev *sdev, + struct snd_sof_widget *swidget, struct snd_sof_platform_stream_params *platform_params) { + struct snd_soc_component *scomp = swidget->scomp; struct sof_ipc4_copier *ipc4_copier = (struct sof_ipc4_copier *)swidget->private; struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; struct sof_ipc4_copier_data *copier_data = &ipc4_copier->data; struct sof_ipc4_pipeline *pipeline = pipe_widget->private; u32 host_dma_id = platform_params->stream_tag - 1; + dev_dbg(scomp->dev, "Host copier %s, type %d, ChainDMA: %s, stream_tag: %d\n", + swidget->widget->name, swidget->id, str_yes_no(pipeline->use_chain_dma), + platform_params->stream_tag); + if (pipeline->use_chain_dma) { pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_HOST_ID_MASK; pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(host_dma_id); @@ -2146,7 +2175,6 @@ sof_ipc4_copier_module_update_params(struct snd_sof_widget *swidget, struct snd_pcm_hw_params *pipeline_params) { struct snd_soc_component *scomp = swidget->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_ipc4_copier_data *copier_data; struct sof_ipc4_copier *ipc4_copier; @@ -2175,13 +2203,13 @@ sof_ipc4_copier_module_update_params(struct snd_sof_widget *swidget, break; } default: - dev_err(sdev->dev, "unsupported type %d for copier %s", + dev_err(scomp->dev, "unsupported type %d for copier %s", swidget->id, swidget->widget->name); return -EINVAL; } /* modify the input params for the next widget */ - return sof_ipc4_update_hw_params(sdev, pipeline_params, + return sof_ipc4_update_hw_params(scomp, pipeline_params, &copier_data->out_format, BIT(SNDRV_PCM_HW_PARAM_FORMAT) | BIT(SNDRV_PCM_HW_PARAM_CHANNELS) | @@ -2196,7 +2224,8 @@ _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 snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct sof_ipc4_copier_data *copier_data; int input_fmt_index, output_fmt_index; struct sof_ipc4_copier *ipc4_copier; @@ -2221,7 +2250,7 @@ _sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, struct sof_ipc4_pipeline *pipeline = pipe_widget->private; struct sof_ipc4_gtw_attributes *gtw_attr; - dev_dbg(sdev->dev, + dev_dbg(scomp->dev, "Host copier %s, type %d, ChainDMA: %s, stream_tag: %d\n", swidget->widget->name, swidget->id, str_yes_no(pipeline->use_chain_dma), @@ -2300,7 +2329,7 @@ _sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; struct sof_ipc4_pipeline *pipeline = pipe_widget->private; - dev_dbg(sdev->dev, "Dai copier %s, type %d, ChainDMA: %s\n", + dev_dbg(scomp->dev, "Dai copier %s, type %d, ChainDMA: %s\n", swidget->widget->name, swidget->id, str_yes_no(pipeline->use_chain_dma)); @@ -2313,34 +2342,38 @@ _sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, copier_data = &ipc4_copier->data; available_fmt = &ipc4_copier->available_fmt; - /* - * Use the fe_params as a base for the copier configuration. - * The ref_params might get updated to reflect what format is - * supported by the copier on the DAI side. - * - * In case of capture the ref_params returned will be used to - * find the input configuration of the copier. - */ - ref_params = kmemdup(fe_params, sizeof(*ref_params), GFP_KERNEL); - if (!ref_params) - return -ENOMEM; - - ret = sof_ipc4_prepare_dai_copier(sdev, dai, ref_params, dir); - if (ret < 0) - return ret; + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + /* + * For playback the pipeline_params needs to be used to + * find the input configuration of the copier. + */ + ref_params = kmemdup(pipeline_params, sizeof(*ref_params), + GFP_KERNEL); + if (!ref_params) + return -ENOMEM; + } else { + /* + * For capture the adjusted fe_params needs to be used + * to find the input configuration of the copier. + * + * The params might be updated in + * sof_ipc4_prepare_dai_copier() to reflect the supported + * input formats by the copier/dai. + */ + ref_params = kmemdup(fe_params, sizeof(*ref_params), GFP_KERNEL); + if (!ref_params) + return -ENOMEM; - /* - * For playback the pipeline_params needs to be used to find the - * input configuration of the copier. - */ - if (dir == SNDRV_PCM_STREAM_PLAYBACK) - memcpy(ref_params, pipeline_params, sizeof(*ref_params)); + ret = sof_ipc4_prepare_dai_copier(dai, ref_params, dir); + if (ret < 0) + return ret; + } break; } case snd_soc_dapm_buffer: { - dev_dbg(sdev->dev, "Module copier %s, type %d\n", + dev_dbg(scomp->dev, "Module copier %s, type %d\n", swidget->widget->name, swidget->id); ipc4_copier = (struct sof_ipc4_copier *)swidget->private; @@ -2354,13 +2387,13 @@ _sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, break; } default: - dev_err(sdev->dev, "unsupported type %d for copier %s", + dev_err(scomp->dev, "unsupported type %d for copier %s", swidget->id, swidget->widget->name); return -EINVAL; } /* set input and output audio formats */ - input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, + input_fmt_index = sof_ipc4_init_input_audio_fmt(swidget, &copier_data->base_config, ref_params, available_fmt); if (input_fmt_index < 0) @@ -2389,15 +2422,34 @@ _sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, } case snd_soc_dapm_aif_out: case snd_soc_dapm_dai_in: - out_ref_rate = params_rate(fe_params); - out_ref_channels = params_channels(fe_params); - ret = sof_ipc4_get_sample_type(sdev, fe_params); + /* + * For capture the fe_params needs to be used to find the output + * configuration of the copier. + * + * For playback the adjusted fe_params needs to be used + * to find the output configuration of the copier. + * + * The params might be updated in + * sof_ipc4_prepare_dai_copier() to reflect the supported + * output formats by the copier/dai. + */ + memcpy(ref_params, fe_params, sizeof(*ref_params)); + if (dir == SNDRV_PCM_STREAM_PLAYBACK && + swidget->id == snd_soc_dapm_dai_in) { + ret = sof_ipc4_prepare_dai_copier(dai, ref_params, dir); + if (ret < 0) + return ret; + } + + out_ref_rate = params_rate(ref_params); + out_ref_channels = params_channels(ref_params); + ret = sof_ipc4_get_sample_type(scomp, ref_params); if (ret < 0) return ret; out_ref_type = (u32)ret; if (!single_output_bitdepth) { - out_ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); + out_ref_valid_bits = sof_ipc4_get_valid_bits(scomp, ref_params); if (out_ref_valid_bits < 0) return out_ref_valid_bits; } @@ -2423,7 +2475,7 @@ _sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, out_ref_type = sof_ipc4_fmt_cfg_to_type(out_fmt->fmt_cfg); } - output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget, + output_fmt_index = sof_ipc4_init_output_audio_fmt(swidget, &copier_data->base_config, available_fmt, out_ref_rate, out_ref_channels, out_ref_valid_bits, @@ -2494,7 +2546,7 @@ _sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, * for all widgets with the same stream name */ i = 0; - list_for_each_entry(w, &sdev->widget_list, list) { + list_for_each_entry(w, &instance->widget_list, list) { u32 node_type; if (!WIDGET_IS_DAI(w->id) || !w->widget->sname || @@ -2557,7 +2609,7 @@ _sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, } /* modify the input params for the next widget */ - ret = sof_ipc4_update_hw_params(sdev, pipeline_params, + ret = sof_ipc4_update_hw_params(scomp, pipeline_params, &copier_data->out_format, BIT(SNDRV_PCM_HW_PARAM_FORMAT) | BIT(SNDRV_PCM_HW_PARAM_CHANNELS) | @@ -2579,7 +2631,7 @@ _sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, copier_data->gtw_cfg.dma_buffer_size = max((u32)SOF_IPC4_MIN_DMA_BUFFER_SIZE, deep_buffer_dma_ms) * copier_data->base_config.ibs; - dev_dbg(sdev->dev, "copier %s, dma buffer%s: %u ms (%u bytes)", + dev_dbg(scomp->dev, "copier %s, dma buffer%s: %u ms (%u bytes)", swidget->widget->name, deep_buffer_dma_ms ? " (using Deep Buffer)" : "", max((u32)SOF_IPC4_MIN_DMA_BUFFER_SIZE, deep_buffer_dma_ms), @@ -2593,7 +2645,7 @@ _sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, copier_data->gtw_cfg.dma_buffer_size = max((u32)SOF_IPC4_MIN_DMA_BUFFER_SIZE, deep_buffer_dma_ms) * copier_data->base_config.obs; - dev_dbg(sdev->dev, "copier %s, dma buffer%s: %u ms (%u bytes)", + dev_dbg(scomp->dev, "copier %s, dma buffer%s: %u ms (%u bytes)", swidget->widget->name, deep_buffer_dma_ms ? " (using Deep Buffer)" : "", max((u32)SOF_IPC4_MIN_DMA_BUFFER_SIZE, deep_buffer_dma_ms), @@ -2629,7 +2681,7 @@ _sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, 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); + dev_dbg(scomp->dev, "copier %s, IPC size is %d", swidget->widget->name, ipc_size); *ipc_config_data = kzalloc(ipc_size, GFP_KERNEL); if (!*ipc_config_data) @@ -2637,11 +2689,11 @@ _sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, *ipc_config_size = ipc_size; - sof_ipc4_dbg_module_audio_format(sdev->dev, swidget, available_fmt, + sof_ipc4_dbg_module_audio_format(scomp->dev, swidget, available_fmt, input_fmt_index, output_fmt_index); /* update pipeline memory usage */ - sof_ipc4_update_resource_usage(sdev, swidget, &copier_data->base_config); + sof_ipc4_update_resource_usage(swidget, &copier_data->base_config); /* copy IPC data */ memcpy(*ipc_config_data, (void *)copier_data, sizeof(*copier_data)); @@ -2685,7 +2737,6 @@ static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget, struct snd_pcm_hw_params *pipeline_params, int dir) { struct snd_soc_component *scomp = swidget->scomp; - 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; @@ -2696,7 +2747,7 @@ static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget, if (unlikely(swidget->prepared)) return 0; - input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, + input_fmt_index = sof_ipc4_init_input_audio_fmt(swidget, &gain->data.base_config, pipeline_params, available_fmt); @@ -2709,7 +2760,7 @@ static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget, out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg); out_ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg); - output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget, + output_fmt_index = sof_ipc4_init_output_audio_fmt(swidget, &gain->data.base_config, available_fmt, out_ref_rate, @@ -2719,11 +2770,11 @@ static int sof_ipc4_prepare_gain_module(struct snd_sof_widget *swidget, if (output_fmt_index < 0) return output_fmt_index; - sof_ipc4_dbg_module_audio_format(sdev->dev, swidget, available_fmt, + sof_ipc4_dbg_module_audio_format(scomp->dev, swidget, available_fmt, input_fmt_index, output_fmt_index); /* update pipeline memory usage */ - sof_ipc4_update_resource_usage(sdev, swidget, &gain->data.base_config); + sof_ipc4_update_resource_usage(swidget, &gain->data.base_config); return 0; } @@ -2734,7 +2785,6 @@ static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget, struct snd_pcm_hw_params *pipeline_params, int dir) { struct snd_soc_component *scomp = swidget->scomp; - 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; @@ -2745,7 +2795,7 @@ static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget, if (swidget->prepared) return 0; - input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, + input_fmt_index = sof_ipc4_init_input_audio_fmt(swidget, &mixer->base_config, pipeline_params, available_fmt); @@ -2758,7 +2808,7 @@ static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget, out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg); out_ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg); - output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget, + output_fmt_index = sof_ipc4_init_output_audio_fmt(swidget, &mixer->base_config, available_fmt, out_ref_rate, @@ -2768,11 +2818,11 @@ static int sof_ipc4_prepare_mixer_module(struct snd_sof_widget *swidget, if (output_fmt_index < 0) return output_fmt_index; - sof_ipc4_dbg_module_audio_format(sdev->dev, swidget, available_fmt, + sof_ipc4_dbg_module_audio_format(scomp->dev, swidget, available_fmt, input_fmt_index, output_fmt_index); /* update pipeline memory usage */ - sof_ipc4_update_resource_usage(sdev, swidget, &mixer->base_config); + sof_ipc4_update_resource_usage(swidget, &mixer->base_config); return 0; } @@ -2783,7 +2833,6 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget, struct snd_pcm_hw_params *pipeline_params, int dir) { struct snd_soc_component *scomp = swidget->scomp; - 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 *out_audio_fmt; @@ -2795,7 +2844,7 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget, if (unlikely(swidget->prepared)) return 0; - input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, + input_fmt_index = sof_ipc4_init_input_audio_fmt(swidget, &src->data.base_config, pipeline_params, available_fmt); @@ -2834,7 +2883,7 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget, for (i = 1; i < available_fmt->num_output_formats; i++) { out_audio_fmt = &available_fmt->output_pin_fmts[i].audio_fmt; if (out_ref_rate != out_audio_fmt->sampling_frequency) { - dev_err(sdev->dev, + dev_err(scomp->dev, "Cannot determine the output rate for SRC: %s\n", swidget->widget->name); return -EINVAL; @@ -2842,7 +2891,7 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget, } } - output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget, + output_fmt_index = sof_ipc4_init_output_audio_fmt(swidget, &src->data.base_config, available_fmt, out_ref_rate, @@ -2852,17 +2901,17 @@ static int sof_ipc4_prepare_src_module(struct snd_sof_widget *swidget, if (output_fmt_index < 0) return output_fmt_index; - sof_ipc4_dbg_module_audio_format(sdev->dev, swidget, available_fmt, + sof_ipc4_dbg_module_audio_format(scomp->dev, swidget, available_fmt, input_fmt_index, output_fmt_index); /* update pipeline memory usage */ - sof_ipc4_update_resource_usage(sdev, swidget, &src->data.base_config); + sof_ipc4_update_resource_usage(swidget, &src->data.base_config); out_audio_fmt = &available_fmt->output_pin_fmts[output_fmt_index].audio_fmt; src->data.sink_rate = out_audio_fmt->sampling_frequency; /* update pipeline_params for sink widgets */ - return sof_ipc4_update_hw_params(sdev, pipeline_params, out_audio_fmt, + return sof_ipc4_update_hw_params(scomp, pipeline_params, out_audio_fmt, BIT(SNDRV_PCM_HW_PARAM_FORMAT) | BIT(SNDRV_PCM_HW_PARAM_CHANNELS) | BIT(SNDRV_PCM_HW_PARAM_RATE)); @@ -2952,7 +3001,6 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, struct snd_pcm_hw_params *pipeline_params, int dir) { struct snd_soc_component *scomp = swidget->scomp; - 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; void *cfg = process->ipc_config_data; @@ -2966,14 +3014,14 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, return 0; /* modify the pipeline params with the output format */ - return sof_ipc4_update_hw_params(sdev, pipeline_params, + return sof_ipc4_update_hw_params(scomp, pipeline_params, &process->output_format, BIT(SNDRV_PCM_HW_PARAM_FORMAT) | BIT(SNDRV_PCM_HW_PARAM_CHANNELS) | BIT(SNDRV_PCM_HW_PARAM_RATE)); } - input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, + input_fmt_index = sof_ipc4_init_input_audio_fmt(swidget, &process->base_config, pipeline_params, available_fmt); @@ -3016,12 +3064,12 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, /* for modules without input formats, use FE params as reference */ ref_rate = params_rate(fe_params); ref_channels = params_channels(fe_params); - ret = sof_ipc4_get_sample_type(sdev, fe_params); + ret = sof_ipc4_get_sample_type(scomp, fe_params); if (ret < 0) return ret; ref_type = (u32)ret; - ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); + ref_valid_bits = sof_ipc4_get_valid_bits(scomp, fe_params); if (ref_valid_bits < 0) return ref_valid_bits; } @@ -3032,16 +3080,16 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, if (available_fmt->changed_params & BIT(SNDRV_PCM_HW_PARAM_CHANNELS)) ref_channels = params_channels(fe_params); if (available_fmt->changed_params & BIT(SNDRV_PCM_HW_PARAM_FORMAT)) { - ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); + ref_valid_bits = sof_ipc4_get_valid_bits(scomp, fe_params); if (ref_valid_bits < 0) return ref_valid_bits; - ref_type = sof_ipc4_get_sample_type(sdev, fe_params); + ref_type = sof_ipc4_get_sample_type(scomp, fe_params); if (ref_type < 0) return ref_type; } } - output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget, + output_fmt_index = sof_ipc4_init_output_audio_fmt(swidget, &process->base_config, available_fmt, ref_rate, @@ -3059,7 +3107,7 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, sizeof(struct sof_ipc4_audio_format)); /* modify the pipeline params with the output format */ - ret = sof_ipc4_update_hw_params(sdev, pipeline_params, + ret = sof_ipc4_update_hw_params(scomp, pipeline_params, &process->output_format, available_fmt->changed_params); if (ret) @@ -3077,11 +3125,11 @@ static int sof_ipc4_prepare_process_module(struct snd_sof_widget *swidget, } } - sof_ipc4_dbg_module_audio_format(sdev->dev, swidget, available_fmt, + sof_ipc4_dbg_module_audio_format(scomp->dev, swidget, available_fmt, input_fmt_index, output_fmt_index); /* update pipeline memory usage */ - sof_ipc4_update_resource_usage(sdev, swidget, &process->base_config); + sof_ipc4_update_resource_usage(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)); @@ -3172,8 +3220,9 @@ static int sof_ipc4_control_load_enum(struct snd_sof_dev *sdev, struct snd_sof_c return 0; } -static int sof_ipc4_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) +static int sof_ipc4_control_load_bytes(struct snd_sof_control *scontrol) { + struct snd_soc_component *scomp = scontrol->scomp; struct sof_ipc4_control_data *control_data; struct sof_ipc4_msg *msg; int ret; @@ -3184,21 +3233,21 @@ static int sof_ipc4_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_ * 'struct sof_ipc4_control_data' */ if (scontrol->max_size < sizeof(struct sof_abi_hdr)) { - dev_err(sdev->dev, + dev_err(scomp->dev, "insufficient maximum size for a bytes control %s: %zu.\n", scontrol->name, scontrol->max_size); return -EINVAL; } if (scontrol->priv_size > scontrol->max_size) { - dev_err(sdev->dev, + dev_err(scomp->dev, "bytes control %s initial data size %zu exceeds max %zu.\n", scontrol->name, scontrol->priv_size, scontrol->max_size); return -EINVAL; } if (scontrol->priv_size && scontrol->priv_size < sizeof(struct sof_abi_hdr)) { - dev_err(sdev->dev, + dev_err(scomp->dev, "bytes control %s initial data size %zu is insufficient.\n", scontrol->name, scontrol->priv_size); return -EINVAL; @@ -3224,7 +3273,7 @@ static int sof_ipc4_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_ scontrol->priv = NULL; if (control_data->data->magic != SOF_IPC4_ABI_MAGIC) { - dev_err(sdev->dev, "Wrong ABI magic (%#x) for control: %s\n", + dev_err(scomp->dev, "Wrong ABI magic (%#x) for control: %s\n", control_data->data->magic, scontrol->name); ret = -EINVAL; goto err; @@ -3234,7 +3283,7 @@ static int sof_ipc4_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_ if (control_data->data->size + sizeof(struct sof_abi_hdr) != scontrol->priv_size) { - dev_err(sdev->dev, "Control %s conflict in bytes %zu vs. priv size %zu.\n", + dev_err(scomp->dev, "Control %s conflict in bytes %zu vs. priv size %zu.\n", scontrol->name, control_data->data->size + sizeof(struct sof_abi_hdr), scontrol->priv_size); @@ -3265,7 +3314,7 @@ static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_contr case SND_SOC_TPLG_CTL_VOLSW_XR_SX: return sof_ipc4_control_load_volume(sdev, scontrol); case SND_SOC_TPLG_CTL_BYTES: - return sof_ipc4_control_load_bytes(sdev, scontrol); + return sof_ipc4_control_load_bytes(scontrol); case SND_SOC_TPLG_CTL_ENUM: case SND_SOC_TPLG_CTL_ENUM_VALUE: return sof_ipc4_control_load_enum(sdev, scontrol); @@ -3276,8 +3325,7 @@ static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_contr return 0; } -static void sof_ipc4_add_init_ext_dp_memory_data(struct snd_sof_dev *sdev, - struct snd_sof_widget *swidget, +static void sof_ipc4_add_init_ext_dp_memory_data(struct snd_sof_widget *swidget, u32 *payload, u32 *ext_pos, struct sof_ipc4_module_init_ext_object **hdr) { @@ -3315,14 +3363,15 @@ sof_ipc4_add_init_ext_module_data(struct snd_sof_dev *sdev, *ext_pos += DIV_ROUND_UP(data_size, sizeof(u32)); } -static int sof_ipc4_widget_mod_init_msg_payload(struct snd_sof_dev *sdev, - struct snd_sof_widget *swidget, +static int sof_ipc4_widget_mod_init_msg_payload(struct snd_sof_widget *swidget, struct sof_ipc4_msg *msg, void *ipc_data, u32 ipc_size, void **new_data) { struct sof_ipc4_process *process = swidget->private; struct sof_ipc4_module_init_ext_object *hdr = NULL; + struct snd_soc_component *scomp = swidget->scomp; + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct sof_ipc4_module_init_ext_init *ext_init; bool in_dp_domain = swidget->comp_domain == SOF_COMP_DOMAIN_DP; bool has_ext_data = WIDGET_IS_PROCESS(swidget->id) && process->init_ext_module_size; @@ -3344,7 +3393,7 @@ static int sof_ipc4_widget_mod_init_msg_payload(struct snd_sof_dev *sdev, /* Add object array objects after ext_init */ if (in_dp_domain) - sof_ipc4_add_init_ext_dp_memory_data(sdev, swidget, payload, + sof_ipc4_add_init_ext_dp_memory_data(swidget, payload, &ext_pos, &hdr); if (has_ext_data) @@ -3357,7 +3406,7 @@ static int sof_ipc4_widget_mod_init_msg_payload(struct snd_sof_dev *sdev, /* Calculate final size and check that it fits to max payload size */ new_size = ext_pos * sizeof(u32) + ipc_size; if (new_size > sdev->ipc->max_payload_size) { - dev_err(sdev->dev, "Max ipc payload size %zu exceeded: %u", + dev_err(scomp->dev, "Max ipc payload size %zu exceeded: %u", sdev->ipc->max_payload_size, new_size); kfree(payload); return -EINVAL; @@ -3375,12 +3424,12 @@ static int sof_ipc4_widget_mod_init_msg_payload(struct snd_sof_dev *sdev, return new_size; } -static void sof_ipc4_widget_pipe_ext_obj_memory_data(struct snd_sof_dev *sdev, - struct snd_sof_widget *swidget, +static void sof_ipc4_widget_pipe_ext_obj_memory_data(struct snd_sof_widget *swidget, u32 *payload, u32 *ext_pos, struct sof_ipc4_glb_pipe_ext_object **hdr) { struct sof_ipc4_glb_pipe_ext_obj_memory_data *mem_data; + struct snd_soc_component *scomp = swidget->scomp; *hdr = (struct sof_ipc4_glb_pipe_ext_object *)&payload[*ext_pos]; (*hdr)->header = @@ -3394,17 +3443,18 @@ static void sof_ipc4_widget_pipe_ext_obj_memory_data(struct snd_sof_dev *sdev, mem_data->heap_bytes = swidget->heap_bytes; *ext_pos += DIV_ROUND_UP(sizeof(*mem_data), sizeof(u32)); - dev_dbg(sdev->dev, + dev_dbg(scomp->dev, "%s; domain_id %u stack %u heap %u bytes", swidget->widget->name, mem_data->domain_id, mem_data->stack_bytes, mem_data->heap_bytes); } -static int sof_ipc4_widget_pipe_create_msg_payload(struct snd_sof_dev *sdev, - struct snd_sof_widget *swidget, +static int sof_ipc4_widget_pipe_create_msg_payload(struct snd_sof_widget *swidget, struct sof_ipc4_msg *msg, void **new_data) { + struct snd_soc_component *scomp = swidget->scomp; + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct sof_ipc4_glb_pipe_payload *payload_hdr; struct sof_ipc4_glb_pipe_ext_object *hdr = NULL; u32 *payload; @@ -3419,7 +3469,7 @@ static int sof_ipc4_widget_pipe_create_msg_payload(struct snd_sof_dev *sdev, payload_hdr->word0 |= SOF_IPC4_GLB_PIPE_EXT_OBJ_ARRAY_MASK; ext_pos = DIV_ROUND_UP(sizeof(*payload_hdr), sizeof(u32)); - sof_ipc4_widget_pipe_ext_obj_memory_data(sdev, swidget, payload, &ext_pos, &hdr); + sof_ipc4_widget_pipe_ext_obj_memory_data(swidget, payload, &ext_pos, &hdr); /* Add following array objects here */ /* Mark end of object array */ @@ -3432,7 +3482,7 @@ static int sof_ipc4_widget_pipe_create_msg_payload(struct snd_sof_dev *sdev, /* Update msg extension bits according to the payload changes */ msg->extension |= SOF_IPC4_GLB_PIPE_PAYLOAD_MASK; - dev_dbg(sdev->dev, "%s: payload word0 %#x", swidget->widget->name, + dev_dbg(scomp->dev, "%s: payload word0 %#x", swidget->widget->name, payload_hdr->word0); return ext_pos * sizeof(int32_t); @@ -3440,6 +3490,8 @@ static int sof_ipc4_widget_pipe_create_msg_payload(struct snd_sof_dev *sdev, static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { + struct snd_soc_component *scomp = swidget->scomp; + struct sof_client_dev *cdev = snd_sof_component_get_cdev(scomp); struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; struct sof_ipc4_fw_data *ipc4_data = sdev->private; struct sof_ipc4_pipeline *pipeline; @@ -3454,12 +3506,12 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget pipeline = swidget->private; if (pipeline->use_chain_dma) { - dev_warn(sdev->dev, "use_chain_dma set for scheduler %s", + dev_warn(scomp->dev, "use_chain_dma set for scheduler %s", swidget->widget->name); return 0; } - dev_dbg(sdev->dev, "pipeline: %d memory pages: %d\n", swidget->pipeline_id, + dev_dbg(scomp->dev, "pipeline: %d memory pages: %d\n", swidget->pipeline_id, pipeline->mem_usage); msg = &pipeline->msg; @@ -3468,7 +3520,7 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget swidget->instance_id = ida_alloc_max(&pipeline_ida, ipc4_data->max_num_pipelines, GFP_KERNEL); if (swidget->instance_id < 0) { - dev_err(sdev->dev, "failed to assign pipeline id for %s: %d\n", + dev_err(scomp->dev, "failed to assign pipeline id for %s: %d\n", swidget->widget->name, swidget->instance_id); return swidget->instance_id; } @@ -3587,7 +3639,7 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget struct sof_ipc4_process *process = swidget->private; if (!process->ipc_config_size) { - dev_err(sdev->dev, "module %s has no config data!\n", + dev_err(scomp->dev, "module %s has no config data!\n", swidget->widget->name); return -EINVAL; } @@ -3599,16 +3651,16 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget break; } default: - dev_err(sdev->dev, "widget type %d not supported", swidget->id); + dev_err(scomp->dev, "widget type %d not supported", swidget->id); return -EINVAL; } if (swidget->id != snd_soc_dapm_scheduler) { int module_id = msg->primary & SOF_IPC4_MOD_ID_MASK; - ret = sof_ipc4_widget_assign_instance_id(sdev, swidget); + ret = sof_ipc4_widget_assign_instance_id(swidget); if (ret < 0) { - dev_err(sdev->dev, "failed to assign instance id for %s\n", + dev_err(scomp->dev, "failed to assign instance id for %s\n", swidget->widget->name); return ret; } @@ -3622,11 +3674,11 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget msg->extension &= ~SOF_IPC4_MOD_EXT_PPL_ID_MASK; msg->extension |= SOF_IPC4_MOD_EXT_PPL_ID(pipe_widget->instance_id); - dev_dbg(sdev->dev, "Create widget %s (pipe %d) - ID %d, instance %d, core %d\n", + dev_dbg(scomp->dev, "Create widget %s (pipe %d) - ID %d, instance %d, core %d\n", swidget->widget->name, swidget->pipeline_id, module_id, swidget->instance_id, swidget->core); - ret = sof_ipc4_widget_mod_init_msg_payload(sdev, swidget, msg, ipc_data, ipc_size, + ret = sof_ipc4_widget_mod_init_msg_payload(swidget, msg, ipc_data, ipc_size, &ext_data); if (ret < 0) goto fail; @@ -3636,12 +3688,12 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget ipc_data = ext_data; } } else { - dev_dbg(sdev->dev, "Create pipeline %s (pipe %d) - instance %d, core %d\n", + dev_dbg(scomp->dev, "Create pipeline %s (pipe %d) - instance %d, core %d\n", swidget->widget->name, swidget->pipeline_id, swidget->instance_id, swidget->core); msg->extension &= ~SOF_IPC4_GLB_PIPE_PAYLOAD_MASK; - ret = sof_ipc4_widget_pipe_create_msg_payload(sdev, swidget, msg, + ret = sof_ipc4_widget_pipe_create_msg_payload(swidget, msg, &ext_data); if (ret < 0) goto fail; @@ -3655,11 +3707,11 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget msg->data_size = ipc_size; msg->data_ptr = ipc_data; - ret = sof_ipc_tx_message_no_reply(sdev->ipc, msg, ipc_size); + ret = sof_client_ipc_tx_message_no_reply(cdev, msg); fail: if (ret < 0) { - dev_err(sdev->dev, "failed to create module %s\n", swidget->widget->name); + dev_err(scomp->dev, "failed to create module %s\n", swidget->widget->name); if (swidget->id != snd_soc_dapm_scheduler) { struct sof_ipc4_fw_module *fw_module = swidget->module_info; @@ -3676,6 +3728,8 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { + struct snd_soc_component *scomp = swidget->scomp; + struct sof_client_dev *cdev = snd_sof_component_get_cdev(scomp); struct sof_ipc4_fw_module *fw_module = swidget->module_info; struct sof_ipc4_fw_data *ipc4_data = sdev->private; int ret = 0; @@ -3689,7 +3743,7 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget u32 header; if (pipeline->use_chain_dma) { - dev_warn(sdev->dev, "use_chain_dma set for scheduler %s", + dev_warn(scomp->dev, "use_chain_dma set for scheduler %s", swidget->widget->name); return 0; } @@ -3701,9 +3755,9 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget msg.primary = header; - ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0); + ret = sof_client_ipc_tx_message_no_reply(cdev, &msg); if (ret < 0) - dev_err(sdev->dev, "failed to free pipeline widget %s\n", + dev_err(scomp->dev, "failed to free pipeline widget %s\n", swidget->widget->name); pipeline->mem_usage = 0; @@ -3810,8 +3864,9 @@ static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev, struct snd_sof_widget *sink_widget, struct snd_sof_route *sroute) { + struct snd_soc_component *scomp = sroute->scomp; + struct sof_client_dev *cdev = snd_sof_component_get_cdev(scomp); 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; @@ -3832,7 +3887,7 @@ static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev, pin_fmt = sof_ipc4_get_input_pin_audio_fmt(sink_widget, sroute->dst_queue_id); if (!pin_fmt) { - dev_err(sdev->dev, + dev_err(scomp->dev, "Failed to get input audio format of %s:%d for output of %s:%d\n", sink_widget->widget->name, sroute->dst_queue_id, src_widget->widget->name, sroute->src_queue_id); @@ -3852,11 +3907,13 @@ static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev, msg.extension = SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_COPIER_MODULE_CFG_PARAM_SET_SINK_FORMAT); - return iops->set_get_data(sdev, &msg, msg.data_size, true); + return sof_client_ipc_set_get_data(cdev, &msg, true); } static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *sroute) { + struct snd_soc_component *scomp = sroute->scomp; + struct sof_client_dev *cdev = snd_sof_component_get_cdev(scomp); struct snd_sof_widget *src_widget = sroute->src_widget; struct snd_sof_widget *sink_widget = sroute->sink_widget; struct snd_sof_widget *src_pipe_widget = src_widget->spipe->pipe_widget; @@ -3872,7 +3929,7 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * /* no route set up if chain DMA is used */ if (src_pipeline->use_chain_dma || sink_pipeline->use_chain_dma) { if (!src_pipeline->use_chain_dma || !sink_pipeline->use_chain_dma) { - dev_err(sdev->dev, + dev_err(scomp->dev, "use_chain_dma must be set for both src %s and sink %s pipelines\n", src_widget->widget->name, sink_widget->widget->name); return -EINVAL; @@ -3881,7 +3938,7 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * } if (!src_fw_module || !sink_fw_module) { - dev_err(sdev->dev, + dev_err(scomp->dev, "cannot bind %s -> %s, no firmware module for: %s%s\n", src_widget->widget->name, sink_widget->widget->name, src_fw_module ? "" : " source", @@ -3893,7 +3950,7 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * sroute->src_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget, SOF_PIN_TYPE_OUTPUT); if (sroute->src_queue_id < 0) { - dev_err(sdev->dev, + dev_err(scomp->dev, "failed to get src_queue_id ID from source widget %s\n", src_widget->widget->name); return sroute->src_queue_id; @@ -3902,7 +3959,7 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * sroute->dst_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget, SOF_PIN_TYPE_INPUT); if (sroute->dst_queue_id < 0) { - dev_err(sdev->dev, + dev_err(scomp->dev, "failed to get dst_queue_id ID from sink widget %s\n", sink_widget->widget->name); sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, @@ -3915,14 +3972,14 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * ret = sof_ipc4_set_copier_sink_format(sdev, src_widget, sink_widget, sroute); if (ret < 0) { - dev_err(sdev->dev, + dev_err(scomp->dev, "failed to set sink format for source %s:%d\n", src_widget->widget->name, sroute->src_queue_id); goto out; } } - dev_dbg(sdev->dev, "bind %s:%d -> %s:%d\n", + dev_dbg(scomp->dev, "bind %s:%d -> %s:%d\n", src_widget->widget->name, sroute->src_queue_id, sink_widget->widget->name, sroute->dst_queue_id); @@ -3940,9 +3997,9 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * msg.primary = header; msg.extension = extension; - ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0); + ret = sof_client_ipc_tx_message_no_reply(cdev, &msg); if (ret < 0) { - dev_err(sdev->dev, "failed to bind modules %s:%d -> %s:%d\n", + dev_err(scomp->dev, "failed to bind modules %s:%d -> %s:%d\n", src_widget->widget->name, sroute->src_queue_id, sink_widget->widget->name, sroute->dst_queue_id); goto out; @@ -3958,6 +4015,8 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *sroute) { + struct snd_soc_component *scomp = sroute->scomp; + struct sof_client_dev *cdev = snd_sof_component_get_cdev(scomp); struct snd_sof_widget *src_widget = sroute->src_widget; struct snd_sof_widget *sink_widget = sroute->sink_widget; struct sof_ipc4_fw_module *src_fw_module = src_widget->module_info; @@ -3974,7 +4033,7 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s if (src_pipeline->use_chain_dma || sink_pipeline->use_chain_dma) return 0; - dev_dbg(sdev->dev, "unbind modules %s:%d -> %s:%d\n", + dev_dbg(scomp->dev, "unbind modules %s:%d -> %s:%d\n", src_widget->widget->name, sroute->src_queue_id, sink_widget->widget->name, sroute->dst_queue_id); @@ -3999,9 +4058,9 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s msg.primary = header; msg.extension = extension; - ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0); + ret = sof_client_ipc_tx_message_no_reply(cdev, &msg); if (ret < 0) - dev_err(sdev->dev, "failed to unbind modules %s:%d -> %s:%d\n", + dev_err(scomp->dev, "failed to unbind modules %s:%d -> %s:%d\n", src_widget->widget->name, sroute->src_queue_id, sink_widget->widget->name, sroute->dst_queue_id); out: @@ -4014,6 +4073,7 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, unsigned int flags, struct snd_sof_dai_config_data *data) { + struct snd_soc_component *scomp = swidget->scomp; struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; struct sof_ipc4_pipeline *pipeline = pipe_widget->private; struct snd_sof_dai *dai = swidget->private; @@ -4022,7 +4082,7 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget * struct sof_ipc4_copier *ipc4_copier; if (!dai || !dai->private) { - dev_err(sdev->dev, "Invalid DAI or DAI private data for %s\n", + dev_err(scomp->dev, "Invalid DAI or DAI private data for %s\n", swidget->widget->name); return -EINVAL; } @@ -4084,7 +4144,7 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget * /* nothing to do for SSP/DMIC */ break; default: - dev_err(sdev->dev, "%s: unsupported dai type %d\n", __func__, + dev_err(scomp->dev, "%s: unsupported dai type %d\n", __func__, ipc4_copier->dai_type); return -EINVAL; } @@ -4095,7 +4155,7 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget * static int sof_ipc4_parse_manifest(struct snd_soc_component *scomp, int index, struct snd_soc_tplg_manifest *man) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct sof_ipc4_fw_data *ipc4_data = sdev->private; struct sof_manifest_tlv *manifest_tlv; struct snd_ipc4_nhlt *tplg_nhlt; @@ -4163,6 +4223,8 @@ static int sof_ipc4_parse_manifest(struct snd_soc_component *scomp, int index, static int sof_ipc4_dai_get_param(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int param_type) { + struct snd_soc_component *scomp = dai->scomp; + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct sof_ipc4_copier *ipc4_copier = dai->private; struct snd_soc_tplg_hw_config *hw_config; struct snd_sof_dai_link *slink; @@ -4173,7 +4235,7 @@ static int sof_ipc4_dai_get_param(struct snd_sof_dev *sdev, struct snd_sof_dai * if (!ipc4_copier) return 0; - list_for_each_entry(slink, &sdev->dai_link_list, list) { + list_for_each_entry(slink, &instance->dai_link_list, list) { if (!strcmp(slink->link->name, dai->name)) { dai_link_found = true; break; @@ -4181,7 +4243,7 @@ static int sof_ipc4_dai_get_param(struct snd_sof_dev *sdev, struct snd_sof_dai * } if (!dai_link_found) { - dev_err(sdev->dev, "no DAI link found for DAI %s\n", dai->name); + dev_err(scomp->dev, "no DAI link found for DAI %s\n", dai->name); return -EINVAL; } @@ -4194,7 +4256,7 @@ static int sof_ipc4_dai_get_param(struct snd_sof_dev *sdev, struct snd_sof_dai * } if (!hw_cfg_found) { - dev_err(sdev->dev, "no matching hw_config found for DAI %s\n", dai->name); + dev_err(scomp->dev, "no matching hw_config found for DAI %s\n", dai->name); return -EINVAL; } @@ -4208,23 +4270,23 @@ static int sof_ipc4_dai_get_param(struct snd_sof_dev *sdev, struct snd_sof_dai * case SOF_DAI_PARAM_INTEL_SSP_TDM_SLOTS: return le32_to_cpu(hw_config->tdm_slots); default: - dev_err(sdev->dev, "invalid SSP param %d\n", param_type); + dev_err(scomp->dev, "invalid SSP param %d\n", param_type); break; } break; default: - dev_err(sdev->dev, "DAI type %d not supported yet!\n", ipc4_copier->dai_type); + dev_err(scomp->dev, "DAI type %d not supported yet!\n", ipc4_copier->dai_type); break; } return -EINVAL; } -static int sof_ipc4_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verify) +static int sof_ipc4_tear_down_all_pipelines(struct snd_soc_component *component, bool verify) { /* * This function is called during system suspend, we need to make sure - * that all streams have been freed up. + * that all streams of this audio instance have been freed up. * Freeing might have been skipped when xrun happened just at the start * of the suspend and it sent a SNDRV_PCM_TRIGGER_STOP to the active * stream. This will call sof_pcm_stream_free() with @@ -4234,7 +4296,7 @@ static int sof_ipc4_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif * This will also make sure that paused streams handled correctly. */ - return sof_pcm_free_all_streams(sdev); + return sof_pcm_free_all_streams(component); } static int sof_ipc4_link_setup(struct snd_sof_dev *sdev, struct snd_soc_dai_link *link) diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c index bf3a122ee651e8..ff86fdc9dbefe1 100644 --- a/sound/soc/sof/ipc4.c +++ b/sound/soc/sof/ipc4.c @@ -259,6 +259,7 @@ const char *sof_ipc4_pipeline_state_str(enum sof_ipc4_pipeline_state state) return " ()"; } } +EXPORT_SYMBOL(sof_ipc4_pipeline_state_str); #else /* CONFIG_SND_SOC_SOF_DEBUG_VERBOSE_IPC */ static void sof_ipc4_log_header(struct device *dev, u8 *text, struct sof_ipc4_msg *msg, bool data_size_valid) @@ -281,8 +282,29 @@ const char *sof_ipc4_pipeline_state_str(enum sof_ipc4_pipeline_state state) { return ""; } +EXPORT_SYMBOL(sof_ipc4_pipeline_state_str); #endif +int sof_ipc4_set_pipeline_state(struct sof_client_dev *cdev, u32 instance_id, u32 state) +{ + struct sof_ipc4_msg msg = {{ 0 }}; + u32 primary; + + dev_dbg(&cdev->auxdev.dev, "Set pipeline %d to state %d%s", instance_id, state, + sof_ipc4_pipeline_state_str(state)); + + primary = state; + primary |= SOF_IPC4_GLB_PIPE_STATE_ID(instance_id); + primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_SET_PIPELINE_STATE); + primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); + primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG); + + msg.primary = primary; + + return sof_client_ipc_tx_message_no_reply(cdev, &msg); +} +EXPORT_SYMBOL(sof_ipc4_set_pipeline_state); + static void sof_ipc4_dump_payload(struct snd_sof_dev *sdev, void *ipc_data, size_t size) { @@ -746,58 +768,42 @@ static int ipc4_fw_ready(struct snd_sof_dev *sdev, struct sof_ipc4_msg *ipc4_msg return sof_ipc4_init_msg_memory(sdev); } -static void sof_ipc4_module_notification_handler(struct snd_sof_dev *sdev, - struct sof_ipc4_msg *ipc4_msg) +/* + * Complete the payload of a module notification. + * + * If the notification includes additional, module specific data, then we need + * to re-allocate the buffer and re-read the whole payload, including the + * event_data. + * + * The payload is owned by the core and it is handed to the notification + * handlers as read-only, it must be completed before the notification is + * dispatched. + */ +static void sof_ipc4_module_notification_get_payload(struct snd_sof_dev *sdev, + struct sof_ipc4_msg *ipc4_msg) { struct sof_ipc4_notify_module_data *data = ipc4_msg->data_ptr; + void *new; + int ret; - /* - * If the notification includes additional, module specific data, then - * we need to re-allocate the buffer and re-read the whole payload, - * including the event_data - */ - if (data->event_data_size) { - void *new; - int ret; - - ipc4_msg->data_size += data->event_data_size; + if (!data->event_data_size) + return; - new = krealloc(ipc4_msg->data_ptr, ipc4_msg->data_size, GFP_KERNEL); - if (!new) { - ipc4_msg->data_size -= data->event_data_size; - return; - } + ipc4_msg->data_size += data->event_data_size; - /* re-read the whole payload */ - ipc4_msg->data_ptr = new; - ret = snd_sof_ipc_msg_data(sdev, NULL, ipc4_msg->data_ptr, - ipc4_msg->data_size); - if (ret < 0) { - dev_err(sdev->dev, - "Failed to read the full module notification: %d\n", - ret); - return; - } - data = ipc4_msg->data_ptr; + new = krealloc(ipc4_msg->data_ptr, ipc4_msg->data_size, GFP_KERNEL); + if (!new) { + ipc4_msg->data_size -= data->event_data_size; + return; } - /* Handle ALSA kcontrol notification */ - switch (data->event_id & SOF_IPC4_NOTIFY_MODULE_EVENTID_SOF_MAGIC_MASK) { - case SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_MAGIC_VAL: - { - const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; - - if (tplg_ops->control->update) - tplg_ops->control->update(sdev, ipc4_msg); - - break; - } - case SOF_IPC4_NOTIFY_MODULE_EVENTID_COMPR_MAGIC_VAL: - sof_ipc4_compr_drain_done(sdev, ipc4_msg); - break; - default: - break; - } + /* re-read the whole payload */ + ipc4_msg->data_ptr = new; + ret = snd_sof_ipc_msg_data(sdev, NULL, ipc4_msg->data_ptr, + ipc4_msg->data_size); + if (ret < 0) + dev_err(sdev->dev, + "Failed to read the full module notification: %d\n", ret); } static void sof_ipc4_rx_msg(struct snd_sof_dev *sdev) @@ -842,7 +848,7 @@ static void sof_ipc4_rx_msg(struct snd_sof_dev *sdev) break; case SOF_IPC4_NOTIFY_MODULE_NOTIFICATION: data_size = sizeof(struct sof_ipc4_notify_module_data); - handler_func = sof_ipc4_module_notification_handler; + handler_func = sof_ipc4_module_notification_get_payload; break; default: dev_dbg(sdev->dev, "Unhandled DSP message: %#x|%#x\n", @@ -870,6 +876,9 @@ static void sof_ipc4_rx_msg(struct snd_sof_dev *sdev) if (handler_func) handler_func(sdev, ipc4_msg); + /* Notify registered clients */ + sof_client_ipc_rx_dispatcher(sdev, ipc4_msg); + sof_ipc4_log_header(sdev->dev, "ipc rx done ", ipc4_msg, true); if (data_size) { @@ -1018,8 +1027,6 @@ const struct sof_ipc_ops ipc4_ops = { .get_reply = sof_ipc4_get_reply, .pm = &ipc4_pm_ops, .fw_loader = &ipc4_loader_ops, - .tplg = &ipc4_tplg_ops, - .pcm = &ipc4_pcm_ops, .fw_tracing = &ipc4_mtrace_ops, }; diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.c b/sound/soc/sof/mediatek/mt8186/mt8186.c index c1bea967737d52..720fc29240fd52 100644 --- a/sound/soc/sof/mediatek/mt8186/mt8186.c +++ b/sound/soc/sof/mediatek/mt8186/mt8186.c @@ -20,6 +20,7 @@ #include #include #include "../../ops.h" +#include "../../sof-client.h" #include "../../sof-of-dev.h" #include "../adsp_helper.h" #include "../mtk-adsp-common.h" @@ -379,6 +380,22 @@ static struct snd_soc_dai_driver mt8186_dai[] = { }; /* mt8186 ops */ +static const struct sof_audio_ops sof_mt8186_audio_ops = { + .pcm_open = sof_stream_pcm_open, + .pcm_hw_params = mtk_adsp_stream_pcm_hw_params, + .pcm_pointer = mtk_adsp_stream_pcm_pointer, + .pcm_close = sof_stream_pcm_close, + + .drv = mt8186_dai, + .num_drv = ARRAY_SIZE(mt8186_dai), + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, +}; + static const struct snd_sof_dsp_ops sof_mt8186_ops = { /* probe and remove */ .probe = mt8186_dsp_probe, @@ -412,22 +429,16 @@ static const struct snd_sof_dsp_ops sof_mt8186_ops = { /* misc */ .get_bar_index = mtk_adsp_get_bar_index, - /* stream callbacks */ - .pcm_open = sof_stream_pcm_open, - .pcm_hw_params = mtk_adsp_stream_pcm_hw_params, - .pcm_pointer = mtk_adsp_stream_pcm_pointer, - .pcm_close = sof_stream_pcm_close, - /* firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, + /* audio client */ + .register_audio_client = sof_register_audio_client, + .unregister_audio_client = sof_unregister_audio_client, + /* Firmware ops */ .dsp_arch_ops = &sof_xtensa_arch_ops, - /* DAI drivers */ - .drv = mt8186_dai, - .num_drv = ARRAY_SIZE(mt8186_dai), - /* Debug information */ .dbg_dump = mt8186_adsp_dump, .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, @@ -435,13 +446,6 @@ static const struct snd_sof_dsp_ops sof_mt8186_ops = { /* PM */ .suspend = mt8186_dsp_suspend, .resume = mt8186_dsp_resume, - - /* ALSA HW info flags */ - .hw_info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, }; static struct snd_sof_of_mach sof_mt8186_machs[] = { @@ -467,6 +471,7 @@ static const struct sof_dev_desc sof_of_mt8186_desc = { }, .nocodec_tplg_filename = "sof-mt8186-nocodec.tplg", .ops = &sof_mt8186_ops, + .audio_ops = &sof_mt8186_audio_ops, }; /* @@ -505,6 +510,22 @@ static struct snd_soc_dai_driver mt8188_dai[] = { }; /* mt8188 ops */ +static const struct sof_audio_ops sof_mt8188_audio_ops = { + .pcm_open = sof_stream_pcm_open, + .pcm_hw_params = mtk_adsp_stream_pcm_hw_params, + .pcm_pointer = mtk_adsp_stream_pcm_pointer, + .pcm_close = sof_stream_pcm_close, + + .drv = mt8188_dai, + .num_drv = ARRAY_SIZE(mt8188_dai), + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, +}; + static struct snd_sof_dsp_ops sof_mt8188_ops; static int sof_mt8188_ops_init(struct snd_sof_dev *sdev) @@ -512,8 +533,7 @@ 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); + sdev->audio_ops = &sof_mt8188_audio_ops; return 0; } @@ -541,6 +561,7 @@ static const struct sof_dev_desc sof_of_mt8188_desc = { }, .nocodec_tplg_filename = "sof-mt8188-nocodec.tplg", .ops = &sof_mt8188_ops, + .audio_ops = &sof_mt8188_audio_ops, .ops_init = sof_mt8188_ops_init, }; @@ -566,5 +587,6 @@ module_platform_driver(snd_sof_of_mt8186_driver); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("SOF support for MT8186/MT8188 platforms"); +MODULE_IMPORT_NS("SND_SOC_SOF_CLIENT"); MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA"); MODULE_IMPORT_NS("SND_SOC_SOF_MTK_COMMON"); diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c index 5dfa8721e059c5..23ec7f1ce5f3bf 100644 --- a/sound/soc/sof/mediatek/mt8195/mt8195.c +++ b/sound/soc/sof/mediatek/mt8195/mt8195.c @@ -20,6 +20,7 @@ #include #include #include "../../ops.h" +#include "../../sof-client.h" #include "../../sof-of-dev.h" #include "../adsp_helper.h" #include "../mtk-adsp-common.h" @@ -402,6 +403,22 @@ static struct snd_soc_dai_driver mt8195_dai[] = { }, }; +static const struct sof_audio_ops sof_mt8195_audio_ops = { + .pcm_open = sof_stream_pcm_open, + .pcm_hw_params = mtk_adsp_stream_pcm_hw_params, + .pcm_pointer = mtk_adsp_stream_pcm_pointer, + .pcm_close = sof_stream_pcm_close, + + .drv = mt8195_dai, + .num_drv = ARRAY_SIZE(mt8195_dai), + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, +}; + /* mt8195 ops */ static const struct snd_sof_dsp_ops sof_mt8195_ops = { /* probe and remove */ @@ -436,15 +453,13 @@ static const struct snd_sof_dsp_ops sof_mt8195_ops = { /* misc */ .get_bar_index = mtk_adsp_get_bar_index, - /* stream callbacks */ - .pcm_open = sof_stream_pcm_open, - .pcm_hw_params = mtk_adsp_stream_pcm_hw_params, - .pcm_pointer = mtk_adsp_stream_pcm_pointer, - .pcm_close = sof_stream_pcm_close, - /* firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, + /* audio client */ + .register_audio_client = sof_register_audio_client, + .unregister_audio_client = sof_unregister_audio_client, + /* Firmware ops */ .dsp_arch_ops = &sof_xtensa_arch_ops, @@ -452,20 +467,9 @@ static const struct snd_sof_dsp_ops sof_mt8195_ops = { .dbg_dump = mt8195_adsp_dump, .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, - /* DAI drivers */ - .drv = mt8195_dai, - .num_drv = ARRAY_SIZE(mt8195_dai), - /* PM */ .suspend = mt8195_dsp_suspend, .resume = mt8195_dsp_resume, - - /* ALSA HW info flags */ - .hw_info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, }; static struct snd_sof_of_mach sof_mt8195_machs[] = { @@ -498,6 +502,7 @@ static const struct sof_dev_desc sof_of_mt8195_desc = { }, .nocodec_tplg_filename = "sof-mt8195-nocodec.tplg", .ops = &sof_mt8195_ops, + .audio_ops = &sof_mt8195_audio_ops, .ipc_timeout = 1000, }; @@ -522,5 +527,6 @@ module_platform_driver(snd_sof_of_mt8195_driver); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("SOF support for MTL 8195 platforms"); +MODULE_IMPORT_NS("SND_SOC_SOF_CLIENT"); MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA"); MODULE_IMPORT_NS("SND_SOC_SOF_MTK_COMMON"); diff --git a/sound/soc/sof/mediatek/mt8365/mt8365.c b/sound/soc/sof/mediatek/mt8365/mt8365.c index 7d2c6955c10c69..7aaeaeed960144 100644 --- a/sound/soc/sof/mediatek/mt8365/mt8365.c +++ b/sound/soc/sof/mediatek/mt8365/mt8365.c @@ -19,6 +19,7 @@ #include #include #include "../../ops.h" +#include "../../sof-client.h" #include "../../sof-of-dev.h" #include "../../sof-audio.h" #include "../adsp_helper.h" @@ -436,7 +437,7 @@ static int mt8365_get_bar_index(struct snd_sof_dev *sdev, u32 type) return type; } -static int mt8365_pcm_hw_params(struct snd_sof_dev *sdev, +static int mt8365_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_sof_platform_stream_params *platform_params) @@ -446,24 +447,16 @@ static int mt8365_pcm_hw_params(struct snd_sof_dev *sdev, return 0; } -static snd_pcm_uframes_t mt8365_pcm_pointer(struct snd_sof_dev *sdev, +static snd_pcm_uframes_t mt8365_pcm_pointer(struct snd_soc_component *component, + struct snd_sof_pcm *spcm, struct snd_pcm_substream *substream) { - struct snd_sof_pcm *spcm; + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct sof_ipc_stream_posn posn; struct snd_sof_pcm_stream *stream; - struct snd_soc_component *scomp = sdev->component; - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); snd_pcm_uframes_t pos; int ret; - spcm = snd_sof_find_spcm_dai(scomp, rtd); - if (!spcm) { - dev_warn_ratelimited(sdev->dev, "warn: can't find PCM with DAI ID %d\n", - rtd->dai_link->id); - return 0; - } - stream = &spcm->stream[substream->stream]; ret = snd_sof_ipc_msg_data(sdev, stream, &posn, sizeof(posn)); if (ret < 0) { @@ -534,6 +527,22 @@ static struct snd_soc_dai_driver mt8365_dai[] = { }, }; +static const struct sof_audio_ops sof_mt8365_audio_ops = { + .pcm_open = sof_stream_pcm_open, + .pcm_hw_params = mt8365_pcm_hw_params, + .pcm_pointer = mt8365_pcm_pointer, + .pcm_close = sof_stream_pcm_close, + + .drv = mt8365_dai, + .num_drv = ARRAY_SIZE(mt8365_dai), + + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, +}; + /* mt8365 ops */ static struct snd_sof_dsp_ops sof_mt8365_ops = { /* probe and remove */ @@ -568,15 +577,13 @@ static struct snd_sof_dsp_ops sof_mt8365_ops = { /* misc */ .get_bar_index = mt8365_get_bar_index, - /* stream callbacks */ - .pcm_open = sof_stream_pcm_open, - .pcm_hw_params = mt8365_pcm_hw_params, - .pcm_pointer = mt8365_pcm_pointer, - .pcm_close = sof_stream_pcm_close, - /* firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, + /* audio client */ + .register_audio_client = sof_register_audio_client, + .unregister_audio_client = sof_unregister_audio_client, + /* Firmware ops */ .dsp_arch_ops = &sof_xtensa_arch_ops, @@ -584,20 +591,9 @@ static struct snd_sof_dsp_ops sof_mt8365_ops = { .dbg_dump = mt8365_adsp_dump, .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, - /* DAI drivers */ - .drv = mt8365_dai, - .num_drv = ARRAY_SIZE(mt8365_dai), - /* PM */ .suspend = mt8365_dsp_suspend, .resume = mt8365_dsp_resume, - - /* ALSA HW info flags */ - .hw_info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, }; static struct snd_sof_of_mach sof_mt8365_machs[] = { @@ -624,6 +620,7 @@ static const struct sof_dev_desc sof_of_mt8365_desc = { }, .nocodec_tplg_filename = "sof-mt8365-nocodec.tplg", .ops = &sof_mt8365_ops, + .audio_ops = &sof_mt8365_audio_ops, .ipc_timeout = 1000, }; @@ -646,6 +643,7 @@ static struct platform_driver snd_sof_of_mt8365_driver = { }; module_platform_driver(snd_sof_of_mt8365_driver); +MODULE_IMPORT_NS("SND_SOC_SOF_CLIENT"); MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA"); MODULE_IMPORT_NS("SND_SOC_SOF_MTK_COMMON"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/mediatek/mtk-adsp-common.c b/sound/soc/sof/mediatek/mtk-adsp-common.c index 75b4af4b5111de..0ac66ad3926496 100644 --- a/sound/soc/sof/mediatek/mtk-adsp-common.c +++ b/sound/soc/sof/mediatek/mtk-adsp-common.c @@ -156,12 +156,12 @@ EXPORT_SYMBOL(mtk_adsp_get_bar_index); /** * mtk_adsp_stream_pcm_hw_params - Platform specific host stream hw params - * @sdev: SOF device + * @component: ASoC component * @substream: PCM Substream * @params: hw params * @platform_params: Platform specific SOF stream parameters */ -int mtk_adsp_stream_pcm_hw_params(struct snd_sof_dev *sdev, +int mtk_adsp_stream_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_sof_platform_stream_params *platform_params) @@ -173,27 +173,20 @@ EXPORT_SYMBOL(mtk_adsp_stream_pcm_hw_params); /** * mtk_adsp_stream_pcm_pointer - Get host stream pointer - * @sdev: SOF device + * @component: ASoC component + * @spcm: SOF PCM the substream belongs to * @substream: PCM substream */ -snd_pcm_uframes_t mtk_adsp_stream_pcm_pointer(struct snd_sof_dev *sdev, +snd_pcm_uframes_t mtk_adsp_stream_pcm_pointer(struct snd_soc_component *component, + struct snd_sof_pcm *spcm, struct snd_pcm_substream *substream) { - struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_soc_component *scomp = sdev->component; + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_sof_pcm_stream *stream; struct sof_ipc_stream_posn posn; - struct snd_sof_pcm *spcm; snd_pcm_uframes_t pos; int ret; - spcm = snd_sof_find_spcm_dai(scomp, rtd); - if (!spcm) { - dev_warn_ratelimited(sdev->dev, "warn: can't find PCM with DAI ID %d\n", - rtd->dai_link->id); - return 0; - } - stream = &spcm->stream[substream->stream]; ret = snd_sof_ipc_msg_data(sdev, stream, &posn, sizeof(posn)); if (ret < 0) { @@ -211,3 +204,4 @@ EXPORT_SYMBOL(mtk_adsp_stream_pcm_pointer); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("SOF helpers for MTK ADSP platforms"); +MODULE_IMPORT_NS("SND_SOC_SOF_CLIENT"); diff --git a/sound/soc/sof/mediatek/mtk-adsp-common.h b/sound/soc/sof/mediatek/mtk-adsp-common.h index dc36b91d6779a0..38c7ad4bbdc651 100644 --- a/sound/soc/sof/mediatek/mtk-adsp-common.h +++ b/sound/soc/sof/mediatek/mtk-adsp-common.h @@ -6,15 +6,18 @@ #define EXCEPT_MAX_HDR_SIZE 0x400 #define MTK_ADSP_STACK_DUMP_SIZE 32 +struct snd_soc_component; + void mtk_adsp_dump(struct snd_sof_dev *sdev, u32 flags); int mtk_adsp_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg); void mtk_adsp_handle_reply(struct mtk_adsp_ipc *ipc); void mtk_adsp_handle_request(struct mtk_adsp_ipc *ipc); int mtk_adsp_get_bar_index(struct snd_sof_dev *sdev, u32 type); -int mtk_adsp_stream_pcm_hw_params(struct snd_sof_dev *sdev, +int mtk_adsp_stream_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_sof_platform_stream_params *platform_params); -snd_pcm_uframes_t mtk_adsp_stream_pcm_pointer(struct snd_sof_dev *sdev, +snd_pcm_uframes_t mtk_adsp_stream_pcm_pointer(struct snd_soc_component *component, + struct snd_sof_pcm *spcm, struct snd_pcm_substream *substream); #endif diff --git a/sound/soc/sof/nocodec.c b/sound/soc/sof/nocodec.c index 34744f8ae34a7a..df941396854c0d 100644 --- a/sound/soc/sof/nocodec.c +++ b/sound/soc/sof/nocodec.c @@ -20,8 +20,8 @@ static struct snd_soc_card sof_nocodec_card = { static int sof_nocodec_bes_setup(struct device *dev, struct snd_soc_dai_driver *drv, - struct snd_soc_dai_link *links, - int link_num) + struct snd_soc_dai_link *links, int link_num, + const char *platform_name) { struct snd_soc_card *card = &sof_nocodec_card; struct snd_soc_dai_link_component *dlc; @@ -54,7 +54,7 @@ static int sof_nocodec_bes_setup(struct device *dev, links[i].id = i; links[i].no_pcm = 1; links[i].cpus->dai_name = drv[i].name; - links[i].platforms->name = dev_name(dev->parent); + links[i].platforms->name = platform_name; links[i].playback_only = drv[i].playback.channels_min && !drv[i].capture.channels_min; links[i].capture_only = !drv[i].playback.channels_min && drv[i].capture.channels_min; @@ -68,7 +68,8 @@ static int sof_nocodec_bes_setup(struct device *dev, return 0; } -static int sof_nocodec_setup(struct device *dev, struct snd_soc_acpi_mach *mach) +static int sof_nocodec_setup(struct device *dev, struct snd_soc_acpi_mach *mach, + const char *platform_name) { u32 num_dai_drivers = mach->mach_params.num_dai_drivers; struct snd_soc_dai_driver *dai_drivers = mach->mach_params.dai_drivers; @@ -79,7 +80,8 @@ static int sof_nocodec_setup(struct device *dev, struct snd_soc_acpi_mach *mach) if (!links) return -ENOMEM; - return sof_nocodec_bes_setup(dev, dai_drivers, links, num_dai_drivers); + return sof_nocodec_bes_setup(dev, dai_drivers, links, num_dai_drivers, + platform_name); } static int sof_nocodec_probe(struct platform_device *pdev) @@ -93,7 +95,7 @@ static int sof_nocodec_probe(struct platform_device *pdev) snd_soc_card_set_topology_name(card, "sof"); - ret = sof_nocodec_setup(card->dev, mach); + ret = sof_nocodec_setup(card->dev, mach, mach->mach_params.platform); if (ret < 0) return ret; diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 19605de680a319..d33922ff98e6bd 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -18,6 +18,7 @@ #include #include #include "sof-priv.h" +#include "sof-audio.h" #define sof_ops(sdev) \ ((sdev)->pdata->desc->ops) @@ -415,131 +416,172 @@ static inline int snd_sof_dsp_send_msg(struct snd_sof_dev *sdev, /* host PCM ops */ static inline int -snd_sof_pcm_platform_open(struct snd_sof_dev *sdev, +snd_sof_pcm_platform_open(struct snd_soc_component *component, + struct snd_sof_pcm *spcm, struct snd_pcm_substream *substream) { - if (sof_ops(sdev) && sof_ops(sdev)->pcm_open) - return sof_ops(sdev)->pcm_open(sdev, substream); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); + + if (ins && ins->audio_ops->pcm_open) + return ins->audio_ops->pcm_open(component, spcm, substream); return 0; } /* disconnect pcm substream to a host stream */ static inline int -snd_sof_pcm_platform_close(struct snd_sof_dev *sdev, +snd_sof_pcm_platform_close(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - if (sof_ops(sdev) && sof_ops(sdev)->pcm_close) - return sof_ops(sdev)->pcm_close(sdev, substream); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); + + if (ins && ins->audio_ops->pcm_close) + return ins->audio_ops->pcm_close(component, substream); return 0; } /* host stream hw params */ static inline int -snd_sof_pcm_platform_hw_params(struct snd_sof_dev *sdev, +snd_sof_pcm_platform_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_sof_platform_stream_params *platform_params) { - if (sof_ops(sdev) && sof_ops(sdev)->pcm_hw_params) - return sof_ops(sdev)->pcm_hw_params(sdev, substream, params, - platform_params); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); + + if (ins && ins->audio_ops->pcm_hw_params) + return ins->audio_ops->pcm_hw_params(component, substream, + params, platform_params); return 0; } static inline int -snd_sof_compr_platform_open(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream) +snd_sof_compr_platform_open(struct snd_soc_component *component, + struct snd_compr_stream *cstream) { - if (sof_ops(sdev) && sof_ops(sdev)->compr_open) - return sof_ops(sdev)->compr_open(sdev, cstream); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); + + if (ins && ins->audio_ops->compr_open) + return ins->audio_ops->compr_open(component, cstream); return 0; } /* disconnect pcm substream to a host stream */ static inline int -snd_sof_compr_platform_close(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream) +snd_sof_compr_platform_close(struct snd_soc_component *component, + struct snd_compr_stream *cstream) { - if (sof_ops(sdev) && sof_ops(sdev)->compr_close) - return sof_ops(sdev)->compr_close(sdev, cstream); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); + + if (ins && ins->audio_ops->compr_close) + return ins->audio_ops->compr_close(component, cstream); return 0; } /* host stream hw params */ static inline int -snd_sof_compr_platform_hw_params(struct snd_sof_dev *sdev, +snd_sof_compr_platform_hw_params(struct snd_soc_component *component, struct snd_compr_stream *cstream, struct snd_compr_params *params, struct snd_sof_platform_stream_params *platform_params) { - if (sof_ops(sdev) && sof_ops(sdev)->compr_hw_params) - return sof_ops(sdev)->compr_hw_params(sdev, cstream, params, platform_params); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); + + if (ins && ins->audio_ops->compr_hw_params) + return ins->audio_ops->compr_hw_params(component, cstream, + params, platform_params); return 0; } static inline int -snd_sof_compr_platform_hw_free(struct snd_sof_dev *sdev, +snd_sof_compr_platform_hw_free(struct snd_soc_component *component, struct snd_compr_stream *cstream) { - if (sof_ops(sdev) && sof_ops(sdev)->compr_hw_free) - return sof_ops(sdev)->compr_hw_free(sdev, cstream); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); + + if (ins && ins->audio_ops->compr_hw_free) + return ins->audio_ops->compr_hw_free(component, cstream); return 0; } static inline int -snd_sof_compr_platform_trigger(struct snd_sof_dev *sdev, +snd_sof_compr_platform_trigger(struct snd_soc_component *component, struct snd_compr_stream *cstream, int cmd) { - if (sof_ops(sdev) && sof_ops(sdev)->compr_trigger) - return sof_ops(sdev)->compr_trigger(sdev, cstream, cmd); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); + + if (ins && ins->audio_ops->compr_trigger) + return ins->audio_ops->compr_trigger(component, cstream, cmd); return 0; } static inline int -snd_sof_compr_platform_pointer(struct snd_sof_dev *sdev, +snd_sof_compr_platform_pointer(struct snd_soc_component *component, struct snd_compr_stream *cstream, struct snd_compr_tstamp64 *tstamp) { - if (sof_ops(sdev) && sof_ops(sdev)->compr_pointer) - return sof_ops(sdev)->compr_pointer(sdev, cstream, tstamp); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); + + if (ins && ins->audio_ops->compr_pointer) + return ins->audio_ops->compr_pointer(component, cstream, tstamp); return 0; } static inline u64 -snd_sof_compr_get_dai_frame_counter(struct snd_sof_dev *sdev, +snd_sof_compr_get_dai_frame_counter(struct snd_soc_component *component, struct snd_compr_stream *cstream) { - if (sof_ops(sdev) && sof_ops(sdev)->compr_get_dai_frame_counter) - return sof_ops(sdev)->compr_get_dai_frame_counter(sdev, cstream); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); + + if (ins && ins->audio_ops->compr_get_dai_frame_counter) + return ins->audio_ops->compr_get_dai_frame_counter(component, + cstream); return 0; } /* host stream hw free */ static inline int -snd_sof_pcm_platform_hw_free(struct snd_sof_dev *sdev, +snd_sof_pcm_platform_hw_free(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - if (sof_ops(sdev) && sof_ops(sdev)->pcm_hw_free) - return sof_ops(sdev)->pcm_hw_free(sdev, substream); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); + + if (ins && ins->audio_ops->pcm_hw_free) + return ins->audio_ops->pcm_hw_free(component, substream); return 0; } /* host stream trigger */ static inline int -snd_sof_pcm_platform_trigger(struct snd_sof_dev *sdev, +snd_sof_pcm_platform_trigger(struct snd_soc_component *component, struct snd_pcm_substream *substream, int cmd) { - if (sof_ops(sdev) && sof_ops(sdev)->pcm_trigger) - return sof_ops(sdev)->pcm_trigger(sdev, substream, cmd); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); + + if (ins && ins->audio_ops->pcm_trigger) + return ins->audio_ops->pcm_trigger(component, substream, cmd); return 0; } @@ -574,47 +616,43 @@ snd_sof_set_stream_data_offset(struct snd_sof_dev *sdev, return 0; } -/* host stream pointer */ -static inline snd_pcm_uframes_t -snd_sof_pcm_platform_pointer(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream) -{ - if (sof_ops(sdev) && sof_ops(sdev)->pcm_pointer) - return sof_ops(sdev)->pcm_pointer(sdev, substream); - - return 0; -} - /* pcm ack */ -static inline int snd_sof_pcm_platform_ack(struct snd_sof_dev *sdev, +static inline int snd_sof_pcm_platform_ack(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - if (sof_ops(sdev) && sof_ops(sdev)->pcm_ack) - return sof_ops(sdev)->pcm_ack(sdev, substream); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); + + if (ins && ins->audio_ops->pcm_ack) + return ins->audio_ops->pcm_ack(component, substream); return 0; } static inline u64 -snd_sof_pcm_get_dai_frame_counter(struct snd_sof_dev *sdev, - struct snd_soc_component *component, +snd_sof_pcm_get_dai_frame_counter(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - if (sof_ops(sdev) && sof_ops(sdev)->get_dai_frame_counter) - return sof_ops(sdev)->get_dai_frame_counter(sdev, component, - substream); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); + + if (ins && ins->audio_ops->get_dai_frame_counter) + return ins->audio_ops->get_dai_frame_counter(component, + substream); return 0; } static inline u64 -snd_sof_pcm_get_host_byte_counter(struct snd_sof_dev *sdev, - struct snd_soc_component *component, +snd_sof_pcm_get_host_byte_counter(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - if (sof_ops(sdev) && sof_ops(sdev)->get_host_byte_counter) - return sof_ops(sdev)->get_host_byte_counter(sdev, component, - substream); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); + + if (ins && ins->audio_ops->get_host_byte_counter) + return ins->audio_ops->get_host_byte_counter(component, + substream); return 0; } diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 42738f12fa3390..9e08acf76cbdf7 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -13,10 +13,12 @@ #include #include #include -#include +#include #include "sof-of-dev.h" #include "sof-priv.h" #include "sof-audio.h" +#include "sof-client.h" +#include "sof-client-audio.h" #include "sof-utils.h" #include "ops.h" @@ -38,33 +40,32 @@ void snd_sof_pcm_init_elapsed_work(struct work_struct *work) } /* - * sof pcm period elapse, this could be called at irq thread context. + * Called from atomic context, the platform interrupt handler may hold its + * own locks, so only queue the work here. */ -void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream) +bool sof_audio_instance_period_elapsed(struct snd_soc_component *component, + struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); struct snd_sof_pcm *spcm; + /* dai_link ids are only unique within a card, match the component first */ + if (component != snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME)) + return false; + spcm = snd_sof_find_spcm_dai(component, rtd); - if (!spcm) { - dev_err(component->dev, - "error: period elapsed for unknown stream!\n"); - return; - } + if (!spcm) + return false; /* - * snd_pcm_period_elapsed() can be called in interrupt context - * before IRQ_HANDLED is returned. Inside snd_pcm_period_elapsed(), - * when the PCM is done draining or xrun happened, a STOP IPC will - * then be sent and this IPC will hit IPC timeout. - * To avoid sending IPC before the previous IPC is handled, we - * schedule delayed work here to call the snd_pcm_period_elapsed(). + * snd_pcm_period_elapsed() sends a STOP IPC when the PCM is done + * draining or an xrun happened, which would hit an IPC timeout if it + * was sent before the previous IPC is handled. */ queue_work(system_highpri_wq, &spcm->stream[substream->stream].period_elapsed_work); + + return true; } -EXPORT_SYMBOL(snd_sof_pcm_period_elapsed); int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd, @@ -88,7 +89,7 @@ sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_run spcm->stream[dir].list = list; - ret = sof_widget_list_prepare(sdev, spcm, params, platform_params, dir); + ret = sof_widget_list_prepare(spcm->scomp, spcm, params, platform_params, dir); if (ret < 0) { spcm_err(spcm, dir, "widget list prepare failed\n"); spcm->stream[dir].list = NULL; @@ -100,12 +101,16 @@ sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_run return 0; } -struct snd_sof_widget *snd_sof_find_swidget_by_comp_id(struct snd_sof_dev *sdev, +struct snd_sof_widget *snd_sof_find_swidget_by_comp_id(struct snd_soc_component *scomp, int comp_id) { + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct snd_sof_widget *swidget; - list_for_each_entry(swidget, &sdev->widget_list, list) { + if (!instance) + return NULL; + + list_for_each_entry(swidget, &instance->widget_list, list) { if (comp_id == swidget->comp_id) return swidget; } @@ -117,10 +122,10 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); - const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_component_get_tplg_ops(component); + const struct sof_ipc_pcm_ops *pcm_ops = snd_sof_component_get_pcm_ops(component); struct snd_sof_platform_stream_params *platform_params; struct snd_pcm_runtime *runtime = substream->runtime; struct snd_sof_widget *host_widget; @@ -160,7 +165,7 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, } platform_params = &spcm->platform_params[substream->stream]; - ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, platform_params); + ret = snd_sof_pcm_platform_hw_params(component, substream, params, platform_params); if (ret < 0) { spcm_err(spcm, substream->stream, "platform hw params failed\n"); return ret; @@ -177,7 +182,7 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, if (!sdev->dspless_mode_selected) { int host_comp_id = spcm->stream[substream->stream].comp_id; - host_widget = snd_sof_find_swidget_by_comp_id(sdev, host_comp_id); + host_widget = snd_sof_find_swidget_by_comp_id(component, host_comp_id); if (!host_widget) { spcm_err(spcm, substream->stream, "failed to find host widget with comp_id %d\n", host_comp_id); @@ -207,23 +212,24 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, } static int sof_pcm_stream_free(struct snd_sof_dev *sdev, + struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_sof_pcm *spcm, int dir, bool free_widget_list) { - const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); + const struct sof_ipc_pcm_ops *pcm_ops = snd_sof_component_get_pcm_ops(component); int ret; int err = 0; if (spcm->prepared[substream->stream]) { /* stop DMA first if needed */ if (pcm_ops && pcm_ops->platform_stop_during_hw_free) - snd_sof_pcm_platform_trigger(sdev, substream, + snd_sof_pcm_platform_trigger(component, substream, SNDRV_PCM_TRIGGER_STOP); /* free PCM in the DSP */ if (pcm_ops && pcm_ops->hw_free) { - ret = pcm_ops->hw_free(sdev->component, substream, spcm, + ret = pcm_ops->hw_free(component, substream, spcm, substream->stream); if (ret < 0) { spcm_err(spcm, substream->stream, @@ -237,7 +243,7 @@ static int sof_pcm_stream_free(struct snd_sof_dev *sdev, } /* reset the DMA */ - ret = snd_sof_pcm_platform_hw_free(sdev, substream); + ret = snd_sof_pcm_platform_hw_free(component, substream); if (ret < 0) { spcm_err(spcm, substream->stream, "platform hw free failed %d\n", ret); @@ -247,7 +253,7 @@ static int sof_pcm_stream_free(struct snd_sof_dev *sdev, /* free widget list */ if (free_widget_list) { - ret = sof_widget_list_free(sdev, spcm, dir); + ret = sof_widget_list_free(component, spcm, dir); if (ret < 0) { spcm_err(spcm, substream->stream, "sof_widget_list_free failed %d\n", ret); @@ -259,13 +265,18 @@ static int sof_pcm_stream_free(struct snd_sof_dev *sdev, return err; } -int sof_pcm_free_all_streams(struct snd_sof_dev *sdev) +int sof_pcm_free_all_streams(struct snd_soc_component *component) { + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_pcm_substream *substream; struct snd_sof_pcm *spcm; int dir, ret; - list_for_each_entry(spcm, &sdev->pcm_list, list) { + if (!instance) + return 0; + + list_for_each_entry(spcm, &instance->pcm_list, list) { for_each_pcm_streams(dir) { substream = spcm->stream[dir].substream; @@ -274,7 +285,7 @@ int sof_pcm_free_all_streams(struct snd_sof_dev *sdev) continue; if (spcm->stream[dir].list) { - ret = sof_pcm_stream_free(sdev, substream, spcm, + ret = sof_pcm_stream_free(sdev, spcm->scomp, substream, spcm, dir, true); if (ret < 0) return ret; @@ -289,7 +300,7 @@ static int sof_pcm_hw_free(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_sof_pcm *spcm; int ret; @@ -303,10 +314,11 @@ static int sof_pcm_hw_free(struct snd_soc_component *component, spcm_dbg(spcm, substream->stream, "Entry: hw_free\n"); - ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, true); + ret = sof_pcm_stream_free(sdev, component, substream, spcm, + substream->stream, true); /* unprepare and free the list of DAPM widgets */ - sof_widget_list_unprepare(sdev, spcm, substream->stream); + sof_widget_list_unprepare(component, spcm, substream->stream); cancel_work_sync(&spcm->stream[substream->stream].period_elapsed_work); @@ -317,8 +329,8 @@ static int sof_pcm_prepare(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + const struct sof_ipc_pcm_ops *pcm_ops = snd_sof_component_get_pcm_ops(component); struct snd_sof_platform_stream_params *platform_params; struct snd_soc_dapm_widget_list *list; struct snd_pcm_hw_params *params; @@ -344,7 +356,7 @@ static int sof_pcm_prepare(struct snd_soc_component *component, * this case should be reached in case of xruns where we absolutely * want to free-up and reset all PCM/DMA resources */ - ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, true); + ret = sof_pcm_stream_free(sdev, component, substream, spcm, dir, true); if (ret < 0) return ret; } @@ -359,9 +371,9 @@ static int sof_pcm_prepare(struct snd_soc_component *component, list = spcm->stream[dir].list; params = &spcm->params[substream->stream]; platform_params = &spcm->platform_params[substream->stream]; - ret = sof_widget_list_setup(sdev, spcm, params, platform_params, dir); + ret = sof_widget_list_setup(component, spcm, params, platform_params, dir); if (ret < 0) { - dev_err(sdev->dev, "failed widget list set up for pcm %d dir %u\n", + dev_err(component->dev, "failed widget list set up for pcm %d dir %u\n", le32_to_cpu(spcm->pcm.pcm_id), dir); spcm->stream[dir].list = NULL; snd_soc_dapm_dai_free_widgets(&list); @@ -387,8 +399,9 @@ static int sof_pcm_trigger(struct snd_soc_component *component, struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); + const struct sof_ipc_pcm_ops *pcm_ops = snd_sof_component_get_pcm_ops(component); + struct sof_client_dev *cdev = snd_sof_component_get_cdev(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_sof_pcm *spcm; bool reset_hw_params = false; bool ipc_first = false; @@ -434,7 +447,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component, * D0I3-compatible streams to keep the firmware pipeline running */ if (pcm_ops && pcm_ops->d0i3_supported_in_s0ix && - sdev->system_suspend_target == SOF_SUSPEND_S0IX && + sof_client_is_suspend_target_s0ix(cdev) && spcm->stream[substream->stream].d0i3_compatible) { spcm->stream[substream->stream].suspend_ignored = true; return 0; @@ -456,7 +469,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component, } if (!ipc_first) - snd_sof_pcm_platform_trigger(sdev, substream, cmd); + snd_sof_pcm_platform_trigger(component, substream, cmd); if (pcm_ops && pcm_ops->trigger) ret = pcm_ops->trigger(component, substream, spcm, cmd, substream->stream); @@ -466,14 +479,14 @@ static int sof_pcm_trigger(struct snd_soc_component *component, case SNDRV_PCM_TRIGGER_START: /* invoke platform trigger to start DMA only if pcm_ops is successful */ if (ipc_first && !ret) - snd_sof_pcm_platform_trigger(sdev, substream, cmd); + snd_sof_pcm_platform_trigger(component, substream, cmd); break; case SNDRV_PCM_TRIGGER_SUSPEND: 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->platform_stop_during_hw_free) - snd_sof_pcm_platform_trigger(sdev, substream, cmd); + snd_sof_pcm_platform_trigger(component, substream, cmd); /* * set the pending_stop flag to indicate that pipeline stop has been delayed. @@ -490,7 +503,8 @@ static int sof_pcm_trigger(struct snd_soc_component *component, /* free PCM if reset_hw_params is set and the STOP IPC is successful */ if (!ret && reset_hw_params) - ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, false); + ret = sof_pcm_stream_free(sdev, component, substream, spcm, + substream->stream, false); return ret; } @@ -499,8 +513,10 @@ static snd_pcm_uframes_t sof_pcm_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + const struct sof_ipc_pcm_ops *pcm_ops = snd_sof_component_get_pcm_ops(component); struct snd_sof_pcm *spcm; snd_pcm_uframes_t host, dai; int ret = -EOPNOTSUPP; @@ -515,14 +531,14 @@ static snd_pcm_uframes_t sof_pcm_pointer(struct snd_soc_component *component, if (ret != -EOPNOTSUPP) return ret ? ret : host; - /* use dsp ops pointer callback directly if set */ - if (sof_ops(sdev)->pcm_pointer) - return sof_ops(sdev)->pcm_pointer(sdev, substream); - spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) return -EINVAL; + /* use dsp ops pointer callback directly if set */ + if (ins && ins->audio_ops->pcm_pointer) + return ins->audio_ops->pcm_pointer(component, spcm, substream); + /* read position from DSP */ host = bytes_to_frames(substream->runtime, spcm->stream[substream->stream].posn.host_posn); @@ -539,8 +555,9 @@ static int sof_pcm_open(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - const struct snd_sof_dsp_ops *ops = sof_ops(sdev); + struct sof_client_dev *cdev = snd_sof_component_get_cdev(component); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); struct snd_sof_pcm *spcm; struct snd_soc_tplg_stream_caps *caps; int ret; @@ -549,6 +566,10 @@ static int sof_pcm_open(struct snd_soc_component *component, if (rtd->dai_link->no_pcm) return 0; + ret = sof_client_core_module_get(cdev); + if (ret) + return ret; + spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) return -EINVAL; @@ -558,7 +579,7 @@ static int sof_pcm_open(struct snd_soc_component *component, caps = &spcm->pcm.caps[substream->stream]; /* set runtime config */ - runtime->hw.info = ops->hw_info; /* platform-specific */ + runtime->hw.info = ins->audio_ops->hw_info; /* platform-specific */ /* set any runtime constraints based on topology */ runtime->hw.formats = le64_to_cpu(caps->formats); @@ -581,10 +602,11 @@ static int sof_pcm_open(struct snd_soc_component *component, spcm->stream[substream->stream].substream = substream; spcm->prepared[substream->stream] = false; - ret = snd_sof_pcm_platform_open(sdev, substream); + ret = snd_sof_pcm_platform_open(component, spcm, substream); if (ret < 0) { spcm_err(spcm, substream->stream, "platform pcm open failed %d\n", ret); + sof_client_core_module_put(cdev); return ret; } @@ -601,7 +623,7 @@ static int sof_pcm_close(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct sof_client_dev *cdev = snd_sof_component_get_cdev(component); struct snd_sof_pcm *spcm; int err; @@ -615,7 +637,7 @@ static int sof_pcm_close(struct snd_soc_component *component, spcm_dbg(spcm, substream->stream, "Entry: close\n"); - err = snd_sof_pcm_platform_close(sdev, substream); + err = snd_sof_pcm_platform_close(component, substream); if (err < 0) { spcm_err(spcm, substream->stream, "platform pcm close failed %d\n", err); @@ -627,6 +649,8 @@ static int sof_pcm_close(struct snd_soc_component *component, spcm->stream[substream->stream].substream = NULL; + sof_client_core_module_put(cdev); + return 0; } @@ -638,7 +662,7 @@ static int sof_pcm_close(struct snd_soc_component *component, static int sof_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_sof_pcm *spcm; struct snd_pcm *pcm = rtd->pcm; struct snd_soc_tplg_stream_caps *caps; @@ -728,8 +752,7 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); struct snd_sof_dai *dai = snd_sof_find_dai(component, (char *)rtd->dai_link->name); - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); + const struct sof_ipc_pcm_ops *pcm_ops = snd_sof_component_get_pcm_ops(component); /* no topology exists for this BE, try a common configuration */ if (!dai) { @@ -759,29 +782,42 @@ EXPORT_SYMBOL(sof_pcm_dai_link_fixup); static int sof_pcm_probe(struct snd_soc_component *component) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct sof_client_dev *cdev = snd_sof_component_get_cdev(component); + struct sof_audio_client_pdata *audio_pdata = dev_get_platdata(&cdev->auxdev.dev); + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); struct snd_sof_pdata *plat_data = sdev->pdata; + struct snd_sof_audio_instance *instance; + bool got_runtime_pm; const char *tplg_filename; int ret; + instance = snd_sof_audio_instance_register(sdev, component); + if (!instance) + return -ENOMEM; + + audio_pdata->component = component; + /* * make sure the device is pm_runtime_active before loading the * topology and initiating IPC or bus transactions */ ret = pm_runtime_resume_and_get(component->dev); - if (ret < 0 && ret != -EACCES) + if (ret < 0 && ret != -EACCES) { + snd_sof_audio_instance_unregister(instance); return ret; + } + got_runtime_pm = ret >= 0; /* load the default topology */ - sdev->component = component; - - tplg_filename = devm_kasprintf(sdev->dev, GFP_KERNEL, + tplg_filename = devm_kasprintf(component->dev, GFP_KERNEL, "%s/%s", plat_data->tplg_filename_prefix, + audio_pdata->machine.sof_tplg_filename ? + audio_pdata->machine.sof_tplg_filename : plat_data->tplg_filename); if (!tplg_filename) { ret = -ENOMEM; - goto pm_error; + goto out; } ret = snd_sof_load_topology(component, tplg_filename); @@ -789,14 +825,38 @@ static int sof_pcm_probe(struct snd_soc_component *component) dev_err(component->dev, "error: failed to load DSP topology %d\n", ret); -pm_error: - pm_runtime_put_autosuspend(component->dev); +out: + if (ret) { + /* + * Remove topology objects to prevent dangling references. + * When sof_complete() or a late topology file fails, DAIs from + * previously successful loads remain on the component's DAI + * list. Their driver structs are allocated under the card + * device (tplg->dev) which gets freed by devres when mc_probe + * fails, leaving dangling driver pointers. Subsequent card + * binding would crash iterating these DAIs. + */ + snd_soc_tplg_component_remove(component); + snd_sof_audio_instance_unregister(instance); + audio_pdata->component = NULL; + } + if (got_runtime_pm) + pm_runtime_put_autosuspend(component->dev); return ret; } static void sof_pcm_remove(struct snd_soc_component *component) { + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(component); + struct sof_client_dev *cdev = snd_sof_component_get_cdev(component); + struct sof_audio_client_pdata *audio_pdata = dev_get_platdata(&cdev->auxdev.dev); + + audio_pdata->component = NULL; + + if (instance) + snd_sof_audio_instance_unregister(instance); + /* remove topology */ snd_soc_tplg_component_remove(component); } @@ -804,16 +864,13 @@ static void sof_pcm_remove(struct snd_soc_component *component) static int sof_pcm_ack(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - - return snd_sof_pcm_platform_ack(sdev, substream); + return snd_sof_pcm_platform_ack(component, substream); } static snd_pcm_sframes_t sof_pcm_delay(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); + const struct sof_ipc_pcm_ops *pcm_ops = snd_sof_component_get_pcm_ops(component); if (pcm_ops && pcm_ops->delay) return pcm_ops->delay(component, substream); @@ -821,18 +878,28 @@ static snd_pcm_sframes_t sof_pcm_delay(struct snd_soc_component *component, return 0; } -void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) +void snd_sof_new_platform_drv(struct sof_client_dev *cdev, + struct snd_soc_component_driver * const pd) { - struct snd_soc_component_driver *pd = &sdev->plat_drv; - struct snd_sof_pdata *plat_data = sdev->pdata; + const struct sof_ipc_pcm_ops *pcm_ops = NULL; const char *drv_name; - if (plat_data->machine) - drv_name = plat_data->machine->drv_name; - else if (plat_data->of_machine) - drv_name = plat_data->of_machine->drv_name; - else - drv_name = NULL; + switch (sof_client_get_ipc_type(cdev)) { +#if defined(CONFIG_SND_SOC_SOF_IPC3) + case SOF_IPC_TYPE_3: + pcm_ops = &ipc3_pcm_ops; + break; +#endif +#if defined(CONFIG_SND_SOC_SOF_IPC4) + case SOF_IPC_TYPE_4: + pcm_ops = &ipc4_pcm_ops; + break; +#endif + default: + break; + } + + drv_name = sof_client_get_machine_drv_name(cdev); pd->name = "sof-audio-component"; pd->probe = sof_pcm_probe; @@ -848,8 +915,6 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) pd->delay = sof_pcm_delay; #if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS) - const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); - if (pcm_ops) pd->compress_ops = pcm_ops->compress_ops; #endif @@ -869,6 +934,6 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) * The fixup is only needed when the DSP is in use as with the DSPless * mode we are directly using the audio interface */ - if (!sdev->dspless_mode_selected) + if (!sof_client_is_dspless(cdev)) pd->be_hw_params_fixup = sof_pcm_dai_link_fixup; } diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 1d71d483588eb1..0ef5d258268b10 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -17,6 +17,25 @@ static int override_on_demand_boot = -1; module_param_named(on_demand_boot, override_on_demand_boot, int, 0444); MODULE_PARM_DESC(on_demand_boot, "Force on-demand DSP boot: 0 - disabled, 1 - enabled"); +/* + * D0i3 is only entered if at least one client has an active stream and all + * of the active streams tolerate the DSP being in the low power substate. + */ +bool snd_sof_dsp_state_is_d0i3_compatible(struct snd_sof_dev *sdev) +{ + enum sof_d0i3_vote vote = sof_client_get_d0i3_vote(sdev); + + return vote == SOF_D0I3_COMPATIBLE_ACTIVE || vote == SOF_D0I3_COMPATIBLE_PLAYBACK; +} +EXPORT_SYMBOL(snd_sof_dsp_state_is_d0i3_compatible); + +/* Streaming power gating additionally requires an active playback stream. */ +bool snd_sof_dsp_state_is_d0i3_streaming(struct snd_sof_dev *sdev) +{ + return sof_client_get_d0i3_vote(sdev) == SOF_D0I3_COMPATIBLE_PLAYBACK; +} +EXPORT_SYMBOL(snd_sof_dsp_state_is_d0i3_streaming); + /* * Helper function to determine the target DSP state during * system suspend. This function only cares about the device @@ -38,11 +57,11 @@ static u32 snd_sof_dsp_power_target(struct snd_sof_dev *sdev) case SOF_SUSPEND_S0IX: /* * Currently, the only criterion for retaining the DSP in D0 - * is that there are streams that ignored the suspend trigger. + * is that a client left something running in it on purpose. * Additional criteria such Soundwire clock-stop mode and * device suspend latency considerations will be added later. */ - if (snd_sof_stream_suspend_ignored(sdev)) + if (sof_client_keep_dsp_in_d0(sdev)) target_dsp_state = SOF_DSP_PM_D0; else target_dsp_state = SOF_DSP_PM_D3; @@ -78,7 +97,6 @@ static void sof_cache_debugfs(struct snd_sof_dev *sdev) int snd_sof_boot_dsp_firmware(struct snd_sof_dev *sdev) { const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm); - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); int ret; guard(mutex)(&sdev->dsp_fw_boot_mutex); @@ -123,15 +141,10 @@ int snd_sof_boot_dsp_firmware(struct snd_sof_dev *sdev) __func__, ret); } - /* restore pipelines */ - if (tplg_ops && tplg_ops->set_up_all_pipelines) { - ret = tplg_ops->set_up_all_pipelines(sdev, false); - if (ret < 0) { - dev_err(sdev->dev, "%s: failed to restore pipeline: %d\n", - __func__, ret); - goto setup_fail; - } - } + /* let the clients rebuild the state they had in the DSP */ + ret = sof_client_fw_booted_dispatcher(sdev); + if (ret < 0) + goto setup_fail; /* Notify clients not managed by pm framework about core resume */ sof_resume_clients(sdev); @@ -230,12 +243,16 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm); - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); pm_message_t pm_state; u32 target_state = snd_sof_dsp_power_target(sdev); - u32 old_state = sdev->dsp_power_state.state; int ret; + /* + * Note: the topology pipelines are torn down by the audio clients from + * their own suspend callbacks. The audio clients are children of the + * SOF device, so the PM core runs them before this callback. + */ + /* do nothing if dsp suspend callback is not set */ if (!runtime_suspend && !sof_ops(sdev)->suspend) return 0; @@ -243,14 +260,6 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) if (runtime_suspend && !sof_ops(sdev)->runtime_suspend) return 0; - /* we need to tear down pipelines only if the DSP hardware is - * active, which happens for PCI devices. if the device is - * suspended, it is brought back to full power and then - * suspended again - */ - if (tplg_ops && tplg_ops->tear_down_all_pipelines && (old_state == SOF_DSP_PM_D0)) - tplg_ops->tear_down_all_pipelines(sdev, false); - if (sdev->fw_state != SOF_FW_BOOT_COMPLETE) goto suspend; diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 0e4e1bb8c35ff1..a61444bfcee862 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -9,11 +9,155 @@ // #include -#include +#include #include "sof-audio.h" +#include "sof-client-audio.h" #include "sof-utils.h" #include "ops.h" +struct snd_sof_audio_instance * +snd_sof_audio_instance_register(struct snd_sof_dev *sdev, + struct snd_soc_component *component) +{ + struct sof_client_dev *cdev = snd_sof_component_get_cdev(component); + struct sof_audio_client_pdata *pdata = dev_get_platdata(&cdev->auxdev.dev); + struct snd_sof_audio_instance *instance; + const struct sof_ipc_tplg_ops *tplg_ops; + const struct sof_ipc_pcm_ops *pcm_ops; + + switch (sdev->pdata->ipc_type) { +#if defined(CONFIG_SND_SOC_SOF_IPC3) + case SOF_IPC_TYPE_3: + tplg_ops = &ipc3_tplg_ops; + pcm_ops = &ipc3_pcm_ops; + break; +#endif +#if defined(CONFIG_SND_SOC_SOF_IPC4) + case SOF_IPC_TYPE_4: + tplg_ops = &ipc4_tplg_ops; + pcm_ops = &ipc4_pcm_ops; + break; +#endif + default: + dev_err(sdev->dev, "Unsupported IPC type %d for audio instance\n", + sdev->pdata->ipc_type); + return NULL; + } + + if (!pcm_ops || !tplg_ops || !tplg_ops->widget || !tplg_ops->control) { + dev_err(sdev->dev, "Missing IPC PCM/topology ops for audio instance\n"); + return NULL; + } + + if (!sdev->audio_ops) { + dev_err(sdev->dev, "Missing audio ops for audio instance\n"); + return NULL; + } + + instance = devm_kzalloc(sdev->dev, sizeof(*instance), GFP_KERNEL); + if (!instance) + return NULL; + + instance->sdev = sdev; + instance->component = component; + instance->audio_ops = sdev->audio_ops; + instance->tplg_ops = tplg_ops; + instance->pcm_ops = pcm_ops; + INIT_LIST_HEAD(&instance->pipeline_list); + INIT_LIST_HEAD(&instance->dai_list); + INIT_LIST_HEAD(&instance->dai_link_list); + INIT_LIST_HEAD(&instance->route_list); + INIT_LIST_HEAD(&instance->pcm_list); + INIT_LIST_HEAD(&instance->kcontrol_list); + INIT_LIST_HEAD(&instance->widget_list); + + pdata->instance = instance; + + return instance; +} + +void snd_sof_audio_instance_unregister(struct snd_sof_audio_instance *instance) +{ + struct sof_client_dev *cdev = snd_sof_component_get_cdev(instance->component); + struct sof_audio_client_pdata *pdata = dev_get_platdata(&cdev->auxdev.dev); + + pdata->instance = NULL; +} + +/* + * Set up the static pipelines of a single audio instance. The pipelines are + * only created if they are not present in the DSP already, which makes it safe + * to call this from both the firmware boot and the audio client resume path. + */ +int sof_instance_set_up_pipelines(struct snd_sof_audio_instance *instance) +{ + int ret; + + if (instance->pipelines_set_up) + return 0; + + if (!instance->tplg_ops || !instance->tplg_ops->set_up_all_pipelines) + return 0; + + ret = instance->tplg_ops->set_up_all_pipelines(instance->component, false); + if (ret < 0) + return ret; + + instance->pipelines_set_up = true; + + return 0; +} + +/* + * Tear down the topology pipelines owned by a single audio instance. The other + * instances backed by the same DSP are left untouched. + */ +int sof_audio_instance_suspend(struct snd_soc_component *component) +{ + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(component); + int ret; + + if (!instance) + return 0; + + if (!instance->tplg_ops || !instance->tplg_ops->tear_down_all_pipelines) + return 0; + + ret = instance->tplg_ops->tear_down_all_pipelines(component, false); + if (ret < 0) + return ret; + + instance->pipelines_set_up = false; + + return 0; +} + +/* + * Set up the topology pipelines owned by a single audio instance. + */ +int sof_audio_instance_resume(struct snd_soc_component *component) +{ + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(component); + + return instance ? sof_instance_set_up_pipelines(instance) : 0; +} + +/* + * Set up the topology pipelines owned by a single audio instance after a DSP + * firmware (re)boot, which left nothing of the previous state in the DSP. + */ +int sof_audio_instance_restore(struct snd_soc_component *component) +{ + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(component); + + if (!instance) + return 0; + + instance->pipelines_set_up = false; + + return sof_instance_set_up_pipelines(instance); +} + /* * Check if a DAI widget is an aggregated DAI. Aggregated DAI's have names ending in numbers * starting with 0. For example: in the case of a SDW speaker with 2 amps, the topology contains @@ -29,26 +173,32 @@ static bool is_aggregated_dai(struct snd_sof_widget *swidget) swidget->widget->name[strlen(swidget->widget->name) - 1] != '0'); } -static bool is_virtual_widget(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget, - const char *func) +static bool is_virtual_widget(struct snd_soc_dapm_widget *widget, const char *func) { + struct snd_sof_widget *swidget = widget->dobj.private; + 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); + if (swidget) + dev_dbg(swidget->scomp->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) +static void sof_reset_route_setup_status(struct snd_sof_widget *widget) { - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(widget->scomp); + struct snd_sof_audio_instance *instance = + snd_sof_component_get_audio_instance(widget->scomp); + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_component_get_tplg_ops(widget->scomp); struct snd_sof_route *sroute; - list_for_each_entry(sroute, &sdev->route_list, list) + list_for_each_entry(sroute, &instance->route_list, list) if (sroute->src_widget == widget || sroute->sink_widget == widget) { if (sroute->setup && tplg_ops && tplg_ops->route_free) tplg_ops->route_free(sdev, sroute); @@ -58,9 +208,10 @@ static void sof_reset_route_setup_status(struct snd_sof_dev *sdev, struct snd_so } static int sof_widget_free_unlocked(struct snd_sof_dev *sdev, + struct snd_soc_component *scomp, struct snd_sof_widget *swidget) { - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_component_get_tplg_ops(scomp); struct snd_sof_pipeline *spipe = swidget->spipe; int err = 0; int ret; @@ -75,7 +226,7 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev, return 0; /* reset route setup status for all routes that contain this widget */ - sof_reset_route_setup_status(sdev, swidget); + sof_reset_route_setup_status(swidget); /* free DAI config and continue to free widget even if it fails */ if (WIDGET_IS_DAI(swidget->id)) { @@ -87,7 +238,8 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev, if (tplg_ops && tplg_ops->dai_config) { err = tplg_ops->dai_config(sdev, swidget, flags, &data); if (err < 0) - dev_err(sdev->dev, "failed to free config for widget %s\n", + dev_err(scomp->dev, + "failed to free config for widget %s\n", swidget->widget->name); } } @@ -109,7 +261,8 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev, for_each_set_bit(i, &spipe->core_mask, sdev->num_cores) { ret = snd_sof_dsp_core_put(sdev, i); if (ret < 0) { - dev_err(sdev->dev, "failed to disable target core: %d for pipeline %s\n", + dev_err(scomp->dev, + "failed to disable target core: %d for pipeline %s\n", i, swidget->widget->name); if (!err) err = ret; @@ -124,13 +277,13 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev, */ if (spipe && spipe->pipe_widget && swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) { - ret = sof_widget_free_unlocked(sdev, spipe->pipe_widget); + ret = sof_widget_free_unlocked(sdev, scomp, spipe->pipe_widget); if (ret < 0 && !err) err = ret; } if (!err) - dev_dbg(sdev->dev, "widget %s freed\n", swidget->widget->name); + dev_dbg(scomp->dev, "widget %s freed\n", swidget->widget->name); return err; } @@ -138,14 +291,14 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev, int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { guard(mutex)(&swidget->setup_mutex); - return sof_widget_free_unlocked(sdev, swidget); + return sof_widget_free_unlocked(sdev, swidget->scomp, swidget); } -EXPORT_SYMBOL(sof_widget_free); static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev, + struct snd_soc_component *scomp, struct snd_sof_widget *swidget) { - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_component_get_tplg_ops(scomp); struct snd_sof_pipeline *spipe = swidget->spipe; int ret; int i; @@ -169,12 +322,13 @@ static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev, */ if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) { if (!swidget->spipe || !swidget->spipe->pipe_widget) { - dev_err(sdev->dev, "No pipeline set for %s\n", swidget->widget->name); + dev_err(scomp->dev, "No pipeline set for %s\n", + swidget->widget->name); ret = -EINVAL; goto use_count_dec; } - ret = sof_widget_setup_unlocked(sdev, swidget->spipe->pipe_widget); + ret = sof_widget_setup_unlocked(sdev, scomp, swidget->spipe->pipe_widget); if (ret < 0) goto use_count_dec; } @@ -184,7 +338,8 @@ static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev, for_each_set_bit(i, &spipe->core_mask, sdev->num_cores) { ret = snd_sof_dsp_core_get(sdev, i); if (ret < 0) { - dev_err(sdev->dev, "failed to enable target core %d for pipeline %s\n", + dev_err(scomp->dev, + "failed to enable target core %d for pipeline %s\n", i, swidget->widget->name); goto pipe_widget_free; } @@ -220,18 +375,18 @@ static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev, goto widget_free; } - dev_dbg(sdev->dev, "widget %s setup complete\n", swidget->widget->name); + dev_dbg(scomp->dev, "widget %s setup complete\n", swidget->widget->name); return 0; widget_free: /* widget use_count and core_put handled by sof_widget_free() */ - sof_widget_free_unlocked(sdev, swidget); + sof_widget_free_unlocked(sdev, scomp, swidget); return ret; pipe_widget_free: if (swidget->id != snd_soc_dapm_scheduler) { - sof_widget_free_unlocked(sdev, swidget->spipe->pipe_widget); + sof_widget_free_unlocked(sdev, scomp, swidget->spipe->pipe_widget); } else { int j; @@ -251,22 +406,23 @@ static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev, int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) { guard(mutex)(&swidget->setup_mutex); - return sof_widget_setup_unlocked(sdev, swidget); + return sof_widget_setup_unlocked(sdev, swidget->scomp, swidget); } -EXPORT_SYMBOL(sof_widget_setup); int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink) { - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_sof_widget *src_widget = wsource->dobj.private; struct snd_sof_widget *sink_widget = wsink->dobj.private; + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_component_get_tplg_ops(src_widget->scomp); + struct snd_sof_audio_instance *instance = + snd_sof_component_get_audio_instance(src_widget->scomp); struct snd_sof_route *sroute; bool route_found = false; /* ignore routes involving virtual widgets in topology */ - if (is_virtual_widget(sdev, src_widget->widget, __func__) || - is_virtual_widget(sdev, sink_widget->widget, __func__)) + if (is_virtual_widget(src_widget->widget, __func__) || + is_virtual_widget(sink_widget->widget, __func__)) return 0; /* skip route if source/sink widget is not set up */ @@ -274,14 +430,15 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc return 0; /* find route matching source and sink widgets */ - list_for_each_entry(sroute, &sdev->route_list, list) + list_for_each_entry(sroute, &instance->route_list, list) if (sroute->src_widget == src_widget && sroute->sink_widget == sink_widget) { route_found = true; break; } if (!route_found) { - dev_err(sdev->dev, "error: cannot find SOF route for source %s -> %s sink\n", + dev_err(src_widget->scomp->dev, + "error: cannot find SOF route for source %s -> %s sink\n", wsource->name, wsink->name); return -EINVAL; } @@ -326,9 +483,11 @@ static int sof_set_up_same_dir_widget_routes(struct snd_sof_dev *sdev, return sof_route_setup(sdev, wsource, wsink); } -static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, - struct snd_soc_dapm_widget_list *list, int dir) +static int sof_setup_pipeline_connections(struct snd_soc_dapm_widget_list *list, + struct snd_soc_component *scomp, int dir) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct snd_soc_dapm_widget *widget; struct snd_sof_route *sroute; struct snd_soc_dapm_path *p; @@ -383,7 +542,7 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, * different directions, e.g. a sidetone or an amplifier feedback connected to a speaker * protection module. */ - list_for_each_entry(sroute, &sdev->route_list, list) { + list_for_each_entry(sroute, &instance->route_list, list) { bool src_widget_in_dapm_list, sink_widget_in_dapm_list; if (sroute->setup) @@ -432,15 +591,17 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, } static void -sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget, +sof_unprepare_widgets_in_path(struct snd_soc_component *component, + struct snd_soc_dapm_widget *widget, struct snd_soc_dapm_widget_list *list, int dir) { - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_sof_widget *swidget = widget->dobj.private; + const struct sof_ipc_tplg_ops *tplg_ops = swidget ? + snd_sof_component_get_tplg_ops(swidget->scomp) : NULL; const struct sof_ipc_tplg_widget_ops *widget_ops; struct snd_soc_dapm_path *p; - if (is_virtual_widget(sdev, widget, __func__)) + if (is_virtual_widget(widget, __func__)) return; if (!swidget) @@ -469,26 +630,28 @@ sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widg if (!p->walking && p->sink->dobj.private) { p->walking = true; - sof_unprepare_widgets_in_path(sdev, p->sink, list, dir); + sof_unprepare_widgets_in_path(component, p->sink, list, dir); p->walking = false; } } } static int -sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget, +sof_prepare_widgets_in_path(struct snd_soc_component *component, + struct snd_soc_dapm_widget *widget, struct snd_pcm_hw_params *fe_params, struct snd_sof_platform_stream_params *platform_params, struct snd_pcm_hw_params *pipeline_params, int dir, struct snd_soc_dapm_widget_list *list) { - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_sof_widget *swidget = widget->dobj.private; + const struct sof_ipc_tplg_ops *tplg_ops = swidget ? + snd_sof_component_get_tplg_ops(swidget->scomp) : NULL; const struct sof_ipc_tplg_widget_ops *widget_ops; struct snd_soc_dapm_path *p; int ret; - if (is_virtual_widget(sdev, widget, __func__)) + if (is_virtual_widget(widget, __func__)) return 0; if (!swidget) @@ -510,7 +673,7 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget ret = widget_ops[widget->id].ipc_prepare(swidget, fe_params, platform_params, pipeline_params, dir); if (ret < 0) { - dev_err(sdev->dev, "failed to prepare widget %s\n", widget->name); + dev_err(swidget->scomp->dev, "failed to prepare widget %s\n", widget->name); return ret; } @@ -524,7 +687,7 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget if (!p->walking && p->sink->dobj.private) { p->walking = true; - ret = sof_prepare_widgets_in_path(sdev, p->sink, fe_params, + ret = sof_prepare_widgets_in_path(component, p->sink, fe_params, platform_params, pipeline_params, dir, list); p->walking = false; @@ -547,11 +710,12 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget * free all widgets in the sink path starting from the source widget * (DAI type for capture, AIF type for playback) */ -static int sof_free_widgets_in_path_internal(struct snd_sof_dev *sdev, +static int sof_free_widgets_in_path_internal(struct snd_soc_component *component, struct snd_soc_dapm_widget *widget, int dir, struct snd_sof_pcm *spcm, struct snd_soc_dapm_widget_list *list) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_sof_widget *swidget = widget->dobj.private; struct snd_soc_dapm_path *p; struct snd_soc_dapm_path *next_p; @@ -561,7 +725,7 @@ static int sof_free_widgets_in_path_internal(struct snd_sof_dev *sdev, if (!list) return 0; - if (is_virtual_widget(sdev, widget, __func__)) + if (is_virtual_widget(widget, __func__)) return 0; if (!swidget) @@ -590,7 +754,7 @@ static int sof_free_widgets_in_path_internal(struct snd_sof_dev *sdev, p->walking = true; - err = sof_free_widgets_in_path_internal(sdev, p->sink, + err = sof_free_widgets_in_path_internal(component, p->sink, dir, spcm, list); if (err < 0) ret = err; @@ -600,11 +764,11 @@ static int sof_free_widgets_in_path_internal(struct snd_sof_dev *sdev, return ret; } -static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, +static int sof_free_widgets_in_path(struct snd_soc_component *component, struct snd_soc_dapm_widget *widget, int dir, struct snd_sof_pcm *spcm) { - return sof_free_widgets_in_path_internal(sdev, widget, dir, spcm, + return sof_free_widgets_in_path_internal(component, widget, dir, spcm, spcm->stream[dir].list); } @@ -631,17 +795,19 @@ static void sof_reset_path_walking_flags(struct snd_soc_dapm_widget_list *list) * (DAI type for capture, AIF type for playback). * The error path in this function ensures that all successfully set up widgets getting freed. */ -static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget, +static int sof_set_up_widgets_in_path(struct snd_soc_component *component, + struct snd_soc_dapm_widget *widget, int dir, struct snd_sof_pcm *spcm) { struct snd_sof_pcm_stream_pipeline_list *pipeline_list = &spcm->stream[dir].pipeline_list; + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; struct snd_sof_widget *swidget = widget->dobj.private; struct snd_sof_pipeline *spipe; struct snd_soc_dapm_path *p; int ret; - if (is_virtual_widget(sdev, widget, __func__)) + if (is_virtual_widget(widget, __func__)) return 0; if (swidget) { @@ -688,7 +854,7 @@ static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_d p->walking = true; - ret = sof_set_up_widgets_in_path(sdev, p->sink, dir, spcm); + ret = sof_set_up_widgets_in_path(component, p->sink, dir, spcm); p->walking = false; if (ret < 0) { if (swidget) @@ -702,7 +868,7 @@ static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_d } static int -sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, +sof_walk_widgets_in_order(struct snd_soc_component *component, struct snd_sof_pcm *spcm, struct snd_pcm_hw_params *fe_params, struct snd_sof_platform_stream_params *platform_params, int dir, enum sof_widget_op op) @@ -728,11 +894,11 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, switch (op) { case SOF_WIDGET_SETUP: - ret = sof_set_up_widgets_in_path(sdev, widget, dir, spcm); + ret = sof_set_up_widgets_in_path(component, widget, dir, spcm); str = "set up"; break; case SOF_WIDGET_FREE: - ret = sof_free_widgets_in_path(sdev, widget, dir, spcm); + ret = sof_free_widgets_in_path(component, widget, dir, spcm); str = "free"; break; case SOF_WIDGET_PREPARE: @@ -748,22 +914,23 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, */ memcpy(&pipeline_params, fe_params, sizeof(*fe_params)); - ret = sof_prepare_widgets_in_path(sdev, widget, fe_params, platform_params, - &pipeline_params, dir, list); + ret = sof_prepare_widgets_in_path(component, widget, fe_params, + platform_params, &pipeline_params, + dir, list); break; } case SOF_WIDGET_UNPREPARE: - sof_unprepare_widgets_in_path(sdev, widget, list, dir); + sof_unprepare_widgets_in_path(component, widget, list, dir); break; default: - dev_err(sdev->dev, "Invalid widget op %d\n", op); + dev_err(spcm->scomp->dev, "Invalid widget op %d\n", op); return -EINVAL; } if (ret < 0) { if (op == SOF_WIDGET_FREE) sof_reset_path_walking_flags(list); - dev_err(sdev->dev, "Failed to %s connected widgets\n", str); + dev_err(spcm->scomp->dev, "Failed to %s connected widgets\n", str); return ret; } } @@ -774,7 +941,7 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, return 0; } -int sof_widget_list_prepare(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, +int sof_widget_list_prepare(struct snd_soc_component *component, struct snd_sof_pcm *spcm, struct snd_pcm_hw_params *fe_params, struct snd_sof_platform_stream_params *platform_params, int dir) @@ -783,27 +950,29 @@ int sof_widget_list_prepare(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, * Prepare widgets for set up. The prepare step is used to allocate memory, assign * instance ID and pick the widget configuration based on the runtime PCM params. */ - return sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params, + return sof_walk_widgets_in_order(component, spcm, fe_params, platform_params, dir, SOF_WIDGET_PREPARE); } -void sof_widget_list_unprepare(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir) +void sof_widget_list_unprepare(struct snd_soc_component *component, struct snd_sof_pcm *spcm, + int dir) { struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; /* unprepare the widget */ - sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE); + sof_walk_widgets_in_order(component, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE); snd_soc_dapm_dai_free_widgets(&list); spcm->stream[dir].list = NULL; } -int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, +int sof_widget_list_setup(struct snd_soc_component *component, struct snd_sof_pcm *spcm, struct snd_pcm_hw_params *fe_params, struct snd_sof_platform_stream_params *platform_params, int dir) { - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_component_get_tplg_ops(spcm->scomp); struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; struct snd_soc_dapm_widget *widget; int i, ret; @@ -813,10 +982,10 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, return 0; /* Set up is used to send the IPC to the DSP to create the widget */ - ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params, + ret = sof_walk_widgets_in_order(component, spcm, fe_params, platform_params, dir, SOF_WIDGET_SETUP); if (ret < 0) { - sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params, + sof_walk_widgets_in_order(component, spcm, fe_params, platform_params, dir, SOF_WIDGET_UNPREPARE); return ret; } @@ -825,7 +994,7 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, * error in setting pipeline connections will result in route status being reset for * routes that were successfully set up when the widgets are freed. */ - ret = sof_setup_pipeline_connections(sdev, list, dir); + ret = sof_setup_pipeline_connections(list, spcm->scomp, dir); if (ret < 0) goto widget_free; @@ -840,7 +1009,7 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, spipe = swidget->spipe; if (!spipe) { - dev_err(sdev->dev, "no pipeline found for %s\n", + dev_err(swidget->scomp->dev, "no pipeline found for %s\n", swidget->widget->name); ret = -EINVAL; goto widget_free; @@ -848,7 +1017,7 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, pipe_widget = spipe->pipe_widget; if (!pipe_widget) { - dev_err(sdev->dev, "error: no pipeline widget found for %s\n", + dev_err(swidget->scomp->dev, "error: no pipeline widget found for %s\n", swidget->widget->name); ret = -EINVAL; goto widget_free; @@ -871,14 +1040,14 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, return 0; widget_free: - sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params, dir, + sof_walk_widgets_in_order(component, spcm, fe_params, platform_params, dir, SOF_WIDGET_FREE); - sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE); + sof_walk_widgets_in_order(component, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE); return ret; } -int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir) +int sof_widget_list_free(struct snd_soc_component *component, struct snd_sof_pcm *spcm, int dir) { struct snd_sof_pcm_stream_pipeline_list *pipeline_list = &spcm->stream[dir].pipeline_list; struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; @@ -889,7 +1058,7 @@ int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int return 0; /* send IPC to free widget in the DSP */ - ret = sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_FREE); + ret = sof_walk_widgets_in_order(component, spcm, NULL, NULL, dir, SOF_WIDGET_FREE); spcm->setup_done[dir] = false; pipeline_list->count = 0; @@ -901,14 +1070,18 @@ int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int * helper to determine if there are only D0i3 compatible * streams active */ -bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev) +enum sof_d0i3_vote sof_audio_instance_d0i3_vote(struct snd_soc_component *component) { + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(component); struct snd_pcm_substream *substream; + enum sof_d0i3_vote vote = SOF_D0I3_NO_ACTIVITY; struct snd_sof_pcm *spcm; - bool d0i3_compatible_active = false; int dir; - list_for_each_entry(spcm, &sdev->pcm_list, list) { + if (!instance) + return vote; + + list_for_each_entry(spcm, &instance->pcm_list, list) { for_each_pcm_streams(dir) { substream = spcm->stream[dir].substream; if (!substream || !substream->runtime) @@ -920,21 +1093,27 @@ bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev) * stream state. */ if (!spcm->stream[dir].d0i3_compatible) - return false; + return SOF_D0I3_INCOMPATIBLE; - d0i3_compatible_active = true; + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + vote = SOF_D0I3_COMPATIBLE_PLAYBACK; + else + vote = max(vote, SOF_D0I3_COMPATIBLE_ACTIVE); } } - return d0i3_compatible_active; + return vote; } -EXPORT_SYMBOL(snd_sof_dsp_only_d0i3_compatible_stream_active); -bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev) +bool sof_audio_instance_suspend_ignored(struct snd_soc_component *component) { + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(component); struct snd_sof_pcm *spcm; - list_for_each_entry(spcm, &sdev->pcm_list, list) { + if (!instance) + return false; + + list_for_each_entry(spcm, &instance->pcm_list, list) { if (spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].suspend_ignored || spcm->stream[SNDRV_PCM_STREAM_CAPTURE].suspend_ignored) return true; @@ -950,10 +1129,10 @@ bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev) struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_soc_component *scomp, const char *name) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct snd_sof_pcm *spcm; - list_for_each_entry(spcm, &sdev->pcm_list, list) { + list_for_each_entry(spcm, &instance->pcm_list, list) { /* match with PCM dai name */ if (strcmp(spcm->pcm.dai_name, name) == 0) return spcm; @@ -976,11 +1155,14 @@ struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp, unsigned int comp_id, int *direction) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct snd_sof_pcm *spcm; int dir; - list_for_each_entry(spcm, &sdev->pcm_list, list) { + if (!instance) + return NULL; + + list_for_each_entry(spcm, &instance->pcm_list, list) { for_each_pcm_streams(dir) { if (spcm->stream[dir].comp_id == comp_id) { *direction = dir; @@ -995,10 +1177,10 @@ struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp, struct snd_sof_widget *snd_sof_find_swidget(struct snd_soc_component *scomp, const char *name) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct snd_sof_widget *swidget; - list_for_each_entry(swidget, &sdev->widget_list, list) { + list_for_each_entry(swidget, &instance->widget_list, list) { if (strcmp(name, swidget->widget->name) == 0) return swidget; } @@ -1011,7 +1193,7 @@ struct snd_sof_widget * snd_sof_find_swidget_sname(struct snd_soc_component *scomp, const char *pcm_name, int dir) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct snd_sof_widget *swidget; enum snd_soc_dapm_type type; @@ -1020,7 +1202,7 @@ snd_sof_find_swidget_sname(struct snd_soc_component *scomp, else type = snd_soc_dapm_aif_out; - list_for_each_entry(swidget, &sdev->widget_list, list) { + list_for_each_entry(swidget, &instance->widget_list, list) { if (!strcmp(pcm_name, swidget->widget->sname) && swidget->id == type) return swidget; @@ -1032,10 +1214,10 @@ snd_sof_find_swidget_sname(struct snd_soc_component *scomp, struct snd_sof_dai *snd_sof_find_dai(struct snd_soc_component *scomp, const char *name) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct snd_sof_dai *dai; - list_for_each_entry(dai, &sdev->dai_list, list) { + list_for_each_entry(dai, &instance->dai_list, list) { if (dai->name && (strcmp(name, dai->name) == 0)) return dai; } @@ -1049,8 +1231,8 @@ static int sof_dai_get_param(struct snd_soc_pcm_runtime *rtd, int param_type) snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); struct snd_sof_dai *dai = snd_sof_find_dai(component, (char *)rtd->dai_link->name); - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_component_get_tplg_ops(component); /* use the tplg configured mclk if existed */ if (!dai) diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index b60246fd5869b1..758b90bb9c039f 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -20,6 +20,7 @@ #include #include #include "sof-priv.h" +#include "sof-client-audio.h" #define SOF_AUDIO_PCM_DRV_NAME "sof-audio-component" @@ -47,6 +48,7 @@ (id) == snd_soc_dapm_decoder || \ (id) == snd_soc_dapm_encoder) + #define SOF_DAI_PARAM_INTEL_SSP_MCLK 0 #define SOF_DAI_PARAM_INTEL_SSP_BCLK 1 #define SOF_DAI_PARAM_INTEL_SSP_TDM_SLOTS 2 @@ -92,6 +94,11 @@ struct snd_sof_control; struct snd_sof_dai; struct snd_sof_pcm; +extern const struct sof_ipc_tplg_ops ipc3_tplg_ops; +extern const struct sof_ipc_tplg_ops ipc4_tplg_ops; +extern const struct sof_ipc_pcm_ops ipc3_pcm_ops; +extern const struct sof_ipc_pcm_ops ipc4_pcm_ops; + struct snd_sof_dai_config_data { int dai_index; int dai_data; /* contains DAI-specific information */ @@ -166,7 +173,7 @@ struct sof_ipc_tplg_control_ops { int (*bytes_ext_put)(struct snd_sof_control *scontrol, const unsigned int __user *binary_data, unsigned int size); /* update control data based on notification from the DSP */ - void (*update)(struct snd_sof_dev *sdev, void *ipc_control_message); + void (*update)(struct snd_soc_component *scomp, void *ipc_control_message); /* Optional callback to setup kcontrols associated with an swidget */ int (*widget_kcontrol_setup)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); /* mandatory callback to set up volume table for volume kcontrols */ @@ -217,8 +224,10 @@ struct sof_ipc_tplg_widget_ops { * @dai_config: Function pointer for sending DAI config IPC to the DSP * @host_config: Function pointer for setting the DMA ID for host widgets * @dai_get_param: Function pointer for getting the DAI parameter - * @set_up_all_pipelines: Function pointer for setting up all topology pipelines - * @tear_down_all_pipelines: Function pointer for tearing down all topology pipelines + * @set_up_all_pipelines: Function pointer for setting up the topology pipelines of + * the audio instance the component belongs to + * @tear_down_all_pipelines: Function pointer for tearing down the topology pipelines of + * the audio instance the component belongs to * @parse_manifest: Function pointer for ipc4 specific parsing of topology manifest * @link_setup: Function pointer for IPC-specific DAI link set up * @@ -240,8 +249,8 @@ struct sof_ipc_tplg_ops { void (*host_config)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, struct snd_sof_platform_stream_params *platform_params); int (*dai_get_param)(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int param_type); - int (*set_up_all_pipelines)(struct snd_sof_dev *sdev, bool verify); - int (*tear_down_all_pipelines)(struct snd_sof_dev *sdev, bool verify); + int (*set_up_all_pipelines)(struct snd_soc_component *component, bool verify); + int (*tear_down_all_pipelines)(struct snd_soc_component *component, bool verify); int (*parse_manifest)(struct snd_soc_component *scomp, int index, struct snd_soc_tplg_manifest *man); int (*link_setup)(struct snd_sof_dev *sdev, struct snd_soc_dai_link *link); @@ -619,14 +628,63 @@ snd_sof_find_swidget_sname(struct snd_soc_component *scomp, struct snd_sof_dai *snd_sof_find_dai(struct snd_soc_component *scomp, const char *name); +static inline struct sof_client_dev * +snd_sof_component_get_cdev(struct snd_soc_component *scomp) +{ + return snd_soc_component_get_drvdata(scomp); +} + +static inline struct snd_sof_dev * +snd_sof_component_get_sdev(struct snd_soc_component *scomp) +{ + return sof_client_dev_to_sof_dev(snd_sof_component_get_cdev(scomp)); +} + +struct snd_sof_audio_instance * +snd_sof_audio_instance_register(struct snd_sof_dev *sdev, + struct snd_soc_component *component); +void snd_sof_audio_instance_unregister(struct snd_sof_audio_instance *instance); + +static inline struct snd_sof_audio_instance * +snd_sof_component_get_audio_instance(struct snd_soc_component *scomp) +{ + struct sof_client_dev *cdev = snd_sof_component_get_cdev(scomp); + struct sof_audio_client_pdata *pdata; + + if (!cdev) + return NULL; + + pdata = dev_get_platdata(&cdev->auxdev.dev); + + return pdata->instance; +} + +static inline const struct sof_ipc_tplg_ops * +snd_sof_component_get_tplg_ops(struct snd_soc_component *scomp) +{ + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); + + return instance ? instance->tplg_ops : NULL; +} + +static inline const struct sof_ipc_pcm_ops * +snd_sof_component_get_pcm_ops(struct snd_soc_component *scomp) +{ + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); + + return instance ? instance->pcm_ops : NULL; +} + +int sof_instance_set_up_pipelines(struct snd_sof_audio_instance *instance); + static inline struct snd_sof_pcm *snd_sof_find_spcm_dai(struct snd_soc_component *scomp, struct snd_soc_pcm_runtime *rtd) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct snd_sof_pcm *spcm; - list_for_each_entry(spcm, &sdev->pcm_list, list) { + list_for_each_entry(spcm, &instance->pcm_list, list) { if (le32_to_cpu(spcm->pcm.dai_id) == rtd->dai_link->id) return spcm; } @@ -639,13 +697,13 @@ struct snd_sof_pcm *snd_sof_find_spcm_name(struct snd_soc_component *scomp, struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp, unsigned int comp_id, int *direction); -void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream); + void snd_sof_pcm_init_elapsed_work(struct work_struct *work); int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd, struct snd_sof_pcm *spcm, struct snd_pcm_hw_params *params, struct snd_sof_platform_stream_params *platform_params, int dir); -struct snd_sof_widget *snd_sof_find_swidget_by_comp_id(struct snd_sof_dev *sdev, +struct snd_sof_widget *snd_sof_find_swidget_by_comp_id(struct snd_soc_component *scomp, int comp_id); /* * snd_sof_pcm specific wrappers for dev_dbg() and dev_err() to provide @@ -683,10 +741,6 @@ static inline void snd_sof_compr_init_elapsed_work(struct work_struct *work) { } /* DAI link fixup */ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params); -/* PM */ -bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev); -bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev); - /* Machine driver enumeration */ int sof_machine_register(struct snd_sof_dev *sdev, void *pdata); void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata); @@ -697,19 +751,20 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc struct snd_soc_dapm_widget *wsink); /* PCM */ -int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, +int sof_widget_list_setup(struct snd_soc_component *component, struct snd_sof_pcm *spcm, struct snd_pcm_hw_params *fe_params, struct snd_sof_platform_stream_params *platform_params, int dir); -int sof_widget_list_prepare(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, +int sof_widget_list_prepare(struct snd_soc_component *component, struct snd_sof_pcm *spcm, struct snd_pcm_hw_params *fe_params, struct snd_sof_platform_stream_params *platform_params, int dir); -void sof_widget_list_unprepare(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir); -int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir); +void sof_widget_list_unprepare(struct snd_soc_component *component, struct snd_sof_pcm *spcm, + int dir); +int sof_widget_list_free(struct snd_soc_component *component, struct snd_sof_pcm *spcm, int dir); int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm); -int sof_pcm_free_all_streams(struct snd_sof_dev *sdev); +int sof_pcm_free_all_streams(struct snd_soc_component *component); int get_token_u32(void *elem, void *object, u32 offset); int get_token_u16(void *elem, void *object, u32 offset); int get_token_comp_format(void *elem, void *object, u32 offset); diff --git a/sound/soc/sof/sof-client-audio-ipc3.c b/sound/soc/sof/sof-client-audio-ipc3.c new file mode 100644 index 00000000000000..1d03aee0dfea16 --- /dev/null +++ b/sound/soc/sof/sof-client-audio-ipc3.c @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// Copyright(c) 2025 Intel Corporation +// +// Author: Peter Ujfalusi +// +// IPC3 specific glue of the SOF audio client +// + +#include +#include +#include +#include + +#include "sof-audio.h" +#include "sof-client.h" +#include "sof-client-audio.h" + +/* IPC stream position. */ +static void sof_audio_client_ipc3_period_elapsed(struct sof_client_dev *cdev, + struct snd_soc_component *component, + u32 msg_id) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + struct snd_sof_pcm_stream *stream; + struct sof_ipc_stream_posn posn; + struct snd_sof_pcm *spcm; + int direction, ret; + + spcm = snd_sof_find_spcm_comp(component, msg_id, &direction); + if (!spcm) { + /* The stream is not owned by this component */ + dev_dbg(&cdev->auxdev.dev, + "period elapsed for unknown stream, msg_id %d\n", msg_id); + return; + } + + stream = &spcm->stream[direction]; + ret = sof_client_ipc_msg_data(cdev, stream, &posn, sizeof(posn)); + if (ret < 0) { + dev_warn(&cdev->auxdev.dev, + "failed to read stream position: %d\n", ret); + return; + } + + trace_sof_ipc3_period_elapsed_position(sdev, &posn); + + memcpy(&stream->posn, &posn, sizeof(posn)); + + if (spcm->pcm.compress) + snd_sof_compr_fragment_elapsed(stream->cstream); + else if (stream->substream->runtime && + !stream->substream->runtime->no_period_wakeup) + /* only inform ALSA for period_wakeup mode */ + queue_work(system_highpri_wq, &stream->period_elapsed_work); +} + +/* DSP notifies host of an XRUN within FW */ +static void sof_audio_client_ipc3_xrun(struct sof_client_dev *cdev, + struct snd_soc_component *component, + u32 msg_id) +{ + struct snd_sof_pcm_stream *stream; + struct sof_ipc_stream_posn posn; + struct snd_sof_pcm *spcm; + int direction, ret; + + spcm = snd_sof_find_spcm_comp(component, msg_id, &direction); + if (!spcm) { + /* The stream is not owned by this component */ + dev_dbg(&cdev->auxdev.dev, "XRUN for unknown stream, msg_id %d\n", + msg_id); + return; + } + + stream = &spcm->stream[direction]; + ret = sof_client_ipc_msg_data(cdev, stream, &posn, sizeof(posn)); + if (ret < 0) { + dev_warn(&cdev->auxdev.dev, + "failed to read overrun position: %d\n", ret); + return; + } + + dev_dbg(&cdev->auxdev.dev, "posn XRUN: host %llx comp %d size %d\n", + posn.host_posn, posn.xrun_comp_id, posn.xrun_size); + +#if defined(CONFIG_SND_SOC_SOF_DEBUG_XRUN_STOP) + /* stop PCM on XRUN - used for pipeline debug */ + memcpy(&stream->posn, &posn, sizeof(posn)); + snd_pcm_stop_xrun(stream->substream); +#endif +} + +/* stream notifications from firmware */ +static void sof_audio_client_ipc3_stream_message(struct sof_client_dev *cdev, + struct sof_ipc_cmd_hdr *hdr) +{ + struct sof_audio_client_pdata *pdata = dev_get_platdata(&cdev->auxdev.dev); + u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; + u32 msg_id = SOF_IPC_MESSAGE_ID(hdr->cmd); + + /* The component is only available while the sound card is bound */ + if (!pdata->component) + return; + + switch (msg_type) { + case SOF_IPC_STREAM_POSITION: + sof_audio_client_ipc3_period_elapsed(cdev, pdata->component, msg_id); + break; + case SOF_IPC_STREAM_TRIG_XRUN: + sof_audio_client_ipc3_xrun(cdev, pdata->component, msg_id); + break; + default: + dev_err(&cdev->auxdev.dev, "unhandled stream message %#x\n", + msg_id); + break; + } +} + +/* component notifications from firmware */ +static void sof_audio_client_ipc3_comp_notification(struct sof_client_dev *cdev, + struct sof_ipc_cmd_hdr *hdr) +{ + struct sof_audio_client_pdata *pdata = dev_get_platdata(&cdev->auxdev.dev); + const struct sof_ipc_tplg_ops *tplg_ops; + u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; + + /* The component is only available while the sound card is bound */ + if (!pdata->component) + return; + + switch (msg_type) { + case SOF_IPC_COMP_GET_VALUE: + case SOF_IPC_COMP_GET_DATA: + break; + default: + dev_err(&cdev->auxdev.dev, "unhandled component message %#x\n", + msg_type); + return; + } + + tplg_ops = snd_sof_component_get_tplg_ops(pdata->component); + if (tplg_ops && tplg_ops->control->update) + tplg_ops->control->update(pdata->component, hdr); +} + +static void sof_audio_client_ipc3_rx_notification(struct sof_client_dev *cdev, + void *msg_buf) +{ + struct sof_ipc_cmd_hdr *hdr = msg_buf; + + switch (hdr->cmd & SOF_GLB_TYPE_MASK) { + case SOF_IPC_GLB_COMP_MSG: + sof_audio_client_ipc3_comp_notification(cdev, hdr); + break; + case SOF_IPC_GLB_STREAM_MSG: + sof_audio_client_ipc3_stream_message(cdev, hdr); + break; + default: + break; + } +} + +const struct sof_audio_client_ipc_ops sof_audio_client_ipc3_ops = { + .rx_notification = sof_audio_client_ipc3_rx_notification, +}; diff --git a/sound/soc/sof/sof-client-audio-ipc4.c b/sound/soc/sof/sof-client-audio-ipc4.c new file mode 100644 index 00000000000000..0b2c2ab8da1989 --- /dev/null +++ b/sound/soc/sof/sof-client-audio-ipc4.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// Copyright(c) 2025 Intel Corporation +// +// Author: Peter Ujfalusi +// +// IPC4 specific glue of the SOF audio client +// + +#include +#include + +#include "sof-audio.h" +#include "ipc4-priv.h" +#include "sof-client.h" +#include "sof-client-audio.h" + +static void sof_audio_client_ipc4_module_notification(struct sof_client_dev *cdev, + struct sof_ipc4_msg *ipc4_msg) +{ + struct sof_audio_client_pdata *pdata = dev_get_platdata(&cdev->auxdev.dev); + struct sof_ipc4_notify_module_data *data = ipc4_msg->data_ptr; + + /* The component is only available while the sound card is bound */ + if (!pdata->component) + return; + + switch (data->event_id & SOF_IPC4_NOTIFY_MODULE_EVENTID_SOF_MAGIC_MASK) { + case SOF_IPC4_NOTIFY_MODULE_EVENTID_ALSA_MAGIC_VAL: + { + const struct sof_ipc_tplg_ops *tplg_ops; + + /* ALSA kcontrol notification */ + tplg_ops = snd_sof_component_get_tplg_ops(pdata->component); + if (tplg_ops && tplg_ops->control->update) + tplg_ops->control->update(pdata->component, ipc4_msg); + + break; + } + case SOF_IPC4_NOTIFY_MODULE_EVENTID_COMPR_MAGIC_VAL: + sof_ipc4_compr_drain_done(pdata->component, ipc4_msg); + break; + default: + break; + } +} + +static void sof_audio_client_ipc4_rx_notification(struct sof_client_dev *cdev, + void *msg_buf) +{ + struct sof_ipc4_msg *ipc4_msg = msg_buf; + + if (!ipc4_msg || !ipc4_msg->data_ptr) + return; + + switch (SOF_IPC4_NOTIFICATION_TYPE_GET(ipc4_msg->primary)) { + case SOF_IPC4_NOTIFY_MODULE_NOTIFICATION: + sof_audio_client_ipc4_module_notification(cdev, ipc4_msg); + break; + default: + break; + } +} + +/* + * Look up a module instance in the topology of this audio client and copy its + * widget name out. The name is copied rather than returned by reference so + * that the caller does not need to hold a reference on the topology. + */ +static int sof_audio_client_ipc4_get_module_name(struct sof_client_dev *cdev, + u32 module_id, int instance_id, + char *name, size_t size) +{ + struct sof_audio_client_pdata *pdata = dev_get_platdata(&cdev->auxdev.dev); + struct snd_sof_widget *swidget; + + /* The component is only available while the sound card is bound */ + if (!pdata->component) + return -ENOENT; + + swidget = sof_ipc4_find_swidget_by_ids(pdata->component, module_id, + instance_id); + if (!swidget) + return -ENOENT; + + strscpy(name, swidget->widget->name, size); + + return 0; +} + +const struct sof_audio_client_ipc_ops sof_audio_client_ipc4_ops = { + .rx_notification = sof_audio_client_ipc4_rx_notification, + .get_module_name = sof_audio_client_ipc4_get_module_name, +}; diff --git a/sound/soc/sof/sof-client-audio.c b/sound/soc/sof/sof-client-audio.c new file mode 100644 index 00000000000000..ff65ed98d5305e --- /dev/null +++ b/sound/soc/sof/sof-client-audio.c @@ -0,0 +1,282 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2026 Intel Corporation +// +// Authors: Peter Ujfalusi +// + +#include +#include +#include +#include +#include +#include + +#include "sof-client.h" +#include "sof-client-audio.h" + +#define CREATE_TRACE_POINTS +#include + +#define SOF_AUDIO_SUSPEND_DELAY_MS 3000 + +static void sof_audio_client_init_debugfs(struct sof_client_dev *cdev, + struct sof_audio_client_pdata *pdata) +{ + struct dentry *debugfs_root = sof_client_get_debugfs_root(cdev); + char *debugfs_dir; + + debugfs_dir = devm_kasprintf(&cdev->auxdev.dev, GFP_KERNEL, "audio.%u", + cdev->auxdev.id); + if (!debugfs_dir) + return; + + pdata->debugfs_root = debugfs_create_dir(debugfs_dir, debugfs_root); + if (IS_ERR_OR_NULL(pdata->debugfs_root)) + return; + + pdata->debug_topology_name = pdata->machine.sof_tplg_filename ? + pdata->machine.sof_tplg_filename : + sof_client_get_topology_name(cdev); + pdata->debug_card_name = pdata->machine.mach_params.card_name; + pdata->debug_machine_driver = pdata->machine.drv_name; + + debugfs_create_str("topology_name", 0444, pdata->debugfs_root, + (char **)&pdata->debug_topology_name); + + if (pdata->debug_card_name) + debugfs_create_str("card_name", 0444, pdata->debugfs_root, + (char **)&pdata->debug_card_name); + + if (pdata->debug_machine_driver) + debugfs_create_str("machine_driver", 0444, pdata->debugfs_root, + (char **)&pdata->debug_machine_driver); +} + +static void sof_audio_client_ipc_rx_handler(struct sof_client_dev *cdev, void *msg_buf) +{ + struct sof_audio_client_pdata *pdata = dev_get_platdata(&cdev->auxdev.dev); + + if (pdata->ipc_ops && pdata->ipc_ops->rx_notification) + pdata->ipc_ops->rx_notification(cdev, msg_buf); +} + +static int sof_audio_client_get_module_name(struct sof_client_dev *cdev, + u32 module_id, int instance_id, + char *name, size_t size) +{ + struct sof_audio_client_pdata *pdata = dev_get_platdata(&cdev->auxdev.dev); + + if (!pdata->ipc_ops || !pdata->ipc_ops->get_module_name) + return -EOPNOTSUPP; + + return pdata->ipc_ops->get_module_name(cdev, module_id, instance_id, + name, size); +} + +static int sof_audio_client_fw_booted(struct sof_client_dev *cdev) +{ + struct sof_audio_client_pdata *pdata = dev_get_platdata(&cdev->auxdev.dev); + + if (!pdata->component) + return 0; + + return sof_audio_instance_restore(pdata->component); +} + +/* Streams which ignored the suspend trigger are left running in the DSP */ +static bool sof_audio_client_keep_dsp_in_d0(struct sof_client_dev *cdev) +{ + struct sof_audio_client_pdata *pdata = dev_get_platdata(&cdev->auxdev.dev); + + if (!pdata->component) + return false; + + return sof_audio_instance_suspend_ignored(pdata->component); +} + +static enum sof_d0i3_vote sof_audio_client_d0i3_vote(struct sof_client_dev *cdev) +{ + struct sof_audio_client_pdata *pdata = dev_get_platdata(&cdev->auxdev.dev); + + if (!pdata->component) + return SOF_D0I3_NO_ACTIVITY; + + return sof_audio_instance_d0i3_vote(pdata->component); +} + +static bool sof_audio_client_period_elapsed(struct sof_client_dev *cdev, + struct snd_pcm_substream *substream) +{ + struct sof_audio_client_pdata *pdata = dev_get_platdata(&cdev->auxdev.dev); + + if (!pdata->component) + return false; + + return sof_audio_instance_period_elapsed(pdata->component, substream); +} + +static const struct sof_client_ops sof_audio_client_ops = { + .ipc_rx_handler = sof_audio_client_ipc_rx_handler, + .fw_booted = sof_audio_client_fw_booted, + .keep_dsp_in_d0 = sof_audio_client_keep_dsp_in_d0, + .d0i3_vote = sof_audio_client_d0i3_vote, + .period_elapsed = sof_audio_client_period_elapsed, + .get_module_name = sof_audio_client_get_module_name, +}; + +static const struct sof_audio_client_ipc_ops * +sof_audio_client_get_ipc_ops(struct sof_client_dev *cdev) +{ + switch (sof_client_get_ipc_type(cdev)) { +#if IS_ENABLED(CONFIG_SND_SOC_SOF_IPC3) + case SOF_IPC_TYPE_3: + return &sof_audio_client_ipc3_ops; +#endif +#if IS_ENABLED(CONFIG_SND_SOC_SOF_IPC4) + case SOF_IPC_TYPE_4: + return &sof_audio_client_ipc4_ops; +#endif + default: + return NULL; + } +} + +static int sof_audio_client_probe(struct auxiliary_device *auxdev, + const struct auxiliary_device_id *id) +{ + struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); + struct sof_audio_client_pdata *pdata = dev_get_platdata(&auxdev->dev); + int ret; + + auxiliary_set_drvdata(auxdev, cdev); + + pdata->ipc_ops = sof_audio_client_get_ipc_ops(cdev); + + ret = sof_client_register_ops(cdev, &sof_audio_client_ops); + if (ret < 0) + return ret; + + snd_sof_new_platform_drv(cdev, &pdata->plat_drv); + + ret = snd_soc_register_component(&auxdev->dev, &pdata->plat_drv, + pdata->drv, pdata->num_drv); + if (ret < 0) { + sof_client_unregister_ops(cdev); + return ret; + } + + ret = sof_client_machine_register(cdev); + if (ret < 0) { + snd_soc_unregister_component(&auxdev->dev); + sof_client_unregister_ops(cdev); + return ret; + } + + sof_audio_client_init_debugfs(cdev, pdata); + + pm_runtime_set_autosuspend_delay(&auxdev->dev, SOF_AUDIO_SUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(&auxdev->dev); + pm_runtime_enable(&auxdev->dev); + pm_runtime_mark_last_busy(&auxdev->dev); + pm_runtime_idle(&auxdev->dev); + + return 0; +} + +static void sof_audio_client_remove(struct auxiliary_device *auxdev) +{ + struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); + struct sof_audio_client_pdata *pdata = dev_get_platdata(&auxdev->dev); + + sof_client_unregister_ops(cdev); + + if (pdata && pdata->debugfs_root) + debugfs_remove_recursive(pdata->debugfs_root); + + pm_runtime_disable(&auxdev->dev); + + sof_client_machine_unregister(cdev); + snd_soc_unregister_component(&auxdev->dev); +} + +/* + * Tear down the topology pipelines owned by this audio client. + * + * The audio clients are children of the SOF device, so the PM core invokes + * their suspend callbacks before the SOF device is suspended. Only the + * pipelines of the calling instance are freed, other audio clients of the same + * DSP are left untouched. + */ +static int sof_audio_client_suspend(struct device *dev) +{ + struct auxiliary_device *auxdev = to_auxiliary_dev(dev); + struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); + struct sof_audio_client_pdata *pdata = dev_get_platdata(dev); + + if (!pdata || !pdata->component) + return 0; + + /* + * The pipelines need to be torn down only if the DSP hardware is + * active. If it is already suspended there is nothing to free up. + */ + if (!sof_client_is_dsp_in_d0(cdev)) + return 0; + + return sof_audio_instance_suspend(pdata->component); +} + +/* + * Set up the static topology pipelines owned by this audio client. + * + * The audio clients are children of the SOF device, so the PM core invokes + * their resume callbacks after the SOF device has been resumed. If the DSP was + * powered down the firmware boot has already restored all instances, this only + * covers the case when the instance was suspended while the DSP remained in D0. + */ +static int sof_audio_client_resume(struct device *dev) +{ + struct auxiliary_device *auxdev = to_auxiliary_dev(dev); + struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev); + struct sof_audio_client_pdata *pdata = dev_get_platdata(dev); + + if (!pdata || !pdata->component) + return 0; + + /* + * With on-demand DSP boot the firmware is not running at this point, + * the pipelines are set up when the DSP is booted. + */ + if (sof_client_get_fw_state(cdev) != SOF_FW_BOOT_COMPLETE) + return 0; + + return sof_audio_instance_resume(pdata->component); +} + +static const struct dev_pm_ops sof_audio_client_pm = { + SYSTEM_SLEEP_PM_OPS(sof_audio_client_suspend, sof_audio_client_resume) + RUNTIME_PM_OPS(sof_audio_client_suspend, sof_audio_client_resume, NULL) +}; + +static const struct auxiliary_device_id sof_audio_client_id_table[] = { + { .name = "snd_sof.audio", }, + {}, +}; +MODULE_DEVICE_TABLE(auxiliary, sof_audio_client_id_table); + +static struct auxiliary_driver sof_audio_client_drv = { + .probe = sof_audio_client_probe, + .remove = sof_audio_client_remove, + .driver = { + /* auxiliary_driver_register() sets .name to be the modname */ + .pm = pm_ptr(&sof_audio_client_pm), + }, + .id_table = sof_audio_client_id_table, +}; + +module_auxiliary_driver(sof_audio_client_drv); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("SOF Audio Client Driver"); +MODULE_IMPORT_NS("SND_SOC_SOF_CLIENT"); diff --git a/sound/soc/sof/sof-client-audio.h b/sound/soc/sof/sof-client-audio.h new file mode 100644 index 00000000000000..6d42f19addee8e --- /dev/null +++ b/sound/soc/sof/sof-client-audio.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __SOF_CLIENT_AUDIO_H +#define __SOF_CLIENT_AUDIO_H + +#include + +struct dentry; +struct sof_client_dev; +struct snd_sof_audio_instance; + +/** + * struct sof_audio_client_ipc_ops - IPC version specific ops of the audio client + * @rx_notification: Handle a firmware initiated notification. The message and + * its payload is owned by the SOF core, it must not be + * modified or freed by the client. + * @get_module_name: Copy the topology name of a module instance owned by this + * audio client. Returns -ENOENT if the module is not part of + * the topology of this client. + * + * Collects the IPC version specific glue of the audio client, most notably the + * demultiplexing of the firmware notifications the client is interested in. + */ +struct sof_audio_client_ipc_ops { + void (*rx_notification)(struct sof_client_dev *cdev, void *msg_buf); + int (*get_module_name)(struct sof_client_dev *cdev, u32 module_id, + int instance_id, char *name, size_t size); +}; + +/** + * struct sof_audio_client_pdata - platform data for the audio sof-client + * @plat_drv: Pre-built ASoC component driver + * @drv: Array of DAI drivers to register + * @num_drv: Number of DAI drivers + * @machine: Per-instance copy of the machine descriptor + * @component: The ASoC component registered by this audio client instance + * @instance: The audio instance backing @component + * @ipc_ops: IPC version specific ops of this audio client instance + * @debugfs_root: Per-audio-client debugfs directory + * @debug_topology_name: Topology loaded by this audio client + * @debug_card_name: Card name for this audio client + * @debug_machine_driver: Machine driver bound to this audio client + */ +struct sof_audio_client_pdata { + struct snd_soc_component_driver plat_drv; + struct snd_soc_dai_driver *drv; + int num_drv; + struct snd_soc_acpi_mach machine; + struct snd_soc_component *component; + struct snd_sof_audio_instance *instance; + const struct sof_audio_client_ipc_ops *ipc_ops; + struct dentry *debugfs_root; + const char *debug_topology_name; + const char *debug_card_name; + const char *debug_machine_driver; +}; + +extern const struct sof_audio_client_ipc_ops sof_audio_client_ipc3_ops; +extern const struct sof_audio_client_ipc_ops sof_audio_client_ipc4_ops; + +/* power management of a single audio instance */ +int sof_audio_instance_suspend(struct snd_soc_component *component); +int sof_audio_instance_resume(struct snd_soc_component *component); +int sof_audio_instance_restore(struct snd_soc_component *component); +bool sof_audio_instance_suspend_ignored(struct snd_soc_component *component); +enum sof_d0i3_vote sof_audio_instance_d0i3_vote(struct snd_soc_component *component); +bool sof_audio_instance_period_elapsed(struct snd_soc_component *component, + struct snd_pcm_substream *substream); + +void snd_sof_new_platform_drv(struct sof_client_dev *cdev, + struct snd_soc_component_driver * const pd); + +#endif /* __SOF_CLIENT_AUDIO_H */ diff --git a/sound/soc/sof/sof-client-probes-ipc4.c b/sound/soc/sof/sof-client-probes-ipc4.c index 2eef32b5539540..a8928fa905eaad 100644 --- a/sound/soc/sof/sof-client-probes-ipc4.c +++ b/sound/soc/sof/sof-client-probes-ipc4.c @@ -288,19 +288,22 @@ static int ipc4_probes_point_print(struct sof_client_dev *cdev, char *buf, size_ struct sof_probe_point_desc *desc) { struct device *dev = &cdev->auxdev.dev; - struct snd_sof_widget *swidget; + char name[SOF_CLIENT_MODULE_NAME_MAX]; int ret; - swidget = sof_client_ipc4_find_swidget_by_id(cdev, SOF_IPC4_MOD_ID_GET(desc->buffer_id), - SOF_IPC4_MOD_INSTANCE_GET(desc->buffer_id)); - if (!swidget) + ret = sof_client_ipc4_get_module_name(cdev, SOF_IPC4_MOD_ID_GET(desc->buffer_id), + SOF_IPC4_MOD_INSTANCE_GET(desc->buffer_id), + name, sizeof(name)); + if (ret < 0) { dev_err(dev, "%s: Failed to find widget for module %lu.%lu\n", __func__, SOF_IPC4_MOD_ID_GET(desc->buffer_id), SOF_IPC4_MOD_INSTANCE_GET(desc->buffer_id)); + strscpy(name, "", sizeof(name)); + } ret = scnprintf(buf, size, "%#x,%#x,%#x\t%s %s buf idx %lu %s\n", desc->buffer_id, desc->purpose, desc->stream_tag, - swidget ? swidget->widget->name : "", + name, sof_probe_ipc4_type_string(SOF_IPC4_PROBE_TYPE_GET(desc->buffer_id)), SOF_IPC4_PROBE_IDX_GET(desc->buffer_id), desc->stream_tag ? "(connected)" : ""); diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index a90d8243d71a7a..0ce9f3a67ac1d8 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -11,40 +11,20 @@ #include #include #include +#include +#include #include +#include #include #include "ops.h" +#include "sof-audio.h" #include "sof-client.h" +#include "sof-client-audio.h" +#include "sof-of-dev.h" #include "sof-priv.h" #include "ipc3-priv.h" #include "ipc4-priv.h" -/** - * struct sof_ipc_event_entry - IPC client event description - * @ipc_msg_type: IPC msg type of the event the client is interested - * @cdev: sof_client_dev of the requesting client - * @callback: Callback function of the client - * @list: item in SOF core client event list - */ -struct sof_ipc_event_entry { - u32 ipc_msg_type; - struct sof_client_dev *cdev; - sof_client_event_callback callback; - struct list_head list; -}; - -/** - * struct sof_state_event_entry - DSP panic event subscription entry - * @cdev: sof_client_dev of the requesting client - * @callback: Callback function of the client - * @list: item in SOF core client event list - */ -struct sof_state_event_entry { - struct sof_client_dev *cdev; - sof_client_fw_state_callback callback; - struct list_head list; -}; - /** * struct sof_client_dev_entry - client device entry for internal management use * @sdev: pointer to SOF core device struct @@ -190,6 +170,16 @@ int sof_register_clients(struct snd_sof_dev *sdev) { int ret; + /* Register audio client (needed in dspless mode too) */ + if (sof_ops(sdev) && sof_ops(sdev)->register_audio_client) { + ret = sof_ops(sdev)->register_audio_client(sdev); + if (ret < 0) { + dev_err(sdev->dev, + "audio client registration failed: %d\n", ret); + return ret; + } + } + if (sdev->dspless_mode_selected) return 0; @@ -197,7 +187,7 @@ int sof_register_clients(struct snd_sof_dev *sdev) ret = sof_register_ipc_flood_test(sdev); if (ret) { dev_err(sdev->dev, "IPC flood test client registration failed\n"); - return ret; + goto err_audio; } ret = sof_register_ipc_msg_injector(sdev); @@ -237,6 +227,10 @@ int sof_register_clients(struct snd_sof_dev *sdev) err_msg_injector: sof_unregister_ipc_flood_test(sdev); +err_audio: + if (sof_ops(sdev) && sof_ops(sdev)->unregister_audio_client) + sof_ops(sdev)->unregister_audio_client(sdev); + return ret; } @@ -249,6 +243,10 @@ void sof_unregister_clients(struct snd_sof_dev *sdev) sof_unregister_ipc_msg_injector(sdev); sof_unregister_ipc_flood_test(sdev); sof_unregister_fw_gdb(sdev); + + /* Audio client last (reverse of registration order) */ + if (sof_ops(sdev) && sof_ops(sdev)->unregister_audio_client) + sof_ops(sdev)->unregister_audio_client(sdev); } int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id, @@ -407,18 +405,32 @@ struct sof_ipc4_fw_module *sof_client_ipc4_find_module(struct sof_client_dev *c, } EXPORT_SYMBOL_NS_GPL(sof_client_ipc4_find_module, "SND_SOC_SOF_CLIENT"); -struct snd_sof_widget *sof_client_ipc4_find_swidget_by_id(struct sof_client_dev *cdev, - u32 module_id, int instance_id) +int sof_client_ipc4_get_module_name(struct sof_client_dev *cdev, u32 module_id, + int instance_id, char *name, size_t size) { struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + struct sof_client_dev *client; - if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) - return sof_ipc4_find_swidget_by_ids(sdev, module_id, instance_id); - dev_err(sdev->dev, "Only supported with IPC4\n"); + if (sdev->pdata->ipc_type != SOF_IPC_TYPE_4) { + dev_err(sdev->dev, "Only supported with IPC4\n"); + return -EOPNOTSUPP; + } - return NULL; + /* The module can be in the topology of any of the audio clients */ + guard(srcu)(&sdev->client_ops_srcu); + list_for_each_entry_srcu(client, &sdev->client_ops_list, ops_node, + srcu_read_lock_held(&sdev->client_ops_srcu)) { + if (!client->ops->get_module_name) + continue; + + if (!client->ops->get_module_name(client, module_id, instance_id, + name, size)) + return 0; + } + + return -ENOENT; } -EXPORT_SYMBOL_NS_GPL(sof_client_ipc4_find_swidget_by_id, "SND_SOC_SOF_CLIENT"); +EXPORT_SYMBOL_NS_GPL(sof_client_ipc4_get_module_name, "SND_SOC_SOF_CLIENT"); ssize_t sof_client_ipc4_find_debug_slot_offset_by_type(struct sof_client_dev *cdev, u32 type) @@ -515,6 +527,145 @@ enum sof_ipc_type sof_client_get_ipc_type(struct sof_client_dev *cdev) } EXPORT_SYMBOL_NS_GPL(sof_client_get_ipc_type, "SND_SOC_SOF_CLIENT"); +/** + * sof_client_is_suspend_target_s0ix - check if the system is suspending to S0iX + * @cdev: SOF client device + * + * The suspend target is only valid while a system suspend is in progress, it + * is reset when the system resumes. + * + * Return: true if the pending system suspend targets S0iX. + */ +bool sof_client_is_suspend_target_s0ix(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + return sdev->system_suspend_target == SOF_SUSPEND_S0IX; +} +EXPORT_SYMBOL_NS_GPL(sof_client_is_suspend_target_s0ix, "SND_SOC_SOF_CLIENT"); + +bool sof_client_is_dsp_in_d0(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + return sdev->dsp_power_state.state == SOF_DSP_PM_D0; +} +EXPORT_SYMBOL_NS_GPL(sof_client_is_dsp_in_d0, "SND_SOC_SOF_CLIENT"); + +/** + * sof_client_get_topology_name - get the topology file name of the SOF device + * @cdev: SOF client device + * + * The returned name is the topology resolved by the SOF core, either from the + * selected machine descriptor or from the firmware file profile. Clients + * owning their own machine description should prefer the topology name from + * it and only fall back to this one. + * + * Return: the topology file name, without the topology path prefix. + */ +const char *sof_client_get_topology_name(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + return sdev->pdata->tplg_filename; +} +EXPORT_SYMBOL_NS_GPL(sof_client_get_topology_name, "SND_SOC_SOF_CLIENT"); + +/** + * sof_client_get_topology_prefix - get the topology path of the SOF device + * @cdev: SOF client device + * + * The topology path is a property of the SOF device, it is shared by all + * clients and it applies to the topology name of the client's own machine + * description as well. + * + * Return: the path the topology files are loaded from. + */ +const char *sof_client_get_topology_prefix(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + return sdev->pdata->tplg_filename_prefix; +} +EXPORT_SYMBOL_NS_GPL(sof_client_get_topology_prefix, "SND_SOC_SOF_CLIENT"); + +/** + * sof_client_is_function_topology_disabled - check if function topology is off + * @cdev: SOF client device + * + * Function topology loading is disabled when the user requested a specific + * topology file to be loaded, in which case that file must be used as is. + * + * Return: true if function topology loading must not be attempted. + */ +bool sof_client_is_function_topology_disabled(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + return sdev->pdata->disable_function_topology; +} +EXPORT_SYMBOL_NS_GPL(sof_client_is_function_topology_disabled, "SND_SOC_SOF_CLIENT"); + +/** + * sof_client_get_machine - get the machine description of the SOF device + * @cdev: SOF client device + * + * This is the machine selected by the SOF core, shared by all clients. A + * client owning its own machine description must use that one instead, this + * is only the fallback for clients which do not have one. + * + * Return: the machine description, or NULL if the SOF device has none. + */ +const struct snd_soc_acpi_mach *sof_client_get_machine(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + return sdev->pdata->machine; +} +EXPORT_SYMBOL_NS_GPL(sof_client_get_machine, "SND_SOC_SOF_CLIENT"); + +/* + * The machine driver is described either by an ACPI or by an OF descriptor, + * depending on the platform. Hide the difference from the clients. + */ +const char *sof_client_get_machine_drv_name(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + struct snd_sof_pdata *plat_data = sdev->pdata; + + if (plat_data->machine) + return plat_data->machine->drv_name; + + if (plat_data->of_machine) + return plat_data->of_machine->drv_name; + + return NULL; +} +EXPORT_SYMBOL_NS_GPL(sof_client_get_machine_drv_name, "SND_SOC_SOF_CLIENT"); + +/** + * sof_client_get_ssp_mclk_id_quirk - get the platform SSP MCLK ID quirk + * @cdev: SOF client device + * @mclk_id: filled with the quirk value when a quirk is present + * + * Some platforms need to override the SSP mclk_id specified by the topology, + * either derived from NHLT or forced via a kernel parameter. + * + * Return: true if @mclk_id was set and the topology value must be overridden. + */ +bool sof_client_get_ssp_mclk_id_quirk(struct sof_client_dev *cdev, u16 *mclk_id) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + if (!sdev->mclk_id_override) + return false; + + *mclk_id = sdev->mclk_id_quirk; + + return true; +} +EXPORT_SYMBOL_NS_GPL(sof_client_get_ssp_mclk_id_quirk, "SND_SOC_SOF_CLIENT"); + int sof_client_boot_dsp(struct sof_client_dev *cdev) { return snd_sof_boot_dsp_firmware(sof_client_dev_to_sof_dev(cdev)); @@ -542,139 +693,162 @@ void sof_client_core_module_put(struct sof_client_dev *cdev) EXPORT_SYMBOL_NS_GPL(sof_client_core_module_put, "SND_SOC_SOF_CLIENT"); /* IPC event handling */ -void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *msg_buf) +int sof_client_ipc_msg_data(struct sof_client_dev *cdev, + struct snd_sof_pcm_stream *sps, void *p, size_t sz) { - struct sof_ipc_event_entry *event; - u32 msg_type; + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); - if (sdev->pdata->ipc_type == SOF_IPC_TYPE_3) { - struct sof_ipc_cmd_hdr *hdr = msg_buf; + return snd_sof_ipc_msg_data(sdev, sps, p, sz); +} +EXPORT_SYMBOL_NS_GPL(sof_client_ipc_msg_data, "SND_SOC_SOF_CLIENT"); - msg_type = hdr->cmd & SOF_GLB_TYPE_MASK; - } else if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) { - struct sof_ipc4_msg *msg = msg_buf; +void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *msg_buf) +{ + struct sof_client_dev *cdev; - msg_type = SOF_IPC4_NOTIFICATION_TYPE_GET(msg->primary); - } else { - dev_dbg_once(sdev->dev, "Not supported IPC version: %d\n", - sdev->pdata->ipc_type); - return; + guard(srcu)(&sdev->client_ops_srcu); + list_for_each_entry_srcu(cdev, &sdev->client_ops_list, ops_node, + srcu_read_lock_held(&sdev->client_ops_srcu)) { + if (cdev->ops->ipc_rx_handler) + cdev->ops->ipc_rx_handler(cdev, msg_buf); } +} + +/* DSP state notification */ +void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev) +{ + struct sof_client_dev *cdev; - guard(mutex)(&sdev->client_event_handler_mutex); - list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) { - if (event->ipc_msg_type == msg_type) - event->callback(event->cdev, msg_buf); + guard(srcu)(&sdev->client_ops_srcu); + list_for_each_entry_srcu(cdev, &sdev->client_ops_list, ops_node, + srcu_read_lock_held(&sdev->client_ops_srcu)) { + if (cdev->ops->fw_state_handler) + cdev->ops->fw_state_handler(cdev, sdev->fw_state); } } -int sof_client_register_ipc_rx_handler(struct sof_client_dev *cdev, - u32 ipc_msg_type, - sof_client_event_callback callback) +/* Let the clients rebuild the state they had in the DSP before the boot */ +int sof_client_fw_booted_dispatcher(struct snd_sof_dev *sdev) { - struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); - struct sof_ipc_event_entry *event; + struct sof_client_dev *cdev; + int ret; - if (!callback) - return -EINVAL; + guard(srcu)(&sdev->client_ops_srcu); + list_for_each_entry_srcu(cdev, &sdev->client_ops_list, ops_node, + srcu_read_lock_held(&sdev->client_ops_srcu)) { + if (!cdev->ops->fw_booted) + continue; - if (sdev->pdata->ipc_type == SOF_IPC_TYPE_3) { - if (!(ipc_msg_type & SOF_GLB_TYPE_MASK)) - return -EINVAL; - } else if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) { - if (!(ipc_msg_type & SOF_IPC4_NOTIFICATION_TYPE_MASK)) - return -EINVAL; - } else { - dev_warn(sdev->dev, "%s: Not supported IPC version: %d\n", - __func__, sdev->pdata->ipc_type); - return -EINVAL; + ret = cdev->ops->fw_booted(cdev); + if (ret < 0) { + dev_err(&cdev->auxdev.dev, + "failed to handle the firmware boot: %d\n", ret); + return ret; + } } - event = kmalloc_obj(*event); - if (!event) - return -ENOMEM; + return 0; +} - event->ipc_msg_type = ipc_msg_type; - event->cdev = cdev; - event->callback = callback; +/* + * Any client which left state running in the DSP on purpose keeps it powered + * over the system suspend. Clients without the callback do not participate. + */ +bool sof_client_keep_dsp_in_d0(struct snd_sof_dev *sdev) +{ + struct sof_client_dev *cdev; - /* add to list of SOF client devices */ - guard(mutex)(&sdev->client_event_handler_mutex); - list_add(&event->list, &sdev->ipc_rx_handler_list); + guard(srcu)(&sdev->client_ops_srcu); + list_for_each_entry_srcu(cdev, &sdev->client_ops_list, ops_node, + srcu_read_lock_held(&sdev->client_ops_srcu)) { + if (cdev->ops->keep_dsp_in_d0 && cdev->ops->keep_dsp_in_d0(cdev)) + return true; + } - return 0; + return false; } -EXPORT_SYMBOL_NS_GPL(sof_client_register_ipc_rx_handler, "SND_SOC_SOF_CLIENT"); +EXPORT_SYMBOL_NS_GPL(sof_client_keep_dsp_in_d0, "SND_SOC_SOF_CLIENT"); -void sof_client_unregister_ipc_rx_handler(struct sof_client_dev *cdev, - u32 ipc_msg_type) +/* + * Fold the D0i3 votes of every client that implements the d0i3_vote callback by + * taking the highest one, SOF_D0I3_INCOMPATIBLE being a veto. + * Clients without the callback do not participate in the vote. + */ +enum sof_d0i3_vote sof_client_get_d0i3_vote(struct snd_sof_dev *sdev) { - struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); - struct sof_ipc_event_entry *event; + enum sof_d0i3_vote result = SOF_D0I3_NO_ACTIVITY; + struct sof_client_dev *cdev; - guard(mutex)(&sdev->ipc_client_mutex); + guard(srcu)(&sdev->client_ops_srcu); + list_for_each_entry_srcu(cdev, &sdev->client_ops_list, ops_node, + srcu_read_lock_held(&sdev->client_ops_srcu)) { + enum sof_d0i3_vote vote; - list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) { - if (event->cdev == cdev && event->ipc_msg_type == ipc_msg_type) { - list_del(&event->list); - kfree(event); - break; - } + if (!cdev->ops->d0i3_vote) + continue; + + vote = cdev->ops->d0i3_vote(cdev); + if (vote == SOF_D0I3_INCOMPATIBLE) + return vote; + + result = max(result, vote); } + + return result; } -EXPORT_SYMBOL_NS_GPL(sof_client_unregister_ipc_rx_handler, "SND_SOC_SOF_CLIENT"); +EXPORT_SYMBOL_NS_GPL(sof_client_get_d0i3_vote, "SND_SOC_SOF_CLIENT"); -/*DSP state notification and query */ -void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev) +void sof_client_period_elapsed(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) { - struct sof_state_event_entry *event; + struct sof_client_dev *cdev; - guard(mutex)(&sdev->ipc_client_mutex); + guard(srcu)(&sdev->client_ops_srcu); + list_for_each_entry_srcu(cdev, &sdev->client_ops_list, ops_node, + srcu_read_lock_held(&sdev->client_ops_srcu)) { + if (!cdev->ops->period_elapsed) + continue; - list_for_each_entry(event, &sdev->fw_state_handler_list, list) - event->callback(event->cdev, sdev->fw_state); + if (cdev->ops->period_elapsed(cdev, substream)) + return; + } + + dev_warn_ratelimited(sdev->dev, "period elapsed for unknown stream\n"); } +EXPORT_SYMBOL_NS_GPL(sof_client_period_elapsed, "SND_SOC_SOF_CLIENT"); -int sof_client_register_fw_state_handler(struct sof_client_dev *cdev, - sof_client_fw_state_callback callback) +int sof_client_register_ops(struct sof_client_dev *cdev, + const struct sof_client_ops *ops) { struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); - struct sof_state_event_entry *event; - if (!callback) + if (!ops) return -EINVAL; - event = kmalloc_obj(*event); - if (!event) - return -ENOMEM; - - event->cdev = cdev; - event->callback = callback; - - /* add to list of SOF client devices */ - guard(mutex)(&sdev->client_event_handler_mutex); - list_add(&event->list, &sdev->fw_state_handler_list); + guard(mutex)(&sdev->client_ops_mutex); + cdev->ops = ops; + list_add_rcu(&cdev->ops_node, &sdev->client_ops_list); return 0; } -EXPORT_SYMBOL_NS_GPL(sof_client_register_fw_state_handler, "SND_SOC_SOF_CLIENT"); +EXPORT_SYMBOL_NS_GPL(sof_client_register_ops, "SND_SOC_SOF_CLIENT"); -void sof_client_unregister_fw_state_handler(struct sof_client_dev *cdev) +void sof_client_unregister_ops(struct sof_client_dev *cdev) { struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); - struct sof_state_event_entry *event; - guard(mutex)(&sdev->ipc_client_mutex); + guard(mutex)(&sdev->client_ops_mutex); + if (!cdev->ops) + return; - list_for_each_entry(event, &sdev->fw_state_handler_list, list) { - if (event->cdev == cdev) { - list_del(&event->list); - kfree(event); - break; - } - } + list_del_rcu(&cdev->ops_node); + + /* a dispatcher may still be executing a callback of this client */ + synchronize_srcu(&sdev->client_ops_srcu); + + cdev->ops = NULL; } -EXPORT_SYMBOL_NS_GPL(sof_client_unregister_fw_state_handler, "SND_SOC_SOF_CLIENT"); +EXPORT_SYMBOL_NS_GPL(sof_client_unregister_ops, "SND_SOC_SOF_CLIENT"); enum sof_fw_state sof_client_get_fw_state(struct sof_client_dev *cdev) { @@ -705,3 +879,136 @@ void sof_client_mailbox_write(struct sof_client_dev *cdev, u32 offset, sof_mailbox_write(sof_client_dev_to_sof_dev(cdev), offset, message, bytes); } EXPORT_SYMBOL_NS_GPL(sof_client_mailbox_write, "SND_SOC_SOF_CLIENT"); + +struct snd_sof_mailbox *sof_client_get_mailbox(struct sof_client_dev *cdev, + enum snd_sof_mailbox_type type) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + switch (type) { + case SOF_MAILBOX_FW_INFO: + return &sdev->fw_info_box; + case SOF_MAILBOX_DSP: + return &sdev->dsp_box; + case SOF_MAILBOX_HOST: + return &sdev->host_box; + case SOF_MAILBOX_STREAM: + return &sdev->stream_box; + case SOF_MAILBOX_DEBUG: + return &sdev->debug_box; + default: + return NULL; + } +} +EXPORT_SYMBOL_NS_GPL(sof_client_get_mailbox, "SND_SOC_SOF_CLIENT"); + +bool sof_client_is_dspless(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + return sdev->dspless_mode_selected; +} +EXPORT_SYMBOL_NS_GPL(sof_client_is_dspless, "SND_SOC_SOF_CLIENT"); + +int sof_client_get_num_cores(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + return sdev->num_cores; +} +EXPORT_SYMBOL_NS_GPL(sof_client_get_num_cores, "SND_SOC_SOF_CLIENT"); + +struct sof_ipc4_fw_data *sof_client_get_ipc4_fw_data(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + return sdev->private; +} +EXPORT_SYMBOL_NS_GPL(sof_client_get_ipc4_fw_data, "SND_SOC_SOF_CLIENT"); + +u32 sof_client_get_new_comp_id(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + return atomic_fetch_inc(&sdev->next_comp_id); +} +EXPORT_SYMBOL_NS_GPL(sof_client_get_new_comp_id, "SND_SOC_SOF_CLIENT"); + +int sof_client_machine_register(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + struct sof_audio_client_pdata *pdata = dev_get_platdata(&cdev->auxdev.dev); + struct snd_sof_pdata *spdata = sdev->pdata; + struct snd_soc_acpi_mach *mach; + struct platform_device *pdev; + + /* Per-client machine: register a dedicated machine device */ + if (pdata->machine.drv_name) { + mach = &pdata->machine; + mach->mach_params.platform = dev_name(&cdev->auxdev.dev); + + pdev = platform_device_register_data(sdev->dev, + mach->drv_name, + PLATFORM_DEVID_AUTO, + mach, sizeof(*mach)); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + + pdata->plat_drv.ignore_machine = dev_name(&pdev->dev); + cdev->data = pdev; + + return 0; + } + + /* + * Legacy: use shared machine from sdev. + * Update the platform name to match the audio component's device so + * that machine drivers can find the SOF platform component. + */ + mach = (struct snd_soc_acpi_mach *)spdata->machine; + if (mach) + mach->mach_params.platform = dev_name(&cdev->auxdev.dev); + + return snd_sof_machine_register(sdev, spdata); +} +EXPORT_SYMBOL_NS_GPL(sof_client_machine_register, "SND_SOC_SOF_CLIENT"); + +void sof_client_machine_unregister(struct sof_client_dev *cdev) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + struct platform_device *pdev = cdev->data; + + if (pdev) { + platform_device_unregister(pdev); + return; + } + + snd_sof_machine_unregister(sdev, sdev->pdata); +} +EXPORT_SYMBOL_NS_GPL(sof_client_machine_unregister, "SND_SOC_SOF_CLIENT"); + +void sof_audio_client_init_pdata(struct snd_sof_dev *sdev, + struct sof_audio_client_pdata *pdata) +{ + memset(pdata, 0, sizeof(*pdata)); + pdata->drv = sdev->audio_ops->drv; + pdata->num_drv = sdev->audio_ops->num_drv; +} +EXPORT_SYMBOL_NS_GPL(sof_audio_client_init_pdata, "SND_SOC_SOF_CLIENT"); + +int sof_register_audio_client(struct snd_sof_dev *sdev) +{ + struct sof_audio_client_pdata pdata; + + sof_audio_client_init_pdata(sdev, &pdata); + + return sof_client_dev_register(sdev, "audio", 0, + &pdata, sizeof(pdata)); +} +EXPORT_SYMBOL_NS_GPL(sof_register_audio_client, "SND_SOC_SOF_CLIENT"); + +void sof_unregister_audio_client(struct snd_sof_dev *sdev) +{ + sof_client_dev_unregister(sdev, "audio", 0); +} +EXPORT_SYMBOL_NS_GPL(sof_unregister_audio_client, "SND_SOC_SOF_CLIENT"); diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index e796801570c823..f6342f9a337c16 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -11,17 +11,25 @@ struct sof_ipc_fw_version; struct sof_ipc_cmd_hdr; struct snd_sof_dev; +struct snd_sof_pcm_stream; +struct snd_soc_acpi_mach; struct dentry; struct sof_ipc4_fw_module; +struct sof_ipc4_fw_data; +struct sof_client_ops; /** * struct sof_client_dev - SOF client device * @auxdev: auxiliary device + * @ops: client event callbacks, set via sof_client_register_ops() + * @ops_node: entry in the SOF core client ops list * @data: device specific data */ struct sof_client_dev { struct auxiliary_device auxdev; + const struct sof_client_ops *ops; + struct list_head ops_node; void *data; }; @@ -39,16 +47,34 @@ static inline int sof_client_ipc_tx_message_no_reply(struct sof_client_dev *cdev } int sof_client_ipc_set_get_data(struct sof_client_dev *cdev, void *ipc_msg, bool set); +int sof_client_ipc_msg_data(struct sof_client_dev *cdev, + struct snd_sof_pcm_stream *sps, void *p, size_t sz); struct sof_ipc4_fw_module *sof_client_ipc4_find_module(struct sof_client_dev *c, const guid_t *u); -struct snd_sof_widget *sof_client_ipc4_find_swidget_by_id(struct sof_client_dev *cdev, - u32 module_id, int instance_id); + +/* + * The name is copied out of the topology of the owning client, which can be + * removed as soon as the lookup returns. Widget names are bound by the + * topology ABI, a truncated name would only affect diagnostic output. + */ +#define SOF_CLIENT_MODULE_NAME_MAX SNDRV_CTL_ELEM_ID_NAME_MAXLEN + +int sof_client_ipc4_get_module_name(struct sof_client_dev *cdev, u32 module_id, + int instance_id, char *name, size_t size); struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev); struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev); const struct sof_ipc_fw_version *sof_client_get_fw_version(struct sof_client_dev *cdev); size_t sof_client_get_ipc_max_payload_size(struct sof_client_dev *cdev); enum sof_ipc_type sof_client_get_ipc_type(struct sof_client_dev *cdev); +const char *sof_client_get_topology_name(struct sof_client_dev *cdev); +const char *sof_client_get_topology_prefix(struct sof_client_dev *cdev); +bool sof_client_get_ssp_mclk_id_quirk(struct sof_client_dev *cdev, u16 *mclk_id); +const struct snd_soc_acpi_mach *sof_client_get_machine(struct sof_client_dev *cdev); +const char *sof_client_get_machine_drv_name(struct sof_client_dev *cdev); +bool sof_client_is_function_topology_disabled(struct sof_client_dev *cdev); +bool sof_client_is_suspend_target_s0ix(struct sof_client_dev *cdev); +bool sof_client_is_dsp_in_d0(struct sof_client_dev *cdev); /* DSP/firmware boot request */ int sof_client_boot_dsp(struct sof_client_dev *cdev); @@ -60,19 +86,86 @@ void sof_client_core_module_put(struct sof_client_dev *cdev); /* IPC notification */ typedef void (*sof_client_event_callback)(struct sof_client_dev *cdev, void *msg_buf); -int sof_client_register_ipc_rx_handler(struct sof_client_dev *cdev, - u32 ipc_msg_type, - sof_client_event_callback callback); -void sof_client_unregister_ipc_rx_handler(struct sof_client_dev *cdev, - u32 ipc_msg_type); - -/* DSP state notification and query */ +/* DSP state notification */ typedef void (*sof_client_fw_state_callback)(struct sof_client_dev *cdev, enum sof_fw_state state); -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_d0i3_vote - a client's vote on DSP D0i3 low-power entry + * + * A client reports whether its currently active streams permit the DSP to + * enter the low-power D0i3 substate. The values are ordered by precedence, the + * core folds the votes of all clients that implement the d0i3_vote callback by + * taking the highest one. Clients without the callback do not participate. + */ +enum sof_d0i3_vote { + SOF_D0I3_NO_ACTIVITY = 0, /* no active stream; does not affect the vote */ + SOF_D0I3_COMPATIBLE_ACTIVE, /* active streams, all D0i3-compatible */ + SOF_D0I3_COMPATIBLE_PLAYBACK, /* the above and at least one is playback */ + SOF_D0I3_INCOMPATIBLE, /* an active stream requires the DSP in D0i0 */ +}; + +typedef enum sof_d0i3_vote (*sof_client_d0i3_vote_callback)(struct sof_client_dev *cdev); + +/* IPC4 module instance name lookup */ +typedef int (*sof_client_module_name_callback)(struct sof_client_dev *cdev, + u32 module_id, int instance_id, + char *name, size_t size); + +typedef int (*sof_client_fw_booted_callback)(struct sof_client_dev *cdev); + +typedef bool (*sof_client_keep_dsp_in_d0_callback)(struct sof_client_dev *cdev); + +typedef bool (*sof_client_period_elapsed_callback)(struct sof_client_dev *cdev, + struct snd_pcm_substream *substream); + +/** + * struct sof_client_ops - SOF client event callbacks + * @ipc_rx_handler: Called for every DSP-initiated IPC notification. The + * client is responsible for filtering the message types it + * is interested in. + * @fw_state_handler: Called on every DSP firmware state change. + * @fw_booted: Called after the DSP firmware has been (re)booted and + * before the firmware context is restored. Nothing of the + * state the client had in the DSP survived the boot. A + * non zero return aborts the boot. + * @keep_dsp_in_d0: Returns true if the client left something running in + * the DSP on purpose which would not survive powering it + * down. The DSP is kept in D0 over the system suspend if + * any client asks for it. + * @d0i3_vote: Returns the client's D0i3 compatibility vote, see + * enum sof_d0i3_vote. + * @period_elapsed: Called when the platform completed a period of a DMA + * stream. Returns true if the substream belongs to the + * client, which ends the search. Unlike the other + * callbacks this one is invoked from atomic context and + * must not sleep. + * @get_module_name: Copies the topology name of a module instance owned by + * the client. Returns -ENOENT if the client does not own + * the module. + * + * A client that wants to receive notifications registers a static instance of + * this structure with sof_client_register_ops() from its probe and drops it + * with sof_client_unregister_ops() from its remove. All callbacks are optional + * and are invoked for every client that registered ops. + * + * The callbacks are invoked from an SRCU read side critical section, so they + * may sleep and may send IPC messages to the DSP, unless noted otherwise. + */ +struct sof_client_ops { + sof_client_event_callback ipc_rx_handler; + sof_client_fw_state_callback fw_state_handler; + sof_client_fw_booted_callback fw_booted; + sof_client_keep_dsp_in_d0_callback keep_dsp_in_d0; + sof_client_d0i3_vote_callback d0i3_vote; + sof_client_period_elapsed_callback period_elapsed; + sof_client_module_name_callback get_module_name; +}; + +int sof_client_register_ops(struct sof_client_dev *cdev, + const struct sof_client_ops *ops); +void sof_client_unregister_ops(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); @@ -80,8 +173,28 @@ void sof_client_mailbox_read(struct sof_client_dev *cdev, u32 offset, void *message, size_t bytes); void sof_client_mailbox_write(struct sof_client_dev *cdev, u32 offset, void *message, size_t bytes); +struct snd_sof_mailbox *sof_client_get_mailbox(struct sof_client_dev *cdev, + enum snd_sof_mailbox_type type); ssize_t sof_client_ipc4_find_debug_slot_offset_by_type(struct sof_client_dev *cdev, u32 type); +bool sof_client_is_dspless(struct sof_client_dev *cdev); +int sof_client_get_num_cores(struct sof_client_dev *cdev); +struct sof_ipc4_fw_data *sof_client_get_ipc4_fw_data(struct sof_client_dev *cdev); +u32 sof_client_get_new_comp_id(struct sof_client_dev *cdev); + +/* machine driver registration */ +int sof_client_machine_register(struct sof_client_dev *cdev); +void sof_client_machine_unregister(struct sof_client_dev *cdev); + +/* audio client pdata initialization */ +struct sof_audio_client_pdata; +void sof_audio_client_init_pdata(struct snd_sof_dev *sdev, + struct sof_audio_client_pdata *pdata); + +/* default audio client registration for vendor ops */ +int sof_register_audio_client(struct snd_sof_dev *sdev); +void sof_unregister_audio_client(struct snd_sof_dev *sdev); + #endif /* __SOC_SOF_CLIENT_H */ diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 83e4d83cc27cbb..d31d521ac9bc2e 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -12,6 +12,7 @@ #define __SOUND_SOC_SOF_PRIV_H #include +#include #include #include #include @@ -20,6 +21,7 @@ #include #include #include +#include "sof-client.h" struct snd_sof_pcm_stream; @@ -126,7 +128,104 @@ struct snd_sof_ipc; struct snd_sof_debugfs_map; struct snd_soc_tplg_ops; struct snd_soc_component; +struct snd_sof_pcm; struct snd_sof_pdata; +struct snd_sof_platform_stream_params; + +/* + * SOF platform-specific audio operations. + * + * These callbacks handle platform-specific stream management (host DMA, + * stream allocation, etc.) and are provided per audio instance. + * All callbacks receive snd_soc_component to identify the instance, + * platform code internally resolves to the hardware device as needed. + */ +struct sof_audio_ops { + /* host stream management */ + int (*pcm_open)(struct snd_soc_component *component, + struct snd_sof_pcm *spcm, + struct snd_pcm_substream *substream); + int (*pcm_close)(struct snd_soc_component *component, + struct snd_pcm_substream *substream); + int (*pcm_hw_params)(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_sof_platform_stream_params *platform_params); + int (*pcm_hw_free)(struct snd_soc_component *component, + struct snd_pcm_substream *substream); + int (*pcm_trigger)(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd); + snd_pcm_uframes_t (*pcm_pointer)(struct snd_soc_component *component, + struct snd_sof_pcm *spcm, + struct snd_pcm_substream *substream); + int (*pcm_ack)(struct snd_soc_component *component, + struct snd_pcm_substream *substream); + + /* compressed streams */ + int (*compr_open)(struct snd_soc_component *component, + struct snd_compr_stream *cstream); + int (*compr_close)(struct snd_soc_component *component, + struct snd_compr_stream *cstream); + int (*compr_hw_params)(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + struct snd_compr_params *params, + struct snd_sof_platform_stream_params *platform_params); + int (*compr_hw_free)(struct snd_soc_component *component, + struct snd_compr_stream *cstream); + int (*compr_trigger)(struct snd_soc_component *component, + struct snd_compr_stream *cstream, int cmd); + int (*compr_pointer)(struct snd_soc_component *component, + struct snd_compr_stream *cstream, + struct snd_compr_tstamp64 *tstamp); + u64 (*compr_get_dai_frame_counter)(struct snd_soc_component *component, + struct snd_compr_stream *cstream); + + /* + * Optional callback to retrieve the number of frames left/arrived + * from/to the DSP on the DAI side (link/codec/DMIC/etc). + * + * The callback is used when the firmware does not provide this + * information via the shared SRAM window and it can be retrieved by + * host. + */ + u64 (*get_dai_frame_counter)(struct snd_soc_component *component, + struct snd_pcm_substream *substream); + + /* + * Optional callback to retrieve the number of bytes left/arrived + * from/to the DSP on the host side (bytes between host ALSA buffer and + * DSP). + * + * The callback is needed for ALSA delay reporting. + */ + u64 (*get_host_byte_counter)(struct snd_soc_component *component, + struct snd_pcm_substream *substream); + + /* DAI drivers */ + struct snd_soc_dai_driver *drv; + int num_drv; + + /* ALSA HW info flags, will be stored in snd_pcm_runtime.hw.info */ + u32 hw_info; +}; + +/* SOF audio instance container */ +struct snd_sof_audio_instance { + struct snd_sof_dev *sdev; + struct snd_soc_component *component; + const struct sof_audio_ops *audio_ops; + const struct sof_ipc_tplg_ops *tplg_ops; + const struct sof_ipc_pcm_ops *pcm_ops; + struct list_head pipeline_list; + struct list_head dai_list; + struct list_head dai_link_list; + struct list_head route_list; + struct list_head pcm_list; + struct list_head kcontrol_list; + struct list_head widget_list; + bool led_present; + bool pipelines_set_up; +}; /** * struct snd_sof_platform_stream_params - platform dependent stream parameters @@ -231,68 +330,6 @@ struct snd_sof_dsp_ops { int (*load_module)(struct snd_sof_dev *sof_dev, struct snd_sof_mod_hdr *hdr); /* optional */ - /* connect pcm substream to a host stream */ - int (*pcm_open)(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream); /* optional */ - /* disconnect pcm substream to a host stream */ - int (*pcm_close)(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream); /* optional */ - - /* host stream hw params */ - int (*pcm_hw_params)(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_sof_platform_stream_params *platform_params); /* optional */ - - /* host stream hw_free */ - int (*pcm_hw_free)(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream); /* optional */ - - /* host stream trigger */ - int (*pcm_trigger)(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream, - int cmd); /* optional */ - - /* host stream pointer */ - snd_pcm_uframes_t (*pcm_pointer)(struct snd_sof_dev *sdev, - struct snd_pcm_substream *substream); /* optional */ - - /* pcm ack */ - int (*pcm_ack)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); /* optional */ - - int (*compr_open)(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream); - int (*compr_close)(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream); - int (*compr_hw_params)(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream, - struct snd_compr_params *params, - struct snd_sof_platform_stream_params *platform_params); - int (*compr_hw_free)(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream); - int (*compr_trigger)(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream, - int cmd); - int (*compr_pointer)(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream, - struct snd_compr_tstamp64 *tstamp); - u64 (*compr_get_dai_frame_counter)(struct snd_sof_dev *sdev, - struct snd_compr_stream *cstream); - /* - * optional callback to retrieve the number of frames left/arrived from/to - * the DSP on the DAI side (link/codec/DMIC/etc). - * - * The callback is used when the firmware does not provide this information - * via the shared SRAM window and it can be retrieved by host. - */ - u64 (*get_dai_frame_counter)(struct snd_sof_dev *sdev, - struct snd_soc_component *component, - struct snd_pcm_substream *substream); /* optional */ - - /* - * Optional callback to retrieve the number of bytes left/arrived from/to - * the DSP on the host side (bytes between host ALSA buffer and DSP). - * - * The callback is needed for ALSA delay reporting. - */ - u64 (*get_host_byte_counter)(struct snd_sof_dev *sdev, - struct snd_soc_component *component, - struct snd_pcm_substream *substream); /* optional */ - /* host read DSP stream data */ int (*ipc_msg_data)(struct snd_sof_dev *sdev, struct snd_sof_pcm_stream *sps, @@ -364,15 +401,13 @@ struct snd_sof_dsp_ops { int (*register_ipc_clients)(struct snd_sof_dev *sdev); /* optional */ void (*unregister_ipc_clients)(struct snd_sof_dev *sdev); /* optional */ - /* DAI ops */ - struct snd_soc_dai_driver *drv; - int num_drv; + /* audio client ops */ + int (*register_audio_client)(struct snd_sof_dev *sdev); /* mandatory */ + void (*unregister_audio_client)(struct snd_sof_dev *sdev); /* mandatory */ + /* DAI ops */ bool (*is_chain_dma_supported)(struct snd_sof_dev *sdev, u32 dai_type); /* optional */ - /* ALSA HW info flags, will be stored in snd_pcm_runtime.hw.info */ - u32 hw_info; - const struct dsp_arch_ops *dsp_arch_ops; }; @@ -420,12 +455,6 @@ struct snd_sof_debugfs_map { enum sof_debugfs_access_type access_type; }; -/* mailbox descriptor, used for host <-> DSP IPC */ -struct snd_sof_mailbox { - size_t size; - u32 offset; -}; - /* IPC message descriptor for host <-> DSP IO */ struct snd_sof_ipc_msg { /* message data */ @@ -493,9 +522,7 @@ struct sof_ipc_pcm_ops; /** * struct sof_ipc_ops - IPC-specific ops - * @tplg: Pointer to IPC-specific topology ops * @pm: Pointer to PM ops - * @pcm: Pointer to PCM ops * @fw_loader: Pointer to Firmware Loader ops * @fw_tracing: Optional pointer to Firmware tracing ops * @@ -519,9 +546,7 @@ struct sof_ipc_pcm_ops; * handled with multiple chunks. */ struct sof_ipc_ops { - const struct sof_ipc_tplg_ops *tplg; const struct sof_ipc_pm_ops *pm; - const struct sof_ipc_pcm_ops *pcm; const struct sof_ipc_fw_loader_ops *fw_loader; const struct sof_ipc_fw_tracing_ops *fw_tracing; @@ -587,12 +612,6 @@ struct snd_sof_dev { /* Main, Base firmware image */ struct sof_firmware basefw; - /* - * ASoC components. plat_drv fields are set dynamically so - * can't use const - */ - struct snd_soc_component_driver plat_drv; - /* current DSP power state */ struct sof_dsp_power_state dsp_power_state; /* mutex to protect the dsp_power_state access */ @@ -624,7 +643,7 @@ struct snd_sof_dev { struct snd_sof_mailbox debug_box; /* Debug info updates */ struct snd_sof_ipc_msg *msg; int ipc_irq; - u32 next_comp_id; /* monotonic - reset during S3 */ + atomic_t next_comp_id; /* monotonic comp_id allocator */ /* memory bases for mmaped DSPs - set by dsp_init() */ void __iomem *bar[SND_SOF_BARS]; /* DSP base address */ @@ -646,16 +665,8 @@ struct snd_sof_dev { /* topology */ struct snd_soc_tplg_ops *tplg_ops; - struct list_head pcm_list; - struct list_head kcontrol_list; - struct list_head widget_list; - struct list_head pipeline_list; - struct list_head dai_list; - struct list_head dai_link_list; - struct list_head route_list; - struct snd_soc_component *component; + const struct sof_audio_ops *audio_ops; u32 enabled_cores_mask; /* keep track of enabled cores */ - bool led_present; /* FW configuration */ struct sof_ipc_window *info_window; @@ -691,20 +702,20 @@ struct snd_sof_dev { /* mutex to protect client list */ struct mutex ipc_client_mutex; - /* - * Used for tracking the IPC client's RX registration for DSP initiated - * message handling. - */ - struct list_head ipc_rx_handler_list; + /* list of clients that registered event ops via sof_client_register_ops() */ + struct list_head client_ops_list; + + /* serializes the writers of client_ops_list */ + struct mutex client_ops_mutex; /* - * Used for tracking the IPC client's registration for DSP state change - * notification + * Readers of client_ops_list. The callbacks are dispatched from within + * the read side critical section and are allowed to sleep, so they must + * not be serialized by client_ops_mutex: a client callback waiting for + * an IPC reply would block the IPC IRQ thread in the notification + * dispatcher and the reply could never be delivered. */ - struct list_head fw_state_handler_list; - - /* to protect the ipc_rx_handler_list and dsp_state_handler_list list */ - struct mutex client_event_handler_mutex; + struct srcu_struct client_ops_srcu; /* quirks to override topology values */ bool mclk_id_override; @@ -735,8 +746,6 @@ int snd_sof_prepare(struct device *dev); void snd_sof_complete(struct device *dev); int snd_sof_boot_dsp_firmware(struct snd_sof_dev *sdev); -void snd_sof_new_platform_drv(struct snd_sof_dev *sdev); - /* * Compress support */ @@ -863,11 +872,15 @@ int sof_set_stream_data_offset(struct snd_sof_dev *sdev, struct snd_sof_pcm_stream *sps, size_t posn_offset); -int sof_stream_pcm_open(struct snd_sof_dev *sdev, +int sof_stream_pcm_open(struct snd_soc_component *component, + struct snd_sof_pcm *spcm, struct snd_pcm_substream *substream); -int sof_stream_pcm_close(struct snd_sof_dev *sdev, +int sof_stream_pcm_close(struct snd_soc_component *component, struct snd_pcm_substream *substream); +bool snd_sof_dsp_state_is_d0i3_compatible(struct snd_sof_dev *sdev); +bool snd_sof_dsp_state_is_d0i3_streaming(struct snd_sof_dev *sdev); + /* SOF client support */ struct sof_client_dev; @@ -881,6 +894,11 @@ int sof_register_clients(struct snd_sof_dev *sdev); void sof_unregister_clients(struct snd_sof_dev *sdev); void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *msg_buf); void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev); +int sof_client_fw_booted_dispatcher(struct snd_sof_dev *sdev); +bool sof_client_keep_dsp_in_d0(struct snd_sof_dev *sdev); +enum sof_d0i3_vote sof_client_get_d0i3_vote(struct snd_sof_dev *sdev); +void sof_client_period_elapsed(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream); int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state); int sof_resume_clients(struct snd_sof_dev *sdev); #else /* CONFIG_SND_SOC_SOF_CLIENT */ @@ -917,6 +935,26 @@ static inline void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev) { } +static inline int sof_client_fw_booted_dispatcher(struct snd_sof_dev *sdev) +{ + return 0; +} + +static inline bool sof_client_keep_dsp_in_d0(struct snd_sof_dev *sdev) +{ + return false; +} + +static inline enum sof_d0i3_vote sof_client_get_d0i3_vote(struct snd_sof_dev *sdev) +{ + return SOF_D0I3_NO_ACTIVITY; +} + +static inline void sof_client_period_elapsed(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) +{ +} + static inline int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state) { return 0; diff --git a/sound/soc/sof/stream-ipc.c b/sound/soc/sof/stream-ipc.c index 78758cf58f0142..59900fba4f5f27 100644 --- a/sound/soc/sof/stream-ipc.c +++ b/sound/soc/sof/stream-ipc.c @@ -96,7 +96,8 @@ int sof_set_stream_data_offset(struct snd_sof_dev *sdev, } EXPORT_SYMBOL(sof_set_stream_data_offset); -int sof_stream_pcm_open(struct snd_sof_dev *sdev, +int sof_stream_pcm_open(struct snd_soc_component *component, + struct snd_sof_pcm *spcm, struct snd_pcm_substream *substream) { struct sof_stream *stream = kmalloc_obj(*stream); @@ -118,7 +119,7 @@ int sof_stream_pcm_open(struct snd_sof_dev *sdev, } EXPORT_SYMBOL(sof_stream_pcm_open); -int sof_stream_pcm_close(struct snd_sof_dev *sdev, +int sof_stream_pcm_close(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct sof_stream *stream = substream->runtime->private_data; diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 666d8f50313feb..5454d69ea7ed30 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -17,6 +17,8 @@ #include #include "sof-priv.h" #include "sof-audio.h" +#include "sof-client.h" +#include "sof-client-audio.h" #include "ops.h" static bool disable_function_topology; @@ -64,8 +66,7 @@ int sof_update_ipc_object(struct snd_soc_component *scomp, void *object, enum so struct snd_sof_tuple *tuples, int num_tuples, size_t object_size, int token_instance_num) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_component_get_tplg_ops(scomp); const struct sof_token_info *token_list; const struct sof_topology_token *tokens; int i, j; @@ -278,8 +279,7 @@ static int set_up_volume_table(struct snd_sof_control *scontrol, int tlv[SOF_TLV_ITEMS], int size) { struct snd_soc_component *scomp = scontrol->scomp; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_component_get_tplg_ops(scomp); if (tplg_ops && tplg_ops->control && tplg_ops->control->set_up_volume_table) return tplg_ops->control->set_up_volume_table(scontrol, tlv, size); @@ -496,7 +496,7 @@ static int sof_parse_uuid_tokens(struct snd_soc_component *scomp, /** * sof_copy_tuples - Parse tokens and copy them to the @tuples array - * @sdev: pointer to struct snd_sof_dev + * @scomp: pointer to struct snd_soc_component * @array: source pointer to consecutive vendor arrays in topology * @array_size: size of @array * @token_id: Token ID associated with a token array @@ -508,11 +508,12 @@ static int sof_parse_uuid_tokens(struct snd_soc_component *scomp, * @num_copied_tuples: pointer to the number of copied tuples in the tuples array * */ -static int sof_copy_tuples(struct snd_sof_dev *sdev, struct snd_soc_tplg_vendor_array *array, +static int sof_copy_tuples(struct snd_soc_component *scomp, + struct snd_soc_tplg_vendor_array *array, int array_size, u32 token_id, int token_instance_num, struct snd_sof_tuple *tuples, int tuples_size, int *num_copied_tuples) { - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_component_get_tplg_ops(scomp); const struct sof_token_info *token_list; const struct sof_topology_token *tokens; int found = 0; @@ -525,7 +526,7 @@ static int sof_copy_tuples(struct snd_sof_dev *sdev, struct snd_soc_tplg_vendor_ return 0; if (!tuples || !num_copied_tuples) { - dev_err(sdev->dev, "Invalid tuples array\n"); + dev_err(scomp->dev, "Invalid tuples array\n"); return -EINVAL; } @@ -533,13 +534,13 @@ static int sof_copy_tuples(struct snd_sof_dev *sdev, struct snd_soc_tplg_vendor_ num_tokens = token_list[token_id].count; if (!tokens) { - dev_err(sdev->dev, "No token array defined for token ID: %d\n", token_id); + dev_err(scomp->dev, "No token array defined for token ID: %d\n", token_id); return -EINVAL; } /* check if there's space in the tuples array for new tokens */ if (*num_copied_tuples >= tuples_size) { - dev_err(sdev->dev, "No space in tuples array for new tokens from %s", + dev_err(scomp->dev, "No space in tuples array for new tokens from %s", token_list[token_id].name); return -EINVAL; } @@ -549,14 +550,14 @@ static int sof_copy_tuples(struct snd_sof_dev *sdev, struct snd_soc_tplg_vendor_ /* validate asize */ if (asize < 0) { - dev_err(sdev->dev, "Invalid array size 0x%x\n", asize); + dev_err(scomp->dev, "Invalid array size 0x%x\n", asize); return -EINVAL; } /* make sure there is enough data before parsing */ array_size -= asize; if (array_size < 0) { - dev_err(sdev->dev, "Invalid array size 0x%x\n", asize); + dev_err(scomp->dev, "Invalid array size 0x%x\n", asize); return -EINVAL; } @@ -583,7 +584,7 @@ static int sof_copy_tuples(struct snd_sof_dev *sdev, struct snd_soc_tplg_vendor_ tuples[*num_copied_tuples].token = tokens[j].token; tuples[*num_copied_tuples].value.s = - devm_kasprintf(sdev->dev, GFP_KERNEL, + devm_kasprintf(scomp->dev, GFP_KERNEL, "%s", elem->string); if (!tuples[*num_copied_tuples].value.s) return -ENOMEM; @@ -842,7 +843,7 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, struct snd_kcontrol_new *kc, struct snd_soc_tplg_ctl_hdr *hdr) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct snd_soc_tplg_mixer_control *mc = container_of(hdr, struct snd_soc_tplg_mixer_control, hdr); int tlv[SOF_TLV_ITEMS]; @@ -862,7 +863,6 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, if (le32_to_cpu(mc->num_channels) > 2) kc->info = snd_sof_volume_info; - scontrol->comp_id = sdev->next_comp_id; scontrol->min_volume_step = le32_to_cpu(mc->min); scontrol->max_volume_step = le32_to_cpu(mc->max); scontrol->num_channels = le32_to_cpu(mc->num_channels); @@ -902,11 +902,11 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, scontrol->access |= mask; kc->access &= ~SNDRV_CTL_ELEM_ACCESS_LED_MASK; kc->access |= mask; - sdev->led_present = true; + instance->led_present = true; } dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d\n", - scontrol->comp_id, scontrol->num_channels); + scontrol->index, scontrol->num_channels); return 0; @@ -922,7 +922,6 @@ static int sof_control_load_enum(struct snd_soc_component *scomp, struct snd_kcontrol_new *kc, struct snd_soc_tplg_ctl_hdr *hdr) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_enum_control *ec = container_of(hdr, struct snd_soc_tplg_enum_control, hdr); @@ -930,11 +929,10 @@ static int sof_control_load_enum(struct snd_soc_component *scomp, if (le32_to_cpu(ec->num_channels) > SND_SOC_TPLG_MAX_CHAN) return -EINVAL; - scontrol->comp_id = sdev->next_comp_id; scontrol->num_channels = le32_to_cpu(ec->num_channels); - dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d comp_id %d\n", - scontrol->comp_id, scontrol->num_channels, scontrol->comp_id); + dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d\n", + scontrol->index, scontrol->num_channels); return 0; } @@ -944,16 +942,14 @@ static int sof_control_load_bytes(struct snd_soc_component *scomp, struct snd_kcontrol_new *kc, struct snd_soc_tplg_ctl_hdr *hdr) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_bytes_control *control = container_of(hdr, struct snd_soc_tplg_bytes_control, hdr); struct soc_bytes_ext *sbe = (struct soc_bytes_ext *)kc->private_value; size_t priv_size = le32_to_cpu(control->priv.size); scontrol->max_size = sbe->max; - scontrol->comp_id = sdev->next_comp_id; - dev_dbg(scomp->dev, "tplg: load kcontrol index %d\n", scontrol->comp_id); + dev_dbg(scomp->dev, "tplg: load kcontrol index %d\n", scontrol->index); /* copy the private data */ if (priv_size > 0) { @@ -975,7 +971,7 @@ static int sof_control_load(struct snd_soc_component *scomp, int index, struct soc_mixer_control *sm; struct soc_bytes_ext *sbe; struct soc_enum *se; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct snd_soc_dobj *dobj; struct snd_sof_control *scontrol; int ret; @@ -1043,15 +1039,15 @@ static int sof_control_load(struct snd_soc_component *scomp, int index, scontrol->led_ctl.led_value = -1; dobj->private = scontrol; - list_add(&scontrol->list, &sdev->kcontrol_list); + list_add(&scontrol->list, &instance->kcontrol_list); return 0; } static int sof_control_unload(struct snd_soc_component *scomp, struct snd_soc_dobj *dobj) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_component_get_tplg_ops(scomp); struct snd_sof_control *scontrol = dobj->private; int ret = 0; @@ -1185,7 +1181,7 @@ static void sof_disconnect_dai_widget(struct snd_soc_component *scomp, static int spcm_bind(struct snd_soc_component *scomp, struct snd_sof_pcm *spcm, int dir) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct snd_sof_widget *host_widget; if (sdev->dspless_mode_selected) @@ -1223,8 +1219,7 @@ static int sof_widget_parse_tokens(struct snd_soc_component *scomp, struct snd_s struct snd_soc_tplg_dapm_widget *tw, enum sof_tokens *object_token_list, int count) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_component_get_tplg_ops(scomp); struct snd_soc_tplg_private *private = &tw->priv; const struct sof_token_info *token_list; int num_tuples = 0; @@ -1279,7 +1274,7 @@ static int sof_widget_parse_tokens(struct snd_soc_component *scomp, struct snd_s num_sets = sof_get_token_value(SOF_TKN_COMP_NUM_INPUT_AUDIO_FORMATS, swidget->tuples, swidget->num_tuples); if (num_sets < 0) { - dev_err(sdev->dev, "Invalid input audio format count for %s\n", + dev_err(scomp->dev, "Invalid input audio format count for %s\n", swidget->widget->name); ret = num_sets; goto err; @@ -1289,7 +1284,7 @@ static int sof_widget_parse_tokens(struct snd_soc_component *scomp, struct snd_s num_sets = sof_get_token_value(SOF_TKN_COMP_NUM_OUTPUT_AUDIO_FORMATS, swidget->tuples, swidget->num_tuples); if (num_sets < 0) { - dev_err(sdev->dev, "Invalid output audio format count for %s\n", + dev_err(scomp->dev, "Invalid output audio format count for %s\n", swidget->widget->name); ret = num_sets; goto err; @@ -1314,7 +1309,7 @@ static int sof_widget_parse_tokens(struct snd_soc_component *scomp, struct snd_s } /* copy one set of tuples per token ID into swidget->tuples */ - ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size), + ret = sof_copy_tuples(scomp, private->array, le32_to_cpu(private->size), object_token_list[i], num_sets, swidget->tuples, num_tuples, &swidget->num_tuples); if (ret < 0) { @@ -1417,13 +1412,54 @@ static const struct sof_topology_token dapm_widget_tokens[] = { get_w_no_wname_in_long_name, 0} }; +/* + * The kcontrols of a widget are loaded before the widget itself, so the + * comp_id of the widget they belong to is not yet known at control load time. + * Assign the widget's comp_id to each of its kcontrols once the widget has + * been created. + */ +static void sof_widget_update_kcontrols_comp_id(struct snd_sof_widget *swidget) +{ + struct snd_soc_dapm_widget *widget = swidget->widget; + const struct snd_kcontrol_new *kc; + struct snd_sof_control *scontrol; + struct soc_mixer_control *sm; + struct soc_bytes_ext *sbe; + struct soc_enum *se; + int i; + + for (i = 0; i < widget->num_kcontrols; i++) { + kc = &widget->kcontrol_news[i]; + switch (widget->dobj.widget.kcontrol_type[i]) { + case SND_SOC_TPLG_TYPE_MIXER: + sm = (struct soc_mixer_control *)kc->private_value; + scontrol = sm->dobj.private; + break; + case SND_SOC_TPLG_TYPE_ENUM: + se = (struct soc_enum *)kc->private_value; + scontrol = se->dobj.private; + break; + case SND_SOC_TPLG_TYPE_BYTES: + sbe = (struct soc_bytes_ext *)kc->private_value; + scontrol = sbe->dobj.private; + break; + default: + continue; + } + + if (scontrol) + scontrol->comp_id = swidget->comp_id; + } +} + /* external widget init - used for any driver specific init */ static int sof_widget_ready(struct snd_soc_component *scomp, int index, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tw) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_component_get_tplg_ops(scomp); const struct sof_ipc_tplg_widget_ops *widget_ops; struct snd_soc_tplg_private *priv = &tw->priv; enum sof_tokens *token_list = NULL; @@ -1432,13 +1468,16 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, int token_list_size = 0; int ret = 0; + if (!instance) + return -EINVAL; + swidget = kzalloc_obj(*swidget); if (!swidget) return -ENOMEM; swidget->scomp = scomp; swidget->widget = w; - swidget->comp_id = sdev->next_comp_id++; + swidget->comp_id = atomic_fetch_inc(&sdev->next_comp_id); swidget->id = w->id; swidget->pipeline_id = index; swidget->private = NULL; @@ -1447,6 +1486,9 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, ida_init(&swidget->output_queue_ida); ida_init(&swidget->input_queue_ida); + /* assign the widget's comp_id to the kcontrols that belong to it */ + sof_widget_update_kcontrols_comp_id(swidget); + ret = sof_parse_tokens(scomp, w, dapm_widget_tokens, ARRAY_SIZE(dapm_widget_tokens), priv->array, le32_to_cpu(priv->size)); if (ret < 0) { @@ -1521,7 +1563,7 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, kfree(dai); break; } - list_add(&dai->list, &sdev->dai_list); + list_add(&dai->list, &instance->dai_list); swidget->private = dai; break; case snd_soc_dapm_decoder: @@ -1615,11 +1657,11 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, spipe->pipe_widget = swidget; swidget->spipe = spipe; - list_add(&spipe->list, &sdev->pipeline_list); + list_add(&spipe->list, &instance->pipeline_list); } w->dobj.private = swidget; - list_add(&swidget->list, &sdev->widget_list); + list_add(&swidget->list, &instance->widget_list); return ret; free: kfree(swidget->private); @@ -1649,8 +1691,7 @@ static int sof_route_unload(struct snd_soc_component *scomp, static int sof_widget_unload(struct snd_soc_component *scomp, struct snd_soc_dobj *dobj) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_component_get_tplg_ops(scomp); const struct sof_ipc_tplg_widget_ops *widget_ops; const struct snd_kcontrol_new *kc; struct snd_soc_dapm_widget *widget; @@ -1748,8 +1789,9 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index, struct snd_soc_dai_driver *dai_drv, struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_pcm_ops *ipc_pcm_ops = sof_ipc_get_ops(sdev, pcm); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); + const struct sof_ipc_pcm_ops *ipc_pcm_ops = snd_sof_component_get_pcm_ops(scomp); struct snd_soc_tplg_stream_caps *caps; struct snd_soc_tplg_private *private = &pcm->priv; struct snd_sof_pcm *spcm; @@ -1787,7 +1829,7 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index, } dai_drv->dobj.private = spcm; - list_add(&spcm->list, &sdev->pcm_list); + list_add(&spcm->list, &instance->pcm_list); ret = sof_parse_tokens(scomp, spcm, stream_tokens, ARRAY_SIZE(stream_tokens), private->array, @@ -1863,8 +1905,8 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index, static int sof_dai_unload(struct snd_soc_component *scomp, struct snd_soc_dobj *dobj) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_pcm_ops *ipc_pcm_ops = sof_ipc_get_ops(sdev, pcm); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); + const struct sof_ipc_pcm_ops *ipc_pcm_ops = snd_sof_component_get_pcm_ops(scomp); struct snd_sof_pcm *spcm = dobj->private; /* free PCM DMA pages */ @@ -1894,8 +1936,9 @@ static const struct sof_topology_token common_dai_link_tokens[] = { static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_soc_dai_link *link, struct snd_soc_tplg_link_config *cfg) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_component_get_tplg_ops(scomp); struct snd_soc_tplg_private *private = &cfg->priv; const struct sof_token_info *token_list; struct snd_sof_dai_link *slink; @@ -2021,7 +2064,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ if (token_list[SOF_DAI_LINK_TOKENS].tokens) { /* parse one set of DAI link tokens */ - ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size), + ret = sof_copy_tuples(scomp, private->array, le32_to_cpu(private->size), SOF_DAI_LINK_TOKENS, 1, slink->tuples, num_tuples, &slink->num_tuples); if (ret < 0) { @@ -2036,7 +2079,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ goto out; /* parse "num_sets" sets of DAI-specific tokens */ - ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size), + ret = sof_copy_tuples(scomp, private->array, le32_to_cpu(private->size), token_id, num_sets, slink->tuples, num_tuples, &slink->num_tuples); if (ret < 0) { dev_err(scomp->dev, "failed to parse %s for dai link %s\n", @@ -2050,12 +2093,12 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ slink->tuples, slink->num_tuples); if (num_sets < 0) { - dev_err(sdev->dev, "Invalid active PDM count for %s\n", link->name); + dev_err(scomp->dev, "Invalid active PDM count for %s\n", link->name); ret = num_sets; goto err; } - ret = sof_copy_tuples(sdev, private->array, le32_to_cpu(private->size), + ret = sof_copy_tuples(scomp, private->array, le32_to_cpu(private->size), SOF_DMIC_PDM_TOKENS, num_sets, slink->tuples, num_tuples, &slink->num_tuples); if (ret < 0) { @@ -2066,7 +2109,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ } out: link->dobj.private = slink; - list_add(&slink->list, &sdev->dai_link_list); + list_add(&slink->list, &instance->dai_link_list); return 0; @@ -2099,7 +2142,7 @@ static int sof_link_unload(struct snd_soc_component *scomp, struct snd_soc_dobj static int sof_route_load(struct snd_soc_component *scomp, int index, struct snd_soc_dapm_route *route) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct snd_sof_widget *source_swidget, *sink_swidget; struct snd_soc_dobj *dobj = &route->dobj; struct snd_sof_route *sroute; @@ -2157,7 +2200,7 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, sroute->sink_widget = sink_swidget; /* add route to route list */ - list_add(&sroute->list, &sdev->route_list); + list_add(&sroute->list, &instance->route_list); return 0; err: @@ -2167,7 +2210,6 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, /** * sof_set_widget_pipeline - Set pipeline for a component - * @sdev: pointer to struct snd_sof_dev * @spipe: pointer to struct snd_sof_pipeline * @swidget: pointer to struct snd_sof_widget that has the same pipeline ID as @pipe_widget * @@ -2175,18 +2217,20 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, * The function checks if @swidget is associated with any volatile controls. If so, setting * the dynamic_pipeline_widget is disallowed. */ -static int sof_set_widget_pipeline(struct snd_sof_dev *sdev, struct snd_sof_pipeline *spipe, +static int sof_set_widget_pipeline(struct snd_sof_pipeline *spipe, struct snd_sof_widget *swidget) { + struct snd_sof_audio_instance *instance = + snd_sof_component_get_audio_instance(swidget->scomp); struct snd_sof_widget *pipe_widget = spipe->pipe_widget; struct snd_sof_control *scontrol; if (pipe_widget->dynamic_pipeline_widget) { /* dynamic widgets cannot have volatile kcontrols */ - list_for_each_entry(scontrol, &sdev->kcontrol_list, list) + list_for_each_entry(scontrol, &instance->kcontrol_list, list) if (scontrol->comp_id == swidget->comp_id && (scontrol->access & SNDRV_CTL_ELEM_ACCESS_VOLATILE)) { - dev_err(sdev->dev, + dev_err(swidget->scomp->dev, "error: volatile control found for dynamic widget %s\n", swidget->widget->name); return -EINVAL; @@ -2203,28 +2247,32 @@ static int sof_set_widget_pipeline(struct snd_sof_dev *sdev, struct snd_sof_pipe /* completion - called at completion of firmware loading */ static int sof_complete(struct snd_soc_component *scomp) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_component_get_tplg_ops(scomp); const struct sof_ipc_tplg_widget_ops *widget_ops; struct snd_sof_control *scontrol; struct snd_sof_pipeline *spipe; int ret; + if (!instance) + return -EINVAL; + widget_ops = tplg_ops ? tplg_ops->widget : NULL; /* first update all control IPC structures based on the IPC version */ if (tplg_ops && tplg_ops->control_setup) - list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { + list_for_each_entry(scontrol, &instance->kcontrol_list, list) { ret = tplg_ops->control_setup(sdev, scontrol); if (ret < 0) { - dev_err(sdev->dev, "failed updating IPC struct for control %s\n", + dev_err(scomp->dev, "failed updating IPC struct for control %s\n", scontrol->name); return ret; } } /* set up the IPC structures for the pipeline widgets */ - list_for_each_entry(spipe, &sdev->pipeline_list, list) { + list_for_each_entry(spipe, &instance->pipeline_list, list) { struct snd_sof_widget *pipe_widget = spipe->pipe_widget; struct snd_sof_widget *swidget; @@ -2234,24 +2282,24 @@ static int sof_complete(struct snd_soc_component *scomp) if (widget_ops && widget_ops[pipe_widget->id].ipc_setup) { ret = widget_ops[pipe_widget->id].ipc_setup(pipe_widget); if (ret < 0) { - dev_err(sdev->dev, "failed updating IPC struct for %s\n", + dev_err(scomp->dev, "failed updating IPC struct for %s\n", pipe_widget->widget->name); return ret; } } /* set the pipeline and update the IPC structure for the non scheduler widgets */ - list_for_each_entry(swidget, &sdev->widget_list, list) + list_for_each_entry(swidget, &instance->widget_list, list) if (swidget->widget->id != snd_soc_dapm_scheduler && swidget->pipeline_id == pipe_widget->pipeline_id) { - ret = sof_set_widget_pipeline(sdev, spipe, swidget); + ret = sof_set_widget_pipeline(spipe, swidget); if (ret < 0) return ret; if (widget_ops && widget_ops[swidget->id].ipc_setup) { ret = widget_ops[swidget->id].ipc_setup(swidget); if (ret < 0) { - dev_err(sdev->dev, + dev_err(scomp->dev, "failed updating IPC struct for %s\n", swidget->widget->name); return ret; @@ -2264,16 +2312,16 @@ static int sof_complete(struct snd_soc_component *scomp) if (sof_debug_check_flag(SOF_DBG_VERIFY_TPLG)) { if (tplg_ops && tplg_ops->set_up_all_pipelines && tplg_ops->tear_down_all_pipelines) { - ret = tplg_ops->set_up_all_pipelines(sdev, true); + ret = tplg_ops->set_up_all_pipelines(scomp, true); if (ret < 0) { - dev_err(sdev->dev, "Failed to set up all topology pipelines: %d\n", + dev_err(scomp->dev, "Failed to set up all topology pipelines: %d\n", ret); return ret; } - ret = tplg_ops->tear_down_all_pipelines(sdev, true); + ret = tplg_ops->tear_down_all_pipelines(scomp, true); if (ret < 0) { - dev_err(sdev->dev, "Failed to tear down topology pipelines: %d\n", + dev_err(scomp->dev, "Failed to tear down topology pipelines: %d\n", ret); return ret; } @@ -2281,18 +2329,14 @@ static int sof_complete(struct snd_soc_component *scomp) } /* set up static pipelines */ - if (tplg_ops && tplg_ops->set_up_all_pipelines) - return tplg_ops->set_up_all_pipelines(sdev, false); - - return 0; + return sof_instance_set_up_pipelines(instance); } /* manifest - optional to inform component of manifest */ static int sof_manifest(struct snd_soc_component *scomp, int index, struct snd_soc_tplg_manifest *man) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_component_get_tplg_ops(scomp); if (tplg_ops && tplg_ops->parse_manifest) return tplg_ops->parse_manifest(scomp, index, man); @@ -2406,7 +2450,8 @@ static int sof_dspless_widget_ready(struct snd_soc_component *scomp, int index, if (WIDGET_IS_DAI(w->id)) { static const struct sof_topology_token dai_tokens[] = { {SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, 0}}; - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = + snd_sof_component_get_audio_instance(scomp); struct snd_sof_widget *swidget; struct snd_sof_dai *sdai; @@ -2441,7 +2486,7 @@ static int sof_dspless_widget_ready(struct snd_soc_component *scomp, int index, swidget->private = sdai; mutex_init(&swidget->setup_mutex); w->dobj.private = swidget; - list_add(&swidget->list, &sdev->widget_list); + list_add(&swidget->list, &instance->widget_list); } return 0; @@ -2503,10 +2548,56 @@ static const struct snd_soc_tplg_ops sof_dspless_tplg_ops = { .bytes_ext_ops_count = ARRAY_SIZE(sof_dspless_bytes_ext_ops), }; +/* + * Check if a feature topology is compatible with the current card by + * verifying that matching BE DAI links exist. + * + * Feature topologies extend specific codec function types. If the card + * doesn't have the corresponding BE DAI links, the topology load would + * fail due to missing routes/widgets. This works transparently for both + * multi-card and single-card configurations. + */ +static bool +sof_feature_tplg_matches_card(struct snd_soc_component *scomp, + const char *feature_tplg) +{ + static const struct { + const char *tplg_key; /* substring in feature topology filename */ + const char *dai_key; /* substring in BE DAI link name */ + } match_table[] = { + { "amp", "SmartAmp" }, + { "jack", "SimpleJack" }, + { "mic", "SmartMic" }, + }; + struct snd_soc_dai_link *dai_link; + bool needs_match = false; + int i, j; + + for (i = 0; i < ARRAY_SIZE(match_table); i++) { + if (!strstr(feature_tplg, match_table[i].tplg_key)) + continue; + + needs_match = true; + + for_each_card_prelinks(scomp->card, j, dai_link) { + if (strstr(dai_link->name, match_table[i].dai_key)) + return true; + } + } + + /* Unknown feature type: load it (backwards compatible) */ + return !needs_match; +} + int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); + struct sof_client_dev *cdev = snd_sof_component_get_cdev(scomp); + struct sof_audio_client_pdata *audio_pdata = dev_get_platdata(&cdev->auxdev.dev); + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); struct snd_sof_pdata *sof_pdata = sdev->pdata; + const struct snd_soc_acpi_mach *mach = audio_pdata->machine.drv_name ? + &audio_pdata->machine : sof_pdata->machine; const char *tplg_filename_prefix = sof_pdata->tplg_filename_prefix; const struct firmware *fw; const char **tplg_files; @@ -2520,7 +2611,7 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) /* Try to use function topologies if possible */ if (!sof_pdata->disable_function_topology && !disable_function_topology && - sof_pdata->machine && sof_pdata->machine->get_function_tplg_files) { + mach && mach->get_function_tplg_files) { /* * When the topology name contains 'dummy' word, it means that * there is no fallback option to monolithic topology in case @@ -2535,11 +2626,11 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) */ bool no_fallback = strstr(file, "dummy"); - tplg_cnt = sof_pdata->machine->get_function_tplg_files(scomp->card, - sof_pdata->machine, - tplg_filename_prefix, - &tplg_files, - no_fallback); + tplg_cnt = mach->get_function_tplg_files(scomp->card, + mach, + tplg_filename_prefix, + &tplg_files, + no_fallback); if (tplg_cnt < 0) { kfree(tplg_files); return tplg_cnt; @@ -2597,11 +2688,25 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) } } - /* Loading user defined topologies */ + /* + * Loading user defined feature topologies. + * Each feature topology extends a specific function type (amp, jack, + * mic, etc.). Only load if the card has a matching BE DAI link, + * otherwise the routes would fail due to missing widgets. + */ for (i = 0; i < feature_tplg_cnt; i++) { - const char *feature_topology = devm_kasprintf(scomp->dev, GFP_KERNEL, "%s/%s", - tplg_filename_prefix, - feature_topologies[i]); + const char *feature_topology; + + if (!sof_feature_tplg_matches_card(scomp, feature_topologies[i])) { + dev_dbg(scomp->dev, + "skip feature topology %s: no matching BE DAI link\n", + feature_topologies[i]); + continue; + } + + feature_topology = devm_kasprintf(scomp->dev, GFP_KERNEL, "%s/%s", + tplg_filename_prefix, + feature_topologies[i]); if (!feature_topology) { ret = -ENOMEM; @@ -2649,11 +2754,10 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) ret = sof_complete(scomp); out: - if (ret >= 0 && sdev->led_present) + if (ret >= 0 && instance->led_present) ret = snd_ctl_led_request(); kfree(tplg_files); return ret; } -EXPORT_SYMBOL(snd_sof_load_topology);