xref: /linux/sound/soc/codecs/cs-amp-lib.c (revision 509d3f45847627f4c5cdce004c3ec79262b5239c)
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/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 static const struct cs_amp_lib_cal_efivar {
41 	efi_char16_t *name;
42 	efi_guid_t *guid;
43 } cs_amp_lib_cal_efivars[] = {
44 	{
45 		.name = HP_CALIBRATION_EFI_NAME,
46 		.guid = &HP_CALIBRATION_EFI_GUID,
47 	},
48 	{
49 		.name = CIRRUS_LOGIC_CALIBRATION_EFI_NAME,
50 		.guid = &CIRRUS_LOGIC_CALIBRATION_EFI_GUID,
51 	},
52 };
53 
54 #define CS_AMP_CAL_DEFAULT_EFI_ATTR			\
55 		(EFI_VARIABLE_NON_VOLATILE |		\
56 		 EFI_VARIABLE_BOOTSERVICE_ACCESS |	\
57 		 EFI_VARIABLE_RUNTIME_ACCESS)
58 
59 /* Offset from Unix time to Windows time (100ns since 1 Jan 1601) */
60 #define UNIX_TIME_TO_WINDOWS_TIME_OFFSET	116444736000000000ULL
61 
62 static DEFINE_MUTEX(cs_amp_efi_cal_write_lock);
63 
64 static u64 cs_amp_time_now_in_windows_time(void)
65 {
66 	u64 time_in_100ns = div_u64(ktime_get_real_ns(), 100);
67 
68 	return time_in_100ns + UNIX_TIME_TO_WINDOWS_TIME_OFFSET;
69 }
70 
71 static int cs_amp_write_cal_coeff(struct cs_dsp *dsp,
72 				  const struct cirrus_amp_cal_controls *controls,
73 				  const char *ctl_name, u32 val)
74 {
75 	struct cs_dsp_coeff_ctl *cs_ctl;
76 	__be32 beval = cpu_to_be32(val);
77 	int ret;
78 
79 	KUNIT_STATIC_STUB_REDIRECT(cs_amp_write_cal_coeff, dsp, controls, ctl_name, val);
80 
81 	if (IS_REACHABLE(CONFIG_FW_CS_DSP)) {
82 		mutex_lock(&dsp->pwr_lock);
83 		cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, controls->mem_region, controls->alg_id);
84 		ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, &beval, sizeof(beval));
85 		mutex_unlock(&dsp->pwr_lock);
86 
87 		if (ret < 0) {
88 			dev_err(dsp->dev, "Failed to write to '%s': %d\n", ctl_name, ret);
89 			return ret;
90 		}
91 
92 		return 0;
93 	}
94 
95 	return -ENODEV;
96 }
97 
98 static int cs_amp_read_cal_coeff(struct cs_dsp *dsp,
99 				 const struct cirrus_amp_cal_controls *controls,
100 				 const char *ctl_name, u32 *val)
101 {
102 	struct cs_dsp_coeff_ctl *cs_ctl;
103 	__be32 beval;
104 	int ret;
105 
106 	KUNIT_STATIC_STUB_REDIRECT(cs_amp_read_cal_coeff, dsp, controls, ctl_name, val);
107 
108 	if (!IS_REACHABLE(CONFIG_FW_CS_DSP))
109 		return -ENODEV;
110 
111 	scoped_guard(mutex, &dsp->pwr_lock) {
112 		cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, controls->mem_region, controls->alg_id);
113 		ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, &beval, sizeof(beval));
114 	}
115 
116 	if (ret < 0) {
117 		dev_err(dsp->dev, "Failed to write to '%s': %d\n", ctl_name, ret);
118 		return ret;
119 	}
120 
121 	*val = be32_to_cpu(beval);
122 
123 	return 0;
124 }
125 
126 static int _cs_amp_write_cal_coeffs(struct cs_dsp *dsp,
127 				    const struct cirrus_amp_cal_controls *controls,
128 				    const struct cirrus_amp_cal_data *data)
129 {
130 	int ret;
131 
132 	dev_dbg(dsp->dev, "Calibration: Ambient=%#x, Status=%#x, CalR=%d\n",
133 		data->calAmbient, data->calStatus, data->calR);
134 
135 	if (list_empty(&dsp->ctl_list)) {
136 		dev_info(dsp->dev, "Calibration disabled due to missing firmware controls\n");
137 		return -ENOENT;
138 	}
139 
140 	ret = cs_amp_write_cal_coeff(dsp, controls, controls->ambient, data->calAmbient);
141 	if (ret)
142 		return ret;
143 
144 	ret = cs_amp_write_cal_coeff(dsp, controls, controls->calr, data->calR);
145 	if (ret)
146 		return ret;
147 
148 	ret = cs_amp_write_cal_coeff(dsp, controls, controls->status, data->calStatus);
149 	if (ret)
150 		return ret;
151 
152 	ret = cs_amp_write_cal_coeff(dsp, controls, controls->checksum, data->calR + 1);
153 	if (ret)
154 		return ret;
155 
156 	return 0;
157 }
158 
159 static int _cs_amp_read_cal_coeffs(struct cs_dsp *dsp,
160 				    const struct cirrus_amp_cal_controls *controls,
161 				    struct cirrus_amp_cal_data *data)
162 {
163 	u64 time;
164 	u32 val;
165 	int ret;
166 
167 	if (list_empty(&dsp->ctl_list)) {
168 		dev_info(dsp->dev, "Calibration disabled due to missing firmware controls\n");
169 		return -ENOENT;
170 	}
171 
172 	ret = cs_amp_read_cal_coeff(dsp, controls, controls->ambient, &val);
173 	if (ret)
174 		return ret;
175 
176 	data->calAmbient = (s8)val;
177 
178 	ret = cs_amp_read_cal_coeff(dsp, controls, controls->calr, &val);
179 	if (ret)
180 		return ret;
181 
182 	data->calR = (u16)val;
183 
184 	ret = cs_amp_read_cal_coeff(dsp, controls, controls->status, &val);
185 	if (ret)
186 		return ret;
187 
188 	data->calStatus = (u8)val;
189 
190 	/* Fill in timestamp */
191 	time = cs_amp_time_now_in_windows_time();
192 	data->calTime[0] = (u32)time;
193 	data->calTime[1] = (u32)(time >> 32);
194 
195 	return 0;
196 }
197 
198 /**
199  * cs_amp_write_cal_coeffs - Write calibration data to firmware controls.
200  * @dsp:	Pointer to struct cs_dsp.
201  * @controls:	Pointer to definition of firmware controls to be written.
202  * @data:	Pointer to calibration data.
203  *
204  * Returns: 0 on success, else negative error value.
205  */
206 int cs_amp_write_cal_coeffs(struct cs_dsp *dsp,
207 			    const struct cirrus_amp_cal_controls *controls,
208 			    const struct cirrus_amp_cal_data *data)
209 {
210 	if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
211 		return _cs_amp_write_cal_coeffs(dsp, controls, data);
212 	else
213 		return -ENODEV;
214 }
215 EXPORT_SYMBOL_NS_GPL(cs_amp_write_cal_coeffs, "SND_SOC_CS_AMP_LIB");
216 
217 /**
218  * cs_amp_read_cal_coeffs - Read calibration data from firmware controls.
219  * @dsp:	Pointer to struct cs_dsp.
220  * @controls:	Pointer to definition of firmware controls to be read.
221  * @data:	Pointer to calibration data where results will be written.
222  *
223  * Returns: 0 on success, else negative error value.
224  */
225 int cs_amp_read_cal_coeffs(struct cs_dsp *dsp,
226 			   const struct cirrus_amp_cal_controls *controls,
227 			   struct cirrus_amp_cal_data *data)
228 {
229 	if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
230 		return _cs_amp_read_cal_coeffs(dsp, controls, data);
231 	else
232 		return -ENODEV;
233 }
234 EXPORT_SYMBOL_NS_GPL(cs_amp_read_cal_coeffs, "SND_SOC_CS_AMP_LIB");
235 
236 /**
237  * cs_amp_write_ambient_temp - write value to calibration ambient temperature
238  * @dsp:	Pointer to struct cs_dsp.
239  * @controls:	Pointer to definition of firmware controls to be read.
240  * @temp:	Temperature in degrees celcius.
241  *
242  * Returns: 0 on success, else negative error value.
243  */
244 int cs_amp_write_ambient_temp(struct cs_dsp *dsp,
245 			      const struct cirrus_amp_cal_controls *controls,
246 			      u32 temp)
247 {
248 	if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
249 		return cs_amp_write_cal_coeff(dsp, controls, controls->ambient, temp);
250 	else
251 		return -ENODEV;
252 }
253 EXPORT_SYMBOL_NS_GPL(cs_amp_write_ambient_temp, "SND_SOC_CS_AMP_LIB");
254 
255 static efi_status_t cs_amp_get_efi_variable(efi_char16_t *name,
256 					    efi_guid_t *guid,
257 					    u32 *returned_attr,
258 					    unsigned long *size,
259 					    void *buf)
260 {
261 	u32 attr;
262 
263 	if (!returned_attr)
264 		returned_attr = &attr;
265 
266 	KUNIT_STATIC_STUB_REDIRECT(cs_amp_get_efi_variable, name, guid,
267 				   returned_attr, size, buf);
268 
269 	if (efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
270 		return efi.get_variable(name, guid, returned_attr, size, buf);
271 
272 	return EFI_NOT_FOUND;
273 }
274 
275 static efi_status_t cs_amp_set_efi_variable(efi_char16_t *name,
276 					    efi_guid_t *guid,
277 					    u32 attr,
278 					    unsigned long size,
279 					    void *buf)
280 {
281 	KUNIT_STATIC_STUB_REDIRECT(cs_amp_set_efi_variable, name, guid, attr, size, buf);
282 
283 	if (!efi_rt_services_supported(EFI_RT_SUPPORTED_SET_VARIABLE))
284 		return EFI_NOT_FOUND;
285 
286 	return efi.set_variable(name, guid, attr, size, buf);
287 }
288 
289 static int cs_amp_convert_efi_status(efi_status_t status)
290 {
291 	switch (status) {
292 	case EFI_SUCCESS:
293 		return 0;
294 	case EFI_NOT_FOUND:
295 		return -ENOENT;
296 	case EFI_BUFFER_TOO_SMALL:
297 		return -EFBIG;
298 	case EFI_WRITE_PROTECTED:
299 	case EFI_UNSUPPORTED:
300 	case EFI_ACCESS_DENIED:
301 	case EFI_SECURITY_VIOLATION:
302 		return -EACCES;
303 	default:
304 		return -EIO;
305 	}
306 }
307 
308 static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev,
309 							     efi_char16_t **name,
310 							     efi_guid_t **guid,
311 							     u32 *attr)
312 {
313 	struct cirrus_amp_efi_data *efi_data __free(kfree) = NULL;
314 	unsigned long data_size = 0;
315 	efi_status_t status;
316 	int i, ret;
317 
318 	/* Find EFI variable and get size */
319 	for (i = 0; i < ARRAY_SIZE(cs_amp_lib_cal_efivars); i++) {
320 		status = cs_amp_get_efi_variable(cs_amp_lib_cal_efivars[i].name,
321 						 cs_amp_lib_cal_efivars[i].guid,
322 						 attr, &data_size, NULL);
323 		if (status == EFI_BUFFER_TOO_SMALL)
324 			break;
325 	}
326 
327 	if (status != EFI_BUFFER_TOO_SMALL)
328 		return ERR_PTR(-ENOENT);
329 
330 	if (name)
331 		*name = cs_amp_lib_cal_efivars[i].name;
332 
333 	if (guid)
334 		*guid = cs_amp_lib_cal_efivars[i].guid;
335 
336 	if (data_size < sizeof(*efi_data)) {
337 		dev_err(dev, "EFI cal variable truncated\n");
338 		return ERR_PTR(-EOVERFLOW);
339 	}
340 
341 	/* Get variable contents into buffer */
342 	efi_data = kmalloc(data_size, GFP_KERNEL);
343 	if (!efi_data)
344 		return ERR_PTR(-ENOMEM);
345 
346 	status = cs_amp_get_efi_variable(cs_amp_lib_cal_efivars[i].name,
347 					 cs_amp_lib_cal_efivars[i].guid,
348 					 attr, &data_size, efi_data);
349 	if (status != EFI_SUCCESS) {
350 		ret = -EINVAL;
351 		goto err;
352 	}
353 
354 	dev_dbg(dev, "Calibration: Size=%d, Amp Count=%d\n", efi_data->size, efi_data->count);
355 
356 	if ((efi_data->count > 128) ||
357 	    struct_size(efi_data, data, efi_data->count) > data_size) {
358 		dev_err(dev, "EFI cal variable truncated\n");
359 		ret = -EOVERFLOW;
360 		goto err;
361 	}
362 
363 	/* This could be zero-filled space pre-allocated by the BIOS */
364 	if (efi_data->size == 0)
365 		efi_data->size = data_size;
366 
367 	return_ptr(efi_data);
368 
369 err:
370 	dev_err(dev, "Failed to read calibration data from EFI: %d\n", ret);
371 
372 	return ERR_PTR(ret);
373 }
374 
375 static int cs_amp_set_cal_efi_buffer(struct device *dev,
376 				     efi_char16_t *name,
377 				     efi_guid_t *guid,
378 				     u32 attr,
379 				     struct cirrus_amp_efi_data *data)
380 {
381 	efi_status_t status;
382 
383 	status = cs_amp_set_efi_variable(name, guid, attr,
384 					 struct_size(data, data, data->count), data);
385 
386 	return cs_amp_convert_efi_status(status);
387 }
388 
389 static int _cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index,
390 					    struct cirrus_amp_cal_data *out_data)
391 {
392 	struct cirrus_amp_efi_data *efi_data __free(kfree) = NULL;
393 	struct cirrus_amp_cal_data *cal = NULL;
394 	int i;
395 
396 	efi_data = cs_amp_get_cal_efi_buffer(dev, NULL, NULL, NULL);
397 	if (IS_ERR(efi_data))
398 		return PTR_ERR(efi_data);
399 
400 	if (target_uid) {
401 		for (i = 0; i < efi_data->count; ++i) {
402 			u64 cal_target = cs_amp_cal_target_u64(&efi_data->data[i]);
403 
404 			/* Skip empty entries */
405 			if (!efi_data->data[i].calTime[0] && !efi_data->data[i].calTime[1])
406 				continue;
407 
408 			/* Skip entries with unpopulated silicon ID */
409 			if (cal_target == 0)
410 				continue;
411 
412 			if (cal_target == target_uid) {
413 				cal = &efi_data->data[i];
414 				break;
415 			}
416 		}
417 	}
418 
419 	if (!cal && (amp_index >= 0) && (amp_index < efi_data->count) &&
420 	    (efi_data->data[amp_index].calTime[0] || efi_data->data[amp_index].calTime[1])) {
421 		u64 cal_target = cs_amp_cal_target_u64(&efi_data->data[amp_index]);
422 
423 		/*
424 		 * Treat unpopulated cal_target as a wildcard.
425 		 * If target_uid != 0 we can only get here if cal_target == 0
426 		 * or it didn't match any cal_target value.
427 		 * If target_uid == 0 it is a wildcard.
428 		 */
429 		if ((cal_target == 0) || (target_uid == 0))
430 			cal = &efi_data->data[amp_index];
431 		else
432 			dev_warn(dev, "Calibration entry %d does not match silicon ID", amp_index);
433 	}
434 
435 	if (!cal) {
436 		dev_warn(dev, "No calibration for silicon ID %#llx\n", target_uid);
437 		return -ENOENT;
438 	}
439 
440 	memcpy(out_data, cal, sizeof(*out_data));
441 
442 	return 0;
443 }
444 
445 static int _cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, int num_amps,
446 					    const struct cirrus_amp_cal_data *in_data)
447 {
448 	u64 cal_target = cs_amp_cal_target_u64(in_data);
449 	unsigned long num_entries;
450 	struct cirrus_amp_efi_data *data __free(kfree) = NULL;
451 	efi_char16_t *name = CIRRUS_LOGIC_CALIBRATION_EFI_NAME;
452 	efi_guid_t *guid = &CIRRUS_LOGIC_CALIBRATION_EFI_GUID;
453 	u32 attr = CS_AMP_CAL_DEFAULT_EFI_ATTR;
454 	int i, ret;
455 
456 	if (cal_target == 0)
457 		return -EINVAL;
458 
459 	data = cs_amp_get_cal_efi_buffer(dev, &name, &guid, &attr);
460 	ret = PTR_ERR_OR_ZERO(data);
461 	if (ret == -ENOENT) {
462 		data = NULL;
463 		goto alloc_new;
464 	} else if (ret) {
465 		return ret;
466 	}
467 
468 	/*
469 	 * If the EFI variable is just zero-filled reserved space the count
470 	 * must be set.
471 	 */
472 	if (data->count == 0)
473 		data->count = (data->size - sizeof(data)) / sizeof(data->data[0]);
474 
475 	if (amp_index < 0) {
476 		/* Is there already a slot for this target? */
477 		for (amp_index = 0; amp_index < data->count; amp_index++) {
478 			if (cs_amp_cal_target_u64(&data->data[amp_index]) == cal_target)
479 				break;
480 		}
481 
482 		/* Else find an empty slot */
483 		if (amp_index >= data->count) {
484 			for (amp_index = 0; amp_index < data->count; amp_index++) {
485 				if ((data->data[amp_index].calTime[0] == 0) &&
486 				    (data->data[amp_index].calTime[1] == 0))
487 					break;
488 			}
489 		}
490 	} else {
491 		/*
492 		 * If the index is forced there could be another active
493 		 * slot with the same calTarget. So deduplicate.
494 		 */
495 		for (i = 0; i < data->count; i++) {
496 			if (i == amp_index)
497 				continue;
498 
499 			if ((data->data[i].calTime[0] == 0) && (data->data[i].calTime[1] == 0))
500 				continue;
501 
502 			if (cs_amp_cal_target_u64(&data->data[i]) == cal_target)
503 				memset(data->data[i].calTime, 0, sizeof(data->data[i].calTime));
504 		}
505 	}
506 
507 alloc_new:
508 	if (amp_index < 0)
509 		amp_index = 0;
510 
511 	num_entries = max(num_amps, amp_index + 1);
512 	if (!data || (data->count < num_entries)) {
513 		struct cirrus_amp_efi_data *old_data __free(kfree) = no_free_ptr(data);
514 		unsigned int new_data_size = struct_size(data, data, num_entries);
515 
516 		data = kzalloc(new_data_size, GFP_KERNEL);
517 		if (!data)
518 			return -ENOMEM;
519 
520 		if (old_data)
521 			memcpy(data, old_data, struct_size(old_data, data, old_data->count));
522 
523 		data->count = num_entries;
524 		data->size = new_data_size;
525 	}
526 
527 	data->data[amp_index] = *in_data;
528 	ret = cs_amp_set_cal_efi_buffer(dev, name, guid, attr, data);
529 	if (ret) {
530 		dev_err(dev, "Failed writing calibration to EFI: %d\n", ret);
531 		return ret;
532 	}
533 
534 	return 0;
535 }
536 
537 /**
538  * cs_amp_get_efi_calibration_data - get an entry from calibration data in EFI.
539  * @dev:	struct device of the caller.
540  * @target_uid:	UID to match, or zero to ignore UID matching.
541  * @amp_index:	Entry index to use, or -1 to prevent lookup by index.
542  * @out_data:	struct cirrus_amp_cal_data where the entry will be copied.
543  *
544  * This function can perform 3 types of lookup:
545  *
546  * (target_uid > 0, amp_index >= 0)
547  *	UID search with fallback to using the array index.
548  *	Search the calibration data for a non-zero calTarget that matches
549  *	target_uid, and if found return that entry. Else, if the entry at
550  *	[amp_index] has calTarget == 0, return that entry. Else fail.
551  *
552  * (target_uid > 0, amp_index < 0)
553  *	UID search only.
554  *	Search the calibration data for a non-zero calTarget that matches
555  *	target_uid, and if found return that entry. Else fail.
556  *
557  * (target_uid == 0, amp_index >= 0)
558  *	Array index fetch only.
559  *	Return the entry at [amp_index].
560  *
561  * An array lookup will be skipped if amp_index exceeds the number of
562  * entries in the calibration array, and in this case the return will
563  * be -ENOENT. An out-of-range amp_index does not prevent matching by
564  * target_uid - it has the same effect as passing amp_index < 0.
565  *
566  * If the EFI data is too short to be a valid entry, or the entry count
567  * in the EFI data overflows the actual length of the data, this function
568  * returns -EOVERFLOW.
569  *
570  * Return: 0 if the entry was found, -ENOENT if no entry was found,
571  *	   -EOVERFLOW if the EFI file is corrupt, else other error value.
572  */
573 int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index,
574 				    struct cirrus_amp_cal_data *out_data)
575 {
576 	if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
577 		return _cs_amp_get_efi_calibration_data(dev, target_uid, amp_index, out_data);
578 	else
579 		return -ENOENT;
580 }
581 EXPORT_SYMBOL_NS_GPL(cs_amp_get_efi_calibration_data, "SND_SOC_CS_AMP_LIB");
582 
583 /**
584  * cs_amp_set_efi_calibration_data - write a calibration data entry to EFI.
585  * @dev:	struct device of the caller.
586  * @amp_index:	Entry index to use, or -1 to use any available slot.
587  * @num_amps:	Maximum number of amps to reserve slots for, or -1 to ignore.
588  * @in_data:	struct cirrus_amp_cal_data entry to be written to EFI.
589  *
590  * If a Vendor-specific variable exists it will be updated,
591  * else if the Cirrus variable exists it will be updated
592  * else the Cirrus variable will be created.
593  *
594  * If amp_index >= 0 the data will be placed in this entry of the calibration
595  * data array, overwriting what was in that entry. Any other entries with the
596  * same calTarget will be marked empty.
597  *
598  * If amp_index < 0 and in_data->calTarget matches any existing entry, that
599  * entry will be overwritten. Else the first available free entry will be used,
600  * extending the size of the EFI variable if there are no free entries.
601  *
602  * If num_amps > 0 the EFI variable will be sized to contain at least this
603  * many calibration entries, with any new entries marked empty.
604  *
605  * Return: 0 if the write was successful, -EFBIG if space could not be made in
606  *	   the EFI file to add the entry, -EACCES if it was not possible to
607  *	   read or write the EFI variable.
608  */
609 int cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, int num_amps,
610 				    const struct cirrus_amp_cal_data *in_data)
611 {
612 	if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) {
613 		scoped_guard(mutex, &cs_amp_efi_cal_write_lock) {
614 			return _cs_amp_set_efi_calibration_data(dev, amp_index,
615 								num_amps, in_data);
616 		}
617 	}
618 
619 	return -ENOENT;
620 }
621 EXPORT_SYMBOL_NS_GPL(cs_amp_set_efi_calibration_data, "SND_SOC_CS_AMP_LIB");
622 
623 struct cs_amp_spkid_efi {
624 	efi_char16_t *name;
625 	efi_guid_t *guid;
626 	u8 values[2];
627 };
628 
629 static int cs_amp_get_efi_byte_spkid(struct device *dev, const struct cs_amp_spkid_efi *info)
630 {
631 	efi_status_t status;
632 	unsigned long size;
633 	u8 spkid;
634 	int i, ret;
635 
636 	size = sizeof(spkid);
637 	status = cs_amp_get_efi_variable(info->name, info->guid, NULL, &size, &spkid);
638 	ret = cs_amp_convert_efi_status(status);
639 	if (ret < 0)
640 		return ret;
641 
642 	if (size == 0)
643 		return -ENOENT;
644 
645 	for (i = 0; i < ARRAY_SIZE(info->values); i++) {
646 		if (info->values[i] == spkid)
647 			return i;
648 	}
649 
650 	dev_err(dev, "EFI speaker ID bad value %#x\n", spkid);
651 
652 	return -EINVAL;
653 }
654 
655 static const struct cs_amp_spkid_efi cs_amp_spkid_byte_types[] = {
656 	{
657 		.name = LENOVO_SPEAKER_ID_EFI_NAME,
658 		.guid = &LENOVO_SPEAKER_ID_EFI_GUID,
659 		.values = { 0xd0, 0xd1 },
660 	},
661 	{
662 		.name = HP_SPEAKER_ID_EFI_NAME,
663 		.guid = &HP_SPEAKER_ID_EFI_GUID,
664 		.values = { 0x30, 0x31 },
665 	},
666 };
667 
668 /**
669  * cs_amp_get_vendor_spkid - get a speaker ID from vendor-specific storage
670  * @dev:	pointer to struct device
671  *
672  * Known vendor-specific methods of speaker ID are checked and if one is
673  * found its speaker ID value is returned.
674  *
675  * Return: >=0 is a valid speaker ID. -ENOENT if a vendor-specific method
676  *	   was not found. -EACCES if the vendor-specific storage could not
677  *	   be read. Other error values indicate that the data from the
678  *	   vendor-specific storage was found but could not be understood.
679  */
680 int cs_amp_get_vendor_spkid(struct device *dev)
681 {
682 	int i, ret;
683 
684 	if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE) &&
685 	    !IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
686 		return -ENOENT;
687 
688 	for (i = 0; i < ARRAY_SIZE(cs_amp_spkid_byte_types); i++) {
689 		ret = cs_amp_get_efi_byte_spkid(dev, &cs_amp_spkid_byte_types[i]);
690 		if (ret != -ENOENT)
691 			return ret;
692 	}
693 
694 	return -ENOENT;
695 }
696 EXPORT_SYMBOL_NS_GPL(cs_amp_get_vendor_spkid, "SND_SOC_CS_AMP_LIB");
697 
698 /**
699  * cs_amp_create_debugfs - create a debugfs directory for a device
700  *
701  * @dev: pointer to struct device
702  *
703  * Creates a node under "cirrus_logic" in the root of the debugfs filesystem.
704  * This is for Cirrus-specific debugfs functionality to be grouped in a
705  * defined way, independently of the debugfs provided by ALSA/ASoC.
706  * The general ALSA/ASoC debugfs may not be enabled, and does not necessarily
707  * have a stable layout or naming convention.
708  *
709  * Return: Pointer to the dentry for the created directory, or -ENODEV.
710  */
711 struct dentry *cs_amp_create_debugfs(struct device *dev)
712 {
713 	struct dentry *dir;
714 
715 	dir = debugfs_lookup("cirrus_logic", NULL);
716 	if (!dir)
717 		dir = debugfs_create_dir("cirrus_logic", NULL);
718 
719 	return debugfs_create_dir(dev_name(dev), dir);
720 }
721 EXPORT_SYMBOL_NS_GPL(cs_amp_create_debugfs, "SND_SOC_CS_AMP_LIB");
722 
723 static const struct cs_amp_test_hooks cs_amp_test_hook_ptrs = {
724 	.get_efi_variable = cs_amp_get_efi_variable,
725 	.set_efi_variable = cs_amp_set_efi_variable,
726 	.write_cal_coeff = cs_amp_write_cal_coeff,
727 	.read_cal_coeff = cs_amp_read_cal_coeff,
728 };
729 
730 const struct cs_amp_test_hooks * const cs_amp_test_hooks =
731 	PTR_IF(IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST), &cs_amp_test_hook_ptrs);
732 EXPORT_SYMBOL_NS_GPL(cs_amp_test_hooks, "SND_SOC_CS_AMP_LIB");
733 
734 MODULE_DESCRIPTION("Cirrus Logic amplifier library");
735 MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
736 MODULE_LICENSE("GPL");
737 MODULE_IMPORT_NS("FW_CS_DSP");
738