1*71f7990aSMaciej Strozek // SPDX-License-Identifier: GPL-2.0 2*71f7990aSMaciej Strozek // Copyright (C) 2025 Cirrus Logic, Inc. and 3*71f7990aSMaciej Strozek // Cirrus Logic International Semiconductor Ltd. 4*71f7990aSMaciej Strozek 5*71f7990aSMaciej Strozek /* 6*71f7990aSMaciej Strozek * The MIPI SDCA specification is available for public downloads at 7*71f7990aSMaciej Strozek * https://www.mipi.org/mipi-sdca-v1-0-download 8*71f7990aSMaciej Strozek */ 9*71f7990aSMaciej Strozek 10*71f7990aSMaciej Strozek #include <linux/acpi.h> 11*71f7990aSMaciej Strozek #include <linux/device.h> 12*71f7990aSMaciej Strozek #include <linux/dev_printk.h> 13*71f7990aSMaciej Strozek #include <linux/dmi.h> 14*71f7990aSMaciej Strozek #include <linux/firmware.h> 15*71f7990aSMaciej Strozek #include <linux/module.h> 16*71f7990aSMaciej Strozek #include <linux/pci.h> 17*71f7990aSMaciej Strozek #include <linux/regmap.h> 18*71f7990aSMaciej Strozek #include <linux/sprintf.h> 19*71f7990aSMaciej Strozek #include <linux/soundwire/sdw.h> 20*71f7990aSMaciej Strozek #include <linux/soundwire/sdw_registers.h> 21*71f7990aSMaciej Strozek #include <sound/sdca.h> 22*71f7990aSMaciej Strozek #include <sound/sdca_fdl.h> 23*71f7990aSMaciej Strozek #include <sound/sdca_function.h> 24*71f7990aSMaciej Strozek #include <sound/sdca_interrupts.h> 25*71f7990aSMaciej Strozek #include <sound/sdca_ump.h> 26*71f7990aSMaciej Strozek 27*71f7990aSMaciej Strozek /** 28*71f7990aSMaciej Strozek * sdca_reset_function - send an SDCA function reset 29*71f7990aSMaciej Strozek * @dev: Device pointer for error messages. 30*71f7990aSMaciej Strozek * @function: Pointer to the SDCA Function. 31*71f7990aSMaciej Strozek * @regmap: Pointer to the SDCA Function regmap. 32*71f7990aSMaciej Strozek * 33*71f7990aSMaciej Strozek * Return: Zero on success or a negative error code. 34*71f7990aSMaciej Strozek */ 35*71f7990aSMaciej Strozek int sdca_reset_function(struct device *dev, struct sdca_function_data *function, 36*71f7990aSMaciej Strozek struct regmap *regmap) 37*71f7990aSMaciej Strozek { 38*71f7990aSMaciej Strozek unsigned int reg = SDW_SDCA_CTL(function->desc->adr, 39*71f7990aSMaciej Strozek SDCA_ENTITY_TYPE_ENTITY_0, 40*71f7990aSMaciej Strozek SDCA_CTL_ENTITY_0_FUNCTION_ACTION, 0); 41*71f7990aSMaciej Strozek unsigned int val, poll_us; 42*71f7990aSMaciej Strozek int ret; 43*71f7990aSMaciej Strozek 44*71f7990aSMaciej Strozek ret = regmap_write(regmap, reg, SDCA_CTL_ENTITY_0_RESET_FUNCTION_NOW); 45*71f7990aSMaciej Strozek if (ret) // Allowed for function reset to not be implemented 46*71f7990aSMaciej Strozek return 0; 47*71f7990aSMaciej Strozek 48*71f7990aSMaciej Strozek if (!function->reset_max_delay) { 49*71f7990aSMaciej Strozek dev_err(dev, "No reset delay specified in DisCo\n"); 50*71f7990aSMaciej Strozek return -EINVAL; 51*71f7990aSMaciej Strozek } 52*71f7990aSMaciej Strozek 53*71f7990aSMaciej Strozek poll_us = umin(function->reset_max_delay >> 4, 1000); 54*71f7990aSMaciej Strozek 55*71f7990aSMaciej Strozek ret = regmap_read_poll_timeout(regmap, reg, val, !val, poll_us, 56*71f7990aSMaciej Strozek function->reset_max_delay); 57*71f7990aSMaciej Strozek if (ret) { 58*71f7990aSMaciej Strozek dev_err(dev, "Failed waiting for function reset: %d\n", ret); 59*71f7990aSMaciej Strozek return ret; 60*71f7990aSMaciej Strozek } 61*71f7990aSMaciej Strozek 62*71f7990aSMaciej Strozek return 0; 63*71f7990aSMaciej Strozek } 64*71f7990aSMaciej Strozek EXPORT_SYMBOL_NS(sdca_reset_function, "SND_SOC_SDCA"); 65*71f7990aSMaciej Strozek 66*71f7990aSMaciej Strozek static char *fdl_get_sku_filename(struct device *dev, 67*71f7990aSMaciej Strozek struct sdca_fdl_file *fdl_file) 68*71f7990aSMaciej Strozek { 69*71f7990aSMaciej Strozek struct device *parent = dev; 70*71f7990aSMaciej Strozek const char *product_vendor; 71*71f7990aSMaciej Strozek const char *product_sku; 72*71f7990aSMaciej Strozek 73*71f7990aSMaciej Strozek /* 74*71f7990aSMaciej Strozek * Try to find pci_dev manually because the card may not be ready to be 75*71f7990aSMaciej Strozek * used for snd_soc_card_get_pci_ssid yet 76*71f7990aSMaciej Strozek */ 77*71f7990aSMaciej Strozek while (parent) { 78*71f7990aSMaciej Strozek if (dev_is_pci(parent)) { 79*71f7990aSMaciej Strozek struct pci_dev *pci_dev = to_pci_dev(parent); 80*71f7990aSMaciej Strozek 81*71f7990aSMaciej Strozek return kasprintf(GFP_KERNEL, "sdca/%x/%x/%x/%x.bin", 82*71f7990aSMaciej Strozek fdl_file->vendor_id, 83*71f7990aSMaciej Strozek pci_dev->subsystem_vendor, 84*71f7990aSMaciej Strozek pci_dev->subsystem_device, 85*71f7990aSMaciej Strozek fdl_file->file_id); 86*71f7990aSMaciej Strozek } else { 87*71f7990aSMaciej Strozek parent = parent->parent; 88*71f7990aSMaciej Strozek } 89*71f7990aSMaciej Strozek } 90*71f7990aSMaciej Strozek 91*71f7990aSMaciej Strozek product_vendor = dmi_get_system_info(DMI_SYS_VENDOR); 92*71f7990aSMaciej Strozek if (!product_vendor || !strcmp(product_vendor, "Default string")) 93*71f7990aSMaciej Strozek product_vendor = dmi_get_system_info(DMI_BOARD_VENDOR); 94*71f7990aSMaciej Strozek if (!product_vendor || !strcmp(product_vendor, "Default string")) 95*71f7990aSMaciej Strozek product_vendor = dmi_get_system_info(DMI_CHASSIS_VENDOR); 96*71f7990aSMaciej Strozek if (!product_vendor) 97*71f7990aSMaciej Strozek product_vendor = "unknown"; 98*71f7990aSMaciej Strozek 99*71f7990aSMaciej Strozek product_sku = dmi_get_system_info(DMI_PRODUCT_SKU); 100*71f7990aSMaciej Strozek if (!product_sku || !strcmp(product_sku, "Default string")) 101*71f7990aSMaciej Strozek product_sku = dmi_get_system_info(DMI_PRODUCT_NAME); 102*71f7990aSMaciej Strozek if (!product_sku) 103*71f7990aSMaciej Strozek product_sku = "unknown"; 104*71f7990aSMaciej Strozek 105*71f7990aSMaciej Strozek return kasprintf(GFP_KERNEL, "sdca/%x/%s/%s/%x.bin", fdl_file->vendor_id, 106*71f7990aSMaciej Strozek product_vendor, product_sku, fdl_file->file_id); 107*71f7990aSMaciej Strozek } 108*71f7990aSMaciej Strozek 109*71f7990aSMaciej Strozek static int fdl_load_file(struct sdca_interrupt *interrupt, 110*71f7990aSMaciej Strozek struct sdca_fdl_set *set, int file_index) 111*71f7990aSMaciej Strozek { 112*71f7990aSMaciej Strozek struct device *dev = interrupt->dev; 113*71f7990aSMaciej Strozek struct sdca_fdl_data *fdl_data = &interrupt->function->fdl_data; 114*71f7990aSMaciej Strozek const struct firmware *firmware = NULL; 115*71f7990aSMaciej Strozek struct acpi_sw_file *swf = NULL, *tmp; 116*71f7990aSMaciej Strozek struct sdca_fdl_file *fdl_file; 117*71f7990aSMaciej Strozek char *disk_filename; 118*71f7990aSMaciej Strozek int ret; 119*71f7990aSMaciej Strozek int i; 120*71f7990aSMaciej Strozek 121*71f7990aSMaciej Strozek if (!set) { 122*71f7990aSMaciej Strozek dev_err(dev, "request to load SWF with no set\n"); 123*71f7990aSMaciej Strozek return -EINVAL; 124*71f7990aSMaciej Strozek } 125*71f7990aSMaciej Strozek 126*71f7990aSMaciej Strozek fdl_file = &set->files[file_index]; 127*71f7990aSMaciej Strozek 128*71f7990aSMaciej Strozek if (fdl_data->swft) { 129*71f7990aSMaciej Strozek tmp = fdl_data->swft->files; 130*71f7990aSMaciej Strozek for (i = 0; i < fdl_data->swft->header.length; i += tmp->file_length, 131*71f7990aSMaciej Strozek tmp = ACPI_ADD_PTR(struct acpi_sw_file, tmp, tmp->file_length)) { 132*71f7990aSMaciej Strozek if (tmp->vendor_id == fdl_file->vendor_id && 133*71f7990aSMaciej Strozek tmp->file_id == fdl_file->file_id) { 134*71f7990aSMaciej Strozek dev_dbg(dev, "located SWF in ACPI: %x-%x-%x\n", 135*71f7990aSMaciej Strozek tmp->vendor_id, tmp->file_id, 136*71f7990aSMaciej Strozek tmp->file_version); 137*71f7990aSMaciej Strozek swf = tmp; 138*71f7990aSMaciej Strozek break; 139*71f7990aSMaciej Strozek } 140*71f7990aSMaciej Strozek } 141*71f7990aSMaciej Strozek } 142*71f7990aSMaciej Strozek 143*71f7990aSMaciej Strozek disk_filename = fdl_get_sku_filename(dev, fdl_file); 144*71f7990aSMaciej Strozek if (!disk_filename) 145*71f7990aSMaciej Strozek return -ENOMEM; 146*71f7990aSMaciej Strozek 147*71f7990aSMaciej Strozek dev_dbg(dev, "FDL disk filename: %s\n", disk_filename); 148*71f7990aSMaciej Strozek 149*71f7990aSMaciej Strozek ret = firmware_request_nowarn(&firmware, disk_filename, dev); 150*71f7990aSMaciej Strozek kfree(disk_filename); 151*71f7990aSMaciej Strozek if (ret) { 152*71f7990aSMaciej Strozek disk_filename = kasprintf(GFP_KERNEL, "sdca/%x/%x.bin", 153*71f7990aSMaciej Strozek fdl_file->vendor_id, fdl_file->file_id); 154*71f7990aSMaciej Strozek if (!disk_filename) 155*71f7990aSMaciej Strozek return -ENOMEM; 156*71f7990aSMaciej Strozek 157*71f7990aSMaciej Strozek dev_dbg(dev, "FDL disk filename: %s\n", disk_filename); 158*71f7990aSMaciej Strozek 159*71f7990aSMaciej Strozek ret = firmware_request_nowarn(&firmware, disk_filename, dev); 160*71f7990aSMaciej Strozek kfree(disk_filename); 161*71f7990aSMaciej Strozek } 162*71f7990aSMaciej Strozek 163*71f7990aSMaciej Strozek if (!ret) { 164*71f7990aSMaciej Strozek tmp = (struct acpi_sw_file *)&firmware->data[0]; 165*71f7990aSMaciej Strozek 166*71f7990aSMaciej Strozek if (firmware->size < sizeof(*tmp) || 167*71f7990aSMaciej Strozek tmp->file_length != firmware->size) { 168*71f7990aSMaciej Strozek dev_err(dev, "bad disk SWF size\n"); 169*71f7990aSMaciej Strozek } else if (!swf || swf->file_version <= tmp->file_version) { 170*71f7990aSMaciej Strozek dev_dbg(dev, "using SWF from disk: %x-%x-%x\n", 171*71f7990aSMaciej Strozek tmp->vendor_id, tmp->file_id, tmp->file_version); 172*71f7990aSMaciej Strozek swf = tmp; 173*71f7990aSMaciej Strozek } 174*71f7990aSMaciej Strozek } 175*71f7990aSMaciej Strozek 176*71f7990aSMaciej Strozek if (!swf) { 177*71f7990aSMaciej Strozek dev_err(dev, "failed to locate SWF\n"); 178*71f7990aSMaciej Strozek return -ENOENT; 179*71f7990aSMaciej Strozek } 180*71f7990aSMaciej Strozek 181*71f7990aSMaciej Strozek ret = sdca_ump_write_message(dev, interrupt->device_regmap, 182*71f7990aSMaciej Strozek interrupt->function_regmap, 183*71f7990aSMaciej Strozek interrupt->function, interrupt->entity, 184*71f7990aSMaciej Strozek SDCA_CTL_XU_FDL_MESSAGEOFFSET, fdl_file->fdl_offset, 185*71f7990aSMaciej Strozek SDCA_CTL_XU_FDL_MESSAGELENGTH, swf->data, 186*71f7990aSMaciej Strozek swf->file_length - offsetof(struct acpi_sw_file, data)); 187*71f7990aSMaciej Strozek release_firmware(firmware); 188*71f7990aSMaciej Strozek return ret; 189*71f7990aSMaciej Strozek } 190*71f7990aSMaciej Strozek 191*71f7990aSMaciej Strozek static struct sdca_fdl_set *fdl_get_set(struct sdca_interrupt *interrupt) 192*71f7990aSMaciej Strozek { 193*71f7990aSMaciej Strozek struct device *dev = interrupt->dev; 194*71f7990aSMaciej Strozek struct sdca_fdl_data *fdl_data = &interrupt->function->fdl_data; 195*71f7990aSMaciej Strozek struct sdca_entity *xu = interrupt->entity; 196*71f7990aSMaciej Strozek struct sdca_control_range *range; 197*71f7990aSMaciej Strozek unsigned int val; 198*71f7990aSMaciej Strozek int i, ret; 199*71f7990aSMaciej Strozek 200*71f7990aSMaciej Strozek ret = regmap_read(interrupt->function_regmap, 201*71f7990aSMaciej Strozek SDW_SDCA_CTL(interrupt->function->desc->adr, xu->id, 202*71f7990aSMaciej Strozek SDCA_CTL_XU_FDL_SET_INDEX, 0), 203*71f7990aSMaciej Strozek &val); 204*71f7990aSMaciej Strozek if (ret < 0) { 205*71f7990aSMaciej Strozek dev_err(dev, "failed to read FDL set index: %d\n", ret); 206*71f7990aSMaciej Strozek return NULL; 207*71f7990aSMaciej Strozek } 208*71f7990aSMaciej Strozek 209*71f7990aSMaciej Strozek range = sdca_selector_find_range(dev, xu, SDCA_CTL_XU_FDL_SET_INDEX, 210*71f7990aSMaciej Strozek SDCA_FDL_SET_INDEX_NCOLS, 0); 211*71f7990aSMaciej Strozek 212*71f7990aSMaciej Strozek val = sdca_range_search(range, SDCA_FDL_SET_INDEX_SET_NUMBER, 213*71f7990aSMaciej Strozek val, SDCA_FDL_SET_INDEX_FILE_SET_ID); 214*71f7990aSMaciej Strozek 215*71f7990aSMaciej Strozek for (i = 0; i < fdl_data->num_sets; i++) { 216*71f7990aSMaciej Strozek if (fdl_data->sets[i].id == val) 217*71f7990aSMaciej Strozek return &fdl_data->sets[i]; 218*71f7990aSMaciej Strozek } 219*71f7990aSMaciej Strozek 220*71f7990aSMaciej Strozek dev_err(dev, "invalid fileset id: %d\n", val); 221*71f7990aSMaciej Strozek return NULL; 222*71f7990aSMaciej Strozek } 223*71f7990aSMaciej Strozek 224*71f7990aSMaciej Strozek static void fdl_end(struct sdca_interrupt *interrupt) 225*71f7990aSMaciej Strozek { 226*71f7990aSMaciej Strozek struct fdl_state *fdl_state = interrupt->priv; 227*71f7990aSMaciej Strozek 228*71f7990aSMaciej Strozek if (!fdl_state->set) 229*71f7990aSMaciej Strozek return; 230*71f7990aSMaciej Strozek 231*71f7990aSMaciej Strozek fdl_state->set = NULL; 232*71f7990aSMaciej Strozek 233*71f7990aSMaciej Strozek dev_dbg(interrupt->dev, "completed FDL process\n"); 234*71f7990aSMaciej Strozek } 235*71f7990aSMaciej Strozek 236*71f7990aSMaciej Strozek static int fdl_status_process(struct sdca_interrupt *interrupt, unsigned int status) 237*71f7990aSMaciej Strozek { 238*71f7990aSMaciej Strozek struct fdl_state *fdl_state = interrupt->priv; 239*71f7990aSMaciej Strozek int ret; 240*71f7990aSMaciej Strozek 241*71f7990aSMaciej Strozek switch (status) { 242*71f7990aSMaciej Strozek case SDCA_CTL_XU_FDLD_NEEDS_SET: 243*71f7990aSMaciej Strozek dev_dbg(interrupt->dev, "starting FDL process...\n"); 244*71f7990aSMaciej Strozek 245*71f7990aSMaciej Strozek fdl_state->file_index = 0; 246*71f7990aSMaciej Strozek fdl_state->set = fdl_get_set(interrupt); 247*71f7990aSMaciej Strozek fallthrough; 248*71f7990aSMaciej Strozek case SDCA_CTL_XU_FDLD_MORE_FILES_OK: 249*71f7990aSMaciej Strozek ret = fdl_load_file(interrupt, fdl_state->set, fdl_state->file_index); 250*71f7990aSMaciej Strozek if (ret) { 251*71f7990aSMaciej Strozek fdl_end(interrupt); 252*71f7990aSMaciej Strozek return SDCA_CTL_XU_FDLH_REQ_ABORT; 253*71f7990aSMaciej Strozek } 254*71f7990aSMaciej Strozek 255*71f7990aSMaciej Strozek return SDCA_CTL_XU_FDLH_FILE_AVAILABLE; 256*71f7990aSMaciej Strozek case SDCA_CTL_XU_FDLD_FILE_OK: 257*71f7990aSMaciej Strozek if (!fdl_state->set) { 258*71f7990aSMaciej Strozek fdl_end(interrupt); 259*71f7990aSMaciej Strozek return SDCA_CTL_XU_FDLH_REQ_ABORT; 260*71f7990aSMaciej Strozek } 261*71f7990aSMaciej Strozek 262*71f7990aSMaciej Strozek fdl_state->file_index++; 263*71f7990aSMaciej Strozek 264*71f7990aSMaciej Strozek if (fdl_state->file_index < fdl_state->set->num_files) 265*71f7990aSMaciej Strozek return SDCA_CTL_XU_FDLH_MORE_FILES; 266*71f7990aSMaciej Strozek fallthrough; 267*71f7990aSMaciej Strozek case SDCA_CTL_XU_FDLD_COMPLETE: 268*71f7990aSMaciej Strozek fdl_end(interrupt); 269*71f7990aSMaciej Strozek return SDCA_CTL_XU_FDLH_COMPLETE; 270*71f7990aSMaciej Strozek default: 271*71f7990aSMaciej Strozek fdl_end(interrupt); 272*71f7990aSMaciej Strozek 273*71f7990aSMaciej Strozek if (status & SDCA_CTL_XU_FDLD_REQ_RESET) 274*71f7990aSMaciej Strozek return SDCA_CTL_XU_FDLH_RESET_ACK; 275*71f7990aSMaciej Strozek else if (status & SDCA_CTL_XU_FDLD_REQ_ABORT) 276*71f7990aSMaciej Strozek return SDCA_CTL_XU_FDLH_COMPLETE; 277*71f7990aSMaciej Strozek 278*71f7990aSMaciej Strozek dev_err(interrupt->dev, "invalid FDL status: %x\n", status); 279*71f7990aSMaciej Strozek return -EINVAL; 280*71f7990aSMaciej Strozek } 281*71f7990aSMaciej Strozek } 282*71f7990aSMaciej Strozek 283*71f7990aSMaciej Strozek /** 284*71f7990aSMaciej Strozek * sdca_fdl_process - Process the FDL state machine 285*71f7990aSMaciej Strozek * @interrupt: SDCA interrupt structure 286*71f7990aSMaciej Strozek * 287*71f7990aSMaciej Strozek * Based on section 13.2.5 Flow Diagram for File Download, Host side. 288*71f7990aSMaciej Strozek * 289*71f7990aSMaciej Strozek * Return: Zero on success or a negative error code. 290*71f7990aSMaciej Strozek */ 291*71f7990aSMaciej Strozek int sdca_fdl_process(struct sdca_interrupt *interrupt) 292*71f7990aSMaciej Strozek { 293*71f7990aSMaciej Strozek struct device *dev = interrupt->dev; 294*71f7990aSMaciej Strozek struct sdca_entity_xu *xu = &interrupt->entity->xu; 295*71f7990aSMaciej Strozek unsigned int reg, status; 296*71f7990aSMaciej Strozek int response, ret; 297*71f7990aSMaciej Strozek 298*71f7990aSMaciej Strozek ret = sdca_ump_get_owner_host(dev, interrupt->function_regmap, 299*71f7990aSMaciej Strozek interrupt->function, interrupt->entity, 300*71f7990aSMaciej Strozek interrupt->control); 301*71f7990aSMaciej Strozek if (ret) 302*71f7990aSMaciej Strozek goto reset_function; 303*71f7990aSMaciej Strozek 304*71f7990aSMaciej Strozek reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id, 305*71f7990aSMaciej Strozek SDCA_CTL_XU_FDL_STATUS, 0); 306*71f7990aSMaciej Strozek ret = regmap_read(interrupt->function_regmap, reg, &status); 307*71f7990aSMaciej Strozek if (ret < 0) { 308*71f7990aSMaciej Strozek dev_err(dev, "failed to read FDL status: %d\n", ret); 309*71f7990aSMaciej Strozek return ret; 310*71f7990aSMaciej Strozek } 311*71f7990aSMaciej Strozek 312*71f7990aSMaciej Strozek dev_dbg(dev, "FDL status: %#x\n", status); 313*71f7990aSMaciej Strozek 314*71f7990aSMaciej Strozek ret = fdl_status_process(interrupt, status); 315*71f7990aSMaciej Strozek if (ret < 0) 316*71f7990aSMaciej Strozek goto reset_function; 317*71f7990aSMaciej Strozek 318*71f7990aSMaciej Strozek response = ret; 319*71f7990aSMaciej Strozek 320*71f7990aSMaciej Strozek dev_dbg(dev, "FDL response: %#x\n", response); 321*71f7990aSMaciej Strozek 322*71f7990aSMaciej Strozek ret = regmap_write(interrupt->function_regmap, reg, 323*71f7990aSMaciej Strozek response | (status & ~SDCA_CTL_XU_FDLH_MASK)); 324*71f7990aSMaciej Strozek if (ret < 0) { 325*71f7990aSMaciej Strozek dev_err(dev, "failed to set FDL status signal: %d\n", ret); 326*71f7990aSMaciej Strozek return ret; 327*71f7990aSMaciej Strozek } 328*71f7990aSMaciej Strozek 329*71f7990aSMaciej Strozek ret = sdca_ump_set_owner_device(dev, interrupt->function_regmap, 330*71f7990aSMaciej Strozek interrupt->function, interrupt->entity, 331*71f7990aSMaciej Strozek interrupt->control); 332*71f7990aSMaciej Strozek if (ret) 333*71f7990aSMaciej Strozek return ret; 334*71f7990aSMaciej Strozek 335*71f7990aSMaciej Strozek switch (response) { 336*71f7990aSMaciej Strozek case SDCA_CTL_XU_FDLH_RESET_ACK: 337*71f7990aSMaciej Strozek dev_dbg(dev, "FDL request reset\n"); 338*71f7990aSMaciej Strozek 339*71f7990aSMaciej Strozek switch (xu->reset_mechanism) { 340*71f7990aSMaciej Strozek default: 341*71f7990aSMaciej Strozek dev_warn(dev, "Requested reset mechanism not implemented\n"); 342*71f7990aSMaciej Strozek fallthrough; 343*71f7990aSMaciej Strozek case SDCA_XU_RESET_FUNCTION: 344*71f7990aSMaciej Strozek goto reset_function; 345*71f7990aSMaciej Strozek } 346*71f7990aSMaciej Strozek default: 347*71f7990aSMaciej Strozek return 0; 348*71f7990aSMaciej Strozek } 349*71f7990aSMaciej Strozek 350*71f7990aSMaciej Strozek reset_function: 351*71f7990aSMaciej Strozek sdca_reset_function(dev, interrupt->function, interrupt->function_regmap); 352*71f7990aSMaciej Strozek 353*71f7990aSMaciej Strozek return ret; 354*71f7990aSMaciej Strozek } 355*71f7990aSMaciej Strozek EXPORT_SYMBOL_NS_GPL(sdca_fdl_process, "SND_SOC_SDCA"); 356*71f7990aSMaciej Strozek 357*71f7990aSMaciej Strozek /** 358*71f7990aSMaciej Strozek * sdca_fdl_alloc_state - allocate state for an FDL interrupt 359*71f7990aSMaciej Strozek * @interrupt: SDCA interrupt structure. 360*71f7990aSMaciej Strozek * 361*71f7990aSMaciej Strozek * Return: Zero on success or a negative error code. 362*71f7990aSMaciej Strozek */ 363*71f7990aSMaciej Strozek int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt) 364*71f7990aSMaciej Strozek { 365*71f7990aSMaciej Strozek struct device *dev = interrupt->dev; 366*71f7990aSMaciej Strozek struct fdl_state *fdl_state; 367*71f7990aSMaciej Strozek 368*71f7990aSMaciej Strozek fdl_state = devm_kzalloc(dev, sizeof(struct fdl_state), GFP_KERNEL); 369*71f7990aSMaciej Strozek if (!fdl_state) 370*71f7990aSMaciej Strozek return -ENOMEM; 371*71f7990aSMaciej Strozek 372*71f7990aSMaciej Strozek interrupt->priv = fdl_state; 373*71f7990aSMaciej Strozek 374*71f7990aSMaciej Strozek return 0; 375*71f7990aSMaciej Strozek } 376*71f7990aSMaciej Strozek EXPORT_SYMBOL_NS_GPL(sdca_fdl_alloc_state, "SND_SOC_SDCA"); 377