@@ -10,6 +10,7 @@
#include <linux/irqdomain.h>
#include <linux/mod_devicetable.h>
#include <linux/bitfield.h>
+#include <sound/sdca.h>
struct sdw_bus;
struct sdw_slave;
@@ -663,6 +664,7 @@ struct sdw_slave_ops {
* @is_mockup_device: status flag used to squelch errors in the command/control
* protocol for SoundWire mockup devices
* @sdw_dev_lock: mutex used to protect callbacks/remove races
+ * @sdca_data: structure containing all device data for SDCA helpers
*/
struct sdw_slave {
struct sdw_slave_id id;
@@ -686,6 +688,7 @@ struct sdw_slave {
bool first_interrupt_done;
bool is_mockup_device;
struct mutex sdw_dev_lock; /* protect callbacks/remove races */
+ struct sdca_device_data sdca_data;
};
#define dev_to_sdw_dev(_dev) container_of(_dev, struct sdw_slave, dev)
new file mode 100644
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+/*
+ * The MIPI SDCA specification is available for public downloads at
+ * https://www.mipi.org/mipi-sdca-v1-0-download
+ *
+ * Copyright(c) 2024 Intel Corporation
+ */
+
+#ifndef __SDCA_H__
+#define __SDCA_H__
+
+struct sdw_slave;
+
+#define SDCA_MAX_FUNCTION_COUNT 8
+
+/**
+ * sdca_device_desc - short descriptor for an SDCA Function
+ * @adr: ACPI address (used for SDCA register access)
+ * @type: Function topology type
+ * @name: human-readable string
+ */
+struct sdca_function_desc {
+ u64 adr;
+ u32 type;
+ const char *name;
+};
+
+/**
+ * sdca_device_data - structure containing all SDCA related information
+ * @sdca_interface_revision: value read from _DSD property, mainly to check
+ * for changes between silicon versions
+ * @num_functions: total number of supported SDCA functions. Invalid/unsupported
+ * functions will be skipped.
+ * @sdca_func: array of function descriptors
+ */
+struct sdca_device_data {
+ u32 interface_revision;
+ int num_functions;
+ struct sdca_function_desc sdca_func[SDCA_MAX_FUNCTION_COUNT];
+};
+
+#if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SND_SOC_SDCA)
+
+void sdca_lookup_functions(struct sdw_slave *slave);
+void sdca_lookup_interface_revision(struct sdw_slave *slave);
+
+#else
+
+static inline void sdca_lookup_functions(struct sdw_slave *slave) {}
+static inline void sdca_lookup_interface_revision(struct sdw_slave *slave) {}
+
+#endif
+
+#endif
new file mode 100644
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+/*
+ * The MIPI SDCA specification is available for public downloads at
+ * https://www.mipi.org/mipi-sdca-v1-0-download
+ *
+ * Copyright(c) 2024 Intel Corporation
+ */
+
+#ifndef __SDCA_FUNCTION_H__
+#define __SDCA_FUNCTION_H__
+
+/*
+ * SDCA Function Types from SDCA specification v1.0a Section 5.1.2
+ * all Function types not described are reserved
+ * Note that SIMPLE_AMP, SIMPLE_MIC and SIMPLE_JACK Function Types
+ * are NOT defined in SDCA 1.0a, but they were defined in earlier
+ * drafts and are planned for 1.1.
+ */
+
+enum sdca_function_type {
+ SDCA_FUNCTION_TYPE_SMART_AMP = 0x01, /* Amplifier with protection features */
+ SDCA_FUNCTION_TYPE_SIMPLE_AMP = 0x02, /* subset of SmartAmp */
+ SDCA_FUNCTION_TYPE_SMART_MIC = 0x03, /* Smart microphone with acoustic triggers */
+ SDCA_FUNCTION_TYPE_SIMPLE_MIC = 0x04, /* subset of SmartMic */
+ SDCA_FUNCTION_TYPE_SPEAKER_MIC = 0x05, /* Combination of SmartMic and SmartAmp */
+ SDCA_FUNCTION_TYPE_UAJ = 0x06, /* 3.5mm Universal Audio jack */
+ SDCA_FUNCTION_TYPE_RJ = 0x07, /* Retaskable jack */
+ SDCA_FUNCTION_TYPE_SIMPLE_JACK = 0x08, /* Subset of UAJ */
+ SDCA_FUNCTION_TYPE_HID = 0x0A, /* Human Interface Device, for e.g. buttons */
+ SDCA_FUNCTION_TYPE_IMP_DEF = 0x1F, /* Implementation-defined function */
+};
+
+/* Human-readable names used for kernel logs and Function device registration/bind */
+#define SDCA_FUNCTION_TYPE_SMART_AMP_NAME "SmartAmp"
+#define SDCA_FUNCTION_TYPE_SIMPLE_AMP_NAME "SimpleAmp"
+#define SDCA_FUNCTION_TYPE_SMART_MIC_NAME "SmartMic"
+#define SDCA_FUNCTION_TYPE_SIMPLE_MIC_NAME "SimpleMic"
+#define SDCA_FUNCTION_TYPE_SPEAKER_MIC_NAME "SpeakerMic"
+#define SDCA_FUNCTION_TYPE_UAJ_NAME "UAJ"
+#define SDCA_FUNCTION_TYPE_RJ_NAME "RJ"
+#define SDCA_FUNCTION_TYPE_SIMPLE_NAME "SimpleJack"
+#define SDCA_FUNCTION_TYPE_HID_NAME "HID"
+
+enum sdca_entity0_controls {
+ SDCA_CONTROL_ENTITY_0_COMMIT_GROUP_MASK = 0x01,
+ SDCA_CONTROL_ENTITY_0_INTSTAT_CLEAR = 0x02,
+ SDCA_CONTROL_ENTITY_0_INT_ENABLE = 0x03,
+ SDCA_CONTROL_ENTITY_0_FUNCTION_SDCA_VERSION = 0x04,
+ SDCA_CONTROL_ENTITY_0_FUNCTION_TOPOLOGY = 0x05,
+ SDCA_CONTROL_ENTITY_0_FUNCTION_MANUFACTURER_ID = 0x06,
+ SDCA_CONTROL_ENTITY_0_FUNCTION_ID = 0x07,
+ SDCA_CONTROL_ENTITY_0_FUNCTION_VERSION = 0x08
+};
+
+#endif
@@ -108,6 +108,7 @@ source "sound/soc/pxa/Kconfig"
source "sound/soc/qcom/Kconfig"
source "sound/soc/rockchip/Kconfig"
source "sound/soc/samsung/Kconfig"
+source "sound/soc/sdca/Kconfig"
source "sound/soc/sh/Kconfig"
source "sound/soc/sof/Kconfig"
source "sound/soc/spear/Kconfig"
@@ -61,6 +61,7 @@ obj-$(CONFIG_SND_SOC) += pxa/
obj-$(CONFIG_SND_SOC) += qcom/
obj-$(CONFIG_SND_SOC) += rockchip/
obj-$(CONFIG_SND_SOC) += samsung/
+obj-$(CONFIG_SND_SOC) += sdca/
obj-$(CONFIG_SND_SOC) += sh/
obj-$(CONFIG_SND_SOC) += sof/
obj-$(CONFIG_SND_SOC) += spear/
new file mode 100644
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config SND_SOC_SDCA
+ tristate "ASoC SDCA library"
+ depends on ACPI
+ help
+ This option enables support for the MIPI SoundWire Device
+ Class for Audio (SDCA).
+
+config SND_SOC_SDCA_OPTIONAL
+ def_tristate SND_SOC_SDCA || !SND_SOC_SDCA
new file mode 100644
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+snd-soc-sdca-objs := sdca_functions.o sdca_device.o
+
+obj-$(CONFIG_SND_SOC_SDCA) += snd-soc-sdca.o
new file mode 100644
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2024 Intel Corporation
+
+/*
+ * The MIPI SDCA specification is available for public downloads at
+ * https://www.mipi.org/mipi-sdca-v1-0-download
+ */
+
+#include <linux/acpi.h>
+#include <linux/soundwire/sdw.h>
+#include <sound/sdca.h>
+
+void sdca_lookup_interface_revision(struct sdw_slave *slave)
+{
+ struct fwnode_handle *fwnode = slave->dev.fwnode;
+
+ /*
+ * if this property is not present, then the sdca_interface_revision will
+ * remain zero, which will be considered as 'not defined' or 'invalid'.
+ */
+ fwnode_property_read_u32(fwnode, "mipi-sdw-sdca-interface-revision",
+ &slave->sdca_data.interface_revision);
+}
+EXPORT_SYMBOL_NS(sdca_lookup_interface_revision, SND_SOC_SDCA);
new file mode 100644
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2024 Intel Corporation
+
+/*
+ * The MIPI SDCA specification is available for public downloads at
+ * https://www.mipi.org/mipi-sdca-v1-0-download
+ */
+
+#include <linux/acpi.h>
+#include <linux/soundwire/sdw.h>
+#include <sound/sdca.h>
+#include <sound/sdca_function.h>
+
+static int patch_sdca_function_type(struct device *dev,
+ u32 interface_revision,
+ u32 *function_type,
+ const char **function_name)
+{
+ unsigned long function_type_patch = 0;
+
+ /*
+ * Unfortunately early SDCA specifications used different indices for Functions,
+ * for backwards compatibility we have to reorder the values found
+ */
+ if (interface_revision >= 0x0801)
+ goto skip_early_draft_order;
+
+ switch (*function_type) {
+ case 1:
+ function_type_patch = SDCA_FUNCTION_TYPE_SMART_AMP;
+ break;
+ case 2:
+ function_type_patch = SDCA_FUNCTION_TYPE_SMART_MIC;
+ break;
+ case 3:
+ function_type_patch = SDCA_FUNCTION_TYPE_SPEAKER_MIC;
+ break;
+ case 4:
+ function_type_patch = SDCA_FUNCTION_TYPE_UAJ;
+ break;
+ case 5:
+ function_type_patch = SDCA_FUNCTION_TYPE_RJ;
+ break;
+ case 6:
+ function_type_patch = SDCA_FUNCTION_TYPE_HID;
+ break;
+ default:
+ dev_warn(dev, "%s: SDCA version %#x unsupported function type %d, skipped\n",
+ __func__, interface_revision, *function_type);
+ return -EINVAL;
+ }
+
+skip_early_draft_order:
+ if (function_type_patch)
+ *function_type = function_type_patch;
+
+ /* now double-check the values */
+ switch (*function_type) {
+ case SDCA_FUNCTION_TYPE_SMART_AMP:
+ *function_name = SDCA_FUNCTION_TYPE_SMART_AMP_NAME;
+ break;
+ case SDCA_FUNCTION_TYPE_SMART_MIC:
+ *function_name = SDCA_FUNCTION_TYPE_SMART_MIC_NAME;
+ break;
+ case SDCA_FUNCTION_TYPE_UAJ:
+ *function_name = SDCA_FUNCTION_TYPE_UAJ_NAME;
+ break;
+ case SDCA_FUNCTION_TYPE_HID:
+ *function_name = SDCA_FUNCTION_TYPE_HID_NAME;
+ break;
+ case SDCA_FUNCTION_TYPE_SIMPLE_AMP:
+ case SDCA_FUNCTION_TYPE_SIMPLE_MIC:
+ case SDCA_FUNCTION_TYPE_SPEAKER_MIC:
+ case SDCA_FUNCTION_TYPE_RJ:
+ case SDCA_FUNCTION_TYPE_IMP_DEF:
+ dev_warn(dev, "%s: found unsupported SDCA function type %d, skipped\n",
+ __func__, *function_type);
+ return -EINVAL;
+ default:
+ dev_err(dev, "%s: found invalid SDCA function type %d, skipped\n",
+ __func__, *function_type);
+ return -EINVAL;
+ }
+
+ dev_info(dev, "%s: found SDCA function %s (type %d)\n",
+ __func__, *function_name, *function_type);
+
+ return 0;
+}
+
+static int find_sdca_function(struct acpi_device *adev, void *data)
+{
+ struct fwnode_handle *function_node = acpi_fwnode_handle(adev);
+ struct sdca_device_data *sdca_data = data;
+ struct device *dev = &adev->dev;
+ struct fwnode_handle *control5; /* used to identify function type */
+ const char *function_name;
+ u32 function_type;
+ int func_index;
+ u64 addr;
+ int ret;
+
+ if (sdca_data->num_functions >= SDCA_MAX_FUNCTION_COUNT) {
+ dev_err(dev, "%s: maximum number of functions exceeded\n", __func__);
+ return -EINVAL;
+ }
+
+ /*
+ * The number of functions cannot exceed 8, we could use
+ * acpi_get_local_address() but the value is stored as u64 so
+ * we might as well avoid casts and intermediate levels
+ */
+ ret = acpi_get_local_u64_address(adev->handle, &addr);
+ if (ret < 0)
+ return ret;
+
+ if (!addr) {
+ dev_err(dev, "%s: no addr\n", __func__);
+ return -ENODEV;
+ }
+
+ /*
+ * Extracting the topology type for an SDCA function is a
+ * convoluted process.
+ * The Function type is only visible as a result of a read
+ * from a control. In theory this would mean reading from the hardware,
+ * but the SDCA/DisCo specs defined the notion of "DC value" - a constant
+ * represented with a DSD subproperty.
+ * Drivers have to query the properties for the control
+ * SDCA_CONTROL_ENTITY_0_FUNCTION_TOPOLOGY (0x05)
+ */
+ control5 = fwnode_get_named_child_node(function_node,
+ "mipi-sdca-control-0x5-subproperties");
+ if (!control5)
+ return -ENODEV;
+
+ ret = fwnode_property_read_u32(control5, "mipi-sdca-control-dc-value",
+ &function_type);
+
+ fwnode_handle_put(control5);
+
+ if (ret < 0) {
+ dev_err(dev, "%s: the function type can only be determined from ACPI information\n",
+ __func__);
+ return ret;
+ }
+
+ ret = patch_sdca_function_type(dev, sdca_data->interface_revision,
+ &function_type, &function_name);
+ if (ret < 0)
+ return ret;
+
+ /* store results */
+ func_index = sdca_data->num_functions;
+ sdca_data->sdca_func[func_index].adr = addr;
+ sdca_data->sdca_func[func_index].type = function_type;
+ sdca_data->sdca_func[func_index].name = function_name;
+ sdca_data->num_functions++;
+
+ return 0;
+}
+
+void sdca_lookup_functions(struct sdw_slave *slave)
+{
+ struct device *dev = &slave->dev;
+ struct acpi_device *adev = to_acpi_device_node(dev->fwnode);
+
+ acpi_dev_for_each_child(adev, find_sdca_function, &slave->sdca_data);
+}
+EXPORT_SYMBOL_NS(sdca_lookup_functions, SND_SOC_SDCA);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("SDCA library");