From 9cbb30cbed2fe9f06ee7478fe6715f776368837a Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 8 Jun 2026 17:05:53 +0300 Subject: [PATCH 001/100] ASoC: SOF: sof-client: Use event-handler mutex consistently Protect IPC RX and FW state handler list unregister/dispatch paths with client_event_handler_mutex to match list registration and locking comments. Fixes: 5c19da34df02 ("ASoC: SOF: Use guard()/scoped_guard() for mutex locks where it makes sense") Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-client.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index a90d8243d71a7a..4f3ffceacb5873 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -612,7 +612,7 @@ void sof_client_unregister_ipc_rx_handler(struct sof_client_dev *cdev, struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); struct sof_ipc_event_entry *event; - guard(mutex)(&sdev->ipc_client_mutex); + guard(mutex)(&sdev->client_event_handler_mutex); list_for_each_entry(event, &sdev->ipc_rx_handler_list, list) { if (event->cdev == cdev && event->ipc_msg_type == ipc_msg_type) { @@ -629,7 +629,7 @@ void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev) { struct sof_state_event_entry *event; - guard(mutex)(&sdev->ipc_client_mutex); + guard(mutex)(&sdev->client_event_handler_mutex); list_for_each_entry(event, &sdev->fw_state_handler_list, list) event->callback(event->cdev, sdev->fw_state); @@ -664,7 +664,7 @@ void sof_client_unregister_fw_state_handler(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_event_handler_mutex); list_for_each_entry(event, &sdev->fw_state_handler_list, list) { if (event->cdev == cdev) { From a0e18c72e589cabfa7a402610ae0a95c993edecf Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 29 Apr 2025 10:22:52 +0300 Subject: [PATCH 002/100] ASoC: SOF: ipc4-topology: Propagate change caused by NHLT data for SSP We need to adjust the params based on the available and picked SSP blob in the similar way we do for DMIC. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/ipc4-topology.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 09246e4c378097..6d51948723dc73 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -1895,6 +1895,12 @@ snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai __func__, dai_index); return dev_type; } + + if (params_width(params) != bit_depth) { + format_change = true; + dev_dbg(sdev->dev, "SSP sample width change from %d to %d\n", + params_width(params), bit_depth); + } break; default: return 0; From abadd7b8a2d62c34f0ffbfeb341e5a373439d576 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 29 Apr 2025 09:38:56 +0300 Subject: [PATCH 003/100] ASoC: SOF: ipc4-topology: Improve playback dai copier in/out format selection The dai copier configuration for playback and capture needs to be separated because it is not correct to configure the dai copier as part of the input format selection for both direction. The input format is the dai format for capture, but it is not for playback, for playback the dai format is on the output side. Currently we configure and adjust the params based on the DAI supported formats when configuring the input side of the copier but right after the format has been adjusted we reset it for playback and loose this information. When using a nocodec passthrough topology (which is a bug) we have SSP blobs supporting 32bit only, copier supporting 16/24/32 bit then on playback the dai and copier will be incorrectly configured: host.copier.in: S16_LE host.copier.out: S16_LE dai.copier.in: S16_LE SSP.blob: S32_LE (we only have S32_LE blobs) dai.copier.out: S16_LE (the dai constraint is ignored) To handle such case the handling of capture and playback streams must be changed: The input format (no changes to previous implementation): for playback it is the pipeline_params for capture it is the adjusted fe_params The output format (no change for capture direction): for playback it is the adjusted fe_params for capture it is the fe_params with this change path format configuration will be correct: host.copier.in: S16_LE host.copier.out: S16_LE dai.copier.in: S16_LE SSP.blob: S32_LE (we only have S32_LE blobs) dai.copier.out: S32_LE Signed-off-by: Peter Ujfalusi --- sound/soc/sof/ipc4-topology.c | 73 +++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 25 deletions(-) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 6d51948723dc73..e8e604ab0ad15f 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -2319,28 +2319,32 @@ _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(sdev, dai, ref_params, dir); + if (ret < 0) + return ret; + } break; } @@ -2395,15 +2399,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(sdev, 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(sdev, 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(sdev, ref_params); if (out_ref_valid_bits < 0) return out_ref_valid_bits; } From 150822c8c629eb5fa535cf7d325a0c7732d63a21 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 6 May 2026 10:51:43 +0300 Subject: [PATCH 004/100] ASoC: SOF: ipc4-topology: Add debug print to sof_ipc4_host_config Print the same information as we print in _sof_ipc4_prepare_copier_module() since the prepare will be not called on system resume, only the host_config will be executed and tracking the stream tag for the host is valuable information. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/ipc4-topology.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index e8e604ab0ad15f..57979934a4d325 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -2137,6 +2137,10 @@ static void sof_ipc4_host_config(struct snd_sof_dev *sdev, struct snd_sof_widget struct sof_ipc4_pipeline *pipeline = pipe_widget->private; u32 host_dma_id = platform_params->stream_tag - 1; + dev_dbg(sdev->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); From 57a367812aa4f9280be06e4d5b095e235ef113ff Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 12 May 2026 08:32:02 +0300 Subject: [PATCH 005/100] ASoC: SOF: ipc3-control: Reject ABI data size larger than the TLV payload sof_ipc3_bytes_ext_put() copies header.length bytes from user space into cdata->data, but the amount of payload later sent to the firmware is taken from the ABI header's own size field. Nothing checks that the two agree, so a user claiming a size larger than the data it actually provided makes the driver send the stale tail of the previous control value to the DSP. The same stale tail is returned to user space by a subsequent bytes_ext_get() that does not read back from the DSP. Reject the payload if the ABI size field exceeds the data available in the TLV block. header.length has already been verified to be at least sizeof(struct sof_abi_hdr), so the subtraction cannot underflow. Fixes: 67ec2a091630 ("ASoC: SOF: Add bytes_ext control IPC ops for IPC3") Cc: stable@vger.kernel.org Signed-off-by: Peter Ujfalusi --- sound/soc/sof/ipc3-control.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index d1697401b1da0d..1b64145a3a3c0e 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -380,6 +380,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 +450,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 */ From 80f11aa4022e487f333e881414d8472012250233 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 7 May 2026 16:08:13 +0300 Subject: [PATCH 006/100] ASoC: SOF: Intel: Fix pipeline state transitions for aggregate DAIs For aggregate DAIs (num_cpus > 1) the pre_trigger/post_trigger callbacks send pipeline state IPCs per-DAI without considering that multiple DAIs may share the same pipeline. This causes premature state transitions where the pipeline goes RUNNING before all link DMAs have started, or individual DAIs send redundant IPCs for shared pipelines. Fix this by checking the HDA stream running state in post_trigger: - START/PAUSE_RELEASE: defer RUNNING IPC until all DAIs sharing the same pipeline have their link DMA streams running - STOP/SUSPEND/PAUSE_PUSH: use pipeline state dedup so the PAUSED IPC is sent once regardless of how many DAIs share the pipeline The running-state check naturally handles all aggregate topologies: shared pipelines, independent pipelines, and mixed cases without requiring per-pipeline counters. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/intel/hda-dai-ops.c | 60 ++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index f0be42048db33c..d46549aab535e6 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -356,6 +356,39 @@ 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) { @@ -383,13 +416,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: + /* + * 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(sdev, 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,11 +478,13 @@ 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 sof_ipc4_fw_data *ipc4_data = sdev->private; struct snd_sof_widget *pipe_widget; struct sof_ipc4_pipeline *pipeline; struct snd_sof_widget *swidget; struct snd_soc_dapm_widget *w; + int num_cpus = rtd->dai_link->num_cpus; int ret = 0; w = snd_soc_dai_get_widget(cpu_dai, substream->stream); @@ -455,6 +499,16 @@ 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, SOF_IPC4_PIPE_PAUSED); @@ -473,6 +527,10 @@ 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: + if (num_cpus > 1 && + !hda_ipc4_all_spipe_dmas_running(substream, swidget->spipe)) + break; + ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, SOF_IPC4_PIPE_RUNNING); if (ret < 0) From d18646a922ab1b7254b71ef7175cac4ce6f33ef3 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 1 Jun 2026 09:52:29 +0300 Subject: [PATCH 007/100] -- SOF multi-component from here -- From 055bf08b51367f7748f99f2aa9da368f7c8f0d9c Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 23 Jul 2026 17:38:09 +0300 Subject: [PATCH 008/100] ASoC: SOF: sof-client: dispatch events via per-client ops DSP-initiated IPC notifications and firmware state changes are delivered to clients through two dedicated subscription lists (ipc_rx_handler_list and fw_state_handler_list), each with its own entry type, allocation and register/unregister API, all guarded by client_event_handler_mutex. This does not scale. Adding a new event type requires yet another list, entry struct and register/unregister pair, duplicating the same bookkeeping. Collapse the per-event lists into a single struct sof_client_ops that a client registers with sof_client_register_ops() from its probe and drops with sof_client_unregister_ops() from its remove. Adding a new event type then only adds a callback field to the ops structure. Rename client_event_handler_mutex to client_ops_mutex and track the clients that registered ops on client_ops_list. The dispatchers walk that list and invoke the relevant callback when present, so only clients that opted in are visited. Clients receive every notification and filter the message types they handle themselves. Assisted-by: GitHub-Copilot:claude-opus-4.8 Signed-off-by: Peter Ujfalusi --- sound/soc/sof/core.c | 5 +- sound/soc/sof/sof-client.c | 159 ++++++------------------------------- sound/soc/sof/sof-client.h | 37 ++++++--- sound/soc/sof/sof-priv.h | 17 +--- 4 files changed, 58 insertions(+), 160 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 1f38fe03e49395..25972d4f326a81 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -691,13 +691,12 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) 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); 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 */ diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index 4f3ffceacb5873..28ed1719e251e9 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -19,32 +19,6 @@ #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 @@ -544,137 +518,54 @@ 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) { - struct sof_ipc_event_entry *event; - u32 msg_type; - - if (sdev->pdata->ipc_type == SOF_IPC_TYPE_3) { - struct sof_ipc_cmd_hdr *hdr = msg_buf; - - 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; - - 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(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); - } -} - -int sof_client_register_ipc_rx_handler(struct sof_client_dev *cdev, - u32 ipc_msg_type, - sof_client_event_callback callback) -{ - struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); - struct sof_ipc_event_entry *event; - - if (!callback) - return -EINVAL; - - 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; - } - - event = kmalloc_obj(*event); - if (!event) - return -ENOMEM; - - event->ipc_msg_type = ipc_msg_type; - 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->ipc_rx_handler_list); - - return 0; -} -EXPORT_SYMBOL_NS_GPL(sof_client_register_ipc_rx_handler, "SND_SOC_SOF_CLIENT"); - -void sof_client_unregister_ipc_rx_handler(struct sof_client_dev *cdev, - u32 ipc_msg_type) -{ - struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); - struct sof_ipc_event_entry *event; - - guard(mutex)(&sdev->client_event_handler_mutex); + struct sof_client_dev *cdev; - 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; - } + guard(mutex)(&sdev->client_ops_mutex); + list_for_each_entry(cdev, &sdev->client_ops_list, ops_node) { + if (cdev->ops->ipc_rx_handler) + cdev->ops->ipc_rx_handler(cdev, msg_buf); } } -EXPORT_SYMBOL_NS_GPL(sof_client_unregister_ipc_rx_handler, "SND_SOC_SOF_CLIENT"); -/*DSP state notification and query */ +/* DSP state notification */ void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev) { - struct sof_state_event_entry *event; - - guard(mutex)(&sdev->client_event_handler_mutex); + struct sof_client_dev *cdev; - list_for_each_entry(event, &sdev->fw_state_handler_list, list) - event->callback(event->cdev, sdev->fw_state); + guard(mutex)(&sdev->client_ops_mutex); + list_for_each_entry(cdev, &sdev->client_ops_list, ops_node) { + if (cdev->ops->fw_state_handler) + cdev->ops->fw_state_handler(cdev, sdev->fw_state); + } } -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(&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->client_event_handler_mutex); - - list_for_each_entry(event, &sdev->fw_state_handler_list, list) { - if (event->cdev == cdev) { - list_del(&event->list); - kfree(event); - break; - } + guard(mutex)(&sdev->client_ops_mutex); + if (cdev->ops) { + list_del(&cdev->ops_node); + 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) { diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index e796801570c823..6ac0025d56aee9 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -14,14 +14,19 @@ struct snd_sof_dev; struct dentry; struct sof_ipc4_fw_module; +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; }; @@ -60,19 +65,31 @@ 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); +/** + * 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. + * + * 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. + */ +struct sof_client_ops { + sof_client_event_callback ipc_rx_handler; + sof_client_fw_state_callback fw_state_handler; +}; + +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); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 83e4d83cc27cbb..49ef09df58d507 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -691,20 +691,11 @@ 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; - - /* - * Used for tracking the IPC client's registration for DSP state change - * notification - */ - struct list_head fw_state_handler_list; + /* list of clients that registered event ops via sof_client_register_ops() */ + struct list_head client_ops_list; - /* to protect the ipc_rx_handler_list and dsp_state_handler_list list */ - struct mutex client_event_handler_mutex; + /* to protect client_ops_list */ + struct mutex client_ops_mutex; /* quirks to override topology values */ bool mclk_id_override; From cd5802795a5a13e7cfb403b5967bb048b69f2a7c Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 17 Jul 2026 14:37:47 +0300 Subject: [PATCH 009/100] ASoC: SOF: ipc4: Call sof_client_ipc_rx_dispatcher() for notifications In order for clients to be able to handle notification they are interested, the sof_client_ipc_rx_dispatcher() must be called from rx handler. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/ipc4.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c index bf3a122ee651e8..852b70f671424c 100644 --- a/sound/soc/sof/ipc4.c +++ b/sound/soc/sof/ipc4.c @@ -870,6 +870,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) { From a609540f8f2e86e3c7ee467852e6c987f13a8b9e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 24 Jul 2026 10:21:42 +0300 Subject: [PATCH 010/100] ASoC: SOF: rename snd_sof_dsp_only_d0i3_compatible_stream_active() Rename snd_sof_dsp_only_d0i3_compatible_stream_active() to the shorter snd_sof_dsp_state_is_d0i3_compatible(), which reads more naturally at the call site. No functional change. Assisted-by: GitHub-Copilot:claude-opus-4.8 Signed-off-by: Peter Ujfalusi --- sound/soc/sof/intel/hda-dsp.c | 2 +- sound/soc/sof/sof-audio.c | 4 ++-- sound/soc/sof/sof-audio.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 9343102c4b1a9f..3576bd5cb3772d 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -1180,7 +1180,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/sof-audio.c b/sound/soc/sof/sof-audio.c index 0e4e1bb8c35ff1..1eeaaf3c4577c2 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -901,7 +901,7 @@ 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) +bool snd_sof_dsp_state_is_d0i3_compatible(struct snd_sof_dev *sdev) { struct snd_pcm_substream *substream; struct snd_sof_pcm *spcm; @@ -928,7 +928,7 @@ bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev) return d0i3_compatible_active; } -EXPORT_SYMBOL(snd_sof_dsp_only_d0i3_compatible_stream_active); +EXPORT_SYMBOL(snd_sof_dsp_state_is_d0i3_compatible); bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev) { diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index b60246fd5869b1..9f2ebab56f2fde 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -685,7 +685,7 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa /* 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); +bool snd_sof_dsp_state_is_d0i3_compatible(struct snd_sof_dev *sdev); /* Machine driver enumeration */ int sof_machine_register(struct snd_sof_dev *sdev, void *pdata); From 3f3ae8559fa3729616901aed6c0f6b877ecff856 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 24 Jul 2026 10:26:44 +0300 Subject: [PATCH 011/100] ASoC: SOF: sof-client: add D0i3 compatibility vote The DSP can only enter the low-power D0i3 substate when every active stream tolerates it. snd_sof_dsp_state_is_d0i3_compatible() decides this today by scanning the audio PCM streams directly, which does not work once audio moves into a removable sof-client, and cannot account for a non-audio client that has its own D0i3-relevant activity. Add a d0i3_vote callback to struct sof_client_ops. A client reports one of three states via enum sof_d0i3_vote: no activity, active and D0i3 compatible, or active and incompatible. sof_client_get_d0i3_vote() folds the votes of all clients that implement the callback: - any SOF_D0I3_INCOMPATIBLE -> SOF_D0I3_INCOMPATIBLE (veto) - else any SOF_D0I3_COMPATIBLE_ACTIVE -> SOF_D0I3_COMPATIBLE_ACTIVE - else -> SOF_D0I3_NO_ACTIVITY Clients without the callback do not participate. The fold is associative, so combining the core scan with the client votes yields the same result as a single scan over all streams. snd_sof_dsp_state_is_d0i3_compatible() now folds the client vote into its own scan. No client implements the callback yet, so the vote is SOF_D0I3_NO_ACTIVITY and the behaviour is unchanged. Assisted-by: GitHub-Copilot:claude-opus-4.8 Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-audio.c | 15 +++++++++++++++ sound/soc/sof/sof-client.c | 32 ++++++++++++++++++++++++++++++++ sound/soc/sof/sof-client.h | 22 ++++++++++++++++++++++ sound/soc/sof/sof-priv.h | 7 +++++++ 4 files changed, 76 insertions(+) diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 1eeaaf3c4577c2..db5c33086ff697 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -926,6 +926,21 @@ bool snd_sof_dsp_state_is_d0i3_compatible(struct snd_sof_dev *sdev) } } + /* + * Fold in the votes of any sof-client that participates in the D0i3 + * decision: an incompatible client stream vetoes D0i3, a compatible + * active client stream counts as compatible activity. + */ + switch (sof_client_get_d0i3_vote(sdev)) { + case SOF_D0I3_INCOMPATIBLE: + return false; + case SOF_D0I3_COMPATIBLE_ACTIVE: + d0i3_compatible_active = true; + break; + case SOF_D0I3_NO_ACTIVITY: + break; + } + return d0i3_compatible_active; } EXPORT_SYMBOL(snd_sof_dsp_state_is_d0i3_compatible); diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index 28ed1719e251e9..d7919729e2a73f 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -539,6 +539,38 @@ void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev) } } +/* + * Fold the D0i3 votes of every client that implements the d0i3_vote callback: + * - any SOF_D0I3_INCOMPATIBLE -> SOF_D0I3_INCOMPATIBLE (veto) + * - else any SOF_D0I3_COMPATIBLE_ACTIVE -> SOF_D0I3_COMPATIBLE_ACTIVE + * - else -> SOF_D0I3_NO_ACTIVITY + * Clients without the callback do not participate in the vote. + */ +enum sof_d0i3_vote sof_client_get_d0i3_vote(struct snd_sof_dev *sdev) +{ + enum sof_d0i3_vote result = SOF_D0I3_NO_ACTIVITY; + struct sof_client_dev *cdev; + + guard(mutex)(&sdev->client_ops_mutex); + list_for_each_entry(cdev, &sdev->client_ops_list, ops_node) { + if (!cdev->ops->d0i3_vote) + continue; + + switch (cdev->ops->d0i3_vote(cdev)) { + case SOF_D0I3_INCOMPATIBLE: + return SOF_D0I3_INCOMPATIBLE; + case SOF_D0I3_COMPATIBLE_ACTIVE: + result = SOF_D0I3_COMPATIBLE_ACTIVE; + break; + case SOF_D0I3_NO_ACTIVITY: + break; + } + } + + return result; +} +EXPORT_SYMBOL_NS_GPL(sof_client_get_d0i3_vote, "SND_SOC_SOF_CLIENT"); + int sof_client_register_ops(struct sof_client_dev *cdev, const struct sof_client_ops *ops) { diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index 6ac0025d56aee9..56f1190e1f7fb7 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -69,12 +69,33 @@ typedef void (*sof_client_event_callback)(struct sof_client_dev *cdev, void *msg typedef void (*sof_client_fw_state_callback)(struct sof_client_dev *cdev, enum sof_fw_state state); +/* + * 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 core folds the votes of all clients + * that implement the d0i3_vote callback: + * - any SOF_D0I3_INCOMPATIBLE -> D0i3 denied + * - else any SOF_D0I3_COMPATIBLE_ACTIVE -> D0i3 allowed + * - else (all SOF_D0I3_NO_ACTIVITY) -> D0i3 denied, nothing to keep it up + * Clients without the callback do not participate in the vote. + */ +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_INCOMPATIBLE, /* an active stream requires the DSP in D0i0 */ +}; + +typedef enum sof_d0i3_vote (*sof_client_d0i3_vote_callback)(struct sof_client_dev *cdev); + /** * 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. + * @d0i3_vote: Returns the client's D0i3 compatibility vote, see + * enum sof_d0i3_vote. * * 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 @@ -84,6 +105,7 @@ typedef void (*sof_client_fw_state_callback)(struct sof_client_dev *cdev, struct sof_client_ops { sof_client_event_callback ipc_rx_handler; sof_client_fw_state_callback fw_state_handler; + sof_client_d0i3_vote_callback d0i3_vote; }; int sof_client_register_ops(struct sof_client_dev *cdev, diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 49ef09df58d507..ec68cdcb9f42a1 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -20,6 +20,7 @@ #include #include #include +#include "sof-client.h" struct snd_sof_pcm_stream; @@ -872,6 +873,7 @@ 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); +enum sof_d0i3_vote sof_client_get_d0i3_vote(struct snd_sof_dev *sdev); 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 */ @@ -908,6 +910,11 @@ static inline void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev) { } +static inline enum sof_d0i3_vote sof_client_get_d0i3_vote(struct snd_sof_dev *sdev) +{ + return SOF_D0I3_NO_ACTIVITY; +} + static inline int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state) { return 0; From d888aa234b0358495c5433282eac2965b360f9e6 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 24 Jul 2026 13:27:49 +0300 Subject: [PATCH 012/100] ASoC: SOF: Move snd_sof_mailbox to the public SOF header The mailbox descriptor is a DSP-architecture concept rather than a private core detail, and it will be needed by sof-client code that must not include sof-priv.h. Move struct snd_sof_mailbox from sof-priv.h to include/sound/sof.h, alongside the other shared DSP-level definitions. Add enum snd_sof_mailbox_type to identify the individual DSP mailboxes (FW info, DSP, host, stream, debug), terminated with SOF_MAILBOX_COUNT for future use. No functional change. Signed-off-by: Peter Ujfalusi --- include/sound/sof.h | 16 ++++++++++++++++ sound/soc/sof/sof-priv.h | 6 ------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/include/sound/sof.h b/include/sound/sof.h index 38d6c8cb5e832b..69e85a87c0fad1 100644 --- a/include/sound/sof.h +++ b/include/sound/sof.h @@ -57,6 +57,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; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index ec68cdcb9f42a1..64dc6f6fa11926 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -421,12 +421,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 */ From cf32b433d8a7303b465b64dcb5e248929d4ed61a Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 24 Jul 2026 13:29:10 +0300 Subject: [PATCH 013/100] ASoC: SOF: sof-client: add sof_client_get_mailbox() accessor Add a client accessor that returns the struct snd_sof_mailbox for a given DSP mailbox, selected by enum snd_sof_mailbox_type. This lets sof-client code obtain a mailbox descriptor (e.g. the FW info box) and its offset without reaching into struct snd_sof_dev directly. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-client.c | 22 ++++++++++++++++++++++ sound/soc/sof/sof-client.h | 2 ++ 2 files changed, 24 insertions(+) diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index d7919729e2a73f..5f6cf70d355013 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -628,3 +628,25 @@ 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"); diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index 56f1190e1f7fb7..d074edac9162b2 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -119,6 +119,8 @@ 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); From df0990ea6236bf20f0f72d6a0dfb7d2832935b25 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 3 Jun 2026 12:09:03 +0300 Subject: [PATCH 014/100] ASoC: SOF: sof-client: add dspless mode and num_cores accessors Add sof_client_is_dspless() and sof_client_get_num_cores() accessor functions to the sof-client API. These will be needed by the audio client driver to query DSP state without direct access to snd_sof_dev. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-client.c | 16 ++++++++++++++++ sound/soc/sof/sof-client.h | 3 +++ 2 files changed, 19 insertions(+) diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index 5f6cf70d355013..d102e3a7faa85b 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -650,3 +650,19 @@ struct snd_sof_mailbox *sof_client_get_mailbox(struct sof_client_dev *cdev, } } 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"); diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index d074edac9162b2..06a40f3d7e5a60 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -125,4 +125,7 @@ struct snd_sof_mailbox *sof_client_get_mailbox(struct sof_client_dev *cdev, 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); + #endif /* __SOC_SOF_CLIENT_H */ From ad4c6c53aa68e5233860b6e1c0fae2830029c5ec Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 24 Jul 2026 14:10:30 +0300 Subject: [PATCH 015/100] ASoC: SOF: sof-client: add sof_client_get_ipc4_fw_data() accessor Add a client accessor returning the IPC4 firmware data (struct sof_ipc4_fw_data) stored in snd_sof_dev::private. This lets sof-client IPC4 code obtain the firmware data without reaching into struct snd_sof_dev directly. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-client.c | 8 ++++++++ sound/soc/sof/sof-client.h | 2 ++ 2 files changed, 10 insertions(+) diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index d102e3a7faa85b..9793c97951a9cd 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -666,3 +666,11 @@ int sof_client_get_num_cores(struct sof_client_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"); diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index 06a40f3d7e5a60..6304f907c088ca 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -14,6 +14,7 @@ struct snd_sof_dev; struct dentry; struct sof_ipc4_fw_module; +struct sof_ipc4_fw_data; struct sof_client_ops; /** @@ -127,5 +128,6 @@ ssize_t sof_client_ipc4_find_debug_slot_offset_by_type(struct sof_client_dev *cd 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); #endif /* __SOC_SOF_CLIENT_H */ From 99c5073bae0ebd3c4ebba9263f61f7a2276388d5 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 24 Jul 2026 14:27:53 +0300 Subject: [PATCH 016/100] ASoC: SOF: topology: assign kcontrol comp_id from its widget The kcontrols of a widget are parsed before the widget itself, so the control load handlers assigned scontrol->comp_id by pre-reading the global next_comp_id counter, relying on the fact that the very next widget allocation would consume that same id. This coupling is fragile and reaches into the DSP-global id counter from the control path. Instead, assign each kcontrol the comp_id of the widget it belongs to once the widget has been created, walking the widget's kcontrols the same way the unload path does. The resulting comp_id is identical, but it is now derived from the owning widget rather than guessed from the allocator. No functional change. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/topology.c | 56 +++++++++++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 666d8f50313feb..ff2c12bf6e858b 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -862,7 +862,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); @@ -906,7 +905,7 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, } 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 +921,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 +928,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 +941,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) { @@ -1417,6 +1412,46 @@ 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, @@ -1447,6 +1482,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) { From 91b9f8b1931ba97c90305f27ea90efe641a4f1d4 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 24 Jul 2026 14:57:30 +0300 Subject: [PATCH 017/100] ASoC: SOF: make next_comp_id atomic next_comp_id is a DSP-global monotonic comp_id allocator that may be accessed concurrently once a single DSP backs multiple audio cards/clients loading topology in parallel. Convert it to atomic_t and allocate ids with atomic_fetch_inc() so the increment is race-free. Also drop the misleading "reset during S3" note: the counter is never reset in code (snd_sof_dev persists across S3), it is only re-initialised when the device is re-probed. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-priv.h | 2 +- sound/soc/sof/topology.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 64dc6f6fa11926..c96f1f190c9170 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -619,7 +619,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 */ diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index ff2c12bf6e858b..338a6830c47f80 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1473,7 +1473,7 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, 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; From d03ecdc8506e2a4d1efdb1071fba5cc3a2ce0665 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 24 Jul 2026 15:09:17 +0300 Subject: [PATCH 018/100] ASoC: SOF: sof-client: add sof_client_get_new_comp_id() accessor Add a client accessor that allocates a new unique component id from the DSP-global monotonic comp_id counter using atomic_fetch_inc(). Component ids belong to the DSP instance, not to an individual audio client, so a single allocator shared by all clients keeps the ids unique even when one DSP backs multiple audio cards. There is no matching free: ids are only handed out, never released. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-client.c | 8 ++++++++ sound/soc/sof/sof-client.h | 1 + 2 files changed, 9 insertions(+) diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index 9793c97951a9cd..a4908a45de0aaf 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -674,3 +674,11 @@ struct sof_ipc4_fw_data *sof_client_get_ipc4_fw_data(struct sof_client_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"); diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index 6304f907c088ca..b9aa693f388316 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -129,5 +129,6 @@ ssize_t sof_client_ipc4_find_debug_slot_offset_by_type(struct sof_client_dev *cd 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); #endif /* __SOC_SOF_CLIENT_H */ From 48e6fe452595cfb5ab1c726584abb7a690e10938 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 27 Jul 2026 14:53:52 +0300 Subject: [PATCH 019/100] ASoC: SOF: sof-client: add sof_client_is_suspend_target_s0ix() accessor The system suspend target is recorded on the SOF device when a system suspend starts, and the PCM trigger path needs it to decide whether a D0i3 compatible stream can be left running over S0iX. Add sof_client_is_suspend_target_s0ix() so that audio code can query this without dereferencing struct snd_sof_dev. A predicate is used rather than exposing the suspend target itself, since audio code is only interested in the S0iX case and enum sof_system_suspend_state is private to the SOF core. No functional change, the accessor has no users yet. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-client.c | 17 +++++++++++++++++ sound/soc/sof/sof-client.h | 1 + 2 files changed, 18 insertions(+) diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index a4908a45de0aaf..4151a83df83dcc 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -489,6 +489,23 @@ 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"); + int sof_client_boot_dsp(struct sof_client_dev *cdev) { return snd_sof_boot_dsp_firmware(sof_client_dev_to_sof_dev(cdev)); diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index b9aa693f388316..4b2c2d6b012301 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -55,6 +55,7 @@ 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); +bool sof_client_is_suspend_target_s0ix(struct sof_client_dev *cdev); /* DSP/firmware boot request */ int sof_client_boot_dsp(struct sof_client_dev *cdev); From a05897909769fb5217d245793a96ba69f5446194 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 27 Jul 2026 15:05:54 +0300 Subject: [PATCH 020/100] ASoC: SOF: sof-client: add sof_client_get_topology_name() accessor The topology file name resolved by the SOF core is needed by audio code, which should not dereference struct snd_sof_dev to reach it. Add sof_client_get_topology_name() so that the name can be queried via the sof-client API. Note that a client owning its own machine description has its own topology name and should only fall back to this one when the machine does not provide it. No functional change, the accessor has no users yet. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-client.c | 19 +++++++++++++++++++ sound/soc/sof/sof-client.h | 1 + 2 files changed, 20 insertions(+) diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index 4151a83df83dcc..05baf46589a708 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -506,6 +506,25 @@ bool sof_client_is_suspend_target_s0ix(struct sof_client_dev *cdev) } EXPORT_SYMBOL_NS_GPL(sof_client_is_suspend_target_s0ix, "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"); + int sof_client_boot_dsp(struct sof_client_dev *cdev) { return snd_sof_boot_dsp_firmware(sof_client_dev_to_sof_dev(cdev)); diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index 4b2c2d6b012301..5446aa2d8a817e 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -55,6 +55,7 @@ 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); bool sof_client_is_suspend_target_s0ix(struct sof_client_dev *cdev); /* DSP/firmware boot request */ From 11f16d51a6a87f7e8df68659e0114cf6019220d5 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 27 Jul 2026 15:06:16 +0300 Subject: [PATCH 021/100] ASoC: SOF: sof-client: add sof_client_get_topology_prefix() accessor The topology path prefix is used to build the full path of the topology file to be requested. Audio code needs it both for the topology name of the SOF device and for the one coming from the client's own machine description. Add sof_client_get_topology_prefix() so that it can be queried via the sof-client API instead of dereferencing struct snd_sof_dev. The path is a property of the SOF device and it is shared by all clients, there is no need for a per client copy of it. No functional change, the accessor has no users yet. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-client.c | 18 ++++++++++++++++++ sound/soc/sof/sof-client.h | 1 + 2 files changed, 19 insertions(+) diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index 05baf46589a708..0a46145cb14535 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -525,6 +525,24 @@ const char *sof_client_get_topology_name(struct sof_client_dev *cdev) } 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"); + int sof_client_boot_dsp(struct sof_client_dev *cdev) { return snd_sof_boot_dsp_firmware(sof_client_dev_to_sof_dev(cdev)); diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index 5446aa2d8a817e..45b971c8e0c661 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -56,6 +56,7 @@ const struct sof_ipc_fw_version *sof_client_get_fw_version(struct sof_client_dev 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_is_suspend_target_s0ix(struct sof_client_dev *cdev); /* DSP/firmware boot request */ From 604a7e5c004c002bd9a2070ed7e65828029a87bf Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 27 Jul 2026 15:36:39 +0300 Subject: [PATCH 022/100] ASoC: SOF: sof-client: add function topology disable accessor Function topology loading is skipped when the user requested a specific topology file via the sof_tplg_filename module parameter, as that file is expected to be loaded as it is. Add sof_client_is_function_topology_disabled() so that audio code can query this without dereferencing struct snd_sof_dev. Note that this is separate from the topology.c local module parameter of the same name, which stays where it is. No functional change, the accessor has no users yet. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-client.c | 17 +++++++++++++++++ sound/soc/sof/sof-client.h | 1 + 2 files changed, 18 insertions(+) diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index 0a46145cb14535..55db60596dec5b 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -543,6 +543,23 @@ const char *sof_client_get_topology_prefix(struct sof_client_dev *cdev) } 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"); + int sof_client_boot_dsp(struct sof_client_dev *cdev) { return snd_sof_boot_dsp_firmware(sof_client_dev_to_sof_dev(cdev)); diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index 45b971c8e0c661..85057257f5d5fc 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -57,6 +57,7 @@ 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_is_function_topology_disabled(struct sof_client_dev *cdev); bool sof_client_is_suspend_target_s0ix(struct sof_client_dev *cdev); /* DSP/firmware boot request */ From 1f473cda2d32b71d2c133b47587a8f8d4beccb0d Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 27 Jul 2026 15:53:55 +0300 Subject: [PATCH 023/100] ASoC: SOF: sof-client: add sof_client_get_machine() accessor The machine description selected by the SOF core is needed by audio code to reach the get_function_tplg_files() callback, but it should not be obtained by dereferencing struct snd_sof_dev. Add sof_client_get_machine() to query it via the sof-client API. The returned machine is shared by all clients. A client which owns a machine description of its own must use that one and only fall back to this one when it has none, so the expectation is that clients resolve the two once and use the result afterwards. No functional change, the accessor has no users yet. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-client.c | 18 ++++++++++++++++++ sound/soc/sof/sof-client.h | 2 ++ 2 files changed, 20 insertions(+) diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index 55db60596dec5b..4f7e4dd676a303 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -560,6 +560,24 @@ bool sof_client_is_function_topology_disabled(struct sof_client_dev *cdev) } 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"); + int sof_client_boot_dsp(struct sof_client_dev *cdev) { return snd_sof_boot_dsp_firmware(sof_client_dev_to_sof_dev(cdev)); diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index 85057257f5d5fc..9c4774a1ebfa7f 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -11,6 +11,7 @@ struct sof_ipc_fw_version; struct sof_ipc_cmd_hdr; struct snd_sof_dev; +struct snd_soc_acpi_mach; struct dentry; struct sof_ipc4_fw_module; @@ -57,6 +58,7 @@ 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); +const struct snd_soc_acpi_mach *sof_client_get_machine(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); From 8a64a4c0e69614d63836411ad8eef6d11fca5f15 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 27 Jul 2026 16:05:06 +0300 Subject: [PATCH 024/100] ASoC: SOF: sof-client: add SSP MCLK ID quirk accessor The sdev level mclk_id_override/mclk_id_quirk pair is set either from NHLT data or from the snd-sof-intel-hda-generic mclk_id module parameter and it has to be consulted when an SSP link is configured. Add sof_client_get_ssp_mclk_id_quirk() so that the quirk can be queried via the sof-client API instead of dereferencing struct snd_sof_dev. The accessor combines the two fields into a single query: it returns true and fills the output parameter only when a quirk is present, so callers cannot use the value without checking its validity. A signed sentinel would not work here as struct snd_sof_dev is zero initialized and 0 is a valid mclk_id. No functional change, the accessor has no users yet. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-client.c | 23 +++++++++++++++++++++++ sound/soc/sof/sof-client.h | 1 + 2 files changed, 24 insertions(+) diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index 4f7e4dd676a303..269d7abd41fb1f 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -578,6 +578,29 @@ const struct snd_soc_acpi_mach *sof_client_get_machine(struct sof_client_dev *cd } EXPORT_SYMBOL_NS_GPL(sof_client_get_machine, "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)); diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index 9c4774a1ebfa7f..ca99cd4cc6d743 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -58,6 +58,7 @@ 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); bool sof_client_is_function_topology_disabled(struct sof_client_dev *cdev); bool sof_client_is_suspend_target_s0ix(struct sof_client_dev *cdev); From de610cffbf366878d38c2cc348d0407ce7fd6cf3 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 28 May 2026 17:07:19 +0300 Subject: [PATCH 025/100] ASoC: SOF: switch to snd_sof_component_get_sdev() Replace direct snd_soc_component_get_drvdata() lookups used to fetch struct snd_sof_dev with snd_sof_component_get_sdev() across sound/soc/sof/. This is a preparatory, mechanical cleanup to keep SOF component accessor naming consistent ahead of follow-up ownership refactoring. No functional behavior change is intended. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/control.c | 22 +++++++++++----------- sound/soc/sof/intel/hda.h | 2 +- sound/soc/sof/ipc3-compress.c | 6 +++--- sound/soc/sof/ipc3-control.c | 2 +- sound/soc/sof/ipc3-pcm.c | 8 ++++---- sound/soc/sof/ipc3-topology.c | 14 +++++++------- sound/soc/sof/ipc3.c | 6 ++---- sound/soc/sof/ipc4-compress.c | 14 +++++++------- sound/soc/sof/ipc4-control.c | 14 +++++++------- sound/soc/sof/ipc4-pcm.c | 8 ++++---- sound/soc/sof/ipc4-topology.c | 22 +++++++++++----------- sound/soc/sof/pcm.c | 24 ++++++++++++------------ sound/soc/sof/sof-audio.c | 20 ++++++++++++++------ sound/soc/sof/sof-audio.h | 11 ++++++++++- sound/soc/sof/topology.c | 34 +++++++++++++++++----------------- 15 files changed, 111 insertions(+), 96 deletions(-) diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 74d997a4f6203c..24b32c86e2c2c9 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -21,7 +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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); if (tplg_ops && tplg_ops->control && tplg_ops->control->volume_get) @@ -36,7 +36,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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); if (tplg_ops && tplg_ops->control && tplg_ops->control->volume_put) @@ -73,7 +73,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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); if (tplg_ops && tplg_ops->control && tplg_ops->control->switch_get) @@ -88,7 +88,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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); if (tplg_ops && tplg_ops->control && tplg_ops->control->switch_put) @@ -103,7 +103,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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); if (tplg_ops && tplg_ops->control && tplg_ops->control->enum_get) @@ -118,7 +118,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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); if (tplg_ops && tplg_ops->control && tplg_ops->control->enum_put) @@ -133,7 +133,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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); if (tplg_ops && tplg_ops->control && tplg_ops->control->bytes_get) @@ -148,7 +148,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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); if (tplg_ops && tplg_ops->control && tplg_ops->control->bytes_put) @@ -164,7 +164,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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); /* make sure we have at least a header */ @@ -183,7 +183,7 @@ 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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); int ret, err; @@ -219,7 +219,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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); if (tplg_ops && tplg_ops->control && tplg_ops->control->bytes_ext_get) diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 701578ec35cf4d..141d13ed32e7e5 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -1102,7 +1102,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/ipc3-compress.c b/sound/soc/sof/ipc3-compress.c index 115d454bcaf5b7..09716d45331a62 100644 --- a/sound/soc/sof/ipc3-compress.c +++ b/sound/soc/sof/ipc3-compress.c @@ -51,7 +51,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 snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct sof_compr_stream *sstream = cstream->runtime->private_data; struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct sof_ipc_stream stream; @@ -83,7 +83,7 @@ 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 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; @@ -200,7 +200,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 snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct sof_ipc_stream stream; struct snd_sof_pcm *spcm; diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index 1b64145a3a3c0e..74c47118ba8786 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -15,7 +15,7 @@ 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_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; diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c index 42f93e596598fd..cc88126952648a 100644 --- a/sound/soc/sof/ipc3-pcm.c +++ b/sound/soc/sof/ipc3-pcm.c @@ -17,7 +17,7 @@ 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 snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct sof_ipc_stream stream; if (!spcm->prepared[dir]) @@ -36,7 +36,7 @@ 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 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; @@ -139,7 +139,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 snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct sof_ipc_stream stream; stream.hdr.size = sizeof(stream); @@ -205,7 +205,7 @@ 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 snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct sof_dai_private_data *private; struct snd_soc_dpcm *dpcm; diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index 26d85ca9be26a1..dc45cc90a8ee09 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -524,7 +524,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 +757,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_dev *sdev = snd_sof_component_get_sdev(scomp); struct sof_ipc_comp_volume *volume; struct snd_sof_control *scontrol; size_t ipc_size = sizeof(*volume); @@ -1429,7 +1429,7 @@ 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 snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct snd_soc_tplg_hw_config *hw_config = slink->hw_configs; struct sof_dai_private_data *private = dai->private; u32 size = sizeof(*config); @@ -1502,7 +1502,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 +1600,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_dev *sdev = snd_sof_component_get_sdev(scomp); struct snd_sof_dai *dai = swidget->private; struct sof_dai_private_data *private; struct sof_ipc_comp_dai *comp_dai; @@ -1948,7 +1948,7 @@ static int sof_ipc3_control_free(struct snd_sof_dev *sdev, struct snd_sof_contro 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 snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct snd_pcm_hw_params *params; struct sof_ipc_pcm_params pcm; struct snd_sof_pcm *spcm; @@ -2004,7 +2004,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 snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct sof_ipc_stream stream; int ret; diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c index 85bb22bbe18d08..4a350d9d91226b 100644 --- a/sound/soc/sof/ipc3.c +++ b/sound/soc/sof/ipc3.c @@ -861,13 +861,12 @@ static int ipc3_fw_ready(struct snd_sof_dev *sdev, u32 cmd) /* 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); + spcm = snd_sof_find_spcm_comp_by_sdev(sdev, msg_id, &direction); if (!spcm) { dev_err(sdev->dev, "period elapsed for unknown stream, msg_id %d\n", msg_id); @@ -896,13 +895,12 @@ static void ipc3_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) /* 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); + spcm = snd_sof_find_spcm_comp_by_sdev(sdev, msg_id, &direction); if (!spcm) { dev_err(sdev->dev, "XRUN for unknown stream, msg_id %d\n", msg_id); diff --git a/sound/soc/sof/ipc4-compress.c b/sound/soc/sof/ipc4-compress.c index 05cebb780032e7..4d98629bb6505d 100644 --- a/sound/soc/sof/ipc4-compress.c +++ b/sound/soc/sof/ipc4-compress.c @@ -67,7 +67,7 @@ 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_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_sof_pcm *spcm; int dir, ret; @@ -145,7 +145,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; @@ -188,7 +188,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,7 +316,7 @@ 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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct sof_ipc4_compr_init_data *compr_data __free(kfree) = NULL; struct snd_soc_pcm_runtime *rtd = cstream->private_data; @@ -511,7 +511,7 @@ 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_sof_dev *sdev = snd_sof_component_get_sdev(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 @@ -534,7 +534,7 @@ 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_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_soc_pcm_runtime *rtd = cstream->private_data; const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); struct snd_sof_pcm *spcm; @@ -653,7 +653,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; diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c index 8d86d32a16caea..ff29023c0df9f6 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -17,7 +17,7 @@ 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_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; @@ -143,7 +143,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_dev *sdev = snd_sof_component_get_sdev(scomp); unsigned int channels = scontrol->num_channels; struct snd_sof_widget *swidget; bool widget_found = false; @@ -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_dev *sdev = snd_sof_component_get_sdev(scomp); struct snd_sof_widget *swidget; bool widget_found = false; bool change = 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_dev *sdev = snd_sof_component_get_sdev(scomp); struct snd_sof_widget *swidget; bool widget_found = false; bool change = false; @@ -552,7 +552,7 @@ 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 snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct sof_abi_hdr *data = cdata->data; const struct sof_abi_hdr *new_hdr = (const struct sof_abi_hdr *)ucontrol->value.bytes.data; @@ -626,7 +626,7 @@ 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 snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct sof_abi_hdr *data = cdata->data; struct sof_abi_hdr abi_hdr; struct snd_ctl_tlv header; @@ -725,7 +725,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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); int ret = sof_ipc4_set_get_bytes_data(sdev, scontrol, false, true); if (ret < 0) diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 5d8fc9b25bcc82..51f9b710531f29 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -398,7 +398,7 @@ 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 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; @@ -809,7 +809,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; @@ -1054,7 +1054,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 +1179,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; diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 57979934a4d325..356af4f6d09746 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -560,7 +560,7 @@ 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); @@ -610,7 +610,7 @@ 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_dev *sdev = snd_sof_component_get_sdev(scomp); struct sof_ipc4_fw_module *fw_module = swidget->module_info; struct snd_sof_control *scontrol; @@ -809,7 +809,7 @@ 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_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; @@ -989,7 +989,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; @@ -2156,7 +2156,7 @@ 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 snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); struct sof_ipc4_copier_data *copier_data; struct sof_ipc4_copier *ipc4_copier; @@ -2206,7 +2206,7 @@ _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_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; @@ -2718,7 +2718,7 @@ 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 snd_sof_dev *sdev = snd_sof_component_get_sdev(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; @@ -2767,7 +2767,7 @@ 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 snd_sof_dev *sdev = snd_sof_component_get_sdev(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; @@ -2816,7 +2816,7 @@ 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 snd_sof_dev *sdev = snd_sof_component_get_sdev(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; @@ -2985,7 +2985,7 @@ 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 snd_sof_dev *sdev = snd_sof_component_get_sdev(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; @@ -4128,7 +4128,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; diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 42738f12fa3390..66120fb5d44022 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -117,7 +117,7 @@ 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); @@ -289,7 +289,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; @@ -317,7 +317,7 @@ 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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); struct snd_sof_platform_stream_params *platform_params; struct snd_soc_dapm_widget_list *list; @@ -387,7 +387,7 @@ 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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); struct snd_sof_pcm *spcm; bool reset_hw_params = false; @@ -499,7 +499,7 @@ 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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); struct snd_sof_pcm *spcm; snd_pcm_uframes_t host, dai; @@ -539,7 +539,7 @@ 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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); const struct snd_sof_dsp_ops *ops = sof_ops(sdev); struct snd_sof_pcm *spcm; struct snd_soc_tplg_stream_caps *caps; @@ -601,7 +601,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 snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_sof_pcm *spcm; int err; @@ -638,7 +638,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,7 +728,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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); /* no topology exists for this BE, try a common configuration */ @@ -759,7 +759,7 @@ 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 snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_sof_pdata *plat_data = sdev->pdata; const char *tplg_filename; int ret; @@ -804,7 +804,7 @@ 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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); return snd_sof_pcm_platform_ack(sdev, substream); } @@ -812,7 +812,7 @@ static int sof_pcm_ack(struct snd_soc_component *component, 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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); if (pcm_ops && pcm_ops->delay) diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index db5c33086ff697..795130aed80013 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -965,7 +965,7 @@ 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_dev *sdev = snd_sof_component_get_sdev(scomp); struct snd_sof_pcm *spcm; list_for_each_entry(spcm, &sdev->pcm_list, list) { @@ -991,7 +991,15 @@ 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_dev *sdev = snd_sof_component_get_sdev(scomp); + + return snd_sof_find_spcm_comp_by_sdev(sdev, comp_id, direction); +} + +struct snd_sof_pcm *snd_sof_find_spcm_comp_by_sdev(struct snd_sof_dev *sdev, + unsigned int comp_id, + int *direction) +{ struct snd_sof_pcm *spcm; int dir; @@ -1010,7 +1018,7 @@ 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_dev *sdev = snd_sof_component_get_sdev(scomp); struct snd_sof_widget *swidget; list_for_each_entry(swidget, &sdev->widget_list, list) { @@ -1026,7 +1034,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_dev *sdev = snd_sof_component_get_sdev(scomp); struct snd_sof_widget *swidget; enum snd_soc_dapm_type type; @@ -1047,7 +1055,7 @@ 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_dev *sdev = snd_sof_component_get_sdev(scomp); struct snd_sof_dai *dai; list_for_each_entry(dai, &sdev->dai_list, list) { @@ -1064,7 +1072,7 @@ 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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); /* use the tplg configured mclk if existed */ diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 9f2ebab56f2fde..b2e2ca164c623a 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -619,11 +619,17 @@ 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 snd_sof_dev * +snd_sof_component_get_sdev(struct snd_soc_component *scomp) +{ + return snd_soc_component_get_drvdata(scomp); +} + 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_dev *sdev = snd_sof_component_get_sdev(scomp); struct snd_sof_pcm *spcm; list_for_each_entry(spcm, &sdev->pcm_list, list) { @@ -639,6 +645,9 @@ 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); +struct snd_sof_pcm *snd_sof_find_spcm_comp_by_sdev(struct snd_sof_dev *sdev, + 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, diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 338a6830c47f80..e97030f87dda72 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -64,7 +64,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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); const struct sof_token_info *token_list; const struct sof_topology_token *tokens; @@ -278,7 +278,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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); if (tplg_ops && tplg_ops->control && tplg_ops->control->set_up_volume_table) @@ -842,7 +842,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_dev *sdev = snd_sof_component_get_sdev(scomp); struct snd_soc_tplg_mixer_control *mc = container_of(hdr, struct snd_soc_tplg_mixer_control, hdr); int tlv[SOF_TLV_ITEMS]; @@ -970,7 +970,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_dev *sdev = snd_sof_component_get_sdev(scomp); struct snd_soc_dobj *dobj; struct snd_sof_control *scontrol; int ret; @@ -1045,7 +1045,7 @@ static int sof_control_load(struct snd_soc_component *scomp, int index, 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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_sof_control *scontrol = dobj->private; int ret = 0; @@ -1180,7 +1180,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) @@ -1218,7 +1218,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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_soc_tplg_private *private = &tw->priv; const struct sof_token_info *token_list; @@ -1457,7 +1457,7 @@ 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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); const struct sof_ipc_tplg_widget_ops *widget_ops; struct snd_soc_tplg_private *priv = &tw->priv; @@ -1687,7 +1687,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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); const struct sof_ipc_tplg_widget_ops *widget_ops; const struct snd_kcontrol_new *kc; @@ -1786,7 +1786,7 @@ 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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_pcm_ops *ipc_pcm_ops = sof_ipc_get_ops(sdev, pcm); struct snd_soc_tplg_stream_caps *caps; struct snd_soc_tplg_private *private = &pcm->priv; @@ -1901,7 +1901,7 @@ 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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_pcm_ops *ipc_pcm_ops = sof_ipc_get_ops(sdev, pcm); struct snd_sof_pcm *spcm = dobj->private; @@ -1932,7 +1932,7 @@ 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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_soc_tplg_private *private = &cfg->priv; const struct sof_token_info *token_list; @@ -2137,7 +2137,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_dev *sdev = snd_sof_component_get_sdev(scomp); struct snd_sof_widget *source_swidget, *sink_swidget; struct snd_soc_dobj *dobj = &route->dobj; struct snd_sof_route *sroute; @@ -2241,7 +2241,7 @@ 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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); const struct sof_ipc_tplg_widget_ops *widget_ops; struct snd_sof_control *scontrol; @@ -2329,7 +2329,7 @@ static int sof_complete(struct snd_soc_component *scomp) 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); + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); if (tplg_ops && tplg_ops->parse_manifest) @@ -2444,7 +2444,7 @@ 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_dev *sdev = snd_sof_component_get_sdev(scomp); struct snd_sof_widget *swidget; struct snd_sof_dai *sdai; @@ -2543,7 +2543,7 @@ static const struct snd_soc_tplg_ops sof_dspless_tplg_ops = { 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_dev *sdev = snd_sof_component_get_sdev(scomp); struct snd_sof_pdata *sof_pdata = sdev->pdata; const char *tplg_filename_prefix = sof_pdata->tplg_filename_prefix; const struct firmware *fw; From 7d67797b5bd18003b76fee20eaacf115f10d90f1 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 28 May 2026 17:26:40 +0300 Subject: [PATCH 026/100] ASoC: SOF: pass component to PCM DSP callbacks Convert the PCM callback family in snd_sof_dsp_ops to pass struct snd_soc_component * instead of struct snd_sof_dev *. Update the PCM callback wrappers and callback implementations to retrieve sdev with snd_sof_component_get_sdev(component). This is a preparatory, mechanical API-churn change only. No functional behavior change is intended, and sdev->component assignment is intentionally kept intact for now. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/amd/acp-pcm.c | 18 ++++++---- sound/soc/sof/amd/acp.h | 8 ++--- sound/soc/sof/intel/hda-pcm.c | 25 ++++++++------ sound/soc/sof/intel/hda-stream.c | 4 ++- sound/soc/sof/intel/hda.h | 15 +++++---- sound/soc/sof/mediatek/mt8365/mt8365.c | 8 ++--- sound/soc/sof/mediatek/mtk-adsp-common.c | 13 +++---- sound/soc/sof/mediatek/mtk-adsp-common.h | 6 ++-- sound/soc/sof/ops.h | 43 ++++++++++++++++-------- sound/soc/sof/pcm.c | 36 ++++++++++---------- sound/soc/sof/sof-priv.h | 19 ++++++----- sound/soc/sof/stream-ipc.c | 4 +-- 12 files changed, 116 insertions(+), 83 deletions(-) diff --git a/sound/soc/sof/amd/acp-pcm.c b/sound/soc/sof/amd/acp-pcm.c index 2802684f26de17..2692407e62ca2b 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_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,18 +92,18 @@ 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_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); + spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) { dev_warn_ratelimited(sdev->dev, "warn: can't find PCM with DAI ID %d\n", rtd->dai_link->id); diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index 1cd9904c2908f2..3b0c15cda21f7e 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -332,12 +332,12 @@ 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_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_pcm_substream *substream); extern const struct snd_sof_dsp_ops sof_acp_common_ops; diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index 83efc9bdf418af..7ef7e3577e3923 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; @@ -217,8 +218,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 +242,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); @@ -259,17 +263,17 @@ 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_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); + spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) { dev_warn_ratelimited(sdev->dev, "warn: can't find PCM with DAI ID %d\n", rtd->dai_link->id); @@ -305,19 +309,19 @@ 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_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); + spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) { dev_err(sdev->dev, "error: can't find PCM with DAI ID %d\n", rtd->dai_link->id); return -EINVAL; @@ -466,9 +470,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; diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index bdd4831e050ce1..9968cd2857d21a 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -803,9 +803,11 @@ 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"); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 141d13ed32e7e5..282f424ea40fbe 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -667,21 +667,22 @@ 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_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_pcm_substream *substream); -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); 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); diff --git a/sound/soc/sof/mediatek/mt8365/mt8365.c b/sound/soc/sof/mediatek/mt8365/mt8365.c index 7d2c6955c10c69..76a18038b98368 100644 --- a/sound/soc/sof/mediatek/mt8365/mt8365.c +++ b/sound/soc/sof/mediatek/mt8365/mt8365.c @@ -436,7 +436,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,18 +446,18 @@ 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_pcm_substream *substream) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_sof_pcm *spcm; 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); + spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) { dev_warn_ratelimited(sdev->dev, "warn: can't find PCM with DAI ID %d\n", rtd->dai_link->id); diff --git a/sound/soc/sof/mediatek/mtk-adsp-common.c b/sound/soc/sof/mediatek/mtk-adsp-common.c index 75b4af4b5111de..0a426e97f1bc57 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,21 +173,21 @@ EXPORT_SYMBOL(mtk_adsp_stream_pcm_hw_params); /** * mtk_adsp_stream_pcm_pointer - Get host stream pointer - * @sdev: SOF device + * @component: ASoC component * @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_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); + spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) { dev_warn_ratelimited(sdev->dev, "warn: can't find PCM with DAI ID %d\n", rtd->dai_link->id); @@ -211,3 +211,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..7980cd4ab4c0c2 100644 --- a/sound/soc/sof/mediatek/mtk-adsp-common.h +++ b/sound/soc/sof/mediatek/mtk-adsp-common.h @@ -6,15 +6,17 @@ #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_pcm_substream *substream); #endif diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 19605de680a319..c87c12286afbd8 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,35 +416,41 @@ 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_pcm_substream *substream) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + if (sof_ops(sdev) && sof_ops(sdev)->pcm_open) - return sof_ops(sdev)->pcm_open(sdev, substream); + return sof_ops(sdev)->pcm_open(component, 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) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + if (sof_ops(sdev) && sof_ops(sdev)->pcm_close) - return sof_ops(sdev)->pcm_close(sdev, substream); + return sof_ops(sdev)->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) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + if (sof_ops(sdev) && sof_ops(sdev)->pcm_hw_params) - return sof_ops(sdev)->pcm_hw_params(sdev, substream, params, + return sof_ops(sdev)->pcm_hw_params(component, substream, params, platform_params); return 0; @@ -524,22 +531,26 @@ snd_sof_compr_get_dai_frame_counter(struct snd_sof_dev *sdev, /* 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) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + if (sof_ops(sdev) && sof_ops(sdev)->pcm_hw_free) - return sof_ops(sdev)->pcm_hw_free(sdev, substream); + return sof_ops(sdev)->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) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + if (sof_ops(sdev) && sof_ops(sdev)->pcm_trigger) - return sof_ops(sdev)->pcm_trigger(sdev, substream, cmd); + return sof_ops(sdev)->pcm_trigger(component, substream, cmd); return 0; } @@ -576,21 +587,25 @@ snd_sof_set_stream_data_offset(struct snd_sof_dev *sdev, /* host stream pointer */ static inline snd_pcm_uframes_t -snd_sof_pcm_platform_pointer(struct snd_sof_dev *sdev, +snd_sof_pcm_platform_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + if (sof_ops(sdev) && sof_ops(sdev)->pcm_pointer) - return sof_ops(sdev)->pcm_pointer(sdev, substream); + return sof_ops(sdev)->pcm_pointer(component, 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) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + if (sof_ops(sdev) && sof_ops(sdev)->pcm_ack) - return sof_ops(sdev)->pcm_ack(sdev, substream); + return sof_ops(sdev)->pcm_ack(component, substream); return 0; } diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 66120fb5d44022..02392b8bbea419 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -160,7 +160,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; @@ -207,6 +207,7 @@ 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) @@ -218,12 +219,12 @@ static int sof_pcm_stream_free(struct snd_sof_dev *sdev, 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 +238,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); @@ -274,7 +275,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; @@ -303,7 +304,8 @@ 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); @@ -344,7 +346,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; } @@ -456,7 +458,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 +468,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 +492,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; } @@ -517,7 +520,7 @@ static snd_pcm_uframes_t sof_pcm_pointer(struct snd_soc_component *component, /* use dsp ops pointer callback directly if set */ if (sof_ops(sdev)->pcm_pointer) - return sof_ops(sdev)->pcm_pointer(sdev, substream); + return sof_ops(sdev)->pcm_pointer(component, substream); spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) @@ -581,7 +584,7 @@ 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, substream); if (ret < 0) { spcm_err(spcm, substream->stream, "platform pcm open failed %d\n", ret); @@ -601,7 +604,6 @@ 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_sof_component_get_sdev(component); struct snd_sof_pcm *spcm; int err; @@ -615,7 +617,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); @@ -804,9 +806,7 @@ 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_sof_component_get_sdev(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, diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index c96f1f190c9170..8b28971358963e 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -233,33 +233,34 @@ struct snd_sof_dsp_ops { struct snd_sof_mod_hdr *hdr); /* optional */ /* connect pcm substream to a host stream */ - int (*pcm_open)(struct snd_sof_dev *sdev, + int (*pcm_open)(struct snd_soc_component *component, struct snd_pcm_substream *substream); /* optional */ /* disconnect pcm substream to a host stream */ - int (*pcm_close)(struct snd_sof_dev *sdev, + int (*pcm_close)(struct snd_soc_component *component, struct snd_pcm_substream *substream); /* optional */ /* host stream hw params */ - int (*pcm_hw_params)(struct snd_sof_dev *sdev, + 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); /* optional */ /* host stream hw_free */ - int (*pcm_hw_free)(struct snd_sof_dev *sdev, + int (*pcm_hw_free)(struct snd_soc_component *component, struct snd_pcm_substream *substream); /* optional */ /* host stream trigger */ - int (*pcm_trigger)(struct snd_sof_dev *sdev, + int (*pcm_trigger)(struct snd_soc_component *component, struct snd_pcm_substream *substream, int cmd); /* optional */ /* host stream pointer */ - snd_pcm_uframes_t (*pcm_pointer)(struct snd_sof_dev *sdev, + snd_pcm_uframes_t (*pcm_pointer)(struct snd_soc_component *component, struct snd_pcm_substream *substream); /* optional */ /* pcm ack */ - int (*pcm_ack)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); /* optional */ + int (*pcm_ack)(struct snd_soc_component *component, + 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); @@ -849,9 +850,9 @@ 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_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); /* SOF client support */ diff --git a/sound/soc/sof/stream-ipc.c b/sound/soc/sof/stream-ipc.c index 78758cf58f0142..44c0f98e2840ab 100644 --- a/sound/soc/sof/stream-ipc.c +++ b/sound/soc/sof/stream-ipc.c @@ -96,7 +96,7 @@ 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_pcm_substream *substream) { struct sof_stream *stream = kmalloc_obj(*stream); @@ -118,7 +118,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; From 27aabb4bc1197fc6b1b66aa81138f6435ab910c3 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 28 May 2026 17:29:56 +0300 Subject: [PATCH 027/100] ASoC: SOF: pass component to compress DSP callbacks Convert the compress callback family in snd_sof_dsp_ops to pass struct snd_soc_component * instead of struct snd_sof_dev *. Update the compress callback wrappers and callback implementations to retrieve sdev with snd_sof_component_get_sdev(component). This is a preparatory, mechanical API-churn change only. No functional behavior change is intended. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/intel/hda-pcm.c | 20 ++++++++++------ sound/soc/sof/intel/hda-stream.c | 4 +++- sound/soc/sof/intel/hda.h | 15 +++++++----- sound/soc/sof/ipc4-compress.c | 21 ++++++++--------- sound/soc/sof/ops.h | 39 ++++++++++++++++++++++---------- sound/soc/sof/sof-priv.h | 18 ++++++++++----- 6 files changed, 74 insertions(+), 43 deletions(-) diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index 7ef7e3577e3923..b39d136ba2a4df 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -152,11 +152,12 @@ int hda_dsp_pcm_hw_params(struct snd_soc_component *component, } 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; @@ -253,9 +254,10 @@ int hda_dsp_pcm_trigger(struct snd_soc_component *component, } 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); @@ -295,7 +297,8 @@ snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_soc_component *component, } 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; @@ -435,15 +438,16 @@ int hda_dsp_pcm_open(struct snd_soc_component *component, } 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_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_soc_pcm_runtime *rtd = cstream->private_data; - struct snd_soc_component *scomp = 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); + spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) { dev_err(sdev->dev, "%s: can't find PCM with DAI ID %d\n", __func__, rtd->dai_link->id); @@ -491,8 +495,10 @@ int hda_dsp_pcm_close(struct snd_soc_component *component, } 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 9968cd2857d21a..747b1b6d13eba0 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -812,9 +812,11 @@ int hda_dsp_stream_hw_free(struct snd_soc_component *component, } 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"); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 282f424ea40fbe..29d092aa6f85ff 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -684,17 +684,20 @@ snd_pcm_uframes_t hda_dsp_pcm_pointer(struct snd_soc_component *component, int hda_dsp_pcm_ack(struct snd_soc_component *component, 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_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, struct snd_compr_stream *cstream); diff --git a/sound/soc/sof/ipc4-compress.c b/sound/soc/sof/ipc4-compress.c index 4d98629bb6505d..cf6e4662aec5cc 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_sof_component_get_sdev(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; @@ -109,13 +108,13 @@ static int sof_ipc4_compr_stream_free(struct snd_sof_dev *sdev, 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) @@ -165,7 +164,7 @@ static int sof_ipc4_compr_free(struct snd_soc_component *component, 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); @@ -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"); @@ -580,7 +579,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", @@ -668,7 +667,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); @@ -754,7 +753,7 @@ void sof_ipc4_compr_drain_done(struct snd_sof_dev *sdev, void *ipc_message) } /* 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_by_sdev(sdev, host_swidget->comp_id, &dir); if (!spcm) { dev_err(sdev->dev, "%s: Stream cannot be found for %s\n", __func__, host_swidget->widget->name); diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index c87c12286afbd8..34ff644934721a 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -457,64 +457,79 @@ snd_sof_pcm_platform_hw_params(struct snd_soc_component *component, } 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) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + if (sof_ops(sdev) && sof_ops(sdev)->compr_open) - return sof_ops(sdev)->compr_open(sdev, cstream); + return sof_ops(sdev)->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) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + if (sof_ops(sdev) && sof_ops(sdev)->compr_close) - return sof_ops(sdev)->compr_close(sdev, cstream); + return sof_ops(sdev)->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) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + if (sof_ops(sdev) && sof_ops(sdev)->compr_hw_params) - return sof_ops(sdev)->compr_hw_params(sdev, cstream, params, platform_params); + return sof_ops(sdev)->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) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + if (sof_ops(sdev) && sof_ops(sdev)->compr_hw_free) - return sof_ops(sdev)->compr_hw_free(sdev, cstream); + return sof_ops(sdev)->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) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + if (sof_ops(sdev) && sof_ops(sdev)->compr_trigger) - return sof_ops(sdev)->compr_trigger(sdev, cstream, cmd); + return sof_ops(sdev)->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) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + if (sof_ops(sdev) && sof_ops(sdev)->compr_pointer) - return sof_ops(sdev)->compr_pointer(sdev, cstream, tstamp); + return sof_ops(sdev)->compr_pointer(component, cstream, tstamp); return 0; } diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 8b28971358963e..bfee7400fcf07d 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -262,15 +262,21 @@ struct snd_sof_dsp_ops { int (*pcm_ack)(struct snd_soc_component *component, 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, + 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_sof_dev *sdev, struct snd_compr_stream *cstream); - int (*compr_trigger)(struct snd_sof_dev *sdev, struct snd_compr_stream *cstream, + 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_sof_dev *sdev, struct snd_compr_stream *cstream, + 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_sof_dev *sdev, struct snd_compr_stream *cstream); From ae21487a524d845dda8894b6d3000513741e2dd8 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 12 Jun 2026 14:32:01 +0300 Subject: [PATCH 028/100] ASoC: SOF: use component/widget device for component-scoped log messages Many log messages in the topology, control, PCM and audio-core paths were printing via sdev->dev even though the context is clearly bound to a specific component (scomp), widget (swidget), route (sroute) or control (scontrol). Logging through sdev->dev in those paths forces all messages to appear under the platform device, making it impossible to distinguish which audio component or pipeline instance triggered a given message. Switch all such messages to use the device that belongs to the available context. Several internal helper functions that received struct snd_sof_dev * solely to reach sdev->dev have also been simplified to derive sdev locally or drop the parameter entirely. sdev->dev is retained where there is no component context yet (e.g. early IPC notification handlers before the widget lookup, platform- level firmware loading, and places where the allocation is tied to the lifetime of sdev rather than the component). Signed-off-by: Peter Ujfalusi --- sound/soc/sof/ipc3-control.c | 12 +- sound/soc/sof/ipc3-topology.c | 49 ++++--- sound/soc/sof/ipc4-compress.c | 9 +- sound/soc/sof/ipc4-control.c | 72 +++++------ sound/soc/sof/ipc4-pcm.c | 12 +- sound/soc/sof/ipc4-topology.c | 232 ++++++++++++++++++---------------- sound/soc/sof/pcm.c | 4 +- sound/soc/sof/sof-audio.c | 76 ++++++----- sound/soc/sof/topology.c | 44 ++++--- 9 files changed, 268 insertions(+), 242 deletions(-) diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index 74c47118ba8786..04ae9e74a6fc1a 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -32,7 +32,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; } @@ -603,7 +603,7 @@ static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_ 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"); - return; + return; /* swidget not yet found, sdev->dev is correct here */ } /* Find the swidget first */ @@ -630,7 +630,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; } @@ -693,7 +693,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; } @@ -722,7 +722,7 @@ static int sof_ipc3_widget_kcontrol_setup(struct snd_sof_dev *sdev, /* 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; @@ -739,7 +739,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-topology.c b/sound/soc/sof/ipc3-topology.c index dc45cc90a8ee09..df3b6e95a814a3 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -1642,7 +1642,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; @@ -1779,6 +1779,7 @@ 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_ipc_pipe_comp_connect connect; int ret; @@ -1787,33 +1788,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)); 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 +1837,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 +1851,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 +1923,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); @@ -2130,10 +2132,11 @@ 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_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)); @@ -2178,7 +2181,8 @@ static int sof_ipc3_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget ret = sof_ipc_tx_message_no_reply(sdev->ipc, &ipc_free, sizeof(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 +2197,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 +2236,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; @@ -2276,7 +2282,8 @@ static int sof_ipc3_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget * if (swidget->use_count > 0) { ret = sof_ipc_tx_message_no_reply(sdev->ipc, config, config->hdr.size); 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; @@ -2321,7 +2328,8 @@ static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget } } 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; } @@ -2393,7 +2401,7 @@ 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; } } @@ -2548,7 +2556,8 @@ static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif */ list_for_each_entry(swidget, &sdev->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/ipc4-compress.c b/sound/soc/sof/ipc4-compress.c index cf6e4662aec5cc..136e016efbd1b2 100644 --- a/sound/soc/sof/ipc4-compress.c +++ b/sound/soc/sof/ipc4-compress.c @@ -510,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_sof_component_get_sdev(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 @@ -518,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; } @@ -543,7 +542,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; } @@ -747,7 +746,7 @@ 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; } @@ -755,7 +754,7 @@ void sof_ipc4_compr_drain_done(struct snd_sof_dev *sdev, void *ipc_message) /* Look up the spcm of the host copier */ spcm = snd_sof_find_spcm_comp_by_sdev(sdev, 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 ff29023c0df9f6..ae4a2f5e4cd52a 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -81,9 +81,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 +126,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; } @@ -177,7 +177,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 +199,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 +228,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); @@ -415,7 +414,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; @@ -474,7 +473,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 +495,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 +534,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 +551,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_sof_component_get_sdev(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 +578,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 +624,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_sof_component_get_sdev(scomp); struct sof_abi_hdr *data = cdata->data; struct sof_abi_hdr abi_hdr; struct snd_ctl_tlv header; @@ -700,7 +697,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 +722,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_sof_component_get_sdev(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,14 +773,14 @@ 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) \ @@ -861,7 +857,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 +874,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 +883,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 +896,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 +908,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); @@ -970,23 +966,21 @@ static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_s 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-pcm.c b/sound/soc/sof/ipc4-pcm.c index 51f9b710531f29..72cda1dc175826 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -699,7 +699,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 +740,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 +751,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 +792,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; } @@ -862,11 +862,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; diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 356af4f6d09746..2f6bee559586f3 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -567,7 +567,7 @@ static int sof_ipc4_widget_set_module_info(struct snd_sof_widget *swidget) 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; } @@ -1382,9 +1382,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; @@ -1435,26 +1437,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 +1465,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 +1490,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 +1509,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 +1542,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 +1570,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 +1613,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 +1626,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 +1643,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 +1666,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 +1689,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 +1725,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); @@ -2030,7 +2035,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) @@ -2071,7 +2076,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); @@ -2108,7 +2113,7 @@ 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; @@ -2128,16 +2133,18 @@ 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(sdev->dev, "Host copier %s, type %d, ChainDMA: %s, stream_tag: %d\n", + 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); @@ -2156,7 +2163,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_sof_component_get_sdev(scomp); struct sof_ipc4_copier_data *copier_data; struct sof_ipc4_copier *ipc4_copier; @@ -2185,13 +2191,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) | @@ -2231,7 +2237,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), @@ -2310,7 +2316,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)); @@ -2354,7 +2360,7 @@ _sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, } 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; @@ -2368,13 +2374,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) @@ -2424,13 +2430,13 @@ _sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, out_ref_rate = params_rate(ref_params); out_ref_channels = params_channels(ref_params); - ret = sof_ipc4_get_sample_type(sdev, 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, ref_params); + out_ref_valid_bits = sof_ipc4_get_valid_bits(scomp, ref_params); if (out_ref_valid_bits < 0) return out_ref_valid_bits; } @@ -2456,7 +2462,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, @@ -2590,7 +2596,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) | @@ -2612,7 +2618,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), @@ -2626,7 +2632,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), @@ -2662,7 +2668,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) @@ -2670,11 +2676,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)); @@ -2718,7 +2724,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_sof_component_get_sdev(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; @@ -2729,7 +2734,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); @@ -2742,7 +2747,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, @@ -2752,11 +2757,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; } @@ -2767,7 +2772,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_sof_component_get_sdev(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; @@ -2778,7 +2782,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); @@ -2791,7 +2795,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, @@ -2801,11 +2805,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; } @@ -2816,7 +2820,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_sof_component_get_sdev(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; @@ -2828,7 +2831,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); @@ -2867,7 +2870,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; @@ -2875,7 +2878,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, @@ -2885,17 +2888,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)); @@ -2985,7 +2988,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_sof_component_get_sdev(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; @@ -2999,14 +3001,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); @@ -3049,12 +3051,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; } @@ -3065,16 +3067,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, @@ -3092,7 +3094,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) @@ -3110,11 +3112,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)); @@ -3205,8 +3207,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; @@ -3217,21 +3220,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; @@ -3257,7 +3260,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; @@ -3267,7 +3270,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); @@ -3298,7 +3301,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); @@ -3356,6 +3359,7 @@ static int sof_ipc4_widget_mod_init_msg_payload(struct snd_sof_dev *sdev, { struct sof_ipc4_process *process = swidget->private; struct sof_ipc4_module_init_ext_object *hdr = NULL; + struct snd_soc_component *scomp = swidget->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; @@ -3390,7 +3394,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; @@ -3473,6 +3477,7 @@ 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 snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; struct sof_ipc4_fw_data *ipc4_data = sdev->private; struct sof_ipc4_pipeline *pipeline; @@ -3487,12 +3492,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; @@ -3501,7 +3506,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; } @@ -3620,7 +3625,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; } @@ -3632,16 +3637,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; } @@ -3655,7 +3660,7 @@ 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); @@ -3669,7 +3674,7 @@ 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); @@ -3692,7 +3697,7 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget 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; @@ -3709,6 +3714,7 @@ 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_ipc4_fw_module *fw_module = swidget->module_info; struct sof_ipc4_fw_data *ipc4_data = sdev->private; int ret = 0; @@ -3722,7 +3728,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; } @@ -3736,7 +3742,7 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0); 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; @@ -3843,6 +3849,7 @@ 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_ipc4_copier_config_set_sink_format format; const struct sof_ipc_ops *iops = sdev->ipc->ops; struct sof_ipc4_base_module_cfg *src_config; @@ -3865,7 +3872,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); @@ -3890,6 +3897,7 @@ static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev, static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *sroute) { + struct snd_soc_component *scomp = sroute->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; @@ -3905,7 +3913,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; @@ -3914,7 +3922,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", @@ -3926,7 +3934,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; @@ -3935,7 +3943,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, @@ -3948,14 +3956,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); @@ -3975,7 +3983,7 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0); 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; @@ -3991,6 +3999,7 @@ 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 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; @@ -4007,7 +4016,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); @@ -4034,7 +4043,7 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s ret = sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0); 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: @@ -4047,6 +4056,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; @@ -4055,7 +4065,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; } @@ -4117,7 +4127,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; } diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 02392b8bbea419..4a2178fe2a064b 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -363,7 +363,7 @@ static int sof_pcm_prepare(struct snd_soc_component *component, platform_params = &spcm->platform_params[substream->stream]; ret = sof_widget_list_setup(sdev, 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); @@ -777,7 +777,7 @@ static int sof_pcm_probe(struct snd_soc_component *component) /* 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, plat_data->tplg_filename); diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 795130aed80013..1b72cda7e4cfd0 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -29,22 +29,26 @@ 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) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(widget->scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_sof_route *sroute; @@ -58,6 +62,7 @@ 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); @@ -75,7 +80,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 +92,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 +115,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 +131,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,11 +145,12 @@ 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); @@ -169,12 +177,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 +193,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 +230,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,7 +261,7 @@ 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); @@ -265,8 +275,8 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc bool route_found = false; /* ignore routes involving virtual widgets in topology */ - 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 */ @@ -281,7 +291,8 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc } 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 +337,10 @@ 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_soc_dapm_widget *widget; struct snd_sof_route *sroute; struct snd_soc_dapm_path *p; @@ -440,7 +452,7 @@ sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widg const struct sof_ipc_tplg_widget_ops *widget_ops; struct snd_soc_dapm_path *p; - if (is_virtual_widget(sdev, widget, __func__)) + if (is_virtual_widget(widget, __func__)) return; if (!swidget) @@ -488,7 +500,7 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget struct snd_soc_dapm_path *p; int ret; - if (is_virtual_widget(sdev, widget, __func__)) + if (is_virtual_widget(widget, __func__)) return 0; if (!swidget) @@ -510,7 +522,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; } @@ -561,7 +573,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) @@ -641,7 +653,7 @@ static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_d struct snd_soc_dapm_path *p; int ret; - if (is_virtual_widget(sdev, widget, __func__)) + if (is_virtual_widget(widget, __func__)) return 0; if (swidget) { @@ -756,14 +768,14 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, sof_unprepare_widgets_in_path(sdev, 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; } } @@ -825,7 +837,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 +852,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 +860,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; diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index e97030f87dda72..16368b789fbe42 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -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,10 +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) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); const struct sof_token_info *token_list; const struct sof_topology_token *tokens; @@ -525,7 +527,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 +535,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 +551,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 +585,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; @@ -1274,7 +1276,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; @@ -1284,7 +1286,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; @@ -1309,7 +1311,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) { @@ -2059,7 +2061,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) { @@ -2074,7 +2076,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", @@ -2088,12 +2090,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) { @@ -2224,7 +2226,7 @@ static int sof_set_widget_pipeline(struct snd_sof_dev *sdev, struct snd_sof_pipe list_for_each_entry(scontrol, &sdev->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; @@ -2255,7 +2257,7 @@ static int sof_complete(struct snd_soc_component *scomp) list_for_each_entry(scontrol, &sdev->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; } @@ -2272,7 +2274,7 @@ 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; } @@ -2289,7 +2291,7 @@ static int sof_complete(struct snd_soc_component *scomp) 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; @@ -2304,14 +2306,14 @@ static int sof_complete(struct snd_soc_component *scomp) tplg_ops->tear_down_all_pipelines) { ret = tplg_ops->set_up_all_pipelines(sdev, 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); 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; } From bd331e6f62e300c9463564db04a59141e939c5cd Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 17 Jul 2026 10:30:20 +0300 Subject: [PATCH 029/100] ASoC: SOF: ipc4: pass component to pipeline state helper Change sof_ipc4_set_pipeline_state() to take the component instead of snd_sof_dev and update the Intel HDA call sites accordingly. This keeps the pipeline state path component-scoped and allows the use of the component device for logging. The snd_sof_dev is still derived locally for the IPC transmission. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/intel/hda-dai-ops.c | 17 +++++++++++++---- sound/soc/sof/ipc4-pcm.c | 15 +++++++++------ sound/soc/sof/ipc4-priv.h | 2 +- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index d46549aab535e6..63e7155f3c50f0 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -392,6 +392,8 @@ static bool hda_ipc4_all_spipe_dmas_running(struct snd_pcm_substream *substream, 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; @@ -399,6 +401,9 @@ static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cp struct snd_soc_dapm_widget *w; int ret = 0; + if (!component) + return -ENODEV; + w = snd_soc_dai_get_widget(cpu_dai, substream->stream); swidget = w->dobj.private; pipe_widget = swidget->spipe->pipe_widget; @@ -426,7 +431,7 @@ static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cp if (pipeline->state == SOF_IPC4_PIPE_PAUSED) break; - ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, + ret = sof_ipc4_set_pipeline_state(component, pipe_widget->instance_id, SOF_IPC4_PIPE_PAUSED); if (ret < 0) return ret; @@ -479,6 +484,7 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c 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; @@ -487,6 +493,9 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c int num_cpus = rtd->dai_link->num_cpus; int ret = 0; + if (!component) + return -ENODEV; + w = snd_soc_dai_get_widget(cpu_dai, substream->stream); swidget = w->dobj.private; pipe_widget = swidget->spipe->pipe_widget; @@ -510,7 +519,7 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c 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(component, pipe_widget->instance_id, SOF_IPC4_PIPE_PAUSED); if (ret < 0) return ret; @@ -518,7 +527,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(component, pipe_widget->instance_id, SOF_IPC4_PIPE_RUNNING); if (ret < 0) return ret; @@ -531,7 +540,7 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c !hda_ipc4_all_spipe_dmas_running(substream, swidget->spipe)) break; - ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, + ret = sof_ipc4_set_pipeline_state(component, pipe_widget->instance_id, SOF_IPC4_PIPE_RUNNING); if (ret < 0) return ret; diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 72cda1dc175826..bf481c4e9abcdf 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -64,16 +64,18 @@ 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 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(component, + trigger_list->pipeline_instance_ids[0], state); dev_dbg(sdev->dev, "Set pipelines %s to state %d%s", @@ -98,12 +100,13 @@ static int sof_ipc4_set_multi_pipeline_state(struct snd_sof_dev *sdev, u32 state return sof_ipc_tx_message_no_reply(sdev->ipc, &msg, ipc_size); } -int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 instance_id, u32 state) +int sof_ipc4_set_pipeline_state(struct snd_soc_component *component, u32 instance_id, u32 state) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct sof_ipc4_msg msg = {{ 0 }}; u32 primary; - dev_dbg(sdev->dev, "Set pipeline %d to state %d%s", instance_id, state, + dev_dbg(component->dev, "Set pipeline %d to state %d%s", instance_id, state, sof_ipc4_pipeline_state_str(state)); primary = state; @@ -514,7 +517,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 +560,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", diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index 31f7d68029ce71..99161a86e74b18 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 snd_soc_component *component, 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); From d9eb6a4fb1527982b2ec62f84270e758646afab8 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 29 May 2026 10:12:18 +0300 Subject: [PATCH 030/100] ASoC: SOF: remove unused component pointer from sdev Drop the snd_sof_dev::component field and the leftover assignment in sof_pcm_probe(). All users were converted to explicit component arguments or helper-based lookups in previous preparatory changes, so this pointer is no longer needed. No functional behavior change is intended. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/pcm.c | 2 -- sound/soc/sof/sof-priv.h | 1 - 2 files changed, 3 deletions(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 4a2178fe2a064b..20f692e1f67740 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -775,8 +775,6 @@ static int sof_pcm_probe(struct snd_soc_component *component) return ret; /* load the default topology */ - sdev->component = component; - tplg_filename = devm_kasprintf(component->dev, GFP_KERNEL, "%s/%s", plat_data->tplg_filename_prefix, diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index bfee7400fcf07d..aa06108ad77599 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -655,7 +655,6 @@ struct snd_sof_dev { struct list_head dai_list; struct list_head dai_link_list; struct list_head route_list; - struct snd_soc_component *component; u32 enabled_cores_mask; /* keep track of enabled cores */ bool led_present; From 8e0b4ae2ea67f2ecd1caa39ca8f0ff22110e8a8a Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 29 May 2026 12:10:00 +0300 Subject: [PATCH 031/100] ASoC: SOF: add audio instance bookkeeping Add per-component audio instance bookkeeping in SOF so component probe/remove can register and look up the associated instance. Use snd_sof_component_get_audio_instance() as the helper name for component-to-audio-instance lookup and keep snd_sof_component_get_sdev() as a thin wrapper around component drvdata. This provides infrastructure for the later drvdata migration away from the global sdev pointer. No functional behavior changes are intended here. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/core.c | 2 ++ sound/soc/sof/pcm.c | 20 ++++++++++++++--- sound/soc/sof/sof-audio.c | 47 +++++++++++++++++++++++++++++++++++++++ sound/soc/sof/sof-audio.h | 8 +++++++ sound/soc/sof/sof-priv.h | 10 +++++++++ 5 files changed, 84 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 25972d4f326a81..1922b8f50f63f3 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -690,10 +690,12 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) INIT_LIST_HEAD(&sdev->dai_list); INIT_LIST_HEAD(&sdev->dai_link_list); INIT_LIST_HEAD(&sdev->route_list); + INIT_LIST_HEAD(&sdev->audio_instance_list); INIT_LIST_HEAD(&sdev->ipc_client_list); INIT_LIST_HEAD(&sdev->client_ops_list); spin_lock_init(&sdev->ipc_lock); spin_lock_init(&sdev->hw_lock); + spin_lock_init(&sdev->audio_instance_list_lock); mutex_init(&sdev->power_state_access); mutex_init(&sdev->ipc_client_mutex); mutex_init(&sdev->client_ops_mutex); diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 20f692e1f67740..a4cbaf057b4fb0 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -763,16 +763,23 @@ static int sof_pcm_probe(struct snd_soc_component *component) { struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); struct snd_sof_pdata *plat_data = sdev->pdata; + struct snd_sof_audio_instance *instance; const char *tplg_filename; int ret; + instance = snd_sof_audio_instance_register(sdev, component); + if (!instance) + return -ENOMEM; + /* * 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; + } /* load the default topology */ tplg_filename = devm_kasprintf(component->dev, GFP_KERNEL, @@ -781,7 +788,7 @@ static int sof_pcm_probe(struct snd_soc_component *component) plat_data->tplg_filename); if (!tplg_filename) { ret = -ENOMEM; - goto pm_error; + goto out; } ret = snd_sof_load_topology(component, tplg_filename); @@ -789,7 +796,9 @@ 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: +out: + if (ret) + snd_sof_audio_instance_unregister(instance); pm_runtime_put_autosuspend(component->dev); return ret; @@ -797,6 +806,11 @@ static int sof_pcm_probe(struct snd_soc_component *component) static void sof_pcm_remove(struct snd_soc_component *component) { + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(component); + + if (instance) + snd_sof_audio_instance_unregister(instance); + /* remove topology */ snd_soc_tplg_component_remove(component); } diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 1b72cda7e4cfd0..a70b5e9ae36741 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -14,6 +14,53 @@ #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 snd_sof_audio_instance *instance; + + instance = devm_kzalloc(sdev->dev, sizeof(*instance), GFP_KERNEL); + if (!instance) + return NULL; + + instance->sdev = sdev; + instance->component = component; + + scoped_guard(spinlock, &sdev->audio_instance_list_lock) + list_add_tail_rcu(&instance->list, &sdev->audio_instance_list); + + return instance; +} + +void snd_sof_audio_instance_unregister(struct snd_sof_audio_instance *instance) +{ + struct snd_sof_dev *sdev = instance->sdev; + + scoped_guard(spinlock, &sdev->audio_instance_list_lock) + list_del_rcu(&instance->list); + synchronize_rcu(); +} + +struct snd_sof_audio_instance * +snd_sof_component_get_audio_instance(struct snd_soc_component *component) +{ + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + struct snd_sof_audio_instance *instance; + + if (!sdev) + return NULL; + + guard(rcu)(); + list_for_each_entry_rcu(instance, &sdev->audio_instance_list, list) { + if (instance->component == component) + return instance; + } + + return NULL; +} +EXPORT_SYMBOL(snd_sof_component_get_audio_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 diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index b2e2ca164c623a..b113d464c09225 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -648,6 +648,14 @@ struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp, struct snd_sof_pcm *snd_sof_find_spcm_comp_by_sdev(struct snd_sof_dev *sdev, unsigned int comp_id, int *direction); + +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); +struct snd_sof_audio_instance * +snd_sof_component_get_audio_instance(struct snd_soc_component *component); + 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, diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index aa06108ad77599..0b49a643c810e3 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -129,6 +129,13 @@ struct snd_soc_tplg_ops; struct snd_soc_component; struct snd_sof_pdata; +/* SOF audio instance container */ +struct snd_sof_audio_instance { + struct snd_sof_dev *sdev; + struct snd_soc_component *component; + struct list_head list; +}; + /** * struct snd_sof_platform_stream_params - platform dependent stream parameters * @phy_addr: Platform dependent address to be used, if @use_phy_addr @@ -655,6 +662,9 @@ struct snd_sof_dev { struct list_head dai_list; struct list_head dai_link_list; struct list_head route_list; + struct list_head audio_instance_list; + /* Protect audio_instance_list */ + spinlock_t audio_instance_list_lock; u32 enabled_cores_mask; /* keep track of enabled cores */ bool led_present; From aa5f0d3c633484daa8106d4ecd66f275f58d6cd6 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 29 May 2026 15:04:38 +0300 Subject: [PATCH 032/100] ASoC: SOF: move pipeline_list to audio instance Move pipeline_list from snd_sof_dev to snd_sof_audio_instance. Transitional patch to detach non core DSP data from sof_dev. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/core.c | 1 - sound/soc/sof/sof-audio.c | 1 + sound/soc/sof/sof-priv.h | 2 +- sound/soc/sof/topology.c | 12 ++++++++++-- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 1922b8f50f63f3..d03799078c2ed2 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -686,7 +686,6 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) 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); diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index a70b5e9ae36741..b9d856c7287474 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -26,6 +26,7 @@ snd_sof_audio_instance_register(struct snd_sof_dev *sdev, instance->sdev = sdev; instance->component = component; + INIT_LIST_HEAD(&instance->pipeline_list); scoped_guard(spinlock, &sdev->audio_instance_list_lock) list_add_tail_rcu(&instance->list, &sdev->audio_instance_list); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 0b49a643c810e3..e425d752a8c08d 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -133,6 +133,7 @@ struct snd_sof_pdata; struct snd_sof_audio_instance { struct snd_sof_dev *sdev; struct snd_soc_component *component; + struct list_head pipeline_list; struct list_head list; }; @@ -658,7 +659,6 @@ struct snd_sof_dev { 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; diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 16368b789fbe42..0a0e73165c0db0 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1460,6 +1460,7 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, struct snd_soc_tplg_dapm_widget *tw) { 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 = sof_ipc_get_ops(sdev, tplg); const struct sof_ipc_tplg_widget_ops *widget_ops; struct snd_soc_tplg_private *priv = &tw->priv; @@ -1469,6 +1470,9 @@ 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; @@ -1655,7 +1659,7 @@ 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; @@ -2244,12 +2248,16 @@ static int sof_set_widget_pipeline(struct snd_sof_dev *sdev, struct snd_sof_pipe static int sof_complete(struct snd_soc_component *scomp) { 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 = sof_ipc_get_ops(sdev, tplg); 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 */ @@ -2264,7 +2272,7 @@ static int sof_complete(struct snd_soc_component *scomp) } /* 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; From bd0ae307bf71508d8144ab85d0ba59233dca081c Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 29 May 2026 15:13:52 +0300 Subject: [PATCH 033/100] ASoC: SOF: move dai_list to audio instance The dai_list is topology-scoped state that belongs to the audio component instance rather than the global snd_sof_dev. Move it to snd_sof_audio_instance to properly scope DAI tracking per component. Update ipc3 and ipc4 SSP DAI config helpers to take the component pointer so they can look up the audio instance directly, removing the now-unused sdev variable in sof_ipc3_pcm_dai_link_fixup(). Signed-off-by: Peter Ujfalusi --- sound/soc/sof/core.c | 1 - sound/soc/sof/ipc3-pcm.c | 10 ++++++---- sound/soc/sof/ipc4-pcm.c | 12 +++++++----- sound/soc/sof/sof-audio.c | 5 +++-- sound/soc/sof/sof-priv.h | 2 +- sound/soc/sof/topology.c | 2 +- 6 files changed, 18 insertions(+), 14 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index d03799078c2ed2..2329c5b1b9bdb8 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -686,7 +686,6 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) INIT_LIST_HEAD(&sdev->pcm_list); INIT_LIST_HEAD(&sdev->kcontrol_list); INIT_LIST_HEAD(&sdev->widget_list); - INIT_LIST_HEAD(&sdev->dai_list); INIT_LIST_HEAD(&sdev->dai_link_list); INIT_LIST_HEAD(&sdev->route_list); INIT_LIST_HEAD(&sdev->audio_instance_list); diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c index cc88126952648a..f6a2a618471c0a 100644 --- a/sound/soc/sof/ipc3-pcm.c +++ b/sound/soc/sof/ipc3-pcm.c @@ -170,9 +170,12 @@ static int sof_ipc3_pcm_trigger(struct snd_soc_component *component, return sof_ipc_tx_message_no_reply(sdev->ipc, &stream, sizeof(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 +184,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 +208,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_sof_component_get_sdev(component); struct sof_dai_private_data *private; struct snd_soc_dpcm *dpcm; @@ -244,7 +246,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/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index bf481c4e9abcdf..d0296e14fe43d1 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -633,10 +633,12 @@ 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_dev *sdev = snd_sof_component_get_sdev(component); struct snd_sof_dai_link *slink; struct snd_sof_dai *dai; bool dai_link_found = false; @@ -681,17 +683,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; @@ -895,7 +897,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); diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index b9d856c7287474..aa1bd829313d1c 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -27,6 +27,7 @@ snd_sof_audio_instance_register(struct snd_sof_dev *sdev, instance->sdev = sdev; instance->component = component; INIT_LIST_HEAD(&instance->pipeline_list); + INIT_LIST_HEAD(&instance->dai_list); scoped_guard(spinlock, &sdev->audio_instance_list_lock) list_add_tail_rcu(&instance->list, &sdev->audio_instance_list); @@ -1115,10 +1116,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_sof_component_get_sdev(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; } diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index e425d752a8c08d..96c130c6e1f953 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -134,6 +134,7 @@ struct snd_sof_audio_instance { struct snd_sof_dev *sdev; struct snd_soc_component *component; struct list_head pipeline_list; + struct list_head dai_list; struct list_head list; }; @@ -659,7 +660,6 @@ struct snd_sof_dev { struct list_head pcm_list; struct list_head kcontrol_list; struct list_head widget_list; - struct list_head dai_list; struct list_head dai_link_list; struct list_head route_list; struct list_head audio_instance_list; diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 0a0e73165c0db0..f10acd18f06ac7 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1565,7 +1565,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: From a7d9d1ae2ee29d79c21aeae7608f4575b5b445b0 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 29 May 2026 15:18:54 +0300 Subject: [PATCH 034/100] ASoC: SOF: move dai_link_list to audio instance The dai_link_list is topology-scoped state that belongs to the audio component instance rather than the global snd_sof_dev. Move it to snd_sof_audio_instance to properly scope DAI link tracking per component. The two IPC4 users look a DAI link up by comparing the link name. The link names come from the topology and they are only unique within a topology, so searching the list of every instance would let a name which is used by two topologies match the link of another card. Both call sites reach the DAI link through a DAI which already carries the component it was created for, so search only the DAI link list of the audio instance owning that component. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/core.c | 1 - sound/soc/sof/ipc3-topology.c | 4 ++-- sound/soc/sof/ipc4-pcm.c | 3 +-- sound/soc/sof/ipc4-topology.c | 8 ++++++-- sound/soc/sof/sof-audio.c | 1 + sound/soc/sof/sof-priv.h | 2 +- sound/soc/sof/topology.c | 3 ++- 7 files changed, 13 insertions(+), 9 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 2329c5b1b9bdb8..7fedc9ddd2c314 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -686,7 +686,6 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) INIT_LIST_HEAD(&sdev->pcm_list); INIT_LIST_HEAD(&sdev->kcontrol_list); INIT_LIST_HEAD(&sdev->widget_list); - INIT_LIST_HEAD(&sdev->dai_link_list); INIT_LIST_HEAD(&sdev->route_list); INIT_LIST_HEAD(&sdev->audio_instance_list); INIT_LIST_HEAD(&sdev->ipc_client_list); diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index df3b6e95a814a3..a38f625e48699d 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -1600,7 +1600,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_sof_component_get_sdev(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; @@ -1656,7 +1656,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; diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index d0296e14fe43d1..04e0b222de0ac5 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -638,7 +638,6 @@ static int ipc4_ssp_dai_config_pcm_params_match(struct snd_soc_component *compon 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 snd_sof_dai_link *slink; struct snd_sof_dai *dai; bool dai_link_found = false; @@ -646,7 +645,7 @@ static int ipc4_ssp_dai_config_pcm_params_match(struct snd_soc_component *compon 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; diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 2f6bee559586f3..94e0450926c561 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -1802,6 +1802,8 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget) static int snd_sof_get_hw_config_params(struct snd_sof_dev *sdev, 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; @@ -1809,7 +1811,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; @@ -4206,6 +4208,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; @@ -4216,7 +4220,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; diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index aa1bd829313d1c..f7d98e4ce72a7e 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -28,6 +28,7 @@ snd_sof_audio_instance_register(struct snd_sof_dev *sdev, instance->component = component; INIT_LIST_HEAD(&instance->pipeline_list); INIT_LIST_HEAD(&instance->dai_list); + INIT_LIST_HEAD(&instance->dai_link_list); scoped_guard(spinlock, &sdev->audio_instance_list_lock) list_add_tail_rcu(&instance->list, &sdev->audio_instance_list); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 96c130c6e1f953..4ec5037b2d608d 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -135,6 +135,7 @@ struct snd_sof_audio_instance { struct snd_soc_component *component; struct list_head pipeline_list; struct list_head dai_list; + struct list_head dai_link_list; struct list_head list; }; @@ -660,7 +661,6 @@ struct snd_sof_dev { struct list_head pcm_list; struct list_head kcontrol_list; struct list_head widget_list; - struct list_head dai_link_list; struct list_head route_list; struct list_head audio_instance_list; /* Protect audio_instance_list */ diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index f10acd18f06ac7..495f0ba0e8ca45 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1939,6 +1939,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ struct snd_soc_tplg_link_config *cfg) { 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 = sof_ipc_get_ops(sdev, tplg); struct snd_soc_tplg_private *private = &cfg->priv; const struct sof_token_info *token_list; @@ -2110,7 +2111,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; From 6bc47b109dfdcacf78dc395451481ef7d6a49a60 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 29 May 2026 15:24:50 +0300 Subject: [PATCH 035/100] ASoC: SOF: move route_list to audio instance The route_list is topology-scoped state that belongs to the audio component instance rather than the global snd_sof_dev. Move it to snd_sof_audio_instance to properly scope route tracking per component. Callers that have a snd_soc_component available use snd_sof_component_get_audio_instance(), while global operations iterate all instances in the audio_instance_list. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/core.c | 1 - sound/soc/sof/ipc3-topology.c | 6 ++++-- sound/soc/sof/sof-audio.c | 12 +++++++++--- sound/soc/sof/sof-audio.h | 4 ++++ sound/soc/sof/sof-priv.h | 2 +- sound/soc/sof/topology.c | 4 ++-- 6 files changed, 20 insertions(+), 9 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 7fedc9ddd2c314..2cddef244902b1 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -686,7 +686,6 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) INIT_LIST_HEAD(&sdev->pcm_list); INIT_LIST_HEAD(&sdev->kcontrol_list); INIT_LIST_HEAD(&sdev->widget_list); - INIT_LIST_HEAD(&sdev->route_list); INIT_LIST_HEAD(&sdev->audio_instance_list); INIT_LIST_HEAD(&sdev->ipc_client_list); INIT_LIST_HEAD(&sdev->client_ops_list); diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index a38f625e48699d..b93e39c5c70639 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -2337,6 +2337,7 @@ static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget static int sof_ipc3_set_up_all_pipelines(struct snd_sof_dev *sdev, bool verify) { struct sof_ipc_fw_version *v = &sdev->fw_ready.version; + struct snd_sof_audio_instance *instance; struct snd_sof_widget *swidget; struct snd_sof_route *sroute; int ret; @@ -2384,7 +2385,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) { + for_each_sroute_in_instances(sroute, sdev, instance) { /* only set up routes belonging to static pipelines */ if (!verify && (sroute->src_widget->dynamic_pipeline_widget || sroute->sink_widget->dynamic_pipeline_widget)) @@ -2509,6 +2510,7 @@ static int sof_ipc3_free_widgets_in_list(struct snd_sof_dev *sdev, bool include_ static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verify) { struct sof_ipc_fw_version *v = &sdev->fw_ready.version; + struct snd_sof_audio_instance *instance; struct snd_sof_widget *swidget; struct snd_sof_route *sroute; bool dyn_widgets = false; @@ -2546,7 +2548,7 @@ static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif if (ret < 0) return ret; - list_for_each_entry(sroute, &sdev->route_list, list) + for_each_sroute_in_instances(sroute, sdev, instance) sroute->setup = false; /* diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index f7d98e4ce72a7e..7b031c03f54223 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -29,6 +29,7 @@ snd_sof_audio_instance_register(struct snd_sof_dev *sdev, 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); scoped_guard(spinlock, &sdev->audio_instance_list_lock) list_add_tail_rcu(&instance->list, &sdev->audio_instance_list); @@ -99,10 +100,12 @@ static bool is_virtual_widget(struct snd_soc_dapm_widget *widget, const char *fu static void sof_reset_route_setup_status(struct snd_sof_widget *widget) { 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 = sof_ipc_get_ops(sdev, tplg); 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); @@ -321,6 +324,8 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc 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; + struct snd_sof_audio_instance *instance = + snd_sof_component_get_audio_instance(src_widget->scomp); struct snd_sof_route *sroute; bool route_found = false; @@ -334,7 +339,7 @@ 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; @@ -391,6 +396,7 @@ 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; @@ -445,7 +451,7 @@ static int sof_setup_pipeline_connections(struct snd_soc_dapm_widget_list *list, * 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) diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index b113d464c09225..9a8c8d17937073 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -47,6 +47,10 @@ (id) == snd_soc_dapm_decoder || \ (id) == snd_soc_dapm_encoder) +#define for_each_sroute_in_instances(sroute, sdev, instance) \ + list_for_each_entry(instance, &(sdev)->audio_instance_list, list) \ + list_for_each_entry(sroute, &(instance)->route_list, list) + #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 diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 4ec5037b2d608d..057d9f30ca2923 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -136,6 +136,7 @@ struct snd_sof_audio_instance { struct list_head pipeline_list; struct list_head dai_list; struct list_head dai_link_list; + struct list_head route_list; struct list_head list; }; @@ -661,7 +662,6 @@ struct snd_sof_dev { struct list_head pcm_list; struct list_head kcontrol_list; struct list_head widget_list; - struct list_head route_list; struct list_head audio_instance_list; /* Protect audio_instance_list */ spinlock_t audio_instance_list_lock; diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 495f0ba0e8ca45..7342292ca1cb9c 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2144,7 +2144,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_sof_component_get_sdev(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; @@ -2202,7 +2202,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: From b2a6ec0a5d6e0aee2849c57f4f3b7095aa6ed3a3 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 29 May 2026 15:32:02 +0300 Subject: [PATCH 036/100] ASoC: SOF: move pcm_list to audio instance The pcm_list is topology-scoped state that belongs to the audio component instance rather than the global snd_sof_dev. Move it to snd_sof_audio_instance to properly scope PCM tracking per component. Move the audio instance function declarations before the snd_sof_find_spcm_dai() inline function in sof-audio.h since the inline now calls snd_sof_component_get_audio_instance(). Signed-off-by: Peter Ujfalusi --- sound/soc/sof/core.c | 1 - sound/soc/sof/intel/hda-dsp.c | 3 ++- sound/soc/sof/pcm.c | 3 ++- sound/soc/sof/sof-audio.c | 14 +++++++++----- sound/soc/sof/sof-audio.h | 22 +++++++++++++--------- sound/soc/sof/sof-priv.h | 2 +- sound/soc/sof/topology.c | 3 ++- 7 files changed, 29 insertions(+), 19 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 2cddef244902b1..40f211919321dc 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -683,7 +683,6 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) 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->audio_instance_list); diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 3576bd5cb3772d..34f6eb3630e8ed 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -529,12 +529,13 @@ static int hda_dsp_update_d0i3c_register(struct snd_sof_dev *sdev, u8 value) */ static bool hda_dsp_d0i3_streaming_applicable(struct snd_sof_dev *sdev) { + struct snd_sof_audio_instance *instance; 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_spcm_in_instances(spcm, sdev, instance) { for_each_pcm_streams(dir) { substream = spcm->stream[dir].substream; if (!substream || !substream->runtime) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index a4cbaf057b4fb0..67ba7cea1ba25b 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -262,11 +262,12 @@ static int sof_pcm_stream_free(struct snd_sof_dev *sdev, int sof_pcm_free_all_streams(struct snd_sof_dev *sdev) { + struct snd_sof_audio_instance *instance; struct snd_pcm_substream *substream; struct snd_sof_pcm *spcm; int dir, ret; - list_for_each_entry(spcm, &sdev->pcm_list, list) { + for_each_spcm_in_instances(spcm, sdev, instance) { for_each_pcm_streams(dir) { substream = spcm->stream[dir].substream; diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 7b031c03f54223..a003e3a31c2167 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -30,6 +30,7 @@ snd_sof_audio_instance_register(struct snd_sof_dev *sdev, 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); scoped_guard(spinlock, &sdev->audio_instance_list_lock) list_add_tail_rcu(&instance->list, &sdev->audio_instance_list); @@ -971,12 +972,13 @@ int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int */ bool snd_sof_dsp_state_is_d0i3_compatible(struct snd_sof_dev *sdev) { + struct snd_sof_audio_instance *instance; struct snd_pcm_substream *substream; struct snd_sof_pcm *spcm; bool d0i3_compatible_active = false; int dir; - list_for_each_entry(spcm, &sdev->pcm_list, list) { + for_each_spcm_in_instances(spcm, sdev, instance) { for_each_pcm_streams(dir) { substream = spcm->stream[dir].substream; if (!substream || !substream->runtime) @@ -1015,9 +1017,10 @@ EXPORT_SYMBOL(snd_sof_dsp_state_is_d0i3_compatible); bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev) { + struct snd_sof_audio_instance *instance; struct snd_sof_pcm *spcm; - list_for_each_entry(spcm, &sdev->pcm_list, list) { + for_each_spcm_in_instances(spcm, sdev, instance) { if (spcm->stream[SNDRV_PCM_STREAM_PLAYBACK].suspend_ignored || spcm->stream[SNDRV_PCM_STREAM_CAPTURE].suspend_ignored) return true; @@ -1033,10 +1036,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_sof_component_get_sdev(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; @@ -1068,10 +1071,11 @@ struct snd_sof_pcm *snd_sof_find_spcm_comp_by_sdev(struct snd_sof_dev *sdev, unsigned int comp_id, int *direction) { + struct snd_sof_audio_instance *instance; struct snd_sof_pcm *spcm; int dir; - list_for_each_entry(spcm, &sdev->pcm_list, list) { + for_each_spcm_in_instances(spcm, sdev, instance) { for_each_pcm_streams(dir) { if (spcm->stream[dir].comp_id == comp_id) { *direction = dir; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 9a8c8d17937073..15f48f4aa6e80b 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -51,6 +51,10 @@ list_for_each_entry(instance, &(sdev)->audio_instance_list, list) \ list_for_each_entry(sroute, &(instance)->route_list, list) +#define for_each_spcm_in_instances(spcm, sdev, instance) \ + list_for_each_entry(instance, &(sdev)->audio_instance_list, list) \ + list_for_each_entry(spcm, &(instance)->pcm_list, list) + #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 @@ -629,14 +633,21 @@ snd_sof_component_get_sdev(struct snd_soc_component *scomp) return snd_soc_component_get_drvdata(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); +struct snd_sof_audio_instance * +snd_sof_component_get_audio_instance(struct snd_soc_component *component); + 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_sof_component_get_sdev(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; } @@ -653,13 +664,6 @@ struct snd_sof_pcm *snd_sof_find_spcm_comp_by_sdev(struct snd_sof_dev *sdev, unsigned int comp_id, int *direction); -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); -struct snd_sof_audio_instance * -snd_sof_component_get_audio_instance(struct snd_soc_component *component); - 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, diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 057d9f30ca2923..57ed4b8c0ec3d5 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -137,6 +137,7 @@ struct snd_sof_audio_instance { struct list_head dai_list; struct list_head dai_link_list; struct list_head route_list; + struct list_head pcm_list; struct list_head list; }; @@ -659,7 +660,6 @@ 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 audio_instance_list; diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 7342292ca1cb9c..9f7d5269e2add2 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1793,6 +1793,7 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index, struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai) { 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 = sof_ipc_get_ops(sdev, pcm); struct snd_soc_tplg_stream_caps *caps; struct snd_soc_tplg_private *private = &pcm->priv; @@ -1831,7 +1832,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, From fa03f09eb0150bf58fa776000fd91ee698489f1f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 29 May 2026 15:51:19 +0300 Subject: [PATCH 037/100] ASoC: SOF: move kcontrol_list to audio instance Move kcontrol_list from the global snd_sof_dev to the per-component snd_sof_audio_instance structure. This is part of the ongoing effort to allow multiple audio components to coexist without sharing topology state. Update all call sites across topology.c, ipc3-control.c, and ipc4-control.c to access the kcontrol_list through the audio instance. Functions that have a snd_sof_widget available derive the instance from swidget->scomp via snd_sof_component_get_audio_instance(). Signed-off-by: Peter Ujfalusi --- sound/soc/sof/core.c | 1 - sound/soc/sof/ipc3-control.c | 4 +++- sound/soc/sof/ipc3-topology.c | 4 ++-- sound/soc/sof/ipc4-control.c | 8 ++++++-- sound/soc/sof/ipc4-topology.c | 4 ++-- sound/soc/sof/sof-audio.c | 1 + sound/soc/sof/sof-priv.h | 2 +- sound/soc/sof/topology.c | 10 ++++++---- 8 files changed, 21 insertions(+), 13 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 40f211919321dc..5b10a572c6c49f 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -683,7 +683,6 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) if (ret) return ret; - INIT_LIST_HEAD(&sdev->kcontrol_list); INIT_LIST_HEAD(&sdev->widget_list); INIT_LIST_HEAD(&sdev->audio_instance_list); INIT_LIST_HEAD(&sdev->ipc_client_list); diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index 04ae9e74a6fc1a..b7818979fd8c22 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -713,11 +713,13 @@ 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); diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index b93e39c5c70639..29a5f6c8ac0e1a 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -757,7 +757,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_sof_component_get_sdev(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 +790,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; diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c index ae4a2f5e4cd52a..303818ab3ef389 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -791,6 +791,7 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, 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; @@ -838,7 +839,8 @@ static void sof_ipc4_control_update(struct snd_sof_dev *sdev, void *ipc_message) /* 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; @@ -957,10 +959,12 @@ 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: diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 94e0450926c561..c2f1a57d83d4b4 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -610,12 +610,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_sof_component_get_sdev(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; diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index a003e3a31c2167..8829b17b7a3416 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -31,6 +31,7 @@ snd_sof_audio_instance_register(struct snd_sof_dev *sdev, 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); scoped_guard(spinlock, &sdev->audio_instance_list_lock) list_add_tail_rcu(&instance->list, &sdev->audio_instance_list); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 57ed4b8c0ec3d5..1227cf40b274ae 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -138,6 +138,7 @@ struct snd_sof_audio_instance { struct list_head dai_link_list; struct list_head route_list; struct list_head pcm_list; + struct list_head kcontrol_list; struct list_head list; }; @@ -660,7 +661,6 @@ struct snd_sof_dev { /* topology */ struct snd_soc_tplg_ops *tplg_ops; - struct list_head kcontrol_list; struct list_head widget_list; struct list_head audio_instance_list; /* Protect audio_instance_list */ diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 9f7d5269e2add2..f97270d0a5080a 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -972,7 +972,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_sof_component_get_sdev(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; @@ -1040,7 +1040,7 @@ 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; } @@ -2224,12 +2224,14 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, static int sof_set_widget_pipeline(struct snd_sof_dev *sdev, 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(swidget->scomp->dev, @@ -2264,7 +2266,7 @@ static int sof_complete(struct snd_soc_component *scomp) /* 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(scomp->dev, "failed updating IPC struct for control %s\n", From a617c9d8c134d5487d7a926bec476513e8ce1f31 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 29 May 2026 16:03:16 +0300 Subject: [PATCH 038/100] ASoC: SOF: move widget_list to audio instance Move widget_list from the global snd_sof_dev to the per-component snd_sof_audio_instance structure. This completes the migration of all topology list state to the audio instance, allowing multiple audio components to coexist without sharing topology state. Update all call sites across topology.c, ipc3-topology.c, ipc3-control.c, ipc4-topology.c, ipc4-compress.c, pcm.c and sof-audio.c to access the widget_list through the audio instance. snd_sof_find_swidget_by_comp_id() takes the component instead of the SOF device and searches only the widget list of the audio instance owning it. Both callers, the PCM hw_params and the IPC4 compress set_params, are looking for the host widget of a PCM they have already resolved within their own component, so a wider search could only return a widget which does not belong to the stream being configured. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/core.c | 1 - sound/soc/sof/ipc3-control.c | 7 +++++-- sound/soc/sof/ipc3-topology.c | 12 +++++++----- sound/soc/sof/ipc4-compress.c | 6 ++++-- sound/soc/sof/ipc4-control.c | 15 ++++++++------- sound/soc/sof/ipc4-topology.c | 9 ++++++--- sound/soc/sof/pcm.c | 10 +++++++--- sound/soc/sof/sof-audio.c | 9 +++++---- sound/soc/sof/sof-audio.h | 6 +++++- sound/soc/sof/sof-priv.h | 2 +- sound/soc/sof/topology.c | 9 +++++---- 11 files changed, 53 insertions(+), 33 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 5b10a572c6c49f..d4af97b70b0c3b 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -683,7 +683,6 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) if (ret) return ret; - INIT_LIST_HEAD(&sdev->widget_list); INIT_LIST_HEAD(&sdev->audio_instance_list); INIT_LIST_HEAD(&sdev->ipc_client_list); INIT_LIST_HEAD(&sdev->client_ops_list); diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index b7818979fd8c22..2b4b874f7c6504 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -15,6 +15,8 @@ static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool set, bool lock) { + 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; @@ -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; @@ -589,6 +591,7 @@ 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) { struct sof_ipc_ctrl_data *cdata = ipc_control_message; + struct snd_sof_audio_instance *instance; struct snd_soc_dapm_widget *widget; struct snd_sof_control *scontrol; struct snd_sof_widget *swidget; @@ -607,7 +610,7 @@ static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_ } /* Find the swidget first */ - list_for_each_entry(swidget, &sdev->widget_list, list) { + for_each_swidget_in_instances(swidget, sdev, instance) { if (swidget->comp_id == cdata->comp_id) { found = true; break; diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index 29a5f6c8ac0e1a..1d9cb9ebec167a 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -2343,7 +2343,7 @@ static int sof_ipc3_set_up_all_pipelines(struct snd_sof_dev *sdev, bool verify) int ret; /* restore pipeline components */ - list_for_each_entry(swidget, &sdev->widget_list, list) { + for_each_swidget_in_instances(swidget, sdev, instance) { /* only set up the widgets belonging to static pipelines */ if (!verify && swidget->dynamic_pipeline_widget) continue; @@ -2408,7 +2408,7 @@ static int sof_ipc3_set_up_all_pipelines(struct snd_sof_dev *sdev, bool verify) } /* complete pipeline */ - list_for_each_entry(swidget, &sdev->widget_list, list) { + for_each_swidget_in_instances(swidget, sdev, instance) { switch (swidget->id) { case snd_soc_dapm_scheduler: /* only complete static pipelines */ @@ -2439,6 +2439,7 @@ static int sof_ipc3_set_up_all_pipelines(struct snd_sof_dev *sdev, bool verify) */ static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev) { + struct snd_sof_audio_instance *instance; struct snd_sof_widget *swidget; int ret; @@ -2455,7 +2456,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) + for_each_swidget_in_instances(swidget, sdev, instance) if (WIDGET_IS_DAI(swidget->id) && swidget->use_count == 1) { ret = sof_widget_free(sdev, swidget); if (ret < 0) @@ -2468,11 +2469,12 @@ static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev) static int sof_ipc3_free_widgets_in_list(struct snd_sof_dev *sdev, bool include_scheduler, bool *dyn_widgets, bool verify) { + struct snd_sof_audio_instance *instance; 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) { + for_each_swidget_in_instances(swidget, sdev, instance) { if (swidget->dynamic_pipeline_widget) { *dyn_widgets = true; continue; @@ -2556,7 +2558,7 @@ 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) { + for_each_swidget_in_instances(swidget, sdev, instance) { if (swidget->use_count != 0) { dev_err(swidget->scomp->dev, "%s: widget %s is still in use: count %d\n", diff --git a/sound/soc/sof/ipc4-compress.c b/sound/soc/sof/ipc4-compress.c index 136e016efbd1b2..3966812ed61904 100644 --- a/sound/soc/sof/ipc4-compress.c +++ b/sound/soc/sof/ipc4-compress.c @@ -336,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); @@ -720,6 +720,7 @@ void sof_ipc4_compr_drain_done(struct snd_sof_dev *sdev, 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; @@ -737,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; diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c index 303818ab3ef389..de485f382b4d06 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -17,6 +17,7 @@ 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_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; @@ -24,7 +25,7 @@ static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, 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; @@ -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_sof_component_get_sdev(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; @@ -382,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_sof_component_get_sdev(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; @@ -402,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; @@ -441,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_sof_component_get_sdev(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; @@ -461,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; diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index c2f1a57d83d4b4..b30264d9b3fe7e 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -221,9 +221,10 @@ static const struct sof_token_info ipc4_token_list[SOF_TOKEN_COUNT] = { struct snd_sof_widget *sof_ipc4_find_swidget_by_ids(struct snd_sof_dev *sdev, u32 module_id, int instance_id) { + struct snd_sof_audio_instance *instance; struct snd_sof_widget *swidget; - list_for_each_entry(swidget, &sdev->widget_list, list) { + for_each_swidget_in_instances(swidget, sdev, instance) { struct sof_ipc4_fw_module *fw_module = swidget->module_info; /* Only active module instances have valid instance_id */ @@ -809,6 +810,7 @@ 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_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; @@ -893,7 +895,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 || @@ -2214,6 +2216,7 @@ _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_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; @@ -2535,7 +2538,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 || diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 67ba7cea1ba25b..cb87b4664fd5a5 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -100,12 +100,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; } @@ -177,7 +181,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); diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 8829b17b7a3416..1d07939f682ffd 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -32,6 +32,7 @@ snd_sof_audio_instance_register(struct snd_sof_dev *sdev, INIT_LIST_HEAD(&instance->route_list); INIT_LIST_HEAD(&instance->pcm_list); INIT_LIST_HEAD(&instance->kcontrol_list); + INIT_LIST_HEAD(&instance->widget_list); scoped_guard(spinlock, &sdev->audio_instance_list_lock) list_add_tail_rcu(&instance->list, &sdev->audio_instance_list); @@ -1091,10 +1092,10 @@ struct snd_sof_pcm *snd_sof_find_spcm_comp_by_sdev(struct snd_sof_dev *sdev, struct snd_sof_widget *snd_sof_find_swidget(struct snd_soc_component *scomp, const char *name) { - 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_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; } @@ -1107,7 +1108,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_sof_component_get_sdev(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; @@ -1116,7 +1117,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; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 15f48f4aa6e80b..9021b8b430cf14 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -55,6 +55,10 @@ list_for_each_entry(instance, &(sdev)->audio_instance_list, list) \ list_for_each_entry(spcm, &(instance)->pcm_list, list) +#define for_each_swidget_in_instances(swidget, sdev, instance) \ + list_for_each_entry(instance, &(sdev)->audio_instance_list, list) \ + list_for_each_entry(swidget, &(instance)->widget_list, list) + #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 @@ -670,7 +674,7 @@ int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm 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 diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 1227cf40b274ae..cf29741b739fac 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -139,6 +139,7 @@ struct snd_sof_audio_instance { struct list_head route_list; struct list_head pcm_list; struct list_head kcontrol_list; + struct list_head widget_list; struct list_head list; }; @@ -661,7 +662,6 @@ struct snd_sof_dev { /* topology */ struct snd_soc_tplg_ops *tplg_ops; - struct list_head widget_list; struct list_head audio_instance_list; /* Protect audio_instance_list */ spinlock_t audio_instance_list_lock; diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index f97270d0a5080a..a1d7d18c8bdebc 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1663,7 +1663,7 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, } w->dobj.private = swidget; - list_add(&swidget->list, &sdev->widget_list); + list_add(&swidget->list, &instance->widget_list); return ret; free: kfree(swidget->private); @@ -2293,7 +2293,7 @@ static int sof_complete(struct snd_soc_component *scomp) } /* 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); @@ -2458,7 +2458,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_sof_component_get_sdev(scomp); + struct snd_sof_audio_instance *instance = + snd_sof_component_get_audio_instance(scomp); struct snd_sof_widget *swidget; struct snd_sof_dai *sdai; @@ -2493,7 +2494,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; From 2f5323f22cd216588d9cde125324f12e62ea6fae Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 27 Jul 2026 14:19:56 +0300 Subject: [PATCH 039/100] ASoC: SOF: move led_present to audio instance The led_present flag tracks whether the loaded topology defined any kcontrol with LED control. Both the producer in sof_control_load_volume() and the consumer in snd_sof_load_topology() operate on a single component's topology, so the flag describes the audio instance rather than the DSP device. Move it to struct snd_sof_audio_instance. This keeps the flag correct once a single DSP backs multiple components, where an instance without LED controls must not request the LED layer because another instance happens to have them. The sdev in sof_control_load_volume() was only used for this flag, so it is replaced by the audio instance. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-priv.h | 2 +- sound/soc/sof/topology.c | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index cf29741b739fac..40b68941a4815e 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -140,6 +140,7 @@ struct snd_sof_audio_instance { struct list_head pcm_list; struct list_head kcontrol_list; struct list_head widget_list; + bool led_present; struct list_head list; }; @@ -666,7 +667,6 @@ struct snd_sof_dev { /* Protect audio_instance_list */ spinlock_t audio_instance_list_lock; u32 enabled_cores_mask; /* keep track of enabled cores */ - bool led_present; /* FW configuration */ struct sof_ipc_window *info_window; diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index a1d7d18c8bdebc..ef2a599fd922ae 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -844,7 +844,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_sof_component_get_sdev(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]; @@ -903,7 +903,7 @@ 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", @@ -2558,6 +2558,7 @@ static const struct snd_soc_tplg_ops sof_dspless_tplg_ops = { int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) { + 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_pdata *sof_pdata = sdev->pdata; const char *tplg_filename_prefix = sof_pdata->tplg_filename_prefix; @@ -2702,7 +2703,7 @@ 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); From b1b4c8d1d7474071903fbd2ae1e99b80ed93fcf7 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 24 Jul 2026 11:50:25 +0300 Subject: [PATCH 040/100] ASoC: SOF: pass component to widget list management The widget list management helpers - sof_widget_list_setup(), sof_widget_list_prepare(), sof_widget_list_unprepare() and sof_widget_list_free() - along with the internal walker sof_walk_widgets_in_order() and the per-path helpers took the global struct snd_sof_dev as their context. Switch these to take the struct snd_soc_component of the stream and derive the snd_sof_dev locally only where the DSP device is still needed (core power management and IPC topology ops). All call sites already have the component available, either directly or via spcm->scomp. This keeps the widget management aligned with the per-component audio instance that owns the widget list and removes the reliance on the global SOF device from this path. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/ipc4-compress.c | 6 ++-- sound/soc/sof/pcm.c | 8 ++--- sound/soc/sof/sof-audio.c | 64 ++++++++++++++++++++--------------- sound/soc/sof/sof-audio.h | 9 ++--- 4 files changed, 49 insertions(+), 38 deletions(-) diff --git a/sound/soc/sof/ipc4-compress.c b/sound/soc/sof/ipc4-compress.c index 3966812ed61904..785c3fc629224e 100644 --- a/sound/soc/sof/ipc4-compress.c +++ b/sound/soc/sof/ipc4-compress.c @@ -132,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; @@ -158,7 +158,7 @@ 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); @@ -468,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; diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index cb87b4664fd5a5..5057cf9d378229 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -88,7 +88,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; @@ -252,7 +252,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); @@ -313,7 +313,7 @@ static int sof_pcm_hw_free(struct snd_soc_component *component, 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); @@ -366,7 +366,7 @@ 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(component->dev, "failed widget list set up for pcm %d dir %u\n", le32_to_cpu(spcm->pcm.pcm_id), dir); diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 1d07939f682ffd..60d0304541b2ac 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -503,9 +503,11 @@ static int sof_setup_pipeline_connections(struct snd_soc_dapm_widget_list *list, } 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) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); 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_widget_ops *widget_ops; @@ -540,19 +542,21 @@ 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) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); 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_widget_ops *widget_ops; @@ -595,7 +599,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; @@ -618,11 +622,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; @@ -661,7 +666,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; @@ -671,11 +676,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); } @@ -702,10 +707,12 @@ 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; @@ -759,7 +766,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) @@ -773,7 +780,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) @@ -799,11 +806,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: @@ -819,12 +826,13 @@ 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(spcm->scomp->dev, "Invalid widget op %d\n", op); @@ -845,7 +853,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) @@ -854,26 +862,28 @@ 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) { + struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; struct snd_soc_dapm_widget *widget; @@ -884,10 +894,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; } @@ -942,14 +952,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; @@ -960,7 +970,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; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 9021b8b430cf14..c9e980504d1b21 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -726,16 +726,17 @@ 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); From 01c7733b57f03e023f75ce92497a23838d43ddcb Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 2 Jun 2026 16:44:26 +0300 Subject: [PATCH 041/100] ASoC: SOF: introduce struct sof_audio_ops Define a new sof_audio_ops structure for component-centric audio stream callbacks. These ops handle platform-specific stream management (host DMA, stream allocation, etc.) and receive snd_soc_component to identify the audio instance rather than snd_sof_dev. Add the audio_ops pointer to snd_sof_audio_instance, snd_sof_dev, and sof_dev_desc. The audio_ops flow is: sof_dev_desc provides it at probe time, core copies it to sdev, and audio_instance_register copies it from sdev to each instance. An audio instance without audio_ops cannot serve a single stream, so refuse to register one and let the caller fail instead of leaving a NULL pointer for every dispatch site to re-check. This is preparatory work for decoupling audio stream operations from snd_sof_dsp_ops, enabling audio to become a standalone sof-client. No functional change. Signed-off-by: Peter Ujfalusi --- include/sound/sof.h | 2 ++ sound/soc/sof/core.c | 1 + sound/soc/sof/sof-audio.c | 6 +++++ sound/soc/sof/sof-priv.h | 55 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+) diff --git a/include/sound/sof.h b/include/sound/sof.h index 69e85a87c0fad1..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 @@ -187,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/sound/soc/sof/core.c b/sound/soc/sof/core.c index d4af97b70b0c3b..b56b187cb872f7 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -684,6 +684,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) return ret; INIT_LIST_HEAD(&sdev->audio_instance_list); + sdev->audio_ops = plat_data->desc->audio_ops; INIT_LIST_HEAD(&sdev->ipc_client_list); INIT_LIST_HEAD(&sdev->client_ops_list); spin_lock_init(&sdev->ipc_lock); diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 60d0304541b2ac..ec8f1faeeb3b7f 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -20,12 +20,18 @@ snd_sof_audio_instance_register(struct snd_sof_dev *sdev, { struct snd_sof_audio_instance *instance; + 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; INIT_LIST_HEAD(&instance->pipeline_list); INIT_LIST_HEAD(&instance->dai_list); INIT_LIST_HEAD(&instance->dai_link_list); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 40b68941a4815e..3d5972f72023f9 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -128,11 +128,65 @@ struct snd_sof_debugfs_map; struct snd_soc_tplg_ops; struct snd_soc_component; 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_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_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); + + /* 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; struct list_head pipeline_list; struct list_head dai_list; struct list_head dai_link_list; @@ -663,6 +717,7 @@ struct snd_sof_dev { /* topology */ struct snd_soc_tplg_ops *tplg_ops; + const struct sof_audio_ops *audio_ops; struct list_head audio_instance_list; /* Protect audio_instance_list */ spinlock_t audio_instance_list_lock; From 0f0235a3ed57234cdbebd77b8bf8dc318ad94f94 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 17 Jul 2026 13:08:53 +0300 Subject: [PATCH 042/100] ASoC: SOF: audio: move PCM/tplg ops to audio instance Remove PCM and topology ops from struct sof_ipc_ops and store them in snd_sof_audio_instance instead. Resolve the per-instance ops from the selected global IPC type during audio instance registration and switch the audio paths to use instance- scoped getters for PCM/topology ops lookup. This keeps the IPC transport/protocol ops global while moving the audio PCM/topology binding to the audio instance where it belongs. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/control.c | 32 +++++-------- sound/soc/sof/intel/hda-dai.c | 2 +- sound/soc/sof/ipc.c | 10 ---- sound/soc/sof/ipc3.c | 4 +- sound/soc/sof/ipc4-compress.c | 7 ++- sound/soc/sof/ipc4.c | 4 +- sound/soc/sof/pcm.c | 36 +++++++++----- sound/soc/sof/pm.c | 4 +- sound/soc/sof/sof-audio.c | 88 +++++++++++++++++++++++++++++++---- sound/soc/sof/sof-audio.h | 9 ++++ sound/soc/sof/sof-priv.h | 6 +-- sound/soc/sof/topology.c | 30 +++++------- 12 files changed, 144 insertions(+), 88 deletions(-) diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 24b32c86e2c2c9..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_sof_component_get_sdev(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_sof_component_get_sdev(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_sof_component_get_sdev(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_sof_component_get_sdev(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_sof_component_get_sdev(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_sof_component_get_sdev(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_sof_component_get_sdev(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_sof_component_get_sdev(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_sof_component_get_sdev(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)) @@ -184,7 +175,7 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _ struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; struct snd_sof_dev *sdev = snd_sof_component_get_sdev(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); 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_sof_component_get_sdev(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/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index c8729b6d82bd4b..bd0de501ddc72f 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); 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.c b/sound/soc/sof/ipc3.c index 4a350d9d91226b..8d1f564dd05d7b 100644 --- a/sound/soc/sof/ipc3.c +++ b/sound/soc/sof/ipc3.c @@ -948,7 +948,7 @@ static void ipc3_stream_message(struct snd_sof_dev *sdev, void *msg_buf) /* 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; + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_sdev_get_tplg_ops(sdev); struct sof_ipc_cmd_hdr *hdr = msg_buf; u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; @@ -1138,9 +1138,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 785c3fc629224e..cb1761cac2d158 100644 --- a/sound/soc/sof/ipc4-compress.c +++ b/sound/soc/sof/ipc4-compress.c @@ -101,7 +101,7 @@ 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; @@ -316,7 +316,7 @@ static int sof_ipc4_compr_set_params(struct snd_soc_component *component, struct snd_compr_params *params) { struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); - 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(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; @@ -532,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_sof_component_get_sdev(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; diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c index 852b70f671424c..2c311c97b1a52a 100644 --- a/sound/soc/sof/ipc4.c +++ b/sound/soc/sof/ipc4.c @@ -785,7 +785,7 @@ static void sof_ipc4_module_notification_handler(struct snd_sof_dev *sdev, 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; + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_sdev_get_tplg_ops(sdev); if (tplg_ops->control->update) tplg_ops->control->update(sdev, ipc4_msg); @@ -1021,8 +1021,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/pcm.c b/sound/soc/sof/pcm.c index 5057cf9d378229..4598280a21af3b 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -123,8 +123,8 @@ static int sof_pcm_hw_params(struct snd_soc_component *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; @@ -216,7 +216,7 @@ static int sof_pcm_stream_free(struct snd_sof_dev *sdev, 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; @@ -325,7 +325,7 @@ static int sof_pcm_prepare(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_sof_dev *sdev = snd_sof_component_get_sdev(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 snd_sof_platform_stream_params *platform_params; struct snd_soc_dapm_widget_list *list; struct snd_pcm_hw_params *params; @@ -395,7 +395,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_sof_dev *sdev = snd_sof_component_get_sdev(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 snd_sof_pcm *spcm; bool reset_hw_params = false; bool ipc_first = false; @@ -508,7 +508,7 @@ static snd_pcm_uframes_t sof_pcm_pointer(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_sof_dev *sdev = snd_sof_component_get_sdev(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 snd_sof_pcm *spcm; snd_pcm_uframes_t host, dai; int ret = -EOPNOTSUPP; @@ -735,8 +735,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_sof_component_get_sdev(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) { @@ -829,8 +828,7 @@ static int sof_pcm_ack(struct snd_soc_component *component, static snd_pcm_sframes_t sof_pcm_delay(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct snd_sof_dev *sdev = snd_sof_component_get_sdev(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); @@ -842,8 +840,24 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev) { 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; + switch (sdev->pdata->ipc_type) { +#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; + } + if (plat_data->machine) drv_name = plat_data->machine->drv_name; else if (plat_data->of_machine) @@ -865,8 +879,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 diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 1d71d483588eb1..752a9e3210412b 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -78,7 +78,7 @@ 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); + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_sdev_get_tplg_ops(sdev); int ret; guard(mutex)(&sdev->dsp_fw_boot_mutex); @@ -230,7 +230,7 @@ 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); + const struct sof_ipc_tplg_ops *tplg_ops = snd_sof_sdev_get_tplg_ops(sdev); pm_message_t pm_state; u32 target_state = snd_sof_dsp_power_target(sdev); u32 old_state = sdev->dsp_power_state.state; diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index ec8f1faeeb3b7f..7626da4f5df23a 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -19,6 +19,32 @@ snd_sof_audio_instance_register(struct snd_sof_dev *sdev, struct snd_soc_component *component) { 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"); @@ -32,6 +58,8 @@ snd_sof_audio_instance_register(struct snd_sof_dev *sdev, 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); @@ -74,6 +102,46 @@ snd_sof_component_get_audio_instance(struct snd_soc_component *component) } EXPORT_SYMBOL(snd_sof_component_get_audio_instance); +const struct sof_ipc_tplg_ops *snd_sof_sdev_get_tplg_ops(struct snd_sof_dev *sdev) +{ + struct snd_sof_audio_instance *instance; + + guard(rcu)(); + list_for_each_entry_rcu(instance, &sdev->audio_instance_list, list) + return instance->tplg_ops; + + return NULL; +} +EXPORT_SYMBOL(snd_sof_sdev_get_tplg_ops); + +const struct sof_ipc_pcm_ops *snd_sof_sdev_get_pcm_ops(struct snd_sof_dev *sdev) +{ + struct snd_sof_audio_instance *instance; + + guard(rcu)(); + list_for_each_entry_rcu(instance, &sdev->audio_instance_list, list) + return instance->pcm_ops; + + return NULL; +} +EXPORT_SYMBOL(snd_sof_sdev_get_pcm_ops); + +const struct sof_ipc_tplg_ops *snd_sof_component_get_tplg_ops(struct snd_soc_component *component) +{ + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(component); + + return instance ? instance->tplg_ops : NULL; +} +EXPORT_SYMBOL(snd_sof_component_get_tplg_ops); + +const struct sof_ipc_pcm_ops *snd_sof_component_get_pcm_ops(struct snd_soc_component *component) +{ + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(component); + + return instance ? instance->pcm_ops : NULL; +} +EXPORT_SYMBOL(snd_sof_component_get_pcm_ops); + /* * 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 @@ -111,7 +179,7 @@ static void sof_reset_route_setup_status(struct snd_sof_widget *widget) 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 = sof_ipc_get_ops(sdev, tplg); + 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, &instance->route_list, list) @@ -127,7 +195,7 @@ 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; @@ -215,7 +283,7 @@ 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; @@ -330,9 +398,9 @@ 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; @@ -513,9 +581,9 @@ 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) { - struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); - 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; @@ -562,9 +630,9 @@ sof_prepare_widgets_in_path(struct snd_soc_component *component, struct snd_pcm_hw_params *pipeline_params, int dir, struct snd_soc_dapm_widget_list *list) { - struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); - 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; @@ -890,7 +958,7 @@ int sof_widget_list_setup(struct snd_soc_component *component, struct snd_sof_pc int dir) { struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); - 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(spcm->scomp); struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; struct snd_soc_dapm_widget *widget; int i, ret; @@ -1163,7 +1231,7 @@ static int sof_dai_get_param(struct snd_soc_pcm_runtime *rtd, int param_type) struct snd_sof_dai *dai = snd_sof_find_dai(component, (char *)rtd->dai_link->name); struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); - 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(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 c9e980504d1b21..37e2995b503469 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -104,6 +104,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 */ @@ -643,6 +648,10 @@ snd_sof_audio_instance_register(struct snd_sof_dev *sdev, void snd_sof_audio_instance_unregister(struct snd_sof_audio_instance *instance); struct snd_sof_audio_instance * snd_sof_component_get_audio_instance(struct snd_soc_component *component); +const struct sof_ipc_tplg_ops *snd_sof_sdev_get_tplg_ops(struct snd_sof_dev *sdev); +const struct sof_ipc_pcm_ops *snd_sof_sdev_get_pcm_ops(struct snd_sof_dev *sdev); +const struct sof_ipc_tplg_ops *snd_sof_component_get_tplg_ops(struct snd_soc_component *component); +const struct sof_ipc_pcm_ops *snd_sof_component_get_pcm_ops(struct snd_soc_component *component); static inline struct snd_sof_pcm *snd_sof_find_spcm_dai(struct snd_soc_component *scomp, diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 3d5972f72023f9..349a672799302e 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -187,6 +187,8 @@ 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; @@ -564,9 +566,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 * @@ -590,9 +590,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; diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index ef2a599fd922ae..d7ba5771f43200 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -64,8 +64,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_sof_component_get_sdev(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 +277,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_sof_component_get_sdev(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); @@ -513,8 +511,7 @@ static int sof_copy_tuples(struct snd_soc_component *scomp, int array_size, u32 token_id, int token_instance_num, struct snd_sof_tuple *tuples, int tuples_size, int *num_copied_tuples) { - struct snd_sof_dev *sdev = snd_sof_component_get_sdev(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 found = 0; @@ -1048,7 +1045,7 @@ static int sof_control_unload(struct snd_soc_component *scomp, struct snd_soc_dobj *dobj) { struct snd_sof_dev *sdev = snd_sof_component_get_sdev(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_sof_control *scontrol = dobj->private; int ret = 0; @@ -1220,8 +1217,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_sof_component_get_sdev(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; @@ -1461,7 +1457,7 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, { 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 = 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; struct snd_soc_tplg_private *priv = &tw->priv; enum sof_tokens *token_list = NULL; @@ -1693,8 +1689,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_sof_component_get_sdev(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; @@ -1794,7 +1789,7 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index, { 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 = sof_ipc_get_ops(sdev, pcm); + 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; @@ -1909,7 +1904,7 @@ static int sof_dai_unload(struct snd_soc_component *scomp, struct snd_soc_dobj *dobj) { struct snd_sof_dev *sdev = snd_sof_component_get_sdev(scomp); - const struct sof_ipc_pcm_ops *ipc_pcm_ops = sof_ipc_get_ops(sdev, pcm); + 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 */ @@ -1941,7 +1936,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_ { 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 = 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 = &cfg->priv; const struct sof_token_info *token_list; struct snd_sof_dai_link *slink; @@ -2253,7 +2248,7 @@ static int sof_complete(struct snd_soc_component *scomp) { 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 = 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; struct snd_sof_control *scontrol; struct snd_sof_pipeline *spipe; @@ -2343,8 +2338,7 @@ static int sof_complete(struct snd_soc_component *scomp) static int sof_manifest(struct snd_soc_component *scomp, int index, struct snd_soc_tplg_manifest *man) { - struct snd_sof_dev *sdev = snd_sof_component_get_sdev(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); From 58384fd1c9e4c520d550411724a0c60dcc717223 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 24 Jul 2026 15:51:22 +0300 Subject: [PATCH 043/100] ASoC: SOF: topology: drop unused sdev from sof_set_widget_pipeline() sof_set_widget_pipeline() takes a struct snd_sof_dev * but does not use it: the audio instance and the log message are already obtained from swidget->scomp. Drop the unused parameter. No functional change. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/topology.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index d7ba5771f43200..5d40843af10f61 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2208,7 +2208,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 * @@ -2216,7 +2215,7 @@ 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 = @@ -2291,7 +2290,7 @@ static int sof_complete(struct snd_soc_component *scomp) 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; From c826248fc7367e8a8eca744bac62e25db03c020f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 24 Jul 2026 15:39:49 +0300 Subject: [PATCH 044/100] ASoC: SOF: ipc4-topology: use component device and drop sdev passing Convert the remaining component-scoped log messages in ipc4-topology to print via the audio component device (derived from the widget or DAI) instead of sdev->dev, so messages are attributed to the component that triggered them. Several file-local topology helpers received struct snd_sof_dev * only to reach sdev->dev or to pass it further down; drop the parameter and derive the component (and sdev, where still needed) locally from the widget or DAI: snd_sof_get_hw_config_params(), snd_sof_get_nhlt_endpoint_data(), sof_ipc4_prepare_dai_copier(), sof_ipc4_widget_mod_init_msg_payload(), sof_ipc4_widget_pipe_create_msg_payload(), sof_ipc4_widget_pipe_ext_obj_memory_data() and sof_ipc4_add_init_ext_dp_memory_data(). sdev->dev is retained where it is the hardware device rather than a log context: the intel_nhlt_*() NHLT table lookups and the devm allocations in sof_ipc4_parse_manifest() that are tied to the sdev lifetime. No functional change. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/ipc4-topology.c | 74 ++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index b30264d9b3fe7e..ad1e547b08fb04 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -1430,7 +1430,7 @@ sof_ipc4_update_resource_usage(struct snd_sof_widget *swidget, 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); } @@ -1801,7 +1801,7 @@ 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; @@ -1821,7 +1821,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; } @@ -1834,7 +1834,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; } @@ -1843,18 +1843,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; @@ -1874,14 +1876,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; @@ -1900,14 +1902,14 @@ 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(sdev->dev, "SSP sample width change from %d to %d\n", + dev_dbg(scomp->dev, "SSP sample width change from %d to %d\n", params_width(params), bit_depth); } break; @@ -1915,7 +1917,7 @@ snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai 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 */ @@ -1971,7 +1973,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; @@ -2004,7 +2006,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) @@ -2088,9 +2090,11 @@ sof_ipc4_adjust_params_to_dai_format(struct snd_soc_component *scomp, } 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; @@ -2124,7 +2128,7 @@ sof_ipc4_prepare_dai_copier(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, 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, @@ -2356,7 +2360,7 @@ _sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, if (!ref_params) return -ENOMEM; - ret = sof_ipc4_prepare_dai_copier(sdev, dai, ref_params, dir); + ret = sof_ipc4_prepare_dai_copier(dai, ref_params, dir); if (ret < 0) return ret; } @@ -2428,7 +2432,7 @@ _sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, 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(sdev, dai, ref_params, dir); + ret = sof_ipc4_prepare_dai_copier(dai, ref_params, dir); if (ret < 0) return ret; } @@ -3317,8 +3321,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) { @@ -3356,8 +3359,7 @@ 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) @@ -3365,6 +3367,7 @@ static int sof_ipc4_widget_mod_init_msg_payload(struct snd_sof_dev *sdev, 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; @@ -3386,7 +3389,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) @@ -3417,12 +3420,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 = @@ -3436,17 +3439,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; @@ -3461,7 +3465,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 */ @@ -3474,7 +3478,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); @@ -3669,7 +3673,7 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget 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; @@ -3684,7 +3688,7 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget 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; @@ -4231,7 +4235,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; } @@ -4244,7 +4248,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; } @@ -4258,12 +4262,12 @@ 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; } From fb9abd461356e297d26564a8b45bd3665b9c5dc5 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 2 Jun 2026 16:57:43 +0300 Subject: [PATCH 045/100] ASoC: SOF: Intel: populate sof_audio_ops per platform Define sof_audio_ops instances for all Intel SOF platforms: - sof_hda_audio_ops in hda-common-ops.c, shared by all HDA platforms (SKL, APL, CNL, ICL, TGL, MTL, LNL, NVL, PTL) - sof_bdw_audio_ops in bdw.c for Broadwell - sof_byt_audio_ops and sof_cht_audio_ops in byt.c for Baytrail and Cherrytrail (different num_drv: 3 vs 6 SSPs) - sof_tng_audio_ops in pci-tng.c for Tangier Wire audio_ops into sof_dev_desc for each platform. The callbacks point to the same functions already used in snd_sof_dsp_ops. No functional change. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/intel/bdw.c | 15 ++++++++++++++ sound/soc/sof/intel/byt.c | 31 ++++++++++++++++++++++++++++ sound/soc/sof/intel/hda-common-ops.c | 27 ++++++++++++++++++++++++ sound/soc/sof/intel/hda.h | 1 + sound/soc/sof/intel/pci-apl.c | 2 ++ sound/soc/sof/intel/pci-cnl.c | 3 +++ sound/soc/sof/intel/pci-icl.c | 2 ++ sound/soc/sof/intel/pci-lnl.c | 1 + sound/soc/sof/intel/pci-mtl.c | 3 +++ sound/soc/sof/intel/pci-nvl.c | 2 ++ sound/soc/sof/intel/pci-ptl.c | 2 ++ sound/soc/sof/intel/pci-skl.c | 2 ++ sound/soc/sof/intel/pci-tgl.c | 8 +++++++ sound/soc/sof/intel/pci-tng.c | 15 ++++++++++++++ 14 files changed, 114 insertions(+) diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c index 9534d18be97dfe..5c2e2975a8b227 100644 --- a/sound/soc/sof/intel/bdw.c +++ b/sound/soc/sof/intel/bdw.c @@ -560,6 +560,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 */ @@ -646,6 +660,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[] = { diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c index 18208f77b84dc6..5ce91032027a3f 100644 --- a/sound/soc/sof/intel/byt.c +++ b/sound/soc/sof/intel/byt.c @@ -213,6 +213,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, @@ -288,6 +302,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, @@ -384,6 +412,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 +435,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 +458,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[] = { diff --git a/sound/soc/sof/intel/hda-common-ops.c b/sound/soc/sof/intel/hda-common-ops.c index aa91f2b6fd657b..5671f555aaa6c3 100644 --- a/sound/soc/sof/intel/hda-common-ops.c +++ b/sound/soc/sof/intel/hda-common-ops.c @@ -14,6 +14,33 @@ #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, + + .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, diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 29d092aa6f85ff..b59a8e1f9656af 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -963,6 +963,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; 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..1e8960067cb3ff 100644 --- a/sound/soc/sof/intel/pci-tng.c +++ b/sound/soc/sof/intel/pci-tng.c @@ -132,6 +132,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, @@ -221,6 +235,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 */ From 891d1f784702563ab4980059e5f7eff9587baba9 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 2 Jun 2026 16:58:17 +0300 Subject: [PATCH 046/100] ASoC: SOF: AMD: populate sof_audio_ops per platform Define per-variant sof_audio_ops for all AMD SOF platforms: Renoir, Rembrandt, Van Gogh, ACP63, and ACP70. Each variant has its own audio_ops with platform-specific DAI drivers and shared ACP PCM callbacks. Set sdev->audio_ops from ops_init since the audio_ops are static to each variant file and the descriptors are in separate pci-*.c files. No functional change. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/amd/acp63.c | 18 ++++++++++++++++++ sound/soc/sof/amd/acp70.c | 18 ++++++++++++++++++ sound/soc/sof/amd/rembrandt.c | 18 ++++++++++++++++++ sound/soc/sof/amd/renoir.c | 18 ++++++++++++++++++ sound/soc/sof/amd/vangogh.c | 18 ++++++++++++++++++ 5 files changed, 90 insertions(+) diff --git a/sound/soc/sof/amd/acp63.c b/sound/soc/sof/amd/acp63.c index a686620b13588b..fcceef47fa8773 100644 --- a/sound/soc/sof/amd/acp63.c +++ b/sound/soc/sof/amd/acp63.c @@ -130,6 +130,22 @@ 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 */ @@ -138,5 +154,7 @@ int sof_acp63_ops_init(struct snd_sof_dev *sdev) 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..c87e8a90e3b764 100644 --- a/sound/soc/sof/amd/acp70.c +++ b/sound/soc/sof/amd/acp70.c @@ -130,6 +130,22 @@ 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 */ @@ -138,5 +154,7 @@ int sof_acp70_ops_init(struct snd_sof_dev *sdev) 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..9afef5ffd9d57c 100644 --- a/sound/soc/sof/amd/rembrandt.c +++ b/sound/soc/sof/amd/rembrandt.c @@ -130,6 +130,22 @@ 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 */ @@ -138,5 +154,7 @@ int sof_rembrandt_ops_init(struct snd_sof_dev *sdev) 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..5477081427357c 100644 --- a/sound/soc/sof/amd/renoir.c +++ b/sound/soc/sof/amd/renoir.c @@ -105,6 +105,22 @@ 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 */ @@ -113,5 +129,7 @@ int sof_renoir_ops_init(struct snd_sof_dev *sdev) 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..dd5835bd2bc4ee 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; @@ -162,6 +178,8 @@ int sof_vangogh_ops_init(struct snd_sof_dev *sdev) 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) { quirks = dmi_id->driver_data; From 0521d45270f368bdc858c175fb5b52e56b8ef528 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 2 Jun 2026 16:58:28 +0300 Subject: [PATCH 047/100] ASoC: SOF: i.MX: populate sof_audio_ops per platform Define per-chip sof_audio_ops for i.MX SOF platforms: imx8, imx8m, imx8ulp, and imx95. Each chip has its own audio_ops with chip-specific DAI drivers and shared stream PCM callbacks. Add audio_ops pointer to imx_chip_info so ops_init can select the correct audio_ops per chip variant. No functional change. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/imx/imx-common.h | 1 + sound/soc/sof/imx/imx8.c | 37 ++++++++++++++++++++++++++++++++++ sound/soc/sof/imx/imx9.c | 16 +++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/sound/soc/sof/imx/imx-common.h b/sound/soc/sof/imx/imx-common.h index 9bd711dbb5d03f..5b00b191b29661 100644 --- a/sound/soc/sof/imx/imx-common.h +++ b/sound/soc/sof/imx/imx-common.h @@ -102,6 +102,7 @@ struct imx_chip_info { 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..fbf0086f7a5369 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) @@ -287,6 +318,8 @@ static int imx8_ops_init(struct snd_sof_dev *sdev) 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; } @@ -341,6 +374,7 @@ static const struct imx_chip_info imx8_chip_info = { .memory = imx8_memory_regions, .drv = imx8_dai, .num_drv = ARRAY_SIZE(imx8_dai), + .audio_ops = &sof_imx8_audio_ops, .ops = &imx8_chip_ops, }; @@ -353,6 +387,7 @@ static const struct imx_chip_info imx8x_chip_info = { .memory = imx8_memory_regions, .drv = imx8_dai, .num_drv = ARRAY_SIZE(imx8_dai), + .audio_ops = &sof_imx8_audio_ops, .ops = &imx8x_chip_ops, }; @@ -365,6 +400,7 @@ static const struct imx_chip_info imx8m_chip_info = { .memory = imx8m_memory_regions, .drv = imx8m_dai, .num_drv = ARRAY_SIZE(imx8m_dai), + .audio_ops = &sof_imx8m_audio_ops, .ops = &imx8m_chip_ops, }; @@ -378,6 +414,7 @@ static const struct imx_chip_info imx8ulp_chip_info = { .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..6641f282899652 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) @@ -25,6 +38,8 @@ static int imx95_ops_init(struct snd_sof_dev *sdev) 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; } @@ -76,6 +91,7 @@ static const struct imx_chip_info imx95_chip_info = { .memory = imx95_memory_regions, .drv = imx95_dai, .num_drv = ARRAY_SIZE(imx95_dai), + .audio_ops = &sof_imx95_audio_ops, .ops = &imx95_chip_ops, }; From 5163ebe725ad0dded3f357b29761b19bd03cc736 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 2 Jun 2026 16:58:36 +0300 Subject: [PATCH 048/100] ASoC: SOF: MediaTek: populate sof_audio_ops per platform Define per-SoC sof_audio_ops for MediaTek SOF platforms: mt8186, mt8188, mt8195, and mt8365. Each SoC has its own audio_ops with SoC-specific DAI drivers and PCM hw_params/pointer callbacks. For mt8188, set sdev->audio_ops from ops_init since it shares the mt8186 ops file and overrides drv/num_drv at init time. No functional change. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/mediatek/mt8186/mt8186.c | 36 ++++++++++++++++++++++++++ sound/soc/sof/mediatek/mt8195/mt8195.c | 17 ++++++++++++ sound/soc/sof/mediatek/mt8365/mt8365.c | 17 ++++++++++++ 3 files changed, 70 insertions(+) diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.c b/sound/soc/sof/mediatek/mt8186/mt8186.c index c1bea967737d52..0c9ce3d3bfce5f 100644 --- a/sound/soc/sof/mediatek/mt8186/mt8186.c +++ b/sound/soc/sof/mediatek/mt8186/mt8186.c @@ -379,6 +379,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, @@ -467,6 +483,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 +522,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) @@ -515,6 +548,8 @@ static int sof_mt8188_ops_init(struct snd_sof_dev *sdev) 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 +576,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, }; diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c index 5dfa8721e059c5..39638f6346ac41 100644 --- a/sound/soc/sof/mediatek/mt8195/mt8195.c +++ b/sound/soc/sof/mediatek/mt8195/mt8195.c @@ -402,6 +402,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 */ @@ -498,6 +514,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, }; diff --git a/sound/soc/sof/mediatek/mt8365/mt8365.c b/sound/soc/sof/mediatek/mt8365/mt8365.c index 76a18038b98368..05c6ea7df57d5b 100644 --- a/sound/soc/sof/mediatek/mt8365/mt8365.c +++ b/sound/soc/sof/mediatek/mt8365/mt8365.c @@ -534,6 +534,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 */ @@ -624,6 +640,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, }; From e243ea7bfb9cc69d67a3ad48a3c76cd6751a777d Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 2 Jun 2026 17:05:59 +0300 Subject: [PATCH 049/100] ASoC: SOF: switch audio dispatch to sof_audio_ops Switch the PCM and compress dispatch functions in ops.h from sof_ops(sdev)->callback to audio_instance->audio_ops->callback pattern. This decouples the audio path from snd_sof_dsp_ops and routes through the sof_audio_ops structure instead. Update core.c to read drv/num_drv from sdev->audio_ops for component registration. Update pcm.c to read hw_info and pcm_pointer from audio_ops. Update intel/hda.c to check audio_ops->pcm_pointer for IPC position mode. The compr_get_dai_frame_counter, pcm_get_dai_frame_counter, and pcm_get_host_byte_counter callbacks are not moved as they take sdev directly and are not part of sof_audio_ops. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/core.c | 7 +-- sound/soc/sof/intel/hda.c | 2 +- sound/soc/sof/ops.h | 95 ++++++++++++++++++++++----------------- sound/soc/sof/pcm.c | 12 ++--- 4 files changed, 66 insertions(+), 50 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index b56b187cb872f7..ba676bccf4c9cc 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -556,8 +556,8 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) /* 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); + sdev->audio_ops->drv, + sdev->audio_ops->num_drv); if (ret < 0) { dev_err(sdev->dev, "error: failed to register DSP DAI driver %d\n", ret); @@ -678,13 +678,14 @@ 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->audio_instance_list); - sdev->audio_ops = plat_data->desc->audio_ops; INIT_LIST_HEAD(&sdev->ipc_client_list); INIT_LIST_HEAD(&sdev->client_ops_list); spin_lock_init(&sdev->ipc_lock); diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 4dbba9186b29d4..ec8309a20985d1 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -822,7 +822,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) diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 34ff644934721a..a00716353ed58b 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -419,10 +419,11 @@ static inline int snd_sof_pcm_platform_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); - if (sof_ops(sdev) && sof_ops(sdev)->pcm_open) - return sof_ops(sdev)->pcm_open(component, substream); + if (ins && ins->audio_ops->pcm_open) + return ins->audio_ops->pcm_open(component, substream); return 0; } @@ -432,10 +433,11 @@ static inline int snd_sof_pcm_platform_close(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); - if (sof_ops(sdev) && sof_ops(sdev)->pcm_close) - return sof_ops(sdev)->pcm_close(component, substream); + if (ins && ins->audio_ops->pcm_close) + return ins->audio_ops->pcm_close(component, substream); return 0; } @@ -447,11 +449,12 @@ snd_sof_pcm_platform_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_sof_component_get_sdev(component); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); - if (sof_ops(sdev) && sof_ops(sdev)->pcm_hw_params) - return sof_ops(sdev)->pcm_hw_params(component, substream, params, - platform_params); + if (ins && ins->audio_ops->pcm_hw_params) + return ins->audio_ops->pcm_hw_params(component, substream, + params, platform_params); return 0; } @@ -460,10 +463,11 @@ static inline int snd_sof_compr_platform_open(struct snd_soc_component *component, struct snd_compr_stream *cstream) { - struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); - if (sof_ops(sdev) && sof_ops(sdev)->compr_open) - return sof_ops(sdev)->compr_open(component, cstream); + if (ins && ins->audio_ops->compr_open) + return ins->audio_ops->compr_open(component, cstream); return 0; } @@ -473,10 +477,11 @@ static inline int snd_sof_compr_platform_close(struct snd_soc_component *component, struct snd_compr_stream *cstream) { - struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); - if (sof_ops(sdev) && sof_ops(sdev)->compr_close) - return sof_ops(sdev)->compr_close(component, cstream); + if (ins && ins->audio_ops->compr_close) + return ins->audio_ops->compr_close(component, cstream); return 0; } @@ -488,11 +493,12 @@ snd_sof_compr_platform_hw_params(struct snd_soc_component *component, 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 snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); - if (sof_ops(sdev) && sof_ops(sdev)->compr_hw_params) - return sof_ops(sdev)->compr_hw_params(component, cstream, params, - platform_params); + if (ins && ins->audio_ops->compr_hw_params) + return ins->audio_ops->compr_hw_params(component, cstream, + params, platform_params); return 0; } @@ -501,10 +507,11 @@ static inline int snd_sof_compr_platform_hw_free(struct snd_soc_component *component, struct snd_compr_stream *cstream) { - struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); - if (sof_ops(sdev) && sof_ops(sdev)->compr_hw_free) - return sof_ops(sdev)->compr_hw_free(component, cstream); + if (ins && ins->audio_ops->compr_hw_free) + return ins->audio_ops->compr_hw_free(component, cstream); return 0; } @@ -513,10 +520,11 @@ static inline int snd_sof_compr_platform_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 snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); - if (sof_ops(sdev) && sof_ops(sdev)->compr_trigger) - return sof_ops(sdev)->compr_trigger(component, cstream, cmd); + if (ins && ins->audio_ops->compr_trigger) + return ins->audio_ops->compr_trigger(component, cstream, cmd); return 0; } @@ -526,10 +534,11 @@ snd_sof_compr_platform_pointer(struct snd_soc_component *component, struct snd_compr_stream *cstream, struct snd_compr_tstamp64 *tstamp) { - struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); - if (sof_ops(sdev) && sof_ops(sdev)->compr_pointer) - return sof_ops(sdev)->compr_pointer(component, cstream, tstamp); + if (ins && ins->audio_ops->compr_pointer) + return ins->audio_ops->compr_pointer(component, cstream, tstamp); return 0; } @@ -549,10 +558,11 @@ static inline int snd_sof_pcm_platform_hw_free(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); - if (sof_ops(sdev) && sof_ops(sdev)->pcm_hw_free) - return sof_ops(sdev)->pcm_hw_free(component, substream); + if (ins && ins->audio_ops->pcm_hw_free) + return ins->audio_ops->pcm_hw_free(component, substream); return 0; } @@ -562,10 +572,11 @@ static inline int snd_sof_pcm_platform_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 snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); - if (sof_ops(sdev) && sof_ops(sdev)->pcm_trigger) - return sof_ops(sdev)->pcm_trigger(component, substream, cmd); + if (ins && ins->audio_ops->pcm_trigger) + return ins->audio_ops->pcm_trigger(component, substream, cmd); return 0; } @@ -605,10 +616,11 @@ static inline snd_pcm_uframes_t snd_sof_pcm_platform_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); - if (sof_ops(sdev) && sof_ops(sdev)->pcm_pointer) - return sof_ops(sdev)->pcm_pointer(component, substream); + if (ins && ins->audio_ops->pcm_pointer) + return ins->audio_ops->pcm_pointer(component, substream); return 0; } @@ -617,10 +629,11 @@ snd_sof_pcm_platform_pointer(struct snd_soc_component *component, static inline int snd_sof_pcm_platform_ack(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + struct snd_sof_audio_instance *ins = + snd_sof_component_get_audio_instance(component); - if (sof_ops(sdev) && sof_ops(sdev)->pcm_ack) - return sof_ops(sdev)->pcm_ack(component, substream); + if (ins && ins->audio_ops->pcm_ack) + return ins->audio_ops->pcm_ack(component, substream); return 0; } diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 4598280a21af3b..5b451e0d150feb 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -507,6 +507,8 @@ 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_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; @@ -524,8 +526,8 @@ static snd_pcm_uframes_t sof_pcm_pointer(struct snd_soc_component *component, return ret ? ret : host; /* use dsp ops pointer callback directly if set */ - if (sof_ops(sdev)->pcm_pointer) - return sof_ops(sdev)->pcm_pointer(component, substream); + if (ins && ins->audio_ops->pcm_pointer) + return ins->audio_ops->pcm_pointer(component, substream); spcm = snd_sof_find_spcm_dai(component, rtd); if (!spcm) @@ -547,8 +549,8 @@ 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_sof_component_get_sdev(component); - const struct snd_sof_dsp_ops *ops = sof_ops(sdev); + 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; @@ -566,7 +568,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); From dd6494c105cf6093f9603d9ccf1a8ff4a9a0d79a Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 2 Jun 2026 17:16:34 +0300 Subject: [PATCH 050/100] ASoC: SOF: Intel: remove audio ops from snd_sof_dsp_ops instances Remove the pcm, compress, DAI driver and hw_info fields from all Intel snd_sof_dsp_ops struct instances as these are now provided through the sof_audio_ops structures. Update hda_set_dai_drv_ops() and related functions to access the DAI driver array through sdev->audio_ops instead of the dsp_ops parameter. Update set_mach_params() in atom.c and bdw.c to read drv/num_drv from desc->audio_ops. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/intel/apl.c | 2 +- sound/soc/sof/intel/atom.c | 4 ++-- sound/soc/sof/intel/bdw.c | 19 ++------------- sound/soc/sof/intel/byt.c | 31 ------------------------ sound/soc/sof/intel/cnl.c | 2 +- sound/soc/sof/intel/hda-common-ops.c | 23 ------------------ sound/soc/sof/intel/hda-dai.c | 36 ++++++++++++++-------------- sound/soc/sof/intel/hda.c | 4 ++-- sound/soc/sof/intel/hda.h | 2 +- sound/soc/sof/intel/icl.c | 2 +- sound/soc/sof/intel/mtl.c | 2 +- sound/soc/sof/intel/pci-tng.c | 15 ------------ sound/soc/sof/intel/skl.c | 2 +- sound/soc/sof/intel/tgl.c | 2 +- 14 files changed, 31 insertions(+), 115 deletions(-) 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 5c2e2975a8b227..f7f4058b57e034 100644 --- a/sound/soc/sof/intel/bdw.c +++ b/sound/soc/sof/intel/bdw.c @@ -530,8 +530,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 */ @@ -613,24 +613,9 @@ static const struct snd_sof_dsp_ops sof_bdw_ops = { .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, }; diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c index 5ce91032027a3f..1e311e774600ae 100644 --- a/sound/soc/sof/intel/byt.c +++ b/sound/soc/sof/intel/byt.c @@ -270,10 +270,6 @@ static const struct snd_sof_dsp_ops sof_byt_ops = { .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, @@ -281,17 +277,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, }; @@ -359,10 +344,6 @@ static const struct snd_sof_dsp_ops sof_cht_ops = { .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, @@ -370,18 +351,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, }; 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 5671f555aaa6c3..89920db60e3002 100644 --- a/sound/soc/sof/intel/hda-common-ops.c +++ b/sound/soc/sof/intel/hda-common-ops.c @@ -76,20 +76,6 @@ const struct snd_sof_dsp_ops sof_hda_common_ops = { .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, @@ -119,8 +105,6 @@ const struct snd_sof_dsp_ops sof_hda_common_ops = { .unregister_ipc_clients = hda_unregister_clients, /* DAI drivers */ - .drv = skl_dai, - .num_drv = SOF_SKL_NUM_DAIS, .is_chain_dma_supported = hda_is_chain_dma_supported, /* PM */ @@ -131,13 +115,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.c b/sound/soc/sof/intel/hda-dai.c index bd0de501ddc72f..da8d4a9a532291 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -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.c b/sound/soc/sof/intel/hda.c index ec8309a20985d1..c5eb71f92c017d 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -1460,8 +1460,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) diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index b59a8e1f9656af..2c73314553850a 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -1030,7 +1030,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 */ 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-tng.c b/sound/soc/sof/intel/pci-tng.c index 1e8960067cb3ff..8d9740d51f4f22 100644 --- a/sound/soc/sof/intel/pci-tng.c +++ b/sound/soc/sof/intel/pci-tng.c @@ -188,24 +188,9 @@ const struct snd_sof_dsp_ops sof_tng_ops = { .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, }; 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; From e08d312dc71e76a4aa568c7841726233cf459c67 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 2 Jun 2026 17:16:42 +0300 Subject: [PATCH 051/100] ASoC: SOF: AMD: remove audio ops from snd_sof_dsp_ops instances Remove the pcm_open, pcm_close, pcm_hw_params, pcm_pointer, drv, num_drv and hw_info fields from the AMD common and per-variant snd_sof_dsp_ops struct instances as these are now provided through the sof_audio_ops structures. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/amd/acp-common.c | 12 ------------ sound/soc/sof/amd/acp63.c | 3 --- sound/soc/sof/amd/acp70.c | 3 --- sound/soc/sof/amd/rembrandt.c | 3 --- sound/soc/sof/amd/renoir.c | 3 --- sound/soc/sof/amd/vangogh.c | 3 --- 6 files changed, 27 deletions(-) diff --git a/sound/soc/sof/amd/acp-common.c b/sound/soc/sof/amd/acp-common.c index df656cdc152773..c1cb10815b36cb 100644 --- a/sound/soc/sof/amd/acp-common.c +++ b/sound/soc/sof/amd/acp-common.c @@ -225,18 +225,6 @@ 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, diff --git a/sound/soc/sof/amd/acp63.c b/sound/soc/sof/amd/acp63.c index fcceef47fa8773..10dc2113f45546 100644 --- a/sound/soc/sof/amd/acp63.c +++ b/sound/soc/sof/amd/acp63.c @@ -151,9 +151,6 @@ 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 c87e8a90e3b764..ab9f57048fde82 100644 --- a/sound/soc/sof/amd/acp70.c +++ b/sound/soc/sof/amd/acp70.c @@ -151,9 +151,6 @@ 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 9afef5ffd9d57c..50351b4ddf6949 100644 --- a/sound/soc/sof/amd/rembrandt.c +++ b/sound/soc/sof/amd/rembrandt.c @@ -151,9 +151,6 @@ 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 5477081427357c..203bcb3610bdf1 100644 --- a/sound/soc/sof/amd/renoir.c +++ b/sound/soc/sof/amd/renoir.c @@ -126,9 +126,6 @@ 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 dd5835bd2bc4ee..9057ed9b39e91c 100644 --- a/sound/soc/sof/amd/vangogh.c +++ b/sound/soc/sof/amd/vangogh.c @@ -175,9 +175,6 @@ 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); From f132027831e4062e7979bf0293b36451d7afa958 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 2 Jun 2026 17:16:51 +0300 Subject: [PATCH 052/100] ASoC: SOF: i.MX: remove audio ops from snd_sof_dsp_ops instances Remove the pcm_open, pcm_close, drv, num_drv and hw_info fields from the i.MX common and per-variant snd_sof_dsp_ops struct instances as these are now provided through the sof_audio_ops structures. Also remove the now-orphaned drv and num_drv fields from the imx_chip_info structure and all its instances. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/imx/imx-common.c | 10 ---------- sound/soc/sof/imx/imx-common.h | 2 -- sound/soc/sof/imx/imx8.c | 12 ------------ sound/soc/sof/imx/imx9.c | 6 ------ 4 files changed, 30 deletions(-) diff --git a/sound/soc/sof/imx/imx-common.c b/sound/soc/sof/imx/imx-common.c index 7a03c8cc5dd473..0b8aa523659ad5 100644 --- a/sound/soc/sof/imx/imx-common.c +++ b/sound/soc/sof/imx/imx-common.c @@ -457,22 +457,12 @@ const struct snd_sof_dsp_ops sof_imx_ops = { .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, - .pcm_open = sof_stream_pcm_open, - .pcm_close = sof_stream_pcm_close, - .runtime_suspend = imx_runtime_suspend, .runtime_resume = imx_runtime_resume, .suspend = imx_suspend, .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); diff --git a/sound/soc/sof/imx/imx-common.h b/sound/soc/sof/imx/imx-common.h index 5b00b191b29661..8c21b3a652d068 100644 --- a/sound/soc/sof/imx/imx-common.h +++ b/sound/soc/sof/imx/imx-common.h @@ -100,8 +100,6 @@ 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 fbf0086f7a5369..12baa41c52413b 100644 --- a/sound/soc/sof/imx/imx8.c +++ b/sound/soc/sof/imx/imx8.c @@ -314,10 +314,6 @@ 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; @@ -372,8 +368,6 @@ 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, }; @@ -385,8 +379,6 @@ 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, }; @@ -398,8 +390,6 @@ 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, }; @@ -412,8 +402,6 @@ 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 6641f282899652..117cbd71bc4bc7 100644 --- a/sound/soc/sof/imx/imx9.c +++ b/sound/soc/sof/imx/imx9.c @@ -34,10 +34,6 @@ 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; @@ -89,8 +85,6 @@ 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, }; From ba66fb2f0c3bcd97787aa60679ae55649999f2fa Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 2 Jun 2026 17:16:59 +0300 Subject: [PATCH 053/100] ASoC: SOF: MediaTek: remove audio ops from snd_sof_dsp_ops instances Remove the pcm_open, pcm_close, pcm_hw_params, pcm_pointer, drv, num_drv and hw_info fields from the MediaTek snd_sof_dsp_ops struct instances as these are now provided through the sof_audio_ops structures. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/mediatek/mt8186/mt8186.c | 20 -------------------- sound/soc/sof/mediatek/mt8195/mt8195.c | 17 ----------------- sound/soc/sof/mediatek/mt8365/mt8365.c | 17 ----------------- 3 files changed, 54 deletions(-) diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.c b/sound/soc/sof/mediatek/mt8186/mt8186.c index 0c9ce3d3bfce5f..65b4714a22b818 100644 --- a/sound/soc/sof/mediatek/mt8186/mt8186.c +++ b/sound/soc/sof/mediatek/mt8186/mt8186.c @@ -428,22 +428,12 @@ 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, /* 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, @@ -451,13 +441,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[] = { @@ -545,9 +528,6 @@ 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; diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c index 39638f6346ac41..bec41f481dacce 100644 --- a/sound/soc/sof/mediatek/mt8195/mt8195.c +++ b/sound/soc/sof/mediatek/mt8195/mt8195.c @@ -452,12 +452,6 @@ 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, @@ -468,20 +462,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[] = { diff --git a/sound/soc/sof/mediatek/mt8365/mt8365.c b/sound/soc/sof/mediatek/mt8365/mt8365.c index 05c6ea7df57d5b..83caf3ae667bae 100644 --- a/sound/soc/sof/mediatek/mt8365/mt8365.c +++ b/sound/soc/sof/mediatek/mt8365/mt8365.c @@ -584,12 +584,6 @@ 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, @@ -600,20 +594,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[] = { From 8a0d9091a80cce6144697a8f4f630391cde9164a Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 2 Jun 2026 17:16:22 +0300 Subject: [PATCH 054/100] ASoC: SOF: remove audio ops from struct snd_sof_dsp_ops Remove the pcm_open, pcm_close, pcm_hw_params, pcm_hw_free, pcm_trigger, pcm_pointer, pcm_ack, compr_open, compr_close, compr_hw_params, compr_hw_free, compr_trigger, compr_pointer, drv, num_drv and hw_info fields from struct snd_sof_dsp_ops as these are now provided through struct sof_audio_ops. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-priv.h | 53 ---------------------------------------- 1 file changed, 53 deletions(-) diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 349a672799302e..3d3a134c6b99c7 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -303,52 +303,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_soc_component *component, - struct snd_pcm_substream *substream); /* optional */ - /* disconnect pcm substream to a host stream */ - int (*pcm_close)(struct snd_soc_component *component, - struct snd_pcm_substream *substream); /* optional */ - - /* host stream hw params */ - 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); /* optional */ - - /* host stream hw_free */ - int (*pcm_hw_free)(struct snd_soc_component *component, - struct snd_pcm_substream *substream); /* optional */ - - /* host stream trigger */ - int (*pcm_trigger)(struct snd_soc_component *component, - struct snd_pcm_substream *substream, - int cmd); /* optional */ - - /* host stream pointer */ - snd_pcm_uframes_t (*pcm_pointer)(struct snd_soc_component *component, - struct snd_pcm_substream *substream); /* optional */ - - /* pcm ack */ - int (*pcm_ack)(struct snd_soc_component *component, - struct snd_pcm_substream *substream); /* optional */ - - 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_sof_dev *sdev, struct snd_compr_stream *cstream); /* @@ -444,14 +398,8 @@ struct snd_sof_dsp_ops { void (*unregister_ipc_clients)(struct snd_sof_dev *sdev); /* optional */ /* DAI ops */ - struct snd_soc_dai_driver *drv; - int num_drv; - 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; }; @@ -717,7 +665,6 @@ struct snd_sof_dev { struct snd_soc_tplg_ops *tplg_ops; const struct sof_audio_ops *audio_ops; struct list_head audio_instance_list; - /* Protect audio_instance_list */ spinlock_t audio_instance_list_lock; u32 enabled_cores_mask; /* keep track of enabled cores */ From 04b83ea8105f94ffa296c53b8d3ea0b0f7d8560e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 3 Jun 2026 12:28:15 +0300 Subject: [PATCH 055/100] ASoC: SOF: pass component_driver to snd_sof_new_platform_drv() Refactor snd_sof_new_platform_drv() to take an explicit snd_soc_component_driver pointer instead of always filling sdev->plat_drv directly. This prepares for the audio sof-client driver which will provide its own component_driver. No functional change. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/core.c | 4 ++-- sound/soc/sof/pcm.c | 4 ++-- sound/soc/sof/sof-priv.h | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index ba676bccf4c9cc..b7a1f92527e100 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -473,7 +473,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) sof_set_fw_state(sdev, SOF_DSPLESS_MODE); /* set up platform component driver */ - snd_sof_new_platform_drv(sdev); + snd_sof_new_platform_drv(sdev, &sdev->plat_drv); goto skip_dsp_init; } @@ -500,7 +500,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) } /* set up platform component driver after initializing the IPC ops */ - snd_sof_new_platform_drv(sdev); + snd_sof_new_platform_drv(sdev, &sdev->plat_drv); /* * skip loading/booting firmware and registering the machine driver when DSP OPS testing diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 5b451e0d150feb..3283d43eaa8f6c 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -838,9 +838,9 @@ 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 snd_sof_dev *sdev, + 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; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 3d3a134c6b99c7..a760192ed4b164 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -737,7 +737,8 @@ 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); +void snd_sof_new_platform_drv(struct snd_sof_dev *sdev, + struct snd_soc_component_driver * const pd); /* * Compress support From 65f3382d6ae0aea5cf280a83ef05ac6eae114b02 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 3 Jun 2026 12:29:13 +0300 Subject: [PATCH 056/100] ASoC: SOF: sof-client: add machine register/unregister API Add sof_client_machine_register() and sof_client_machine_unregister() to the sof-client API. These allow client drivers to manage machine driver registration without direct access to snd_sof_dev. The audio sof-client driver will use these to register and unregister the machine driver as part of its lifecycle. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-client.c | 16 ++++++++++++++++ sound/soc/sof/sof-client.h | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index 269d7abd41fb1f..520c06f7e42069 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -794,3 +794,19 @@ u32 sof_client_get_new_comp_id(struct sof_client_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); + + return snd_sof_machine_register(sdev, sdev->pdata); +} +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); + + snd_sof_machine_unregister(sdev, sdev->pdata); +} +EXPORT_SYMBOL_NS_GPL(sof_client_machine_unregister, "SND_SOC_SOF_CLIENT"); diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index ca99cd4cc6d743..0b455e51d6e979 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -138,4 +138,8 @@ 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); + #endif /* __SOC_SOF_CLIENT_H */ From 74e86dc61c38796d11f729003f46e270fa3b78ab Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 3 Jun 2026 12:33:41 +0300 Subject: [PATCH 057/100] ASoC: SOF: register audio as a sof-client auxiliary device Move audio component and machine driver registration from core.c into a dedicated sof-client auxiliary driver (snd-sof-audio.ko). Core.c now registers a 'snd_sof.audio' auxiliary device with the pre-built snd_soc_component_driver and DAI drivers passed as platform_data. The audio client driver's probe registers the ASoC component and machine driver, and its remove handles teardown in the correct order. The component is still registered on the parent device (sdev->dev) via sof_client_get_dma_dev() to maintain machine driver compatibility. SND_SOC_SOF now selects SND_SOC_SOF_CLIENT to ensure the client framework is always available. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/Kconfig | 1 + sound/soc/sof/Makefile | 2 ++ sound/soc/sof/core.c | 39 ++++++++++---------- sound/soc/sof/sof-client-audio.c | 62 ++++++++++++++++++++++++++++++++ sound/soc/sof/sof-client-audio.h | 22 ++++++++++++ 5 files changed, 107 insertions(+), 19 deletions(-) create mode 100644 sound/soc/sof/sof-client-audio.c create mode 100644 sound/soc/sof/sof-client-audio.h 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..64d4159a2d2388 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -28,6 +28,7 @@ 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 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 +43,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/core.c b/sound/soc/sof/core.c index b7a1f92527e100..e64e8f1a62d3c6 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -14,6 +14,7 @@ #include #include "sof-priv.h" #include "sof-of-dev.h" +#include "sof-client-audio.h" #include "ops.h" #define CREATE_TRACE_POINTS @@ -554,27 +555,28 @@ 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, - sdev->audio_ops->drv, - sdev->audio_ops->num_drv); - if (ret < 0) { - dev_err(sdev->dev, - "error: failed to register DSP DAI driver %d\n", ret); - goto fw_trace_err; + /* Register audio client device */ + { + struct sof_audio_client_pdata audio_pdata = { + .plat_drv = sdev->plat_drv, + .drv = sdev->audio_ops->drv, + .num_drv = sdev->audio_ops->num_drv, + }; + + ret = sof_client_dev_register(sdev, "audio", 0, + &audio_pdata, + sizeof(audio_pdata)); } - - ret = snd_sof_machine_register(sdev, plat_data); if (ret < 0) { dev_err(sdev->dev, - "error: failed to register machine driver %d\n", ret); + "error: failed to register audio client %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 audio_client_err; } /* @@ -592,8 +594,8 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) return 0; -sof_machine_err: - snd_sof_machine_unregister(sdev, plat_data); +audio_client_err: + sof_client_dev_unregister(sdev, "audio", 0); fw_trace_err: sof_fw_trace_free(sdev); fw_run_err: @@ -746,7 +748,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; @@ -760,11 +761,11 @@ 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. + * Unregister audio client device. This will unregister the machine + * driver and the ASoC component, which will unbind the snd_card, + * unload the topology and free the snd_card. */ - snd_sof_machine_unregister(sdev, pdata); + sof_client_dev_unregister(sdev, "audio", 0); /* * Balance the runtime pm usage count in case we are faced with an diff --git a/sound/soc/sof/sof-client-audio.c b/sound/soc/sof/sof-client-audio.c new file mode 100644 index 00000000000000..ec961dca4443f4 --- /dev/null +++ b/sound/soc/sof/sof-client-audio.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2026 Intel Corporation +// +// Authors: Peter Ujfalusi +// + +#include +#include +#include + +#include "sof-client.h" +#include "sof-client-audio.h" + +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); + struct device *dma_dev = sof_client_get_dma_dev(cdev); + int ret; + + ret = snd_soc_register_component(dma_dev, &pdata->plat_drv, + pdata->drv, pdata->num_drv); + if (ret < 0) + return ret; + + ret = sof_client_machine_register(cdev); + if (ret < 0) { + snd_soc_unregister_component(dma_dev); + return ret; + } + + 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 device *dma_dev = sof_client_get_dma_dev(cdev); + + sof_client_machine_unregister(cdev); + snd_soc_unregister_component(dma_dev); +} + +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, + .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..0e844ac8df540d --- /dev/null +++ b/sound/soc/sof/sof-client-audio.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __SOF_CLIENT_AUDIO_H +#define __SOF_CLIENT_AUDIO_H + +#include + +/** + * 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 + */ +struct sof_audio_client_pdata { + const struct snd_soc_component_driver plat_drv; + struct snd_soc_dai_driver *drv; + int num_drv; + struct snd_soc_acpi_mach machine; +}; + +#endif /* __SOF_CLIENT_AUDIO_H */ From df6868360ef787a7f1bceecccf231eb3723a06c0 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 3 Jun 2026 12:59:04 +0300 Subject: [PATCH 058/100] ASoC: SOF: move audio client registration to vendor ops Move the audio client device registration from core.c's probe path into vendor-controlled callbacks in snd_sof_dsp_ops. This allows each vendor/platform to control the audio client lifecycle. Add register_audio_client/unregister_audio_client callbacks to struct snd_sof_dsp_ops and provide default implementations (sof_register_audio_client/sof_unregister_audio_client) that all vendors use for now. The audio client registration is called from sof_register_clients() before the dspless mode early return, ensuring audio works in both normal and dspless modes. The snd_sof_new_platform_drv() call is moved into sof_register_audio_client(), making core.c no longer responsible for building the platform driver or audio client pdata. With the audio client pdata now built locally in the helper, sdev->plat_drv is no longer needed and is removed from struct snd_sof_dev. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/amd/acp-common.c | 6 +++ sound/soc/sof/core.c | 36 +---------------- sound/soc/sof/imx/imx-common.c | 6 +++ sound/soc/sof/intel/bdw.c | 6 +++ sound/soc/sof/intel/byt.c | 10 +++++ sound/soc/sof/intel/hda-common-ops.c | 5 +++ sound/soc/sof/intel/hda.c | 1 + sound/soc/sof/intel/pci-tng.c | 6 +++ sound/soc/sof/mediatek/mt8186/mt8186.c | 6 +++ sound/soc/sof/mediatek/mt8195/mt8195.c | 6 +++ sound/soc/sof/mediatek/mt8365/mt8365.c | 6 +++ sound/soc/sof/sof-client.c | 56 +++++++++++++++++++++++++- sound/soc/sof/sof-client.h | 9 +++++ sound/soc/sof/sof-priv.h | 10 ++--- 14 files changed, 127 insertions(+), 42 deletions(-) diff --git a/sound/soc/sof/amd/acp-common.c b/sound/soc/sof/amd/acp-common.c index c1cb10815b36cb..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" @@ -230,6 +231,10 @@ const struct snd_sof_dsp_ops sof_acp_common_ops = { .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, @@ -252,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/core.c b/sound/soc/sof/core.c index e64e8f1a62d3c6..5ea83a0c30ac26 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -14,7 +14,6 @@ #include #include "sof-priv.h" #include "sof-of-dev.h" -#include "sof-client-audio.h" #include "ops.h" #define CREATE_TRACE_POINTS @@ -473,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, &sdev->plat_drv); - goto skip_dsp_init; } @@ -500,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, &sdev->plat_drv); - /* * 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. @@ -555,28 +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; - /* Register audio client device */ - { - struct sof_audio_client_pdata audio_pdata = { - .plat_drv = sdev->plat_drv, - .drv = sdev->audio_ops->drv, - .num_drv = sdev->audio_ops->num_drv, - }; - - ret = sof_client_dev_register(sdev, "audio", 0, - &audio_pdata, - sizeof(audio_pdata)); - } - if (ret < 0) { - dev_err(sdev->dev, - "error: failed to register audio client %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 audio_client_err; + goto fw_trace_err; } /* @@ -594,8 +569,6 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) return 0; -audio_client_err: - sof_client_dev_unregister(sdev, "audio", 0); fw_trace_err: sof_fw_trace_free(sdev); fw_run_err: @@ -760,13 +733,6 @@ int snd_sof_device_remove(struct device *dev) */ sof_unregister_clients(sdev); - /* - * Unregister audio client device. This will unregister the machine - * driver and the ASoC component, which will unbind the snd_card, - * unload the topology and free the snd_card. - */ - sof_client_dev_unregister(sdev, "audio", 0); - /* * 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/imx/imx-common.c b/sound/soc/sof/imx/imx-common.c index 0b8aa523659ad5..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,6 +456,10 @@ const struct snd_sof_dsp_ops sof_imx_ops = { .get_bar_index = imx_get_bar_index, .load_firmware = snd_sof_load_firmware_memcpy, + /* audio client */ + .register_audio_client = sof_register_audio_client, + .unregister_audio_client = sof_unregister_audio_client, + .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem, .runtime_suspend = imx_runtime_suspend, @@ -468,3 +473,4 @@ 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/intel/bdw.c b/sound/soc/sof/intel/bdw.c index f7f4058b57e034..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" @@ -607,6 +608,10 @@ 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), @@ -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 1e311e774600ae..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" @@ -264,6 +265,10 @@ 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), @@ -338,6 +343,10 @@ 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), @@ -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/hda-common-ops.c b/sound/soc/sof/intel/hda-common-ops.c index 89920db60e3002..d5c77f739bc514 100644 --- a/sound/soc/sof/intel/hda-common-ops.c +++ b/sound/soc/sof/intel/hda-common-ops.c @@ -11,6 +11,7 @@ */ #include "../sof-priv.h" +#include "../sof-client.h" #include "hda.h" #include "../sof-audio.h" @@ -104,6 +105,10 @@ 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 = sof_register_audio_client, + .unregister_audio_client = sof_unregister_audio_client, + /* DAI drivers */ .is_chain_dma_supported = hda_is_chain_dma_supported, diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index c5eb71f92c017d..ca04651d975718 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -1783,6 +1783,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/pci-tng.c b/sound/soc/sof/intel/pci-tng.c index 8d9740d51f4f22..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" @@ -182,6 +183,10 @@ 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), @@ -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/mediatek/mt8186/mt8186.c b/sound/soc/sof/mediatek/mt8186/mt8186.c index 65b4714a22b818..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" @@ -431,6 +432,10 @@ static const struct snd_sof_dsp_ops sof_mt8186_ops = { /* 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, @@ -582,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 bec41f481dacce..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" @@ -455,6 +456,10 @@ static const struct snd_sof_dsp_ops sof_mt8195_ops = { /* 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, @@ -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 83caf3ae667bae..87b43f4e814254 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" @@ -587,6 +588,10 @@ static struct snd_sof_dsp_ops sof_mt8365_ops = { /* 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, @@ -646,6 +651,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/sof-client.c b/sound/soc/sof/sof-client.c index 520c06f7e42069..f0854d8cf32b8f 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -15,6 +15,7 @@ #include #include "ops.h" #include "sof-client.h" +#include "sof-client-audio.h" #include "sof-priv.h" #include "ipc3-priv.h" #include "ipc4-priv.h" @@ -164,6 +165,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; @@ -171,7 +182,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); @@ -211,6 +222,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; } @@ -223,6 +238,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, @@ -806,7 +825,42 @@ 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) +{ + void *plat_drv = (void *)&pdata->plat_drv; + + memset(pdata, 0, sizeof(*pdata)); + snd_sof_new_platform_drv(sdev, plat_drv); + 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 0b455e51d6e979..4d7da648dfc2f5 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -142,4 +142,13 @@ u32 sof_client_get_new_comp_id(struct sof_client_dev *cdev); 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 a760192ed4b164..c8ce4c287e50a1 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -397,6 +397,10 @@ 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 */ + /* 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 */ @@ -604,12 +608,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 */ From 12e87a206d2f1d6ebf15e338f2ba6e701464ae22 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 27 Jul 2026 16:17:33 +0300 Subject: [PATCH 059/100] ASoC: soc-acpi: add card_name to snd_soc_acpi_mach_params When several cards are created on top of a single audio DSP the machine parameters need to carry the name of the card the machine driver is expected to register, as the driver level name is no longer unique. Add the card_name field to struct snd_soc_acpi_mach_params. It is optional, machine drivers keep their current name when it is not set. No functional change, the field has no producer or consumer yet. Signed-off-by: Peter Ujfalusi --- include/sound/soc-acpi.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h index 0519afd7217f13..f0bba5fd20f195 100644 --- a/include/sound/soc-acpi.h +++ b/include/sound/soc-acpi.h @@ -74,6 +74,7 @@ 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 */ struct snd_soc_acpi_mach_params { u32 acpi_ipc_irq_index; @@ -90,6 +91,7 @@ struct snd_soc_acpi_mach_params { unsigned short subsystem_rev; bool subsystem_id_set; u32 bt_link_mask; + const char *card_name; }; /** From 70cc9e0502447a71e9c8ab7eeaad0351ce723afd Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 3 Jun 2026 14:10:44 +0300 Subject: [PATCH 060/100] ASoC: SOF: Register audio component on the auxiliary device Register the ASoC component on the sof-client auxiliary device (&cdev->auxdev.dev) instead of the parent DSP device (sdev->dev). This gives a natural path from the component to the client device: snd_soc_component_get_drvdata() returns the cdev directly. A new helper snd_sof_component_get_cdev() is added, and the existing snd_sof_component_get_sdev() is updated to go through the client device using sof_client_dev_to_sof_dev(). The machine driver platform name (mach_params.platform) is updated in sof_client_machine_register() to match the auxiliary device name, ensuring machine driver platform matching works correctly. The nocodec path is updated to receive the platform name as a parameter instead of hardcoding dev_name(dev->parent). Add sof_client_core_module_get/put calls in PCM open/close to ensure the core DSP driver module use count is incremented while audio is active. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/debug.c | 2 -- sound/soc/sof/nocodec.c | 14 ++++---- sound/soc/sof/pcm.c | 35 ++++++++++++++++-- sound/soc/sof/sof-audio.h | 8 ++++- sound/soc/sof/sof-client-audio.c | 62 +++++++++++++++++++++++++++++--- sound/soc/sof/sof-client-audio.h | 12 ++++++- sound/soc/sof/sof-client.c | 38 +++++++++++++++++--- sound/soc/sof/topology.c | 20 +++++++---- 8 files changed, 162 insertions(+), 29 deletions(-) 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/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/pcm.c b/sound/soc/sof/pcm.c index 3283d43eaa8f6c..3b169804824bef 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -17,6 +17,8 @@ #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" @@ -549,6 +551,7 @@ 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 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; @@ -559,6 +562,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; @@ -595,6 +602,7 @@ static int sof_pcm_open(struct snd_soc_component *component, if (ret < 0) { spcm_err(spcm, substream->stream, "platform pcm open failed %d\n", ret); + sof_client_core_module_put(cdev); return ret; } @@ -611,6 +619,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 sof_client_dev *cdev = snd_sof_component_get_cdev(component); struct snd_sof_pcm *spcm; int err; @@ -636,6 +645,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; } @@ -767,9 +778,12 @@ EXPORT_SYMBOL(sof_pcm_dai_link_fixup); static int sof_pcm_probe(struct snd_soc_component *component) { - struct snd_sof_dev *sdev = snd_sof_component_get_sdev(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; @@ -786,11 +800,14 @@ static int sof_pcm_probe(struct snd_soc_component *component) snd_sof_audio_instance_unregister(instance); return ret; } + got_runtime_pm = ret >= 0; /* load the default topology */ 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; @@ -803,9 +820,21 @@ static int sof_pcm_probe(struct snd_soc_component *component) ret); out: - if (ret) + 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); - pm_runtime_put_autosuspend(component->dev); + } + if (got_runtime_pm) + pm_runtime_put_autosuspend(component->dev); return ret; } diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 37e2995b503469..2fbea3e6d0bbc1 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -636,10 +636,16 @@ 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 snd_soc_component_get_drvdata(scomp); + return sof_client_dev_to_sof_dev(snd_sof_component_get_cdev(scomp)); } struct snd_sof_audio_instance * diff --git a/sound/soc/sof/sof-client-audio.c b/sound/soc/sof/sof-client-audio.c index ec961dca4443f4..f5b0057926d081 100644 --- a/sound/soc/sof/sof-client-audio.c +++ b/sound/soc/sof/sof-client-audio.c @@ -6,41 +6,93 @@ // #include +#include #include +#include +#include #include #include "sof-client.h" #include "sof-client-audio.h" +#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 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); - struct device *dma_dev = sof_client_get_dma_dev(cdev); int ret; - ret = snd_soc_register_component(dma_dev, &pdata->plat_drv, + auxiliary_set_drvdata(auxdev, cdev); + + ret = snd_soc_register_component(&auxdev->dev, &pdata->plat_drv, pdata->drv, pdata->num_drv); if (ret < 0) return ret; ret = sof_client_machine_register(cdev); if (ret < 0) { - snd_soc_unregister_component(dma_dev); + snd_soc_unregister_component(&auxdev->dev); 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 device *dma_dev = sof_client_get_dma_dev(cdev); + struct sof_audio_client_pdata *pdata = dev_get_platdata(&auxdev->dev); + + 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(dma_dev); + snd_soc_unregister_component(&auxdev->dev); } static const struct auxiliary_device_id sof_audio_client_id_table[] = { diff --git a/sound/soc/sof/sof-client-audio.h b/sound/soc/sof/sof-client-audio.h index 0e844ac8df540d..9db10aa7889ada 100644 --- a/sound/soc/sof/sof-client-audio.h +++ b/sound/soc/sof/sof-client-audio.h @@ -5,18 +5,28 @@ #include +struct dentry; + /** * 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 + * @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 { - const struct snd_soc_component_driver plat_drv; + struct snd_soc_component_driver plat_drv; struct snd_soc_dai_driver *drv; int num_drv; struct snd_soc_acpi_mach machine; + struct dentry *debugfs_root; + const char *debug_topology_name; + const char *debug_card_name; + const char *debug_machine_driver; }; #endif /* __SOF_CLIENT_AUDIO_H */ diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index f0854d8cf32b8f..5aacb251f1d100 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include "ops.h" @@ -817,8 +818,39 @@ 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 snd_sof_machine_register(sdev, sdev->pdata); + 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"); @@ -839,10 +871,8 @@ 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) { - void *plat_drv = (void *)&pdata->plat_drv; - memset(pdata, 0, sizeof(*pdata)); - snd_sof_new_platform_drv(sdev, plat_drv); + snd_sof_new_platform_drv(sdev, &pdata->plat_drv); pdata->drv = sdev->audio_ops->drv; pdata->num_drv = sdev->audio_ops->num_drv; } diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 5d40843af10f61..e262cb1eb1ab4c 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; @@ -2552,8 +2554,12 @@ static const struct snd_soc_tplg_ops sof_dspless_tplg_ops = { int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) { 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_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; @@ -2567,7 +2573,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 @@ -2582,11 +2588,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; From 4272beb487a748e938803b8bfee07e1e76f57388 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 16 Jul 2026 15:32:38 +0300 Subject: [PATCH 061/100] ASoC: SOF: topology: use sof_client IPC wrappers Replace direct IPC send paths in IPC3/IPC4 topology helpers with sof_client wrapper calls derived from component context. This removes direct sdev->ipc usage for message send/set_get in: - ipc3-topology - ipc4-topology Signed-off-by: Peter Ujfalusi --- sound/soc/sof/ipc3-topology.c | 37 ++++++++++++++++++----------------- sound/soc/sof/ipc4-topology.c | 17 ++++++++++------ 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index 1d9cb9ebec167a..c54557e60dbd82 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" @@ -1780,6 +1781,7 @@ 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; @@ -1793,7 +1795,7 @@ static int sof_ipc3_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * 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(scomp->dev, "%s: route %s -> %s failed\n", __func__, sroute->src_widget->widget->name, sroute->sink_widget->widget->name); @@ -1936,6 +1938,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; @@ -1943,14 +1946,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_sof_component_get_sdev(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; @@ -1994,7 +1997,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); @@ -2006,7 +2009,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_sof_component_get_sdev(scomp); + struct sof_client_dev *cdev = snd_sof_component_get_cdev(scomp); struct sof_ipc_stream stream; int ret; @@ -2016,7 +2019,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); @@ -2133,6 +2136,7 @@ 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; @@ -2144,7 +2148,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; @@ -2153,6 +2157,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), @@ -2179,7 +2184,7 @@ 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(swidget->scomp->dev, "failed to free widget %s\n", swidget->widget->name); @@ -2280,7 +2285,8 @@ 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(swidget->scomp->dev, "Failed to set dai config for %s\n", dai->name); @@ -2294,6 +2300,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) @@ -2305,9 +2312,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: @@ -2315,18 +2321,13 @@ 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(swidget->scomp->dev, "Failed to setup widget %s\n", swidget->widget->name); diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index ad1e547b08fb04..66565c7833041f 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" @@ -3487,6 +3488,7 @@ static int sof_ipc4_widget_pipe_create_msg_payload(struct snd_sof_widget *swidge 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; @@ -3702,7 +3704,7 @@ 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) { @@ -3724,6 +3726,7 @@ 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; @@ -3749,7 +3752,7 @@ 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(scomp->dev, "failed to free pipeline widget %s\n", swidget->widget->name); @@ -3859,8 +3862,8 @@ static int sof_ipc4_set_copier_sink_format(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_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; @@ -3901,12 +3904,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; @@ -3990,7 +3994,7 @@ 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(scomp->dev, "failed to bind modules %s:%d -> %s:%d\n", src_widget->widget->name, sroute->src_queue_id, @@ -4009,6 +4013,7 @@ 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; @@ -4050,7 +4055,7 @@ 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(scomp->dev, "failed to unbind modules %s:%d -> %s:%d\n", src_widget->widget->name, sroute->src_queue_id, From 8aa474c97abc93b7d10de88c96d8f744f078740e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 17 Jul 2026 10:37:17 +0300 Subject: [PATCH 062/100] ASoC: SOF: pcm: use sof_client IPC wrappers Replace direct IPC send paths in IPC3/IPC4 PCM helpers with sof_client wrapper calls derived from component context. This removes direct sdev->ipc usage for message send paths in: - ipc3-pcm - ipc4-pcm (chain DMA and pipeline state) Signed-off-by: Peter Ujfalusi --- sound/soc/sof/ipc3-pcm.c | 14 ++++++++------ sound/soc/sof/ipc4-pcm.c | 14 +++++++++----- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c index f6a2a618471c0a..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_sof_component_get_sdev(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,6 +37,7 @@ 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 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; @@ -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_sof_component_get_sdev(component); + struct sof_client_dev *cdev = snd_sof_component_get_cdev(component); struct sof_ipc_stream stream; stream.hdr.size = sizeof(stream); @@ -167,7 +169,7 @@ 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_soc_component *component, diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 04e0b222de0ac5..2060ab53604c3f 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 @@ -67,6 +68,7 @@ char *sof_ipc4_set_multi_pipeline_state_debug(struct snd_sof_dev *sdev, char *bu 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; @@ -97,12 +99,12 @@ static int sof_ipc4_set_multi_pipeline_state(struct snd_soc_component *component 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_soc_component *component, u32 instance_id, u32 state) { - struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); + struct sof_client_dev *cdev = snd_sof_component_get_cdev(component); struct sof_ipc4_msg msg = {{ 0 }}; u32 primary; @@ -117,7 +119,7 @@ int sof_ipc4_set_pipeline_state(struct snd_soc_component *component, u32 instanc msg.primary = primary; - return sof_ipc_tx_message_no_reply(sdev->ipc, &msg, 0); + return sof_client_ipc_tx_message_no_reply(cdev, &msg); } EXPORT_SYMBOL(sof_ipc4_set_pipeline_state); @@ -296,6 +298,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) @@ -389,7 +392,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; @@ -402,6 +405,7 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, struct snd_sof_pcm *spcm, int dir) { 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; @@ -437,7 +441,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; From 9aa0e135ec9593a133ae70593505ac8043865926 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 17 Jul 2026 10:59:53 +0300 Subject: [PATCH 063/100] ASoC: SOF: ipc3-compress: use sof_client IPC wrappers Replace direct IPC send paths in IPC3 compress helpers with sof_client wrapper calls derived from component context. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/ipc3-compress.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/sound/soc/sof/ipc3-compress.c b/sound/soc/sof/ipc3-compress.c index 09716d45331a62..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_sof_component_get_sdev(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,6 +84,7 @@ static int sof_ipc3_compr_set_params(struct snd_soc_component *component, struct snd_compr_stream *cstream, struct snd_compr_params *params) { + 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; @@ -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_sof_component_get_sdev(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, From 22824210e1e2f3338dcecd293b3df2c28ba871d6 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 27 Jul 2026 16:05:06 +0300 Subject: [PATCH 064/100] ASoC: SOF: ipc3-topology: use sof_client_get_ssp_mclk_id_quirk() sof_link_ssp_load() is the only consumer of the SSP MCLK ID quirk. Use the sof-client accessor to query it, which also removes the last snd_sof_component_get_sdev() user from this function. No functional change. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/ipc3-topology.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index c54557e60dbd82..fee899551c9661 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -1430,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_sof_component_get_sdev(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; /* @@ -1455,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 */ From c807112ec69af216c9b3d4c12848ef8481c8c7ab Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 27 Jul 2026 14:53:53 +0300 Subject: [PATCH 065/100] ASoC: SOF: pcm: use sof_client_is_suspend_target_s0ix() Query the system suspend target through the sof-client API in sof_pcm_trigger() instead of reading sdev->system_suspend_target directly. No functional change. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/pcm.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 3b169804824bef..a9d004be7f5c77 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -396,8 +396,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_sof_component_get_sdev(component); 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; @@ -443,7 +444,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; From a5c6fae03bf612a6edc2757411c60425abc9707d Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 28 Jul 2026 09:22:14 +0300 Subject: [PATCH 066/100] ASoC: SOF: Set up the topology pipelines per audio instance set_up_all_pipelines() takes the SOF device and walks the widget and route lists of every audio instance backed by that DSP. When more than one sound card is served by the same DSP this couples unrelated instances: setting up one card also (re)creates the static pipelines of the other cards. Pass the ASoC component to the callback instead, so that it only handles the audio instance the component belongs to, and add sof_instance_set_up_pipelines() to set up a single instance. The firmware boot is a DSP wide event which invalidates the state of every instance, so introduce sof_restore_pipelines() to walk all instances of the DSP and use it from snd_sof_boot_dsp_firmware(). Signed-off-by: Peter Ujfalusi --- sound/soc/sof/ipc3-topology.c | 14 +++++++++----- sound/soc/sof/pm.c | 15 ++++++--------- sound/soc/sof/sof-audio.c | 30 ++++++++++++++++++++++++++++++ sound/soc/sof/sof-audio.h | 7 +++++-- sound/soc/sof/topology.c | 7 ++----- 5 files changed, 52 insertions(+), 21 deletions(-) diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index fee899551c9661..3b4e5dd5118620 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -2336,16 +2336,20 @@ static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget 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_audio_instance *instance; struct snd_sof_widget *swidget; struct snd_sof_route *sroute; int ret; + if (!instance) + return 0; + /* restore pipeline components */ - for_each_swidget_in_instances(swidget, sdev, instance) { + 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; @@ -2387,7 +2391,7 @@ static int sof_ipc3_set_up_all_pipelines(struct snd_sof_dev *sdev, bool verify) } /* restore pipeline connections */ - for_each_sroute_in_instances(sroute, sdev, instance) { + 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)) @@ -2410,7 +2414,7 @@ static int sof_ipc3_set_up_all_pipelines(struct snd_sof_dev *sdev, bool verify) } /* complete pipeline */ - for_each_swidget_in_instances(swidget, sdev, instance) { + list_for_each_entry(swidget, &instance->widget_list, list) { switch (swidget->id) { case snd_soc_dapm_scheduler: /* only complete static pipelines */ diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 752a9e3210412b..9116d5816e2b7f 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -78,7 +78,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 = snd_sof_sdev_get_tplg_ops(sdev); int ret; guard(mutex)(&sdev->dsp_fw_boot_mutex); @@ -123,14 +122,12 @@ 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; - } + /* restore the pipelines of all audio instances backed by this DSP */ + ret = sof_restore_pipelines(sdev); + if (ret < 0) { + dev_err(sdev->dev, "%s: failed to restore pipeline: %d\n", + __func__, ret); + goto setup_fail; } /* Notify clients not managed by pm framework about core resume */ diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 7626da4f5df23a..ae70bbfe2eb32f 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -142,6 +142,36 @@ const struct sof_ipc_pcm_ops *snd_sof_component_get_pcm_ops(struct snd_soc_compo } EXPORT_SYMBOL(snd_sof_component_get_pcm_ops); +/* + * Set up the static pipelines of a single audio instance. + */ +int sof_instance_set_up_pipelines(struct snd_sof_audio_instance *instance) +{ + if (!instance->tplg_ops || !instance->tplg_ops->set_up_all_pipelines) + return 0; + + return instance->tplg_ops->set_up_all_pipelines(instance->component, false); +} + +/* + * Set up the static pipelines of every audio instance. Called after the DSP + * firmware has been (re)booted, which invalidates the state of all instances + * backed by that DSP. + */ +int sof_restore_pipelines(struct snd_sof_dev *sdev) +{ + struct snd_sof_audio_instance *instance; + int ret; + + list_for_each_entry(instance, &sdev->audio_instance_list, list) { + ret = sof_instance_set_up_pipelines(instance); + if (ret < 0) + return ret; + } + + return 0; +} + /* * 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 diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 2fbea3e6d0bbc1..0f266f23f4694f 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -234,7 +234,8 @@ 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 + * @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 all topology pipelines * @parse_manifest: Function pointer for ipc4 specific parsing of topology manifest * @link_setup: Function pointer for IPC-specific DAI link set up @@ -257,7 +258,7 @@ 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 (*set_up_all_pipelines)(struct snd_soc_component *component, bool verify); int (*tear_down_all_pipelines)(struct snd_sof_dev *sdev, bool verify); int (*parse_manifest)(struct snd_soc_component *scomp, int index, struct snd_soc_tplg_manifest *man); @@ -658,6 +659,8 @@ const struct sof_ipc_tplg_ops *snd_sof_sdev_get_tplg_ops(struct snd_sof_dev *sde const struct sof_ipc_pcm_ops *snd_sof_sdev_get_pcm_ops(struct snd_sof_dev *sdev); const struct sof_ipc_tplg_ops *snd_sof_component_get_tplg_ops(struct snd_soc_component *component); const struct sof_ipc_pcm_ops *snd_sof_component_get_pcm_ops(struct snd_soc_component *component); +int sof_instance_set_up_pipelines(struct snd_sof_audio_instance *instance); +int sof_restore_pipelines(struct snd_sof_dev *sdev); static inline struct snd_sof_pcm *snd_sof_find_spcm_dai(struct snd_soc_component *scomp, diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index e262cb1eb1ab4c..26c55ca54a290a 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2312,7 +2312,7 @@ 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(scomp->dev, "Failed to set up all topology pipelines: %d\n", ret); @@ -2329,10 +2329,7 @@ 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 */ From 1a1401db6b3d27586d296287f529be53eff11ef4 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 28 Jul 2026 09:25:16 +0300 Subject: [PATCH 067/100] ASoC: SOF: Tear down the topology pipelines from the audio client tear_down_all_pipelines() takes the SOF device and frees the widgets and routes of every audio instance backed by that DSP. It is called from sof_suspend() and, for the one time topology verification, from sof_complete(). With multiple sound cards sharing a DSP both callers are wrong: the suspend of one card frees the pipelines still in use by another card, and the topology verification of a card being probed tears down the pipelines of the cards which are already up. Pass the ASoC component to the callback so that it only frees the pipelines of the audio instance the component belongs to, and convert sof_pcm_free_all_streams() the same way. The audio clients are children of the SOF device, hence the PM core runs their suspend callbacks before the one of the SOF device. Install dev_pm_ops for the audio client auxiliary driver and move the teardown there, so that each instance frees only what it owns. The component is stored in the client platform data to make it reachable from the client PM callback. This removes the last user of for_each_sroute_in_instances(), delete it. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/ipc3-topology.c | 37 +++++++++++++++++++------------- sound/soc/sof/ipc4-topology.c | 6 +++--- sound/soc/sof/pcm.c | 17 ++++++++++++--- sound/soc/sof/pm.c | 16 ++++++-------- sound/soc/sof/sof-audio.h | 11 ++++------ sound/soc/sof/sof-client-audio.c | 16 ++++++++++++++ sound/soc/sof/sof-client-audio.h | 2 ++ sound/soc/sof/sof-client.c | 33 ++++++++++++++++++++++++++++ sound/soc/sof/sof-client.h | 3 +++ sound/soc/sof/topology.c | 2 +- 10 files changed, 104 insertions(+), 39 deletions(-) diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index 3b4e5dd5118620..ad36348e13f979 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -2443,9 +2443,10 @@ static int sof_ipc3_set_up_all_pipelines(struct snd_soc_component *component, bo * 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; + 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; @@ -2454,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; @@ -2462,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. */ - for_each_swidget_in_instances(swidget, sdev, instance) + 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) @@ -2472,15 +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; + 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; - for_each_swidget_in_instances(swidget, sdev, instance) { + list_for_each_entry(swidget, &instance->widget_list, list) { if (swidget->dynamic_pipeline_widget) { *dyn_widgets = true; continue; @@ -2515,15 +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_audio_instance *instance; 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 @@ -2532,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; @@ -2544,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; - for_each_sroute_in_instances(sroute, sdev, instance) + list_for_each_entry(sroute, &instance->route_list, list) sroute->setup = false; /* @@ -2564,7 +2571,7 @@ 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 */ - for_each_swidget_in_instances(swidget, sdev, instance) { + list_for_each_entry(swidget, &instance->widget_list, list) { if (swidget->use_count != 0) { dev_err(swidget->scomp->dev, "%s: widget %s is still in use: count %d\n", diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 66565c7833041f..c098da4e8fa187 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -4279,11 +4279,11 @@ static int sof_ipc4_dai_get_param(struct snd_sof_dev *sdev, struct snd_sof_dai * 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 @@ -4293,7 +4293,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/pcm.c b/sound/soc/sof/pcm.c index a9d004be7f5c77..e903a9a419458b 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -266,14 +266,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; + 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; - for_each_spcm_in_instances(spcm, sdev, instance) { + if (!instance) + return 0; + + list_for_each_entry(spcm, &instance->pcm_list, list) { for_each_pcm_streams(dir) { substream = spcm->stream[dir].substream; @@ -792,6 +796,8 @@ static int sof_pcm_probe(struct snd_soc_component *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 @@ -833,6 +839,7 @@ static int sof_pcm_probe(struct snd_soc_component *component) */ 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); @@ -843,6 +850,10 @@ static int sof_pcm_probe(struct snd_soc_component *component) 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); diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 9116d5816e2b7f..63ec443631011a 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -227,12 +227,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 = snd_sof_sdev_get_tplg_ops(sdev); 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; @@ -240,14 +244,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.h b/sound/soc/sof/sof-audio.h index 0f266f23f4694f..51664c4a399660 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -47,10 +47,6 @@ (id) == snd_soc_dapm_decoder || \ (id) == snd_soc_dapm_encoder) -#define for_each_sroute_in_instances(sroute, sdev, instance) \ - list_for_each_entry(instance, &(sdev)->audio_instance_list, list) \ - list_for_each_entry(sroute, &(instance)->route_list, list) - #define for_each_spcm_in_instances(spcm, sdev, instance) \ list_for_each_entry(instance, &(sdev)->audio_instance_list, list) \ list_for_each_entry(spcm, &(instance)->pcm_list, list) @@ -236,7 +232,8 @@ struct sof_ipc_tplg_widget_ops { * @dai_get_param: Function pointer for getting the DAI parameter * @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 all topology pipelines + * @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 * @@ -259,7 +256,7 @@ struct sof_ipc_tplg_ops { 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_soc_component *component, bool verify); - int (*tear_down_all_pipelines)(struct snd_sof_dev *sdev, 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); @@ -757,7 +754,7 @@ void sof_widget_list_unprepare(struct snd_soc_component *component, struct snd_s 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.c b/sound/soc/sof/sof-client-audio.c index f5b0057926d081..5d2efe8bfabfeb 100644 --- a/sound/soc/sof/sof-client-audio.c +++ b/sound/soc/sof/sof-client-audio.c @@ -95,6 +95,18 @@ static void sof_audio_client_remove(struct auxiliary_device *auxdev) snd_soc_unregister_component(&auxdev->dev); } +static int sof_audio_client_suspend(struct device *dev) +{ + struct auxiliary_device *auxdev = to_auxiliary_dev(dev); + + return sof_client_audio_suspend(auxiliary_dev_to_sof_client_dev(auxdev)); +} + +static const struct dev_pm_ops sof_audio_client_pm = { + SYSTEM_SLEEP_PM_OPS(sof_audio_client_suspend, NULL) + RUNTIME_PM_OPS(sof_audio_client_suspend, NULL, NULL) +}; + static const struct auxiliary_device_id sof_audio_client_id_table[] = { { .name = "snd_sof.audio", }, {}, @@ -104,6 +116,10 @@ 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, }; diff --git a/sound/soc/sof/sof-client-audio.h b/sound/soc/sof/sof-client-audio.h index 9db10aa7889ada..258a4f9f1e66c0 100644 --- a/sound/soc/sof/sof-client-audio.h +++ b/sound/soc/sof/sof-client-audio.h @@ -13,6 +13,7 @@ struct dentry; * @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 * @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 @@ -23,6 +24,7 @@ struct sof_audio_client_pdata { struct snd_soc_dai_driver *drv; int num_drv; struct snd_soc_acpi_mach machine; + struct snd_soc_component *component; struct dentry *debugfs_root; const char *debug_topology_name; const char *debug_card_name; diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index 5aacb251f1d100..328899b926db62 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -15,6 +15,7 @@ #include #include #include "ops.h" +#include "sof-audio.h" #include "sof-client.h" #include "sof-client-audio.h" #include "sof-priv.h" @@ -868,6 +869,38 @@ void sof_client_machine_unregister(struct sof_client_dev *cdev) } EXPORT_SYMBOL_NS_GPL(sof_client_machine_unregister, "SND_SOC_SOF_CLIENT"); +/* + * 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. + */ +int sof_client_audio_suspend(struct sof_client_dev *cdev) +{ + struct sof_audio_client_pdata *pdata = dev_get_platdata(&cdev->auxdev.dev); + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + const struct sof_ipc_tplg_ops *tplg_ops; + + 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 (sdev->dsp_power_state.state != SOF_DSP_PM_D0) + return 0; + + tplg_ops = snd_sof_component_get_tplg_ops(pdata->component); + if (!tplg_ops || !tplg_ops->tear_down_all_pipelines) + return 0; + + return tplg_ops->tear_down_all_pipelines(pdata->component, false); +} +EXPORT_SYMBOL_NS_GPL(sof_client_audio_suspend, "SND_SOC_SOF_CLIENT"); + void sof_audio_client_init_pdata(struct snd_sof_dev *sdev, struct sof_audio_client_pdata *pdata) { diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index 4d7da648dfc2f5..6dd00eb24013cf 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -142,6 +142,9 @@ u32 sof_client_get_new_comp_id(struct sof_client_dev *cdev); int sof_client_machine_register(struct sof_client_dev *cdev); void sof_client_machine_unregister(struct sof_client_dev *cdev); +/* audio client power management */ +int sof_client_audio_suspend(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, diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 26c55ca54a290a..977f0b102842dc 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2319,7 +2319,7 @@ static int sof_complete(struct snd_soc_component *scomp) 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(scomp->dev, "Failed to tear down topology pipelines: %d\n", ret); From bae281359a5008fe22c4ae3e8eebe262be631527 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 28 Jul 2026 09:25:55 +0300 Subject: [PATCH 068/100] ASoC: SOF: Restore the topology pipelines from the audio client resume Since the pipelines are torn down by the audio client suspend, an instance can lose its static pipelines while the DSP itself stays in D0, for example when another audio client keeps the SOF device active. On the resume of such an instance snd_sof_boot_dsp_firmware() returns early because the firmware is still booted, so nothing recreates the pipelines of the instance. Set up the pipelines of the instance from the audio client resume to close this gap. The setup is skipped if the firmware is not booted since in that case the pipelines are set up by snd_sof_boot_dsp_firmware(). The firmware boot can also be triggered by paths which are not backed by an audio client, like the SoundWire BPT transfers or debugfs, and the firmware can be rebooted while the instances are resumed. Both the boot and the client resume can therefore set up the same instance, so track the state in the instance to keep the setup idempotent and to avoid unbalanced widget use counts. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-audio.c | 20 +++++++++++-- sound/soc/sof/sof-client-audio.c | 11 ++++++-- sound/soc/sof/sof-client.c | 48 ++++++++++++++++++++++++++++++-- sound/soc/sof/sof-client.h | 1 + sound/soc/sof/sof-priv.h | 1 + 5 files changed, 75 insertions(+), 6 deletions(-) diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index ae70bbfe2eb32f..a5c7ef0e911f50 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -143,14 +143,27 @@ const struct sof_ipc_pcm_ops *snd_sof_component_get_pcm_ops(struct snd_soc_compo EXPORT_SYMBOL(snd_sof_component_get_pcm_ops); /* - * Set up the static pipelines of a single audio instance. + * 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; - return instance->tplg_ops->set_up_all_pipelines(instance->component, false); + ret = instance->tplg_ops->set_up_all_pipelines(instance->component, false); + if (ret < 0) + return ret; + + instance->pipelines_set_up = true; + + return 0; } /* @@ -164,6 +177,9 @@ int sof_restore_pipelines(struct snd_sof_dev *sdev) int ret; list_for_each_entry(instance, &sdev->audio_instance_list, list) { + /* nothing of the previous state is left in the DSP */ + instance->pipelines_set_up = false; + ret = sof_instance_set_up_pipelines(instance); if (ret < 0) return ret; diff --git a/sound/soc/sof/sof-client-audio.c b/sound/soc/sof/sof-client-audio.c index 5d2efe8bfabfeb..08bda68c534835 100644 --- a/sound/soc/sof/sof-client-audio.c +++ b/sound/soc/sof/sof-client-audio.c @@ -102,9 +102,16 @@ static int sof_audio_client_suspend(struct device *dev) return sof_client_audio_suspend(auxiliary_dev_to_sof_client_dev(auxdev)); } +static int sof_audio_client_resume(struct device *dev) +{ + struct auxiliary_device *auxdev = to_auxiliary_dev(dev); + + return sof_client_audio_resume(auxiliary_dev_to_sof_client_dev(auxdev)); +} + static const struct dev_pm_ops sof_audio_client_pm = { - SYSTEM_SLEEP_PM_OPS(sof_audio_client_suspend, NULL) - RUNTIME_PM_OPS(sof_audio_client_suspend, NULL, NULL) + 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[] = { diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index 328899b926db62..62d45486a37550 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -881,7 +881,9 @@ int sof_client_audio_suspend(struct sof_client_dev *cdev) { struct sof_audio_client_pdata *pdata = dev_get_platdata(&cdev->auxdev.dev); struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + struct snd_sof_audio_instance *instance; const struct sof_ipc_tplg_ops *tplg_ops; + int ret; if (!pdata || !pdata->component) return 0; @@ -893,14 +895,56 @@ int sof_client_audio_suspend(struct sof_client_dev *cdev) if (sdev->dsp_power_state.state != SOF_DSP_PM_D0) return 0; - tplg_ops = snd_sof_component_get_tplg_ops(pdata->component); + instance = snd_sof_component_get_audio_instance(pdata->component); + if (!instance) + return 0; + + tplg_ops = instance->tplg_ops; if (!tplg_ops || !tplg_ops->tear_down_all_pipelines) return 0; - return tplg_ops->tear_down_all_pipelines(pdata->component, false); + ret = tplg_ops->tear_down_all_pipelines(pdata->component, false); + if (ret < 0) + return ret; + + instance->pipelines_set_up = false; + + return 0; } EXPORT_SYMBOL_NS_GPL(sof_client_audio_suspend, "SND_SOC_SOF_CLIENT"); +/* + * 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. + */ +int sof_client_audio_resume(struct sof_client_dev *cdev) +{ + struct sof_audio_client_pdata *pdata = dev_get_platdata(&cdev->auxdev.dev); + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + struct snd_sof_audio_instance *instance; + + 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 (sdev->fw_state != SOF_FW_BOOT_COMPLETE) + return 0; + + instance = snd_sof_component_get_audio_instance(pdata->component); + if (!instance) + return 0; + + return sof_instance_set_up_pipelines(instance); +} +EXPORT_SYMBOL_NS_GPL(sof_client_audio_resume, "SND_SOC_SOF_CLIENT"); + void sof_audio_client_init_pdata(struct snd_sof_dev *sdev, struct sof_audio_client_pdata *pdata) { diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index 6dd00eb24013cf..1e7b74699c0727 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -144,6 +144,7 @@ void sof_client_machine_unregister(struct sof_client_dev *cdev); /* audio client power management */ int sof_client_audio_suspend(struct sof_client_dev *cdev); +int sof_client_audio_resume(struct sof_client_dev *cdev); /* audio client pdata initialization */ struct sof_audio_client_pdata; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index c8ce4c287e50a1..465d3df892637d 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -197,6 +197,7 @@ struct snd_sof_audio_instance { struct list_head kcontrol_list; struct list_head widget_list; bool led_present; + bool pipelines_set_up; struct list_head list; }; From dfef606eb2556bb9d9f576195b81ea6c2e5032c1 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 28 Jul 2026 13:53:11 +0300 Subject: [PATCH 069/100] ASoC: SOF: Move the IPC4 module notification handling to the audio client The IPC4 module notification is consumed exclusively by audio functionality: it either carries an ALSA kcontrol update or the end-of-stream indication of a compressed stream. Handling it in ipc4.c means that the core has to reach into the audio scope, which is one of the ties that prevents the PCM, topology and control code from being built as part of the audio client module. Introduce sof-client-audio-ipc4.c as the IPC version specific glue of the audio client and move the demultiplexing of the module notification there. The audio client registers a struct sof_client_ops with an ipc_rx_handler which forwards the notification to the IPC version specific ops, selected at probe time based on the IPC type. This follows the pattern already used by the probes client. The core keeps the ownership of the message payload. The variable sized part of the module notification is only known after the fixed part has been read, so sof_ipc4_module_notification_handler() is reduced to sof_ipc4_module_notification_get_payload(), which grows the buffer and re-reads the payload but no longer acts on it. The notification is dispatched to the clients afterwards, therefore they receive a complete, read-only message. The audio client resolves the topology ops via its own component instead of via the SOF device, which scopes the kcontrol update to the audio instance owning the component. The component is set and cleared together with the audio instance registration, so the window in which the notification is acted upon is unchanged. sof_ipc4_compr_drain_done() is exported for now to keep the call from the client module working. The export goes away once the compress code moves into the audio client module as well. No functional change intended. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/Makefile | 3 ++ sound/soc/sof/ipc4-compress.c | 1 + sound/soc/sof/ipc4.c | 76 +++++++++++---------------- sound/soc/sof/sof-client-audio-ipc4.c | 68 ++++++++++++++++++++++++ sound/soc/sof/sof-client-audio.c | 38 +++++++++++++- sound/soc/sof/sof-client-audio.h | 18 +++++++ 6 files changed, 157 insertions(+), 47 deletions(-) create mode 100644 sound/soc/sof/sof-client-audio-ipc4.c diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index 64d4159a2d2388..fd8d8606ef3d9b 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -29,6 +29,9 @@ 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 +ifneq ($(CONFIG_SND_SOC_SOF_IPC4),) +snd-sof-audio-y += sof-client-audio-ipc4.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 diff --git a/sound/soc/sof/ipc4-compress.c b/sound/soc/sof/ipc4-compress.c index cb1761cac2d158..6305ce7de253ba 100644 --- a/sound/soc/sof/ipc4-compress.c +++ b/sound/soc/sof/ipc4-compress.c @@ -765,6 +765,7 @@ void sof_ipc4_compr_drain_done(struct snd_sof_dev *sdev, void *ipc_message) if (spcm->stream[dir].cstream) snd_compr_drain_notify(spcm->stream[dir].cstream); } +EXPORT_SYMBOL(sof_ipc4_compr_drain_done); const struct snd_compress_ops sof_ipc4_compressed_ops = { .open = sof_ipc4_compr_open, diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c index 2c311c97b1a52a..c7f97942d1334d 100644 --- a/sound/soc/sof/ipc4.c +++ b/sound/soc/sof/ipc4.c @@ -746,58 +746,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 = snd_sof_sdev_get_tplg_ops(sdev); - - 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 +826,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", 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..88d6ca05a6c81e --- /dev/null +++ b/sound/soc/sof/sof-client-audio-ipc4.c @@ -0,0 +1,68 @@ +// 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; + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + /* 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(sdev, ipc4_msg); + + break; + } + case SOF_IPC4_NOTIFY_MODULE_EVENTID_COMPR_MAGIC_VAL: + sof_ipc4_compr_drain_done(sdev, 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; + } +} + +const struct sof_audio_client_ipc_ops sof_audio_client_ipc4_ops = { + .rx_notification = sof_audio_client_ipc4_rx_notification, +}; diff --git a/sound/soc/sof/sof-client-audio.c b/sound/soc/sof/sof-client-audio.c index 08bda68c534835..f17008c65dd186 100644 --- a/sound/soc/sof/sof-client-audio.c +++ b/sound/soc/sof/sof-client-audio.c @@ -50,6 +50,31 @@ static void sof_audio_client_init_debugfs(struct sof_client_dev *cdev, (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 const struct sof_client_ops sof_audio_client_ops = { + .ipc_rx_handler = sof_audio_client_ipc_rx_handler, +}; + +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_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) { @@ -59,14 +84,23 @@ static int sof_audio_client_probe(struct auxiliary_device *auxdev, 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; + ret = snd_soc_register_component(&auxdev->dev, &pdata->plat_drv, pdata->drv, pdata->num_drv); - if (ret < 0) + 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; } @@ -86,6 +120,8 @@ 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); diff --git a/sound/soc/sof/sof-client-audio.h b/sound/soc/sof/sof-client-audio.h index 258a4f9f1e66c0..b96e1eaaa0d99b 100644 --- a/sound/soc/sof/sof-client-audio.h +++ b/sound/soc/sof/sof-client-audio.h @@ -6,6 +6,20 @@ #include struct dentry; +struct sof_client_dev; + +/** + * 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. + * + * 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); +}; /** * struct sof_audio_client_pdata - platform data for the audio sof-client @@ -14,6 +28,7 @@ struct dentry; * @num_drv: Number of DAI drivers * @machine: Per-instance copy of the machine descriptor * @component: The ASoC component registered by this audio client instance + * @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 @@ -25,10 +40,13 @@ struct sof_audio_client_pdata { int num_drv; struct snd_soc_acpi_mach machine; struct snd_soc_component *component; + 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_ipc4_ops; + #endif /* __SOF_CLIENT_AUDIO_H */ From 03d247e354d6d87d8368418ae57df2a98c3a0227 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 28 Jul 2026 16:46:43 +0300 Subject: [PATCH 070/100] ASoC: SOF: Move the IPC3 component notification handling to the audio client The IPC3 component notification only carries ALSA kcontrol updates, which is audio functionality. Handling it in ipc3.c means that the core has to reach into the audio scope, which is one of the ties that prevents the PCM, topology and control code from being built as part of the audio client module. Introduce sof-client-audio-ipc3.c as the IPC3 counterpart of the audio client glue added for IPC4 and move ipc3_comp_notification() there. The message is already dispatched to the clients in full by sof_ipc3_do_rx_work(), so no new core interface is needed: the client only has to decode the global message type. Like the IPC4 case, the audio client resolves the topology ops via its own component instead of via the SOF device, which scopes the kcontrol update to the audio instance owning the component. No functional change intended. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/Makefile | 3 ++ sound/soc/sof/ipc3.c | 21 --------- sound/soc/sof/sof-client-audio-ipc3.c | 61 +++++++++++++++++++++++++++ sound/soc/sof/sof-client-audio.c | 4 ++ sound/soc/sof/sof-client-audio.h | 1 + 5 files changed, 69 insertions(+), 21 deletions(-) create mode 100644 sound/soc/sof/sof-client-audio-ipc3.c diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index fd8d8606ef3d9b..f652845b5f9464 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -29,6 +29,9 @@ 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 +ifneq ($(CONFIG_SND_SOC_SOF_IPC3),) +snd-sof-audio-y += sof-client-audio-ipc3.o +endif ifneq ($(CONFIG_SND_SOC_SOF_IPC4),) snd-sof-audio-y += sof-client-audio-ipc4.o endif diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c index 8d1f564dd05d7b..9b485bd722c87e 100644 --- a/sound/soc/sof/ipc3.c +++ b/sound/soc/sof/ipc3.c @@ -946,25 +946,6 @@ static void ipc3_stream_message(struct snd_sof_dev *sdev, void *msg_buf) } /* 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 = snd_sof_sdev_get_tplg_ops(sdev); - 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; @@ -1017,9 +998,7 @@ 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; 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..22ce659b06e6d8 --- /dev/null +++ b/sound/soc/sof/sof-client-audio-ipc3.c @@ -0,0 +1,61 @@ +// 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 "sof-audio.h" +#include "sof-client.h" +#include "sof-client-audio.h" + +/* 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); + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + 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(sdev, 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; + 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.c b/sound/soc/sof/sof-client-audio.c index f17008c65dd186..85ea09bfd625ae 100644 --- a/sound/soc/sof/sof-client-audio.c +++ b/sound/soc/sof/sof-client-audio.c @@ -66,6 +66,10 @@ 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; diff --git a/sound/soc/sof/sof-client-audio.h b/sound/soc/sof/sof-client-audio.h index b96e1eaaa0d99b..ad3b7d786a7dde 100644 --- a/sound/soc/sof/sof-client-audio.h +++ b/sound/soc/sof/sof-client-audio.h @@ -47,6 +47,7 @@ struct sof_audio_client_pdata { 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; #endif /* __SOF_CLIENT_AUDIO_H */ From fab8e3061e31104aa0f9c9e88f0a20e732a8d0ca Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 28 Jul 2026 16:49:42 +0300 Subject: [PATCH 071/100] ASoC: SOF: Move the IPC3 stream notification handling to the audio client The IPC3 stream position and XRUN notifications are handled in ipc3.c, where the core has to look up the PCM the notification belongs to, inform ALSA about the elapsed period and, for compressed streams, about the elapsed fragment. All of this is audio functionality and it is one of the ties that prevents the PCM, topology and control code from being built as part of the audio client module. Move ipc3_stream_message() and its two workers to sof-client-audio-ipc3.c. The audio client looks the PCM up via its own component, which scopes the notification to the audio instance owning the component. Unlike the other notifications, the stream position is not part of the message which is dispatched to the clients: with a stream given, the ipc_msg_data() DSP operation reads from the position window of that stream instead of the DSP mailbox. The core cannot read it in advance as it does not know which stream the notification belongs to until the message id has been resolved to a PCM, which is audio knowledge. Add sof_client_ipc_msg_data() so that the client can do the read once it has found the PCM. The tracepoint used for the stream position is created by the core, so export it to make it usable from the audio client module. It can be moved to a trace header of its own once all its users live in the audio client module. snd_sof_find_spcm_comp() and snd_sof_compr_fragment_elapsed() are exported for now to keep the calls from the client module working. The exports go away once the PCM code moves into the audio client module as well. No functional change intended. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/core.c | 3 + sound/soc/sof/ipc3.c | 89 ---------------------- sound/soc/sof/sof-audio.c | 2 + sound/soc/sof/sof-client-audio-ipc3.c | 104 ++++++++++++++++++++++++++ sound/soc/sof/sof-client.c | 9 +++ sound/soc/sof/sof-client.h | 3 + 6 files changed, 121 insertions(+), 89 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 5ea83a0c30ac26..0145442b7ec3fe 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -19,6 +19,9 @@ #define CREATE_TRACE_POINTS #include +/* used by the audio client for the IPC3 stream position notification */ +EXPORT_TRACEPOINT_SYMBOL_GPL(sof_ipc3_period_elapsed_position); + /* Module parameters for firmware, topology and IPC type override */ static char *override_fw_path; module_param_named(fw_path, override_fw_path, charp, 0444); diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c index 9b485bd722c87e..0a8cc3b8c8efcb 100644 --- a/sound/soc/sof/ipc3.c +++ b/sound/soc/sof/ipc3.c @@ -858,93 +858,6 @@ 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_sof_pcm_stream *stream; - struct sof_ipc_stream_posn posn; - struct snd_sof_pcm *spcm; - int direction, ret; - - spcm = snd_sof_find_spcm_comp_by_sdev(sdev, 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_sof_pcm_stream *stream; - struct sof_ipc_stream_posn posn; - struct snd_sof_pcm *spcm; - int direction, ret; - - spcm = snd_sof_find_spcm_comp_by_sdev(sdev, 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_trace_message(struct snd_sof_dev *sdev, void *msg_buf) { @@ -999,9 +912,7 @@ void sof_ipc3_do_rx_work(struct snd_sof_dev *sdev, struct sof_ipc_cmd_hdr *hdr, case SOF_IPC_GLB_TPLG_MSG: case SOF_IPC_GLB_PM_MSG: case SOF_IPC_GLB_COMP_MSG: - break; case SOF_IPC_GLB_STREAM_MSG: - rx_callback = ipc3_stream_message; break; case SOF_IPC_GLB_TRACE_MSG: rx_callback = ipc3_trace_message; diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index a5c7ef0e911f50..17a59fe37f8331 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -1198,6 +1198,7 @@ struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp, return snd_sof_find_spcm_comp_by_sdev(sdev, comp_id, direction); } +EXPORT_SYMBOL(snd_sof_find_spcm_comp); struct snd_sof_pcm *snd_sof_find_spcm_comp_by_sdev(struct snd_sof_dev *sdev, unsigned int comp_id, @@ -1380,6 +1381,7 @@ void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstream) /* use the same workqueue-based solution as for PCM, cf. snd_sof_pcm_elapsed */ schedule_work(&spcm->stream[cstream->direction].period_elapsed_work); } +EXPORT_SYMBOL(snd_sof_compr_fragment_elapsed); int snd_sof_compr_create_page_table(struct snd_soc_component *component, struct snd_compr_stream *cstream, diff --git a/sound/soc/sof/sof-client-audio-ipc3.c b/sound/soc/sof/sof-client-audio-ipc3.c index 22ce659b06e6d8..49860a41891bd3 100644 --- a/sound/soc/sof/sof-client-audio-ipc3.c +++ b/sound/soc/sof/sof-client-audio-ipc3.c @@ -9,11 +9,112 @@ #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) { + dev_err(&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 */ + snd_sof_pcm_period_elapsed(stream->substream); +} + +/* 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) { + dev_err(&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) @@ -51,6 +152,9 @@ static void sof_audio_client_ipc3_rx_notification(struct sof_client_dev *cdev, 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; } diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index 62d45486a37550..ae05ab844c2512 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -649,6 +649,15 @@ 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 */ +int sof_client_ipc_msg_data(struct sof_client_dev *cdev, + struct snd_sof_pcm_stream *sps, void *p, size_t sz) +{ + struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + + return snd_sof_ipc_msg_data(sdev, sps, p, sz); +} +EXPORT_SYMBOL_NS_GPL(sof_client_ipc_msg_data, "SND_SOC_SOF_CLIENT"); + void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *msg_buf) { struct sof_client_dev *cdev; diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index 1e7b74699c0727..d05cc5ba976703 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -11,6 +11,7 @@ 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; @@ -46,6 +47,8 @@ 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, From 0d6c0273ad8f5496e2ccebda9af5c8b08e202c95 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 29 Jul 2026 10:23:13 +0300 Subject: [PATCH 072/100] ASoC: SOF: Scope the control update and drain done notifications The firmware notifications are dispatched to every audio client of the DSP, but the handlers which act on them still resolve the addressed object by walking all audio instances of the SOF device. With more than one audio instance every client finds the same widget and acts on the same notification, so the kcontrol update and the compressed stream drain done are handled once per client instead of once. Should the lookup fail it is an error for every client, so the failure is reported as many times as there are clients. Pass the component of the audio client to the control update operation and to sof_ipc4_compr_drain_done(), and look the addressed object up within the topology of that component only: - sof_ipc4_find_swidget_by_ids() takes the component and searches the widget list of the audio instance owning it - the IPC3 control update searches the widget list of its own instance - snd_sof_find_spcm_comp() searches the PCM list of its own instance, which makes snd_sof_find_spcm_comp_by_sdev() unused, remove it With that a lookup miss no longer means that the object does not exist, it means that it is owned by another audio client, which is the normal case. Report these misses with dev_dbg() instead of dev_err(). The diagnostics past the ownership check keep using dev_err() as they are genuine errors of the owning client. The IPC4 probes client needs to find a module instance without knowing which audio client owns it. Move the search over the audio instances to sof_client_ipc4_find_swidget_by_id(), which is its only user. The audio instance list is RCU protected, walk it as such. This was the last user of for_each_swidget_in_instances(), delete it. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/ipc3-control.c | 19 +++++++++++++------ sound/soc/sof/ipc4-compress.c | 9 +++++---- sound/soc/sof/ipc4-control.c | 11 ++++++----- sound/soc/sof/ipc4-priv.h | 6 +++--- sound/soc/sof/ipc4-topology.c | 9 ++++++--- sound/soc/sof/sof-audio.c | 18 ++++++------------ sound/soc/sof/sof-audio.h | 9 +-------- sound/soc/sof/sof-client-audio-ipc3.c | 9 +++++---- sound/soc/sof/sof-client-audio-ipc4.c | 5 ++--- sound/soc/sof/sof-client.c | 20 +++++++++++++++++--- 10 files changed, 64 insertions(+), 51 deletions(-) diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index 2b4b874f7c6504..0cd9a6067e86a3 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -588,10 +588,10 @@ 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_sof_audio_instance *instance; struct snd_soc_dapm_widget *widget; struct snd_sof_control *scontrol; struct snd_sof_widget *swidget; @@ -603,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"); - return; /* swidget not yet found, sdev->dev is correct here */ + dev_err(scomp->dev, "Component data is not supported in control notification\n"); + return; } - /* Find the swidget first */ - for_each_swidget_in_instances(swidget, sdev, instance) { + /* + * 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; diff --git a/sound/soc/sof/ipc4-compress.c b/sound/soc/sof/ipc4-compress.c index 6305ce7de253ba..89ac57f56ef27b 100644 --- a/sound/soc/sof/ipc4-compress.c +++ b/sound/soc/sof/ipc4-compress.c @@ -714,7 +714,7 @@ 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; @@ -725,10 +725,11 @@ void sof_ipc4_compr_drain_done(struct snd_sof_dev *sdev, void *ipc_message) 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; } @@ -753,7 +754,7 @@ void sof_ipc4_compr_drain_done(struct snd_sof_dev *sdev, void *ipc_message) } /* Look up the spcm of the host copier */ - spcm = snd_sof_find_spcm_comp_by_sdev(sdev, host_swidget->comp_id, &dir); + spcm = snd_sof_find_spcm_comp(host_swidget->scomp, host_swidget->comp_id, &dir); if (!spcm) { dev_err(host_swidget->scomp->dev, "%s: Stream cannot be found for %s\n", __func__, host_swidget->widget->name); diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c index de485f382b4d06..e8d85a5d4b1c52 100644 --- a/sound/soc/sof/ipc4-control.c +++ b/sound/soc/sof/ipc4-control.c @@ -787,7 +787,7 @@ static int sof_ipc4_volsw_setup(struct snd_sof_widget *swidget, #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; @@ -803,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); @@ -822,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); @@ -830,10 +830,11 @@ 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; } diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index 99161a86e74b18..3b644da79f840f 100644 --- a/sound/soc/sof/ipc4-priv.h +++ b/sound/soc/sof/ipc4-priv.h @@ -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 c098da4e8fa187..6bcefe45653451 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -219,13 +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; + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct snd_sof_widget *swidget; - for_each_swidget_in_instances(swidget, sdev, instance) { + 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 */ diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 17a59fe37f8331..5320ced03a912f 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -1194,21 +1194,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_sof_component_get_sdev(scomp); - - return snd_sof_find_spcm_comp_by_sdev(sdev, comp_id, direction); -} -EXPORT_SYMBOL(snd_sof_find_spcm_comp); - -struct snd_sof_pcm *snd_sof_find_spcm_comp_by_sdev(struct snd_sof_dev *sdev, - unsigned int comp_id, - int *direction) -{ - struct snd_sof_audio_instance *instance; + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); struct snd_sof_pcm *spcm; int dir; - for_each_spcm_in_instances(spcm, sdev, instance) { + 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; @@ -1219,6 +1212,7 @@ struct snd_sof_pcm *snd_sof_find_spcm_comp_by_sdev(struct snd_sof_dev *sdev, return NULL; } +EXPORT_SYMBOL(snd_sof_find_spcm_comp); struct snd_sof_widget *snd_sof_find_swidget(struct snd_soc_component *scomp, const char *name) diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 51664c4a399660..fbb1748461688d 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -51,10 +51,6 @@ list_for_each_entry(instance, &(sdev)->audio_instance_list, list) \ list_for_each_entry(spcm, &(instance)->pcm_list, list) -#define for_each_swidget_in_instances(swidget, sdev, instance) \ - list_for_each_entry(instance, &(sdev)->audio_instance_list, list) \ - list_for_each_entry(swidget, &(instance)->widget_list, list) - #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 @@ -179,7 +175,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 */ @@ -679,9 +675,6 @@ 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); -struct snd_sof_pcm *snd_sof_find_spcm_comp_by_sdev(struct snd_sof_dev *sdev, - 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); diff --git a/sound/soc/sof/sof-client-audio-ipc3.c b/sound/soc/sof/sof-client-audio-ipc3.c index 49860a41891bd3..919141ef7dcf48 100644 --- a/sound/soc/sof/sof-client-audio-ipc3.c +++ b/sound/soc/sof/sof-client-audio-ipc3.c @@ -29,7 +29,8 @@ static void sof_audio_client_ipc3_period_elapsed(struct sof_client_dev *cdev, spcm = snd_sof_find_spcm_comp(component, msg_id, &direction); if (!spcm) { - dev_err(&cdev->auxdev.dev, + /* 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; } @@ -66,7 +67,8 @@ static void sof_audio_client_ipc3_xrun(struct sof_client_dev *cdev, spcm = snd_sof_find_spcm_comp(component, msg_id, &direction); if (!spcm) { - dev_err(&cdev->auxdev.dev, "XRUN for unknown stream, msg_id %d\n", + /* The stream is not owned by this component */ + dev_dbg(&cdev->auxdev.dev, "XRUN for unknown stream, msg_id %d\n", msg_id); return; } @@ -120,7 +122,6 @@ 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); - struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); const struct sof_ipc_tplg_ops *tplg_ops; u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK; @@ -140,7 +141,7 @@ static void sof_audio_client_ipc3_comp_notification(struct sof_client_dev *cdev, tplg_ops = snd_sof_component_get_tplg_ops(pdata->component); if (tplg_ops && tplg_ops->control->update) - tplg_ops->control->update(sdev, hdr); + tplg_ops->control->update(pdata->component, hdr); } static void sof_audio_client_ipc3_rx_notification(struct sof_client_dev *cdev, diff --git a/sound/soc/sof/sof-client-audio-ipc4.c b/sound/soc/sof/sof-client-audio-ipc4.c index 88d6ca05a6c81e..4f5d0ccc58d545 100644 --- a/sound/soc/sof/sof-client-audio-ipc4.c +++ b/sound/soc/sof/sof-client-audio-ipc4.c @@ -20,7 +20,6 @@ static void sof_audio_client_ipc4_module_notification(struct sof_client_dev *cde { struct sof_audio_client_pdata *pdata = dev_get_platdata(&cdev->auxdev.dev); struct sof_ipc4_notify_module_data *data = ipc4_msg->data_ptr; - struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); /* The component is only available while the sound card is bound */ if (!pdata->component) @@ -34,12 +33,12 @@ static void sof_audio_client_ipc4_module_notification(struct sof_client_dev *cde /* ALSA kcontrol notification */ tplg_ops = snd_sof_component_get_tplg_ops(pdata->component); if (tplg_ops && tplg_ops->control->update) - tplg_ops->control->update(sdev, ipc4_msg); + tplg_ops->control->update(pdata->component, ipc4_msg); break; } case SOF_IPC4_NOTIFY_MODULE_EVENTID_COMPR_MAGIC_VAL: - sof_ipc4_compr_drain_done(sdev, ipc4_msg); + sof_ipc4_compr_drain_done(pdata->component, ipc4_msg); break; default: break; diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index ae05ab844c2512..9105960f16ae3c 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include "ops.h" @@ -406,10 +407,23 @@ struct snd_sof_widget *sof_client_ipc4_find_swidget_by_id(struct sof_client_dev u32 module_id, int instance_id) { struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); + struct snd_sof_audio_instance *instance; - 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 NULL; + } + + /* The module can be in the topology of any of the audio instances */ + guard(rcu)(); + list_for_each_entry_rcu(instance, &sdev->audio_instance_list, list) { + struct snd_sof_widget *swidget; + + swidget = sof_ipc4_find_swidget_by_ids(instance->component, + module_id, instance_id); + if (swidget) + return swidget; + } return NULL; } From da08d6e47c450b527ac034337ebd175871d00f3c Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 31 Jul 2026 14:31:04 +0300 Subject: [PATCH 073/100] ASoC: SOF: move the stream counter callbacks to sof_audio_ops compr_get_dai_frame_counter(), get_dai_frame_counter() and get_host_byte_counter() are the last stream callbacks still living in snd_sof_dsp_ops. They are only ever reached from the PCM and compress delay reporting paths, so the audio code has to keep dereferencing sdev->pdata->desc->ops to call them, which is exactly what the audio client must not do. Move the three callbacks to struct sof_audio_ops and give them the component based prototype used by the rest of that structure. The accessors in ops.h resolve the audio instance from the component like their neighbours, and all call sites already have the component at hand. sof_ipc4_pcm_setup() takes it from spcm->scomp. None of the HDA implementations used the snd_sof_dev argument, so drop it from hda_dsp_get_stream_llp(), hda_dsp_compr_get_stream_llp(), hda_dsp_get_stream_ldp() and from the hda_dsp_get_llp() helper they share. With this no audio path reaches into snd_sof_dsp_ops any more. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/intel/hda-common-ops.c | 10 +++--- sound/soc/sof/intel/hda-stream.c | 20 +++++------- sound/soc/sof/intel/hda.h | 8 ++--- sound/soc/sof/ipc4-compress.c | 2 +- sound/soc/sof/ipc4-pcm.c | 12 +++++--- sound/soc/sof/ops.h | 34 ++++++++++++-------- sound/soc/sof/sof-priv.h | 46 ++++++++++++++-------------- 7 files changed, 67 insertions(+), 65 deletions(-) diff --git a/sound/soc/sof/intel/hda-common-ops.c b/sound/soc/sof/intel/hda-common-ops.c index d5c77f739bc514..0d7724eeca009b 100644 --- a/sound/soc/sof/intel/hda-common-ops.c +++ b/sound/soc/sof/intel/hda-common-ops.c @@ -30,6 +30,10 @@ const struct sof_audio_ops sof_hda_audio_ops = { .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, @@ -76,12 +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 */ - .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, diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 747b1b6d13eba0..fbcd2c6cf95bf0 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -1195,8 +1195,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; @@ -1249,44 +1248,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.h b/sound/soc/sof/intel/hda.h index 2c73314553850a..6234d37e25707b 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -699,7 +699,7 @@ int hda_dsp_compr_trigger(struct snd_soc_component *component, 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); /* @@ -727,11 +727,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 * diff --git a/sound/soc/sof/ipc4-compress.c b/sound/soc/sof/ipc4-compress.c index 89ac57f56ef27b..988d22bf754fb0 100644 --- a/sound/soc/sof/ipc4-compress.c +++ b/sound/soc/sof/ipc4-compress.c @@ -690,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; diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 2060ab53604c3f..bdcdcf190c317b 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -455,8 +455,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; } @@ -931,6 +931,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; @@ -948,7 +950,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) { @@ -1218,7 +1220,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; @@ -1232,7 +1234,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/ops.h b/sound/soc/sof/ops.h index a00716353ed58b..489c7edd913518 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -544,11 +544,15 @@ snd_sof_compr_platform_pointer(struct snd_soc_component *component, } 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; } @@ -639,25 +643,29 @@ static inline int snd_sof_pcm_platform_ack(struct snd_soc_component *component, } 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/sof-priv.h b/sound/soc/sof/sof-priv.h index 465d3df892637d..8d088c8ff8aa03 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -173,6 +173,29 @@ struct sof_audio_ops { 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; @@ -304,29 +327,6 @@ struct snd_sof_dsp_ops { int (*load_module)(struct snd_sof_dev *sof_dev, struct snd_sof_mod_hdr *hdr); /* optional */ - 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, From 2fa2878e9cf91d8c95cf354dc8520c11ea5ebbc6 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 3 Aug 2026 13:17:27 +0300 Subject: [PATCH 074/100] ASoC: SOF: Move the audio instance PM handling to the audio client sof_client_audio_suspend() and sof_client_audio_resume() implement the power management of a single audio instance, but they live in sof-client.c, which is the generic client infrastructure of the SOF core. Their only caller is the PM callback pair of the audio client. Fold them into sof_audio_client_suspend() and sof_audio_client_resume() and let the audio core provide sof_audio_instance_suspend() and sof_audio_instance_resume() for the pipeline handling, so that the instance internals are not accessed from outside of the audio code. The moved code inspected sdev->dsp_power_state and sdev->fw_state directly. Use sof_client_get_fw_state() for the latter and add sof_client_is_dsp_in_d0() for the former to keep the client on the client API instead of the core internals. No functional change intended. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-audio.c | 37 ++++++++++++++ sound/soc/sof/sof-client-audio.c | 44 ++++++++++++++++- sound/soc/sof/sof-client-audio.h | 4 ++ sound/soc/sof/sof-client.c | 84 +++----------------------------- sound/soc/sof/sof-client.h | 5 +- 5 files changed, 92 insertions(+), 82 deletions(-) diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 5320ced03a912f..df69c388334ed4 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -11,6 +11,7 @@ #include #include #include "sof-audio.h" +#include "sof-client-audio.h" #include "sof-utils.h" #include "ops.h" @@ -166,6 +167,42 @@ int sof_instance_set_up_pipelines(struct snd_sof_audio_instance *instance) 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; +} +EXPORT_SYMBOL(sof_audio_instance_suspend); + +/* + * 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; +} +EXPORT_SYMBOL(sof_audio_instance_resume); + /* * Set up the static pipelines of every audio instance. Called after the DSP * firmware has been (re)booted, which invalidates the state of all instances diff --git a/sound/soc/sof/sof-client-audio.c b/sound/soc/sof/sof-client-audio.c index 85ea09bfd625ae..b05de3a9a46d9d 100644 --- a/sound/soc/sof/sof-client-audio.c +++ b/sound/soc/sof/sof-client-audio.c @@ -135,18 +135,58 @@ static void sof_audio_client_remove(struct auxiliary_device *auxdev) 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_client_audio_suspend(auxiliary_dev_to_sof_client_dev(auxdev)); + 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_client_audio_resume(auxiliary_dev_to_sof_client_dev(auxdev)); + return sof_audio_instance_resume(pdata->component); } static const struct dev_pm_ops sof_audio_client_pm = { diff --git a/sound/soc/sof/sof-client-audio.h b/sound/soc/sof/sof-client-audio.h index ad3b7d786a7dde..52bca4e259245e 100644 --- a/sound/soc/sof/sof-client-audio.h +++ b/sound/soc/sof/sof-client-audio.h @@ -50,4 +50,8 @@ struct sof_audio_client_pdata { 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); + #endif /* __SOF_CLIENT_AUDIO_H */ diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index 9105960f16ae3c..cdf9430fb56fdc 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -541,6 +541,14 @@ bool sof_client_is_suspend_target_s0ix(struct sof_client_dev *cdev) } 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 @@ -892,82 +900,6 @@ void sof_client_machine_unregister(struct sof_client_dev *cdev) } EXPORT_SYMBOL_NS_GPL(sof_client_machine_unregister, "SND_SOC_SOF_CLIENT"); -/* - * 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. - */ -int sof_client_audio_suspend(struct sof_client_dev *cdev) -{ - struct sof_audio_client_pdata *pdata = dev_get_platdata(&cdev->auxdev.dev); - struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); - struct snd_sof_audio_instance *instance; - const struct sof_ipc_tplg_ops *tplg_ops; - int ret; - - 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 (sdev->dsp_power_state.state != SOF_DSP_PM_D0) - return 0; - - instance = snd_sof_component_get_audio_instance(pdata->component); - if (!instance) - return 0; - - tplg_ops = instance->tplg_ops; - if (!tplg_ops || !tplg_ops->tear_down_all_pipelines) - return 0; - - ret = tplg_ops->tear_down_all_pipelines(pdata->component, false); - if (ret < 0) - return ret; - - instance->pipelines_set_up = false; - - return 0; -} -EXPORT_SYMBOL_NS_GPL(sof_client_audio_suspend, "SND_SOC_SOF_CLIENT"); - -/* - * 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. - */ -int sof_client_audio_resume(struct sof_client_dev *cdev) -{ - struct sof_audio_client_pdata *pdata = dev_get_platdata(&cdev->auxdev.dev); - struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); - struct snd_sof_audio_instance *instance; - - 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 (sdev->fw_state != SOF_FW_BOOT_COMPLETE) - return 0; - - instance = snd_sof_component_get_audio_instance(pdata->component); - if (!instance) - return 0; - - return sof_instance_set_up_pipelines(instance); -} -EXPORT_SYMBOL_NS_GPL(sof_client_audio_resume, "SND_SOC_SOF_CLIENT"); - void sof_audio_client_init_pdata(struct snd_sof_dev *sdev, struct sof_audio_client_pdata *pdata) { diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index d05cc5ba976703..bdf9a8751f5f60 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -65,6 +65,7 @@ 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); 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); @@ -145,10 +146,6 @@ u32 sof_client_get_new_comp_id(struct sof_client_dev *cdev); int sof_client_machine_register(struct sof_client_dev *cdev); void sof_client_machine_unregister(struct sof_client_dev *cdev); -/* audio client power management */ -int sof_client_audio_suspend(struct sof_client_dev *cdev); -int sof_client_audio_resume(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, From 30a79838e72745e7614564877b833b8843d7553e Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 3 Aug 2026 15:36:48 +0300 Subject: [PATCH 075/100] ASoC: SOF: Build the ASoC component driver in the audio client The pre-built snd_soc_component_driver of an audio instance is assembled by sof_audio_client_init_pdata(), which runs in the SOF core when the audio auxiliary device is registered. The template only describes the PCM operations of the client that is about to be created, so the client itself is a better place to build it. Move the snd_sof_new_platform_drv() call to sof_audio_client_probe() and convert the function to take the sof_client_dev instead of the snd_sof_dev, as the clients have no access to the latter. The IPC type and the DSPless mode already have client accessors, add sof_client_get_machine_drv_name() for the machine driver name, which hides whether the machine is described by an ACPI or an OF descriptor. No functional change intended. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/pcm.c | 15 +++++---------- sound/soc/sof/sof-client-audio.c | 2 ++ sound/soc/sof/sof-client-audio.h | 3 +++ sound/soc/sof/sof-client.c | 21 ++++++++++++++++++++- sound/soc/sof/sof-client.h | 1 + sound/soc/sof/sof-priv.h | 3 --- 6 files changed, 31 insertions(+), 14 deletions(-) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index e903a9a419458b..7154dcf344949e 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -879,14 +879,13 @@ 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_sof_pdata *plat_data = sdev->pdata; const struct sof_ipc_pcm_ops *pcm_ops = NULL; const char *drv_name; - switch (sdev->pdata->ipc_type) { + switch (sof_client_get_ipc_type(cdev)) { #if defined(CONFIG_SND_SOC_SOF_IPC3) case SOF_IPC_TYPE_3: pcm_ops = &ipc3_pcm_ops; @@ -901,12 +900,7 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev, break; } - 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; + drv_name = sof_client_get_machine_drv_name(cdev); pd->name = "sof-audio-component"; pd->probe = sof_pcm_probe; @@ -941,6 +935,7 @@ 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; } +EXPORT_SYMBOL(snd_sof_new_platform_drv); diff --git a/sound/soc/sof/sof-client-audio.c b/sound/soc/sof/sof-client-audio.c index b05de3a9a46d9d..12eef102cd1458 100644 --- a/sound/soc/sof/sof-client-audio.c +++ b/sound/soc/sof/sof-client-audio.c @@ -94,6 +94,8 @@ static int sof_audio_client_probe(struct auxiliary_device *auxdev, 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) { diff --git a/sound/soc/sof/sof-client-audio.h b/sound/soc/sof/sof-client-audio.h index 52bca4e259245e..ddd632f5a127f1 100644 --- a/sound/soc/sof/sof-client-audio.h +++ b/sound/soc/sof/sof-client-audio.h @@ -54,4 +54,7 @@ extern const struct sof_audio_client_ipc_ops sof_audio_client_ipc4_ops; int sof_audio_instance_suspend(struct snd_soc_component *component); int sof_audio_instance_resume(struct snd_soc_component *component); +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.c b/sound/soc/sof/sof-client.c index cdf9430fb56fdc..2c2f305dd94065 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -19,6 +19,7 @@ #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" @@ -621,6 +622,25 @@ const struct snd_soc_acpi_mach *sof_client_get_machine(struct sof_client_dev *cd } 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 @@ -904,7 +924,6 @@ void sof_audio_client_init_pdata(struct snd_sof_dev *sdev, struct sof_audio_client_pdata *pdata) { memset(pdata, 0, sizeof(*pdata)); - snd_sof_new_platform_drv(sdev, &pdata->plat_drv); pdata->drv = sdev->audio_ops->drv; pdata->num_drv = sdev->audio_ops->num_drv; } diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index bdf9a8751f5f60..250c95c5338960 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -63,6 +63,7 @@ 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); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 8d088c8ff8aa03..64b70d2f6c7989 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -736,9 +736,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, - struct snd_soc_component_driver * const pd); - /* * Compress support */ From c152f92b4d6e200e0b0983a6774b4851879ace7f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 3 Aug 2026 16:10:42 +0300 Subject: [PATCH 076/100] ASoC: SOF: Look up IPC4 module names via a client callback sof_client_ipc4_find_swidget_by_id() lets the probes client resolve a module instance to its topology widget. It is implemented in the SOF core, which walks the audio instances and calls into the audio code to do the lookup, one of the last places where the core depends on audio. Turn it into a get_module_name() callback of struct sof_client_ops. The core only asks the clients which implement the callback, so the lookup no longer needs the audio symbols. Letting the probes client call into the audio client instead would tie the two modules together and would need the audio module to be pinned for as long as probes is loaded. The callback copies the widget name instead of returning a pointer to it. Before audio became a client, snd_sof_device_remove() unregistered all clients before unbinding the machine driver, so the topology could not be unloaded while the probes client was still around to dereference a widget. Audio clients are now bound and unbound on their own, so that ordering no longer holds and the name is copied while the core still holds the client ops mutex. The name is only used for a debugfs string, truncation is harmless. It is not bound by the topology name length as the ASoC core may prefix the widget name with the component name. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/ipc4-topology.c | 1 + sound/soc/sof/sof-client-audio-ipc4.c | 27 ++++++++++++++++++++++++ sound/soc/sof/sof-client-audio.c | 14 +++++++++++++ sound/soc/sof/sof-client-audio.h | 5 +++++ sound/soc/sof/sof-client-probes-ipc4.c | 13 +++++++----- sound/soc/sof/sof-client.c | 29 +++++++++++++------------- sound/soc/sof/sof-client.h | 21 +++++++++++++++++-- 7 files changed, 88 insertions(+), 22 deletions(-) diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 6bcefe45653451..f41e42e01a9e27 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -242,6 +242,7 @@ struct snd_sof_widget *sof_ipc4_find_swidget_by_ids(struct snd_soc_component *sc return NULL; } +EXPORT_SYMBOL(sof_ipc4_find_swidget_by_ids); static u32 sof_ipc4_fmt_cfg_to_type(u32 fmt_cfg) { diff --git a/sound/soc/sof/sof-client-audio-ipc4.c b/sound/soc/sof/sof-client-audio-ipc4.c index 4f5d0ccc58d545..0b2c2ab8da1989 100644 --- a/sound/soc/sof/sof-client-audio-ipc4.c +++ b/sound/soc/sof/sof-client-audio-ipc4.c @@ -62,6 +62,33 @@ static void sof_audio_client_ipc4_rx_notification(struct sof_client_dev *cdev, } } +/* + * 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 index 12eef102cd1458..ce6e8a24f2ca8e 100644 --- a/sound/soc/sof/sof-client-audio.c +++ b/sound/soc/sof/sof-client-audio.c @@ -58,8 +58,22 @@ static void sof_audio_client_ipc_rx_handler(struct sof_client_dev *cdev, void *m 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 const struct sof_client_ops sof_audio_client_ops = { .ipc_rx_handler = sof_audio_client_ipc_rx_handler, + .get_module_name = sof_audio_client_get_module_name, }; static const struct sof_audio_client_ipc_ops * diff --git a/sound/soc/sof/sof-client-audio.h b/sound/soc/sof/sof-client-audio.h index ddd632f5a127f1..95f519c6335414 100644 --- a/sound/soc/sof/sof-client-audio.h +++ b/sound/soc/sof/sof-client-audio.h @@ -13,12 +13,17 @@ struct sof_client_dev; * @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); }; /** 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 2c2f305dd94065..a3505f9f7395bf 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include "ops.h" @@ -404,31 +403,31 @@ 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 snd_sof_audio_instance *instance; + struct sof_client_dev *client; if (sdev->pdata->ipc_type != SOF_IPC_TYPE_4) { dev_err(sdev->dev, "Only supported with IPC4\n"); - return NULL; + return -EOPNOTSUPP; } - /* The module can be in the topology of any of the audio instances */ - guard(rcu)(); - list_for_each_entry_rcu(instance, &sdev->audio_instance_list, list) { - struct snd_sof_widget *swidget; + /* The module can be in the topology of any of the audio clients */ + guard(mutex)(&sdev->client_ops_mutex); + list_for_each_entry(client, &sdev->client_ops_list, ops_node) { + if (!client->ops->get_module_name) + continue; - swidget = sof_ipc4_find_swidget_by_ids(instance->component, - module_id, instance_id); - if (swidget) - return swidget; + if (!client->ops->get_module_name(client, module_id, instance_id, + name, size)) + return 0; } - return NULL; + 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) diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index 250c95c5338960..7fd6e6f4860fe4 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -51,8 +51,16 @@ 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); @@ -101,6 +109,11 @@ enum sof_d0i3_vote { 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); + /** * struct sof_client_ops - SOF client event callbacks * @ipc_rx_handler: Called for every DSP-initiated IPC notification. The @@ -109,6 +122,9 @@ typedef enum sof_d0i3_vote (*sof_client_d0i3_vote_callback)(struct sof_client_de * @fw_state_handler: Called on every DSP firmware state change. * @d0i3_vote: Returns the client's D0i3 compatibility vote, see * enum sof_d0i3_vote. + * @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 @@ -119,6 +135,7 @@ struct sof_client_ops { sof_client_event_callback ipc_rx_handler; sof_client_fw_state_callback fw_state_handler; sof_client_d0i3_vote_callback d0i3_vote; + sof_client_module_name_callback get_module_name; }; int sof_client_register_ops(struct sof_client_dev *cdev, From 62f768d97feff15a651924765ae35fbee10ecfa1 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 3 Aug 2026 17:03:21 +0300 Subject: [PATCH 077/100] ASoC: SOF: Protect the client ops list with SRCU The client event callbacks are dispatched with client_ops_mutex held, which restricts them to non sleeping work. A callback which sends an IPC message and waits for the reply would deadlock: the IPC IRQ thread handles both the replies and the DSP initiated notifications and it takes the same mutex in sof_client_ipc_rx_dispatcher(). If a notification arrives while the mutex is held then the IRQ thread blocks on it and the reply the callback is waiting for can never be delivered. On Intel platforms the BUSY interrupt is left masked until the notification handler returns, so the DSP cannot raise the next message either. None of the current callbacks sends IPC, but the firmware boot needs to notify the audio clients to let them restore their pipelines, which is IPC heavy. Protect the list with SRCU instead. The mutex is kept to serialize the writers while the dispatchers use the read side critical section, which is allowed to sleep. sof_client_unregister_ops() waits for the grace period to pass before it drops the ops pointer, a client module cannot be removed while one of its callbacks is still running. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/core.c | 16 ++++++++++++++++ sound/soc/sof/sof-client.c | 37 ++++++++++++++++++++++++------------- sound/soc/sof/sof-client.h | 3 +++ sound/soc/sof/sof-priv.h | 12 +++++++++++- 4 files changed, 54 insertions(+), 14 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 0145442b7ec3fe..17dd3557852cf5 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -626,6 +626,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; @@ -666,6 +673,15 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) INIT_LIST_HEAD(&sdev->audio_instance_list); INIT_LIST_HEAD(&sdev->ipc_client_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); spin_lock_init(&sdev->audio_instance_list_lock); diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index a3505f9f7395bf..b4946251d5a4fc 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -12,7 +12,9 @@ #include #include #include +#include #include +#include #include #include "ops.h" #include "sof-audio.h" @@ -415,8 +417,9 @@ int sof_client_ipc4_get_module_name(struct sof_client_dev *cdev, u32 module_id, } /* The module can be in the topology of any of the audio clients */ - guard(mutex)(&sdev->client_ops_mutex); - list_for_each_entry(client, &sdev->client_ops_list, ops_node) { + 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; @@ -703,8 +706,9 @@ void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *msg_buf) { struct sof_client_dev *cdev; - guard(mutex)(&sdev->client_ops_mutex); - list_for_each_entry(cdev, &sdev->client_ops_list, ops_node) { + 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); } @@ -715,8 +719,9 @@ void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev) { struct sof_client_dev *cdev; - guard(mutex)(&sdev->client_ops_mutex); - list_for_each_entry(cdev, &sdev->client_ops_list, ops_node) { + 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); } @@ -734,8 +739,9 @@ enum sof_d0i3_vote sof_client_get_d0i3_vote(struct snd_sof_dev *sdev) enum sof_d0i3_vote result = SOF_D0I3_NO_ACTIVITY; struct sof_client_dev *cdev; - guard(mutex)(&sdev->client_ops_mutex); - list_for_each_entry(cdev, &sdev->client_ops_list, ops_node) { + 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->d0i3_vote) continue; @@ -764,7 +770,7 @@ int sof_client_register_ops(struct sof_client_dev *cdev, guard(mutex)(&sdev->client_ops_mutex); cdev->ops = ops; - list_add(&cdev->ops_node, &sdev->client_ops_list); + list_add_rcu(&cdev->ops_node, &sdev->client_ops_list); return 0; } @@ -775,10 +781,15 @@ void sof_client_unregister_ops(struct sof_client_dev *cdev) struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev); guard(mutex)(&sdev->client_ops_mutex); - if (cdev->ops) { - list_del(&cdev->ops_node); - cdev->ops = NULL; - } + if (!cdev->ops) + return; + + 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_ops, "SND_SOC_SOF_CLIENT"); diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index 7fd6e6f4860fe4..d156be996be952 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -130,6 +130,9 @@ typedef int (*sof_client_module_name_callback)(struct sof_client_dev *cdev, * 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. */ struct sof_client_ops { sof_client_event_callback ipc_rx_handler; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 64b70d2f6c7989..7f7884c40c4bca 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 @@ -704,9 +705,18 @@ struct snd_sof_dev { /* list of clients that registered event ops via sof_client_register_ops() */ struct list_head client_ops_list; - /* to protect client_ops_list */ + /* serializes the writers of client_ops_list */ struct mutex client_ops_mutex; + /* + * 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 srcu_struct client_ops_srcu; + /* quirks to override topology values */ bool mclk_id_override; u16 mclk_id_quirk; /* same size as in IPC3 definitions */ From bd69fc066a3a77efd40bda8e0f35fed462cc9597 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 3 Aug 2026 17:08:35 +0300 Subject: [PATCH 078/100] ASoC: SOF: Restore the audio instance pipelines from the audio client sof_restore_pipelines() walks the audio instances of the SOF device from the firmware boot path, which is one of the last places where the core reaches into the audio stack. Turn it into a fw_booted client callback: the core tells every client that the firmware has been (re)booted and that nothing of the state the client had in the DSP survived it, the audio client sets up the static pipelines of its own instance. Booting the DSP is aborted if a client fails to rebuild its state, as it was before. The set of clients which have a component is the set of the registered audio instances, sof_pcm_probe() sets the component when it registers the instance and clears it when the registration fails or the instance is unregistered, so the same instances are restored as before. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/pm.c | 9 +++------ sound/soc/sof/sof-audio.c | 23 +++++++++-------------- sound/soc/sof/sof-audio.h | 1 - sound/soc/sof/sof-client-audio.c | 11 +++++++++++ sound/soc/sof/sof-client-audio.h | 1 + sound/soc/sof/sof-client.c | 23 +++++++++++++++++++++++ sound/soc/sof/sof-client.h | 7 +++++++ sound/soc/sof/sof-priv.h | 6 ++++++ 8 files changed, 60 insertions(+), 21 deletions(-) diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 63ec443631011a..1b229542873433 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -122,13 +122,10 @@ int snd_sof_boot_dsp_firmware(struct snd_sof_dev *sdev) __func__, ret); } - /* restore the pipelines of all audio instances backed by this DSP */ - ret = sof_restore_pipelines(sdev); - if (ret < 0) { - dev_err(sdev->dev, "%s: failed to restore pipeline: %d\n", - __func__, ret); + /* 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); diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index df69c388334ed4..076067a5ef2f89 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -204,26 +204,21 @@ int sof_audio_instance_resume(struct snd_soc_component *component) EXPORT_SYMBOL(sof_audio_instance_resume); /* - * Set up the static pipelines of every audio instance. Called after the DSP - * firmware has been (re)booted, which invalidates the state of all instances - * backed by that DSP. + * 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_restore_pipelines(struct snd_sof_dev *sdev) +int sof_audio_instance_restore(struct snd_soc_component *component) { - struct snd_sof_audio_instance *instance; - int ret; + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(component); - list_for_each_entry(instance, &sdev->audio_instance_list, list) { - /* nothing of the previous state is left in the DSP */ - instance->pipelines_set_up = false; + if (!instance) + return 0; - ret = sof_instance_set_up_pipelines(instance); - if (ret < 0) - return ret; - } + instance->pipelines_set_up = false; - return 0; + return sof_instance_set_up_pipelines(instance); } +EXPORT_SYMBOL(sof_audio_instance_restore); /* * Check if a DAI widget is an aggregated DAI. Aggregated DAI's have names ending in numbers diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index fbb1748461688d..af44ccb8ed2610 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -653,7 +653,6 @@ const struct sof_ipc_pcm_ops *snd_sof_sdev_get_pcm_ops(struct snd_sof_dev *sdev) const struct sof_ipc_tplg_ops *snd_sof_component_get_tplg_ops(struct snd_soc_component *component); const struct sof_ipc_pcm_ops *snd_sof_component_get_pcm_ops(struct snd_soc_component *component); int sof_instance_set_up_pipelines(struct snd_sof_audio_instance *instance); -int sof_restore_pipelines(struct snd_sof_dev *sdev); static inline struct snd_sof_pcm *snd_sof_find_spcm_dai(struct snd_soc_component *scomp, diff --git a/sound/soc/sof/sof-client-audio.c b/sound/soc/sof/sof-client-audio.c index ce6e8a24f2ca8e..dbb3f9730482eb 100644 --- a/sound/soc/sof/sof-client-audio.c +++ b/sound/soc/sof/sof-client-audio.c @@ -71,8 +71,19 @@ static int sof_audio_client_get_module_name(struct sof_client_dev *cdev, 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); +} + 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, .get_module_name = sof_audio_client_get_module_name, }; diff --git a/sound/soc/sof/sof-client-audio.h b/sound/soc/sof/sof-client-audio.h index 95f519c6335414..659b831b992199 100644 --- a/sound/soc/sof/sof-client-audio.h +++ b/sound/soc/sof/sof-client-audio.h @@ -58,6 +58,7 @@ 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); void snd_sof_new_platform_drv(struct sof_client_dev *cdev, struct snd_soc_component_driver * const pd); diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index b4946251d5a4fc..2107eeefbbf584 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -727,6 +727,29 @@ void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev) } } +/* 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 sof_client_dev *cdev; + int ret; + + 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; + + 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; + } + } + + return 0; +} + /* * Fold the D0i3 votes of every client that implements the d0i3_vote callback: * - any SOF_D0I3_INCOMPATIBLE -> SOF_D0I3_INCOMPATIBLE (veto) diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index d156be996be952..4a188a7ec2add8 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -114,12 +114,18 @@ 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); + /** * 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. * @d0i3_vote: Returns the client's D0i3 compatibility vote, see * enum sof_d0i3_vote. * @get_module_name: Copies the topology name of a module instance owned by @@ -137,6 +143,7 @@ typedef int (*sof_client_module_name_callback)(struct sof_client_dev *cdev, 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_d0i3_vote_callback d0i3_vote; sof_client_module_name_callback get_module_name; }; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 7f7884c40c4bca..14ce2c222f91c3 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -890,6 +890,7 @@ 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); enum sof_d0i3_vote sof_client_get_d0i3_vote(struct snd_sof_dev *sdev); int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state); int sof_resume_clients(struct snd_sof_dev *sdev); @@ -927,6 +928,11 @@ 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 enum sof_d0i3_vote sof_client_get_d0i3_vote(struct snd_sof_dev *sdev) { return SOF_D0I3_NO_ACTIVITY; From a23a12fc55cbc692dc7d710b7a55a784951b5fd0 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 4 Aug 2026 08:44:27 +0300 Subject: [PATCH 079/100] ASoC: SOF: Let the clients ask for the DSP to be kept in D0 snd_sof_stream_suspend_ignored() walks the PCMs of every audio instance of the SOF device to decide whether the DSP can be powered down when the system suspends to S0IX. The core has no business knowing what a stream is, and the audio client is not the only one which can leave something running in the DSP on purpose. Replace it with a keep_dsp_in_d0 client callback and fold the answers in sof_client_keep_dsp_in_d0(): any client which needs what it has in the DSP to survive keeps it in D0. The audio client answers for the streams of its own instance which ignored the suspend trigger. The wording is kept generic on purpose, the criteria listed in snd_sof_dsp_power_target() which are still to be implemented are not specific to audio streams either. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/pm.c | 4 ++-- sound/soc/sof/sof-audio.c | 10 +++++++--- sound/soc/sof/sof-audio.h | 1 - sound/soc/sof/sof-client-audio.c | 12 ++++++++++++ sound/soc/sof/sof-client-audio.h | 1 + sound/soc/sof/sof-client.c | 19 +++++++++++++++++++ sound/soc/sof/sof-client.h | 7 +++++++ sound/soc/sof/sof-priv.h | 6 ++++++ 8 files changed, 54 insertions(+), 6 deletions(-) diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 1b229542873433..cf44317fab1e19 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -38,11 +38,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; diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 076067a5ef2f89..9ff10ed734d731 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -1179,12 +1179,15 @@ bool snd_sof_dsp_state_is_d0i3_compatible(struct snd_sof_dev *sdev) } EXPORT_SYMBOL(snd_sof_dsp_state_is_d0i3_compatible); -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; + struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(component); struct snd_sof_pcm *spcm; - for_each_spcm_in_instances(spcm, sdev, instance) { + 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; @@ -1192,6 +1195,7 @@ bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev) return false; } +EXPORT_SYMBOL(sof_audio_instance_suspend_ignored); /* * Generic object lookup APIs. diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index af44ccb8ed2610..64ead5e4c4b25c 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -720,7 +720,6 @@ static inline void snd_sof_compr_init_elapsed_work(struct work_struct *work) { } 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_state_is_d0i3_compatible(struct snd_sof_dev *sdev); /* Machine driver enumeration */ diff --git a/sound/soc/sof/sof-client-audio.c b/sound/soc/sof/sof-client-audio.c index dbb3f9730482eb..32e7b625e7cf6d 100644 --- a/sound/soc/sof/sof-client-audio.c +++ b/sound/soc/sof/sof-client-audio.c @@ -81,9 +81,21 @@ static int sof_audio_client_fw_booted(struct sof_client_dev *cdev) 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 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, .get_module_name = sof_audio_client_get_module_name, }; diff --git a/sound/soc/sof/sof-client-audio.h b/sound/soc/sof/sof-client-audio.h index 659b831b992199..f49940cef2f153 100644 --- a/sound/soc/sof/sof-client-audio.h +++ b/sound/soc/sof/sof-client-audio.h @@ -59,6 +59,7 @@ extern const struct sof_audio_client_ipc_ops sof_audio_client_ipc4_ops; 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); void snd_sof_new_platform_drv(struct sof_client_dev *cdev, struct snd_soc_component_driver * const pd); diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index 2107eeefbbf584..009dc308f671af 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -750,6 +750,25 @@ int sof_client_fw_booted_dispatcher(struct snd_sof_dev *sdev) return 0; } +/* + * 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; + + 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 false; +} +EXPORT_SYMBOL_NS_GPL(sof_client_keep_dsp_in_d0, "SND_SOC_SOF_CLIENT"); + /* * Fold the D0i3 votes of every client that implements the d0i3_vote callback: * - any SOF_D0I3_INCOMPATIBLE -> SOF_D0I3_INCOMPATIBLE (veto) diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index 4a188a7ec2add8..17dc0d4c7b6483 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -116,6 +116,8 @@ typedef int (*sof_client_module_name_callback)(struct sof_client_dev *cdev, 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); + /** * struct sof_client_ops - SOF client event callbacks * @ipc_rx_handler: Called for every DSP-initiated IPC notification. The @@ -126,6 +128,10 @@ typedef int (*sof_client_fw_booted_callback)(struct sof_client_dev *cdev); * 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. * @get_module_name: Copies the topology name of a module instance owned by @@ -144,6 +150,7 @@ 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_module_name_callback get_module_name; }; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 14ce2c222f91c3..085a3aa646c547 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -891,6 +891,7 @@ 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); int sof_suspend_clients(struct snd_sof_dev *sdev, pm_message_t state); int sof_resume_clients(struct snd_sof_dev *sdev); @@ -933,6 +934,11 @@ 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; From c479cb66608340dedea5a29dab5189f5b1772ee1 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 4 Aug 2026 12:35:58 +0300 Subject: [PATCH 080/100] ASoC: SOF: audio: Remove the unused sdev level ops getters snd_sof_sdev_get_tplg_ops() and snd_sof_sdev_get_pcm_ops() return the ops of the first audio instance found on the SOF device, which is not a meaningful answer once a device can have several instances. They have no users left in the tree, remove them. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-audio.c | 24 ------------------------ sound/soc/sof/sof-audio.h | 2 -- 2 files changed, 26 deletions(-) diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 9ff10ed734d731..4a38b0698ba347 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -103,30 +103,6 @@ snd_sof_component_get_audio_instance(struct snd_soc_component *component) } EXPORT_SYMBOL(snd_sof_component_get_audio_instance); -const struct sof_ipc_tplg_ops *snd_sof_sdev_get_tplg_ops(struct snd_sof_dev *sdev) -{ - struct snd_sof_audio_instance *instance; - - guard(rcu)(); - list_for_each_entry_rcu(instance, &sdev->audio_instance_list, list) - return instance->tplg_ops; - - return NULL; -} -EXPORT_SYMBOL(snd_sof_sdev_get_tplg_ops); - -const struct sof_ipc_pcm_ops *snd_sof_sdev_get_pcm_ops(struct snd_sof_dev *sdev) -{ - struct snd_sof_audio_instance *instance; - - guard(rcu)(); - list_for_each_entry_rcu(instance, &sdev->audio_instance_list, list) - return instance->pcm_ops; - - return NULL; -} -EXPORT_SYMBOL(snd_sof_sdev_get_pcm_ops); - const struct sof_ipc_tplg_ops *snd_sof_component_get_tplg_ops(struct snd_soc_component *component) { struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(component); diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 64ead5e4c4b25c..56433a842c295d 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -648,8 +648,6 @@ snd_sof_audio_instance_register(struct snd_sof_dev *sdev, void snd_sof_audio_instance_unregister(struct snd_sof_audio_instance *instance); struct snd_sof_audio_instance * snd_sof_component_get_audio_instance(struct snd_soc_component *component); -const struct sof_ipc_tplg_ops *snd_sof_sdev_get_tplg_ops(struct snd_sof_dev *sdev); -const struct sof_ipc_pcm_ops *snd_sof_sdev_get_pcm_ops(struct snd_sof_dev *sdev); const struct sof_ipc_tplg_ops *snd_sof_component_get_tplg_ops(struct snd_soc_component *component); const struct sof_ipc_pcm_ops *snd_sof_component_get_pcm_ops(struct snd_soc_component *component); int sof_instance_set_up_pipelines(struct snd_sof_audio_instance *instance); From a0f605441ac5e4125d816ebfc4e764a7811a6bbc Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 4 Aug 2026 12:43:33 +0300 Subject: [PATCH 081/100] ASoC: SOF: Pass the SOF PCM to the platform pcm_pointer callback Every platform pcm_pointer implementation starts with the same snd_sof_find_spcm_dai() lookup to get the snd_sof_pcm the substream belongs to. That lookup is the only reason why the AMD and the MediaTek platform drivers reference symbols owned by the audio code, which stands in the way of moving the audio support to a separate module. The core resolves the very same snd_sof_pcm a few lines after the callback is invoked, so pass it to the callback instead and drop the open coded lookup from the platform drivers. In sof_pcm_pointer() the lookup is moved ahead of the callback, which also makes the (theoretical) case of a missing snd_sof_pcm consistently reported as -EINVAL. The snd_sof_pcm_platform_pointer() wrapper in ops.h has no users, it is dropped rather than updated. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/amd/acp-pcm.c | 10 +--------- sound/soc/sof/amd/acp.h | 1 + sound/soc/sof/intel/hda-pcm.c | 10 +--------- sound/soc/sof/intel/hda.h | 1 + sound/soc/sof/mediatek/mt8365/mt8365.c | 10 +--------- sound/soc/sof/mediatek/mtk-adsp-common.c | 11 ++--------- sound/soc/sof/mediatek/mtk-adsp-common.h | 1 + sound/soc/sof/ops.h | 14 -------------- sound/soc/sof/pcm.c | 8 ++++---- sound/soc/sof/sof-priv.h | 2 ++ 10 files changed, 14 insertions(+), 54 deletions(-) diff --git a/sound/soc/sof/amd/acp-pcm.c b/sound/soc/sof/amd/acp-pcm.c index 2692407e62ca2b..915caac36a0fce 100644 --- a/sound/soc/sof/amd/acp-pcm.c +++ b/sound/soc/sof/amd/acp-pcm.c @@ -93,23 +93,15 @@ int acp_pcm_close(struct snd_soc_component *component, EXPORT_SYMBOL_NS(acp_pcm_close, "SND_SOC_SOF_AMD_COMMON"); 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_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(component, 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 3b0c15cda21f7e..59c1356f8e1af7 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -338,6 +338,7 @@ int acp_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substr struct snd_pcm_hw_params *params, struct snd_sof_platform_stream_params *platform_params); 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/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index b39d136ba2a4df..3f57f166a16492 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -266,22 +266,14 @@ int hda_dsp_compr_trigger(struct snd_soc_component *component, EXPORT_SYMBOL_NS(hda_dsp_compr_trigger, "SND_SOC_SOF_INTEL_HDA_COMMON"); 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_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(component, 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; diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 6234d37e25707b..ac4f81ca9e24c8 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -680,6 +680,7 @@ int hda_dsp_stream_hw_free(struct snd_soc_component *component, 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_soc_component *component, + struct snd_sof_pcm *spcm, struct snd_pcm_substream *substream); int hda_dsp_pcm_ack(struct snd_soc_component *component, struct snd_pcm_substream *substream); diff --git a/sound/soc/sof/mediatek/mt8365/mt8365.c b/sound/soc/sof/mediatek/mt8365/mt8365.c index 87b43f4e814254..7aaeaeed960144 100644 --- a/sound/soc/sof/mediatek/mt8365/mt8365.c +++ b/sound/soc/sof/mediatek/mt8365/mt8365.c @@ -448,23 +448,15 @@ static int mt8365_pcm_hw_params(struct snd_soc_component *component, } 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_dev *sdev = snd_sof_component_get_sdev(component); - struct snd_sof_pcm *spcm; struct sof_ipc_stream_posn posn; struct snd_sof_pcm_stream *stream; - 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(component, 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/mediatek/mtk-adsp-common.c b/sound/soc/sof/mediatek/mtk-adsp-common.c index 0a426e97f1bc57..0ac66ad3926496 100644 --- a/sound/soc/sof/mediatek/mtk-adsp-common.c +++ b/sound/soc/sof/mediatek/mtk-adsp-common.c @@ -174,26 +174,19 @@ EXPORT_SYMBOL(mtk_adsp_stream_pcm_hw_params); /** * mtk_adsp_stream_pcm_pointer - Get host stream pointer * @component: ASoC component + * @spcm: SOF PCM the substream belongs to * @substream: PCM substream */ 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_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(component, 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/mediatek/mtk-adsp-common.h b/sound/soc/sof/mediatek/mtk-adsp-common.h index 7980cd4ab4c0c2..38c7ad4bbdc651 100644 --- a/sound/soc/sof/mediatek/mtk-adsp-common.h +++ b/sound/soc/sof/mediatek/mtk-adsp-common.h @@ -18,5 +18,6 @@ int mtk_adsp_stream_pcm_hw_params(struct snd_soc_component *component, 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_soc_component *component, + struct snd_sof_pcm *spcm, struct snd_pcm_substream *substream); #endif diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 489c7edd913518..2187c73b2177fd 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -615,20 +615,6 @@ 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_soc_component *component, - struct snd_pcm_substream *substream) -{ - struct snd_sof_audio_instance *ins = - snd_sof_component_get_audio_instance(component); - - if (ins && ins->audio_ops->pcm_pointer) - return ins->audio_ops->pcm_pointer(component, substream); - - return 0; -} - /* pcm ack */ static inline int snd_sof_pcm_platform_ack(struct snd_soc_component *component, struct snd_pcm_substream *substream) diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 7154dcf344949e..7f93d2c4c11a7f 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -532,14 +532,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 (ins && ins->audio_ops->pcm_pointer) - return ins->audio_ops->pcm_pointer(component, 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); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 085a3aa646c547..cfbc1af15049e7 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -128,6 +128,7 @@ 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; @@ -154,6 +155,7 @@ struct sof_audio_ops { 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); From 62d3f943bc98035c69fd8b6f0728f22c88ca1e58 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 4 Aug 2026 12:45:49 +0300 Subject: [PATCH 082/100] ASoC: SOF: Intel: hda: Drop the unused PCM lookup in hda_dsp_compr_open() hda_dsp_compr_open() looks up the snd_sof_pcm for the compressed stream but it only uses it to bail out if it is not found. The core has already done the very same lookup before it calls the platform callback, so the check can never trigger. Drop the lookup, it serves no purpose. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/intel/hda-pcm.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index 3f57f166a16492..a980df901aa266 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -434,18 +434,9 @@ int hda_dsp_compr_open(struct snd_soc_component *component, struct snd_compr_stream *cstream) { struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); - struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct hdac_ext_stream *dsp_stream; - struct snd_sof_pcm *spcm; int direction = cstream->direction; - spcm = snd_sof_find_spcm_dai(component, 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__); From c0825224bf9191cc9b314f08916e204b5bd798a4 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 4 Aug 2026 12:52:12 +0300 Subject: [PATCH 083/100] ASoC: SOF: Pass the SOF PCM to the platform pcm_open callback The HDaudio pcm_open implementation repeats the snd_sof_find_spcm_dai() lookup which the core has already done just before it invokes the callback. Pass the resolved snd_sof_pcm to the callback and drop the open coded lookup, removing the last PCM lookup from the platform PCM callbacks. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/amd/acp-pcm.c | 2 +- sound/soc/sof/amd/acp.h | 3 ++- sound/soc/sof/intel/hda-pcm.c | 9 +-------- sound/soc/sof/intel/hda.h | 1 + sound/soc/sof/ops.h | 3 ++- sound/soc/sof/pcm.c | 2 +- sound/soc/sof/sof-priv.h | 2 ++ sound/soc/sof/stream-ipc.c | 1 + 8 files changed, 11 insertions(+), 12 deletions(-) diff --git a/sound/soc/sof/amd/acp-pcm.c b/sound/soc/sof/amd/acp-pcm.c index 915caac36a0fce..a16a40c6b59c3d 100644 --- a/sound/soc/sof/amd/acp-pcm.c +++ b/sound/soc/sof/amd/acp-pcm.c @@ -56,7 +56,7 @@ int acp_pcm_hw_params(struct snd_soc_component *component, } EXPORT_SYMBOL_NS(acp_pcm_hw_params, "SND_SOC_SOF_AMD_COMMON"); -int acp_pcm_open(struct snd_soc_component *component, +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); diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index 59c1356f8e1af7..eaffb947aef22d 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -332,7 +332,8 @@ 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_soc_component *component, 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, diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index a980df901aa266..4e5bbbb0bb4504 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -305,23 +305,16 @@ int hda_dsp_compr_pointer(struct snd_soc_component *component, EXPORT_SYMBOL_NS(hda_dsp_compr_pointer, "SND_SOC_SOF_INTEL_HDA_COMMON"); 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 hdac_ext_stream *dsp_stream; - struct snd_sof_pcm *spcm; int direction = substream->stream; u32 flags = 0; - spcm = snd_sof_find_spcm_dai(component, 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. diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index ac4f81ca9e24c8..ada5f94e04b807 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -668,6 +668,7 @@ 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_soc_component *component, + struct snd_sof_pcm *spcm, struct snd_pcm_substream *substream); int hda_dsp_pcm_close(struct snd_soc_component *component, struct snd_pcm_substream *substream); diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index 2187c73b2177fd..d33922ff98e6bd 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -417,13 +417,14 @@ 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_soc_component *component, + struct snd_sof_pcm *spcm, struct snd_pcm_substream *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, substream); + return ins->audio_ops->pcm_open(component, spcm, substream); return 0; } diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 7f93d2c4c11a7f..e865ac90cdec4e 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -603,7 +603,7 @@ 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(component, 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); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index cfbc1af15049e7..5e557a497c4c82 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -143,6 +143,7 @@ struct snd_sof_platform_stream_params; 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); @@ -875,6 +876,7 @@ int sof_set_stream_data_offset(struct snd_sof_dev *sdev, size_t posn_offset); 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_soc_component *component, struct snd_pcm_substream *substream); diff --git a/sound/soc/sof/stream-ipc.c b/sound/soc/sof/stream-ipc.c index 44c0f98e2840ab..59900fba4f5f27 100644 --- a/sound/soc/sof/stream-ipc.c +++ b/sound/soc/sof/stream-ipc.c @@ -97,6 +97,7 @@ 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_soc_component *component, + struct snd_sof_pcm *spcm, struct snd_pcm_substream *substream) { struct sof_stream *stream = kmalloc_obj(*stream); From c7794e71a1727d0e7fc8d3805e91a55873e98ea4 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 4 Aug 2026 13:04:04 +0300 Subject: [PATCH 084/100] ASoC: SOF: ipc4: Move sof_ipc4_set_pipeline_state() to ipc4.c sof_ipc4_set_pipeline_state() sends the generic SET_PIPELINE_STATE message to the firmware. It is not tied to PCM handling in any way, it only happens to live in ipc4-pcm.c, which makes it one of the few symbols the HDaudio DAI code needs from the audio side. Move it next to sof_ipc4_pipeline_state_str() in ipc4.c and take the sof_client_dev directly instead of deriving it from the ASoC component, which is not needed for a plain IPC message. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/intel/hda-dai-ops.c | 14 ++++++++++---- sound/soc/sof/ipc4-pcm.c | 23 +---------------------- sound/soc/sof/ipc4-priv.h | 2 +- sound/soc/sof/ipc4.c | 20 ++++++++++++++++++++ 4 files changed, 32 insertions(+), 27 deletions(-) diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index 63e7155f3c50f0..e760b3506e0710 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -399,11 +399,14 @@ static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cp 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; @@ -431,7 +434,7 @@ static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cp if (pipeline->state == SOF_IPC4_PIPE_PAUSED) break; - ret = sof_ipc4_set_pipeline_state(component, pipe_widget->instance_id, + ret = sof_ipc4_set_pipeline_state(cdev, pipe_widget->instance_id, SOF_IPC4_PIPE_PAUSED); if (ret < 0) return ret; @@ -490,12 +493,15 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c 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; @@ -519,7 +525,7 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c break; if (pipeline->state != SOF_IPC4_PIPE_PAUSED) { - ret = sof_ipc4_set_pipeline_state(component, pipe_widget->instance_id, + ret = sof_ipc4_set_pipeline_state(cdev, pipe_widget->instance_id, SOF_IPC4_PIPE_PAUSED); if (ret < 0) return ret; @@ -527,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(component, pipe_widget->instance_id, + ret = sof_ipc4_set_pipeline_state(cdev, pipe_widget->instance_id, SOF_IPC4_PIPE_RUNNING); if (ret < 0) return ret; @@ -540,7 +546,7 @@ static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *c !hda_ipc4_all_spipe_dmas_running(substream, swidget->spipe)) break; - ret = sof_ipc4_set_pipeline_state(component, pipe_widget->instance_id, + 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/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index bdcdcf190c317b..b8c0fc9c9a53a6 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -76,7 +76,7 @@ static int sof_ipc4_set_multi_pipeline_state(struct snd_soc_component *component /* trigger a single pipeline */ if (trigger_list->count == 1) - return sof_ipc4_set_pipeline_state(component, + return sof_ipc4_set_pipeline_state(cdev, trigger_list->pipeline_instance_ids[0], state); @@ -102,27 +102,6 @@ static int sof_ipc4_set_multi_pipeline_state(struct snd_soc_component *component return sof_client_ipc_tx_message_no_reply(cdev, &msg); } -int sof_ipc4_set_pipeline_state(struct snd_soc_component *component, u32 instance_id, u32 state) -{ - struct sof_client_dev *cdev = snd_sof_component_get_cdev(component); - struct sof_ipc4_msg msg = {{ 0 }}; - u32 primary; - - dev_dbg(component->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_add_pipeline_by_priority(struct ipc4_pipeline_set_state_data *trigger_list, struct snd_sof_widget *pipe_widget, s8 *pipe_priority, bool ascend) diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index 3b644da79f840f..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_soc_component *component, 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); diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c index c7f97942d1334d..6f3dd5279667e4 100644 --- a/sound/soc/sof/ipc4.c +++ b/sound/soc/sof/ipc4.c @@ -283,6 +283,26 @@ const char *sof_ipc4_pipeline_state_str(enum sof_ipc4_pipeline_state state) } #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) { From 8bf51fd184f94af7ec9989358bcba7f62c2d94ae Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 4 Aug 2026 13:17:12 +0300 Subject: [PATCH 085/100] ASoC: SOF: Turn the D0i3 compatibility check into a client vote snd_sof_dsp_state_is_d0i3_compatible() walked the PCMs of every audio instance from the core and only then folded in the votes of the other clients, which made the core depend on the audio code. Move the PCM walk to the audio client as its d0i3_vote callback, the callback the core already has for exactly this purpose, and leave snd_sof_dsp_state_is_d0i3_compatible() as the fold of the client votes. The IPC4 side will need to participate in the same vote soon, this gives it a place to plug into. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/pm.c | 10 ++++++++++ sound/soc/sof/sof-audio.c | 34 +++++++++++--------------------- sound/soc/sof/sof-audio.h | 3 --- sound/soc/sof/sof-client-audio.c | 11 +++++++++++ sound/soc/sof/sof-client-audio.h | 1 + sound/soc/sof/sof-priv.h | 2 ++ 6 files changed, 35 insertions(+), 26 deletions(-) diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index cf44317fab1e19..f20b2c1d43c894 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -17,6 +17,16 @@ 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) +{ + return sof_client_get_d0i3_vote(sdev) == SOF_D0I3_COMPATIBLE_ACTIVE; +} +EXPORT_SYMBOL(snd_sof_dsp_state_is_d0i3_compatible); + /* * Helper function to determine the target DSP state during * system suspend. This function only cares about the device diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 4a38b0698ba347..6f68e4b1294150 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -1110,15 +1110,18 @@ int sof_widget_list_free(struct snd_soc_component *component, struct snd_sof_pcm * helper to determine if there are only D0i3 compatible * streams active */ -bool snd_sof_dsp_state_is_d0i3_compatible(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; + 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; - for_each_spcm_in_instances(spcm, sdev, instance) { + 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) @@ -1130,30 +1133,15 @@ bool snd_sof_dsp_state_is_d0i3_compatible(struct snd_sof_dev *sdev) * stream state. */ if (!spcm->stream[dir].d0i3_compatible) - return false; + return SOF_D0I3_INCOMPATIBLE; - d0i3_compatible_active = true; + vote = SOF_D0I3_COMPATIBLE_ACTIVE; } } - /* - * Fold in the votes of any sof-client that participates in the D0i3 - * decision: an incompatible client stream vetoes D0i3, a compatible - * active client stream counts as compatible activity. - */ - switch (sof_client_get_d0i3_vote(sdev)) { - case SOF_D0I3_INCOMPATIBLE: - return false; - case SOF_D0I3_COMPATIBLE_ACTIVE: - d0i3_compatible_active = true; - break; - case SOF_D0I3_NO_ACTIVITY: - break; - } - - return d0i3_compatible_active; + return vote; } -EXPORT_SYMBOL(snd_sof_dsp_state_is_d0i3_compatible); +EXPORT_SYMBOL(sof_audio_instance_d0i3_vote); bool sof_audio_instance_suspend_ignored(struct snd_soc_component *component) { diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 56433a842c295d..368359f72b3742 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -717,9 +717,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_dsp_state_is_d0i3_compatible(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); diff --git a/sound/soc/sof/sof-client-audio.c b/sound/soc/sof/sof-client-audio.c index 32e7b625e7cf6d..a3ecad7bcf2eb8 100644 --- a/sound/soc/sof/sof-client-audio.c +++ b/sound/soc/sof/sof-client-audio.c @@ -92,10 +92,21 @@ static bool sof_audio_client_keep_dsp_in_d0(struct sof_client_dev *cdev) 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 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, .get_module_name = sof_audio_client_get_module_name, }; diff --git a/sound/soc/sof/sof-client-audio.h b/sound/soc/sof/sof-client-audio.h index f49940cef2f153..4053f16c50a984 100644 --- a/sound/soc/sof/sof-client-audio.h +++ b/sound/soc/sof/sof-client-audio.h @@ -60,6 +60,7 @@ 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); void snd_sof_new_platform_drv(struct sof_client_dev *cdev, struct snd_soc_component_driver * const pd); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 5e557a497c4c82..d5bec17d41432c 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -881,6 +881,8 @@ int sof_stream_pcm_open(struct snd_soc_component *component, 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); + /* SOF client support */ struct sof_client_dev; From 2e71e4cf090a0b08fa7fb58a331f8a2e1f3b09f9 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 4 Aug 2026 18:37:06 +0300 Subject: [PATCH 086/100] ASoC: SOF: Notify the clients about elapsed periods The platform interrupt handler has no way to tell which audio instance owns the DMA stream which just completed a period, it only has the substream. Add a period_elapsed client callback and let the clients themselves decide whether the substream is theirs. The first client claiming the substream ends the search, the same way as the get_module_name callback works. The callback is special among the client ops: it is invoked from the interrupt handler, which may hold its own locks, so it must not sleep. The audio client only takes the ownership if the substream is bound to its own ASoC component, matching on the dai_link id alone is not enough as the ids are only unique within a card. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/intel/hda-stream.c | 7 +++--- sound/soc/sof/pcm.c | 32 +++++++++++++-------------- sound/soc/sof/sof-audio.h | 1 - sound/soc/sof/sof-client-audio-ipc3.c | 2 +- sound/soc/sof/sof-client-audio.c | 12 ++++++++++ sound/soc/sof/sof-client-audio.h | 2 ++ sound/soc/sof/sof-client.c | 19 ++++++++++++++++ sound/soc/sof/sof-client.h | 11 ++++++++- sound/soc/sof/sof-priv.h | 7 ++++++ 9 files changed, 71 insertions(+), 22 deletions(-) diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index fbcd2c6cf95bf0..0cad6eaf95fa4d 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -859,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; @@ -897,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); @@ -926,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) { diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index e865ac90cdec4e..3f81349befaa86 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -40,33 +40,33 @@ 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); +EXPORT_SYMBOL(sof_audio_instance_period_elapsed); int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd, diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 368359f72b3742..8bc1fc5e718873 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -673,7 +673,6 @@ 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, diff --git a/sound/soc/sof/sof-client-audio-ipc3.c b/sound/soc/sof/sof-client-audio-ipc3.c index 919141ef7dcf48..41ef5b0e3aed0c 100644 --- a/sound/soc/sof/sof-client-audio-ipc3.c +++ b/sound/soc/sof/sof-client-audio-ipc3.c @@ -52,7 +52,7 @@ static void sof_audio_client_ipc3_period_elapsed(struct sof_client_dev *cdev, 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); + queue_work(system_highpri_wq, &stream->period_elapsed_work); } /* DSP notifies host of an XRUN within FW */ diff --git a/sound/soc/sof/sof-client-audio.c b/sound/soc/sof/sof-client-audio.c index a3ecad7bcf2eb8..8ceb7d43ae37c8 100644 --- a/sound/soc/sof/sof-client-audio.c +++ b/sound/soc/sof/sof-client-audio.c @@ -102,11 +102,23 @@ static enum sof_d0i3_vote sof_audio_client_d0i3_vote(struct sof_client_dev *cdev 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, }; diff --git a/sound/soc/sof/sof-client-audio.h b/sound/soc/sof/sof-client-audio.h index 4053f16c50a984..9cdec679ed2e31 100644 --- a/sound/soc/sof/sof-client-audio.h +++ b/sound/soc/sof/sof-client-audio.h @@ -61,6 +61,8 @@ 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); diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index 009dc308f671af..e5916e147e38de 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -802,6 +802,25 @@ enum sof_d0i3_vote sof_client_get_d0i3_vote(struct snd_sof_dev *sdev) } EXPORT_SYMBOL_NS_GPL(sof_client_get_d0i3_vote, "SND_SOC_SOF_CLIENT"); +void sof_client_period_elapsed(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream) +{ + struct sof_client_dev *cdev; + + 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; + + 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_ops(struct sof_client_dev *cdev, const struct sof_client_ops *ops) { diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index 17dc0d4c7b6483..2f9a8fc006959b 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -118,6 +118,9 @@ 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 @@ -134,6 +137,11 @@ typedef bool (*sof_client_keep_dsp_in_d0_callback)(struct sof_client_dev *cdev); * 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. @@ -144,7 +152,7 @@ typedef bool (*sof_client_keep_dsp_in_d0_callback)(struct sof_client_dev *cdev); * 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. + * may sleep and may send IPC messages to the DSP, unless noted otherwise. */ struct sof_client_ops { sof_client_event_callback ipc_rx_handler; @@ -152,6 +160,7 @@ struct sof_client_ops { 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; }; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index d5bec17d41432c..f4a2e38b03575d 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -899,6 +899,8 @@ 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 */ @@ -950,6 +952,11 @@ static inline enum sof_d0i3_vote sof_client_get_d0i3_vote(struct snd_sof_dev *sd 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; From 5b4d478787e62b73cbd22bd7861cceffb0b09ad6 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 6 Aug 2026 10:45:33 +0300 Subject: [PATCH 087/100] ASoC: SOF: Reach the audio instance directly from the component The audio instance of an ASoC component was looked up by walking the audio instance list of the SOF device, which the platform drivers had to follow as well to get to the topology ops. There is exactly one audio instance per audio client and the component already carries the client device as its drvdata, so save the instance in the client platform data and turn the component level getters into pointer dereferences. The getters are inline now, which removes the last symbol reference from the platform drivers to the audio code. The instance list itself is kept for the cross instance aggregations. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/sof-audio.c | 43 ++++++-------------------------- sound/soc/sof/sof-audio.h | 36 +++++++++++++++++++++++--- sound/soc/sof/sof-client-audio.h | 3 +++ 3 files changed, 43 insertions(+), 39 deletions(-) diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 6f68e4b1294150..8a41eef668879a 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -19,6 +19,8 @@ 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; @@ -72,53 +74,24 @@ snd_sof_audio_instance_register(struct snd_sof_dev *sdev, scoped_guard(spinlock, &sdev->audio_instance_list_lock) list_add_tail_rcu(&instance->list, &sdev->audio_instance_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); struct snd_sof_dev *sdev = instance->sdev; + pdata->instance = NULL; + scoped_guard(spinlock, &sdev->audio_instance_list_lock) list_del_rcu(&instance->list); synchronize_rcu(); } -struct snd_sof_audio_instance * -snd_sof_component_get_audio_instance(struct snd_soc_component *component) -{ - struct snd_sof_dev *sdev = snd_sof_component_get_sdev(component); - struct snd_sof_audio_instance *instance; - - if (!sdev) - return NULL; - - guard(rcu)(); - list_for_each_entry_rcu(instance, &sdev->audio_instance_list, list) { - if (instance->component == component) - return instance; - } - - return NULL; -} -EXPORT_SYMBOL(snd_sof_component_get_audio_instance); - -const struct sof_ipc_tplg_ops *snd_sof_component_get_tplg_ops(struct snd_soc_component *component) -{ - struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(component); - - return instance ? instance->tplg_ops : NULL; -} -EXPORT_SYMBOL(snd_sof_component_get_tplg_ops); - -const struct sof_ipc_pcm_ops *snd_sof_component_get_pcm_ops(struct snd_soc_component *component) -{ - struct snd_sof_audio_instance *instance = snd_sof_component_get_audio_instance(component); - - return instance ? instance->pcm_ops : NULL; -} -EXPORT_SYMBOL(snd_sof_component_get_pcm_ops); - /* * 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 diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 8bc1fc5e718873..84ed5b9ae900b7 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" @@ -646,10 +647,37 @@ 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); -struct snd_sof_audio_instance * -snd_sof_component_get_audio_instance(struct snd_soc_component *component); -const struct sof_ipc_tplg_ops *snd_sof_component_get_tplg_ops(struct snd_soc_component *component); -const struct sof_ipc_pcm_ops *snd_sof_component_get_pcm_ops(struct snd_soc_component *component); + +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 diff --git a/sound/soc/sof/sof-client-audio.h b/sound/soc/sof/sof-client-audio.h index 9cdec679ed2e31..6d42f19addee8e 100644 --- a/sound/soc/sof/sof-client-audio.h +++ b/sound/soc/sof/sof-client-audio.h @@ -7,6 +7,7 @@ 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 @@ -33,6 +34,7 @@ struct sof_audio_client_ipc_ops { * @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 @@ -45,6 +47,7 @@ struct sof_audio_client_pdata { 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; From 4c2040dd7a1a271b0e24c15ac20004b66a473e95 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 6 Aug 2026 11:17:38 +0300 Subject: [PATCH 088/100] ASoC: SOF: Drop the audio instance list of the SOF device The last user of the instance list was the HDA D0i3 streaming check, which walked the PCMs of every audio instance to tell if all the active streams are D0i3 compatible and at least one of them is playback. The D0i3 vote of the clients already answers the first half, extend it with a playback flavour of the compatible vote to answer the second one as well and fold the votes by their precedence. With that the instance list, its lock and the iteration macro have no users left, an audio instance is only reachable via its own component. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/core.c | 2 -- sound/soc/sof/intel/hda-dsp.c | 32 ++------------------------------ sound/soc/sof/pm.c | 11 ++++++++++- sound/soc/sof/sof-audio.c | 13 ++++--------- sound/soc/sof/sof-audio.h | 3 --- sound/soc/sof/sof-client.c | 22 +++++++++------------- sound/soc/sof/sof-client.h | 10 ++++------ sound/soc/sof/sof-priv.h | 4 +--- 8 files changed, 30 insertions(+), 67 deletions(-) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 17dd3557852cf5..2c1611bc8360c4 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -670,7 +670,6 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) if (ret) return ret; - INIT_LIST_HEAD(&sdev->audio_instance_list); INIT_LIST_HEAD(&sdev->ipc_client_list); INIT_LIST_HEAD(&sdev->client_ops_list); @@ -684,7 +683,6 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data) spin_lock_init(&sdev->ipc_lock); spin_lock_init(&sdev->hw_lock); - spin_lock_init(&sdev->audio_instance_list_lock); mutex_init(&sdev->power_state_access); mutex_init(&sdev->ipc_client_mutex); mutex_init(&sdev->client_ops_mutex); diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 34f6eb3630e8ed..a04c830c6a659e 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -523,35 +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_sof_audio_instance *instance; - struct snd_pcm_substream *substream; - struct snd_sof_pcm *spcm; - bool playback_active = false; - int dir; - - for_each_spcm_in_instances(spcm, sdev, instance) { - 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) { @@ -594,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 */ diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index f20b2c1d43c894..0ef5d258268b10 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -23,10 +23,19 @@ MODULE_PARM_DESC(on_demand_boot, "Force on-demand DSP boot: 0 - disabled, 1 - en */ bool snd_sof_dsp_state_is_d0i3_compatible(struct snd_sof_dev *sdev) { - return sof_client_get_d0i3_vote(sdev) == SOF_D0I3_COMPATIBLE_ACTIVE; + 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 diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 8a41eef668879a..5d964ce58fbe1c 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -71,9 +71,6 @@ snd_sof_audio_instance_register(struct snd_sof_dev *sdev, INIT_LIST_HEAD(&instance->kcontrol_list); INIT_LIST_HEAD(&instance->widget_list); - scoped_guard(spinlock, &sdev->audio_instance_list_lock) - list_add_tail_rcu(&instance->list, &sdev->audio_instance_list); - pdata->instance = instance; return instance; @@ -83,13 +80,8 @@ 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); - struct snd_sof_dev *sdev = instance->sdev; pdata->instance = NULL; - - scoped_guard(spinlock, &sdev->audio_instance_list_lock) - list_del_rcu(&instance->list); - synchronize_rcu(); } /* @@ -1108,7 +1100,10 @@ enum sof_d0i3_vote sof_audio_instance_d0i3_vote(struct snd_soc_component *compon if (!spcm->stream[dir].d0i3_compatible) return SOF_D0I3_INCOMPATIBLE; - vote = SOF_D0I3_COMPATIBLE_ACTIVE; + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + vote = SOF_D0I3_COMPATIBLE_PLAYBACK; + else + vote = max(vote, SOF_D0I3_COMPATIBLE_ACTIVE); } } diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 84ed5b9ae900b7..758b90bb9c039f 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -48,9 +48,6 @@ (id) == snd_soc_dapm_decoder || \ (id) == snd_soc_dapm_encoder) -#define for_each_spcm_in_instances(spcm, sdev, instance) \ - list_for_each_entry(instance, &(sdev)->audio_instance_list, list) \ - list_for_each_entry(spcm, &(instance)->pcm_list, list) #define SOF_DAI_PARAM_INTEL_SSP_MCLK 0 #define SOF_DAI_PARAM_INTEL_SSP_BCLK 1 diff --git a/sound/soc/sof/sof-client.c b/sound/soc/sof/sof-client.c index e5916e147e38de..0ce9f3a67ac1d8 100644 --- a/sound/soc/sof/sof-client.c +++ b/sound/soc/sof/sof-client.c @@ -770,10 +770,8 @@ bool sof_client_keep_dsp_in_d0(struct snd_sof_dev *sdev) EXPORT_SYMBOL_NS_GPL(sof_client_keep_dsp_in_d0, "SND_SOC_SOF_CLIENT"); /* - * Fold the D0i3 votes of every client that implements the d0i3_vote callback: - * - any SOF_D0I3_INCOMPATIBLE -> SOF_D0I3_INCOMPATIBLE (veto) - * - else any SOF_D0I3_COMPATIBLE_ACTIVE -> SOF_D0I3_COMPATIBLE_ACTIVE - * - else -> SOF_D0I3_NO_ACTIVITY + * 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) @@ -784,18 +782,16 @@ enum sof_d0i3_vote sof_client_get_d0i3_vote(struct snd_sof_dev *sdev) 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; + if (!cdev->ops->d0i3_vote) continue; - switch (cdev->ops->d0i3_vote(cdev)) { - case SOF_D0I3_INCOMPATIBLE: - return SOF_D0I3_INCOMPATIBLE; - case SOF_D0I3_COMPATIBLE_ACTIVE: - result = SOF_D0I3_COMPATIBLE_ACTIVE; - break; - case SOF_D0I3_NO_ACTIVITY: - break; - } + vote = cdev->ops->d0i3_vote(cdev); + if (vote == SOF_D0I3_INCOMPATIBLE) + return vote; + + result = max(result, vote); } return result; diff --git a/sound/soc/sof/sof-client.h b/sound/soc/sof/sof-client.h index 2f9a8fc006959b..f6342f9a337c16 100644 --- a/sound/soc/sof/sof-client.h +++ b/sound/soc/sof/sof-client.h @@ -94,16 +94,14 @@ typedef void (*sof_client_fw_state_callback)(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 core folds the votes of all clients - * that implement the d0i3_vote callback: - * - any SOF_D0I3_INCOMPATIBLE -> D0i3 denied - * - else any SOF_D0I3_COMPATIBLE_ACTIVE -> D0i3 allowed - * - else (all SOF_D0I3_NO_ACTIVITY) -> D0i3 denied, nothing to keep it up - * Clients without the callback do not participate in the vote. + * 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 */ }; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index f4a2e38b03575d..d31d521ac9bc2e 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -225,7 +225,6 @@ struct snd_sof_audio_instance { struct list_head widget_list; bool led_present; bool pipelines_set_up; - struct list_head list; }; /** @@ -667,8 +666,6 @@ struct snd_sof_dev { /* topology */ struct snd_soc_tplg_ops *tplg_ops; const struct sof_audio_ops *audio_ops; - struct list_head audio_instance_list; - spinlock_t audio_instance_list_lock; u32 enabled_cores_mask; /* keep track of enabled cores */ /* FW configuration */ @@ -882,6 +879,7 @@ 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; From 2c1b3c58a238d2b2191fb069c0204ecf2be822a5 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 6 Aug 2026 12:14:40 +0300 Subject: [PATCH 089/100] ASoC: SOF: Build the audio objects into the snd-sof-audio module The 12 objects implementing the ASoC component (PCM, topology, control and their IPC3/IPC4 backends) no longer reference anything provided by the platform drivers, so they can finally leave snd-sof.ko and join the audio client in snd-sof-audio.ko. Export the handful of core facilities they still need: the widget, position and topology tracepoints, sof_ipc4_find_module_by_uuid(), sof_ipc4_update_cpc_from_manifest() and sof_ipc4_pipeline_state_str(). snd-sof-audio.ko now depends on snd-sof.ko and the platform drivers only depend on snd-sof.ko, so there is no dependency cycle. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/Makefile | 23 +++++++++++------------ sound/soc/sof/core.c | 5 ++++- sound/soc/sof/ipc4-loader.c | 2 ++ sound/soc/sof/ipc4.c | 2 ++ 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index f652845b5f9464..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,12 +23,16 @@ 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 +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 +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 +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),) diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 2c1611bc8360c4..6941b9204faa09 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -19,8 +19,11 @@ #define CREATE_TRACE_POINTS #include -/* used by the audio client for the IPC3 stream position notification */ +/* used by the audio client */ EXPORT_TRACEPOINT_SYMBOL_GPL(sof_ipc3_period_elapsed_position); +EXPORT_TRACEPOINT_SYMBOL_GPL(sof_pcm_pointer_position); +EXPORT_TRACEPOINT_SYMBOL_GPL(sof_widget_setup); +EXPORT_TRACEPOINT_SYMBOL_GPL(sof_widget_free); /* Module parameters for firmware, topology and IPC type override */ static char *override_fw_path; 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.c b/sound/soc/sof/ipc4.c index 6f3dd5279667e4..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,6 +282,7 @@ 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) From 83694e873847c1ec2107ea70842d8487eec32cef Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 6 Aug 2026 15:50:55 +0300 Subject: [PATCH 090/100] ASoC: SOF: Move the audio tracepoints to a trace header of their own The tracepoints of the SOF core are declared in a single trace header, include/trace/events/sof.h. CREATE_TRACE_POINTS can only be defined in one translation unit per trace header, and that unit defines every event of the header, so the header has exactly one owning module: core.c in snd-sof.ko. Four of the six events are only used by the audio client module, which made it necessary to export them with EXPORT_TRACEPOINT_SYMBOL_GPL() to be able to use them from there. Move sof_widget_setup, sof_widget_free, sof_ipc3_period_elapsed_position and sof_pcm_pointer_position to a new header, include/trace/events/sof_audio.h, and instantiate it in the audio client. Each event is now defined by the module which uses it and none of them needs to be exported. The new header keeps TRACE_SYSTEM as sof and only overrides TRACE_INCLUDE_FILE, so the events stay under the same /sys/kernel/tracing/events/sof/ directory as before. Signed-off-by: Peter Ujfalusi --- include/trace/events/sof.h | 74 +-------------------- include/trace/events/sof_audio.h | 94 +++++++++++++++++++++++++++ sound/soc/sof/core.c | 6 -- sound/soc/sof/pcm.c | 2 +- sound/soc/sof/sof-audio.c | 2 +- sound/soc/sof/sof-client-audio-ipc3.c | 2 +- sound/soc/sof/sof-client-audio.c | 3 + 7 files changed, 101 insertions(+), 82 deletions(-) create mode 100644 include/trace/events/sof_audio.h 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/sof/core.c b/sound/soc/sof/core.c index 6941b9204faa09..4398d544e34dc9 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -19,12 +19,6 @@ #define CREATE_TRACE_POINTS #include -/* used by the audio client */ -EXPORT_TRACEPOINT_SYMBOL_GPL(sof_ipc3_period_elapsed_position); -EXPORT_TRACEPOINT_SYMBOL_GPL(sof_pcm_pointer_position); -EXPORT_TRACEPOINT_SYMBOL_GPL(sof_widget_setup); -EXPORT_TRACEPOINT_SYMBOL_GPL(sof_widget_free); - /* Module parameters for firmware, topology and IPC type override */ static char *override_fw_path; module_param_named(fw_path, override_fw_path, charp, 0444); diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 3f81349befaa86..35a876a45f5a86 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include "sof-of-dev.h" #include "sof-priv.h" #include "sof-audio.h" diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 5d964ce58fbe1c..250703afffafcd 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -9,7 +9,7 @@ // #include -#include +#include #include "sof-audio.h" #include "sof-client-audio.h" #include "sof-utils.h" diff --git a/sound/soc/sof/sof-client-audio-ipc3.c b/sound/soc/sof/sof-client-audio-ipc3.c index 41ef5b0e3aed0c..1d03aee0dfea16 100644 --- a/sound/soc/sof/sof-client-audio-ipc3.c +++ b/sound/soc/sof/sof-client-audio-ipc3.c @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include "sof-audio.h" #include "sof-client.h" diff --git a/sound/soc/sof/sof-client-audio.c b/sound/soc/sof/sof-client-audio.c index 8ceb7d43ae37c8..ff65ed98d5305e 100644 --- a/sound/soc/sof/sof-client-audio.c +++ b/sound/soc/sof/sof-client-audio.c @@ -15,6 +15,9 @@ #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, From 496ae8d7caceb54f8cdc1018b1b3c57e01690871 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 6 Aug 2026 12:23:08 +0300 Subject: [PATCH 091/100] ASoC: SOF: Drop the exports which became module internal With the audio objects living in snd-sof-audio.ko these symbols are only used within that module, no need to place them into the kernel's symbol table. The remaining exports have users outside of the module: the machine drivers call the DAI parameter helpers and snd-sof-nocodec.ko uses sof_pcm_dai_link_fixup(). Signed-off-by: Peter Ujfalusi --- sound/soc/sof/ipc4-compress.c | 1 - sound/soc/sof/ipc4-topology.c | 1 - sound/soc/sof/pcm.c | 2 -- sound/soc/sof/sof-audio.c | 9 --------- sound/soc/sof/topology.c | 1 - 5 files changed, 14 deletions(-) diff --git a/sound/soc/sof/ipc4-compress.c b/sound/soc/sof/ipc4-compress.c index 988d22bf754fb0..6c74f4d0ba18bc 100644 --- a/sound/soc/sof/ipc4-compress.c +++ b/sound/soc/sof/ipc4-compress.c @@ -766,7 +766,6 @@ void sof_ipc4_compr_drain_done(struct snd_soc_component *scomp, void *ipc_messag if (spcm->stream[dir].cstream) snd_compr_drain_notify(spcm->stream[dir].cstream); } -EXPORT_SYMBOL(sof_ipc4_compr_drain_done); const struct snd_compress_ops sof_ipc4_compressed_ops = { .open = sof_ipc4_compr_open, diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index f41e42e01a9e27..6bcefe45653451 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -242,7 +242,6 @@ struct snd_sof_widget *sof_ipc4_find_swidget_by_ids(struct snd_soc_component *sc return NULL; } -EXPORT_SYMBOL(sof_ipc4_find_swidget_by_ids); static u32 sof_ipc4_fmt_cfg_to_type(u32 fmt_cfg) { diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 35a876a45f5a86..9e08acf76cbdf7 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -66,7 +66,6 @@ bool sof_audio_instance_period_elapsed(struct snd_soc_component *component, return true; } -EXPORT_SYMBOL(sof_audio_instance_period_elapsed); int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd, @@ -938,4 +937,3 @@ void snd_sof_new_platform_drv(struct sof_client_dev *cdev, if (!sof_client_is_dspless(cdev)) pd->be_hw_params_fixup = sof_pcm_dai_link_fixup; } -EXPORT_SYMBOL(snd_sof_new_platform_drv); diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 250703afffafcd..a61444bfcee862 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -131,7 +131,6 @@ int sof_audio_instance_suspend(struct snd_soc_component *component) return 0; } -EXPORT_SYMBOL(sof_audio_instance_suspend); /* * Set up the topology pipelines owned by a single audio instance. @@ -142,7 +141,6 @@ int sof_audio_instance_resume(struct snd_soc_component *component) return instance ? sof_instance_set_up_pipelines(instance) : 0; } -EXPORT_SYMBOL(sof_audio_instance_resume); /* * Set up the topology pipelines owned by a single audio instance after a DSP @@ -159,7 +157,6 @@ int sof_audio_instance_restore(struct snd_soc_component *component) return sof_instance_set_up_pipelines(instance); } -EXPORT_SYMBOL(sof_audio_instance_restore); /* * Check if a DAI widget is an aggregated DAI. Aggregated DAI's have names ending in numbers @@ -296,7 +293,6 @@ 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->scomp, swidget); } -EXPORT_SYMBOL(sof_widget_free); static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev, struct snd_soc_component *scomp, @@ -412,7 +408,6 @@ 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->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) @@ -1109,7 +1104,6 @@ enum sof_d0i3_vote sof_audio_instance_d0i3_vote(struct snd_soc_component *compon return vote; } -EXPORT_SYMBOL(sof_audio_instance_d0i3_vote); bool sof_audio_instance_suspend_ignored(struct snd_soc_component *component) { @@ -1127,7 +1121,6 @@ bool sof_audio_instance_suspend_ignored(struct snd_soc_component *component) return false; } -EXPORT_SYMBOL(sof_audio_instance_suspend_ignored); /* * Generic object lookup APIs. @@ -1180,7 +1173,6 @@ struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp, return NULL; } -EXPORT_SYMBOL(snd_sof_find_spcm_comp); struct snd_sof_widget *snd_sof_find_swidget(struct snd_soc_component *scomp, const char *name) @@ -1343,7 +1335,6 @@ void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstream) /* use the same workqueue-based solution as for PCM, cf. snd_sof_pcm_elapsed */ schedule_work(&spcm->stream[cstream->direction].period_elapsed_work); } -EXPORT_SYMBOL(snd_sof_compr_fragment_elapsed); int snd_sof_compr_create_page_table(struct snd_soc_component *component, struct snd_compr_stream *cstream, diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 977f0b102842dc..f0ae3539a0b717 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2706,4 +2706,3 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file) return ret; } -EXPORT_SYMBOL(snd_sof_load_topology); From b75b90154eaf3f4c2b843e4c1451a15bef9f4e3a Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 7 Aug 2026 08:10:26 +0300 Subject: [PATCH 092/100] -- multi-card from here -- From d55dd0865996225c10a115fc1b5743ecd7bf7084 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 3 Jun 2026 19:54:03 +0300 Subject: [PATCH 093/100] ASoC: core: scope CPU DAI override to component-owned DAIs When soc_check_tplg_fes() overrides BE DAI link CPU component names, only set cpu->name when the DAI is actually registered by the matching component. This prevents cross-component DAI binding issues when multiple components provide identically-named DAIs (e.g., SDW DAIs in soundwire_intel vs SOF audio components). Signed-off-by: Peter Ujfalusi --- sound/soc/soc-core.c | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) 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; From d6ef747a31956a470790de6d54c70d07b2459338 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 3 Jun 2026 19:53:53 +0300 Subject: [PATCH 094/100] ASoC: Intel: sof-function-topology-lib: skip unsupported DAI links When a function topology does not support a DAI link, continue to the next one instead of failing the entire topology load. This allows partial topology loading where not all DAI links have matching function topologies. Signed-off-by: Peter Ujfalusi --- sound/soc/intel/common/sof-function-topology-lib.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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; From 2bce5a4988140ab35854a5a42e7c5e279e704a0a Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 5 Jun 2026 11:40:19 +0300 Subject: [PATCH 095/100] ASoC: SOF: topology: skip feature topologies without matching BE DAI links Before loading a user-specified feature topology, verify that the card has a matching BE DAI link. Feature topologies extend specific codec function types (amp, jack, mic) and reference widgets that only exist when the corresponding BE DAI links are present. Without this check, loading an amplifier feature topology on a card without SmartAmp DAI links would fail with -EINVAL due to missing route endpoints, killing the entire card probe. This is needed for multi-card configurations where cards are split by function type, but also provides a safety net in single-card mode when feature topologies don't match the hardware. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/topology.c | 63 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index f0ae3539a0b717..5454d69ea7ed30 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2548,6 +2548,47 @@ 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_audio_instance *instance = snd_sof_component_get_audio_instance(scomp); @@ -2647,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; From 7521f105e0a17d9a02bd4c59685248a7fcb29126 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 9 Jun 2026 09:36:17 +0300 Subject: [PATCH 096/100] ASoC: Intel: sof_sdw: add multi-card support When more than one card is created on top of the same audio DSP the sof_sdw machine driver must be able to name its card and to know which DAI types it owns, both coming from the machine parameters. Use mach_params.card_name in mc_probe() to name the card when it is provided and keep the "soundwire" name otherwise. Add the dai_type_mask field to snd_soc_acpi_mach_params, it is used by the SoundWire endpoint parsing in a follow-up patch. Signed-off-by: Peter Ujfalusi --- include/sound/soc-acpi.h | 3 +++ sound/soc/intel/boards/sof_sdw.c | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h index f0bba5fd20f195..10389154c20cad 100644 --- a/include/sound/soc-acpi.h +++ b/include/sound/soc-acpi.h @@ -75,6 +75,8 @@ static inline struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg) * 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; @@ -92,6 +94,7 @@ struct snd_soc_acpi_mach_params { bool subsystem_id_set; u32 bt_link_mask; const char *card_name; + u32 dai_type_mask; }; /** 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; From 7836b4482eb845065afd74b8ef1f87ab9457f6ec Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 4 Jun 2026 15:54:44 +0300 Subject: [PATCH 097/100] ASoC: sdw_utils: add dai_type_mask filtering for multi-card support Add dai_type_mask field filtering in asoc_sdw_count_sdw_endpoints() and asoc_sdw_parse_sdw_endpoints() to allow multi-card configurations to include only specific DAI types. When dai_type_mask is non-zero, only codec endpoints with matching dai_type bits are counted and parsed. Auxiliary devices for codecs without matching endpoints are also skipped. Signed-off-by: Peter Ujfalusi --- sound/soc/sdw_utils/soc_sdw_utils.c | 89 ++++++++++++++++++++++------- 1 file changed, 68 insertions(+), 21 deletions(-) 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); /* From 0ebd3ce4de44b89460e1ed21a9bedc0540b0507f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Wed, 3 Jun 2026 19:54:25 +0300 Subject: [PATCH 098/100] ASoC: SOF: Intel: HDA: add multi-card audio client registration Add Intel-specific multi-card audio client registration that creates separate sound cards per codec function type when the multi_card module parameter is set. The registration groups SoundWire endpoints by DAI type (jack, amp, mic) and creates a card per group using codec-specific names (e.g. cs42l43, cs35l56) with a generic function name fallback (jack, speaker, mic). DMIC and HDMI are registered as additional separate cards. Individual card registration failures are non-fatal to allow partial audio functionality when some components fail to probe. When multi_card is not set (default), falls back to the generic single-card sof_register_audio_client(). This is experimental and opt-in via: modprobe snd-sof-intel-hda-generic multi_card=1 Signed-off-by: Peter Ujfalusi --- sound/soc/sof/intel/hda-common-ops.c | 4 +- sound/soc/sof/intel/hda.c | 237 +++++++++++++++++++++++++++ sound/soc/sof/intel/hda.h | 7 + 3 files changed, 246 insertions(+), 2 deletions(-) diff --git a/sound/soc/sof/intel/hda-common-ops.c b/sound/soc/sof/intel/hda-common-ops.c index 0d7724eeca009b..610d05425020fc 100644 --- a/sound/soc/sof/intel/hda-common-ops.c +++ b/sound/soc/sof/intel/hda-common-ops.c @@ -104,8 +104,8 @@ const struct snd_sof_dsp_ops sof_hda_common_ops = { .unregister_ipc_clients = hda_unregister_clients, /* audio client */ - .register_audio_client = sof_register_audio_client, - .unregister_audio_client = sof_unregister_audio_client, + .register_audio_client = hda_register_audio_client, + .unregister_audio_client = hda_unregister_audio_client, /* DAI drivers */ .is_chain_dma_supported = hda_is_chain_dma_supported, diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index ca04651d975718..fba00096ce14a4 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" @@ -1767,6 +1769,241 @@ 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."); + +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" }, +}; + +/* + * 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)) { + 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"; + ret = sof_client_dev_register(sdev, "audio", card_idx, + &pdata, sizeof(pdata)); + 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; +} + +int hda_register_audio_client(struct snd_sof_dev *sdev) +{ + if (multi_card) { + const struct snd_soc_acpi_mach *mach = sdev->pdata->machine; + + 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"); + } + + return sof_register_audio_client(sdev); +} + +void hda_unregister_audio_client(struct snd_sof_dev *sdev) +{ + if (multi_card) { + const struct snd_soc_acpi_mach *mach = sdev->pdata->machine; + + 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; + } + } + + sof_unregister_audio_client(sdev); +} + int hda_register_clients(struct snd_sof_dev *sdev) { return hda_probes_register(sdev); diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index ada5f94e04b807..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) @@ -1013,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, From fe4752bc6f8541b70ab83a042d92833fa78048b6 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 9 Jun 2026 09:19:20 +0300 Subject: [PATCH 099/100] ASoC: Intel: skl_hda_dsp_generic: use mach params for card setup Use mach_params.card_name when provided. Propagate mach_params.dmic_num to ctx->dmic_be_num. Machine data then controls DMIC BE exposure in this card. This enables per-audio-client card setup for split configurations. Signed-off-by: Peter Ujfalusi --- sound/soc/intel/boards/skl_hda_dsp_generic.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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; From 22fc861421c7c7dc645ef0dc44d692eb35e05172 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 11 Jun 2026 11:38:31 +0300 Subject: [PATCH 100/100] ASoC: SOF: Intel: hda: add split_hdmi audio-client mode Add split_hdmi mode to register separate analog and HDMI audio clients when iDisp is present. Keep the analog card on the selected machine configuration with iDisp masked out, and register HDMI as a dedicated generic HDA card using sof-hda-generic-idisp.tplg. Update unregister handling to support split fallback to the legacy single-client path when split registration is not used. Signed-off-by: Peter Ujfalusi --- sound/soc/sof/intel/hda.c | 128 ++++++++++++++++++++++++++++++++++---- 1 file changed, 116 insertions(+), 12 deletions(-) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index fba00096ce14a4..1b861bd58f8604 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -1773,6 +1773,10 @@ 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; @@ -1784,6 +1788,28 @@ static const struct hda_sdw_func_card sdw_func_cards[] = { { 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 @@ -1947,14 +1973,7 @@ static int hda_register_audio_client_multi(struct snd_sof_dev *sdev) /* HDMI card */ if (HDA_IDISP_CODEC(mach->mach_params.codec_mask)) { - 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"; - ret = sof_client_dev_register(sdev, "audio", card_idx, - &pdata, sizeof(pdata)); + 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", @@ -1968,11 +1987,75 @@ static int hda_register_audio_client_multi(struct snd_sof_dev *sdev) 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) { - if (multi_card) { - const struct snd_soc_acpi_mach *mach = sdev->pdata->machine; + 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); @@ -1981,14 +2064,17 @@ int hda_register_audio_client(struct snd_sof_dev *sdev) "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) { - if (multi_card) { - const struct snd_soc_acpi_mach *mach = sdev->pdata->machine; + 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; @@ -2001,6 +2087,24 @@ void hda_unregister_audio_client(struct snd_sof_dev *sdev) } } + 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); }