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