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