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
cs_amp_time_now_in_windows_time(void)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
cs_amp_write_cal_coeff(struct cs_dsp * dsp,const struct cirrus_amp_cal_controls * controls,const char * ctl_name,u32 val)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
cs_amp_read_cal_coeff(struct cs_dsp * dsp,const struct cirrus_amp_cal_controls * controls,const char * ctl_name,u32 * val)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
_cs_amp_write_cal_coeffs(struct cs_dsp * dsp,const struct cirrus_amp_cal_controls * controls,const struct cirrus_amp_cal_data * data)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
_cs_amp_read_cal_coeffs(struct cs_dsp * dsp,const struct cirrus_amp_cal_controls * controls,struct cirrus_amp_cal_data * data)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 */
cs_amp_write_cal_coeffs(struct cs_dsp * dsp,const struct cirrus_amp_cal_controls * controls,const struct cirrus_amp_cal_data * data)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 */
cs_amp_read_cal_coeffs(struct cs_dsp * dsp,const struct cirrus_amp_cal_controls * controls,struct cirrus_amp_cal_data * data)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 */
cs_amp_write_ambient_temp(struct cs_dsp * dsp,const struct cirrus_amp_cal_controls * controls,u32 temp)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
cs_amp_get_efi_variable(efi_char16_t * name,efi_guid_t * guid,u32 * returned_attr,unsigned long * size,void * buf)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
cs_amp_set_efi_variable(efi_char16_t * name,efi_guid_t * guid,u32 attr,unsigned long size,void * buf)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
cs_amp_convert_efi_status(efi_status_t status)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
cs_amp_alloc_get_efi_variable(efi_char16_t * name,efi_guid_t * guid,u32 * returned_attr)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_SUCCESS)
321 return ERR_PTR(-ENOENT);
322 if (status != EFI_BUFFER_TOO_SMALL)
323 return ERR_PTR(cs_amp_convert_efi_status(status));
324
325 /* Over-alloc to ensure strings are always NUL-terminated */
326 void *buf __free(kfree) = kzalloc(size + 1, GFP_KERNEL);
327 if (!buf)
328 return ERR_PTR(-ENOMEM);
329
330 status = cs_amp_get_efi_variable(name, guid, returned_attr, &size, buf);
331 if (status != EFI_SUCCESS)
332 return ERR_PTR(cs_amp_convert_efi_status(status));
333
334 return_ptr(buf);
335 }
336
cs_amp_get_cal_efi_buffer(struct device * dev,efi_char16_t ** name,efi_guid_t ** guid,u32 * attr)337 static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev,
338 efi_char16_t **name,
339 efi_guid_t **guid,
340 u32 *attr)
341 {
342 struct cirrus_amp_efi_data *efi_data;
343 unsigned long data_size = 0;
344 u8 *data;
345 efi_status_t status;
346 int i, ret;
347
348 /* Find EFI variable and get size */
349 for (i = 0; i < ARRAY_SIZE(cs_amp_lib_cal_efivars); i++) {
350 status = cs_amp_get_efi_variable(cs_amp_lib_cal_efivars[i].name,
351 cs_amp_lib_cal_efivars[i].guid,
352 attr, &data_size, NULL);
353 if (status == EFI_BUFFER_TOO_SMALL)
354 break;
355 }
356
357 if (status != EFI_BUFFER_TOO_SMALL)
358 return ERR_PTR(-ENOENT);
359
360 if (name)
361 *name = cs_amp_lib_cal_efivars[i].name;
362
363 if (guid)
364 *guid = cs_amp_lib_cal_efivars[i].guid;
365
366 if (data_size < sizeof(*efi_data)) {
367 dev_err(dev, "EFI cal variable truncated\n");
368 return ERR_PTR(-EOVERFLOW);
369 }
370
371 /* Get variable contents into buffer */
372 data = kmalloc(data_size, GFP_KERNEL);
373 if (!data)
374 return ERR_PTR(-ENOMEM);
375
376 status = cs_amp_get_efi_variable(cs_amp_lib_cal_efivars[i].name,
377 cs_amp_lib_cal_efivars[i].guid,
378 attr, &data_size, data);
379 if (status != EFI_SUCCESS) {
380 ret = -EINVAL;
381 goto err;
382 }
383
384 efi_data = (struct cirrus_amp_efi_data *)data;
385 dev_dbg(dev, "Calibration: Size=%d, Amp Count=%d\n", efi_data->size, efi_data->count);
386
387 if ((efi_data->count > 128) ||
388 struct_size(efi_data, data, efi_data->count) > data_size) {
389 dev_err(dev, "EFI cal variable truncated\n");
390 ret = -EOVERFLOW;
391 goto err;
392 }
393
394 /* This could be zero-filled space pre-allocated by the BIOS */
395 if (efi_data->size == 0)
396 efi_data->size = data_size;
397
398 return efi_data;
399
400 err:
401 kfree(data);
402 dev_err(dev, "Failed to read calibration data from EFI: %d\n", ret);
403
404 return ERR_PTR(ret);
405 }
406
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)407 static int cs_amp_set_cal_efi_buffer(struct device *dev,
408 efi_char16_t *name,
409 efi_guid_t *guid,
410 u32 attr,
411 struct cirrus_amp_efi_data *data)
412 {
413 efi_status_t status;
414
415 status = cs_amp_set_efi_variable(name, guid, attr,
416 struct_size(data, data, data->count), data);
417
418 return cs_amp_convert_efi_status(status);
419 }
420
_cs_amp_get_efi_calibration_data(struct device * dev,u64 target_uid,int amp_index,struct cirrus_amp_cal_data * out_data)421 static int _cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index,
422 struct cirrus_amp_cal_data *out_data)
423 {
424 struct cirrus_amp_efi_data *efi_data;
425 struct cirrus_amp_cal_data *cal = NULL;
426 int i, ret;
427
428 efi_data = cs_amp_get_cal_efi_buffer(dev, NULL, NULL, NULL);
429 if (IS_ERR(efi_data))
430 return PTR_ERR(efi_data);
431
432 if (target_uid) {
433 for (i = 0; i < efi_data->count; ++i) {
434 u64 cal_target = cs_amp_cal_target_u64(&efi_data->data[i]);
435
436 /* Skip empty entries */
437 if (!efi_data->data[i].calTime[0] && !efi_data->data[i].calTime[1])
438 continue;
439
440 /* Skip entries with unpopulated silicon ID */
441 if (cal_target == 0)
442 continue;
443
444 if (cal_target == target_uid) {
445 cal = &efi_data->data[i];
446 break;
447 }
448 }
449 }
450
451 if (!cal && (amp_index >= 0) && (amp_index < efi_data->count) &&
452 (efi_data->data[amp_index].calTime[0] || efi_data->data[amp_index].calTime[1])) {
453 u64 cal_target = cs_amp_cal_target_u64(&efi_data->data[amp_index]);
454
455 /*
456 * Treat unpopulated cal_target as a wildcard.
457 * If target_uid != 0 we can only get here if cal_target == 0
458 * or it didn't match any cal_target value.
459 * If target_uid == 0 it is a wildcard.
460 */
461 if ((cal_target == 0) || (target_uid == 0))
462 cal = &efi_data->data[amp_index];
463 else
464 dev_warn(dev, "Calibration entry %d does not match silicon ID", amp_index);
465 }
466
467 if (cal) {
468 memcpy(out_data, cal, sizeof(*out_data));
469 ret = 0;
470 } else {
471 dev_warn(dev, "No calibration for silicon ID %#llx\n", target_uid);
472 ret = -ENOENT;
473 }
474
475 kfree(efi_data);
476
477 return ret;
478 }
479
_cs_amp_set_efi_calibration_data(struct device * dev,int amp_index,int num_amps,const struct cirrus_amp_cal_data * in_data)480 static int _cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, int num_amps,
481 const struct cirrus_amp_cal_data *in_data)
482 {
483 u64 cal_target = cs_amp_cal_target_u64(in_data);
484 unsigned long num_entries;
485 struct cirrus_amp_efi_data *data;
486 efi_char16_t *name = CIRRUS_LOGIC_CALIBRATION_EFI_NAME;
487 efi_guid_t *guid = &CIRRUS_LOGIC_CALIBRATION_EFI_GUID;
488 u32 attr = CS_AMP_CAL_DEFAULT_EFI_ATTR;
489 int i, ret;
490
491 if (cal_target == 0)
492 return -EINVAL;
493
494 data = cs_amp_get_cal_efi_buffer(dev, &name, &guid, &attr);
495 ret = PTR_ERR_OR_ZERO(data);
496 if (ret == -ENOENT) {
497 data = NULL;
498 goto alloc_new;
499 } else if (ret) {
500 return ret;
501 }
502
503 /*
504 * If the EFI variable is just zero-filled reserved space the count
505 * must be set.
506 */
507 if (data->count == 0)
508 data->count = (data->size - struct_offset(data, data)) / sizeof(data->data[0]);
509
510 if (amp_index < 0) {
511 /* Is there already a slot for this target? */
512 for (amp_index = 0; amp_index < data->count; amp_index++) {
513 if (cs_amp_cal_target_u64(&data->data[amp_index]) == cal_target)
514 break;
515 }
516
517 /* Else find an empty slot */
518 if (amp_index >= data->count) {
519 for (amp_index = 0; amp_index < data->count; amp_index++) {
520 if ((data->data[amp_index].calTime[0] == 0) &&
521 (data->data[amp_index].calTime[1] == 0))
522 break;
523 }
524 }
525 } else {
526 /*
527 * If the index is forced there could be another active
528 * slot with the same calTarget. So deduplicate.
529 */
530 for (i = 0; i < data->count; i++) {
531 if (i == amp_index)
532 continue;
533
534 if ((data->data[i].calTime[0] == 0) && (data->data[i].calTime[1] == 0))
535 continue;
536
537 if (cs_amp_cal_target_u64(&data->data[i]) == cal_target)
538 memset(data->data[i].calTime, 0, sizeof(data->data[i].calTime));
539 }
540 }
541
542 alloc_new:
543 if (amp_index < 0)
544 amp_index = 0;
545
546 num_entries = max(num_amps, amp_index + 1);
547 if (!data || (data->count < num_entries)) {
548 struct cirrus_amp_efi_data *new_data;
549 unsigned int new_data_size = struct_size(data, data, num_entries);
550
551 new_data = kzalloc(new_data_size, GFP_KERNEL);
552 if (!new_data) {
553 ret = -ENOMEM;
554 goto err;
555 }
556
557 if (data) {
558 memcpy(new_data, data, struct_size(data, data, data->count));
559 kfree(data);
560 }
561
562 data = new_data;
563 data->count = num_entries;
564 data->size = new_data_size;
565 }
566
567 data->data[amp_index] = *in_data;
568 ret = cs_amp_set_cal_efi_buffer(dev, name, guid, attr, data);
569 if (ret)
570 dev_err(dev, "Failed writing calibration to EFI: %d\n", ret);
571 err:
572 kfree(data);
573
574 return ret;
575 }
576
577 /**
578 * cs_amp_get_efi_calibration_data - get an entry from calibration data in EFI.
579 * @dev: struct device of the caller.
580 * @target_uid: UID to match, or zero to ignore UID matching.
581 * @amp_index: Entry index to use, or -1 to prevent lookup by index.
582 * @out_data: struct cirrus_amp_cal_data where the entry will be copied.
583 *
584 * This function can perform 3 types of lookup:
585 *
586 * (target_uid > 0, amp_index >= 0)
587 * UID search with fallback to using the array index.
588 * Search the calibration data for a non-zero calTarget that matches
589 * target_uid, and if found return that entry. Else, if the entry at
590 * [amp_index] has calTarget == 0, return that entry. Else fail.
591 *
592 * (target_uid > 0, amp_index < 0)
593 * UID search only.
594 * Search the calibration data for a non-zero calTarget that matches
595 * target_uid, and if found return that entry. Else fail.
596 *
597 * (target_uid == 0, amp_index >= 0)
598 * Array index fetch only.
599 * Return the entry at [amp_index].
600 *
601 * An array lookup will be skipped if amp_index exceeds the number of
602 * entries in the calibration array, and in this case the return will
603 * be -ENOENT. An out-of-range amp_index does not prevent matching by
604 * target_uid - it has the same effect as passing amp_index < 0.
605 *
606 * If the EFI data is too short to be a valid entry, or the entry count
607 * in the EFI data overflows the actual length of the data, this function
608 * returns -EOVERFLOW.
609 *
610 * Return: 0 if the entry was found, -ENOENT if no entry was found,
611 * -EOVERFLOW if the EFI file is corrupt, else other error value.
612 */
cs_amp_get_efi_calibration_data(struct device * dev,u64 target_uid,int amp_index,struct cirrus_amp_cal_data * out_data)613 int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index,
614 struct cirrus_amp_cal_data *out_data)
615 {
616 if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS))
617 return _cs_amp_get_efi_calibration_data(dev, target_uid, amp_index, out_data);
618 else
619 return -ENOENT;
620 }
621 EXPORT_SYMBOL_NS_GPL(cs_amp_get_efi_calibration_data, "SND_SOC_CS_AMP_LIB");
622
623 /**
624 * cs_amp_set_efi_calibration_data - write a calibration data entry to EFI.
625 * @dev: struct device of the caller.
626 * @amp_index: Entry index to use, or -1 to use any available slot.
627 * @num_amps: Maximum number of amps to reserve slots for, or -1 to ignore.
628 * @in_data: struct cirrus_amp_cal_data entry to be written to EFI.
629 *
630 * If a Vendor-specific variable exists it will be updated,
631 * else if the Cirrus variable exists it will be updated
632 * else the Cirrus variable will be created.
633 *
634 * If amp_index >= 0 the data will be placed in this entry of the calibration
635 * data array, overwriting what was in that entry. Any other entries with the
636 * same calTarget will be marked empty.
637 *
638 * If amp_index < 0 and in_data->calTarget matches any existing entry, that
639 * entry will be overwritten. Else the first available free entry will be used,
640 * extending the size of the EFI variable if there are no free entries.
641 *
642 * If num_amps > 0 the EFI variable will be sized to contain at least this
643 * many calibration entries, with any new entries marked empty.
644 *
645 * Return: 0 if the write was successful, -EFBIG if space could not be made in
646 * the EFI file to add the entry, -EACCES if it was not possible to
647 * read or write the EFI variable.
648 */
cs_amp_set_efi_calibration_data(struct device * dev,int amp_index,int num_amps,const struct cirrus_amp_cal_data * in_data)649 int cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, int num_amps,
650 const struct cirrus_amp_cal_data *in_data)
651 {
652 if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS)) {
653 scoped_guard(mutex, &cs_amp_efi_cal_write_lock) {
654 return _cs_amp_set_efi_calibration_data(dev, amp_index,
655 num_amps, in_data);
656 }
657 }
658
659 return -ENOENT;
660 }
661 EXPORT_SYMBOL_NS_GPL(cs_amp_set_efi_calibration_data, "SND_SOC_CS_AMP_LIB");
662
663 struct cs_amp_spkid_efi {
664 efi_char16_t *name;
665 efi_guid_t *guid;
666 u8 values[2];
667 };
668
cs_amp_get_efi_byte_spkid(struct device * dev,const struct cs_amp_spkid_efi * info)669 static int cs_amp_get_efi_byte_spkid(struct device *dev, const struct cs_amp_spkid_efi *info)
670 {
671 efi_status_t status;
672 unsigned long size;
673 u8 spkid;
674 int i, ret;
675
676 size = sizeof(spkid);
677 status = cs_amp_get_efi_variable(info->name, info->guid, NULL, &size, &spkid);
678 ret = cs_amp_convert_efi_status(status);
679 if (ret < 0)
680 return ret;
681
682 if (size == 0)
683 return -ENOENT;
684
685 for (i = 0; i < ARRAY_SIZE(info->values); i++) {
686 if (info->values[i] == spkid)
687 return i;
688 }
689
690 dev_err(dev, "EFI speaker ID bad value %#x\n", spkid);
691
692 return -EINVAL;
693 }
694
695 static const struct cs_amp_spkid_efi cs_amp_spkid_byte_types[] = {
696 {
697 .name = LENOVO_SPEAKER_ID_EFI_NAME,
698 .guid = &LENOVO_SPEAKER_ID_EFI_GUID,
699 .values = { 0xd0, 0xd1 },
700 },
701 {
702 .name = HP_SPEAKER_ID_EFI_NAME,
703 .guid = &HP_SPEAKER_ID_EFI_GUID,
704 .values = { 0x30, 0x31 },
705 },
706 };
707
708 /**
709 * cs_amp_get_vendor_spkid - get a speaker ID from vendor-specific storage
710 * @dev: pointer to struct device
711 *
712 * Known vendor-specific methods of speaker ID are checked and if one is
713 * found its speaker ID value is returned.
714 *
715 * Return: >=0 is a valid speaker ID. -ENOENT if a vendor-specific method
716 * was not found. -EACCES if the vendor-specific storage could not
717 * be read. Other error values indicate that the data from the
718 * vendor-specific storage was found but could not be understood.
719 */
cs_amp_get_vendor_spkid(struct device * dev)720 int cs_amp_get_vendor_spkid(struct device *dev)
721 {
722 int i, ret;
723
724 KUNIT_STATIC_STUB_REDIRECT(cs_amp_get_vendor_spkid, dev);
725
726 if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE) &&
727 !IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS))
728 return -ENOENT;
729
730 for (i = 0; i < ARRAY_SIZE(cs_amp_spkid_byte_types); i++) {
731 ret = cs_amp_get_efi_byte_spkid(dev, &cs_amp_spkid_byte_types[i]);
732 if (ret != -ENOENT)
733 return ret;
734 }
735
736 return -ENOENT;
737 }
738 EXPORT_SYMBOL_NS_GPL(cs_amp_get_vendor_spkid, "SND_SOC_CS_AMP_LIB");
739
cs_amp_devm_get_dell_ssidex(struct device * dev,int ssid_vendor,int ssid_device)740 static const char *cs_amp_devm_get_dell_ssidex(struct device *dev,
741 int ssid_vendor, int ssid_device)
742 {
743 unsigned int hex_prefix;
744 char audio_id[4];
745 char delim;
746 char *p;
747 int ret;
748
749 if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE) &&
750 !IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS))
751 return ERR_PTR(-ENOENT);
752
753 char *ssidex_buf __free(kfree) = cs_amp_alloc_get_efi_variable(DELL_SSIDEXV2_EFI_NAME,
754 &DELL_SSIDEXV2_EFI_GUID,
755 NULL);
756 if (IS_ERR(ssidex_buf))
757 return ssidex_buf;
758
759 /*
760 * SSIDExV2 string is a series of underscore delimited fields.
761 * First field is all or part of the SSID. Second field should be
762 * a 2-character audio hardware id, followed by other identifiers.
763 * Older models did not have the 2-character audio id, so reject
764 * the string if the second field is not 2 characters.
765 */
766 ret = sscanf(ssidex_buf, "%8x_%2s%c", &hex_prefix, audio_id, &delim);
767 if (ret < 2)
768 return ERR_PTR(-ENOENT);
769
770 if ((ret == 3) && (delim != '_'))
771 return ERR_PTR(-ENOENT);
772
773 if (strlen(audio_id) != 2)
774 return ERR_PTR(-ENOENT);
775
776 p = devm_kstrdup(dev, audio_id, GFP_KERNEL);
777 if (!p)
778 return ERR_PTR(-ENOMEM);
779
780 return p;
781 }
782
783 /**
784 * cs_amp_devm_get_vendor_specific_variant_id - get variant ID string
785 * @dev: pointer to struct device
786 * @ssid_vendor: PCI Subsystem Vendor (-1 if unknown)
787 * @ssid_device: PCI Subsystem Device (-1 if unknown)
788 *
789 * Known vendor-specific hardware identifiers are checked and if one is
790 * found its content is returned as a NUL-terminated string. The returned
791 * string is devm-managed.
792 *
793 * The returned string is not guaranteed to be globally unique.
794 * Generally it should be combined with some other qualifier, such as
795 * PCI SSID, to create a globally unique ID.
796 *
797 * If the caller has a PCI SSID it should pass it in @ssid_vendor and
798 * @ssid_device. If the vendor-spefic ID contains this SSID it will be
799 * stripped from the returned string to prevent duplication.
800 *
801 * If the caller does not have a PCI SSID, pass -1 for @ssid_vendor and
802 * @ssid_device.
803 *
804 * Return:
805 * * a pointer to a devm-managed string
806 * * ERR_PTR(-ENOENT) if no vendor-specific qualifier
807 * * ERR_PTR error value
808 */
cs_amp_devm_get_vendor_specific_variant_id(struct device * dev,int ssid_vendor,int ssid_device)809 const char *cs_amp_devm_get_vendor_specific_variant_id(struct device *dev,
810 int ssid_vendor,
811 int ssid_device)
812 {
813 KUNIT_STATIC_STUB_REDIRECT(cs_amp_devm_get_vendor_specific_variant_id,
814 dev, ssid_vendor, ssid_device);
815
816 if ((ssid_vendor == PCI_VENDOR_ID_DELL) || (ssid_vendor < 0))
817 return cs_amp_devm_get_dell_ssidex(dev, ssid_vendor, ssid_device);
818
819 return ERR_PTR(-ENOENT);
820 }
821 EXPORT_SYMBOL_NS_GPL(cs_amp_devm_get_vendor_specific_variant_id, "SND_SOC_CS_AMP_LIB");
822
823 /**
824 * cs_amp_create_debugfs - create a debugfs directory for a device
825 *
826 * @dev: pointer to struct device
827 *
828 * Creates a node under "cirrus_logic" in the root of the debugfs filesystem.
829 * This is for Cirrus-specific debugfs functionality to be grouped in a
830 * defined way, independently of the debugfs provided by ALSA/ASoC.
831 * The general ALSA/ASoC debugfs may not be enabled, and does not necessarily
832 * have a stable layout or naming convention.
833 *
834 * Return: Pointer to the dentry for the created directory, or -ENODEV.
835 */
cs_amp_create_debugfs(struct device * dev)836 struct dentry *cs_amp_create_debugfs(struct device *dev)
837 {
838 struct dentry *dir, *created;
839
840 /* debugfs_lookup() can return NULL or ERR_PTR on error */
841 dir = debugfs_lookup("cirrus_logic", NULL);
842 if (!IS_ERR_OR_NULL(dir)) {
843 created = debugfs_create_dir(dev_name(dev), dir);
844 dput(dir);
845
846 return created;
847 }
848
849 dir = debugfs_create_dir("cirrus_logic", NULL);
850
851 return debugfs_create_dir(dev_name(dev), dir);
852 }
853 EXPORT_SYMBOL_NS_GPL(cs_amp_create_debugfs, "SND_SOC_CS_AMP_LIB");
854
855 static const struct cs_amp_test_hooks cs_amp_test_hook_ptrs = {
856 .get_efi_variable = cs_amp_get_efi_variable,
857 .set_efi_variable = cs_amp_set_efi_variable,
858 .write_cal_coeff = cs_amp_write_cal_coeff,
859 .read_cal_coeff = cs_amp_read_cal_coeff,
860 };
861
862 const struct cs_amp_test_hooks * const cs_amp_test_hooks =
863 PTR_IF(IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS), &cs_amp_test_hook_ptrs);
864 EXPORT_SYMBOL_NS_GPL(cs_amp_test_hooks, "SND_SOC_CS_AMP_LIB");
865
866 MODULE_DESCRIPTION("Cirrus Logic amplifier library");
867 MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
868 MODULE_LICENSE("GPL");
869 MODULE_IMPORT_NS("FW_CS_DSP");
870