Skip to content

[PoC] ASoC: SOF: Move audio support as sof-client (support for multiple cards)#5815

Draft
ujfalusi wants to merge 83 commits into
thesofproject:topic/sof-devfrom
ujfalusi:peter/topic/multi_card_poc01
Draft

[PoC] ASoC: SOF: Move audio support as sof-client (support for multiple cards)#5815
ujfalusi wants to merge 83 commits into
thesofproject:topic/sof-devfrom
ujfalusi:peter/topic/multi_card_poc01

Conversation

@ujfalusi

Copy link
Copy Markdown
Collaborator

The PR contains my PoC work in progress patches to rewrite SOF and detach the sound from the DSP core driver as 'independent' sof-client.
The DSP management, booting, IPC, PM, ID allocation still remain in the core sdev level, but audio is mostly detached from sdev into snd_sof_instance, which now owns the topology loading, widgets, pipelines, PCMs, component and sound card.

booting with this branch there must not be any functional change, everything should work as before wit the exception that we have a new snd_sof_audio module.
I have introduced two Intel specific module parameters to snd_sof_intel_hda_generic:
multi_card - applicable in case of function topologies and it will create sound cards per main codecs
'split_hdmi` - this will separate only the HDMI card from the main card into it's own.

-- multi_card --

# cat /proc/asound/cards 
 0 [sofcs35l56     ]: sof-cs35l56 - sof-cs35l56
                      DellInc.-DellPro514P514260
 1 [sofhdmi        ]: sof-hdmi - sof-hdmi
                      DellInc.-DellPro514P514260
 2 [sofcs42l45     ]: sof-cs42l45 - sof-cs42l45
                      DellInc.-DellPro514P514260

# cat /proc/asound/pcm 
00-02: Speaker (*) :  : playback 1
00-35: Deepbuffer Speaker (*) :  : playback 1
01-03: HDMI1 (*) : HDMI 1 : playback 1
01-04: HDMI2 (*) : HDMI 2 : playback 1
01-05: HDMI3 (*) : HDMI 3 : playback 1
02-00: Jack Out (*) :  : playback 1
02-01: Jack In (*) :  : capture 1
02-04: Microphone (*) :  : capture 1
02-31: Deepbuffer Jack Out (*) :  : playback 1

# amixer -c0 info
Card sysdefault:0 'sofcs35l56'/'DellInc.-DellPro514P514260'
  Mixer name    : ''
  Components    : ' cfg-amp:2 iec61937-pcm:7,6,5 spk:cs35l56'
  Controls      : 32
  Simple ctrls  : 27

# amixer -c1 info
Card sysdefault:1 'sofhdmi'/'DellInc.-DellPro514P514260'
  Mixer name    : 'Intel Panther Lake HDMI'
  Components    : 'HDA:80862822,80860101,00100000 iec61937-pcm:5,4,3'
  Controls      : 21
  Simple ctrls  : 3

# amixer -c2 info
Card sysdefault:2 'sofcs42l45'/'DellInc.-DellPro514P514260'
  Mixer name    : ''
  Components    : ' cfg-amp:0 iec61937-pcm:7,6,5 hs:cs42l45 mic:cs42l45-dmic'
  Controls      : 36
  Simple ctrls  : 24

# ls -al /sys/bus/auxiliary/drivers/snd_sof_audio/
total 0
drwxr-xr-x.  2 root root    0 Jun 12 16:12 .
drwxr-xr-x. 10 root root    0 Jun 12 16:06 ..
--w-------.  1 root root 4096 Jun 12 16:12 bind
lrwxrwxrwx.  1 root root    0 Jun 12 16:12 module -> ../../../../module/snd_sof_audio
lrwxrwxrwx.  1 root root    0 Jun 12 16:12 snd_sof.audio.0 -> ../../../../devices/pci0000:00/0000:00:1f.3/snd_sof.audio.0
lrwxrwxrwx.  1 root root    0 Jun 12 16:12 snd_sof.audio.1 -> ../../../../devices/pci0000:00/0000:00:1f.3/snd_sof.audio.1
lrwxrwxrwx.  1 root root    0 Jun 12 16:12 snd_sof.audio.2 -> ../../../../devices/pci0000:00/0000:00:1f.3/snd_sof.audio.2
--w-------.  1 root root 4096 Jun 12 16:11 uevent
--w-------.  1 root root 4096 Jun 12 16:12 unbind

-- split_hdmi --

