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