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

ALSA: hda: Fix the control element identification for multiple codecs

Some motherboards have multiple HDA codecs connected to the serial bus.
The current code may create multiple mixer controls with the almost
identical identification.

The current code use id.device field from the control element structure
to store the codec address to avoid such clashes for multiple codecs.
Unfortunately, the user space do not handle this correctly. For mixer
controls, only name and index are used for the identifiers.

This patch fixes this problem to compose the index using the codec
address as an offset in case, when the control already exists. It is
really unlikely that one codec will create 10 similar controls.

This patch adds new kernel module parameter 'ctl_dev_id' to allow
select the old behaviour, too. The CONFIG_SND_HDA_CTL_DEV_ID Kconfig
option sets the default value.

BugLink: https://github.com/alsa-project/alsa-lib/issues/294
BugLink: https://github.com/alsa-project/alsa-lib/issues/205
Fixes: 54d174031576 ("[ALSA] hda-codec - Fix connection list parsing")
Fixes: 1afe206ab699 ("ALSA: hda - Try to find an empty control index when it's occupied")
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20230202092013.4066998-1-perex@perex.cz
Signed-off-by: Takashi Iwai <tiwai@suse.de>

authored by

Jaroslav Kysela and committed by
Takashi Iwai
d045bcef 372a0d78

+32 -3
+1
include/sound/hda_codec.h
··· 259 259 unsigned int relaxed_resume:1; /* don't resume forcibly for jack */ 260 260 unsigned int forced_resume:1; /* forced resume for jack */ 261 261 unsigned int no_stream_clean_at_suspend:1; /* do not clean streams at suspend */ 262 + unsigned int ctl_dev_id:1; /* old control element id build behaviour */ 262 263 263 264 #ifdef CONFIG_PM 264 265 unsigned long power_on_acct;
+14
sound/pci/hda/Kconfig
··· 302 302 This feature can impact power consumption as resources 303 303 are kept reserved both at transmitter and receiver. 304 304 305 + config SND_HDA_CTL_DEV_ID 306 + bool "Use the device identifier field for controls" 307 + depends on SND_HDA_INTEL 308 + help 309 + Say Y to use the device identifier field for (mixer) 310 + controls (old behaviour until this option is available). 311 + 312 + When enabled, the multiple HDA codecs may set the device 313 + field in control (mixer) element identifiers. The use 314 + of this field is not recommended and defined for mixer controls. 315 + 316 + The old behaviour (Y) is obsolete and will be removed. Consider 317 + to not enable this option. 318 + 305 319 endif 306 320 307 321 endmenu
+10 -3
sound/pci/hda/hda_codec.c
··· 3389 3389 kctl = snd_ctl_new1(knew, codec); 3390 3390 if (!kctl) 3391 3391 return -ENOMEM; 3392 - if (addr > 0) 3392 + /* Do not use the id.device field for MIXER elements. 3393 + * This field is for real device numbers (like PCM) but codecs 3394 + * are hidden components from the user space view (unrelated 3395 + * to the mixer element identification). 3396 + */ 3397 + if (addr > 0 && codec->ctl_dev_id) 3393 3398 kctl->id.device = addr; 3394 3399 if (idx > 0) 3395 3400 kctl->id.index = idx; ··· 3405 3400 * the codec addr; if it still fails (or it's the 3406 3401 * primary codec), then try another control index 3407 3402 */ 3408 - if (!addr && codec->core.addr) 3403 + if (!addr && codec->core.addr) { 3409 3404 addr = codec->core.addr; 3410 - else if (!idx && !knew->index) { 3405 + if (!codec->ctl_dev_id) 3406 + idx += 10 * addr; 3407 + } else if (!idx && !knew->index) { 3411 3408 idx = find_empty_mixer_ctl_idx(codec, 3412 3409 knew->name, 0); 3413 3410 if (idx <= 0)
+1
sound/pci/hda/hda_controller.c
··· 1231 1231 continue; 1232 1232 codec->jackpoll_interval = chip->jackpoll_interval; 1233 1233 codec->beep_mode = chip->beep_mode; 1234 + codec->ctl_dev_id = chip->ctl_dev_id; 1234 1235 codecs++; 1235 1236 } 1236 1237 }
+1
sound/pci/hda/hda_controller.h
··· 124 124 /* HD codec */ 125 125 int codec_probe_mask; /* copied from probe_mask option */ 126 126 unsigned int beep_mode; 127 + bool ctl_dev_id; 127 128 128 129 #ifdef CONFIG_SND_HDA_PATCH_LOADER 129 130 const struct firmware *fw;
+5
sound/pci/hda/hda_intel.c
··· 119 119 CONFIG_SND_HDA_INPUT_BEEP_MODE}; 120 120 #endif 121 121 static bool dmic_detect = 1; 122 + static bool ctl_dev_id = IS_ENABLED(CONFIG_SND_HDA_CTL_DEV_ID) ? 1 : 0; 122 123 123 124 module_param_array(index, int, NULL, 0444); 124 125 MODULE_PARM_DESC(index, "Index value for Intel HD audio interface."); ··· 158 157 MODULE_PARM_DESC(dmic_detect, "Allow DSP driver selection (bypass this driver) " 159 158 "(0=off, 1=on) (default=1); " 160 159 "deprecated, use snd-intel-dspcfg.dsp_driver option instead"); 160 + module_param(ctl_dev_id, bool, 0444); 161 + MODULE_PARM_DESC(ctl_dev_id, "Use control device identifier (based on codec address)."); 161 162 162 163 #ifdef CONFIG_PM 163 164 static int param_set_xint(const char *val, const struct kernel_param *kp); ··· 2280 2277 #ifdef CONFIG_SND_HDA_INPUT_BEEP 2281 2278 chip->beep_mode = beep_mode[dev]; 2282 2279 #endif 2280 + 2281 + chip->ctl_dev_id = ctl_dev_id; 2283 2282 2284 2283 /* create codec instances */ 2285 2284 if (bus->codec_mask) {