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 9935cd06bSCharles Keepax #define dev_fmt(fmt) "%s: " fmt, __func__ 10935cd06bSCharles Keepax 113a513da1SPierre-Louis Bossart #include <linux/acpi.h> 12deb01520SCharles Keepax #include <linux/device.h> 13deb01520SCharles Keepax #include <linux/module.h> 14deb01520SCharles Keepax #include <linux/property.h> 153a513da1SPierre-Louis Bossart #include <linux/soundwire/sdw.h> 16deb01520SCharles Keepax #include <linux/types.h> 173a513da1SPierre-Louis Bossart #include <sound/sdca.h> 183a513da1SPierre-Louis Bossart #include <sound/sdca_function.h> 193a513da1SPierre-Louis Bossart 20*69dcf023SCharles Keepax static int patch_sdca_function_type(u32 interface_revision, u32 *function_type) 213a513da1SPierre-Louis Bossart { 223a513da1SPierre-Louis Bossart /* 233a513da1SPierre-Louis Bossart * Unfortunately early SDCA specifications used different indices for Functions, 243a513da1SPierre-Louis Bossart * for backwards compatibility we have to reorder the values found 253a513da1SPierre-Louis Bossart */ 26*69dcf023SCharles Keepax if (interface_revision < 0x0801) { 273a513da1SPierre-Louis Bossart switch (*function_type) { 283a513da1SPierre-Louis Bossart case 1: 29*69dcf023SCharles Keepax *function_type = SDCA_FUNCTION_TYPE_SMART_AMP; 303a513da1SPierre-Louis Bossart break; 313a513da1SPierre-Louis Bossart case 2: 32*69dcf023SCharles Keepax *function_type = SDCA_FUNCTION_TYPE_SMART_MIC; 333a513da1SPierre-Louis Bossart break; 343a513da1SPierre-Louis Bossart case 3: 35*69dcf023SCharles Keepax *function_type = SDCA_FUNCTION_TYPE_SPEAKER_MIC; 363a513da1SPierre-Louis Bossart break; 373a513da1SPierre-Louis Bossart case 4: 38*69dcf023SCharles Keepax *function_type = SDCA_FUNCTION_TYPE_UAJ; 393a513da1SPierre-Louis Bossart break; 403a513da1SPierre-Louis Bossart case 5: 41*69dcf023SCharles Keepax *function_type = SDCA_FUNCTION_TYPE_RJ; 423a513da1SPierre-Louis Bossart break; 433a513da1SPierre-Louis Bossart case 6: 44*69dcf023SCharles Keepax *function_type = SDCA_FUNCTION_TYPE_HID; 453a513da1SPierre-Louis Bossart break; 463a513da1SPierre-Louis Bossart default: 473a513da1SPierre-Louis Bossart return -EINVAL; 483a513da1SPierre-Louis Bossart } 493a513da1SPierre-Louis Bossart } 503a513da1SPierre-Louis Bossart 513a513da1SPierre-Louis Bossart return 0; 523a513da1SPierre-Louis Bossart } 533a513da1SPierre-Louis Bossart 54*69dcf023SCharles Keepax static const char *get_sdca_function_name(u32 function_type) 55*69dcf023SCharles Keepax { 56*69dcf023SCharles Keepax switch (function_type) { 57*69dcf023SCharles Keepax case SDCA_FUNCTION_TYPE_SMART_AMP: 58*69dcf023SCharles Keepax return SDCA_FUNCTION_TYPE_SMART_AMP_NAME; 59*69dcf023SCharles Keepax case SDCA_FUNCTION_TYPE_SMART_MIC: 60*69dcf023SCharles Keepax return SDCA_FUNCTION_TYPE_SMART_MIC_NAME; 61*69dcf023SCharles Keepax case SDCA_FUNCTION_TYPE_UAJ: 62*69dcf023SCharles Keepax return SDCA_FUNCTION_TYPE_UAJ_NAME; 63*69dcf023SCharles Keepax case SDCA_FUNCTION_TYPE_HID: 64*69dcf023SCharles Keepax return SDCA_FUNCTION_TYPE_HID_NAME; 65*69dcf023SCharles Keepax case SDCA_FUNCTION_TYPE_SIMPLE_AMP: 66*69dcf023SCharles Keepax return SDCA_FUNCTION_TYPE_SIMPLE_AMP_NAME; 67*69dcf023SCharles Keepax case SDCA_FUNCTION_TYPE_SIMPLE_MIC: 68*69dcf023SCharles Keepax return SDCA_FUNCTION_TYPE_SIMPLE_MIC_NAME; 69*69dcf023SCharles Keepax case SDCA_FUNCTION_TYPE_SPEAKER_MIC: 70*69dcf023SCharles Keepax return SDCA_FUNCTION_TYPE_SPEAKER_MIC_NAME; 71*69dcf023SCharles Keepax case SDCA_FUNCTION_TYPE_RJ: 72*69dcf023SCharles Keepax return SDCA_FUNCTION_TYPE_RJ_NAME; 73*69dcf023SCharles Keepax case SDCA_FUNCTION_TYPE_IMP_DEF: 74*69dcf023SCharles Keepax return SDCA_FUNCTION_TYPE_IMP_DEF_NAME; 75*69dcf023SCharles Keepax default: 76*69dcf023SCharles Keepax return NULL; 77*69dcf023SCharles Keepax } 78*69dcf023SCharles Keepax } 79*69dcf023SCharles Keepax 803a513da1SPierre-Louis Bossart static int find_sdca_function(struct acpi_device *adev, void *data) 813a513da1SPierre-Louis Bossart { 823a513da1SPierre-Louis Bossart struct fwnode_handle *function_node = acpi_fwnode_handle(adev); 833a513da1SPierre-Louis Bossart struct sdca_device_data *sdca_data = data; 843a513da1SPierre-Louis Bossart struct device *dev = &adev->dev; 853a513da1SPierre-Louis Bossart struct fwnode_handle *control5; /* used to identify function type */ 863a513da1SPierre-Louis Bossart const char *function_name; 873a513da1SPierre-Louis Bossart u32 function_type; 883a513da1SPierre-Louis Bossart int func_index; 893a513da1SPierre-Louis Bossart u64 addr; 903a513da1SPierre-Louis Bossart int ret; 913a513da1SPierre-Louis Bossart 923a513da1SPierre-Louis Bossart if (sdca_data->num_functions >= SDCA_MAX_FUNCTION_COUNT) { 93935cd06bSCharles Keepax dev_err(dev, "maximum number of functions exceeded\n"); 943a513da1SPierre-Louis Bossart return -EINVAL; 953a513da1SPierre-Louis Bossart } 963a513da1SPierre-Louis Bossart 973a513da1SPierre-Louis Bossart ret = acpi_get_local_u64_address(adev->handle, &addr); 983a513da1SPierre-Louis Bossart if (ret < 0) 993a513da1SPierre-Louis Bossart return ret; 1003a513da1SPierre-Louis Bossart 101c36297b1SCharles Keepax if (!addr || addr > 0x7) { 102c36297b1SCharles Keepax dev_err(dev, "invalid addr: 0x%llx\n", addr); 1033a513da1SPierre-Louis Bossart return -ENODEV; 1043a513da1SPierre-Louis Bossart } 1053a513da1SPierre-Louis Bossart 1063a513da1SPierre-Louis Bossart /* 1073a513da1SPierre-Louis Bossart * Extracting the topology type for an SDCA function is a 1083a513da1SPierre-Louis Bossart * convoluted process. 1093a513da1SPierre-Louis Bossart * The Function type is only visible as a result of a read 1103a513da1SPierre-Louis Bossart * from a control. In theory this would mean reading from the hardware, 1113a513da1SPierre-Louis Bossart * but the SDCA/DisCo specs defined the notion of "DC value" - a constant 1123a513da1SPierre-Louis Bossart * represented with a DSD subproperty. 1133a513da1SPierre-Louis Bossart * Drivers have to query the properties for the control 1143a513da1SPierre-Louis Bossart * SDCA_CONTROL_ENTITY_0_FUNCTION_TOPOLOGY (0x05) 1153a513da1SPierre-Louis Bossart */ 1163a513da1SPierre-Louis Bossart control5 = fwnode_get_named_child_node(function_node, 1173a513da1SPierre-Louis Bossart "mipi-sdca-control-0x5-subproperties"); 1183a513da1SPierre-Louis Bossart if (!control5) 1193a513da1SPierre-Louis Bossart return -ENODEV; 1203a513da1SPierre-Louis Bossart 1213a513da1SPierre-Louis Bossart ret = fwnode_property_read_u32(control5, "mipi-sdca-control-dc-value", 1223a513da1SPierre-Louis Bossart &function_type); 1233a513da1SPierre-Louis Bossart 1243a513da1SPierre-Louis Bossart fwnode_handle_put(control5); 1253a513da1SPierre-Louis Bossart 1263a513da1SPierre-Louis Bossart if (ret < 0) { 127935cd06bSCharles Keepax dev_err(dev, "function type only supported as DisCo constant\n"); 1283a513da1SPierre-Louis Bossart return ret; 1293a513da1SPierre-Louis Bossart } 1303a513da1SPierre-Louis Bossart 131*69dcf023SCharles Keepax ret = patch_sdca_function_type(sdca_data->interface_revision, &function_type); 132*69dcf023SCharles Keepax if (ret < 0) { 133*69dcf023SCharles Keepax dev_err(dev, "SDCA version %#x invalid function type %d\n", 134*69dcf023SCharles Keepax sdca_data->interface_revision, function_type); 1353a513da1SPierre-Louis Bossart return ret; 136*69dcf023SCharles Keepax } 137*69dcf023SCharles Keepax 138*69dcf023SCharles Keepax function_name = get_sdca_function_name(function_type); 139*69dcf023SCharles Keepax if (!function_name) { 140*69dcf023SCharles Keepax dev_err(dev, "invalid SDCA function type %d\n", function_type); 141*69dcf023SCharles Keepax return -EINVAL; 142*69dcf023SCharles Keepax } 143*69dcf023SCharles Keepax 144*69dcf023SCharles Keepax dev_info(dev, "SDCA function %s (type %d) at 0x%llx\n", 145*69dcf023SCharles Keepax function_name, function_type, addr); 1463a513da1SPierre-Louis Bossart 1473a513da1SPierre-Louis Bossart /* store results */ 1483a513da1SPierre-Louis Bossart func_index = sdca_data->num_functions; 1493a513da1SPierre-Louis Bossart sdca_data->sdca_func[func_index].adr = addr; 1503a513da1SPierre-Louis Bossart sdca_data->sdca_func[func_index].type = function_type; 1513a513da1SPierre-Louis Bossart sdca_data->sdca_func[func_index].name = function_name; 1523a513da1SPierre-Louis Bossart sdca_data->num_functions++; 1533a513da1SPierre-Louis Bossart 1543a513da1SPierre-Louis Bossart return 0; 1553a513da1SPierre-Louis Bossart } 1563a513da1SPierre-Louis Bossart 1573a513da1SPierre-Louis Bossart void sdca_lookup_functions(struct sdw_slave *slave) 1583a513da1SPierre-Louis Bossart { 1593a513da1SPierre-Louis Bossart struct device *dev = &slave->dev; 1603a513da1SPierre-Louis Bossart struct acpi_device *adev = to_acpi_device_node(dev->fwnode); 1613a513da1SPierre-Louis Bossart 162de35b06bSBard Liao if (!adev) { 163de35b06bSBard Liao dev_info(dev, "No matching ACPI device found, ignoring peripheral\n"); 164de35b06bSBard Liao return; 165de35b06bSBard Liao } 1663a513da1SPierre-Louis Bossart acpi_dev_for_each_child(adev, find_sdca_function, &slave->sdca_data); 1673a513da1SPierre-Louis Bossart } 168cdd30ebbSPeter Zijlstra EXPORT_SYMBOL_NS(sdca_lookup_functions, "SND_SOC_SDCA"); 1693a513da1SPierre-Louis Bossart 1703a513da1SPierre-Louis Bossart MODULE_LICENSE("Dual BSD/GPL"); 1713a513da1SPierre-Louis Bossart MODULE_DESCRIPTION("SDCA library"); 172