Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

ASoC: cs35l56: Add support for new Dell laptops

Merge series from Richard Fitzgerald <rf@opensource.cirrus.com>:

On new Dell models the driver must read a UEFI variable to get a
variant identifier for the audio hardware. Without this, the driver
cannot know which firmware file to load to the amps.

+741 -25
+3
include/sound/cs-amp-lib.h
··· 58 58 int cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, int num_amps, 59 59 const struct cirrus_amp_cal_data *in_data); 60 60 int cs_amp_get_vendor_spkid(struct device *dev); 61 + const char *cs_amp_devm_get_vendor_specific_variant_id(struct device *dev, 62 + int ssid_vendor, 63 + int ssid_device); 61 64 struct dentry *cs_amp_create_debugfs(struct device *dev); 62 65 63 66 static inline u64 cs_amp_cal_target_u64(const struct cirrus_amp_cal_data *data)
+18
sound/soc/codecs/Kconfig
··· 786 786 config SND_SOC_CS_AMP_LIB 787 787 tristate 788 788 789 + config SND_SOC_CS_AMP_LIB_TEST_HOOKS 790 + bool 791 + depends on SND_SOC_CS_AMP_LIB 792 + 789 793 config SND_SOC_CS_AMP_LIB_TEST 790 794 tristate "KUnit test for Cirrus Logic cs-amp-lib" if !KUNIT_ALL_TESTS 791 795 depends on SND_SOC_CS_AMP_LIB && KUNIT 796 + select SND_SOC_CS_AMP_LIB_TEST_HOOKS 792 797 default KUNIT_ALL_TESTS 793 798 help 794 799 This builds KUnit tests for the Cirrus Logic common ··· 933 928 On most platforms this is not needed. 934 929 935 930 If unsure select "N". 931 + 932 + config SND_SOC_CS35L56_TEST 933 + tristate "KUnit test for Cirrus Logic cs35l56 driver" if !KUNIT_ALL_TESTS 934 + depends on SND_SOC_CS35L56 && KUNIT 935 + default KUNIT_ALL_TESTS 936 + select SND_SOC_CS_AMP_LIB_TEST_HOOKS 937 + help 938 + This builds KUnit tests for the Cirrus Logic cs35l56 939 + codec driver. 940 + For more information on KUnit and unit tests in general, 941 + please refer to the KUnit documentation in 942 + Documentation/dev-tools/kunit/. 943 + If in doubt, say "N". 936 944 endmenu 937 945 938 946 config SND_SOC_CS40L50
+2
sound/soc/codecs/Makefile
··· 81 81 snd-soc-cs35l56-i2c-y := cs35l56-i2c.o 82 82 snd-soc-cs35l56-spi-y := cs35l56-spi.o 83 83 snd-soc-cs35l56-sdw-y := cs35l56-sdw.o 84 + snd-soc-cs35l56-test-y := cs35l56-test.o 84 85 snd-soc-cs40l50-y := cs40l50-codec.o 85 86 snd-soc-cs42l42-y := cs42l42.o 86 87 snd-soc-cs42l42-i2c-y := cs42l42-i2c.o ··· 517 516 obj-$(CONFIG_SND_SOC_CS35L56_I2C) += snd-soc-cs35l56-i2c.o 518 517 obj-$(CONFIG_SND_SOC_CS35L56_SPI) += snd-soc-cs35l56-spi.o 519 518 obj-$(CONFIG_SND_SOC_CS35L56_SDW) += snd-soc-cs35l56-sdw.o 519 + obj-$(CONFIG_SND_SOC_CS35L56_TEST) += snd-soc-cs35l56-test.o 520 520 obj-$(CONFIG_SND_SOC_CS40L50) += snd-soc-cs40l50.o 521 521 obj-$(CONFIG_SND_SOC_CS42L42_CORE) += snd-soc-cs42l42.o 522 522 obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42-i2c.o
+169
sound/soc/codecs/cs-amp-lib-test.c
··· 16 16 #include <linux/list.h> 17 17 #include <linux/module.h> 18 18 #include <linux/overflow.h> 19 + #include <linux/pci_ids.h> 19 20 #include <linux/platform_device.h> 20 21 #include <linux/random.h> 21 22 #include <sound/cs-amp-lib.h> ··· 57 56 struct cs_amp_lib_test_param { 58 57 int num_amps; 59 58 int amp_index; 59 + char *vendor_sysid; 60 + char *expected_sysid; 60 61 }; 61 62 62 63 static struct cirrus_amp_efi_data *cs_amp_lib_test_cal_blob_dup(struct kunit *test) ··· 2308 2305 KUNIT_EXPECT_EQ(test, 1, cs_amp_get_vendor_spkid(dev)); 2309 2306 } 2310 2307 2308 + static efi_status_t cs_amp_lib_test_get_efi_vendor_sysid(efi_char16_t *name, 2309 + efi_guid_t *guid, 2310 + u32 *returned_attr, 2311 + unsigned long *size, 2312 + void *buf) 2313 + { 2314 + struct kunit *test = kunit_get_current_test(); 2315 + const struct cs_amp_lib_test_param *param = test->param_value; 2316 + unsigned int len; 2317 + 2318 + KUNIT_ASSERT_NOT_NULL(test, param->vendor_sysid); 2319 + len = strlen(param->vendor_sysid); 2320 + 2321 + if (*size < len) { 2322 + *size = len; 2323 + return EFI_BUFFER_TOO_SMALL; 2324 + } 2325 + 2326 + KUNIT_ASSERT_NOT_NULL(test, buf); 2327 + memcpy(buf, param->vendor_sysid, len); 2328 + 2329 + return EFI_SUCCESS; 2330 + } 2331 + 2332 + /* Fetch SSIDExV2 string from UEFI */ 2333 + static void cs_amp_lib_test_ssidexv2_fetch(struct kunit *test) 2334 + { 2335 + const struct cs_amp_lib_test_param *param = test->param_value; 2336 + struct cs_amp_lib_test_priv *priv = test->priv; 2337 + struct device *dev = &priv->amp_dev->dev; 2338 + const char *got; 2339 + 2340 + kunit_activate_static_stub(test, 2341 + cs_amp_test_hooks->get_efi_variable, 2342 + cs_amp_lib_test_get_efi_vendor_sysid); 2343 + 2344 + got = cs_amp_devm_get_vendor_specific_variant_id(dev, PCI_VENDOR_ID_DELL, 0xabcd); 2345 + KUNIT_EXPECT_NOT_ERR_OR_NULL(test, got); 2346 + KUNIT_EXPECT_STREQ(test, got, param->expected_sysid); 2347 + } 2348 + 2349 + /* Invalid SSIDExV2 string should be ignored */ 2350 + static void cs_amp_lib_test_ssidexv2_fetch_invalid(struct kunit *test) 2351 + { 2352 + struct cs_amp_lib_test_priv *priv = test->priv; 2353 + struct device *dev = &priv->amp_dev->dev; 2354 + const char *got; 2355 + 2356 + kunit_activate_static_stub(test, 2357 + cs_amp_test_hooks->get_efi_variable, 2358 + cs_amp_lib_test_get_efi_vendor_sysid); 2359 + 2360 + got = cs_amp_devm_get_vendor_specific_variant_id(dev, PCI_VENDOR_ID_DELL, 0xabcd); 2361 + KUNIT_EXPECT_NOT_NULL(test, got); 2362 + KUNIT_EXPECT_EQ(test, PTR_ERR_OR_ZERO(got), -ENOENT); 2363 + } 2364 + 2365 + static void cs_amp_lib_test_ssidexv2_not_dell(struct kunit *test) 2366 + { 2367 + struct cs_amp_lib_test_priv *priv = test->priv; 2368 + struct device *dev = &priv->amp_dev->dev; 2369 + const char *got; 2370 + 2371 + kunit_activate_static_stub(test, 2372 + cs_amp_test_hooks->get_efi_variable, 2373 + cs_amp_lib_test_get_efi_vendor_sysid); 2374 + 2375 + /* Not returned if SSID vendor is not Dell */ 2376 + got = cs_amp_devm_get_vendor_specific_variant_id(dev, PCI_VENDOR_ID_CIRRUS, 0xabcd); 2377 + KUNIT_EXPECT_NOT_NULL(test, got); 2378 + KUNIT_EXPECT_EQ(test, PTR_ERR_OR_ZERO(got), -ENOENT); 2379 + } 2380 + 2381 + static void cs_amp_lib_test_vendor_variant_id_not_found(struct kunit *test) 2382 + { 2383 + struct cs_amp_lib_test_priv *priv = test->priv; 2384 + struct device *dev = &priv->amp_dev->dev; 2385 + const char *got; 2386 + 2387 + kunit_activate_static_stub(test, 2388 + cs_amp_test_hooks->get_efi_variable, 2389 + cs_amp_lib_test_get_efi_variable_none); 2390 + 2391 + got = cs_amp_devm_get_vendor_specific_variant_id(dev, PCI_VENDOR_ID_DELL, 0xabcd); 2392 + KUNIT_EXPECT_NOT_NULL(test, got); 2393 + KUNIT_EXPECT_EQ(test, PTR_ERR_OR_ZERO(got), -ENOENT); 2394 + 2395 + got = cs_amp_devm_get_vendor_specific_variant_id(dev, -1, -1); 2396 + KUNIT_EXPECT_NOT_NULL(test, got); 2397 + KUNIT_EXPECT_EQ(test, PTR_ERR_OR_ZERO(got), -ENOENT); 2398 + } 2399 + 2311 2400 static int cs_amp_lib_test_case_init(struct kunit *test) 2312 2401 { 2313 2402 struct cs_amp_lib_test_priv *priv; ··· 2470 2375 KUNIT_ARRAY_PARAM(cs_amp_lib_test_get_cal, cs_amp_lib_test_get_cal_param_cases, 2471 2376 cs_amp_lib_test_get_cal_param_desc); 2472 2377 2378 + static const struct cs_amp_lib_test_param cs_amp_lib_test_ssidexv2_param_cases[] = { 2379 + { .vendor_sysid = "abcd_00", .expected_sysid = "00" }, 2380 + { .vendor_sysid = "abcd_01", .expected_sysid = "01" }, 2381 + { .vendor_sysid = "abcd_XY", .expected_sysid = "XY" }, 2382 + 2383 + { .vendor_sysid = "1028abcd_00", .expected_sysid = "00" }, 2384 + { .vendor_sysid = "1028abcd_01", .expected_sysid = "01" }, 2385 + { .vendor_sysid = "1028abcd_XY", .expected_sysid = "XY" }, 2386 + 2387 + { .vendor_sysid = "abcd_00_WF", .expected_sysid = "00" }, 2388 + { .vendor_sysid = "abcd_01_WF", .expected_sysid = "01" }, 2389 + { .vendor_sysid = "abcd_XY_WF", .expected_sysid = "XY" }, 2390 + 2391 + { .vendor_sysid = "1028abcd_00_WF", .expected_sysid = "00" }, 2392 + { .vendor_sysid = "1028abcd_01_WF", .expected_sysid = "01" }, 2393 + { .vendor_sysid = "1028abcd_XY_WF", .expected_sysid = "XY" }, 2394 + 2395 + { .vendor_sysid = "abcd_00_AA_BB", .expected_sysid = "00" }, 2396 + { .vendor_sysid = "abcd_01_AA_BB", .expected_sysid = "01" }, 2397 + { .vendor_sysid = "abcd_XY_AA_BB", .expected_sysid = "XY" }, 2398 + 2399 + { .vendor_sysid = "1028abcd_00_AA_BB", .expected_sysid = "00" }, 2400 + { .vendor_sysid = "1028abcd_01_AA_BB", .expected_sysid = "01" }, 2401 + { .vendor_sysid = "1028abcd_XY_A_BB", .expected_sysid = "XY" }, 2402 + }; 2403 + 2404 + static void cs_amp_lib_test_ssidexv2_param_desc(const struct cs_amp_lib_test_param *param, 2405 + char *desc) 2406 + { 2407 + snprintf(desc, KUNIT_PARAM_DESC_SIZE, "vendor_sysid:'%s' expected_sysid:'%s'", 2408 + param->vendor_sysid, param->expected_sysid); 2409 + } 2410 + 2411 + KUNIT_ARRAY_PARAM(cs_amp_lib_test_ssidexv2, cs_amp_lib_test_ssidexv2_param_cases, 2412 + cs_amp_lib_test_ssidexv2_param_desc); 2413 + 2414 + static const struct cs_amp_lib_test_param cs_amp_lib_test_ssidexv2_invalid_param_cases[] = { 2415 + { .vendor_sysid = "abcd" }, 2416 + { .vendor_sysid = "abcd_0" }, 2417 + { .vendor_sysid = "abcd_1" }, 2418 + { .vendor_sysid = "abcd_0_1" }, 2419 + { .vendor_sysid = "abcd_1_1" }, 2420 + { .vendor_sysid = "abcd_1_X" }, 2421 + { .vendor_sysid = "abcd_1_X" }, 2422 + { .vendor_sysid = "abcd_000" }, 2423 + { .vendor_sysid = "abcd_010" }, 2424 + { .vendor_sysid = "abcd_000_01" }, 2425 + { .vendor_sysid = "abcd_000_01" }, 2426 + 2427 + { .vendor_sysid = "1234abcd" }, 2428 + { .vendor_sysid = "1234abcd_0" }, 2429 + { .vendor_sysid = "1234abcd_1" }, 2430 + { .vendor_sysid = "1234abcd_0_1" }, 2431 + { .vendor_sysid = "1234abcd_1_1" }, 2432 + { .vendor_sysid = "1234abcd_1_X" }, 2433 + { .vendor_sysid = "1234abcd_1_X" }, 2434 + { .vendor_sysid = "1234abcd_000" }, 2435 + { .vendor_sysid = "1234abcd_010" }, 2436 + { .vendor_sysid = "1234abcd_000_01" }, 2437 + { .vendor_sysid = "1234abcd_000_01" }, 2438 + }; 2439 + 2440 + KUNIT_ARRAY_PARAM(cs_amp_lib_test_ssidexv2_invalid, cs_amp_lib_test_ssidexv2_invalid_param_cases, 2441 + cs_amp_lib_test_ssidexv2_param_desc); 2442 + 2473 2443 static struct kunit_case cs_amp_lib_test_cases[] = { 2474 2444 /* Tests for getting calibration data from EFI */ 2475 2445 KUNIT_CASE(cs_amp_lib_test_cal_data_too_short_test), ··· 2593 2433 KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_oversize), 2594 2434 KUNIT_CASE(cs_amp_lib_test_spkid_hp_30), 2595 2435 KUNIT_CASE(cs_amp_lib_test_spkid_hp_31), 2436 + 2437 + /* Test cases for SSIDExV2 */ 2438 + KUNIT_CASE_PARAM(cs_amp_lib_test_ssidexv2_fetch, 2439 + cs_amp_lib_test_ssidexv2_gen_params), 2440 + KUNIT_CASE_PARAM(cs_amp_lib_test_ssidexv2_fetch_invalid, 2441 + cs_amp_lib_test_ssidexv2_invalid_gen_params), 2442 + KUNIT_CASE_PARAM(cs_amp_lib_test_ssidexv2_not_dell, 2443 + cs_amp_lib_test_ssidexv2_gen_params), 2444 + KUNIT_CASE(cs_amp_lib_test_vendor_variant_id_not_found), 2596 2445 2597 2446 { } /* terminator */ 2598 2447 };
+121 -10
sound/soc/codecs/cs-amp-lib.c
··· 15 15 #include <linux/module.h> 16 16 #include <linux/mutex.h> 17 17 #include <linux/overflow.h> 18 + #include <linux/pci_ids.h> 18 19 #include <linux/slab.h> 19 20 #include <linux/timekeeping.h> 20 21 #include <linux/types.h> ··· 36 35 #define HP_CALIBRATION_EFI_NAME L"SmartAmpCalibrationData" 37 36 #define HP_CALIBRATION_EFI_GUID \ 38 37 EFI_GUID(0x53559579, 0x8753, 0x4f5c, 0x91, 0x30, 0xe8, 0x2a, 0xcf, 0xb8, 0xd8, 0x93) 38 + 39 + #define DELL_SSIDEXV2_EFI_NAME L"SSIDexV2Data" 40 + #define DELL_SSIDEXV2_EFI_GUID \ 41 + EFI_GUID(0x6a5f35df, 0x1432, 0x4656, 0x85, 0x97, 0x31, 0x04, 0xd5, 0xbf, 0x3a, 0xb0) 39 42 40 43 static const struct cs_amp_lib_cal_efivar { 41 44 efi_char16_t *name; ··· 211 206 const struct cirrus_amp_cal_controls *controls, 212 207 const struct cirrus_amp_cal_data *data) 213 208 { 214 - if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) 209 + if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS)) 215 210 return _cs_amp_write_cal_coeffs(dsp, controls, data); 216 211 else 217 212 return -ENODEV; ··· 230 225 const struct cirrus_amp_cal_controls *controls, 231 226 struct cirrus_amp_cal_data *data) 232 227 { 233 - if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) 228 + if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS)) 234 229 return _cs_amp_read_cal_coeffs(dsp, controls, data); 235 230 else 236 231 return -ENODEV; ··· 249 244 const struct cirrus_amp_cal_controls *controls, 250 245 u32 temp) 251 246 { 252 - if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) 253 - return cs_amp_write_cal_coeff(dsp, controls, controls->ambient, temp); 254 - else 255 - return -ENODEV; 247 + return cs_amp_write_cal_coeff(dsp, controls, controls->ambient, temp); 256 248 } 257 249 EXPORT_SYMBOL_NS_GPL(cs_amp_write_ambient_temp, "SND_SOC_CS_AMP_LIB"); 258 250 ··· 304 302 default: 305 303 return -EIO; 306 304 } 305 + } 306 + 307 + static void *cs_amp_alloc_get_efi_variable(efi_char16_t *name, 308 + efi_guid_t *guid, 309 + u32 *returned_attr) 310 + { 311 + efi_status_t status; 312 + unsigned long size = 0; 313 + 314 + status = cs_amp_get_efi_variable(name, guid, NULL, &size, NULL); 315 + if (status != EFI_BUFFER_TOO_SMALL) 316 + return ERR_PTR(cs_amp_convert_efi_status(status)); 317 + 318 + /* Over-alloc to ensure strings are always NUL-terminated */ 319 + void *buf __free(kfree) = kzalloc(size + 1, GFP_KERNEL); 320 + if (!buf) 321 + return ERR_PTR(-ENOMEM); 322 + 323 + status = cs_amp_get_efi_variable(name, guid, returned_attr, &size, buf); 324 + if (status != EFI_SUCCESS) 325 + return ERR_PTR(cs_amp_convert_efi_status(status)); 326 + 327 + return_ptr(buf); 307 328 } 308 329 309 330 static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev, ··· 608 583 int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index, 609 584 struct cirrus_amp_cal_data *out_data) 610 585 { 611 - if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) 586 + if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS)) 612 587 return _cs_amp_get_efi_calibration_data(dev, target_uid, amp_index, out_data); 613 588 else 614 589 return -ENOENT; ··· 644 619 int cs_amp_set_efi_calibration_data(struct device *dev, int amp_index, int num_amps, 645 620 const struct cirrus_amp_cal_data *in_data) 646 621 { 647 - if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) { 622 + if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS)) { 648 623 scoped_guard(mutex, &cs_amp_efi_cal_write_lock) { 649 624 return _cs_amp_set_efi_calibration_data(dev, amp_index, 650 625 num_amps, in_data); ··· 717 692 int i, ret; 718 693 719 694 if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE) && 720 - !IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST)) 695 + !IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS)) 721 696 return -ENOENT; 722 697 723 698 for (i = 0; i < ARRAY_SIZE(cs_amp_spkid_byte_types); i++) { ··· 729 704 return -ENOENT; 730 705 } 731 706 EXPORT_SYMBOL_NS_GPL(cs_amp_get_vendor_spkid, "SND_SOC_CS_AMP_LIB"); 707 + 708 + static const char *cs_amp_devm_get_dell_ssidex(struct device *dev, 709 + int ssid_vendor, int ssid_device) 710 + { 711 + unsigned int hex_prefix; 712 + char audio_id[4]; 713 + char delim; 714 + char *p; 715 + int ret; 716 + 717 + if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE) && 718 + !IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS)) 719 + return ERR_PTR(-ENOENT); 720 + 721 + char *ssidex_buf __free(kfree) = cs_amp_alloc_get_efi_variable(DELL_SSIDEXV2_EFI_NAME, 722 + &DELL_SSIDEXV2_EFI_GUID, 723 + NULL); 724 + ret = PTR_ERR_OR_ZERO(ssidex_buf); 725 + if (ret == -ENOENT) 726 + return ERR_PTR(-ENOENT); 727 + else if (ret < 0) 728 + return ssidex_buf; 729 + 730 + /* 731 + * SSIDExV2 string is a series of underscore delimited fields. 732 + * First field is all or part of the SSID. Second field should be 733 + * a 2-character audio hardware id, followed by other identifiers. 734 + * Older models did not have the 2-character audio id, so reject 735 + * the string if the second field is not 2 characters. 736 + */ 737 + ret = sscanf(ssidex_buf, "%8x_%2s%c", &hex_prefix, audio_id, &delim); 738 + if (ret < 2) 739 + return ERR_PTR(-ENOENT); 740 + 741 + if ((ret == 3) && (delim != '_')) 742 + return ERR_PTR(-ENOENT); 743 + 744 + if (strlen(audio_id) != 2) 745 + return ERR_PTR(-ENOENT); 746 + 747 + p = devm_kstrdup(dev, audio_id, GFP_KERNEL); 748 + if (!p) 749 + return ERR_PTR(-ENOMEM); 750 + 751 + return p; 752 + } 753 + 754 + /** 755 + * cs_amp_devm_get_vendor_specific_variant_id - get variant ID string 756 + * @dev: pointer to struct device 757 + * @ssid_vendor: PCI Subsystem Vendor (-1 if unknown) 758 + * @ssid_device: PCI Subsystem Device (-1 if unknown) 759 + * 760 + * Known vendor-specific hardware identifiers are checked and if one is 761 + * found its content is returned as a NUL-terminated string. The returned 762 + * string is devm-managed. 763 + * 764 + * The returned string is not guaranteed to be globally unique. 765 + * Generally it should be combined with some other qualifier, such as 766 + * PCI SSID, to create a globally unique ID. 767 + * 768 + * If the caller has a PCI SSID it should pass it in @ssid_vendor and 769 + * @ssid_device. If the vendor-spefic ID contains this SSID it will be 770 + * stripped from the returned string to prevent duplication. 771 + * 772 + * If the caller does not have a PCI SSID, pass -1 for @ssid_vendor and 773 + * @ssid_device. 774 + * 775 + * Return: 776 + * * a pointer to a devm-managed string 777 + * * ERR_PTR(-ENOENT) if no vendor-specific qualifier 778 + * * ERR_PTR error value 779 + */ 780 + const char *cs_amp_devm_get_vendor_specific_variant_id(struct device *dev, 781 + int ssid_vendor, 782 + int ssid_device) 783 + { 784 + KUNIT_STATIC_STUB_REDIRECT(cs_amp_devm_get_vendor_specific_variant_id, 785 + dev, ssid_vendor, ssid_device); 786 + 787 + if ((ssid_vendor == PCI_VENDOR_ID_DELL) || (ssid_vendor < 0)) 788 + return cs_amp_devm_get_dell_ssidex(dev, ssid_vendor, ssid_device); 789 + 790 + return ERR_PTR(-ENOENT); 791 + } 792 + EXPORT_SYMBOL_NS_GPL(cs_amp_devm_get_vendor_specific_variant_id, "SND_SOC_CS_AMP_LIB"); 732 793 733 794 /** 734 795 * cs_amp_create_debugfs - create a debugfs directory for a device ··· 849 738 }; 850 739 851 740 const struct cs_amp_test_hooks * const cs_amp_test_hooks = 852 - PTR_IF(IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST), &cs_amp_test_hook_ptrs); 741 + PTR_IF(IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST_HOOKS), &cs_amp_test_hook_ptrs); 853 742 EXPORT_SYMBOL_NS_GPL(cs_amp_test_hooks, "SND_SOC_CS_AMP_LIB"); 854 743 855 744 MODULE_DESCRIPTION("Cirrus Logic amplifier library");
+365
sound/soc/codecs/cs35l56-test.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + // 3 + // KUnit test for the Cirrus Logic cs35l56 driver. 4 + // 5 + // Copyright (C) 2026 Cirrus Logic, Inc. and 6 + // Cirrus Logic International Semiconductor Ltd. 7 + 8 + #include <kunit/resource.h> 9 + #include <kunit/test.h> 10 + #include <kunit/static_stub.h> 11 + #include <linux/efi.h> 12 + #include <linux/device/faux.h> 13 + #include <linux/firmware/cirrus/cs_dsp.h> 14 + #include <linux/firmware/cirrus/wmfw.h> 15 + #include <linux/module.h> 16 + #include <linux/overflow.h> 17 + #include <linux/pci_ids.h> 18 + #include <linux/soundwire/sdw.h> 19 + #include <sound/cs35l56.h> 20 + #include <sound/cs-amp-lib.h> 21 + #include "cs35l56.h" 22 + 23 + KUNIT_DEFINE_ACTION_WRAPPER(faux_device_destroy_wrapper, faux_device_destroy, 24 + struct faux_device *) 25 + 26 + struct cs35l56_test_priv { 27 + struct faux_device *amp_dev; 28 + struct cs35l56_private *cs35l56_priv; 29 + 30 + const char *ssidexv2; 31 + }; 32 + 33 + struct cs35l56_test_param { 34 + u8 type; 35 + u8 rev; 36 + }; 37 + 38 + static const char *cs35l56_test_devm_get_vendor_specific_variant_id_none(struct device *dev, 39 + int ssid_vendor, 40 + int ssid_device) 41 + { 42 + return ERR_PTR(-ENOENT); 43 + } 44 + 45 + static void cs35l56_test_l56_b0_suffix_sdw(struct kunit *test) 46 + { 47 + struct cs35l56_test_priv *priv = test->priv; 48 + struct cs35l56_private *cs35l56 = priv->cs35l56_priv; 49 + 50 + /* Set device type info */ 51 + cs35l56->base.type = 0x56; 52 + cs35l56->base.rev = 0xb0; 53 + 54 + /* Set the ALSA name prefix */ 55 + cs35l56->component->name_prefix = "AMP1"; 56 + 57 + /* Set SoundWire link and UID number */ 58 + cs35l56->sdw_link_num = 1; 59 + cs35l56->sdw_unique_id = 5; 60 + 61 + kunit_activate_static_stub(test, 62 + cs35l56_test_devm_get_vendor_specific_variant_id_none, 63 + cs_amp_devm_get_vendor_specific_variant_id); 64 + 65 + KUNIT_EXPECT_EQ(test, 0, cs35l56_set_fw_suffix(cs35l56)); 66 + 67 + /* Priority suffix should be the legacy ALSA prefix */ 68 + KUNIT_EXPECT_STREQ(test, cs35l56->dsp.fwf_suffix, "AMP1"); 69 + 70 + /* Fallback suffix should be the new SoundWire ID */ 71 + KUNIT_EXPECT_STREQ(test, cs35l56->fallback_fw_suffix, "l1u5"); 72 + } 73 + 74 + static void cs35l56_test_suffix_sdw(struct kunit *test) 75 + { 76 + struct cs35l56_test_priv *priv = test->priv; 77 + struct cs35l56_private *cs35l56 = priv->cs35l56_priv; 78 + 79 + /* Set the ALSA name prefix */ 80 + cs35l56->component->name_prefix = "AMP1"; 81 + 82 + /* Set SoundWire link and UID number */ 83 + cs35l56->sdw_link_num = 1; 84 + cs35l56->sdw_unique_id = 5; 85 + 86 + kunit_activate_static_stub(test, 87 + cs35l56_test_devm_get_vendor_specific_variant_id_none, 88 + cs_amp_devm_get_vendor_specific_variant_id); 89 + 90 + KUNIT_EXPECT_EQ(test, 0, cs35l56_set_fw_suffix(cs35l56)); 91 + 92 + /* Suffix should be the SoundWire ID without a fallback */ 93 + KUNIT_EXPECT_STREQ(test, cs35l56->dsp.fwf_suffix, "l1u5"); 94 + KUNIT_EXPECT_NULL(test, cs35l56->fallback_fw_suffix); 95 + } 96 + 97 + static void cs35l56_test_suffix_i2cspi(struct kunit *test) 98 + { 99 + struct cs35l56_test_priv *priv = test->priv; 100 + struct cs35l56_private *cs35l56 = priv->cs35l56_priv; 101 + 102 + /* Set the ALSA name prefix */ 103 + cs35l56->component->name_prefix = "AMP1"; 104 + 105 + kunit_activate_static_stub(test, 106 + cs35l56_test_devm_get_vendor_specific_variant_id_none, 107 + cs_amp_devm_get_vendor_specific_variant_id); 108 + 109 + KUNIT_EXPECT_EQ(test, 0, cs35l56_set_fw_suffix(cs35l56)); 110 + 111 + /* Suffix strings should not be set: use default wm_adsp suffixing */ 112 + KUNIT_EXPECT_NULL(test, cs35l56->dsp.fwf_suffix); 113 + KUNIT_EXPECT_NULL(test, cs35l56->fallback_fw_suffix); 114 + } 115 + 116 + static efi_status_t cs35l56_test_get_efi_ssidexv2(efi_char16_t *name, 117 + efi_guid_t *guid, 118 + u32 *returned_attr, 119 + unsigned long *size, 120 + void *buf) 121 + { 122 + struct kunit *test = kunit_get_current_test(); 123 + struct cs35l56_test_priv *priv = test->priv; 124 + unsigned int len; 125 + 126 + KUNIT_ASSERT_NOT_NULL(test, priv->ssidexv2); 127 + len = strlen(priv->ssidexv2); 128 + 129 + if (*size < len) { 130 + *size = len; 131 + return EFI_BUFFER_TOO_SMALL; 132 + } 133 + 134 + KUNIT_ASSERT_NOT_NULL(test, buf); 135 + memcpy(buf, priv->ssidexv2, len); 136 + 137 + return EFI_SUCCESS; 138 + } 139 + 140 + static void cs35l56_test_ssidexv2_suffix_sdw(struct kunit *test) 141 + { 142 + struct cs35l56_test_priv *priv = test->priv; 143 + struct cs35l56_private *cs35l56 = priv->cs35l56_priv; 144 + 145 + /* Set the ALSA name prefix */ 146 + cs35l56->component->name_prefix = "AMP1"; 147 + 148 + /* Set SoundWire link and UID number */ 149 + cs35l56->sdw_link_num = 1; 150 + cs35l56->sdw_unique_id = 5; 151 + 152 + /* Set a SSID to enable lookup of SSIDExV2 */ 153 + snd_soc_card_set_pci_ssid(cs35l56->component->card, PCI_VENDOR_ID_DELL, 0x1234); 154 + 155 + priv->ssidexv2 = "10281234_01_BB_CC"; 156 + 157 + kunit_activate_static_stub(test, 158 + cs_amp_test_hooks->get_efi_variable, 159 + cs35l56_test_get_efi_ssidexv2); 160 + 161 + KUNIT_EXPECT_EQ(test, 0, cs35l56_set_fw_suffix(cs35l56)); 162 + 163 + /* Priority suffix should be the SSIDExV2 string with SoundWire ID */ 164 + KUNIT_EXPECT_STREQ(test, cs35l56->dsp.fwf_suffix, "01-l1u5"); 165 + 166 + /* Fallback suffix should be the SoundWireID */ 167 + KUNIT_EXPECT_STREQ(test, cs35l56->fallback_fw_suffix, "l1u5"); 168 + } 169 + 170 + static void cs35l56_test_ssidexv2_suffix_i2cspi(struct kunit *test) 171 + { 172 + struct cs35l56_test_priv *priv = test->priv; 173 + struct cs35l56_private *cs35l56 = priv->cs35l56_priv; 174 + 175 + /* Set the ALSA name prefix */ 176 + cs35l56->component->name_prefix = "AMP1"; 177 + 178 + /* Set a SSID to enable lookup of SSIDExV2 */ 179 + snd_soc_card_set_pci_ssid(cs35l56->component->card, PCI_VENDOR_ID_DELL, 0x1234); 180 + 181 + priv->ssidexv2 = "10281234_01_BB_CC"; 182 + 183 + kunit_activate_static_stub(test, 184 + cs_amp_test_hooks->get_efi_variable, 185 + cs35l56_test_get_efi_ssidexv2); 186 + 187 + KUNIT_EXPECT_EQ(test, 0, cs35l56_set_fw_suffix(cs35l56)); 188 + 189 + /* Priority suffix should be the SSIDExV2 string with ALSA name prefix */ 190 + KUNIT_EXPECT_STREQ(test, cs35l56->dsp.fwf_suffix, "01-AMP1"); 191 + 192 + /* Fallback suffix should be the ALSA name prefix */ 193 + KUNIT_EXPECT_STREQ(test, cs35l56->fallback_fw_suffix, "AMP1"); 194 + } 195 + 196 + /* 197 + * CS35L56 B0 SoundWire should ignore any SSIDExV2 suffix. It isn't needed 198 + * on any products with B0 silicon and would interfere with the fallback 199 + * to legacy naming convention for early B0-based laptops. 200 + */ 201 + static void cs35l56_test_l56_b0_ssidexv2_ignored_suffix_sdw(struct kunit *test) 202 + { 203 + struct cs35l56_test_priv *priv = test->priv; 204 + struct cs35l56_private *cs35l56 = priv->cs35l56_priv; 205 + 206 + /* Set device type info */ 207 + cs35l56->base.type = 0x56; 208 + cs35l56->base.rev = 0xb0; 209 + 210 + /* Set the ALSA name prefix */ 211 + cs35l56->component->name_prefix = "AMP1"; 212 + 213 + /* Set SoundWire link and UID number */ 214 + cs35l56->sdw_link_num = 1; 215 + cs35l56->sdw_unique_id = 5; 216 + 217 + /* Set a SSID to enable lookup of SSIDExV2 */ 218 + snd_soc_card_set_pci_ssid(cs35l56->component->card, PCI_VENDOR_ID_DELL, 0x1234); 219 + 220 + priv->ssidexv2 = "10281234_01_BB_CC"; 221 + 222 + kunit_activate_static_stub(test, 223 + cs_amp_test_hooks->get_efi_variable, 224 + cs35l56_test_get_efi_ssidexv2); 225 + 226 + KUNIT_EXPECT_EQ(test, 0, cs35l56_set_fw_suffix(cs35l56)); 227 + 228 + /* Priority suffix should be the legacy ALSA prefix */ 229 + KUNIT_EXPECT_STREQ(test, cs35l56->dsp.fwf_suffix, "AMP1"); 230 + 231 + /* Fallback suffix should be the new SoundWire ID */ 232 + KUNIT_EXPECT_STREQ(test, cs35l56->fallback_fw_suffix, "l1u5"); 233 + } 234 + 235 + static int cs35l56_test_case_init_common(struct kunit *test) 236 + { 237 + struct cs35l56_test_priv *priv; 238 + const struct cs35l56_test_param *param = test->param_value; 239 + struct cs35l56_private *cs35l56; 240 + 241 + KUNIT_ASSERT_NOT_NULL(test, cs_amp_test_hooks); 242 + 243 + priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); 244 + if (!priv) 245 + return -ENOMEM; 246 + 247 + test->priv = priv; 248 + 249 + /* Create dummy amp driver dev */ 250 + priv->amp_dev = faux_device_create("cs35l56_test_drv", NULL, NULL); 251 + KUNIT_ASSERT_NOT_NULL(test, priv->amp_dev); 252 + KUNIT_ASSERT_EQ(test, 0, 253 + kunit_add_action_or_reset(test, 254 + faux_device_destroy_wrapper, 255 + priv->amp_dev)); 256 + 257 + /* Construct minimal set of driver structs */ 258 + priv->cs35l56_priv = kunit_kzalloc(test, sizeof(*priv->cs35l56_priv), GFP_KERNEL); 259 + KUNIT_ASSERT_NOT_NULL(test, priv->cs35l56_priv); 260 + cs35l56 = priv->cs35l56_priv; 261 + cs35l56->base.dev = &priv->amp_dev->dev; 262 + 263 + cs35l56->component = kunit_kzalloc(test, sizeof(*cs35l56->component), GFP_KERNEL); 264 + KUNIT_ASSERT_NOT_NULL(test, cs35l56->component); 265 + cs35l56->component->dev = cs35l56->base.dev; 266 + 267 + cs35l56->component->card = kunit_kzalloc(test, sizeof(*cs35l56->component->card), 268 + GFP_KERNEL); 269 + KUNIT_ASSERT_NOT_NULL(test, cs35l56->component->card); 270 + 271 + if (param) { 272 + cs35l56->base.type = param->type; 273 + cs35l56->base.rev = param->rev; 274 + } 275 + 276 + return 0; 277 + } 278 + 279 + static int cs35l56_test_case_init_soundwire(struct kunit *test) 280 + { 281 + struct cs35l56_test_priv *priv; 282 + struct cs35l56_private *cs35l56; 283 + int ret; 284 + 285 + ret = cs35l56_test_case_init_common(test); 286 + if (ret) 287 + return ret; 288 + 289 + priv = test->priv; 290 + cs35l56 = priv->cs35l56_priv; 291 + 292 + /* Dummy to indicate this is Soundwire */ 293 + cs35l56->sdw_peripheral = kunit_kzalloc(test, sizeof(*cs35l56->sdw_peripheral), 294 + GFP_KERNEL); 295 + if (!cs35l56->sdw_peripheral) 296 + return -ENOMEM; 297 + 298 + 299 + return 0; 300 + } 301 + 302 + static void cs35l56_test_type_rev_param_desc(const struct cs35l56_test_param *param, 303 + char *desc) 304 + { 305 + snprintf(desc, KUNIT_PARAM_DESC_SIZE, "type: %02x rev: %02x", 306 + param->type, param->rev); 307 + } 308 + 309 + static const struct cs35l56_test_param cs35l56_test_type_rev_ex_b0_param_cases[] = { 310 + { .type = 0x56, .rev = 0xb2 }, 311 + { .type = 0x57, .rev = 0xb2 }, 312 + { .type = 0x63, .rev = 0xa1 }, 313 + }; 314 + KUNIT_ARRAY_PARAM(cs35l56_test_type_rev_ex_b0, cs35l56_test_type_rev_ex_b0_param_cases, 315 + cs35l56_test_type_rev_param_desc); 316 + 317 + 318 + static const struct cs35l56_test_param cs35l56_test_type_rev_all_param_cases[] = { 319 + { .type = 0x56, .rev = 0xb0 }, 320 + { .type = 0x56, .rev = 0xb2 }, 321 + { .type = 0x57, .rev = 0xb2 }, 322 + { .type = 0x63, .rev = 0xa1 }, 323 + }; 324 + KUNIT_ARRAY_PARAM(cs35l56_test_type_rev_all, cs35l56_test_type_rev_all_param_cases, 325 + cs35l56_test_type_rev_param_desc); 326 + 327 + static struct kunit_case cs35l56_test_cases_soundwire[] = { 328 + KUNIT_CASE(cs35l56_test_l56_b0_suffix_sdw), 329 + KUNIT_CASE_PARAM(cs35l56_test_suffix_sdw, cs35l56_test_type_rev_ex_b0_gen_params), 330 + KUNIT_CASE_PARAM(cs35l56_test_ssidexv2_suffix_sdw, 331 + cs35l56_test_type_rev_ex_b0_gen_params), 332 + KUNIT_CASE(cs35l56_test_l56_b0_ssidexv2_ignored_suffix_sdw), 333 + 334 + { } /* terminator */ 335 + }; 336 + 337 + static struct kunit_case cs35l56_test_cases_not_soundwire[] = { 338 + KUNIT_CASE_PARAM(cs35l56_test_suffix_i2cspi, cs35l56_test_type_rev_all_gen_params), 339 + KUNIT_CASE_PARAM(cs35l56_test_ssidexv2_suffix_i2cspi, 340 + cs35l56_test_type_rev_all_gen_params), 341 + 342 + { } /* terminator */ 343 + }; 344 + 345 + static struct kunit_suite cs35l56_test_suite_soundwire = { 346 + .name = "snd-soc-cs35l56-test-soundwire", 347 + .init = cs35l56_test_case_init_soundwire, 348 + .test_cases = cs35l56_test_cases_soundwire, 349 + }; 350 + 351 + static struct kunit_suite cs35l56_test_suite_not_soundwire = { 352 + .name = "snd-soc-cs35l56-test-not-soundwire", 353 + .init = cs35l56_test_case_init_common, 354 + .test_cases = cs35l56_test_cases_not_soundwire, 355 + }; 356 + 357 + kunit_test_suites( 358 + &cs35l56_test_suite_soundwire, 359 + &cs35l56_test_suite_not_soundwire, 360 + ); 361 + 362 + MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB"); 363 + MODULE_DESCRIPTION("KUnit test for Cirrus Logic cs35l56 codec driver"); 364 + MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); 365 + MODULE_LICENSE("GPL");
+59 -15
sound/soc/codecs/cs35l56.c
··· 5 5 // Copyright (C) 2023 Cirrus Logic, Inc. and 6 6 // Cirrus Logic International Semiconductor Ltd. 7 7 8 + #include <kunit/static_stub.h> 9 + #include <kunit/visibility.h> 8 10 #include <linux/acpi.h> 9 11 #include <linux/array_size.h> 10 12 #include <linux/completion.h> ··· 1109 1107 SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE), 1110 1108 }; 1111 1109 1112 - static int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56) 1110 + VISIBLE_IF_KUNIT int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56) 1113 1111 { 1112 + unsigned short vendor, device; 1113 + const char *vendor_id; 1114 + int ret; 1115 + 1114 1116 if (cs35l56->dsp.fwf_suffix) 1115 1117 return 0; 1116 1118 1117 - if (!cs35l56->sdw_peripheral) 1118 - return 0; 1119 + if (cs35l56->sdw_peripheral) { 1120 + cs35l56->dsp.fwf_suffix = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL, 1121 + "l%uu%u", 1122 + cs35l56->sdw_link_num, 1123 + cs35l56->sdw_unique_id); 1124 + if (!cs35l56->dsp.fwf_suffix) 1125 + return -ENOMEM; 1119 1126 1120 - cs35l56->dsp.fwf_suffix = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL, 1121 - "l%uu%u", 1122 - cs35l56->sdw_link_num, 1123 - cs35l56->sdw_unique_id); 1124 - if (!cs35l56->dsp.fwf_suffix) 1125 - return -ENOMEM; 1127 + /* 1128 + * There are published firmware files for L56 B0 silicon using 1129 + * the ALSA prefix as the filename suffix. Default to trying these 1130 + * first, with the new SoundWire suffix as a fallback. 1131 + * None of these older systems use a vendor-specific ID. 1132 + */ 1133 + if ((cs35l56->base.type == 0x56) && (cs35l56->base.rev == 0xb0)) { 1134 + cs35l56->fallback_fw_suffix = cs35l56->dsp.fwf_suffix; 1135 + cs35l56->dsp.fwf_suffix = cs35l56->component->name_prefix; 1136 + 1137 + return 0; 1138 + } 1139 + } 1126 1140 1127 1141 /* 1128 - * There are published firmware files for L56 B0 silicon using 1129 - * the ALSA prefix as the filename suffix. Default to trying these 1130 - * first, with the new name as an alternate. 1142 + * Some manufacturers use the same SSID on multiple products and have 1143 + * a vendor-specific qualifier to distinguish different models. 1144 + * Models with the same SSID but different qualifier might require 1145 + * different audio firmware, or they might all have the same audio 1146 + * firmware. 1147 + * Try searching for a firmware with this qualifier first, else 1148 + * fallback to standard naming. 1131 1149 */ 1132 - if ((cs35l56->base.type == 0x56) && (cs35l56->base.rev == 0xb0)) { 1133 - cs35l56->fallback_fw_suffix = cs35l56->dsp.fwf_suffix; 1134 - cs35l56->dsp.fwf_suffix = cs35l56->component->name_prefix; 1150 + if (snd_soc_card_get_pci_ssid(cs35l56->component->card, &vendor, &device) < 0) { 1151 + vendor_id = cs_amp_devm_get_vendor_specific_variant_id(cs35l56->base.dev, -1, -1); 1152 + } else { 1153 + vendor_id = cs_amp_devm_get_vendor_specific_variant_id(cs35l56->base.dev, 1154 + vendor, device); 1155 + } 1156 + ret = PTR_ERR_OR_ZERO(vendor_id); 1157 + if (ret == -ENOENT) 1158 + return 0; 1159 + else if (ret) 1160 + return ret; 1161 + 1162 + if (vendor_id) { 1163 + if (cs35l56->dsp.fwf_suffix) 1164 + cs35l56->fallback_fw_suffix = cs35l56->dsp.fwf_suffix; 1165 + else 1166 + cs35l56->fallback_fw_suffix = cs35l56->component->name_prefix; 1167 + 1168 + cs35l56->dsp.fwf_suffix = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL, 1169 + "%s-%s", 1170 + vendor_id, 1171 + cs35l56->fallback_fw_suffix); 1172 + if (!cs35l56->dsp.fwf_suffix) 1173 + return -ENOMEM; 1135 1174 } 1136 1175 1137 1176 return 0; 1138 1177 } 1178 + EXPORT_SYMBOL_IF_KUNIT(cs35l56_set_fw_suffix); 1139 1179 1140 1180 static int cs35l56_component_probe(struct snd_soc_component *component) 1141 1181 {
+4
sound/soc/codecs/cs35l56.h
··· 74 74 int cs35l56_init(struct cs35l56_private *cs35l56); 75 75 void cs35l56_remove(struct cs35l56_private *cs35l56); 76 76 77 + #if IS_ENABLED(CONFIG_KUNIT) 78 + int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56); 79 + #endif 80 + 77 81 #endif /* ifndef CS35L56_H */