xref: /linux/sound/soc/sdca/sdca_fdl.c (revision 71f7990a34cdb11f82d3cbbcddaca77a55635466)
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