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