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/slab.h> 19 #include <linux/timekeeping.h> 20 #include <linux/types.h> 21 #include <sound/cs-amp-lib.h> 22 23 #define CIRRUS_LOGIC_CALIBRATION_EFI_NAME L"CirrusSmartAmpCalibrationData" 24 #define CIRRUS_LOGIC_CALIBRATION_EFI_GUID \ 25 EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3) 26 27 #define LENOVO_SPEAKER_ID_EFI_NAME L"SdwSpeaker" 28 #define LENOVO_SPEAKER_ID_EFI_GUID \ 29 EFI_GUID(0x48df970e, 0xe27f, 0x460a, 0xb5, 0x86, 0x77, 0x19, 0x80, 0x1d, 0x92, 0x82) 30 31 #define HP_SPEAKER_ID_EFI_NAME L"HPSpeakerID" 32 #define HP_SPEAKER_ID_EFI_GUID \ 33 EFI_GUID(0xc49593a4, 0xd099, 0x419b, 0xa2, 0xc3, 0x67, 0xe9, 0x80, 0xe6, 0x1d, 0x1e) 34 35 #define HP_CALIBRATION_EFI_NAME L"SmartAmpCalibrationData" 36 #define HP_CALIBRATION_EFI_GUID \ 37 EFI_GUID(0x53559579, 0x8753, 0x4f5c, 0x91, 0x30, 0xe8, 0x2a, 0xcf, 0xb8, 0xd8, 0x93) 38 39 static const struct cs_amp_lib_cal_efivar { 40 efi_char16_t *name; 41 efi_guid_t *guid; 42 } cs_amp_lib_cal_efivars[] = { 43 { 44 .name = HP_CALIBRATION_EFI_NAME, 45 .guid = &HP_CALIBRATION_EFI_GUID, 46 }, 47 { 48 .name = CIRRUS_LOGIC_CALIBRATION_EFI_NAME, 49 .guid = &CIRRUS_LOGIC_CALIBRATION_EFI_GUID, 50 }, 51 }; 52 53 #define CS_AMP_CAL_DEFAULT_EFI_ATTR \ 54 (EFI_VARIABLE_NON_VOLATILE | \ 55 EFI_VARIABLE_BOOTSERVICE_ACCESS | \ 56 EFI_VARIABLE_RUNTIME_ACCESS) 57 58 /* Offset from Unix time to Windows time (100ns since 1 Jan 1601) */ 59 #define UNIX_TIME_TO_WINDOWS_TIME_OFFSET 116444736000000000ULL 60 61 static DEFINE_MUTEX(cs_amp_efi_cal_write_lock); 62 63 static u64 cs_amp_time_now_in_windows_time(void) 64 { 65 u64 time_in_100ns = div_u64(ktime_get_real_ns(), 100); 66 67 return time_in_100ns + UNIX_TIME_TO_WINDOWS_TIME_OFFSET; 68 } 69 70 static int cs_amp_write_cal_coeff(struct cs_dsp *dsp, 71 const struct cirrus_amp_cal_controls *controls, 72 const char *ctl_name, u32 val) 73 { 74 struct cs_dsp_coeff_ctl *cs_ctl; 75 __be32 beval = cpu_to_be32(val); 76 int ret; 77 78 KUNIT_STATIC_STUB_REDIRECT(cs_amp_write_cal_coeff, dsp, controls, ctl_name, val); 79 80 if (IS_REACHABLE(CONFIG_FW_CS_DSP)) { 81 mutex_lock(&dsp->pwr_lock); 82 cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, controls->mem_region, controls->alg_id); 83 ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, &beval, sizeof(beval)); 84 mutex_unlock(&dsp->pwr_lock); 85 86 if (ret < 0) { 87 dev_err(dsp->dev, "Failed to write to '%s': %d\n", ctl_name, ret); 88 return ret; 89 } 90 91 return 0; 92 } 93 94 return -ENODEV; 95 } 96 97 static int cs_amp_read_cal_coeff(struct cs_dsp *dsp, 98 const struct cirrus_amp_cal_controls *controls, 99 const char *ctl_name, u32 *val) 100 { 101 struct cs_dsp_coeff_ctl *cs_ctl; 102 __be32 beval; 103 int ret; 104 105 KUNIT_STATIC_STUB_REDIRECT(cs_amp_read_cal_coeff, dsp, controls, ctl_name, val); 106 107 if (!IS_REACHABLE(CONFIG_FW_CS_DSP)) 108 return -ENODEV; 109 110 scoped_guard(mutex, &dsp->pwr_lock) { 111 cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, controls->mem_region, controls->alg_id); 112 ret = cs_dsp_coeff_read_ctrl(cs_ctl, 0, &beval, sizeof(beval)); 113 } 114 115 if (ret < 0) { 116 dev_err(dsp->dev, "Failed to write to '%s': %d\n", ctl_name, ret); 117 return ret; 118 } 119 120 *val = be32_to_cpu(beval); 121 122 return 0; 123 } 124 125 static int _cs_amp_write_cal_coeffs(struct cs_dsp *dsp, 126 const struct cirrus_amp_cal_controls *controls, 127 const struct cirrus_amp_cal_data *data) 128 { 129 int ret; 130 131 dev_dbg(dsp->dev, "Calibration: Ambient=%#x, Status=%#x, CalR=%d\n", 132 data->calAmbient, data->calStatus, data->calR); 133 134 if (list_empty(&dsp->ctl_list)) { 135 dev_info(dsp->dev, "Calibration disabled due to missing firmware controls\n"); 136 return -ENOENT; 137 } 138 139 ret = cs_amp_write_cal_coeff(dsp, controls, controls->ambient, data->calAmbient); 140 if (ret) 141 return ret; 142 143 ret = cs_amp_write_cal_coeff(dsp, controls, controls->calr, data->calR); 144 if (ret) 145 return ret; 146 147 ret = cs_amp_write_cal_coeff(dsp, controls, controls->status, data->calStatus); 148 if (ret) 149 return ret; 150 151 ret = cs_amp_write_cal_coeff(dsp, controls, controls->checksum, data->calR + 1); 152 if (ret) 153 return ret; 154 155 return 0; 156 } 157 158 static int _cs_amp_read_cal_coeffs(struct cs_dsp *dsp, 159 const struct cirrus_amp_cal_controls *controls, 160 struct cirrus_amp_cal_data *data) 161 { 162 u64 time; 163 u32 val; 164 int ret; 165 166 if (list_empty(&dsp->ctl_list)) { 167 dev_info(dsp->dev, "Calibration disabled due to missing firmware controls\n"); 168 return -ENOENT; 169 } 170 171 ret = cs_amp_read_cal_coeff(dsp, controls, controls->ambient, &val); 172 if (ret) 173 return ret; 174 175 data->calAmbient = (s8)val; 176 177 ret = cs_amp_read_cal_coeff(dsp, controls, controls->calr, &val); 178 if (ret) 179 return ret; 180 181 data->calR = (u16)val; 182 183 ret = cs_amp_read_cal_coeff(dsp, controls, controls->status, &val); 184 if (ret) 185 return ret; 186 187 data->calStatus = (u8)val; 188 189 /* Fill in timestamp */ 190 time = cs_amp_time_now_in_windows_time(); 191 data->calTime[0] = (u32)time; 192 data->calTime[1] = (u32)(time >> 32); 193 194 return 0; 195 } 196 197 /** 198 * cs_amp_write_cal_coeffs - Write calibration data to firmware controls. 199 * @dsp: Pointer to struct cs_dsp. 200 * @controls: Pointer to definition of firmware controls to be written. 201 * @data: Pointer to calibration data. 202 * 203 * Returns: 0 on success, else negative error value. 204 */ 205 int cs_amp_write_cal_coeffs(struct cs_dsp *dsp, 206 const struct cirrus_amp_cal_controls *controls, 207 const struct cirrus_amp_cal_data *data) 208 { 209 if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) 210 return _cs_amp_write_cal_coeffs(dsp, controls, data); 211 else 212 return -ENODEV; 213 } 214 EXPORT_SYMBOL_NS_GPL(cs_amp_write_cal_coeffs, "SND_SOC_CS_AMP_LIB"); 215 216 /** 217 * cs_amp_read_cal_coeffs - Read calibration data from firmware controls. 218 * @dsp: Pointer to struct cs_dsp. 219 * @controls: Pointer to definition of firmware controls to be read. 220 * @data: Pointer to calibration data where results will be written. 221 * 222 * Returns: 0 on success, else negative error value. 223 */ 224 int cs_amp_read_cal_coeffs(struct cs_dsp *dsp, 225 const struct cirrus_amp_cal_controls *controls, 226 struct cirrus_amp_cal_data *data) 227 { 228 if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) 229 return _cs_amp_read_cal_coeffs(dsp, controls, data); 230 else 231 return -ENODEV; 232 } 233 EXPORT_SYMBOL_NS_GPL(cs_amp_read_cal_coeffs, "SND_SOC_CS_AMP_LIB"); 234 235 /** 236 * cs_amp_write_ambient_temp - write value to calibration ambient temperature 237 * @dsp: Pointer to struct cs_dsp. 238 * @controls: Pointer to definition of firmware controls to be read. 239 * @temp: Temperature in degrees celcius. 240 * 241 * Returns: 0 on success, else negative error value. 242 */ 243 int cs_amp_write_ambient_temp(struct cs_dsp *dsp, 244 const struct cirrus_amp_cal_controls *controls, 245 u32 temp) 246 { 247 if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) 248 return cs_amp_write_cal_coeff(dsp, controls, controls->ambient, temp); 249 else 250 return -ENODEV; 251 } 252 EXPORT_SYMBOL_NS_GPL(cs_amp_write_ambient_temp, "SND_SOC_CS_AMP_LIB"); 253 254 static efi_status_t cs_amp_get_efi_variable(efi_char16_t *name, 255 efi_guid_t *guid, 256 u32 *returned_attr, 257 unsigned long *size, 258 void *buf) 259 { 260 u32 attr; 261 262 if (!returned_attr) 263 returned_attr = &attr; 264 265 KUNIT_STATIC_STUB_REDIRECT(cs_amp_get_efi_variable, name, guid, 266 returned_attr, size, buf); 267 268 if (efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) 269 return efi.get_variable(name, guid, returned_attr, size, buf); 270 271 return EFI_NOT_FOUND; 272 } 273 274 static efi_status_t cs_amp_set_efi_variable(efi_char16_t *name, 275 efi_guid_t *guid, 276 u32 attr, 277 unsigned long size, 278 void *buf) 279 { 280 KUNIT_STATIC_STUB_REDIRECT(cs_amp_set_efi_variable, name, guid, attr, size, buf); 281 282 if (!efi_rt_services_supported(EFI_RT_SUPPORTED_SET_VARIABLE)) 283 return EFI_NOT_FOUND; 284 285 return efi.set_variable(name, guid, attr, size, buf); 286 } 287 288 static int cs_amp_convert_efi_status(efi_status_t status) 289 { 290 switch (status) { 291 case EFI_SUCCESS: 292 return 0; 293 case EFI_NOT_FOUND: 294 return -ENOENT; 295 case EFI_BUFFER_TOO_SMALL: 296 return -EFBIG; 297 case EFI_WRITE_PROTECTED: 298 case EFI_UNSUPPORTED: 299 case EFI_ACCESS_DENIED: 300 case EFI_SECURITY_VIOLATION: 301 return -EACCES; 302 default: 303 return -EIO; 304 } 305 } 306 307 static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev, 308 efi_char16_t **name, 309 efi_guid_t **guid, 310 u32 *attr) 311 { 312 struct cirrus_amp_efi_data *efi_data; 313 unsigned long data_size = 0; 314 u8 *data; 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 data = kmalloc(data_size, GFP_KERNEL); 343 if (!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, data); 349 if (status != EFI_SUCCESS) { 350 ret = -EINVAL; 351 goto err; 352 } 353 354 efi_data = (struct cirrus_amp_efi_data *)data; 355 dev_dbg(dev, "Calibration: Size=%d, Amp Count=%d\n", efi_data->size, efi_data->count); 356 357 if ((efi_data->count > 128) || 358 struct_size(efi_data, data, efi_data->count) > data_size) { 359 dev_err(dev, "EFI cal variable truncated\n"); 360 ret = -EOVERFLOW; 361 goto err; 362 } 363 364 /* This could be zero-filled space pre-allocated by the BIOS */ 365 if (efi_data->size == 0) 366 efi_data->size = data_size; 367 368 return efi_data; 369 370 err: 371 kfree(data); 372 dev_err(dev, "Failed to read calibration data from EFI: %d\n", ret); 373 374 return ERR_PTR(ret); 375 } 376 377 static int cs_amp_set_cal_efi_buffer(struct device *dev, 378 efi_char16_t *name, 379 efi_guid_t *guid, 380 u32 attr, 381 struct cirrus_amp_efi_data *data) 382 { 383 efi_status_t status; 384 385 status = cs_amp_set_efi_variable(name, guid, attr, 386 struct_size(data, data, data->count), data); 387 388 return cs_amp_convert_efi_status(status); 389 } 390 391 static int _cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index, 392 struct cirrus_amp_cal_data *out_data) 393 { 394 struct cirrus_amp_efi_data *efi_data; 395 struct cirrus_amp_cal_data *cal = NULL; 396 int i, ret; 397 398 efi_data = cs_amp_get_cal_efi_buffer(dev, NULL, NULL, NULL); 399 if (IS_ERR(efi_data)) 400 return PTR_ERR(efi_data); 401 402 if (target_uid) { 403 for (i = 0; i < efi_data->count; ++i) { 404 u64 cal_target = cs_amp_cal_target_u64(&efi_data->data[i]); 405 406 /* Skip empty entries */ 407 if (!efi_data->data[i].calTime[0] && !efi_data->data[i].calTime[1]) 408 continue; 409 410 /* Skip entries with unpopulated silicon ID */ 411 if (cal_target == 0) 412 continue; 413 414 if (cal_target == target_uid) { 415 cal = &efi_data->data[i]; 416 break; 417 } 418 } 419 } 420 421 if (!cal && (amp_index >= 0) && (amp_index < efi_data->count) && 422 (efi_data->data[amp_index].calTime[0] || efi_data->data[amp_index].calTime[1])) { 423 u64 cal_target = cs_amp_cal_target_u64(&efi_data->data[amp_index]); 424 425 /* 426 * Treat unpopulated cal_target as a wildcard. 427 * If target_uid != 0 we can only get here if cal_target == 0 428 * or it didn't match any cal_target value. 429 * If target_uid == 0 it is a wildcard. 430 */ 431 if ((cal_target == 0) || (target_uid == 0)) 432 cal = &efi_data->data[amp_index]; 433 else 434 dev_warn(dev, "Calibration entry %d does not match silicon ID", amp_index); 435 } 436 437 if (cal) { 438 memcpy(out_data, cal, sizeof(*out_data)); 439 ret = 0; 440 } else { 441 dev_warn(dev, "No calibration for silicon ID %#llx\n", target_uid); 442 ret = -ENOENT; 443 } 444 445 kfree(efi_data); 446 447 return ret; 448 } 449 450 static int _cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, int num_amps, 451 const struct cirrus_amp_cal_data *in_data) 452 { 453 u64 cal_target = cs_amp_cal_target_u64(in_data); 454 unsigned long num_entries; 455 struct cirrus_amp_efi_data *data __free(kfree) = NULL; 456 efi_char16_t *name = CIRRUS_LOGIC_CALIBRATION_EFI_NAME; 457 efi_guid_t *guid = &CIRRUS_LOGIC_CALIBRATION_EFI_GUID; 458 u32 attr = CS_AMP_CAL_DEFAULT_EFI_ATTR; 459 int i, ret; 460 461 if (cal_target == 0) 462 return -EINVAL; 463 464 data = cs_amp_get_cal_efi_buffer(dev, &name, &guid, &attr); 465 ret = PTR_ERR_OR_ZERO(data); 466 if (ret == -ENOENT) { 467 data = NULL; 468 goto alloc_new; 469 } else if (ret) { 470 return ret; 471 } 472 473 /* 474 * If the EFI variable is just zero-filled reserved space the count 475 * must be set. 476 */ 477 if (data->count == 0) 478 data->count = (data->size - sizeof(data)) / sizeof(data->data[0]); 479 480 if (amp_index < 0) { 481 /* Is there already a slot for this target? */ 482 for (amp_index = 0; amp_index < data->count; amp_index++) { 483 if (cs_amp_cal_target_u64(&data->data[amp_index]) == cal_target) 484 break; 485 } 486 487 /* Else find an empty slot */ 488 if (amp_index >= data->count) { 489 for (amp_index = 0; amp_index < data->count; amp_index++) { 490 if ((data->data[amp_index].calTime[0] == 0) && 491 (data->data[amp_index].calTime[1] == 0)) 492 break; 493 } 494 } 495 } else { 496 /* 497 * If the index is forced there could be another active 498 * slot with the same calTarget. So deduplicate. 499 */ 500 for (i = 0; i < data->count; i++) { 501 if (i == amp_index) 502 continue; 503 504 if ((data->data[i].calTime[0] == 0) && (data->data[i].calTime[1] == 0)) 505 continue; 506 507 if (cs_amp_cal_target_u64(&data->data[i]) == cal_target) 508 memset(data->data[i].calTime, 0, sizeof(data->data[i].calTime)); 509 } 510 } 511 512 alloc_new: 513 if (amp_index < 0) 514 amp_index = 0; 515 516 num_entries = max(num_amps, amp_index + 1); 517 if (!data || (data->count < num_entries)) { 518 struct cirrus_amp_efi_data *old_data __free(kfree) = no_free_ptr(data); 519 unsigned int new_data_size = struct_size(data, data, num_entries); 520 521 data = kzalloc(new_data_size, GFP_KERNEL); 522 if (!data) 523 return -ENOMEM; 524 525 if (old_data) 526 memcpy(data, old_data, struct_size(old_data, data, old_data->count)); 527 528 data->count = num_entries; 529 data->size = new_data_size; 530 } 531 532 data->data[amp_index] = *in_data; 533 ret = cs_amp_set_cal_efi_buffer(dev, name, guid, attr, data); 534 if (ret) { 535 dev_err(dev, "Failed writing calibration to EFI: %d\n", ret); 536 return ret; 537 } 538 539 return 0; 540 } 541 542 /** 543 * cs_amp_get_efi_calibration_data - get an entry from calibration data in EFI. 544 * @dev: struct device of the caller. 545 * @target_uid: UID to match, or zero to ignore UID matching. 546 * @amp_index: Entry index to use, or -1 to prevent lookup by index. 547 * @out_data: struct cirrus_amp_cal_data where the entry will be copied. 548 * 549 * This function can perform 3 types of lookup: 550 * 551 * (target_uid > 0, amp_index >= 0) 552 * UID search with fallback to using the array index. 553 * Search the calibration data for a non-zero calTarget that matches 554 * target_uid, and if found return that entry. Else, if the entry at 555 * [amp_index] has calTarget == 0, return that entry. Else fail. 556 * 557 * (target_uid > 0, amp_index < 0) 558 * UID search only. 559 * Search the calibration data for a non-zero calTarget that matches 560 * target_uid, and if found return that entry. Else fail. 561 * 562 * (target_uid == 0, amp_index >= 0) 563 * Array index fetch only. 564 * Return the entry at [amp_index]. 565 * 566 * An array lookup will be skipped if amp_index exceeds the number of 567 * entries in the calibration array, and in this case the return will 568 * be -ENOENT. An out-of-range amp_index does not prevent matching by 569 * target_uid - it has the same effect as passing amp_index < 0. 570 * 571 * If the EFI data is too short to be a valid entry, or the entry count 572 * in the EFI data overflows the actual length of the data, this function 573 * returns -EOVERFLOW. 574 * 575 * Return: 0 if the entry was found, -ENOENT if no entry was found, 576 * -EOVERFLOW if the EFI file is corrupt, else other error value. 577 */ 578 int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index, 579 struct cirrus_amp_cal_data *out_data) 580 { 581 if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) 582 return _cs_amp_get_efi_calibration_data(dev, target_uid, amp_index, out_data); 583 else 584 return -ENOENT; 585 } 586 EXPORT_SYMBOL_NS_GPL(cs_amp_get_efi_calibration_data, "SND_SOC_CS_AMP_LIB"); 587 588 /** 589 * cs_amp_set_efi_calibration_data - write a calibration data entry to EFI. 590 * @dev: struct device of the caller. 591 * @amp_index: Entry index to use, or -1 to use any available slot. 592 * @num_amps: Maximum number of amps to reserve slots for, or -1 to ignore. 593 * @in_data: struct cirrus_amp_cal_data entry to be written to EFI. 594 * 595 * If a Vendor-specific variable exists it will be updated, 596 * else if the Cirrus variable exists it will be updated 597 * else the Cirrus variable will be created. 598 * 599 * If amp_index >= 0 the data will be placed in this entry of the calibration 600 * data array, overwriting what was in that entry. Any other entries with the 601 * same calTarget will be marked empty. 602 * 603 * If amp_index < 0 and in_data->calTarget matches any existing entry, that 604 * entry will be overwritten. Else the first available free entry will be used, 605 * extending the size of the EFI variable if there are no free entries. 606 * 607 * If num_amps > 0 the EFI variable will be sized to contain at least this 608 * many calibration entries, with any new entries marked empty. 609 * 610 * Return: 0 if the write was successful, -EFBIG if space could not be made in 611 * the EFI file to add the entry, -EACCES if it was not possible to 612 * read or write the EFI variable. 613 */ 614 int cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, int num_amps, 615 const struct cirrus_amp_cal_data *in_data) 616 { 617 if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) { 618 scoped_guard(mutex, &cs_amp_efi_cal_write_lock) { 619 return _cs_amp_set_efi_calibration_data(dev, amp_index, 620 num_amps, in_data); 621 } 622 } 623 624 return -ENOENT; 625 } 626 EXPORT_SYMBOL_NS_GPL(cs_amp_set_efi_calibration_data, "SND_SOC_CS_AMP_LIB"); 627 628 struct cs_amp_spkid_efi { 629 efi_char16_t *name; 630 efi_guid_t *guid; 631 u8 values[2]; 632 }; 633 634 static int cs_amp_get_efi_byte_spkid(struct device *dev, const struct cs_amp_spkid_efi *info) 635 { 636 efi_status_t status; 637 unsigned long size; 638 u8 spkid; 639 int i, ret; 640 641 size = sizeof(spkid); 642 status = cs_amp_get_efi_variable(info->name, info->guid, NULL, &size, &spkid); 643 ret = cs_amp_convert_efi_status(status); 644 if (ret < 0) 645 return ret; 646 647 if (size == 0) 648 return -ENOENT; 649 650 for (i = 0; i < ARRAY_SIZE(info->values); i++) { 651 if (info->values[i] == spkid) 652 return i; 653 } 654 655 dev_err(dev, "EFI speaker ID bad value %#x\n", spkid); 656 657 return -EINVAL; 658 } 659 660 static const struct cs_amp_spkid_efi cs_amp_spkid_byte_types[] = { 661 { 662 .name = LENOVO_SPEAKER_ID_EFI_NAME, 663 .guid = &LENOVO_SPEAKER_ID_EFI_GUID, 664 .values = { 0xd0, 0xd1 }, 665 }, 666 { 667 .name = HP_SPEAKER_ID_EFI_NAME, 668 .guid = &HP_SPEAKER_ID_EFI_GUID, 669 .values = { 0x30, 0x31 }, 670 }, 671 }; 672 673 /** 674 * cs_amp_get_vendor_spkid - get a speaker ID from vendor-specific storage 675 * @dev: pointer to struct device 676 * 677 * Known vendor-specific methods of speaker ID are checked and if one is 678 * found its speaker ID value is returned. 679 * 680 * Return: >=0 is a valid speaker ID. -ENOENT if a vendor-specific method 681 * was not found. -EACCES if the vendor-specific storage could not 682 * be read. Other error values indicate that the data from the 683 * vendor-specific storage was found but could not be understood. 684 */ 685 int cs_amp_get_vendor_spkid(struct device *dev) 686 { 687 int i, ret; 688 689 if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE) && 690 !IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) 691 return -ENOENT; 692 693 for (i = 0; i < ARRAY_SIZE(cs_amp_spkid_byte_types); i++) { 694 ret = cs_amp_get_efi_byte_spkid(dev, &cs_amp_spkid_byte_types[i]); 695 if (ret != -ENOENT) 696 return ret; 697 } 698 699 return -ENOENT; 700 } 701 EXPORT_SYMBOL_NS_GPL(cs_amp_get_vendor_spkid, "SND_SOC_CS_AMP_LIB"); 702 703 /** 704 * cs_amp_create_debugfs - create a debugfs directory for a device 705 * 706 * @dev: pointer to struct device 707 * 708 * Creates a node under "cirrus_logic" in the root of the debugfs filesystem. 709 * This is for Cirrus-specific debugfs functionality to be grouped in a 710 * defined way, independently of the debugfs provided by ALSA/ASoC. 711 * The general ALSA/ASoC debugfs may not be enabled, and does not necessarily 712 * have a stable layout or naming convention. 713 * 714 * Return: Pointer to the dentry for the created directory, or -ENODEV. 715 */ 716 struct dentry *cs_amp_create_debugfs(struct device *dev) 717 { 718 struct dentry *dir; 719 720 dir = debugfs_lookup("cirrus_logic", NULL); 721 if (!dir) 722 dir = debugfs_create_dir("cirrus_logic", NULL); 723 724 return debugfs_create_dir(dev_name(dev), dir); 725 } 726 EXPORT_SYMBOL_NS_GPL(cs_amp_create_debugfs, "SND_SOC_CS_AMP_LIB"); 727 728 static const struct cs_amp_test_hooks cs_amp_test_hook_ptrs = { 729 .get_efi_variable = cs_amp_get_efi_variable, 730 .set_efi_variable = cs_amp_set_efi_variable, 731 .write_cal_coeff = cs_amp_write_cal_coeff, 732 .read_cal_coeff = cs_amp_read_cal_coeff, 733 }; 734 735 const struct cs_amp_test_hooks * const cs_amp_test_hooks = 736 PTR_IF(IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST), &cs_amp_test_hook_ptrs); 737 EXPORT_SYMBOL_NS_GPL(cs_amp_test_hooks, "SND_SOC_CS_AMP_LIB"); 738 739 MODULE_DESCRIPTION("Cirrus Logic amplifier library"); 740 MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); 741 MODULE_LICENSE("GPL"); 742 MODULE_IMPORT_NS("FW_CS_DSP"); 743