1 // SPDX-License-Identifier: GPL-2.0-only 2 // 3 // Common code for Cirrus Logic Smart Amplifiers 4 // 5 // Copyright (C) 2024 Cirrus Logic, Inc. and 6 // Cirrus Logic International Semiconductor Ltd. 7 8 #include <asm/byteorder.h> 9 #include <kunit/static_stub.h> 10 #include <linux/debugfs.h> 11 #include <linux/dev_printk.h> 12 #include <linux/efi.h> 13 #include <linux/firmware/cirrus/cs_dsp.h> 14 #include <linux/math64.h> 15 #include <linux/module.h> 16 #include <linux/mutex.h> 17 #include <linux/overflow.h> 18 #include <linux/pci_ids.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 #define DELL_SSIDEXV2_EFI_NAME L"SSIDexV2Data" 41 #define DELL_SSIDEXV2_EFI_GUID \ 42 EFI_GUID(0x6a5f35df, 0x1432, 0x4656, 0x85, 0x97, 0x31, 0x04, 0xd5, 0xbf, 0x3a, 0xb0) 43 44 static const struct cs_amp_lib_cal_efivar { 45 efi_char16_t *name; 46 efi_guid_t *guid; 47 } cs_amp_lib_cal_efivars[] = { 48 { 49 .name = HP_CALIBRATION_EFI_NAME, 50 .guid = &HP_CALIBRATION_EFI_GUID, 51 }, 52 { 53 .name = CIRRUS_LOGIC_CALIBRATION_EFI_NAME, 54 .guid = &CIRRUS_LOGIC_CALIBRATION_EFI_GUID, 55 }, 56 }; 57 58 #define CS_AMP_CAL_DEFAULT_EFI_ATTR \ 59 (EFI_VARIABLE_NON_VOLATILE | \ 60 EFI_VARIABLE_BOOTSERVICE_ACCESS | \ 61 EFI_VARIABLE_RUNTIME_ACCESS) 62 63 /* Offset from Unix time to Windows time (100ns since 1 Jan 1601) */ 64 #define UNIX_TIME_TO_WINDOWS_TIME_OFFSET 116444736000000000ULL 65 66 static DEFINE_MUTEX(cs_amp_efi_cal_write_lock); 67 68 static u64 cs_amp_time_now_in_windows_time(void) 69 { 70 u64 time_in_100ns = div_u64(ktime_get_real_ns(), 100); 71 72 return time_in_100ns + UNIX_TIME_TO_WINDOWS_TIME_OFFSET; 73 } 74 75 static int cs_amp_write_cal_coeff(struct cs_dsp *dsp, 76 const struct cirrus_amp_cal_controls *controls, 77 const char *ctl_name, u32 val) 78 { 79 struct cs_dsp_coeff_ctl *cs_ctl; 80 __be32 beval = cpu_to_be32(val); 81 int ret; 82 83 KUNIT_STATIC_STUB_REDIRECT(cs_amp_write_cal_coeff, dsp, controls, ctl_name, val); 84 85 if (IS_REACHABLE(CONFIG_FW_CS_DSP)) { 86 mutex_lock(&dsp->pwr_lock); 87 cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, controls->mem_region, controls->alg_id); 88 ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, &beval, sizeof(beval)); 89 mutex_unlock(&dsp->pwr_lock); 90 91 if (ret < 0) { 92 dev_err(dsp->dev, "Failed to write to '%s': %d\n", ctl_name, ret); 93 return ret; 94 } 95 96 return 0; 97 } 98 99 return -ENODEV; 100 } 101 102 static int cs_amp_read_cal_coeff(struct cs_dsp *dsp, 103 const struct cirrus_amp_cal_controls *controls, 104 const char *ctl_name, u32 *val) 105 { 106 struct cs_dsp_coeff_ctl *cs_ctl; 107 __be32 beval; 108 int ret; 109 110 KUNIT_STATIC_STUB_REDIRECT(cs_amp_read_cal_coeff, dsp, controls, ctl_name, val); 111 112 if (!IS_REACHABLE(CONFIG_FW_CS_DSP)) 113 return -ENODEV; 114 115 scoped_guard(mutex, &dsp->pwr_lock) { 116 cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, controls->mem_region, controls->alg_id); 117 ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, &beval, sizeof(beval)); 118 } 119 120 if (ret < 0) { 121 dev_err(dsp->dev, "Failed to write to '%s': %d\n", ctl_name, ret); 122 return ret; 123 } 124 125 *val = be32_to_cpu(beval); 126 127 return 0; 128 } 129 130 static int _cs_amp_write_cal_coeffs(struct cs_dsp *dsp, 131 const struct cirrus_amp_cal_controls *controls, 132 const struct cirrus_amp_cal_data *data) 133 { 134 int ret; 135 136 dev_dbg(dsp->dev, "Calibration: Ambient=%#x, Status=%#x, CalR=%d\n", 137 data->calAmbient, data->calStatus, data->calR); 138 139 if (list_empty(&dsp->ctl_list)) { 140 dev_info(dsp->dev, "Calibration disabled due to missing firmware controls\n"); 141 return -ENOENT; 142 } 143 144 ret = cs_amp_write_cal_coeff(dsp, controls, controls->ambient, data->calAmbient); 145 if (ret) 146 return ret; 147 148 ret = cs_amp_write_cal_coeff(dsp, controls, controls->calr, data->calR); 149 if (ret) 150 return ret; 151 152 ret = cs_amp_write_cal_coeff(dsp, controls, controls->status, data->calStatus); 153 if (ret) 154 return ret; 155 156 ret = cs_amp_write_cal_coeff(dsp, controls, controls->checksum, data->calR + 1); 157 if (ret) 158 return ret; 159 160 return 0; 161 } 162 163 static int _cs_amp_read_cal_coeffs(struct cs_dsp *dsp, 164 const struct cirrus_amp_cal_controls *controls, 165 struct cirrus_amp_cal_data *data) 166 { 167 u64 time; 168 u32 val; 169 int ret; 170 171 if (list_empty(&dsp->ctl_list)) { 172 dev_info(dsp->dev, "Calibration disabled due to missing firmware controls\n"); 173 return -ENOENT; 174 } 175 176 ret = cs_amp_read_cal_coeff(dsp, controls, controls->ambient, &val); 177 if (ret) 178 return ret; 179 180 data->calAmbient = (s8)val; 181 182 ret = cs_amp_read_cal_coeff(dsp, controls, controls->calr, &val); 183 if (ret) 184 return ret; 185 186 data->calR = (u16)val; 187 188 ret = cs_amp_read_cal_coeff(dsp, controls, controls->status, &val); 189 if (ret) 190 return ret; 191 192 data->calStatus = (u8)val; 193 194 /* Fill in timestamp */ 195 time = cs_amp_time_now_in_windows_time(); 196 data->calTime[0] = (u32)time; 197 data->calTime[1] = (u32)(time >> 32); 198 199 return 0; 200 } 201 202 /** 203 * cs_amp_write_cal_coeffs - Write calibration data to firmware controls. 204 * @dsp: Pointer to struct cs_dsp. 205 * @controls: Pointer to definition of firmware controls to be written. 206 * @data: Pointer to calibration data. 207 * 208 * Returns: 0 on success, else negative error value. 209 */ 210 int cs_amp_write_cal_coeffs(struct cs_dsp *dsp, 211 const struct cirrus_amp_cal_controls *controls, 212 const struct cirrus_amp_cal_data *data) 213 { 214 if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS)) 215 return _cs_amp_write_cal_coeffs(dsp, controls, data); 216 else 217 return -ENODEV; 218 } 219 EXPORT_SYMBOL_NS_GPL(cs_amp_write_cal_coeffs, "SND_SOC_CS_AMP_LIB"); 220 221 /** 222 * cs_amp_read_cal_coeffs - Read calibration data from firmware controls. 223 * @dsp: Pointer to struct cs_dsp. 224 * @controls: Pointer to definition of firmware controls to be read. 225 * @data: Pointer to calibration data where results will be written. 226 * 227 * Returns: 0 on success, else negative error value. 228 */ 229 int cs_amp_read_cal_coeffs(struct cs_dsp *dsp, 230 const struct cirrus_amp_cal_controls *controls, 231 struct cirrus_amp_cal_data *data) 232 { 233 if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS)) 234 return _cs_amp_read_cal_coeffs(dsp, controls, data); 235 else 236 return -ENODEV; 237 } 238 EXPORT_SYMBOL_NS_GPL(cs_amp_read_cal_coeffs, "SND_SOC_CS_AMP_LIB"); 239 240 /** 241 * cs_amp_write_ambient_temp - write value to calibration ambient temperature 242 * @dsp: Pointer to struct cs_dsp. 243 * @controls: Pointer to definition of firmware controls to be read. 244 * @temp: Temperature in degrees celcius. 245 * 246 * Returns: 0 on success, else negative error value. 247 */ 248 int cs_amp_write_ambient_temp(struct cs_dsp *dsp, 249 const struct cirrus_amp_cal_controls *controls, 250 u32 temp) 251 { 252 return cs_amp_write_cal_coeff(dsp, controls, controls->ambient, temp); 253 } 254 EXPORT_SYMBOL_NS_GPL(cs_amp_write_ambient_temp, "SND_SOC_CS_AMP_LIB"); 255 256 static efi_status_t cs_amp_get_efi_variable(efi_char16_t *name, 257 efi_guid_t *guid, 258 u32 *returned_attr, 259 unsigned long *size, 260 void *buf) 261 { 262 u32 attr; 263 264 if (!returned_attr) 265 returned_attr = &attr; 266 267 KUNIT_STATIC_STUB_REDIRECT(cs_amp_get_efi_variable, name, guid, 268 returned_attr, size, buf); 269 270 if (efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) 271 return efi.get_variable(name, guid, returned_attr, size, buf); 272 273 return EFI_NOT_FOUND; 274 } 275 276 static efi_status_t cs_amp_set_efi_variable(efi_char16_t *name, 277 efi_guid_t *guid, 278 u32 attr, 279 unsigned long size, 280 void *buf) 281 { 282 KUNIT_STATIC_STUB_REDIRECT(cs_amp_set_efi_variable, name, guid, attr, size, buf); 283 284 if (!efi_rt_services_supported(EFI_RT_SUPPORTED_SET_VARIABLE)) 285 return EFI_NOT_FOUND; 286 287 return efi.set_variable(name, guid, attr, size, buf); 288 } 289 290 static int cs_amp_convert_efi_status(efi_status_t status) 291 { 292 switch (status) { 293 case EFI_SUCCESS: 294 return 0; 295 case EFI_NOT_FOUND: 296 return -ENOENT; 297 case EFI_BUFFER_TOO_SMALL: 298 return -EFBIG; 299 case EFI_WRITE_PROTECTED: 300 case EFI_UNSUPPORTED: 301 case EFI_ACCESS_DENIED: 302 case EFI_SECURITY_VIOLATION: 303 return -EACCES; 304 default: 305 return -EIO; 306 } 307 } 308 309 static void *cs_amp_alloc_get_efi_variable(efi_char16_t *name, 310 efi_guid_t *guid, 311 u32 *returned_attr) 312 { 313 efi_status_t status; 314 unsigned long size = 0; 315 316 status = cs_amp_get_efi_variable(name, guid, NULL, &size, NULL); 317 if (status != EFI_BUFFER_TOO_SMALL) 318 return ERR_PTR(cs_amp_convert_efi_status(status)); 319 320 /* Over-alloc to ensure strings are always NUL-terminated */ 321 void *buf __free(kfree) = kzalloc(size + 1, GFP_KERNEL); 322 if (!buf) 323 return ERR_PTR(-ENOMEM); 324 325 status = cs_amp_get_efi_variable(name, guid, returned_attr, &size, buf); 326 if (status != EFI_SUCCESS) 327 return ERR_PTR(cs_amp_convert_efi_status(status)); 328 329 return_ptr(buf); 330 } 331 332 static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev, 333 efi_char16_t **name, 334 efi_guid_t **guid, 335 u32 *attr) 336 { 337 struct cirrus_amp_efi_data *efi_data; 338 unsigned long data_size = 0; 339 u8 *data; 340 efi_status_t status; 341 int i, ret; 342 343 /* Find EFI variable and get size */ 344 for (i = 0; i < ARRAY_SIZE(cs_amp_lib_cal_efivars); i++) { 345 status = cs_amp_get_efi_variable(cs_amp_lib_cal_efivars[i].name, 346 cs_amp_lib_cal_efivars[i].guid, 347 attr, &data_size, NULL); 348 if (status == EFI_BUFFER_TOO_SMALL) 349 break; 350 } 351 352 if (status != EFI_BUFFER_TOO_SMALL) 353 return ERR_PTR(-ENOENT); 354 355 if (name) 356 *name = cs_amp_lib_cal_efivars[i].name; 357 358 if (guid) 359 *guid = cs_amp_lib_cal_efivars[i].guid; 360 361 if (data_size < sizeof(*efi_data)) { 362 dev_err(dev, "EFI cal variable truncated\n"); 363 return ERR_PTR(-EOVERFLOW); 364 } 365 366 /* Get variable contents into buffer */ 367 data = kmalloc(data_size, GFP_KERNEL); 368 if (!data) 369 return ERR_PTR(-ENOMEM); 370 371 status = cs_amp_get_efi_variable(cs_amp_lib_cal_efivars[i].name, 372 cs_amp_lib_cal_efivars[i].guid, 373 attr, &data_size, data); 374 if (status != EFI_SUCCESS) { 375 ret = -EINVAL; 376 goto err; 377 } 378 379 efi_data = (struct cirrus_amp_efi_data *)data; 380 dev_dbg(dev, "Calibration: Size=%d, Amp Count=%d\n", efi_data->size, efi_data->count); 381 382 if ((efi_data->count > 128) || 383 struct_size(efi_data, data, efi_data->count) > data_size) { 384 dev_err(dev, "EFI cal variable truncated\n"); 385 ret = -EOVERFLOW; 386 goto err; 387 } 388 389 /* This could be zero-filled space pre-allocated by the BIOS */ 390 if (efi_data->size == 0) 391 efi_data->size = data_size; 392 393 return efi_data; 394 395 err: 396 kfree(data); 397 dev_err(dev, "Failed to read calibration data from EFI: %d\n", ret); 398 399 return ERR_PTR(ret); 400 } 401 402 static int cs_amp_set_cal_efi_buffer(struct device *dev, 403 efi_char16_t *name, 404 efi_guid_t *guid, 405 u32 attr, 406 struct cirrus_amp_efi_data *data) 407 { 408 efi_status_t status; 409 410 status = cs_amp_set_efi_variable(name, guid, attr, 411 struct_size(data, data, data->count), data); 412 413 return cs_amp_convert_efi_status(status); 414 } 415 416 static int _cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index, 417 struct cirrus_amp_cal_data *out_data) 418 { 419 struct cirrus_amp_efi_data *efi_data; 420 struct cirrus_amp_cal_data *cal = NULL; 421 int i, ret; 422 423 efi_data = cs_amp_get_cal_efi_buffer(dev, NULL, NULL, NULL); 424 if (IS_ERR(efi_data)) 425 return PTR_ERR(efi_data); 426 427 if (target_uid) { 428 for (i = 0; i < efi_data->count; ++i) { 429 u64 cal_target = cs_amp_cal_target_u64(&efi_data->data[i]); 430 431 /* Skip empty entries */ 432 if (!efi_data->data[i].calTime[0] && !efi_data->data[i].calTime[1]) 433 continue; 434 435 /* Skip entries with unpopulated silicon ID */ 436 if (cal_target == 0) 437 continue; 438 439 if (cal_target == target_uid) { 440 cal = &efi_data->data[i]; 441 break; 442 } 443 } 444 } 445 446 if (!cal && (amp_index >= 0) && (amp_index < efi_data->count) && 447 (efi_data->data[amp_index].calTime[0] || efi_data->data[amp_index].calTime[1])) { 448 u64 cal_target = cs_amp_cal_target_u64(&efi_data->data[amp_index]); 449 450 /* 451 * Treat unpopulated cal_target as a wildcard. 452 * If target_uid != 0 we can only get here if cal_target == 0 453 * or it didn't match any cal_target value. 454 * If target_uid == 0 it is a wildcard. 455 */ 456 if ((cal_target == 0) || (target_uid == 0)) 457 cal = &efi_data->data[amp_index]; 458 else 459 dev_warn(dev, "Calibration entry %d does not match silicon ID", amp_index); 460 } 461 462 if (cal) { 463 memcpy(out_data, cal, sizeof(*out_data)); 464 ret = 0; 465 } else { 466 dev_warn(dev, "No calibration for silicon ID %#llx\n", target_uid); 467 ret = -ENOENT; 468 } 469 470 kfree(efi_data); 471 472 return ret; 473 } 474 475 static int _cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, int num_amps, 476 const struct cirrus_amp_cal_data *in_data) 477 { 478 u64 cal_target = cs_amp_cal_target_u64(in_data); 479 unsigned long num_entries; 480 struct cirrus_amp_efi_data *data; 481 efi_char16_t *name = CIRRUS_LOGIC_CALIBRATION_EFI_NAME; 482 efi_guid_t *guid = &CIRRUS_LOGIC_CALIBRATION_EFI_GUID; 483 u32 attr = CS_AMP_CAL_DEFAULT_EFI_ATTR; 484 int i, ret; 485 486 if (cal_target == 0) 487 return -EINVAL; 488 489 data = cs_amp_get_cal_efi_buffer(dev, &name, &guid, &attr); 490 ret = PTR_ERR_OR_ZERO(data); 491 if (ret == -ENOENT) { 492 data = NULL; 493 goto alloc_new; 494 } else if (ret) { 495 return ret; 496 } 497 498 /* 499 * If the EFI variable is just zero-filled reserved space the count 500 * must be set. 501 */ 502 if (data->count == 0) 503 data->count = (data->size - sizeof(data)) / sizeof(data->data[0]); 504 505 if (amp_index < 0) { 506 /* Is there already a slot for this target? */ 507 for (amp_index = 0; amp_index < data->count; amp_index++) { 508 if (cs_amp_cal_target_u64(&data->data[amp_index]) == cal_target) 509 break; 510 } 511 512 /* Else find an empty slot */ 513 if (amp_index >= data->count) { 514 for (amp_index = 0; amp_index < data->count; amp_index++) { 515 if ((data->data[amp_index].calTime[0] == 0) && 516 (data->data[amp_index].calTime[1] == 0)) 517 break; 518 } 519 } 520 } else { 521 /* 522 * If the index is forced there could be another active 523 * slot with the same calTarget. So deduplicate. 524 */ 525 for (i = 0; i < data->count; i++) { 526 if (i == amp_index) 527 continue; 528 529 if ((data->data[i].calTime[0] == 0) && (data->data[i].calTime[1] == 0)) 530 continue; 531 532 if (cs_amp_cal_target_u64(&data->data[i]) == cal_target) 533 memset(data->data[i].calTime, 0, sizeof(data->data[i].calTime)); 534 } 535 } 536 537 alloc_new: 538 if (amp_index < 0) 539 amp_index = 0; 540 541 num_entries = max(num_amps, amp_index + 1); 542 if (!data || (data->count < num_entries)) { 543 struct cirrus_amp_efi_data *new_data; 544 unsigned int new_data_size = struct_size(data, data, num_entries); 545 546 new_data = kzalloc(new_data_size, GFP_KERNEL); 547 if (!new_data) { 548 ret = -ENOMEM; 549 goto err; 550 } 551 552 if (data) { 553 memcpy(new_data, data, struct_size(data, data, data->count)); 554 kfree(data); 555 } 556 557 data = new_data; 558 data->count = num_entries; 559 data->size = new_data_size; 560 } 561 562 data->data[amp_index] = *in_data; 563 ret = cs_amp_set_cal_efi_buffer(dev, name, guid, attr, data); 564 if (ret) 565 dev_err(dev, "Failed writing calibration to EFI: %d\n", ret); 566 err: 567 kfree(data); 568 569 return ret; 570 } 571 572 /** 573 * cs_amp_get_efi_calibration_data - get an entry from calibration data in EFI. 574 * @dev: struct device of the caller. 575 * @target_uid: UID to match, or zero to ignore UID matching. 576 * @amp_index: Entry index to use, or -1 to prevent lookup by index. 577 * @out_data: struct cirrus_amp_cal_data where the entry will be copied. 578 * 579 * This function can perform 3 types of lookup: 580 * 581 * (target_uid > 0, amp_index >= 0) 582 * UID search with fallback to using the array index. 583 * Search the calibration data for a non-zero calTarget that matches 584 * target_uid, and if found return that entry. Else, if the entry at 585 * [amp_index] has calTarget == 0, return that entry. Else fail. 586 * 587 * (target_uid > 0, amp_index < 0) 588 * UID search only. 589 * Search the calibration data for a non-zero calTarget that matches 590 * target_uid, and if found return that entry. Else fail. 591 * 592 * (target_uid == 0, amp_index >= 0) 593 * Array index fetch only. 594 * Return the entry at [amp_index]. 595 * 596 * An array lookup will be skipped if amp_index exceeds the number of 597 * entries in the calibration array, and in this case the return will 598 * be -ENOENT. An out-of-range amp_index does not prevent matching by 599 * target_uid - it has the same effect as passing amp_index < 0. 600 * 601 * If the EFI data is too short to be a valid entry, or the entry count 602 * in the EFI data overflows the actual length of the data, this function 603 * returns -EOVERFLOW. 604 * 605 * Return: 0 if the entry was found, -ENOENT if no entry was found, 606 * -EOVERFLOW if the EFI file is corrupt, else other error value. 607 */ 608 int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index, 609 struct cirrus_amp_cal_data *out_data) 610 { 611 if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS)) 612 return _cs_amp_get_efi_calibration_data(dev, target_uid, amp_index, out_data); 613 else 614 return -ENOENT; 615 } 616 EXPORT_SYMBOL_NS_GPL(cs_amp_get_efi_calibration_data, "SND_SOC_CS_AMP_LIB"); 617 618 /** 619 * cs_amp_set_efi_calibration_data - write a calibration data entry to EFI. 620 * @dev: struct device of the caller. 621 * @amp_index: Entry index to use, or -1 to use any available slot. 622 * @num_amps: Maximum number of amps to reserve slots for, or -1 to ignore. 623 * @in_data: struct cirrus_amp_cal_data entry to be written to EFI. 624 * 625 * If a Vendor-specific variable exists it will be updated, 626 * else if the Cirrus variable exists it will be updated 627 * else the Cirrus variable will be created. 628 * 629 * If amp_index >= 0 the data will be placed in this entry of the calibration 630 * data array, overwriting what was in that entry. Any other entries with the 631 * same calTarget will be marked empty. 632 * 633 * If amp_index < 0 and in_data->calTarget matches any existing entry, that 634 * entry will be overwritten. Else the first available free entry will be used, 635 * extending the size of the EFI variable if there are no free entries. 636 * 637 * If num_amps > 0 the EFI variable will be sized to contain at least this 638 * many calibration entries, with any new entries marked empty. 639 * 640 * Return: 0 if the write was successful, -EFBIG if space could not be made in 641 * the EFI file to add the entry, -EACCES if it was not possible to 642 * read or write the EFI variable. 643 */ 644 int cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, int num_amps, 645 const struct cirrus_amp_cal_data *in_data) 646 { 647 if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS)) { 648 scoped_guard(mutex, &cs_amp_efi_cal_write_lock) { 649 return _cs_amp_set_efi_calibration_data(dev, amp_index, 650 num_amps, in_data); 651 } 652 } 653 654 return -ENOENT; 655 } 656 EXPORT_SYMBOL_NS_GPL(cs_amp_set_efi_calibration_data, "SND_SOC_CS_AMP_LIB"); 657 658 struct cs_amp_spkid_efi { 659 efi_char16_t *name; 660 efi_guid_t *guid; 661 u8 values[2]; 662 }; 663 664 static int cs_amp_get_efi_byte_spkid(struct device *dev, const struct cs_amp_spkid_efi *info) 665 { 666 efi_status_t status; 667 unsigned long size; 668 u8 spkid; 669 int i, ret; 670 671 size = sizeof(spkid); 672 status = cs_amp_get_efi_variable(info->name, info->guid, NULL, &size, &spkid); 673 ret = cs_amp_convert_efi_status(status); 674 if (ret < 0) 675 return ret; 676 677 if (size == 0) 678 return -ENOENT; 679 680 for (i = 0; i < ARRAY_SIZE(info->values); i++) { 681 if (info->values[i] == spkid) 682 return i; 683 } 684 685 dev_err(dev, "EFI speaker ID bad value %#x\n", spkid); 686 687 return -EINVAL; 688 } 689 690 static const struct cs_amp_spkid_efi cs_amp_spkid_byte_types[] = { 691 { 692 .name = LENOVO_SPEAKER_ID_EFI_NAME, 693 .guid = &LENOVO_SPEAKER_ID_EFI_GUID, 694 .values = { 0xd0, 0xd1 }, 695 }, 696 { 697 .name = HP_SPEAKER_ID_EFI_NAME, 698 .guid = &HP_SPEAKER_ID_EFI_GUID, 699 .values = { 0x30, 0x31 }, 700 }, 701 }; 702 703 /** 704 * cs_amp_get_vendor_spkid - get a speaker ID from vendor-specific storage 705 * @dev: pointer to struct device 706 * 707 * Known vendor-specific methods of speaker ID are checked and if one is 708 * found its speaker ID value is returned. 709 * 710 * Return: >=0 is a valid speaker ID. -ENOENT if a vendor-specific method 711 * was not found. -EACCES if the vendor-specific storage could not 712 * be read. Other error values indicate that the data from the 713 * vendor-specific storage was found but could not be understood. 714 */ 715 int cs_amp_get_vendor_spkid(struct device *dev) 716 { 717 int i, ret; 718 719 if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE) && 720 !IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS)) 721 return -ENOENT; 722 723 for (i = 0; i < ARRAY_SIZE(cs_amp_spkid_byte_types); i++) { 724 ret = cs_amp_get_efi_byte_spkid(dev, &cs_amp_spkid_byte_types[i]); 725 if (ret != -ENOENT) 726 return ret; 727 } 728 729 return -ENOENT; 730 } 731 EXPORT_SYMBOL_NS_GPL(cs_amp_get_vendor_spkid, "SND_SOC_CS_AMP_LIB"); 732 733 static const char *cs_amp_devm_get_dell_ssidex(struct device *dev, 734 int ssid_vendor, int ssid_device) 735 { 736 unsigned int hex_prefix; 737 char audio_id[4]; 738 char delim; 739 char *p; 740 int ret; 741 742 if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE) && 743 !IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS)) 744 return ERR_PTR(-ENOENT); 745 746 char *ssidex_buf __free(kfree) = cs_amp_alloc_get_efi_variable(DELL_SSIDEXV2_EFI_NAME, 747 &DELL_SSIDEXV2_EFI_GUID, 748 NULL); 749 ret = PTR_ERR_OR_ZERO(ssidex_buf); 750 if (ret == -ENOENT) 751 return ERR_PTR(-ENOENT); 752 else if (ret < 0) 753 return ssidex_buf; 754 755 /* 756 * SSIDExV2 string is a series of underscore delimited fields. 757 * First field is all or part of the SSID. Second field should be 758 * a 2-character audio hardware id, followed by other identifiers. 759 * Older models did not have the 2-character audio id, so reject 760 * the string if the second field is not 2 characters. 761 */ 762 ret = sscanf(ssidex_buf, "%8x_%2s%c", &hex_prefix, audio_id, &delim); 763 if (ret < 2) 764 return ERR_PTR(-ENOENT); 765 766 if ((ret == 3) && (delim != '_')) 767 return ERR_PTR(-ENOENT); 768 769 if (strlen(audio_id) != 2) 770 return ERR_PTR(-ENOENT); 771 772 p = devm_kstrdup(dev, audio_id, GFP_KERNEL); 773 if (!p) 774 return ERR_PTR(-ENOMEM); 775 776 return p; 777 } 778 779 /** 780 * cs_amp_devm_get_vendor_specific_variant_id - get variant ID string 781 * @dev: pointer to struct device 782 * @ssid_vendor: PCI Subsystem Vendor (-1 if unknown) 783 * @ssid_device: PCI Subsystem Device (-1 if unknown) 784 * 785 * Known vendor-specific hardware identifiers are checked and if one is 786 * found its content is returned as a NUL-terminated string. The returned 787 * string is devm-managed. 788 * 789 * The returned string is not guaranteed to be globally unique. 790 * Generally it should be combined with some other qualifier, such as 791 * PCI SSID, to create a globally unique ID. 792 * 793 * If the caller has a PCI SSID it should pass it in @ssid_vendor and 794 * @ssid_device. If the vendor-spefic ID contains this SSID it will be 795 * stripped from the returned string to prevent duplication. 796 * 797 * If the caller does not have a PCI SSID, pass -1 for @ssid_vendor and 798 * @ssid_device. 799 * 800 * Return: 801 * * a pointer to a devm-managed string 802 * * ERR_PTR(-ENOENT) if no vendor-specific qualifier 803 * * ERR_PTR error value 804 */ 805 const char *cs_amp_devm_get_vendor_specific_variant_id(struct device *dev, 806 int ssid_vendor, 807 int ssid_device) 808 { 809 KUNIT_STATIC_STUB_REDIRECT(cs_amp_devm_get_vendor_specific_variant_id, 810 dev, ssid_vendor, ssid_device); 811 812 if ((ssid_vendor == PCI_VENDOR_ID_DELL) || (ssid_vendor < 0)) 813 return cs_amp_devm_get_dell_ssidex(dev, ssid_vendor, ssid_device); 814 815 return ERR_PTR(-ENOENT); 816 } 817 EXPORT_SYMBOL_NS_GPL(cs_amp_devm_get_vendor_specific_variant_id, "SND_SOC_CS_AMP_LIB"); 818 819 /** 820 * cs_amp_create_debugfs - create a debugfs directory for a device 821 * 822 * @dev: pointer to struct device 823 * 824 * Creates a node under "cirrus_logic" in the root of the debugfs filesystem. 825 * This is for Cirrus-specific debugfs functionality to be grouped in a 826 * defined way, independently of the debugfs provided by ALSA/ASoC. 827 * The general ALSA/ASoC debugfs may not be enabled, and does not necessarily 828 * have a stable layout or naming convention. 829 * 830 * Return: Pointer to the dentry for the created directory, or -ENODEV. 831 */ 832 struct dentry *cs_amp_create_debugfs(struct device *dev) 833 { 834 struct dentry *dir; 835 836 dir = debugfs_lookup("cirrus_logic", NULL); 837 if (!dir) 838 dir = debugfs_create_dir("cirrus_logic", NULL); 839 840 return debugfs_create_dir(dev_name(dev), dir); 841 } 842 EXPORT_SYMBOL_NS_GPL(cs_amp_create_debugfs, "SND_SOC_CS_AMP_LIB"); 843 844 static const struct cs_amp_test_hooks cs_amp_test_hook_ptrs = { 845 .get_efi_variable = cs_amp_get_efi_variable, 846 .set_efi_variable = cs_amp_set_efi_variable, 847 .write_cal_coeff = cs_amp_write_cal_coeff, 848 .read_cal_coeff = cs_amp_read_cal_coeff, 849 }; 850 851 const struct cs_amp_test_hooks * const cs_amp_test_hooks = 852 PTR_IF(IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS), &cs_amp_test_hook_ptrs); 853 EXPORT_SYMBOL_NS_GPL(cs_amp_test_hooks, "SND_SOC_CS_AMP_LIB"); 854 855 MODULE_DESCRIPTION("Cirrus Logic amplifier library"); 856 MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); 857 MODULE_LICENSE("GPL"); 858 MODULE_IMPORT_NS("FW_CS_DSP"); 859