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 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 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 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 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 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 */ 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 */ 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 */ 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 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 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 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 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 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 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 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 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 */ 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 */ 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 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 */ 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 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 */ 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 */ 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