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: Intel: avs: 16 channels support

Merge series from Cezary Rojewski <cezary.rojewski@intel.com>:

Relatively small delta-wise patchset which raises max channels supported
from 8 to 16. The existing limitation is software-based, not hardware
based. The hardware, as per HDAudio specification, section 1.2.2,
(relevant register at SDnFMT, section 3.3.41) supports the
configurations for years. The avs-driver becomes the first consumer of
that configuration on the Linux kernel side.

Set starts off with update to string_helpers so that functionality added
with parse_int_array_user() can be utilized in kernel-kernel
interactions.

Follow up is rasing the cap on HDAudio-library side. The format
selection procedure found in the library is good-to-go as is.

Everything that follows these two patches is avs-driver specific:
- raise channels_max for every DAI-driver template
- provide i2s_test module parameter for testing purposes. When combined
with I2S loopback card, allows to test 16ch on most Intel hardware post
Broadwell era
- adjust TDM masks to reflect the 8 -> 16 channels change

+109 -99
+1
include/linux/string_helpers.h
··· 31 31 int string_get_size(u64 size, u64 blk_size, const enum string_size_units units, 32 32 char *buf, int len); 33 33 34 + int parse_int_array(const char *buf, size_t count, int **array); 34 35 int parse_int_array_user(const char __user *from, size_t count, int **array); 35 36 36 37 #define UNESCAPE_SPACE BIT(0)
+21 -18
lib/string_helpers.c
··· 138 138 } 139 139 EXPORT_SYMBOL(string_get_size); 140 140 141 + int parse_int_array(const char *buf, size_t count, int **array) 142 + { 143 + int *ints, nints; 144 + 145 + get_options(buf, 0, &nints); 146 + if (!nints) 147 + return -ENOENT; 148 + 149 + ints = kcalloc(nints + 1, sizeof(*ints), GFP_KERNEL); 150 + if (!ints) 151 + return -ENOMEM; 152 + 153 + get_options(buf, nints + 1, ints); 154 + *array = ints; 155 + 156 + return 0; 157 + } 158 + EXPORT_SYMBOL(parse_int_array); 159 + 141 160 /** 142 161 * parse_int_array_user - Split string into a sequence of integers 143 162 * @from: The user space buffer to read from ··· 172 153 */ 173 154 int parse_int_array_user(const char __user *from, size_t count, int **array) 174 155 { 175 - int *ints, nints; 176 156 char *buf; 177 - int ret = 0; 157 + int ret; 178 158 179 159 buf = memdup_user_nul(from, count); 180 160 if (IS_ERR(buf)) 181 161 return PTR_ERR(buf); 182 162 183 - get_options(buf, 0, &nints); 184 - if (!nints) { 185 - ret = -ENOENT; 186 - goto free_buf; 187 - } 188 - 189 - ints = kcalloc(nints + 1, sizeof(*ints), GFP_KERNEL); 190 - if (!ints) { 191 - ret = -ENOMEM; 192 - goto free_buf; 193 - } 194 - 195 - get_options(buf, nints + 1, ints); 196 - *array = ints; 197 - 198 - free_buf: 163 + ret = parse_int_array(buf, count, array); 199 164 kfree(buf); 200 165 return ret; 201 166 }
+1 -1
sound/hda/hdac_device.c
··· 801 801 if (!rate_bits[i].hz) 802 802 return 0; 803 803 804 - if (channels == 0 || channels > 8) 804 + if (channels == 0 || channels > 16) 805 805 return 0; 806 806 val |= channels - 1; 807 807
+74 -69
sound/soc/intel/avs/board_selection.c
··· 19 19 #include "avs.h" 20 20 #include "utils.h" 21 21 22 - static bool i2s_test; 23 - module_param(i2s_test, bool, 0444); 24 - MODULE_PARM_DESC(i2s_test, "Probe I2S test-board and skip all other I2S boards"); 22 + static char *i2s_test; 23 + module_param(i2s_test, charp, 0444); 24 + MODULE_PARM_DESC(i2s_test, "Use I2S test-board instead of ACPI, i2s_test=ssp0tdm,ssp1tdm,... 0 to ignore port"); 25 25 26 26 bool obsolete_card_names = IS_ENABLED(CONFIG_SND_SOC_INTEL_AVS_CARDNAME_OBSOLETE); 27 27 module_param_named(obsolete_card_names, obsolete_card_names, bool, 0444); ··· 331 331 {} 332 332 }; 333 333 334 - static struct snd_soc_acpi_mach avs_test_i2s_machines[] = { 335 - { 336 - .drv_name = "avs_i2s_test", 337 - .mach_params = { 338 - .i2s_link_mask = AVS_SSP(0), 339 - }, 340 - .tplg_filename = "i2s-test-tplg.bin", 341 - }, 342 - { 343 - .drv_name = "avs_i2s_test", 344 - .mach_params = { 345 - .i2s_link_mask = AVS_SSP(1), 346 - }, 347 - .tplg_filename = "i2s-test-tplg.bin", 348 - }, 349 - { 350 - .drv_name = "avs_i2s_test", 351 - .mach_params = { 352 - .i2s_link_mask = AVS_SSP(2), 353 - }, 354 - .tplg_filename = "i2s-test-tplg.bin", 355 - }, 356 - { 357 - .drv_name = "avs_i2s_test", 358 - .mach_params = { 359 - .i2s_link_mask = AVS_SSP(3), 360 - }, 361 - .tplg_filename = "i2s-test-tplg.bin", 362 - }, 363 - { 364 - .drv_name = "avs_i2s_test", 365 - .mach_params = { 366 - .i2s_link_mask = AVS_SSP(4), 367 - }, 368 - .tplg_filename = "i2s-test-tplg.bin", 369 - }, 370 - { 371 - .drv_name = "avs_i2s_test", 372 - .mach_params = { 373 - .i2s_link_mask = AVS_SSP(5), 374 - }, 375 - .tplg_filename = "i2s-test-tplg.bin", 376 - }, 377 - /* no NULL terminator, as we depend on ARRAY SIZE due to .id == NULL */ 378 - }; 379 - 380 334 struct avs_acpi_boards { 381 335 int id; 382 336 struct snd_soc_acpi_mach *machs; ··· 462 508 int num_ssps; 463 509 char *name; 464 510 int ret; 511 + int uid; 465 512 466 513 num_ssps = adev->hw_cfg.i2s_caps.ctrl_count; 467 514 if (fls(mach->mach_params.i2s_link_mask) > num_ssps) { ··· 472 517 return -ENODEV; 473 518 } 474 519 475 - name = devm_kasprintf(adev->dev, GFP_KERNEL, "%s.%d-platform", mach->drv_name, 476 - mach->mach_params.i2s_link_mask); 520 + uid = mach->mach_params.i2s_link_mask; 521 + if (avs_mach_singular_ssp(mach)) 522 + uid = (uid << AVS_CHANNELS_MAX) + avs_mach_ssp_tdm(mach, avs_mach_ssp_port(mach)); 523 + 524 + name = devm_kasprintf(adev->dev, GFP_KERNEL, "%s.%d-platform", mach->drv_name, uid); 477 525 if (!name) 478 526 return -ENOMEM; 479 527 ··· 494 536 495 537 mach->mach_params.platform = name; 496 538 497 - board = platform_device_register_data(NULL, mach->drv_name, mach->mach_params.i2s_link_mask, 539 + board = platform_device_register_data(NULL, mach->drv_name, uid, 498 540 (const void *)mach, sizeof(*mach)); 499 541 if (IS_ERR(board)) { 500 542 dev_err(adev->dev, "ssp board register failed\n"); ··· 505 547 if (ret < 0) { 506 548 platform_device_unregister(board); 507 549 return ret; 550 + } 551 + 552 + return 0; 553 + } 554 + 555 + static int avs_register_i2s_test_board(struct avs_dev *adev, int ssp_port, int tdm_slot) 556 + { 557 + struct snd_soc_acpi_mach *mach; 558 + int tdm_mask = BIT(tdm_slot); 559 + unsigned long *tdm_cfg; 560 + char *tplg_name; 561 + int ret; 562 + 563 + mach = devm_kzalloc(adev->dev, sizeof(*mach), GFP_KERNEL); 564 + tdm_cfg = devm_kcalloc(adev->dev, ssp_port + 1, sizeof(unsigned long), GFP_KERNEL); 565 + tplg_name = devm_kasprintf(adev->dev, GFP_KERNEL, AVS_STRING_FMT("i2s", "-test-tplg.bin", 566 + ssp_port, tdm_slot)); 567 + if (!mach || !tdm_cfg || !tplg_name) 568 + return -ENOMEM; 569 + 570 + mach->drv_name = "avs_i2s_test"; 571 + mach->mach_params.i2s_link_mask = AVS_SSP(ssp_port); 572 + tdm_cfg[ssp_port] = tdm_mask; 573 + mach->pdata = tdm_cfg; 574 + mach->tplg_filename = tplg_name; 575 + 576 + ret = avs_register_i2s_board(adev, mach); 577 + if (ret < 0) { 578 + dev_warn(adev->dev, "register i2s %s failed: %d\n", mach->drv_name, ret); 579 + return ret; 580 + } 581 + 582 + return 0; 583 + } 584 + 585 + static int avs_register_i2s_test_boards(struct avs_dev *adev) 586 + { 587 + int max_ssps = adev->hw_cfg.i2s_caps.ctrl_count; 588 + int ssp_port, tdm_slot, ret; 589 + unsigned long tdm_slots; 590 + u32 *array, num_elems; 591 + 592 + ret = parse_int_array(i2s_test, strlen(i2s_test), (int **)&array); 593 + if (ret < 0) { 594 + dev_err(adev->dev, "failed to parse i2s_test parameter\n"); 595 + return ret; 596 + } 597 + 598 + num_elems = *array; 599 + if (num_elems > max_ssps) { 600 + dev_err(adev->dev, "board supports only %d SSP, %d specified\n", 601 + max_ssps, num_elems); 602 + return -EINVAL; 603 + } 604 + 605 + for (ssp_port = 0; ssp_port < num_elems; ssp_port++) { 606 + tdm_slots = array[1 + ssp_port]; 607 + for_each_set_bit(tdm_slot, &tdm_slots, 16) { 608 + ret = avs_register_i2s_test_board(adev, ssp_port, tdm_slot); 609 + if (ret) 610 + return ret; 611 + } 508 612 } 509 613 510 614 return 0; ··· 583 563 return 0; 584 564 } 585 565 586 - if (i2s_test) { 587 - int i, num_ssps; 588 - 589 - num_ssps = adev->hw_cfg.i2s_caps.ctrl_count; 590 - /* constrain just in case FW says there can be more SSPs than possible */ 591 - num_ssps = min_t(int, ARRAY_SIZE(avs_test_i2s_machines), num_ssps); 592 - 593 - mach = avs_test_i2s_machines; 594 - 595 - for (i = 0; i < num_ssps; i++) { 596 - ret = avs_register_i2s_board(adev, &mach[i]); 597 - if (ret < 0) 598 - dev_warn(adev->dev, "register i2s %s failed: %d\n", mach->drv_name, 599 - ret); 600 - } 601 - return 0; 602 - } 566 + if (i2s_test) 567 + return avs_register_i2s_test_boards(adev); 603 568 604 569 boards = avs_get_i2s_boards(adev); 605 570 if (!boards) {
+3 -2
sound/soc/intel/avs/messages.h
··· 699 699 AVS_SAMPLE_TYPE_FLOAT = 4, 700 700 }; 701 701 702 - #define AVS_CHANNELS_MAX 8 702 + #define AVS_COEFF_CHANNELS_MAX 8 703 703 #define AVS_ALL_CHANNELS_MASK UINT_MAX 704 + #define AVS_CHANNELS_MAX 16 704 705 705 706 struct avs_audio_format { 706 707 u32 sampling_freq; ··· 876 875 struct avs_modcfg_base base; 877 876 u32 out_channel_config; 878 877 u32 coefficients_select; 879 - s32 coefficients[AVS_CHANNELS_MAX]; 878 + s32 coefficients[AVS_COEFF_CHANNELS_MAX]; 880 879 u32 channel_map; 881 880 } __packed; 882 881 static_assert(sizeof(struct avs_updown_mixer_cfg) == 84);
+1 -1
sound/soc/intel/avs/path.c
··· 495 495 cfg.base.audio_fmt = *t->in_fmt; 496 496 cfg.out_channel_config = t->cfg_ext->updown_mix.out_channel_config; 497 497 cfg.coefficients_select = t->cfg_ext->updown_mix.coefficients_select; 498 - for (i = 0; i < AVS_CHANNELS_MAX; i++) 498 + for (i = 0; i < AVS_COEFF_CHANNELS_MAX; i++) 499 499 cfg.coefficients[i] = t->cfg_ext->updown_mix.coefficients[i]; 500 500 cfg.channel_map = t->cfg_ext->updown_mix.channel_map; 501 501
+5 -5
sound/soc/intel/avs/pcm.c
··· 1398 1398 static const struct snd_soc_dai_driver i2s_dai_template = { 1399 1399 .playback = { 1400 1400 .channels_min = 1, 1401 - .channels_max = 8, 1401 + .channels_max = AVS_CHANNELS_MAX, 1402 1402 .rates = SNDRV_PCM_RATE_8000_192000 | 1403 1403 SNDRV_PCM_RATE_12000 | 1404 1404 SNDRV_PCM_RATE_24000 | ··· 1411 1411 }, 1412 1412 .capture = { 1413 1413 .channels_min = 1, 1414 - .channels_max = 8, 1414 + .channels_max = AVS_CHANNELS_MAX, 1415 1415 .rates = SNDRV_PCM_RATE_8000_192000 | 1416 1416 SNDRV_PCM_RATE_12000 | 1417 1417 SNDRV_PCM_RATE_24000 | ··· 1473 1473 goto plat_register; 1474 1474 1475 1475 for_each_set_bit(i, &port_mask, ssp_count) { 1476 - for_each_set_bit(j, &tdms[i], ssp_count) { 1476 + for_each_set_bit(j, &tdms[i], AVS_CHANNELS_MAX) { 1477 1477 memcpy(dai, &i2s_dai_template, sizeof(*dai)); 1478 1478 1479 1479 dai->name = ··· 1499 1499 .ops = &avs_dai_hda_be_ops, 1500 1500 .playback = { 1501 1501 .channels_min = 1, 1502 - .channels_max = 8, 1502 + .channels_max = AVS_CHANNELS_MAX, 1503 1503 .rates = SNDRV_PCM_RATE_8000_192000, 1504 1504 .formats = SNDRV_PCM_FMTBIT_S16_LE | 1505 1505 SNDRV_PCM_FMTBIT_S32_LE, ··· 1509 1509 }, 1510 1510 .capture = { 1511 1511 .channels_min = 1, 1512 - .channels_max = 8, 1512 + .channels_max = AVS_CHANNELS_MAX, 1513 1513 .rates = SNDRV_PCM_RATE_8000_192000, 1514 1514 .formats = SNDRV_PCM_FMTBIT_S16_LE | 1515 1515 SNDRV_PCM_FMTBIT_S32_LE,
+2 -2
sound/soc/intel/avs/topology.c
··· 1668 1668 1669 1669 /* See parse_link_formatted_string() for dynamic naming when(s). */ 1670 1670 if (avs_mach_singular_tdm(mach, ssp_port)) { 1671 - /* size is based on possible %d -> SSP:TDM, where SSP and TDM < 10 + '\0' */ 1672 - size_t size = strlen(dw->name) + 2; 1671 + /* size is based on possible %d -> SSP:TDM, where SSP and TDM < 16 + '\0' */ 1672 + size_t size = strlen(dw->name) + 3; 1673 1673 char *buf; 1674 1674 1675 1675 tdm_slot = avs_mach_ssp_tdm(mach, ssp_port);
+1 -1
sound/soc/intel/avs/topology.h
··· 87 87 struct { 88 88 u32 out_channel_config; 89 89 u32 coefficients_select; 90 - s32 coefficients[AVS_CHANNELS_MAX]; 90 + s32 coefficients[AVS_COEFF_CHANNELS_MAX]; 91 91 u32 channel_map; 92 92 } updown_mix; 93 93 struct {