@@ -61,6 +61,13 @@ config SND_SOC_SOF_DEBUG_PROBES
Say Y if you want to enable probes.
If unsure, select "N".
+config SND_SOC_SOF_CLIENT
+ tristate
+ select AUXILIARY_BUS
+ help
+ This option is not user-selectable but automagically handled by
+ 'select' statements at a higher level.
+
config SND_SOC_SOF_DEVELOPER_SUPPORT
bool "SOF developer options support"
depends on EXPERT && SND_SOC_SOF
@@ -2,6 +2,7 @@
snd-sof-objs := 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
+snd-sof-$(CONFIG_SND_SOC_SOF_CLIENT) += sof-client.o
snd-sof-$(CONFIG_SND_SOC_SOF_DEBUG_PROBES) += sof-probes.o
snd-sof-$(CONFIG_SND_SOC_SOF_COMPRESS) += compress.o
@@ -122,6 +122,27 @@ void sof_print_oops_and_stack(struct snd_sof_dev *sdev, const char *level,
}
EXPORT_SYMBOL(sof_print_oops_and_stack);
+/* Helper to manage DSP state */
+void sof_set_fw_state(struct snd_sof_dev *sdev, enum sof_fw_state new_state)
+{
+ if (sdev->fw_state == new_state)
+ return;
+
+ dev_dbg(sdev->dev, "fw_state change: %d -> %d\n", sdev->fw_state, new_state);
+ sdev->fw_state = new_state;
+
+ switch (new_state) {
+ case SOF_FW_BOOT_NOT_STARTED:
+ case SOF_FW_BOOT_COMPLETE:
+ case SOF_FW_CRASHED:
+ sof_client_fw_state_dispatcher(sdev);
+ fallthrough;
+ default:
+ break;
+ }
+}
+EXPORT_SYMBOL(sof_set_fw_state);
+
/*
* FW Boot State Transition Diagram
*
@@ -266,6 +287,12 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
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;
+ }
+
/*
* Some platforms in SOF, ex: BYT, may not have their platform PM
* callbacks set. Increment the usage count so as to
@@ -281,6 +308,8 @@ static int sof_probe_continue(struct snd_sof_dev *sdev)
return 0;
+sof_machine_err:
+ snd_sof_machine_unregister(sdev, plat_data);
fw_trace_err:
snd_sof_free_trace(sdev);
fw_run_err:
@@ -329,7 +358,6 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
sdev->pdata = plat_data;
sdev->first_boot = true;
- sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED);
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
sdev->extractor_stream_tag = SOF_PROBE_INVALID_NODE_ID;
#endif
@@ -350,9 +378,14 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
INIT_LIST_HEAD(&sdev->widget_list);
INIT_LIST_HEAD(&sdev->dai_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);
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);
/* set default timeouts if none provided */
if (plat_data->desc->ipc_timeout == 0)
@@ -364,6 +397,8 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
else
sdev->boot_timeout = plat_data->desc->boot_timeout;
+ sof_set_fw_state(sdev, SOF_FW_BOOT_NOT_STARTED);
+
if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) {
INIT_WORK(&sdev->probe_work, sof_probe_work);
schedule_work(&sdev->probe_work);
@@ -391,6 +426,12 @@ int snd_sof_device_remove(struct device *dev)
if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE))
cancel_work_sync(&sdev->probe_work);
+ /*
+ * Unregister any registered client device first before IPC and debugfs
+ * to allow client drivers to be removed cleanly
+ */
+ sof_unregister_clients(sdev);
+
/*
* Unregister machine driver. This will unbind the snd_card which
* will remove the component driver and unload the topology
@@ -557,22 +557,25 @@ void snd_sof_ipc_msgs_rx(struct snd_sof_dev *sdev)
break;
}
- if (rx_callback) {
- /* read the full message as we have rx handler for it */
- msg_buf = kmalloc(hdr.size, GFP_KERNEL);
- if (!msg_buf)
- return;
+ /* read the full message */
+ msg_buf = kmalloc(hdr.size, GFP_KERNEL);
+ if (!msg_buf)
+ return;
- err = snd_sof_ipc_msg_data(sdev, NULL, msg_buf, hdr.size);
- if (err < 0)
- dev_err(sdev->dev, "%s: Failed to read message: %d\n",
- __func__, err);
- else
+ err = snd_sof_ipc_msg_data(sdev, NULL, msg_buf, hdr.size);
+ if (err < 0) {
+ dev_err(sdev->dev, "%s: Failed to read message: %d\n", __func__, err);
+ } else {
+ /* Call local handler for the message */
+ if (rx_callback)
rx_callback(sdev, msg_buf);
- kfree(msg_buf);
+ /* Notify registered clients */
+ sof_client_ipc_rx_dispatcher(sdev, msg_buf);
}
+ kfree(msg_buf);
+
ipc_log_header(sdev->dev, "ipc rx done", hdr.cmd);
}
EXPORT_SYMBOL(snd_sof_ipc_msgs_rx);
new file mode 100644
@@ -0,0 +1,340 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2022 Intel Corporation. All rights reserved.
+//
+// Authors: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
+// Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
+//
+
+#include <linux/debugfs.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include "ops.h"
+#include "sof-client.h"
+#include "sof-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;
+};
+
+static void sof_client_auxdev_release(struct device *dev)
+{
+ struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
+ struct sof_client_dev *cdev = auxiliary_dev_to_sof_client_dev(auxdev);
+
+ kfree(cdev->auxdev.dev.platform_data);
+ kfree(cdev);
+}
+
+static int sof_client_dev_add_data(struct sof_client_dev *cdev, const void *data,
+ size_t size)
+{
+ void *d = NULL;
+
+ if (data) {
+ d = kmemdup(data, size, GFP_KERNEL);
+ if (!d)
+ return -ENOMEM;
+ }
+
+ cdev->auxdev.dev.platform_data = d;
+ return 0;
+}
+
+int sof_register_clients(struct snd_sof_dev *sdev)
+{
+ if (sof_ops(sdev) && sof_ops(sdev)->register_ipc_clients)
+ return sof_ops(sdev)->register_ipc_clients(sdev);
+
+ return 0;
+}
+
+void sof_unregister_clients(struct snd_sof_dev *sdev)
+{
+ if (sof_ops(sdev) && sof_ops(sdev)->unregister_ipc_clients)
+ sof_ops(sdev)->unregister_ipc_clients(sdev);
+}
+
+int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id,
+ const void *data, size_t size)
+{
+ struct auxiliary_device *auxdev;
+ struct sof_client_dev *cdev;
+ int ret;
+
+ cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
+ if (!cdev)
+ return -ENOMEM;
+
+ cdev->sdev = sdev;
+ auxdev = &cdev->auxdev;
+ auxdev->name = name;
+ auxdev->dev.parent = sdev->dev;
+ auxdev->dev.release = sof_client_auxdev_release;
+ auxdev->id = id;
+
+ ret = sof_client_dev_add_data(cdev, data, size);
+ if (ret < 0)
+ goto err_dev_add_data;
+
+ ret = auxiliary_device_init(auxdev);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to initialize client dev %s.%d\n", name, id);
+ goto err_dev_init;
+ }
+
+ ret = auxiliary_device_add(&cdev->auxdev);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to add client dev %s.%d\n", name, id);
+ /*
+ * sof_client_auxdev_release() will be invoked to free up memory
+ * allocations through put_device()
+ */
+ auxiliary_device_uninit(&cdev->auxdev);
+ return ret;
+ }
+
+ /* add to list of SOF client devices */
+ mutex_lock(&sdev->ipc_client_mutex);
+ list_add(&cdev->list, &sdev->ipc_client_list);
+ mutex_unlock(&sdev->ipc_client_mutex);
+
+ return 0;
+
+err_dev_init:
+ kfree(cdev->auxdev.dev.platform_data);
+
+err_dev_add_data:
+ kfree(cdev);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_dev_register, SND_SOC_SOF_CLIENT);
+
+void sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 id)
+{
+ struct sof_client_dev *cdev;
+
+ mutex_lock(&sdev->ipc_client_mutex);
+
+ /*
+ * sof_client_auxdev_release() will be invoked to free up memory
+ * allocations through put_device()
+ */
+ list_for_each_entry(cdev, &sdev->ipc_client_list, list) {
+ if (!strcmp(cdev->auxdev.name, name) && cdev->auxdev.id == id) {
+ list_del(&cdev->list);
+ auxiliary_device_delete(&cdev->auxdev);
+ auxiliary_device_uninit(&cdev->auxdev);
+ break;
+ }
+ }
+
+ mutex_unlock(&sdev->ipc_client_mutex);
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_dev_unregister, SND_SOC_SOF_CLIENT);
+
+int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg,
+ void *reply_data, size_t reply_bytes)
+{
+ struct sof_ipc_cmd_hdr *hdr = ipc_msg;
+
+ return sof_ipc_tx_message(cdev->sdev->ipc, hdr->cmd, ipc_msg, hdr->size,
+ reply_data, reply_bytes);
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_ipc_tx_message, SND_SOC_SOF_CLIENT);
+
+struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev)
+{
+ return cdev->sdev->debugfs_root;
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_get_debugfs_root, SND_SOC_SOF_CLIENT);
+
+/* DMA buffer allocation in client drivers must use the core SOF device */
+struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev)
+{
+ return cdev->sdev->dev;
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_get_dma_dev, SND_SOC_SOF_CLIENT);
+
+const struct sof_ipc_fw_version *sof_client_get_fw_version(struct sof_client_dev *cdev)
+{
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+
+ return &sdev->fw_ready.version;
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_get_fw_version, SND_SOC_SOF_CLIENT);
+
+/* module refcount management of SOF core */
+int sof_client_core_module_get(struct sof_client_dev *cdev)
+{
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+
+ if (!try_module_get(sdev->dev->driver->owner))
+ return -ENODEV;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_core_module_get, SND_SOC_SOF_CLIENT);
+
+void sof_client_core_module_put(struct sof_client_dev *cdev)
+{
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+
+ module_put(sdev->dev->driver->owner);
+}
+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_cmd_hdr *hdr = msg_buf;
+ u32 msg_type = hdr->cmd & SOF_GLB_TYPE_MASK;
+ struct sof_ipc_event_entry *event;
+
+ mutex_lock(&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);
+ }
+
+ mutex_unlock(&sdev->client_event_handler_mutex);
+}
+
+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 || !(ipc_msg_type & SOF_GLB_TYPE_MASK))
+ return -EINVAL;
+
+ event = kmalloc(sizeof(*event), GFP_KERNEL);
+ if (!event)
+ return -ENOMEM;
+
+ event->ipc_msg_type = ipc_msg_type;
+ event->cdev = cdev;
+ event->callback = callback;
+
+ /* add to list of SOF client devices */
+ mutex_lock(&sdev->client_event_handler_mutex);
+ list_add(&event->list, &sdev->ipc_rx_handler_list);
+ mutex_unlock(&sdev->client_event_handler_mutex);
+
+ 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;
+
+ mutex_lock(&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) {
+ list_del(&event->list);
+ kfree(event);
+ break;
+ }
+ }
+
+ mutex_unlock(&sdev->client_event_handler_mutex);
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_unregister_ipc_rx_handler, SND_SOC_SOF_CLIENT);
+
+/*DSP state notification and query */
+void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev)
+{
+ struct sof_state_event_entry *event;
+
+ mutex_lock(&sdev->client_event_handler_mutex);
+
+ list_for_each_entry(event, &sdev->fw_state_handler_list, list)
+ event->callback(event->cdev, sdev->fw_state);
+
+ mutex_unlock(&sdev->client_event_handler_mutex);
+}
+
+int sof_client_register_fw_state_handler(struct sof_client_dev *cdev,
+ sof_client_fw_state_callback callback)
+{
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+ struct sof_state_event_entry *event;
+
+ if (!callback)
+ return -EINVAL;
+
+ event = kmalloc(sizeof(*event), GFP_KERNEL);
+ if (!event)
+ return -ENOMEM;
+
+ event->cdev = cdev;
+ event->callback = callback;
+
+ /* add to list of SOF client devices */
+ mutex_lock(&sdev->client_event_handler_mutex);
+ list_add(&event->list, &sdev->fw_state_handler_list);
+ mutex_unlock(&sdev->client_event_handler_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_register_fw_state_handler, SND_SOC_SOF_CLIENT);
+
+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;
+
+ mutex_lock(&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;
+ }
+ }
+
+ mutex_unlock(&sdev->client_event_handler_mutex);
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_unregister_fw_state_handler, SND_SOC_SOF_CLIENT);
+
+enum sof_fw_state sof_client_get_fw_state(struct sof_client_dev *cdev)
+{
+ struct snd_sof_dev *sdev = sof_client_dev_to_sof_dev(cdev);
+
+ return sdev->fw_state;
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_get_fw_state, SND_SOC_SOF_CLIENT);
new file mode 100644
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __SOC_SOF_CLIENT_H
+#define __SOC_SOF_CLIENT_H
+
+#include <linux/auxiliary_bus.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <sound/sof.h>
+
+struct sof_ipc_fw_version;
+struct sof_ipc_cmd_hdr;
+struct snd_sof_dev;
+struct dentry;
+
+/**
+ * struct sof_client_dev - SOF client device
+ * @auxdev: auxiliary device
+ * @sdev: pointer to SOF core device struct
+ * @list: item in SOF core client dev list
+ * @data: device specific data
+ */
+struct sof_client_dev {
+ struct auxiliary_device auxdev;
+ struct snd_sof_dev *sdev;
+ struct list_head list;
+ void *data;
+};
+
+#define sof_client_dev_to_sof_dev(cdev) ((cdev)->sdev)
+
+#define auxiliary_dev_to_sof_client_dev(auxiliary_dev) \
+ container_of(auxiliary_dev, struct sof_client_dev, auxdev)
+
+#define dev_to_sof_client_dev(dev) \
+ container_of(to_auxiliary_dev(dev), struct sof_client_dev, auxdev)
+
+int sof_client_ipc_tx_message(struct sof_client_dev *cdev, void *ipc_msg,
+ void *reply_data, size_t reply_bytes);
+
+struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev);
+struct device *sof_client_get_dma_dev(struct sof_client_dev *cdev);
+const struct sof_ipc_fw_version *sof_client_get_fw_version(struct sof_client_dev *cdev);
+
+/* module refcount management of SOF core */
+int sof_client_core_module_get(struct sof_client_dev *cdev);
+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 */
+typedef void (*sof_client_fw_state_callback)(struct sof_client_dev *cdev,
+ enum sof_fw_state state);
+
+int sof_client_register_fw_state_handler(struct sof_client_dev *cdev,
+ sof_client_fw_state_callback callback);
+void sof_client_unregister_fw_state_handler(struct sof_client_dev *cdev);
+enum sof_fw_state sof_client_get_fw_state(struct sof_client_dev *cdev);
+
+#endif /* __SOC_SOF_CLIENT_H */
@@ -292,6 +292,10 @@ struct snd_sof_dsp_ops {
void (*set_mach_params)(struct snd_soc_acpi_mach *mach,
struct snd_sof_dev *sdev); /* optional */
+ /* IPC client ops */
+ int (*register_ipc_clients)(struct snd_sof_dev *sdev); /* optional */
+ void (*unregister_ipc_clients)(struct snd_sof_dev *sdev); /* optional */
+
/* DAI ops */
struct snd_soc_dai_driver *drv;
int num_drv;
@@ -479,6 +483,30 @@ struct snd_sof_dev {
*/
int dsp_core_ref_count[SOF_MAX_DSP_NUM_CORES];
+ /*
+ * Used to keep track of registered IPC client devices so that they can
+ * be removed when the parent SOF module is removed.
+ */
+ struct list_head ipc_client_list;
+
+ /* 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;
+
+ /* to protect the ipc_rx_handler_list and dsp_state_handler_list list */
+ struct mutex client_event_handler_mutex;
+
void *private; /* core does not touch this */
};
@@ -582,15 +610,7 @@ extern const struct dsp_arch_ops sof_xtensa_arch_ops;
/*
* Firmware state tracking
*/
-static inline void sof_set_fw_state(struct snd_sof_dev *sdev,
- enum sof_fw_state new_state)
-{
- if (sdev->fw_state == new_state)
- return;
-
- dev_dbg(sdev->dev, "fw_state change: %d -> %d\n", sdev->fw_state, new_state);
- sdev->fw_state = new_state;
-}
+void sof_set_fw_state(struct snd_sof_dev *sdev, enum sof_fw_state new_state);
/*
* Utilities
@@ -623,4 +643,44 @@ int sof_stream_pcm_close(struct snd_sof_dev *sdev,
struct snd_pcm_substream *substream);
int sof_machine_check(struct snd_sof_dev *sdev);
+
+/* SOF client support */
+#if IS_ENABLED(CONFIG_SND_SOC_SOF_CLIENT)
+int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, u32 id,
+ const void *data, size_t size);
+void sof_client_dev_unregister(struct snd_sof_dev *sdev, const char *name, u32 id);
+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);
+#else /* CONFIG_SND_SOC_SOF_CLIENT */
+static inline int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name,
+ u32 id, const void *data, size_t size)
+{
+ return 0;
+}
+
+static inline void sof_client_dev_unregister(struct snd_sof_dev *sdev,
+ const char *name, u32 id)
+{
+}
+
+static inline int sof_register_clients(struct snd_sof_dev *sdev)
+{
+ return 0;
+}
+
+static inline void sof_unregister_clients(struct snd_sof_dev *sdev)
+{
+}
+
+static inline void sof_client_ipc_rx_dispatcher(struct snd_sof_dev *sdev, void *msg_buf)
+{
+}
+
+static inline void sof_client_fw_state_dispatcher(struct snd_sof_dev *sdev)
+{
+}
+#endif /* CONFIG_SND_SOC_SOF_CLIENT */
+
#endif