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