xref: /linux/sound/soc/codecs/cs-amp-lib.c (revision af0bc3ac9a9e830cb52b718ecb237c4e76a466be)
1 // SPDX-License-Identifier: GPL-2.0-only
2 //
3 // Common code for Cirrus Logic Smart Amplifiers
4 //
5 // Copyright (C) 2024 Cirrus Logic, Inc. and
6 //               Cirrus Logic International Semiconductor Ltd.
7 
8 #include <asm/byteorder.h>
9 #include <kunit/static_stub.h>
10 #include <linux/debugfs.h>
11 #include <linux/dev_printk.h>
12 #include <linux/efi.h>
13 #include <linux/firmware/cirrus/cs_dsp.h>
14 #include <linux/math64.h>
15 #include <linux/module.h>
16 #include <linux/mutex.h>
17 #include <linux/overflow.h>
18 #include <linux/pci_ids.h>
19 #include <linux/slab.h>
20 #include <linux/timekeeping.h>
21 #include <linux/types.h>
22 #include <sound/cs-amp-lib.h>
23 
24 #define CIRRUS_LOGIC_CALIBRATION_EFI_NAME L"CirrusSmartAmpCalibrationData"
25 #define CIRRUS_LOGIC_CALIBRATION_EFI_GUID \
26 	EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3)
27 
28 #define LENOVO_SPEAKER_ID_EFI_NAME L"SdwSpeaker"
29 #define LENOVO_SPEAKER_ID_EFI_GUID \
30 	EFI_GUID(0x48df970e, 0xe27f, 0x460a, 0xb5, 0x86, 0x77, 0x19, 0x80, 0x1d, 0x92, 0x82)
31 
32 #define HP_SPEAKER_ID_EFI_NAME L"HPSpeakerID"
33 #define HP_SPEAKER_ID_EFI_GUID \
34 	EFI_GUID(0xc49593a4, 0xd099, 0x419b, 0xa2, 0xc3, 0x67, 0xe9, 0x80, 0xe6, 0x1d, 0x1e)
35 
36 #define HP_CALIBRATION_EFI_NAME L"SmartAmpCalibrationData"
37 #define HP_CALIBRATION_EFI_GUID \
38 	EFI_GUID(0x53559579, 0x8753, 0x4f5c, 0x91, 0x30, 0xe8, 0x2a, 0xcf, 0xb8, 0xd8, 0x93)
39 
40 #define DELL_SSIDEXV2_EFI_NAME L"SSIDexV2Data"
41 #define DELL_SSIDEXV2_EFI_GUID \
42 	EFI_GUID(0x6a5f35df, 0x1432, 0x4656, 0x85, 0x97, 0x31, 0x04, 0xd5, 0xbf, 0x3a, 0xb0)
43 
44 static const struct cs_amp_lib_cal_efivar {
45 	efi_char16_t *name;
46 	efi_guid_t *guid;
47 } cs_amp_lib_cal_efivars[] = {
48 	{
49 		.name = HP_CALIBRATION_EFI_NAME,
50 		.guid = &HP_CALIBRATION_EFI_GUID,
51 	},
52 	{
53 		.name = CIRRUS_LOGIC_CALIBRATION_EFI_NAME,
54 		.guid = &CIRRUS_LOGIC_CALIBRATION_EFI_GUID,
55 	},
56 };
57 
58 #define CS_AMP_CAL_DEFAULT_EFI_ATTR			\
59 		(EFI_VARIABLE_NON_VOLATILE |		\
60 		 EFI_VARIABLE_BOOTSERVICE_ACCESS |	\
61 		 EFI_VARIABLE_RUNTIME_ACCESS)
62 
63 /* Offset from Unix time to Windows time (100ns since 1 Jan 1601) */
64 #define UNIX_TIME_TO_WINDOWS_TIME_OFFSET	116444736000000000ULL
65 
66 static DEFINE_MUTEX(cs_amp_efi_cal_write_lock);
67 
68 static u64 cs_amp_time_now_in_windows_time(void)
69 {
70 	u64 time_in_100ns = div_u64(ktime_get_real_ns(), 100);
71 
72 	return time_in_100ns + UNIX_TIME_TO_WINDOWS_TIME_OFFSET;
73 }
74 
75 static int cs_amp_write_cal_coeff(struct cs_dsp *dsp,
76 				  const struct cirrus_amp_cal_controls *controls,
77 				  const char *ctl_name, u32 val)
78 {
79 	struct cs_dsp_coeff_ctl *cs_ctl;
80 	__be32 beval = cpu_to_be32(val);
81 	int ret;
82 
83 	KUNIT_STATIC_STUB_REDIRECT(cs_amp_write_cal_coeff, dsp, controls, ctl_name, val);
84 
85 	if (IS_REACHABLE(CONFIG_FW_CS_DSP)) {
86 		mutex_lock(&dsp->pwr_lock);
87 		cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, controls->mem_region, controls->alg_id);
88 		ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, &beval, sizeof(beval));
89 		mutex_unlock(&dsp->pwr_lock);
90 
91 		if (ret < 0) {
92 			dev_err(dsp->dev, "Failed to write to '%s': %d\n", ctl_name, ret);
93 			return ret;
94 		}
95 
96 		return 0;
97 	}
98 
99 	return -ENODEV;
100 }
101 
102 static int cs_amp_read_cal_coeff(struct cs_dsp *dsp,
103 				 const struct cirrus_amp_cal_controls *controls,
104 				 const char *ctl_name, u32 *val)
105 {
106 	struct cs_dsp_coeff_ctl *cs_ctl;
107 	__be32 beval;
108 	int ret;
109 
110 	KUNIT_STATIC_STUB_REDIRECT(cs_amp_read_cal_coeff, dsp, controls, ctl_name, val);
111 
112 	if (!IS_REACHABLE(CONFIG_FW_CS_DSP))
113 		return -ENODEV;
114 
115 	scoped_guard(mutex, &dsp->pwr_lock) {
116 		cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, controls->mem_region, controls->alg_id);
117 		ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, &beval, sizeof(beval));
118 	}
119 
120 	if (ret < 0) {
121 		dev_err(dsp->dev, "Failed to write to '%s': %d\n", ctl_name, ret);
122 		return ret;
123 	}
124 
125 	*val = be32_to_cpu(beval);
126 
127 	return 0;
128 }
129 
130 static int _cs_amp_write_cal_coeffs(struct cs_dsp *dsp,
131 				    const struct cirrus_amp_cal_controls *controls,
132 				    const struct cirrus_amp_cal_data *data)
133 {
134 	int ret;
135 
136 	dev_dbg(dsp->dev, "Calibration: Ambient=%#x, Status=%#x, CalR=%d\n",
137 		data->calAmbient, data->calStatus, data->calR);
138 
139 	if (list_empty(&dsp->ctl_list)) {
140 		dev_info(dsp->dev, "Calibration disabled due to missing firmware controls\n");
141 		return -ENOENT;
142 	}
143 
144 	ret = cs_amp_write_cal_coeff(dsp, controls, controls->ambient, data->calAmbient);
145 	if (ret)
146 		return ret;
147 
148 	ret = cs_amp_write_cal_coeff(dsp, controls, controls->calr, data->calR);
149 	if (ret)
150 		return ret;
151 
152 	ret = cs_amp_write_cal_coeff(dsp, controls, controls->status, data->calStatus);
153 	if (ret)
154 		return ret;
155 
156 	ret = cs_amp_write_cal_coeff(dsp, controls, controls->checksum, data->calR + 1);
157 	if (ret)
158 		return ret;
159 
160 	return 0;
161 }
162 
163 static int _cs_amp_read_cal_coeffs(struct cs_dsp *dsp,
164 				    const struct cirrus_amp_cal_controls *controls,
165 				    struct cirrus_amp_cal_data *data)
166 {
167 	u64 time;
168 	u32 val;
169 	int ret;
170 
171 	if (list_empty(&dsp->ctl_list)) {
172 		dev_info(dsp->dev, "Calibration disabled due to missing firmware controls\n");
173 		return -ENOENT;
174 	}
175 
176 	ret = cs_amp_read_cal_coeff(dsp, controls, controls->ambient, &val);
177 	if (ret)
178 		return ret;
179 
180 	data->calAmbient = (s8)val;
181 
182 	ret = cs_amp_read_cal_coeff(dsp, controls, controls->calr, &val);
183 	if (ret)
184 		return ret;
185 
186 	data->calR = (u16)val;
187 
188 	ret = cs_amp_read_cal_coeff(dsp, controls, controls->status, &val);
189 	if (ret)
190 		return ret;
191 
192 	data->calStatus = (u8)val;
193 
194 	/* Fill in timestamp */
195 	time = cs_amp_time_now_in_windows_time();
196 	data->calTime[0] = (u32)time;
197 	data->calTime[1] = (u32)(time >> 32);
198 
199 	return 0;
200 }
201 
202 /**
203  * cs_amp_write_cal_coeffs - Write calibration data to firmware controls.
204  * @dsp:	Pointer to struct cs_dsp.
205  * @controls:	Pointer to definition of firmware controls to be written.
206  * @data:	Pointer to calibration data.
207  *
208  * Returns: 0 on success, else negative error value.
209  */
210 int cs_amp_write_cal_coeffs(struct cs_dsp *dsp,
211 			    const struct cirrus_amp_cal_controls *controls,
212 			    const struct cirrus_amp_cal_data *data)
213 {
214 	if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS))
215 		return _cs_amp_write_cal_coeffs(dsp, controls, data);
216 	else
217 		return -ENODEV;
218 }
219 EXPORT_SYMBOL_NS_GPL(cs_amp_write_cal_coeffs, "SND_SOC_CS_AMP_LIB");
220 
221 /**
222  * cs_amp_read_cal_coeffs - Read calibration data from firmware controls.
223  * @dsp:	Pointer to struct cs_dsp.
224  * @controls:	Pointer to definition of firmware controls to be read.
225  * @data:	Pointer to calibration data where results will be written.
226  *
227  * Returns: 0 on success, else negative error value.
228  */
229 int cs_amp_read_cal_coeffs(struct cs_dsp *dsp,
230 			   const struct cirrus_amp_cal_controls *controls,
231 			   struct cirrus_amp_cal_data *data)
232 {
233 	if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS))
234 		return _cs_amp_read_cal_coeffs(dsp, controls, data);
235 	else
236 		return -ENODEV;
237 }
238 EXPORT_SYMBOL_NS_GPL(cs_amp_read_cal_coeffs, "SND_SOC_CS_AMP_LIB");
239 
240 /**
241  * cs_amp_write_ambient_temp - write value to calibration ambient temperature
242  * @dsp:	Pointer to struct cs_dsp.
243  * @controls:	Pointer to definition of firmware controls to be read.
244  * @temp:	Temperature in degrees celcius.
245  *
246  * Returns: 0 on success, else negative error value.
247  */
248 int cs_amp_write_ambient_temp(struct cs_dsp *dsp,
249 			      const struct cirrus_amp_cal_controls *controls,
250 			      u32 temp)
251 {
252 	return cs_amp_write_cal_coeff(dsp, controls, controls->ambient, temp);
253 }
254 EXPORT_SYMBOL_NS_GPL(cs_amp_write_ambient_temp, "SND_SOC_CS_AMP_LIB");
255 
256 static efi_status_t cs_amp_get_efi_variable(efi_char16_t *name,
257 					    efi_guid_t *guid,
258 					    u32 *returned_attr,
259 					    unsigned long *size,
260 					    void *buf)
261 {
262 	u32 attr;
263 
264 	if (!returned_attr)
265 		returned_attr = &attr;
266 
267 	KUNIT_STATIC_STUB_REDIRECT(cs_amp_get_efi_variable, name, guid,
268 				   returned_attr, size, buf);
269 
270 	if (efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
271 		return efi.get_variable(name, guid, returned_attr, size, buf);
272 
273 	return EFI_NOT_FOUND;
274 }
275 
276 static efi_status_t cs_amp_set_efi_variable(efi_char16_t *name,
277 					    efi_guid_t *guid,
278 					    u32 attr,
279 					    unsigned long size,
280 					    void *buf)
281 {
282 	KUNIT_STATIC_STUB_REDIRECT(cs_amp_set_efi_variable, name, guid, attr, size, buf);
283 
284 	if (!efi_rt_services_supported(EFI_RT_SUPPORTED_SET_VARIABLE))
285 		return EFI_NOT_FOUND;
286 
287 	return efi.set_variable(name, guid, attr, size, buf);
288 }
289 
290 static int cs_amp_convert_efi_status(efi_status_t status)
291 {
292 	switch (status) {
293 	case EFI_SUCCESS:
294 		return 0;
295 	case EFI_NOT_FOUND:
296 		return -ENOENT;
297 	case EFI_BUFFER_TOO_SMALL:
298 		return -EFBIG;
299 	case EFI_WRITE_PROTECTED:
300 	case EFI_UNSUPPORTED:
301 	case EFI_ACCESS_DENIED:
302 	case EFI_SECURITY_VIOLATION:
303 		return -EACCES;
304 	default:
305 		return -EIO;
306 	}
307 }
308 
309 static void *cs_amp_alloc_get_efi_variable(efi_char16_t *name,
310 					   efi_guid_t *guid,
311 					   u32 *returned_attr)
312 {
313 	efi_status_t status;
314 	unsigned long size = 0;
315 
316 	status = cs_amp_get_efi_variable(name, guid, NULL, &size, NULL);
317 	if (status != EFI_BUFFER_TOO_SMALL)
318 		return ERR_PTR(cs_amp_convert_efi_status(status));
319 
320 	/* Over-alloc to ensure strings are always NUL-terminated */
321 	void *buf __free(kfree) = kzalloc(size + 1, GFP_KERNEL);
322 	if (!buf)
323 		return ERR_PTR(-ENOMEM);
324 
325 	status = cs_amp_get_efi_variable(name, guid, returned_attr, &size, buf);
326 	if (status != EFI_SUCCESS)
327 		return ERR_PTR(cs_amp_convert_efi_status(status));
328 
329 	return_ptr(buf);
330 }
331 
332 static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev,
333 							     efi_char16_t **name,
334 							     efi_guid_t **guid,
335 							     u32 *attr)
336 {
337 	struct cirrus_amp_efi_data *efi_data;
338 	unsigned long data_size = 0;
339 	u8 *data;
340 	efi_status_t status;
341 	int i, ret;
342 
343 	/* Find EFI variable and get size */
344 	for (i = 0; i < ARRAY_SIZE(cs_amp_lib_cal_efivars); i++) {
345 		status = cs_amp_get_efi_variable(cs_amp_lib_cal_efivars[i].name,
346 						 cs_amp_lib_cal_efivars[i].guid,
347 						 attr, &data_size, NULL);
348 		if (status == EFI_BUFFER_TOO_SMALL)
349 			break;
350 	}
351 
352 	if (status != EFI_BUFFER_TOO_SMALL)
353 		return ERR_PTR(-ENOENT);
354 
355 	if (name)
356 		*name = cs_amp_lib_cal_efivars[i].name;
357 
358 	if (guid)
359 		*guid = cs_amp_lib_cal_efivars[i].guid;
360 
361 	if (data_size < sizeof(*efi_data)) {
362 		dev_err(dev, "EFI cal variable truncated\n");
363 		return ERR_PTR(-EOVERFLOW);
364 	}
365 
366 	/* Get variable contents into buffer */
367 	data = kmalloc(data_size, GFP_KERNEL);
368 	if (!data)
369 		return ERR_PTR(-ENOMEM);
370 
371 	status = cs_amp_get_efi_variable(cs_amp_lib_cal_efivars[i].name,
372 					 cs_amp_lib_cal_efivars[i].guid,
373 					 attr, &data_size, data);
374 	if (status != EFI_SUCCESS) {
375 		ret = -EINVAL;
376 		goto err;
377 	}
378 
379 	efi_data = (struct cirrus_amp_efi_data *)data;
380 	dev_dbg(dev, "Calibration: Size=%d, Amp Count=%d\n", efi_data->size, efi_data->count);
381 
382 	if ((efi_data->count > 128) ||
383 	    struct_size(efi_data, data, efi_data->count) > data_size) {
384 		dev_err(dev, "EFI cal variable truncated\n");
385 		ret = -EOVERFLOW;
386 		goto err;
387 	}
388 
389 	/* This could be zero-filled space pre-allocated by the BIOS */
390 	if (efi_data->size == 0)
391 		efi_data->size = data_size;
392 
393 	return efi_data;
394 
395 err:
396 	kfree(data);
397 	dev_err(dev, "Failed to read calibration data from EFI: %d\n", ret);
398 
399 	return ERR_PTR(ret);
400 }
401 
402 static int cs_amp_set_cal_efi_buffer(struct device *dev,
403 				     efi_char16_t *name,
404 				     efi_guid_t *guid,
405 				     u32 attr,
406 				     struct cirrus_amp_efi_data *data)
407 {
408 	efi_status_t status;
409 
410 	status = cs_amp_set_efi_variable(name, guid, attr,
411 					 struct_size(data, data, data->count), data);
412 
413 	return cs_amp_convert_efi_status(status);
414 }
415 
416 static int _cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index,
417 					    struct cirrus_amp_cal_data *out_data)
418 {
419 	struct cirrus_amp_efi_data *efi_data;
420 	struct cirrus_amp_cal_data *cal = NULL;
421 	int i, ret;
422 
423 	efi_data = cs_amp_get_cal_efi_buffer(dev, NULL, NULL, NULL);
424 	if (IS_ERR(efi_data))
425 		return PTR_ERR(efi_data);
426 
427 	if (target_uid) {
428 		for (i = 0; i < efi_data->count; ++i) {
429 			u64 cal_target = cs_amp_cal_target_u64(&efi_data->data[i]);
430 
431 			/* Skip empty entries */
432 			if (!efi_data->data[i].calTime[0] && !efi_data->data[i].calTime[1])
433 				continue;
434 
435 			/* Skip entries with unpopulated silicon ID */
436 			if (cal_target == 0)
437 				continue;
438 
439 			if (cal_target == target_uid) {
440 				cal = &efi_data->data[i];
441 				break;
442 			}
443 		}
444 	}
445 
446 	if (!cal && (amp_index >= 0) && (amp_index < efi_data->count) &&
447 	    (efi_data->data[amp_index].calTime[0] || efi_data->data[amp_index].calTime[1])) {
448 		u64 cal_target = cs_amp_cal_target_u64(&efi_data->data[amp_index]);
449 
450 		/*
451 		 * Treat unpopulated cal_target as a wildcard.
452 		 * If target_uid != 0 we can only get here if cal_target == 0
453 		 * or it didn't match any cal_target value.
454 		 * If target_uid == 0 it is a wildcard.
455 		 */
456 		if ((cal_target == 0) || (target_uid == 0))
457 			cal = &efi_data->data[amp_index];
458 		else
459 			dev_warn(dev, "Calibration entry %d does not match silicon ID", amp_index);
460 	}
461 
462 	if (cal) {
463 		memcpy(out_data, cal, sizeof(*out_data));
464 		ret = 0;
465 	} else {
466 		dev_warn(dev, "No calibration for silicon ID %#llx\n", target_uid);
467 		ret = -ENOENT;
468 	}
469 
470 	kfree(efi_data);
471 
472 	return ret;
473 }
474 
475 static int _cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, int num_amps,
476 					    const struct cirrus_amp_cal_data *in_data)
477 {
478 	u64 cal_target = cs_amp_cal_target_u64(in_data);
479 	unsigned long num_entries;
480 	struct cirrus_amp_efi_data *data;
481 	efi_char16_t *name = CIRRUS_LOGIC_CALIBRATION_EFI_NAME;
482 	efi_guid_t *guid = &CIRRUS_LOGIC_CALIBRATION_EFI_GUID;
483 	u32 attr = CS_AMP_CAL_DEFAULT_EFI_ATTR;
484 	int i, ret;
485 
486 	if (cal_target == 0)
487 		return -EINVAL;
488 
489 	data = cs_amp_get_cal_efi_buffer(dev, &name, &guid, &attr);
490 	ret = PTR_ERR_OR_ZERO(data);
491 	if (ret == -ENOENT) {
492 		data = NULL;
493 		goto alloc_new;
494 	} else if (ret) {
495 		return ret;
496 	}
497 
498 	/*
499 	 * If the EFI variable is just zero-filled reserved space the count
500 	 * must be set.
501 	 */
502 	if (data->count == 0)
503 		data->count = (data->size - sizeof(data)) / sizeof(data->data[0]);
504 
505 	if (amp_index < 0) {
506 		/* Is there already a slot for this target? */
507 		for (amp_index = 0; amp_index < data->count; amp_index++) {
508 			if (cs_amp_cal_target_u64(&data->data[amp_index]) == cal_target)
509 				break;
510 		}
511 
512 		/* Else find an empty slot */
513 		if (amp_index >= data->count) {
514 			for (amp_index = 0; amp_index < data->count; amp_index++) {
515 				if ((data->data[amp_index].calTime[0] == 0) &&
516 				    (data->data[amp_index].calTime[1] == 0))
517 					break;
518 			}
519 		}
520 	} else {
521 		/*
522 		 * If the index is forced there could be another active
523 		 * slot with the same calTarget. So deduplicate.
524 		 */
525 		for (i = 0; i < data->count; i++) {
526 			if (i == amp_index)
527 				continue;
528 
529 			if ((data->data[i].calTime[0] == 0) && (data->data[i].calTime[1] == 0))
530 				continue;
531 
532 			if (cs_amp_cal_target_u64(&data->data[i]) == cal_target)
533 				memset(data->data[i].calTime, 0, sizeof(data->data[i].calTime));
534 		}
535 	}
536 
537 alloc_new:
538 	if (amp_index < 0)
539 		amp_index = 0;
540 
541 	num_entries = max(num_amps, amp_index + 1);
542 	if (!data || (data->count < num_entries)) {
543 		struct cirrus_amp_efi_data *new_data;
544 		unsigned int new_data_size = struct_size(data, data, num_entries);
545 
546 		new_data = kzalloc(new_data_size, GFP_KERNEL);
547 		if (!new_data) {
548 			ret = -ENOMEM;
549 			goto err;
550 		}
551 
552 		if (data) {
553 			memcpy(new_data, data, struct_size(data, data, data->count));
554 			kfree(data);
555 		}
556 
557 		data = new_data;
558 		data->count = num_entries;
559 		data->size = new_data_size;
560 	}
561 
562 	data->data[amp_index] = *in_data;
563 	ret = cs_amp_set_cal_efi_buffer(dev, name, guid, attr, data);
564 	if (ret)
565 		dev_err(dev, "Failed writing calibration to EFI: %d\n", ret);
566 err:
567 	kfree(data);
568 
569 	return ret;
570 }
571 
572 /**
573  * cs_amp_get_efi_calibration_data - get an entry from calibration data in EFI.
574  * @dev:	struct device of the caller.
575  * @target_uid:	UID to match, or zero to ignore UID matching.
576  * @amp_index:	Entry index to use, or -1 to prevent lookup by index.
577  * @out_data:	struct cirrus_amp_cal_data where the entry will be copied.
578  *
579  * This function can perform 3 types of lookup:
580  *
581  * (target_uid > 0, amp_index >= 0)
582  *	UID search with fallback to using the array index.
583  *	Search the calibration data for a non-zero calTarget that matches
584  *	target_uid, and if found return that entry. Else, if the entry at
585  *	[amp_index] has calTarget == 0, return that entry. Else fail.
586  *
587  * (target_uid > 0, amp_index < 0)
588  *	UID search only.
589  *	Search the calibration data for a non-zero calTarget that matches
590  *	target_uid, and if found return that entry. Else fail.
591  *
592  * (target_uid == 0, amp_index >= 0)
593  *	Array index fetch only.
594  *	Return the entry at [amp_index].
595  *
596  * An array lookup will be skipped if amp_index exceeds the number of
597  * entries in the calibration array, and in this case the return will
598  * be -ENOENT. An out-of-range amp_index does not prevent matching by
599  * target_uid - it has the same effect as passing amp_index < 0.
600  *
601  * If the EFI data is too short to be a valid entry, or the entry count
602  * in the EFI data overflows the actual length of the data, this function
603  * returns -EOVERFLOW.
604  *
605  * Return: 0 if the entry was found, -ENOENT if no entry was found,
606  *	   -EOVERFLOW if the EFI file is corrupt, else other error value.
607  */
608 int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index,
609 				    struct cirrus_amp_cal_data *out_data)
610 {
611 	if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS))
612 		return _cs_amp_get_efi_calibration_data(dev, target_uid, amp_index, out_data);
613 	else
614 		return -ENOENT;
615 }
616 EXPORT_SYMBOL_NS_GPL(cs_amp_get_efi_calibration_data, "SND_SOC_CS_AMP_LIB");
617 
618 /**
619  * cs_amp_set_efi_calibration_data - write a calibration data entry to EFI.
620  * @dev:	struct device of the caller.
621  * @amp_index:	Entry index to use, or -1 to use any available slot.
622  * @num_amps:	Maximum number of amps to reserve slots for, or -1 to ignore.
623  * @in_data:	struct cirrus_amp_cal_data entry to be written to EFI.
624  *
625  * If a Vendor-specific variable exists it will be updated,
626  * else if the Cirrus variable exists it will be updated
627  * else the Cirrus variable will be created.
628  *
629  * If amp_index >= 0 the data will be placed in this entry of the calibration
630  * data array, overwriting what was in that entry. Any other entries with the
631  * same calTarget will be marked empty.
632  *
633  * If amp_index < 0 and in_data->calTarget matches any existing entry, that
634  * entry will be overwritten. Else the first available free entry will be used,
635  * extending the size of the EFI variable if there are no free entries.
636  *
637  * If num_amps > 0 the EFI variable will be sized to contain at least this
638  * many calibration entries, with any new entries marked empty.
639  *
640  * Return: 0 if the write was successful, -EFBIG if space could not be made in
641  *	   the EFI file to add the entry, -EACCES if it was not possible to
642  *	   read or write the EFI variable.
643  */
644 int cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, int num_amps,
645 				    const struct cirrus_amp_cal_data *in_data)
646 {
647 	if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS)) {
648 		scoped_guard(mutex, &cs_amp_efi_cal_write_lock) {
649 			return _cs_amp_set_efi_calibration_data(dev, amp_index,
650 								num_amps, in_data);
651 		}
652 	}
653 
654 	return -ENOENT;
655 }
656 EXPORT_SYMBOL_NS_GPL(cs_amp_set_efi_calibration_data, "SND_SOC_CS_AMP_LIB");
657 
658 struct cs_amp_spkid_efi {
659 	efi_char16_t *name;
660 	efi_guid_t *guid;
661 	u8 values[2];
662 };
663 
664 static int cs_amp_get_efi_byte_spkid(struct device *dev, const struct cs_amp_spkid_efi *info)
665 {
666 	efi_status_t status;
667 	unsigned long size;
668 	u8 spkid;
669 	int i, ret;
670 
671 	size = sizeof(spkid);
672 	status = cs_amp_get_efi_variable(info->name, info->guid, NULL, &size, &spkid);
673 	ret = cs_amp_convert_efi_status(status);
674 	if (ret < 0)
675 		return ret;
676 
677 	if (size == 0)
678 		return -ENOENT;
679 
680 	for (i = 0; i < ARRAY_SIZE(info->values); i++) {
681 		if (info->values[i] == spkid)
682 			return i;
683 	}
684 
685 	dev_err(dev, "EFI speaker ID bad value %#x\n", spkid);
686 
687 	return -EINVAL;
688 }
689 
690 static const struct cs_amp_spkid_efi cs_amp_spkid_byte_types[] = {
691 	{
692 		.name = LENOVO_SPEAKER_ID_EFI_NAME,
693 		.guid = &LENOVO_SPEAKER_ID_EFI_GUID,
694 		.values = { 0xd0, 0xd1 },
695 	},
696 	{
697 		.name = HP_SPEAKER_ID_EFI_NAME,
698 		.guid = &HP_SPEAKER_ID_EFI_GUID,
699 		.values = { 0x30, 0x31 },
700 	},
701 };
702 
703 /**
704  * cs_amp_get_vendor_spkid - get a speaker ID from vendor-specific storage
705  * @dev:	pointer to struct device
706  *
707  * Known vendor-specific methods of speaker ID are checked and if one is
708  * found its speaker ID value is returned.
709  *
710  * Return: >=0 is a valid speaker ID. -ENOENT if a vendor-specific method
711  *	   was not found. -EACCES if the vendor-specific storage could not
712  *	   be read. Other error values indicate that the data from the
713  *	   vendor-specific storage was found but could not be understood.
714  */
715 int cs_amp_get_vendor_spkid(struct device *dev)
716 {
717 	int i, ret;
718 
719 	if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE) &&
720 	    !IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS))
721 		return -ENOENT;
722 
723 	for (i = 0; i < ARRAY_SIZE(cs_amp_spkid_byte_types); i++) {
724 		ret = cs_amp_get_efi_byte_spkid(dev, &cs_amp_spkid_byte_types[i]);
725 		if (ret != -ENOENT)
726 			return ret;
727 	}
728 
729 	return -ENOENT;
730 }
731 EXPORT_SYMBOL_NS_GPL(cs_amp_get_vendor_spkid, "SND_SOC_CS_AMP_LIB");
732 
733 static const char *cs_amp_devm_get_dell_ssidex(struct device *dev,
734 					       int ssid_vendor, int ssid_device)
735 {
736 	unsigned int hex_prefix;
737 	char audio_id[4];
738 	char delim;
739 	char *p;
740 	int ret;
741 
742 	if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE) &&
743 	    !IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS))
744 		return ERR_PTR(-ENOENT);
745 
746 	char *ssidex_buf __free(kfree) = cs_amp_alloc_get_efi_variable(DELL_SSIDEXV2_EFI_NAME,
747 								       &DELL_SSIDEXV2_EFI_GUID,
748 								       NULL);
749 	ret = PTR_ERR_OR_ZERO(ssidex_buf);
750 	if (ret == -ENOENT)
751 		return ERR_PTR(-ENOENT);
752 	else if (ret < 0)
753 		return ssidex_buf;
754 
755 	/*
756 	 * SSIDExV2 string is a series of underscore delimited fields.
757 	 * First field is all or part of the SSID. Second field should be
758 	 * a 2-character audio hardware id, followed by other identifiers.
759 	 * Older models did not have the 2-character audio id, so reject
760 	 * the string if the second field is not 2 characters.
761 	 */
762 	ret = sscanf(ssidex_buf, "%8x_%2s%c", &hex_prefix, audio_id, &delim);
763 	if (ret < 2)
764 		return ERR_PTR(-ENOENT);
765 
766 	if ((ret == 3) && (delim != '_'))
767 		return ERR_PTR(-ENOENT);
768 
769 	if (strlen(audio_id) != 2)
770 		return ERR_PTR(-ENOENT);
771 
772 	p = devm_kstrdup(dev, audio_id, GFP_KERNEL);
773 	if (!p)
774 		return ERR_PTR(-ENOMEM);
775 
776 	return p;
777 }
778 
779 /**
780  * cs_amp_devm_get_vendor_specific_variant_id - get variant ID string
781  * @dev:	 pointer to struct device
782  * @ssid_vendor: PCI Subsystem Vendor (-1 if unknown)
783  * @ssid_device: PCI Subsystem Device (-1 if unknown)
784  *
785  * Known vendor-specific hardware identifiers are checked and if one is
786  * found its content is returned as a NUL-terminated string. The returned
787  * string is devm-managed.
788  *
789  * The returned string is not guaranteed to be globally unique.
790  * Generally it should be combined with some other qualifier, such as
791  * PCI SSID, to create a globally unique ID.
792  *
793  * If the caller has a PCI SSID it should pass it in @ssid_vendor and
794  * @ssid_device. If the vendor-spefic ID contains this SSID it will be
795  * stripped from the returned string to prevent duplication.
796  *
797  * If the caller does not have a PCI SSID, pass -1 for @ssid_vendor and
798  * @ssid_device.
799  *
800  * Return:
801  * * a pointer to a devm-managed string
802  * * ERR_PTR(-ENOENT) if no vendor-specific qualifier
803  * * ERR_PTR error value
804  */
805 const char *cs_amp_devm_get_vendor_specific_variant_id(struct device *dev,
806 						       int ssid_vendor,
807 						       int ssid_device)
808 {
809 	KUNIT_STATIC_STUB_REDIRECT(cs_amp_devm_get_vendor_specific_variant_id,
810 				   dev, ssid_vendor, ssid_device);
811 
812 	if ((ssid_vendor == PCI_VENDOR_ID_DELL) || (ssid_vendor < 0))
813 		return cs_amp_devm_get_dell_ssidex(dev, ssid_vendor, ssid_device);
814 
815 	return ERR_PTR(-ENOENT);
816 }
817 EXPORT_SYMBOL_NS_GPL(cs_amp_devm_get_vendor_specific_variant_id, "SND_SOC_CS_AMP_LIB");
818 
819 /**
820  * cs_amp_create_debugfs - create a debugfs directory for a device
821  *
822  * @dev: pointer to struct device
823  *
824  * Creates a node under "cirrus_logic" in the root of the debugfs filesystem.
825  * This is for Cirrus-specific debugfs functionality to be grouped in a
826  * defined way, independently of the debugfs provided by ALSA/ASoC.
827  * The general ALSA/ASoC debugfs may not be enabled, and does not necessarily
828  * have a stable layout or naming convention.
829  *
830  * Return: Pointer to the dentry for the created directory, or -ENODEV.
831  */
832 struct dentry *cs_amp_create_debugfs(struct device *dev)
833 {
834 	struct dentry *dir;
835 
836 	dir = debugfs_lookup("cirrus_logic", NULL);
837 	if (!dir)
838 		dir = debugfs_create_dir("cirrus_logic", NULL);
839 
840 	return debugfs_create_dir(dev_name(dev), dir);
841 }
842 EXPORT_SYMBOL_NS_GPL(cs_amp_create_debugfs, "SND_SOC_CS_AMP_LIB");
843 
844 static const struct cs_amp_test_hooks cs_amp_test_hook_ptrs = {
845 	.get_efi_variable = cs_amp_get_efi_variable,
846 	.set_efi_variable = cs_amp_set_efi_variable,
847 	.write_cal_coeff = cs_amp_write_cal_coeff,
848 	.read_cal_coeff = cs_amp_read_cal_coeff,
849 };
850 
851 const struct cs_amp_test_hooks * const cs_amp_test_hooks =
852 	PTR_IF(IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS), &cs_amp_test_hook_ptrs);
853 EXPORT_SYMBOL_NS_GPL(cs_amp_test_hooks, "SND_SOC_CS_AMP_LIB");
854 
855 MODULE_DESCRIPTION("Cirrus Logic amplifier library");
856 MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
857 MODULE_LICENSE("GPL");
858 MODULE_IMPORT_NS("FW_CS_DSP");
859