@@ -50,6 +50,24 @@ 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 ANCILLARY_BUS
+ help
+ This option is not user-selectable but automagically handled by
+ 'select' statements at a higher level
+
+config SND_SOC_SOF_CLIENT_SUPPORT
+ bool "SOF enable clients"
+ depends on SND_SOC_SOF
+ help
+ This adds support for ancillary client devices to separate out the debug
+ functionality for IPC tests, probes etc. into separate devices. This
+ option would also allow adding client devices based on DSP FW
+ capabilities and ACPI/OF device information.
+ Say Y if you want to enable clients with SOF.
+ If unsure select "N".
+
config SND_SOC_SOF_DEVELOPER_SUPPORT
bool "SOF developer options support"
depends on EXPERT
@@ -186,6 +204,7 @@ endif ## SND_SOC_SOF_DEVELOPER_SUPPORT
config SND_SOC_SOF
tristate
+ select SND_SOC_SOF_CLIENT if SND_SOC_SOF_CLIENT_SUPPORT
select SND_SOC_TOPOLOGY
select SND_SOC_SOF_NOCODEC if SND_SOC_SOF_NOCODEC_SUPPORT
help
@@ -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 utils.o sof-audio.o
+snd-sof-client-objs := sof-client.o
snd-sof-$(CONFIG_SND_SOC_SOF_DEBUG_PROBES) += probe.o compress.o
snd-sof-pci-objs := sof-pci-dev.o
@@ -18,6 +19,8 @@ obj-$(CONFIG_SND_SOC_SOF_ACPI) += snd-sof-acpi.o
obj-$(CONFIG_SND_SOC_SOF_OF) += snd-sof-of.o
obj-$(CONFIG_SND_SOC_SOF_PCI) += snd-sof-pci.o
+obj-$(CONFIG_SND_SOC_SOF_CLIENT) += snd-sof-client.o
+
obj-$(CONFIG_SND_SOC_SOF_INTEL_TOPLEVEL) += intel/
obj-$(CONFIG_SND_SOC_SOF_IMX_TOPLEVEL) += imx/
obj-$(CONFIG_SND_SOC_SOF_XTENSA) += xtensa/
@@ -314,8 +314,10 @@ 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->client_list);
spin_lock_init(&sdev->ipc_lock);
spin_lock_init(&sdev->hw_lock);
+ mutex_init(&sdev->client_mutex);
if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE))
INIT_WORK(&sdev->probe_work, sof_probe_work);
new file mode 100644
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2020 Intel Corporation. All rights reserved.
+//
+// Author: Ranjani Sridharan <ranjani.sridharan@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 "sof-client.h"
+#include "sof-priv.h"
+
+static void sof_client_ancildev_release(struct device *dev)
+{
+ struct ancillary_device *ancildev = to_ancillary_dev(dev);
+ struct sof_client_dev *cdev = ancillary_dev_to_sof_client_dev(ancildev);
+
+ ida_simple_remove(cdev->client_ida, ancildev->id);
+ kfree(cdev);
+}
+
+static struct sof_client_dev *sof_client_dev_alloc(struct snd_sof_dev *sdev, const char *name,
+ struct ida *client_ida)
+{
+ struct sof_client_dev *cdev;
+ struct ancillary_device *ancildev;
+ int ret;
+
+ cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
+ if (!cdev)
+ return NULL;
+
+ cdev->sdev = sdev;
+ cdev->client_ida = client_ida;
+ ancildev = &cdev->ancildev;
+ ancildev->name = name;
+ ancildev->dev.parent = sdev->dev;
+ ancildev->dev.release = sof_client_ancildev_release;
+
+ ancildev->id = ida_alloc(client_ida, GFP_KERNEL);
+ if (ancildev->id < 0) {
+ dev_err(sdev->dev, "error: get IDA idx for ancillary device %s failed\n", name);
+ ret = ancildev->id;
+ goto err_free;
+ }
+
+ ret = ancillary_device_initialize(ancildev);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed to initialize client dev %s\n", name);
+ ida_simple_remove(client_ida, ancildev->id);
+ goto err_free;
+ }
+
+ return cdev;
+
+err_free:
+ kfree(cdev);
+ return NULL;
+}
+
+int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, struct ida *client_ida)
+{
+ struct sof_client_dev *cdev;
+ int ret;
+
+ cdev = sof_client_dev_alloc(sdev, name, client_ida);
+ if (!cdev)
+ return -ENODEV;
+
+ ret = ancillary_device_add(&cdev->ancildev);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: failed to add client dev %s\n", name);
+ put_device(&cdev->ancildev.dev);
+ return ret;
+ }
+
+ /* add to list of SOF client devices */
+ mutex_lock(&sdev->client_mutex);
+ list_add(&cdev->list, &sdev->client_list);
+ mutex_unlock(&sdev->client_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_dev_register, SND_SOC_SOF_CLIENT);
+
+void sof_client_dev_unregister(struct sof_client_dev *cdev)
+{
+ struct snd_sof_dev *sdev = cdev->sdev;
+
+ /* remove from list of SOF client devices */
+ mutex_lock(&sdev->client_mutex);
+ list_del(&cdev->list);
+ mutex_unlock(&sdev->client_mutex);
+
+ ancillary_device_unregister(&cdev->ancildev);
+}
+EXPORT_SYMBOL_NS_GPL(sof_client_dev_unregister, SND_SOC_SOF_CLIENT);
+
+int sof_client_ipc_tx_message(struct sof_client_dev *cdev, u32 header, void *msg_data,
+ size_t msg_bytes, void *reply_data, size_t reply_bytes)
+{
+ return sof_ipc_tx_message(cdev->sdev->ipc, header, msg_data, msg_bytes,
+ 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);
+
+MODULE_LICENSE("GPL");
new file mode 100644
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: (GPL-2.0-only) */
+
+#ifndef __SOUND_SOC_SOF_CLIENT_H
+#define __SOUND_SOC_SOF_CLIENT_H
+
+#include <linux/ancillary_bus.h>
+
+#define SOF_CLIENT_PROBE_TIMEOUT_MS 2000
+
+struct snd_sof_dev;
+
+/* SOF client device */
+struct sof_client_dev {
+ struct ancillary_device ancildev;
+ struct snd_sof_dev *sdev;
+ struct list_head list; /* item in SOF core client dev list */
+ struct ida *client_ida;
+ void *data;
+};
+
+/* client-specific ops, all optional */
+struct sof_client_ops {
+ int (*client_ipc_rx)(struct sof_client_dev *cdev, u32 msg_cmd);
+};
+
+struct sof_client_drv {
+ const char *name;
+ const struct sof_client_ops ops;
+ struct ancillary_driver ancillary_drv;
+};
+
+#define ancillary_dev_to_sof_client_dev(ancillary_dev) \
+ container_of(ancillary_dev, struct sof_client_dev, ancildev)
+
+static inline int sof_client_drv_register(struct sof_client_drv *drv)
+{
+ return ancillary_driver_register(&drv->ancillary_drv);
+}
+
+static inline void sof_client_drv_unregister(struct sof_client_drv *drv)
+{
+ ancillary_driver_unregister(&drv->ancillary_drv);
+}
+
+int sof_client_dev_register(struct snd_sof_dev *sdev, const char *name, struct ida *client_ida);
+void sof_client_dev_unregister(struct sof_client_dev *cdev);
+
+int sof_client_ipc_tx_message(struct sof_client_dev *cdev, u32 header, void *msg_data,
+ size_t msg_bytes, void *reply_data, size_t reply_bytes);
+
+struct dentry *sof_client_get_debugfs_root(struct sof_client_dev *cdev);
+
+/**
+ * module_sof_client_driver() - Helper macro for registering an SOF Client
+ * driver
+ * @__sof_client_driver: SOF client driver struct
+ *
+ * Helper macro for SOF client drivers which do not do anything special in
+ * module init/exit. This eliminates a lot of boilerplate. Each module may only
+ * use this macro once, and calling it replaces module_init() and module_exit()
+ */
+#define module_sof_client_driver(__sof_client_driver) \
+ module_driver(__sof_client_driver, sof_client_drv_register, sof_client_drv_unregister)
+
+#endif
@@ -438,6 +438,12 @@ struct snd_sof_dev {
bool msi_enabled;
+ /* list of client devices */
+ struct list_head client_list;
+
+ /* mutex to protect client list */
+ struct mutex client_mutex;
+
void *private; /* core does not touch this */
};