Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 2040

kernel-2.6.18-238.el5.src.rpm

From: John Feeney <jfeeney@redhat.com>
Subject: [RHEL5.1 PATCH] Microphone on Fila stops working
Date: Wed, 22 Aug 2007 18:22:15 -0400
Bugzilla: 240716
Message-Id: <46CCB717.5050103@redhat.com>
Changelog: [misc] Microphone stops working


bugzilla 240716
FEAT PPORT: Internal and external Mic on Fila (M4300) stops
working intermittently

http://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=240716

Description of problem:
After some reboots, the microphone will not work unless the user
toggles the "Input So" from "Mic" to "Line" and back again in
alsamixer. Thus, at worst case, it appears as though the microphone
does not work, and even if the magic sequence is known, this will
lead to a bad customer experience.

Solution:
>From the upstream patch:
hda-codec - bug fixes for stac92xx HDA codecs.

* fixed surround playback on stac922x. Pin direction control bits were
not being set correctly in stac92xx_set_pinctl(). Specifically it
would refuse to set the port as an output if the port was already
configured as an input. Last hunk (#8).

* fixed an input mux bug on 92xx codecs. When there is more than one
possible input calculated for the muxes, the actual mux widget never
gets set from its reset default, which is index 0, in the stac9221
case that is port E. So alsamixer/amixer/gnome-mixer report the Mic
as being the selected input source, but in fact is something else
(line-in port E in stac9221 case). Another problem with this is that
if you actually try to set the mux input to "Mic", nothing happens
because *cur_val == idx (see snd_hda_input_mux_put). You have to
actually toggle input source to line-in then back to mic to actually
set the mux widget. Hunk #7.

* fixed some typos in patch_sigmatel.c. Hunk #6.

* fix to stac92xx_add_dyn_out_pins() that fixes surround playback on
codecs with less that 4 DACs (stac9205 for example). It reads the widget
caps cache created by hda_codec to count the total number of analog DACs
found. It then uses that to determine whether there will be enough
independent DACs available for line/mic switch controls. Hunk #1, #2,
and #3.

* improvements to stac92xx_auto_fill_dac_nids() to make it more general.
This fixes surround playback on some codecs in combination with the
fix to stac92xx_add_dyn_out_pins() above. It reads the full connection
list now, instead of just the first entry, and then locates an analog
DAC in the list. If one is found and it's free, assign it to that line-out.
If no free DAC is found for the line-out, return -ENODEV. It also makes
sure to actually select the chosen DAC if more than one DAC is input to
the pin. Hunks #4, #5.

Upstream status:
ALSA commit:
http://hg-mirror.alsa-project.org/alsa-kernel/rev/c1bed2fb4ffe

Linus' git tree:
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=7b043899992e5d9c0b1a620cdad9158d2e5484d7

Testing:
Dell has successfully tested the complete patch with Brew-built RHEL5.1 
rpms.

Acks would be appreciated, thanks.


--- linux-2.6.18.noarch/sound/pci/hda/patch_sigmatel.c.orig
+++ linux-2.6.18.noarch/sound/pci/hda/patch_sigmatel.c
@@ -1055,11 +1055,23 @@ static int stac92xx_add_control(struct s
 static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cfg *cfg)
 {
 	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];
+		wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+		if (wtype == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL))
+			num_dacs++;
+	}
+
+	snd_printdd("%s: total dac count=%d\n", __func__, num_dacs);
 
 	switch (cfg->line_outs) {
 	case 3:
 		/* add line-in as side */
-		if (cfg->input_pins[AUTO_PIN_LINE]) {
+		if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 3) {
 			cfg->line_out_pins[3] = cfg->input_pins[AUTO_PIN_LINE];
 			spec->line_switch = 1;
 			cfg->line_outs++;
@@ -1067,12 +1079,12 @@ static int stac92xx_add_dyn_out_pins(str
 		break;
 	case 2:
 		/* add line-in as clfe and mic as side */
-		if (cfg->input_pins[AUTO_PIN_LINE]) {
+		if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 2) {
 			cfg->line_out_pins[2] = cfg->input_pins[AUTO_PIN_LINE];
 			spec->line_switch = 1;
 			cfg->line_outs++;
 		}
-		if (cfg->input_pins[AUTO_PIN_MIC]) {
+		if (cfg->input_pins[AUTO_PIN_MIC] && num_dacs > 3) {
 			cfg->line_out_pins[3] = cfg->input_pins[AUTO_PIN_MIC];
 			spec->mic_switch = 1;
 			cfg->line_outs++;
@@ -1080,12 +1092,12 @@ static int stac92xx_add_dyn_out_pins(str
 		break;
 	case 1:
 		/* add line-in as surr and mic as clfe */
-		if (cfg->input_pins[AUTO_PIN_LINE]) {
+		if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 1) {
 			cfg->line_out_pins[1] = cfg->input_pins[AUTO_PIN_LINE];
 			spec->line_switch = 1;
 			cfg->line_outs++;
 		}
-		if (cfg->input_pins[AUTO_PIN_MIC]) {
+		if (cfg->input_pins[AUTO_PIN_MIC] && num_dacs > 2) {
 			cfg->line_out_pins[2] = cfg->input_pins[AUTO_PIN_MIC];
 			spec->mic_switch = 1;
 			cfg->line_outs++;
@@ -1096,32 +1108,74 @@ static int stac92xx_add_dyn_out_pins(str
 	return 0;
 }
 
+static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
+{
+	int i;
+
+	for (i = 0; i < spec->multiout.num_dacs; i++) {
+		if (spec->multiout.dac_nids[i] == nid)
+			return 1;
+	}
+
+	return 0;
+}
+
 /*
- * XXX The line_out pin widget connection list may not be set to the
- * desired DAC nid. This is the case on 927x where ports A and B can
- * be routed to several DACs.
- *
- * This requires an analysis of the line-out/hp pin configuration
- * to provide a best fit for pin/DAC configurations that are routable.
- * For now, 927x DAC4 is not supported and 927x DAC1 output to ports
- * A and B is not supported.
+ * Fill in the dac_nids table from the parsed pin configuration
+ * This function only works when every pin in line_out_pins[]
+ * contains atleast one DAC in its connection list. Some 92xx
+ * codecs are not connected directly to a DAC, such as the 9200
+ * and 9202/925x. For those, dac_nids[] must be hard-coded. 
  */
-/* fill in the dac_nids table from the parsed pin configuration */
 static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec,
 				       const struct auto_pin_cfg *cfg)
 {
 	struct sigmatel_spec *spec = codec->spec;
-	hda_nid_t nid;
-	int i;
+	int i, j, conn_len = 0;
+	hda_nid_t nid, conn[HDA_MAX_CONNECTIONS];
+	unsigned int wcaps, wtype; 
 
-	/* check the pins hardwired to audio widget */
 	for (i = 0; i < cfg->line_outs; i++) {
 		nid = cfg->line_out_pins[i];
-		spec->multiout.dac_nids[i] = snd_hda_codec_read(codec, nid, 0,
-					AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
+		conn_len = snd_hda_get_connections(codec, nid, conn,
+				HDA_MAX_CONNECTIONS);
+		for (j = 0; j < conn_len; j++) {
+			wcaps = snd_hda_param_read(codec, conn[j],
+					AC_PAR_AUDIO_WIDGET_CAP);
+			wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+
+			if (wtype != AC_WID_AUD_OUT ||
+			    (wcaps & AC_WCAP_DIGITAL))
+				continue;
+			/* conn[j] is a DAC routed to this line-out */
+			if (!is_in_dac_nids(spec, conn[j]))
+				break;
+		}
+
+		if (j == conn_len) {
+			/* error out, no available DAC found */
+			snd_printk(KERN_ERR
+				"%s: No available DAC for pin 0x%x\n",
+				__func__, nid);
+			return -ENODEV;
+		}
+
+		spec->multiout.dac_nids[i] = conn[j];
+		spec->multiout.num_dacs++;
+		if (conn_len > 1) {
+			/* select this DAC in the pin's input mux */
+			snd_hda_codec_write(codec, nid, 0,
+				AC_VERB_SET_CONNECT_SEL, j);
+		}
 	}
 
-	spec->multiout.num_dacs = cfg->line_outs;
+	snd_printd("dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
+		spec->multiout.num_dacs,
+		spec->multiout.dac_nids[0],
+		spec->multiout.dac_nids[1],
+		spec->multiout.dac_nids[2],
+		spec->multiout.dac_nids[3],
+		spec->multiout.dac_nids[4]); 
 
 	return 0;
 }
@@ -1189,12 +1243,8 @@ static int stac92xx_auto_create_multi_ou
 
 static int check_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
 {
-	int i;
-
-	for (i = 0; i < spec->multiout.num_dacs; i++) {
-		if (spec->multiout.dac_nids[i] == nid)
-			return 1;
-	}
+	if (is_in_dac_nids(spec, nid))
+		return 1; 
 	if (spec->multiout.hp_nid == nid)
 		return 1;
 	return 0;
@@ -1236,12 +1286,10 @@ static int stac92xx_auto_create_hp_ctls(
 		add_spec_dacs(spec, nid);
 	}
 	for (i = 0; i < cfg->speaker_outs; i++) {
-		nid = snd_hda_codec_read(codec, cfg->speaker_pins[0], 0,
+		nid = snd_hda_codec_read(codec, cfg->speaker_pins[i], 0,
 					 AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
 		if (check_in_dac_nids(spec, nid))
 			nid = 0;
-		if (check_in_dac_nids(spec, nid))
-			nid = 0;
 		if (! nid)
 			continue;
 		add_spec_dacs(spec, nid);
@@ -1355,7 +1403,7 @@ static int stac92xx_auto_create_analog_i
 		imux->num_items++;
 	}
 
-	if (imux->num_items == 1) {
+	if (imux->num_items) {
 		/*
 		 * Set the current input for the muxes.
 		 * The STAC9221 has two input muxes with identical source
@@ -1675,8 +1723,12 @@ static void stac92xx_set_pinctl(struct h
 {
 	unsigned int pin_ctl = snd_hda_codec_read(codec, nid,
 			0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00);
-	if (flag == AC_PINCTL_OUT_EN && (pin_ctl & AC_PINCTL_IN_EN))
-		return;
+
+	/* if setting pin direction bits, clear the current
+	   direction bits first */
+	if (flag & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN))
+		pin_ctl &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
+
 	snd_hda_codec_write(codec, nid, 0,
 			AC_VERB_SET_PIN_WIDGET_CONTROL,
 			pin_ctl | flag);