[PATCH 3/3] [UBUNTU:sound/hda] Fix BIOS autoconfig, thereby fixing inaudible sound on recent Dell laptops with STAC9200

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[PATCH 3/3] [UBUNTU:sound/hda] Fix BIOS autoconfig, thereby fixing inaudible sound on recent Dell laptops with STAC9200

Daniel T Chen
UpstreamStatus: Added in upstream pci/hda/hda_codec.c r1.41,
                                  pci/hda/hda_generic.c r1.12,
                                  pci/hda/hda_local.h r1.14,
                                  pci/hda/patch_analog.c r1.20,
                                  pci/hda/patch_realtek.c r1.62,
                                  pci/hda/patch_sigmatel.c r1.20

Takashi Iwai committed this patch to fix autoconfig of speaker/hp
detection. Now the speaker or headphone pins are used if no lineouts
are available (which is the case with recent Dell laptops, among
others, with STAC9200 [ALSA #1843]).

This patch also fixes the analog/realtek/sigmatel autoconfig parsers.

Signed-off-by: Daniel T Chen <[hidden email]>

---

 sound/pci/hda/hda_codec.c      |   77 +++++++++++++++++++++++-
 sound/pci/hda/hda_generic.c    |  128 +++++++++++++++++++++++++++++-----------
 sound/pci/hda/hda_local.h      |    4 +
 sound/pci/hda/patch_analog.c   |   23 +++----
 sound/pci/hda/patch_realtek.c  |   47 ++++-----------
 sound/pci/hda/patch_sigmatel.c |   83 ++++++++++++++++++--------
 6 files changed, 252 insertions(+), 110 deletions(-)

bc802805e2493398641f3e71ec13a79a3bfa6b6b
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index a537397..24f19fc 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -1898,6 +1898,13 @@ int snd_hda_multi_out_analog_prepare(str
  if (mout->hp_nid)
  /* headphone out will just decode front left/right (stereo) */
  snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 0, format);
+ /* extra outputs copied from front */
+ for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
+ if (mout->extra_out_nid[i])
+ snd_hda_codec_setup_stream(codec,
+   mout->extra_out_nid[i],
+   stream_tag, 0, format);
+
  /* surrounds */
  for (i = 1; i < mout->num_dacs; i++) {
  if (chs >= (i + 1) * 2) /* independent out */
@@ -1922,6 +1929,11 @@ int snd_hda_multi_out_analog_cleanup(str
  snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
  if (mout->hp_nid)
  snd_hda_codec_setup_stream(codec, mout->hp_nid, 0, 0, 0);
+ for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
+ if (mout->extra_out_nid[i])
+ snd_hda_codec_setup_stream(codec,
+   mout->extra_out_nid[i],
+   0, 0, 0);
  down(&codec->spdif_mutex);
  if (mout->dig_out_nid && mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
  snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 0, 0, 0);
@@ -1943,13 +1955,29 @@ static int is_in_nid_list(hda_nid_t nid,
  return 0;
 }
 
-/* parse all pin widgets and store the useful pin nids to cfg */
+/*
+ * Parse all pin widgets and store the useful pin nids to cfg
+ *
+ * The number of line-outs or any primary output is stored in line_outs,
+ * and the corresponding output pins are assigned to line_out_pins[],
+ * in the order of front, rear, CLFE, side, ...
+ *
+ * If more extra outputs (speaker and headphone) are found, the pins are
+ * assisnged to hp_pin and speaker_pins[], respectively.  If no line-out jack
+ * is detected, one of speaker of HP pins is assigned as the primary
+ * output, i.e. to line_out_pins[0].  So, line_outs is always positive
+ * if any analog output exists.
+ *
+ * The analog input pins are assigned to input_pins array.
+ * The digital input/output pins are assigned to dig_in_pin and dig_out_pin,
+ * respectively.
+ */
 int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *cfg,
  hda_nid_t *ignore_nids)
 {
  hda_nid_t nid, nid_start;
  int i, j, nodes;
- short seq, sequences[4], assoc_line_out;
+ short seq, assoc_line_out, sequences[ARRAY_SIZE(cfg->line_out_pins)];
 
  memset(cfg, 0, sizeof(*cfg));
 
@@ -1991,7 +2019,10 @@ int snd_hda_parse_pin_def_config(struct
  cfg->line_outs++;
  break;
  case AC_JACK_SPEAKER:
- cfg->speaker_pin = nid;
+ if (cfg->speaker_outs >= ARRAY_SIZE(cfg->speaker_pins))
+ continue;
+ cfg->speaker_pins[cfg->speaker_outs] = nid;
+ cfg->speaker_outs++;
  break;
  case AC_JACK_HP_OUT:
  cfg->hp_pin = nid;
@@ -2056,6 +2087,46 @@ int snd_hda_parse_pin_def_config(struct
  break;
  }
 
+ /*
+ * debug prints of the parsed results
+ */
+ snd_printd("autoconfig: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
+   cfg->line_outs, cfg->line_out_pins[0], cfg->line_out_pins[1],
+   cfg->line_out_pins[2], cfg->line_out_pins[3],
+   cfg->line_out_pins[4]);
+ snd_printd("   speaker_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
+   cfg->speaker_outs, cfg->speaker_pins[0],
+   cfg->speaker_pins[1], cfg->speaker_pins[2],
+   cfg->speaker_pins[3], cfg->speaker_pins[4]);
+ snd_printd("   hp=0x%x, dig_out=0x%x, din_in=0x%x\n",
+   cfg->hp_pin, cfg->dig_out_pin, cfg->dig_in_pin);
+ snd_printd("   inputs: mic=0x%x, fmic=0x%x, line=0x%x, fline=0x%x,"
+   " cd=0x%x, aux=0x%x\n",
+   cfg->input_pins[AUTO_PIN_MIC],
+   cfg->input_pins[AUTO_PIN_FRONT_MIC],
+   cfg->input_pins[AUTO_PIN_LINE],
+   cfg->input_pins[AUTO_PIN_FRONT_LINE],
+   cfg->input_pins[AUTO_PIN_CD],
+   cfg->input_pins[AUTO_PIN_AUX]);
+
+ /*
+ * FIX-UP: if no line-outs are detected, try to use speaker or HP pin
+ * as a primary output
+ */
+ if (! cfg->line_outs) {
+ if (cfg->speaker_outs) {
+ cfg->line_outs = cfg->speaker_outs;
+ memcpy(cfg->line_out_pins, cfg->speaker_pins,
+       sizeof(cfg->speaker_pins));
+ cfg->speaker_outs = 0;
+ memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
+ } else if (cfg->hp_pin) {
+ cfg->line_outs = 1;
+ cfg->line_out_pins[0] = cfg->hp_pin;
+ cfg->hp_pin = 0;
+ }
+ }
+
  return 0;
 }
 
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 00ead66..b7954c8 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -47,10 +47,10 @@ struct hda_gnode {
 
 /* patch-specific record */
 struct hda_gspec {
- struct hda_gnode *dac_node; /* DAC node */
- struct hda_gnode *out_pin_node; /* Output pin (Line-Out) node */
- struct hda_gnode *pcm_vol_node; /* Node for PCM volume */
- unsigned int pcm_vol_index; /* connection of PCM volume */
+ struct hda_gnode *dac_node[2]; /* DAC node */
+ struct hda_gnode *out_pin_node[2]; /* Output pin (Line-Out) node */
+ struct hda_gnode *pcm_vol_node[2]; /* Node for PCM volume */
+ unsigned int pcm_vol_index[2]; /* connection of PCM volume */
 
  struct hda_gnode *adc_node; /* ADC node */
  struct hda_gnode *cap_vol_node; /* Node for capture volume */
@@ -69,8 +69,12 @@ struct hda_gspec {
 /*
  * retrieve the default device type from the default config value
  */
-#define defcfg_type(node) (((node)->def_cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT)
-#define defcfg_location(node) (((node)->def_cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT)
+#define defcfg_type(node) (((node)->def_cfg & AC_DEFCFG_DEVICE) >> \
+   AC_DEFCFG_DEVICE_SHIFT)
+#define defcfg_location(node) (((node)->def_cfg & AC_DEFCFG_LOCATION) >> \
+       AC_DEFCFG_LOCATION_SHIFT)
+#define defcfg_port_conn(node) (((node)->def_cfg & AC_DEFCFG_PORT_CONN) >> \
+ AC_DEFCFG_PORT_CONN_SHIFT)
 
 /*
  * destructor
@@ -261,7 +265,7 @@ static void clear_check_flags(struct hda
  * returns 0 if not found, 1 if found, or a negative error code.
  */
 static int parse_output_path(struct hda_codec *codec, struct hda_gspec *spec,
-     struct hda_gnode *node)
+     struct hda_gnode *node, int dac_idx)
 {
  int i, err;
  struct hda_gnode *child;
@@ -276,14 +280,14 @@ static int parse_output_path(struct hda_
  return 0;
  }
  snd_printdd("AUD_OUT found %x\n", node->nid);
- if (spec->dac_node) {
+ if (spec->dac_node[dac_idx]) {
  /* already DAC node is assigned, just unmute & connect */
- return node == spec->dac_node;
+ return node == spec->dac_node[dac_idx];
  }
- spec->dac_node = node;
+ spec->dac_node[dac_idx] = node;
  if (node->wid_caps & AC_WCAP_OUT_AMP) {
- spec->pcm_vol_node = node;
- spec->pcm_vol_index = 0;
+ spec->pcm_vol_node[dac_idx] = node;
+ spec->pcm_vol_index[dac_idx] = 0;
  }
  return 1; /* found */
  }
@@ -292,7 +296,7 @@ static int parse_output_path(struct hda_
  child = hda_get_node(spec, node->conn_list[i]);
  if (! child)
  continue;
- err = parse_output_path(codec, spec, child);
+ err = parse_output_path(codec, spec, child, dac_idx);
  if (err < 0)
  return err;
  else if (err > 0) {
@@ -303,13 +307,13 @@ static int parse_output_path(struct hda_
  select_input_connection(codec, node, i);
  unmute_input(codec, node, i);
  unmute_output(codec, node);
- if (! spec->pcm_vol_node) {
+ if (! spec->pcm_vol_node[dac_idx]) {
  if (node->wid_caps & AC_WCAP_IN_AMP) {
- spec->pcm_vol_node = node;
- spec->pcm_vol_index = i;
+ spec->pcm_vol_node[dac_idx] = node;
+ spec->pcm_vol_index[dac_idx] = i;
  } else if (node->wid_caps & AC_WCAP_OUT_AMP) {
- spec->pcm_vol_node = node;
- spec->pcm_vol_index = 0;
+ spec->pcm_vol_node[dac_idx] = node;
+ spec->pcm_vol_index[dac_idx] = 0;
  }
  }
  return 1;
@@ -339,6 +343,8 @@ static struct hda_gnode *parse_output_ja
  /* output capable? */
  if (! (node->pin_caps & AC_PINCAP_OUT))
  continue;
+ if (defcfg_port_conn(node) == AC_JACK_PORT_NONE)
+ continue; /* unconnected */
  if (jack_type >= 0) {
  if (jack_type != defcfg_type(node))
  continue;
@@ -350,10 +356,15 @@ static struct hda_gnode *parse_output_ja
  continue;
  }
  clear_check_flags(spec);
- err = parse_output_path(codec, spec, node);
+ err = parse_output_path(codec, spec, node, 0);
  if (err < 0)
  return NULL;
- else if (err > 0) {
+ if (! err && spec->out_pin_node[0]) {
+ err = parse_output_path(codec, spec, node, 1);
+ if (err < 0)
+ return NULL;
+ }
+ if (err > 0) {
  /* unmute the PIN output */
  unmute_output(codec, node);
  /* set PIN-Out enable */
@@ -381,20 +392,28 @@ static int parse_output(struct hda_codec
  /* first, look for the line-out pin */
  node = parse_output_jack(codec, spec, AC_JACK_LINE_OUT);
  if (node) /* found, remember the PIN node */
- spec->out_pin_node = node;
+ spec->out_pin_node[0] = node;
+ else {
+ /* if no line-out is found, try speaker out */
+ node = parse_output_jack(codec, spec, AC_JACK_SPEAKER);
+ if (node)
+ spec->out_pin_node[0] = node;
+ }
  /* look for the HP-out pin */
  node = parse_output_jack(codec, spec, AC_JACK_HP_OUT);
  if (node) {
- if (! spec->out_pin_node)
- spec->out_pin_node = node;
+ if (! spec->out_pin_node[0])
+ spec->out_pin_node[0] = node;
+ else
+ spec->out_pin_node[1] = node;
  }
 
- if (! spec->out_pin_node) {
+ if (! spec->out_pin_node[0]) {
  /* no line-out or HP pins found,
  * then choose for the first output pin
  */
- spec->out_pin_node = parse_output_jack(codec, spec, -1);
- if (! spec->out_pin_node)
+ spec->out_pin_node[0] = parse_output_jack(codec, spec, -1);
+ if (! spec->out_pin_node[0])
  snd_printd("hda_generic: no proper output path found\n");
  }
 
@@ -505,6 +524,9 @@ static int parse_adc_sub_nodes(struct hd
  if (! (node->pin_caps & AC_PINCAP_IN))
  return 0;
 
+ if (defcfg_port_conn(node) == AC_JACK_PORT_NONE)
+ return 0; /* unconnected */
+
  if (node->wid_caps & AC_WCAP_DIGITAL)
  return 0; /* skip SPDIF */
 
@@ -703,12 +725,16 @@ static int check_existing_control(struct
 static int build_output_controls(struct hda_codec *codec)
 {
  struct hda_gspec *spec = codec->spec;
- int err;
+ static const char *types[2] = { "Master", "Headphone" };
+ int i, err;
 
- err = create_mixer(codec, spec->pcm_vol_node, spec->pcm_vol_index,
-   "PCM", "Playback");
- if (err < 0)
- return err;
+ for (i = 0; i < 2 && spec->pcm_vol_node[i]; i++) {
+ err = create_mixer(codec, spec->pcm_vol_node[i],
+   spec->pcm_vol_index[i],
+   types[i], "Playback");
+ if (err < 0)
+ return err;
+ }
  return 0;
 }
 
@@ -805,7 +831,7 @@ static int build_loopback_controls(struc
  int err;
  const char *type;
 
- if (! spec->out_pin_node)
+ if (! spec->out_pin_node[0])
  return 0;
 
  list_for_each(p, &spec->nid_list) {
@@ -820,7 +846,8 @@ static int build_loopback_controls(struc
  if (check_existing_control(codec, type, "Playback"))
  continue;
  clear_check_flags(spec);
- err = parse_loopback_path(codec, spec, spec->out_pin_node,
+ err = parse_loopback_path(codec, spec,
+  spec->out_pin_node[0],
   node, type);
  if (err < 0)
  return err;
@@ -855,12 +882,37 @@ static struct hda_pcm_stream generic_pcm
  .channels_max = 2,
 };
 
+static int generic_pcm2_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ snd_pcm_substream_t *substream)
+{
+ struct hda_gspec *spec = codec->spec;
+
+ snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format);
+ snd_hda_codec_setup_stream(codec, spec->dac_node[1]->nid,
+   stream_tag, 0, format);
+ return 0;
+}
+
+static int generic_pcm2_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ snd_pcm_substream_t *substream)
+{
+ struct hda_gspec *spec = codec->spec;
+
+ snd_hda_codec_setup_stream(codec, hinfo->nid, 0, 0, 0);
+ snd_hda_codec_setup_stream(codec, spec->dac_node[1]->nid, 0, 0, 0);
+ return 0;
+}
+
 static int build_generic_pcms(struct hda_codec *codec)
 {
  struct hda_gspec *spec = codec->spec;
  struct hda_pcm *info = &spec->pcm_rec;
 
- if (! spec->dac_node && ! spec->adc_node) {
+ if (! spec->dac_node[0] && ! spec->adc_node) {
  snd_printd("hda_generic: no PCM found\n");
  return 0;
  }
@@ -869,9 +921,13 @@ static int build_generic_pcms(struct hda
  codec->pcm_info = info;
 
  info->name = "HDA Generic";
- if (spec->dac_node) {
+ if (spec->dac_node[0]) {
  info->stream[0] = generic_pcm_playback;
- info->stream[0].nid = spec->dac_node->nid;
+ info->stream[0].nid = spec->dac_node[0]->nid;
+ if (spec->dac_node[1]) {
+ info->stream[0].ops.prepare = generic_pcm2_prepare;
+ info->stream[0].ops.cleanup = generic_pcm2_cleanup;
+ }
  }
  if (spec->adc_node) {
  info->stream[1] = generic_pcm_playback;
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 5597cd4..be16d9a 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -133,6 +133,7 @@ struct hda_multi_out {
  int num_dacs; /* # of DACs, must be more than 1 */
  hda_nid_t *dac_nids; /* DAC list */
  hda_nid_t hp_nid; /* optional DAC for HP, 0 when not exists */
+ hda_nid_t extra_out_nid[3]; /* optional DACs, 0 when not exists */
  hda_nid_t dig_out_nid; /* digital out audio widget */
  int max_channels; /* currently supported analog channels */
  int dig_out_used; /* current usage of digital out (HDA_DIG_XXX) */
@@ -219,7 +220,8 @@ extern const char *auto_pin_cfg_labels[A
 struct auto_pin_cfg {
  int line_outs;
  hda_nid_t line_out_pins[5]; /* sorted in the order of Front/Surr/CLFE/Side */
- hda_nid_t speaker_pin;
+ int speaker_outs;
+ hda_nid_t speaker_pins[5];
  hda_nid_t hp_pin;
  hda_nid_t input_pins[AUTO_PIN_LAST];
  hda_nid_t dig_out_pin;
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index 147df08..e21418b 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -784,6 +784,8 @@ enum { AD1986A_6STACK, AD1986A_3STACK, A
 static struct hda_board_config ad1986a_cfg_tbl[] = {
  { .modelname = "6stack", .config = AD1986A_6STACK },
  { .modelname = "3stack", .config = AD1986A_3STACK },
+ { .pci_subvendor = 0x10de, .pci_subdevice = 0xcb84,
+  .config = AD1986A_3STACK }, /* ASUS A8N-VM CSM */
  { .modelname = "laptop", .config = AD1986A_LAPTOP },
  { .pci_subvendor = 0x144d, .pci_subdevice = 0xc01e,
   .config = AD1986A_LAPTOP }, /* FSC V2060 */
@@ -2251,14 +2253,11 @@ static int ad1988_auto_create_extra_out(
 
  idx = ad1988_pin_idx(pin);
  nid = ad1988_idx_to_dac(codec, idx);
- if (! spec->multiout.dac_nids[0]) {
- /* use this as the primary output */
- spec->multiout.dac_nids[0] = nid;
- if (! spec->multiout.num_dacs)
- spec->multiout.num_dacs = 1;
- } else
- /* specify the DAC as the extra output */
+ /* specify the DAC as the extra output */
+ if (! spec->multiout.hp_nid)
  spec->multiout.hp_nid = nid;
+ else
+ spec->multiout.extra_out_nid[0] = nid;
  /* control HP volume/switch on the output mixer amp */
  sprintf(name, "%s Playback Volume", pfx);
  if ((err = add_control(spec, AD_CTL_WIDGET_VOL, name,
@@ -2377,7 +2376,7 @@ static void ad1988_auto_init_extra_out(s
  struct ad198x_spec *spec = codec->spec;
  hda_nid_t pin;
 
- pin = spec->autocfg.speaker_pin;
+ pin = spec->autocfg.speaker_pins[0];
  if (pin) /* connect to front */
  ad1988_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
  pin = spec->autocfg.hp_pin;
@@ -2426,13 +2425,13 @@ static int ad1988_parse_auto_config(stru
  return err;
  if ((err = ad1988_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
  return err;
- if (! spec->autocfg.line_outs && ! spec->autocfg.speaker_pin &&
-    ! spec->autocfg.hp_pin)
+ if (! spec->autocfg.line_outs)
  return 0; /* can't find valid BIOS pin config */
  if ((err = ad1988_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
-    (err = ad1988_auto_create_extra_out(codec, spec->autocfg.speaker_pin,
+    (err = ad1988_auto_create_extra_out(codec,
+ spec->autocfg.speaker_pins[0],
  "Speaker")) < 0 ||
-    (err = ad1988_auto_create_extra_out(codec, spec->autocfg.speaker_pin,
+    (err = ad1988_auto_create_extra_out(codec, spec->autocfg.hp_pin,
  "Headphone")) < 0 ||
     (err = ad1988_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
  return err;
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 23ea9fc..d0e92e5 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -2431,14 +2431,11 @@ static int alc880_auto_create_extra_out(
 
  if (alc880_is_fixed_pin(pin)) {
  nid = alc880_idx_to_dac(alc880_fixed_pin_idx(pin));
- if (! spec->multiout.dac_nids[0]) {
- /* use this as the primary output */
- spec->multiout.dac_nids[0] = nid;
- if (! spec->multiout.num_dacs)
- spec->multiout.num_dacs = 1;
- } else
- /* specify the DAC as the extra output */
+ /* specify the DAC as the extra output */
+ if (! spec->multiout.hp_nid)
  spec->multiout.hp_nid = nid;
+ else
+ spec->multiout.extra_out_nid[0] = nid;
  /* control HP volume/switch on the output mixer amp */
  nid = alc880_idx_to_mixer(alc880_fixed_pin_idx(pin));
  sprintf(name, "%s Playback Volume", pfx);
@@ -2451,12 +2448,6 @@ static int alc880_auto_create_extra_out(
  return err;
  } else if (alc880_is_multi_pin(pin)) {
  /* set manual connection */
- if (! spec->multiout.dac_nids[0]) {
- /* use this as the primary output */
- spec->multiout.dac_nids[0] = alc880_idx_to_dac(alc880_multi_pin_idx(pin));
- if (! spec->multiout.num_dacs)
- spec->multiout.num_dacs = 1;
- }
  /* we have only a switch on HP-out PIN */
  sprintf(name, "%s Playback Switch", pfx);
  if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
@@ -2540,7 +2531,7 @@ static void alc880_auto_init_extra_out(s
  struct alc_spec *spec = codec->spec;
  hda_nid_t pin;
 
- pin = spec->autocfg.speaker_pin;
+ pin = spec->autocfg.speaker_pins[0];
  if (pin) /* connect to front */
  alc880_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
  pin = spec->autocfg.hp_pin;
@@ -2576,15 +2567,15 @@ static int alc880_parse_auto_config(stru
  if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
  alc880_ignore)) < 0)
  return err;
- if (! spec->autocfg.line_outs && ! spec->autocfg.speaker_pin &&
-    ! spec->autocfg.hp_pin)
+ if (! spec->autocfg.line_outs)
  return 0; /* can't find valid BIOS pin config */
 
  if ((err = alc880_auto_fill_dac_nids(spec, &spec->autocfg)) < 0 ||
     (err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
-    (err = alc880_auto_create_extra_out(spec, spec->autocfg.speaker_pin,
+    (err = alc880_auto_create_extra_out(spec,
+ spec->autocfg.speaker_pins[0],
  "Speaker")) < 0 ||
-    (err = alc880_auto_create_extra_out(spec, spec->autocfg.speaker_pin,
+    (err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pin,
  "Headphone")) < 0 ||
     (err = alc880_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
  return err;
@@ -3445,7 +3436,7 @@ static int alc260_auto_create_multi_out_
  return err;
  }
 
- nid = cfg->speaker_pin;
+ nid = cfg->speaker_pins[0];
  if (nid) {
  err = alc260_add_playback_controls(spec, nid, "Speaker");
  if (err < 0)
@@ -3518,7 +3509,7 @@ static void alc260_auto_init_multi_out(s
  if (nid)
  alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0);
 
- nid = spec->autocfg.speaker_pin;
+ nid = spec->autocfg.speaker_pins[0];
  if (nid)
  alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0);
 
@@ -4602,7 +4593,7 @@ static int alc262_auto_create_multi_out_
  return err;
  }
 
- nid = cfg->speaker_pin;
+ nid = cfg->speaker_pins[0];
  if (nid) {
  if (nid == 0x16) {
  if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "Speaker Playback Volume",
@@ -4612,10 +4603,6 @@ static int alc262_auto_create_multi_out_
        HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT))) < 0)
  return err;
  } else {
- if (! cfg->line_out_pins[0])
- if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "Speaker Playback Volume",
-       HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT))) < 0)
- return err;
  if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, "Speaker Playback Switch",
        HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
  return err;
@@ -4632,10 +4619,6 @@ static int alc262_auto_create_multi_out_
        HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT))) < 0)
  return err;
  } else {
- if (! cfg->line_out_pins[0])
- if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "Headphone Playback Volume",
-       HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT))) < 0)
- return err;
  if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, "Headphone Playback Switch",
        HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
  return err;
@@ -4729,8 +4712,7 @@ static int alc262_parse_auto_config(stru
  if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
  alc262_ignore)) < 0)
  return err;
- if (! spec->autocfg.line_outs && ! spec->autocfg.speaker_pin &&
-    ! spec->autocfg.hp_pin)
+ if (! spec->autocfg.line_outs)
  return 0; /* can't find valid BIOS pin config */
  if ((err = alc262_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
     (err = alc262_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
@@ -5404,8 +5386,7 @@ static int alc861_parse_auto_config(stru
  if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
  alc861_ignore)) < 0)
  return err;
- if (! spec->autocfg.line_outs && ! spec->autocfg.speaker_pin &&
-    ! spec->autocfg.hp_pin)
+ if (! spec->autocfg.line_outs)
  return 0; /* can't find valid BIOS pin config */
 
  if ((err = alc861_auto_fill_dac_nids(spec, &spec->autocfg)) < 0 ||
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 9d73e05..b92bf16 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -51,6 +51,7 @@ struct sigmatel_spec {
  unsigned int line_switch: 1;
  unsigned int mic_switch: 1;
  unsigned int alt_switch: 1;
+ unsigned int hp_detect: 1;
 
  /* playback */
  struct hda_multi_out multiout;
@@ -696,12 +697,7 @@ static int stac92xx_auto_fill_dac_nids(s
  AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
  }
 
- if (cfg->line_outs)
- spec->multiout.num_dacs = cfg->line_outs;
- else if (cfg->hp_pin) {
- spec->multiout.dac_nids[0] = snd_hda_codec_read(codec, cfg->hp_pin, 0, AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
- spec->multiout.num_dacs = 1;
- }
+ spec->multiout.num_dacs = cfg->line_outs;
 
  return 0;
 }
@@ -772,11 +768,13 @@ static int stac92xx_auto_create_hp_ctls(
 
  wid_caps = get_wcaps(codec, pin);
 
- if (wid_caps & AC_WCAP_UNSOL_CAP)
+ if (wid_caps & AC_WCAP_UNSOL_CAP) {
  /* Enable unsolicited responses on the HP widget */
  snd_hda_codec_write(codec, pin, 0,
  AC_VERB_SET_UNSOLICITED_ENABLE,
  STAC_UNSOL_ENABLE);
+ spec->hp_detect = 1;
+ }
 
  nid = snd_hda_codec_read(codec, pin, 0, AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
  for (i = 0; i < cfg->line_outs; i++) {
@@ -810,9 +808,6 @@ static int stac92xx_auto_create_analog_i
  for (i = 0; i < AUTO_PIN_LAST; i++) {
  int index = -1;
  if (cfg->input_pins[i]) {
- /* Enable active pin widget as an input */
- stac92xx_auto_set_pinctl(codec, cfg->input_pins[i], AC_PINCTL_IN_EN);
-
  imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
 
  for (j=0; j<spec->num_muxes; j++) {
@@ -861,10 +856,8 @@ static int stac92xx_parse_auto_config(st
 
  if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL)) < 0)
  return err;
- if (! spec->autocfg.line_outs && ! spec->autocfg.hp_pin)
+ if (! spec->autocfg.line_outs)
  return 0; /* can't find valid pin config */
- stac92xx_auto_init_multi_out(codec);
- stac92xx_auto_init_hp_out(codec);
  if ((err = stac92xx_add_dyn_out_pins(codec, &spec->autocfg)) < 0)
  return err;
  if ((err = stac92xx_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
@@ -879,14 +872,10 @@ static int stac92xx_parse_auto_config(st
  if (spec->multiout.max_channels > 2)
  spec->surr_switch = 1;
 
- if (spec->autocfg.dig_out_pin) {
+ if (spec->autocfg.dig_out_pin)
  spec->multiout.dig_out_nid = dig_out;
- stac92xx_auto_set_pinctl(codec, spec->autocfg.dig_out_pin, AC_PINCTL_OUT_EN);
- }
- if (spec->autocfg.dig_in_pin) {
+ if (spec->autocfg.dig_in_pin)
  spec->dig_in_nid = dig_in;
- stac92xx_auto_set_pinctl(codec, spec->autocfg.dig_in_pin, AC_PINCTL_IN_EN);
- }
 
  if (spec->kctl_alloc)
  spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
@@ -896,6 +885,29 @@ static int stac92xx_parse_auto_config(st
  return 1;
 }
 
+/* add playback controls for HP output */
+static int stac9200_auto_create_hp_ctls(struct hda_codec *codec,
+ struct auto_pin_cfg *cfg)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ hda_nid_t pin = cfg->hp_pin;
+ unsigned int wid_caps;
+
+ if (! pin)
+ return 0;
+
+ wid_caps = get_wcaps(codec, pin);
+ if (wid_caps & AC_WCAP_UNSOL_CAP) {
+ /* Enable unsolicited responses on the HP widget */
+ snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_UNSOLICITED_ENABLE,
+ STAC_UNSOL_ENABLE);
+ spec->hp_detect = 1;
+ }
+
+ return 0;
+}
+
 static int stac9200_parse_auto_config(struct hda_codec *codec)
 {
  struct sigmatel_spec *spec = codec->spec;
@@ -907,14 +919,13 @@ static int stac9200_parse_auto_config(st
  if ((err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg)) < 0)
  return err;
 
- if (spec->autocfg.dig_out_pin) {
+ if ((err = stac9200_auto_create_hp_ctls(codec, &spec->autocfg)) < 0)
+ return err;
+
+ if (spec->autocfg.dig_out_pin)
  spec->multiout.dig_out_nid = 0x05;
- stac92xx_auto_set_pinctl(codec, spec->autocfg.dig_out_pin, AC_PINCTL_OUT_EN);
- }
- if (spec->autocfg.dig_in_pin) {
+ if (spec->autocfg.dig_in_pin)
  spec->dig_in_nid = 0x04;
- stac92xx_auto_set_pinctl(codec, spec->autocfg.dig_in_pin, AC_PINCTL_IN_EN);
- }
 
  if (spec->kctl_alloc)
  spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
@@ -927,9 +938,31 @@ static int stac9200_parse_auto_config(st
 static int stac92xx_init(struct hda_codec *codec)
 {
  struct sigmatel_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
 
  snd_hda_sequence_write(codec, spec->init);
 
+ /* set up pins */
+ if (spec->hp_detect) {
+ /* fake event to set up pins */
+ codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
+ } else {
+ stac92xx_auto_init_multi_out(codec);
+ stac92xx_auto_init_hp_out(codec);
+ }
+ for (i = 0; i < AUTO_PIN_LAST; i++) {
+ if (cfg->input_pins[i])
+ stac92xx_auto_set_pinctl(codec, cfg->input_pins[i],
+ AC_PINCTL_IN_EN);
+ }
+ if (cfg->dig_out_pin)
+ stac92xx_auto_set_pinctl(codec, cfg->dig_out_pin,
+ AC_PINCTL_OUT_EN);
+ if (cfg->dig_in_pin)
+ stac92xx_auto_set_pinctl(codec, cfg->dig_in_pin,
+ AC_PINCTL_IN_EN);
+
  return 0;
 }
 
--
1.1.3

--
Daniel T. Chen            [hidden email]
GPG key:   www.sh.nu/~crimsun/pubkey.gpg.asc

--
kernel-team mailing list
[hidden email]
https://lists.ubuntu.com/mailman/listinfo/kernel-team

attachment0 (196 bytes) Download Attachment