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

ALSA: hda - Keep powering up ADCs on Cirrus codecs

Although one weird behavior about the input path (inconsistent D0/D3
switch) on Cirrus CS420x codecs was fixed in the previous commit,
there is still an issue on some Mac machines: the capture stream
stalls when switching the ADCs on the fly. More badly, this keeps
stuck until the next reboot.

The dynamic ADC switching is already a bit fragile and assuming
optimistically that the chip accepts the frequent power changes. On
Cirrus codecs, this doesn't seem applicable.

As a quick workaround, we pin down the ADCs to keep up in D0 when
spec->dyn_adc_switch is set. In this way, the ADCs are kept up only
for the system that were confirmed to be broken.

Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=116171
Cc: <stable@vger.kernel.org> # v4.4+
Signed-off-by: Takashi Iwai <tiwai@suse.de>

+14
+14
sound/pci/hda/patch_cirrus.c
··· 361 361 { 362 362 struct cs_spec *spec = codec->spec; 363 363 int err; 364 + int i; 364 365 365 366 err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); 366 367 if (err < 0) ··· 370 369 err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); 371 370 if (err < 0) 372 371 return err; 372 + 373 + /* keep the ADCs powered up when it's dynamically switchable */ 374 + if (spec->gen.dyn_adc_switch) { 375 + unsigned int done = 0; 376 + for (i = 0; i < spec->gen.input_mux.num_items; i++) { 377 + int idx = spec->gen.dyn_adc_idx[i]; 378 + if (done & (1 << idx)) 379 + continue; 380 + snd_hda_gen_fix_pin_power(codec, 381 + spec->gen.adc_nids[idx]); 382 + done |= 1 << idx; 383 + } 384 + } 373 385 374 386 return 0; 375 387 }