# cat /proc/asound/cards 
 0 [sofsoundwire   ]: sof-soundwire - sof-soundwire
                      DellInc.-DellPro514P514260
 1 [sofhdmi        ]: sof-hdmi - sof-hdmi
                      DellInc.-DellPro514P514260

# cat /proc/asound/pcm 
00-00: Jack Out (*) :  : playback 1
00-01: Jack In (*) :  : capture 1
00-02: Speaker (*) :  : playback 1
00-04: Microphone (*) :  : capture 1
00-31: Deepbuffer Jack Out (*) :  : playback 1
00-35: Deepbuffer Speaker (*) :  : playback 1
01-03: HDMI1 (*) : HDMI 1 : playback 1
01-04: HDMI2 (*) : HDMI 2 : playback 1
01-05: HDMI3 (*) : HDMI 3 : playback 1

# amixer -c0 info
Card sysdefault:0 'sofsoundwire'/'DellInc.-DellPro514P514260'
  Mixer name    : ''
  Components    : ' cfg-amp:2 iec61937-pcm:7,6,5 hs:cs42l45 mic:cs42l45-dmic spk:cs35l56'
  Controls      : 68
  Simple ctrls  : 51

# amixer -c1 info
Card sysdefault:1 'sofhdmi'/'DellInc.-DellPro514P514260'
  Mixer name    : 'Intel Panther Lake HDMI'
  Components    : 'HDA:80862822,80860101,00100000 iec61937-pcm:5,4,3'
  Controls      : 21
  Simple ctrls  : 3

In either case, you can unbind any card runtime w/o affecting the operation of the other cards, like in multi_card, one can remove the sof-hdmi card by:

echo -n "snd_sof.audio.2" > /sys/bus/auxiliary/drivers/snd_sof_audio/unbind 

The sof-hdmi card will always use the skl_hda machine driver with sof-hda-generic-idisp.tplg, so it's PCM list will be constant.

The split_hdmi works with IPC3 hda and sndw machines as well.

The topology name is moved from fw_profile of debugfs to audio.X/topology_name.

The branch should not case regression to @thesofproject/amd, @thesofproject/nxp or @thesofproject/mediatek, it works with nocodec and I2S machine drivers as well.

There are still layering violation around sdev accesses, some code still relies on sdev while it should not, NHLT blob is iffy a bit, but not much worst than what we have, but not ideal.

Concurrent card usage works fine as well, UIs will be broken for the multi_card but likely going to work with split_hdmi, only that we will miss the HDMI from profile (likely) as we keep the original card's name for the card from where the HDMI is removed.

ujfalusi and others added 30 commits June 10, 2026 16:28
If either tplg_ops->dai_config or widget_kcontrol_setup fail during widget
setup we would double decrement the use_count of the widget because the
sof_widget_free_unlocked() would be called twice, similarly the core_put
would be invoked twice as well.

Since the use_count and core_put() is handled within the widget_free
function we need to return without falling through the pipe_widget_free
label.

The fixes tag is picked to the last change around this part of the code
which is adequately old enough for backporting purposes.

Link: thesofproject/sof#10826
Fixes: 31ed8da ("ASoC: SOF: sof-audio: Modify logic for enabling/disabling topology cores")
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Protect IPC RX and FW state handler list unregister/dispatch paths with
client_event_handler_mutex to match list registration and locking
comments.

Fixes: 5c19da3 ("ASoC: SOF: Use guard()/scoped_guard() for mutex locks where it makes sense")
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
The IPC4 definition of pipeline prioity is:
0 - highest priority
7 - lowest priority

RUNNING should use ascending order (highest priority first), otherwise
descending order (lowest priority first) should be used.

Fixes: 4df7d6a ("ASoC: SOF: IPC4: sort pipeline based on priority")
Cc: stable@vger.kernel.org
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
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 <peter.ujfalusi@linux.intel.com>
…election

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 <peter.ujfalusi@linux.intel.com>
Couple the Host and Link DMA when the stream is put.
When the hda_dsp_stream_hw_free() is called the Link DMA might be still
locked and we would leave the DMAs decoupled.
hda_dsp_stream_hw_free() is not called for code loader use or in case of
probes or trace use for example.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Programing SoundWire registers are required for stream process. And we can
only program then after the peripheral is attached and initialized.
Currently, we wait initialization_complete in codec resume. To reduce
the resume time, we will remove the waiting from codec resume in the
follow up commits.

Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com>
…t of a module

