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