At Thu, 25 Oct 2007 15:44:42 +0800, zhejiang wrote:
On Tue, 2007-10-23 at 11:11 +0200, Takashi Iwai wrote:
At Mon, 22 Oct 2007 14:08:19 +0800, zhejiang wrote:
Ah, then it's basically similar like 3-stack model but with two headphones instead of a pair of line-out and line-in.
Without your modification of pin config, how the driver detects? Do you have an output of kernel message with debug option about auto pin configs?
I don't have the hardware. I have asked the bug reporter to do the experiment, I will post it here if i get any response.
Thanks.
According to the response from the reporter,here is the pin config of the original driver.
ALSA /tmp/alsa-driver-build/alsa-driver-1.0.15/pci/hda/hda_codec.c:2765: autoconfig: line_outs=3 (0xd/0xc/0xf/0x0/0x0) ALSA /tmp/alsa-driver-build/alsa-driver-1.0.15/pci/hda/hda_codec.c:2769: speaker_outs=0 (0x0/0x0/0x0/0x0/0x0) ALSA /tmp/alsa-driver-build/alsa-driver-1.0.15/pci/hda/hda_codec.c:2773: hp_outs=1 (0xa/0x0/0x0/0x0/0x0) ALSA /tmp/alsa-driver-build/alsa-driver-1.0.15/pci/hda/hda_codec.c:2781: inputs: mic=0x0, fmic=0x0, line=0x0, fline=0x0, cd=0x0, aux=0x0 ALSA /tmp/alsa-driver-build/alsa-driver-1.0.15/pci/hda/../../alsa-kernel/pci/hda/patch_sigmatel.c:1652: stac92xx_add_dyn_out_pins: total dac count=4 ALSA /tmp/alsa-driver-build/alsa-driver-1.0.15/pci/hda/../../alsa-kernel/pci/hda/patch_sigmatel.c:1775: dac_nids=3 (0x2/0x3/0x5/0x0/0x0)
Interesting. Then the driver seems probing a fairly wrong pin mapping. So obviously the PCI SSID table entry for this laptop isn't correct.
Looking through the sigmatel code, we have several known use cases:
- desktop
1a. 3stack + front panel front HP, front mic, rear line-out, rear line-in, rear mic
1b. 6stack + front panel front HP, front mic, rear line-out, rear surr, rear CLFE rear side, rear line-in, rear mic and variants without front panel.
In the case 1a, rear line-in and rear mic can be used as surround outputs.
- laptop
2a. minimal HP, speaker, mic, built-in mic 2b. a la desktop HP, speaker, line-in, mic, built-in mic 2c. dell HP x 2, speaker(s), mic, built-in mic
Now, the case 2c requires a similar hack. But, before going more deeply, it's better to recheck patch_sigmatel.c again. Basically, it has already the code to add output sharing using line-in/mic-in. The problem is that it checks only limited use-cases (1a) and it adds cfg->line_outs and thus this influences on all other places.
The call graph of stac92xx_parse_auto_config() :
1.snd_hda_parse_pin_def_config()
2.stac92xx_add_dyn_out_pins() It check the cfg->line_outs and input_pins[] to add dynamic lineouts.
3.stac92xx_auto_fill_dac_nids() It only fills the dac_nids from the cfg->line_out_pins[],
4.stac92xx_auto_create_multi_out_ctls() It add controls according to the cfg->line_outs.
we can see that they heavily depend on the line_outs[] info.
How about this method?
In stac92xx_add_dyn_out_pins(),we check the cfg->hp_outs,IF IT IS 2,then we switch the cfg->hp_pins[] with cfg->line_out_pins[].
Maybe better to check cfg->line_out_type. I guess it's AUTO_PIN_SPEAKER_OUT in the case of this Dell laptop. The driver takes speaker in prior to headphone as the primary line-out.
The 2,3,4 may work without change. At the end of the stac92xx_parse_auto_config(),we restore the cfg->hp_pins[] and cfg->line_out_pins[].
Alternatively, instead of saving/restoring informatoin, we can fix snd_hda_parse_pin_def_config() itself to check cfg->hp_outs and cfg->speaker_outs to determine the primary output. If one of them is greater, take it as the primary output. It's less hackish but *might* cause some regression in rare cases.
I think that we need to restore the cfg->hp_pins and cfg->line_out_pins for the rest code,because:
- stac92xx_auto_init_hp_out() need to use the hp_pins to enable the
AC_PINCTL_HP_EN
stac92xx_auto_init_multi_out() should check line_out_type and sets HP_EN appropriatley, too.
- stac92xx_hp_detect() need to use the hp_outs to detect jack presense and will disable line_out_pins and speaker_pins.
- stac92xx_init() use the hp_pins to enable unsolicited response.
Hm, these are nasty, indeed.
OK, what about the patch below? It doesn't change the pincfg, so it wouldn't work as is, though.
Takashi
diff -r 09088524dd7f pci/hda/hda_codec.c --- a/pci/hda/hda_codec.c Thu Oct 25 11:46:24 2007 +0200 +++ b/pci/hda/hda_codec.c Thu Oct 25 12:42:00 2007 +0200 @@ -2600,11 +2600,13 @@ int snd_hda_parse_pin_def_config(struct short seq, assoc_line_out, assoc_speaker; short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)]; short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)]; + short sequences_hp[ARRAY_SIZE(cfg->hp_pins)];
memset(cfg, 0, sizeof(*cfg));
memset(sequences_line_out, 0, sizeof(sequences_line_out)); memset(sequences_speaker, 0, sizeof(sequences_speaker)); + memset(sequences_hp, 0, sizeof(sequences_hp)); assoc_line_out = assoc_speaker = 0;
nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid_start); @@ -2659,9 +2661,12 @@ int snd_hda_parse_pin_def_config(struct cfg->speaker_outs++; break; case AC_JACK_HP_OUT: + seq = get_defcfg_sequence(def_conf); + assoc = get_defcfg_association(def_conf); if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins)) continue; cfg->hp_pins[cfg->hp_outs] = nid; + sequences_hp[cfg->hp_outs] = (assoc << 4) | seq; cfg->hp_outs++; break; case AC_JACK_MIC_IN: { @@ -2705,7 +2710,24 @@ int snd_hda_parse_pin_def_config(struct cfg->line_outs); sort_pins_by_sequence(cfg->speaker_pins, sequences_speaker, cfg->speaker_outs); + sort_pins_by_sequence(cfg->hp_pins, sequences_hp, + cfg->hp_outs); + /* if we have only one mic, make it AUTO_PIN_MIC */ + if (!cfg->input_pins[AUTO_PIN_MIC] && + cfg->input_pins[AUTO_PIN_FRONT_MIC]) { + cfg->input_pins[AUTO_PIN_MIC] = + cfg->input_pins[AUTO_PIN_FRONT_MIC]; + cfg->input_pins[AUTO_PIN_FRONT_MIC] = 0; + } + /* ditto for line-in */ + if (!cfg->input_pins[AUTO_PIN_LINE] && + cfg->input_pins[AUTO_PIN_FRONT_LINE]) { + cfg->input_pins[AUTO_PIN_LINE] = + cfg->input_pins[AUTO_PIN_FRONT_LINE]; + cfg->input_pins[AUTO_PIN_FRONT_LINE] = 0; + } + /* * FIX-UP: if no line-outs are detected, try to use speaker or HP pin * as a primary output diff -r 09088524dd7f pci/hda/patch_sigmatel.c --- a/pci/hda/patch_sigmatel.c Thu Oct 25 11:46:24 2007 +0200 +++ b/pci/hda/patch_sigmatel.c Thu Oct 25 12:42:00 2007 +0200 @@ -1649,12 +1649,14 @@ static int stac92xx_add_control(struct s }
/* flag inputs as additional dynamic lineouts */ -static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cfg *cfg) +static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, + struct auto_pin_cfg *cfg, + int line_outs, hda_nid_t *line_out_pins) { struct sigmatel_spec *spec = codec->spec; unsigned int wcaps, wtype; int i, num_dacs = 0; - + /* use the wcaps cache to count all DACs available for line-outs */ for (i = 0; i < codec->num_nodes; i++) { wcaps = codec->wcaps[i]; @@ -1665,49 +1667,48 @@ static int stac92xx_add_dyn_out_pins(str
snd_printdd("%s: total dac count=%d\n", __func__, num_dacs); - switch (cfg->line_outs) { + switch (line_outs) { case 3: /* add line-in as side */ if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 3) { - cfg->line_out_pins[cfg->line_outs] = + line_out_pins[line_outs] = cfg->input_pins[AUTO_PIN_LINE]; spec->line_switch = 1; - cfg->line_outs++; + line_outs++; } break; case 2: /* add line-in as clfe and mic as side */ if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 2) { - cfg->line_out_pins[cfg->line_outs] = + line_out_pins[line_outs] = cfg->input_pins[AUTO_PIN_LINE]; spec->line_switch = 1; - cfg->line_outs++; + line_outs++; } if (cfg->input_pins[AUTO_PIN_MIC] && num_dacs > 3) { - cfg->line_out_pins[cfg->line_outs] = + line_out_pins[line_outs] = cfg->input_pins[AUTO_PIN_MIC]; spec->mic_switch = 1; - cfg->line_outs++; + line_outs++; } break; case 1: /* add line-in as surr and mic as clfe */ if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 1) { - cfg->line_out_pins[cfg->line_outs] = + line_out_pins[line_outs] = cfg->input_pins[AUTO_PIN_LINE]; spec->line_switch = 1; - cfg->line_outs++; + line_outs++; } if (cfg->input_pins[AUTO_PIN_MIC] && num_dacs > 2) { - cfg->line_out_pins[cfg->line_outs] = + line_out_pins[line_outs] = cfg->input_pins[AUTO_PIN_MIC]; spec->mic_switch = 1; - cfg->line_outs++; + line_outs++; } break; } - - return 0; + return line_outs; }
@@ -1731,15 +1732,16 @@ static int is_in_dac_nids(struct sigmate * and 9202/925x. For those, dac_nids[] must be hard-coded. */ static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec, - struct auto_pin_cfg *cfg) + struct auto_pin_cfg *cfg, + int line_outs, hda_nid_t *line_out_pins) { struct sigmatel_spec *spec = codec->spec; int i, j, conn_len = 0; hda_nid_t nid, conn[HDA_MAX_CONNECTIONS]; unsigned int wcaps, wtype; - for (i = 0; i < cfg->line_outs; i++) { - nid = cfg->line_out_pins[i]; + for (i = 0; i < line_outs; i++) { + nid = line_out_pins[i]; conn_len = snd_hda_get_connections(codec, nid, conn, HDA_MAX_CONNECTIONS); for (j = 0; j < conn_len; j++) { @@ -1760,7 +1762,7 @@ static int stac92xx_auto_fill_dac_nids(s /* we have already working output pins, * so let's drop the broken ones again */ - cfg->line_outs = spec->multiout.num_dacs; + line_outs = spec->multiout.num_dacs; break; } /* error out, no available DAC found */ @@ -1787,7 +1789,8 @@ static int stac92xx_auto_fill_dac_nids(s spec->multiout.dac_nids[2], spec->multiout.dac_nids[3], spec->multiout.dac_nids[4]); - return 0; + + return line_outs; }
/* create volume control/switch for the given prefx type */ @@ -2087,6 +2090,8 @@ static int stac92xx_parse_auto_config(st static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out, hda_nid_t dig_in) { struct sigmatel_spec *spec = codec->spec; + int line_outs, *line_out_p; + hda_nid_t *line_out_pins; int err;
if ((err = snd_hda_parse_pin_def_config(codec, @@ -2096,11 +2101,32 @@ static int stac92xx_parse_auto_config(st if (! spec->autocfg.line_outs) return 0; /* can't find valid pin config */
- if ((err = stac92xx_add_dyn_out_pins(codec, &spec->autocfg)) < 0) + /* if we have multiple headphones and no line-outs, + * let's set up headphones as primary outputs and allow + * surround outputs + */ + if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT && + spec->autocfg.hp_outs > 1) { + line_out_p = &spec->autocfg.hp_outs; + line_out_pins = spec->autocfg.hp_pins; + } else { + line_out_p = &spec->autocfg.line_outs; + line_out_pins = spec->autocfg.line_out_pins; + } + line_outs = *line_out_p; + err = stac92xx_add_dyn_out_pins(codec, &spec->autocfg, + line_outs, line_out_pins); + if (err < 0) return err; - if (spec->multiout.num_dacs == 0) - if ((err = stac92xx_auto_fill_dac_nids(codec, &spec->autocfg)) < 0) + line_outs = err; + if (spec->multiout.num_dacs == 0) { + err = stac92xx_auto_fill_dac_nids(codec, &spec->autocfg, + line_outs, line_out_pins); + if (err < 0) return err; + line_outs = err; + } + *line_out_p = line_outs; /* store back to autocfg */
err = stac92xx_auto_create_multi_out_ctls(codec, &spec->autocfg);