Based on the input and output formats we can evaluate what param might be
changed by the module instance.
If there is a difference between the input rate/channels/format and the
output  rate/channels/format it means that the module can change one or
multiple of the params.

Store this information during init for later use.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
The process module can change different parameters in the audio path and
this change has to be properly evaluated and applied.

In case of playback we are converting from multiple input formats to a
single format (or just passing through without change), the output format
lookup must be based on the input format.

In case of capture, we are converting from a single input format to a
format which is to be passed to the FE, we need to use the input parameters
and the FE parameters to be able to find the correct format:
for those parameters that are modified by the module instance we need to
use the FE parameter while for the rest we use the input parameters.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
if the message times out and it was delayed, remove it as well.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
If the module in path has been already prepared on a branch type of
topology, where the branching happens downstream:
A1--> A2 ---> B1 --> B2 ... B-branch
          |-> C1 --> C2 ... C-branch

In this case if B-branch is started then A1/A2 is prepared, but when
C-branch starts we still need to refine the parameters up to C1 to arrive
with a correct params to configure C1.

This branching can happen with copiers process modules.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
…mt init

Even if there is a single format supported by the module on it's input, it
must be validated that it is matching with the reference parameters.

The DAI copier's DAI side reference is adjusted to the formats it supports,
but modules deep within the path might have incorrect configuration in
topology (single format which is not matching with the previous module's
output for example). This should be reported as errors and not silently
accepted.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
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 <peter.ujfalusi@linux.intel.com>
…_run()

It is expected that the DSP is in power down state when the firmware boot
is attempted.
If the DSP for any reason was left powered up then the DSP boot will
fail since the ROM boot sequence might not be able to run.

Make sure that the DSP is off before proceeding to boot it up.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
…_run()

It is expected that the DSP is in power down state when the firmware boot
is attempted.
If the DSP for any reason was left powered up then the DSP boot will
fail since the ROM boot sequence might not be able to run.

Make sure that the DSP is off before proceeding to boot it up.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
The ipc_control_data buffer is allocated as kzalloc(max_size), where
max_size covers the entire struct sof_ipc_ctrl_data including its
flexible array payload. However, the bounds checks in bytes_ext_put
and _bytes_ext_get compared user data lengths against max_size
directly, ignoring that cdata->data sits at an offset of
sizeof(struct sof_ipc_ctrl_data) bytes into the allocation.

This allowed writing up to sizeof(struct sof_ipc_ctrl_data) bytes past
the end of the heap buffer from unprivileged userspace via the ALSA TLV
kcontrol interface, and similarly allowed over-reading adjacent heap
data on the get path.

Fix all bounds checks to subtract sizeof(*cdata) from max_size so they
reflect the actual space available at the cdata->data offset. Also fix
the error-path restore in bytes_ext_put which wrote to cdata->data
instead of cdata, causing the same overflow. Additionally verify that
the TLV payload length is large enough to cover what the ABI header's
size field claims, preventing stale data from being sent to the DSP.

Fixes: 67ec2a0 ("ASoC: SOF: Add bytes_ext control IPC ops for IPC3")
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
On Intel ACE2+ platforms, each SoundWire link uses a single pair of
PDIs (PDI0 for TX, PDI1 for RX) and associated DMA resources for
Bulk Port Transfers. When two codecs on the same link initiate BRA
transfers concurrently (e.g. during firmware download), the second
sdw_bpt_send_async() call races with the first:

  - intel_ace2x_bpt_open_stream() has a TOCTOU on the bpt_stream
    pointer: both callers see it as NULL and proceed
  - The second open overwrites the first's stream allocation, leaking
    the original sdw_stream_runtime
  - PCMSyCM mappings for the first transfer get overwritten, causing
    chain DMA to operate with wrong PDI assignments
  - IOC timeouts, use-after-free, and double-free follow on close

Add a per-bus bpt_lock mutex held across the send_async/wait span to
serialize BPT transfers. The lock is acquired in sdw_bpt_send_async()
before calling the master ops and released in sdw_bpt_wait() after the
transfer completes. On send_async failure, the lock is released
immediately.

Cross-link transfers (different sdw_bus instances) remain concurrent
since each bus has its own lock.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
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 <peter.ujfalusi@linux.intel.com>
Take a module reference in snd_compr_open() and release it in
snd_compr_free(). This pins the card driver module for the
lifetime of an open compress stream and prevents card removal
while the stream file is still in use.

