xref: /linux/sound/soc/sdca/sdca_functions.c (revision deb015208f7be9a62cb68dd8337d075b1829ee1d)
13a513da1SPierre-Louis Bossart // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
23a513da1SPierre-Louis Bossart // Copyright(c) 2024 Intel Corporation
33a513da1SPierre-Louis Bossart 
43a513da1SPierre-Louis Bossart /*
53a513da1SPierre-Louis Bossart  * The MIPI SDCA specification is available for public downloads at
63a513da1SPierre-Louis Bossart  * https://www.mipi.org/mipi-sdca-v1-0-download
73a513da1SPierre-Louis Bossart  */
83a513da1SPierre-Louis Bossart 
93a513da1SPierre-Louis Bossart #include <linux/acpi.h>
10*deb01520SCharles Keepax #include <linux/device.h>
11*deb01520SCharles Keepax #include <linux/module.h>
12*deb01520SCharles Keepax #include <linux/property.h>
133a513da1SPierre-Louis Bossart #include <linux/soundwire/sdw.h>
14*deb01520SCharles Keepax #include <linux/types.h>
153a513da1SPierre-Louis Bossart #include <sound/sdca.h>
163a513da1SPierre-Louis Bossart #include <sound/sdca_function.h>
173a513da1SPierre-Louis Bossart 
183a513da1SPierre-Louis Bossart static int patch_sdca_function_type(struct device *dev,
193a513da1SPierre-Louis Bossart 				    u32 interface_revision,
203a513da1SPierre-Louis Bossart 				    u32 *function_type,
213a513da1SPierre-Louis Bossart 				    const char **function_name)
223a513da1SPierre-Louis Bossart {
233a513da1SPierre-Louis Bossart 	unsigned long function_type_patch = 0;
243a513da1SPierre-Louis Bossart 
253a513da1SPierre-Louis Bossart 	/*
263a513da1SPierre-Louis Bossart 	 * Unfortunately early SDCA specifications used different indices for Functions,
273a513da1SPierre-Louis Bossart 	 * for backwards compatibility we have to reorder the values found
283a513da1SPierre-Louis Bossart 	 */
293a513da1SPierre-Louis Bossart 	if (interface_revision >= 0x0801)
303a513da1SPierre-Louis Bossart 		goto skip_early_draft_order;
313a513da1SPierre-Louis Bossart 
323a513da1SPierre-Louis Bossart 	switch (*function_type) {
333a513da1SPierre-Louis Bossart 	case 1:
343a513da1SPierre-Louis Bossart 		function_type_patch = SDCA_FUNCTION_TYPE_SMART_AMP;
353a513da1SPierre-Louis Bossart 		break;
363a513da1SPierre-Louis Bossart 	case 2:
373a513da1SPierre-Louis Bossart 		function_type_patch = SDCA_FUNCTION_TYPE_SMART_MIC;
383a513da1SPierre-Louis Bossart 		break;
393a513da1SPierre-Louis Bossart 	case 3:
403a513da1SPierre-Louis Bossart 		function_type_patch = SDCA_FUNCTION_TYPE_SPEAKER_MIC;
413a513da1SPierre-Louis Bossart 		break;
423a513da1SPierre-Louis Bossart 	case 4:
433a513da1SPierre-Louis Bossart 		function_type_patch = SDCA_FUNCTION_TYPE_UAJ;
443a513da1SPierre-Louis Bossart 		break;
453a513da1SPierre-Louis Bossart 	case 5:
463a513da1SPierre-Louis Bossart 		function_type_patch = SDCA_FUNCTION_TYPE_RJ;
473a513da1SPierre-Louis Bossart 		break;
483a513da1SPierre-Louis Bossart 	case 6:
493a513da1SPierre-Louis Bossart 		function_type_patch = SDCA_FUNCTION_TYPE_HID;
503a513da1SPierre-Louis Bossart 		break;
513a513da1SPierre-Louis Bossart 	default:
523a513da1SPierre-Louis Bossart 		dev_warn(dev, "%s: SDCA version %#x unsupported function type %d, skipped\n",
533a513da1SPierre-Louis Bossart 			 __func__, interface_revision, *function_type);
543a513da1SPierre-Louis Bossart 		return -EINVAL;
553a513da1SPierre-Louis Bossart 	}
563a513da1SPierre-Louis Bossart 
573a513da1SPierre-Louis Bossart skip_early_draft_order:
583a513da1SPierre-Louis Bossart 	if (function_type_patch)
593a513da1SPierre-Louis Bossart 		*function_type = function_type_patch;
603a513da1SPierre-Louis Bossart 
613a513da1SPierre-Louis Bossart 	/* now double-check the values */
623a513da1SPierre-Louis Bossart 	switch (*function_type) {
633a513da1SPierre-Louis Bossart 	case SDCA_FUNCTION_TYPE_SMART_AMP:
643a513da1SPierre-Louis Bossart 		*function_name = SDCA_FUNCTION_TYPE_SMART_AMP_NAME;
653a513da1SPierre-Louis Bossart 		break;
663a513da1SPierre-Louis Bossart 	case SDCA_FUNCTION_TYPE_SMART_MIC:
673a513da1SPierre-Louis Bossart 		*function_name = SDCA_FUNCTION_TYPE_SMART_MIC_NAME;
683a513da1SPierre-Louis Bossart 		break;
693a513da1SPierre-Louis Bossart 	case SDCA_FUNCTION_TYPE_UAJ:
703a513da1SPierre-Louis Bossart 		*function_name = SDCA_FUNCTION_TYPE_UAJ_NAME;
713a513da1SPierre-Louis Bossart 		break;
723a513da1SPierre-Louis Bossart 	case SDCA_FUNCTION_TYPE_HID:
733a513da1SPierre-Louis Bossart 		*function_name = SDCA_FUNCTION_TYPE_HID_NAME;
743a513da1SPierre-Louis Bossart 		break;
753a513da1SPierre-Louis Bossart 	case SDCA_FUNCTION_TYPE_SIMPLE_AMP:
763a513da1SPierre-Louis Bossart 	case SDCA_FUNCTION_TYPE_SIMPLE_MIC:
773a513da1SPierre-Louis Bossart 	case SDCA_FUNCTION_TYPE_SPEAKER_MIC:
783a513da1SPierre-Louis Bossart 	case SDCA_FUNCTION_TYPE_RJ:
793a513da1SPierre-Louis Bossart 	case SDCA_FUNCTION_TYPE_IMP_DEF:
803a513da1SPierre-Louis Bossart 		dev_warn(dev, "%s: found unsupported SDCA function type %d, skipped\n",
813a513da1SPierre-Louis Bossart 			 __func__, *function_type);
823a513da1SPierre-Louis Bossart 		return -EINVAL;
833a513da1SPierre-Louis Bossart 	default:
843a513da1SPierre-Louis Bossart 		dev_err(dev, "%s: found invalid SDCA function type %d, skipped\n",
853a513da1SPierre-Louis Bossart 			__func__, *function_type);
863a513da1SPierre-Louis Bossart 		return -EINVAL;
873a513da1SPierre-Louis Bossart 	}
883a513da1SPierre-Louis Bossart 
893a513da1SPierre-Louis Bossart 	dev_info(dev, "%s: found SDCA function %s (type %d)\n",
903a513da1SPierre-Louis Bossart 		 __func__, *function_name, *function_type);
913a513da1SPierre-Louis Bossart 
923a513da1SPierre-Louis Bossart 	return 0;
933a513da1SPierre-Louis Bossart }
943a513da1SPierre-Louis Bossart 
953a513da1SPierre-Louis Bossart static int find_sdca_function(struct acpi_device *adev, void *data)
963a513da1SPierre-Louis Bossart {
973a513da1SPierre-Louis Bossart 	struct fwnode_handle *function_node = acpi_fwnode_handle(adev);
983a513da1SPierre-Louis Bossart 	struct sdca_device_data *sdca_data = data;
993a513da1SPierre-Louis Bossart 	struct device *dev = &adev->dev;
1003a513da1SPierre-Louis Bossart 	struct fwnode_handle *control5; /* used to identify function type */
1013a513da1SPierre-Louis Bossart 	const char *function_name;
1023a513da1SPierre-Louis Bossart 	u32 function_type;
1033a513da1SPierre-Louis Bossart 	int func_index;
1043a513da1SPierre-Louis Bossart 	u64 addr;
1053a513da1SPierre-Louis Bossart 	int ret;
1063a513da1SPierre-Louis Bossart 
1073a513da1SPierre-Louis Bossart 	if (sdca_data->num_functions >= SDCA_MAX_FUNCTION_COUNT) {
1083a513da1SPierre-Louis Bossart 		dev_err(dev, "%s: maximum number of functions exceeded\n", __func__);
1093a513da1SPierre-Louis Bossart 		return -EINVAL;
1103a513da1SPierre-Louis Bossart 	}
1113a513da1SPierre-Louis Bossart 
1123a513da1SPierre-Louis Bossart 	/*
1133a513da1SPierre-Louis Bossart 	 * The number of functions cannot exceed 8, we could use
1143a513da1SPierre-Louis Bossart 	 * acpi_get_local_address() but the value is stored as u64 so
1153a513da1SPierre-Louis Bossart 	 * we might as well avoid casts and intermediate levels
1163a513da1SPierre-Louis Bossart 	 */
1173a513da1SPierre-Louis Bossart 	ret = acpi_get_local_u64_address(adev->handle, &addr);
1183a513da1SPierre-Louis Bossart 	if (ret < 0)
1193a513da1SPierre-Louis Bossart 		return ret;
1203a513da1SPierre-Louis Bossart 
1213a513da1SPierre-Louis Bossart 	if (!addr) {
1223a513da1SPierre-Louis Bossart 		dev_err(dev, "%s: no addr\n", __func__);
1233a513da1SPierre-Louis Bossart 		return -ENODEV;
1243a513da1SPierre-Louis Bossart 	}
1253a513da1SPierre-Louis Bossart 
1263a513da1SPierre-Louis Bossart 	/*
1273a513da1SPierre-Louis Bossart 	 * Extracting the topology type for an SDCA function is a
1283a513da1SPierre-Louis Bossart 	 * convoluted process.
1293a513da1SPierre-Louis Bossart 	 * The Function type is only visible as a result of a read
1303a513da1SPierre-Louis Bossart 	 * from a control. In theory this would mean reading from the hardware,
1313a513da1SPierre-Louis Bossart 	 * but the SDCA/DisCo specs defined the notion of "DC value" - a constant
1323a513da1SPierre-Louis Bossart 	 * represented with a DSD subproperty.
1333a513da1SPierre-Louis Bossart 	 * Drivers have to query the properties for the control
1343a513da1SPierre-Louis Bossart 	 * SDCA_CONTROL_ENTITY_0_FUNCTION_TOPOLOGY (0x05)
1353a513da1SPierre-Louis Bossart 	 */
1363a513da1SPierre-Louis Bossart 	control5 = fwnode_get_named_child_node(function_node,
1373a513da1SPierre-Louis Bossart 					       "mipi-sdca-control-0x5-subproperties");
1383a513da1SPierre-Louis Bossart 	if (!control5)
1393a513da1SPierre-Louis Bossart 		return -ENODEV;
1403a513da1SPierre-Louis Bossart 
1413a513da1SPierre-Louis Bossart 	ret = fwnode_property_read_u32(control5, "mipi-sdca-control-dc-value",
1423a513da1SPierre-Louis Bossart 				       &function_type);
1433a513da1SPierre-Louis Bossart 
1443a513da1SPierre-Louis Bossart 	fwnode_handle_put(control5);
1453a513da1SPierre-Louis Bossart 
1463a513da1SPierre-Louis Bossart 	if (ret < 0) {
1473a513da1SPierre-Louis Bossart 		dev_err(dev, "%s: the function type can only be determined from ACPI information\n",
1483a513da1SPierre-Louis Bossart 			__func__);
1493a513da1SPierre-Louis Bossart 		return ret;
1503a513da1SPierre-Louis Bossart 	}
1513a513da1SPierre-Louis Bossart 
1523a513da1SPierre-Louis Bossart 	ret = patch_sdca_function_type(dev, sdca_data->interface_revision,
1533a513da1SPierre-Louis Bossart 				       &function_type, &function_name);
1543a513da1SPierre-Louis Bossart 	if (ret < 0)
1553a513da1SPierre-Louis Bossart 		return ret;
1563a513da1SPierre-Louis Bossart 
1573a513da1SPierre-Louis Bossart 	/* store results */
1583a513da1SPierre-Louis Bossart 	func_index = sdca_data->num_functions;
1593a513da1SPierre-Louis Bossart 	sdca_data->sdca_func[func_index].adr = addr;
1603a513da1SPierre-Louis Bossart 	sdca_data->sdca_func[func_index].type = function_type;
1613a513da1SPierre-Louis Bossart 	sdca_data->sdca_func[func_index].name = function_name;
1623a513da1SPierre-Louis Bossart 	sdca_data->num_functions++;
1633a513da1SPierre-Louis Bossart 
1643a513da1SPierre-Louis Bossart 	return 0;
1653a513da1SPierre-Louis Bossart }
1663a513da1SPierre-Louis Bossart 
1673a513da1SPierre-Louis Bossart void sdca_lookup_functions(struct sdw_slave *slave)
1683a513da1SPierre-Louis Bossart {
1693a513da1SPierre-Louis Bossart 	struct device *dev = &slave->dev;
1703a513da1SPierre-Louis Bossart 	struct acpi_device *adev = to_acpi_device_node(dev->fwnode);
1713a513da1SPierre-Louis Bossart 
172de35b06bSBard Liao 	if (!adev) {
173de35b06bSBard Liao 		dev_info(dev, "No matching ACPI device found, ignoring peripheral\n");
174de35b06bSBard Liao 		return;
175de35b06bSBard Liao 	}
1763a513da1SPierre-Louis Bossart 	acpi_dev_for_each_child(adev, find_sdca_function, &slave->sdca_data);
1773a513da1SPierre-Louis Bossart }
178cdd30ebbSPeter Zijlstra EXPORT_SYMBOL_NS(sdca_lookup_functions, "SND_SOC_SDCA");
1793a513da1SPierre-Louis Bossart 
1803a513da1SPierre-Louis Bossart MODULE_LICENSE("Dual BSD/GPL");
1813a513da1SPierre-Louis Bossart MODULE_DESCRIPTION("SDCA library");
182