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