Adjust the open() cleanup paths to drop the added module
reference only when it was acquired, and keep release ordering
safe by dropping the module reference before freeing stream
data.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Track open compressed streams per device so disconnect can stop
active streams and wake waiters before snd_unregister_device().

This aligns compressed stream teardown with PCM disconnect behavior
and prevents active userspace streams from running into unregister
races.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
With DPCM when compr is used on FE side, the BE is still running as
'normal' PCM.
It is expected that the be_substream->runtime on the BE is not NULL, for
example some codec drivers expect to have the substream->runtime valid, to
store configuration for example.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
… PCMs

The FE-BE trigger sequence should be dynamic, similarly how soc-pcm.c
dpcm_fe_dai_do_trigger() does it.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
If the last trigger that the compr device received is a DRAIN then the
DPCM is left in running state (no stop trigger is sent).
Before we execute the free we need to send a STOP trigger to make sure that
both BE and FE is in expected state and prepared for closing.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
In preparation for adding support for compressed offload support for
IPC4, rename the current compress implementation with the IPC3 prefix.
Introduce a new field in struct sof_ipc_pcm_ops to save the
IPC-specific compressed ops pointer. This should be set when the
component driver ops are assigned during SOF device probe. Expose a couple
of common functions that will be used by both IPC-specific implementations
and rename the compress.c file to ipc3-compress.c

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Serialize trigger/free with pipeline_state_mutex and validate
pipeline entries before use.

Also clear pipeline_list->count when freeing lists to avoid stale
entries during concurrent teardown.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
During widget FREE traversal, SOF walks DAPM sink paths recursively while
widgets and paths can be torn down. This can lead to stale pointer usage in
sof_free_widgets_in_path() when path entries disappear during recursion.

Harden the FREE path by:
- validating scheduler/pipeline pointers before recursive free
- using a safe DAPM path iterator for sink traversal
- carrying a stable widget-list snapshot through recursion
- skipping NULL sink edges during traversal

The safe traversal can leave path->walking set on surviving edges after FREE,
which may short-circuit later DAPM walks and break consecutive playback
open/stop cycles. Reset walking flags for widgets in the current DAPM list
after FREE walks (including error paths) to keep subsequent traversals clean.

This keeps teardown robust for module-remove race scenarios while preserving
normal consecutive playback behavior.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
These are common functions that will also be needed for the IPC4
compressed support.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
In order to reuse the pipeline triggering logic for compressed support
with IPC4, modify the signature of the trigger and hw_free PCM IPC ops
so that they can be reused.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
ujfalusi added 30 commits June 12, 2026 15:58
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 <peter.ujfalusi@linux.intel.com>
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 <peter.ujfalusi@linux.intel.com>
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 <peter.ujfalusi@linux.intel.com>
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.
Functions that have a snd_soc_component or snd_sof_widget available
use snd_sof_component_get_audio_instance(), while global operations
iterate all instances in the audio_instance_list.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
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.

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 <peter.ujfalusi@linux.intel.com>
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 <peter.ujfalusi@linux.intel.com>
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 <peter.ujfalusi@linux.intel.com>
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 <peter.ujfalusi@linux.intel.com>
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 <peter.ujfalusi@linux.intel.com>
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 <peter.ujfalusi@linux.intel.com>
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 <peter.ujfalusi@linux.intel.com>
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 <peter.ujfalusi@linux.intel.com>
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 <peter.ujfalusi@linux.intel.com>
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 <peter.ujfalusi@linux.intel.com>
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 <peter.ujfalusi@linux.intel.com>
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 <peter.ujfalusi@linux.intel.com>
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 <peter.ujfalusi@linux.intel.com>
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 <peter.ujfalusi@linux.intel.com>
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 <peter.ujfalusi@linux.intel.com>
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 <peter.ujfalusi@linux.intel.com>
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 <peter.ujfalusi@linux.intel.com>
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 <peter.ujfalusi@linux.intel.com>
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 <peter.ujfalusi@linux.intel.com>
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 <peter.ujfalusi@linux.intel.com>
Add card_name and dai_type_mask fields to snd_soc_acpi_mach_params
to allow per-instance card naming and DAI type filtering.

Use card_name in mc_probe() to set the card name when provided.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
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 <peter.ujfalusi@linux.intel.com>
…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 <peter.ujfalusi@linux.intel.com>
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 <peter.ujfalusi@linux.intel.com>
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 <peter.ujfalusi@linux.intel.com>
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 <peter.ujfalusi@linux.intel.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants