diff options
Diffstat (limited to 'sound')
| -rw-r--r-- | sound/soc/Kconfig | 1 | ||||
| -rw-r--r-- | sound/soc/Makefile | 1 | ||||
| -rw-r--r-- | sound/soc/sdca/Kconfig | 11 | ||||
| -rw-r--r-- | sound/soc/sdca/Makefile | 5 | ||||
| -rw-r--r-- | sound/soc/sdca/sdca_device.c | 24 | ||||
| -rw-r--r-- | sound/soc/sdca/sdca_functions.c | 173 |
6 files changed, 215 insertions, 0 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index e87bd15a8b43..8e01b421fe8d 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -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" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 775bb38c2ed4..5307b0b62a93 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -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/ diff --git a/sound/soc/sdca/Kconfig b/sound/soc/sdca/Kconfig new file mode 100644 index 000000000000..07f6822fa614 --- /dev/null +++ b/sound/soc/sdca/Kconfig @@ -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 diff --git a/sound/soc/sdca/Makefile b/sound/soc/sdca/Makefile new file mode 100644 index 000000000000..c296bd5a0a7c --- /dev/null +++ b/sound/soc/sdca/Makefile @@ -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 diff --git a/sound/soc/sdca/sdca_device.c b/sound/soc/sdca/sdca_device.c new file mode 100644 index 000000000000..58f5f6f0f723 --- /dev/null +++ b/sound/soc/sdca/sdca_device.c @@ -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); diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c new file mode 100644 index 000000000000..a6ad57430dd4 --- /dev/null +++ b/sound/soc/sdca/sdca_functions.c @@ -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"); |
