[alsa-devel] [PATCH2/2] hda: Add support for IDT codecs to report jack presence

Matthew Ranostay mranostay at embeddedalley.com
Mon Oct 20 23:40:22 CEST 2008


Add support for reporting line out, headphone, and microphone jack insertions
via the jack abstraction layer for the Sigmatel/IDT codec family.

Signed-off-by: Matthew Ranostay <mranostay at embeddedalley.com>
---

diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index d106ea5..e3a5e7a 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -36,6 +36,7 @@
 #include "hda_patch.h"
 #include "hda_beep.h"

+#define STAC_INSERT_EVENT	0x10
 #define STAC_PWR_EVENT		0x20
 #define STAC_HP_EVENT		0x30
 #define STAC_VREF_EVENT		0x40
@@ -129,6 +130,17 @@ enum {
 	STAC_927X_MODELS
 };

+struct sigmatel_event {
+	hda_nid_t nid;
+	int data;
+};
+
+struct sigmatel_jack {
+	hda_nid_t nid;
+	int type;
+	struct snd_jack *jack;
+};
+
 struct sigmatel_spec {
 	struct snd_kcontrol_new *mixers[4];
 	unsigned int num_mixers;
@@ -161,6 +173,12 @@ struct sigmatel_spec {
 	hda_nid_t *pwr_nids;
 	hda_nid_t *dac_list;

+	/* jack detection */
+	struct snd_array jacks;
+
+	/* events */
+	struct snd_array events;
+
 	/* playback */
 	struct hda_input_mux *mono_mux;
 	struct hda_input_mux *amp_mux;
@@ -216,9 +234,6 @@ struct sigmatel_spec {

 	struct hda_pcm pcm_rec[2];	/* PCM information */

-	/* jack detection */
-	struct snd_jack *jack;
-
 	/* dynamic controls and input_mux */
 	struct auto_pin_cfg autocfg;
 	struct snd_array kctls;
@@ -2458,13 +2473,15 @@ static int stac92xx_hp_switch_put(struct snd_kcontrol
*kcontrol,
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct sigmatel_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	int nid = cfg->hp_pins[cfg->hp_outs - 1];

 	spec->hp_switch = ucontrol->value.integer.value[0];

 	/* check to be sure that the ports are upto date with
 	 * switch changes
 	 */
-	codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
+	codec->patch_ops.unsol_event(codec, (STAC_HP_EVENT | nid) << 26);

 	return 1;
 }
@@ -2504,7 +2521,8 @@ static int stac92xx_io_switch_put(struct snd_kcontrol
*kcontrol, struct snd_ctl_
 	 * appropriately according to the pin direction
 	 */
 	if (spec->hp_detect)
-		codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
+		codec->patch_ops.unsol_event(codec,
+			(STAC_HP_EVENT | nid) << 26);

         return 1;
 }
@@ -3574,13 +3592,70 @@ static void stac_gpio_set(struct hda_codec *codec,
unsigned int mask,
 			   AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */
 }

+static int stac92xx_add_jack(struct hda_codec *codec,
+		hda_nid_t nid, int type)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	struct sigmatel_jack *jack;
+	int def_conf = snd_hda_codec_read(codec, nid,
+			0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+	int connectivity = get_defcfg_connect(def_conf);
+	char name[32];
+
+	if (connectivity && connectivity != AC_JACK_PORT_FIXED)
+		return 0;
+
+	snd_array_init(&spec->jacks, sizeof(*jack), 32);
+	jack = snd_array_new(&spec->jacks);
+	if (!jack)
+		return -ENOMEM;
+	jack->nid = nid;
+	jack->type = type;
+
+	sprintf(name, "%s at %s %s Jack",
+		get_jack_type(def_conf),
+		get_jack_connectivity(def_conf),
+		get_jack_location(def_conf));
+
+	return snd_jack_new(codec->bus->card, name, type, &jack->jack);
+}
+
+static int stac92xx_add_event(struct sigmatel_spec *spec, hda_nid_t nid,
+			     int data)
+{
+	struct sigmatel_event *event;
+
+	snd_array_init(&spec->events, sizeof(*event), 32);
+	event = snd_array_new(&spec->events);
+	if (!event)
+		return -ENOMEM;
+	event->nid = nid;
+	event->data = data;
+
+	return 0;
+}
+
+static int stac92xx_event_data(struct hda_codec *codec, hda_nid_t nid)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	struct sigmatel_event *events = spec->events.list;
+	if (events) {
+		int i;
+		for (i = 0; i < spec->events.used; i++)
+			if (events[i].nid == nid)
+				return events[i].data;
+	}
+	return 0;
+}
+
 static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid,
 			      unsigned int event)
 {
-	if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP)
+	if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) {
 		snd_hda_codec_write_cache(codec, nid, 0,
 					  AC_VERB_SET_UNSOLICITED_ENABLE,
-					  (AC_USRSP_EN | event));
+					  (AC_USRSP_EN | event | nid));
+	}
 }

 static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid)
@@ -3623,27 +3698,34 @@ static int stac92xx_init(struct hda_codec *codec)
 	/* set up pins */
 	if (spec->hp_detect) {
 		/* Enable unsolicited responses on the HP widget */
-		for (i = 0; i < cfg->hp_outs; i++)
-			enable_pin_detect(codec, cfg->hp_pins[i],
-					  STAC_HP_EVENT);
+		for (i = 0; i < cfg->hp_outs; i++) {
+			hda_nid_t nid = cfg->hp_pins[i];
+			enable_pin_detect(codec, nid, STAC_HP_EVENT | nid);
+			/* jack detection */
+			err = stac92xx_add_jack(codec, nid, cfg->hp_outs == i
+				? SND_JACK_SWITCH : SND_JACK_HEADPHONE);
+			if (err < 0)
+				return err;
+
+		}
 		/* force to enable the first line-out; the others are set up
 		 * in unsol_event
 		 */
 		stac92xx_auto_set_pinctl(codec, spec->autocfg.line_out_pins[0],
-					 AC_PINCTL_OUT_EN);
-		stac92xx_auto_init_hp_out(codec);
-		/* jack detection */
-		err = snd_jack_new(codec->bus->card,
-			"Headphone Jack",
-			SND_JACK_HEADPHONE, &spec->jack);
-		if (err < 0)
-			return err;
+				AC_PINCTL_OUT_EN);
 		/* fake event to set up pins */
-		codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
+		codec->patch_ops.unsol_event(codec,
+			(STAC_HP_EVENT | spec->autocfg.hp_pins[0]) << 26);
 	} else {
 		stac92xx_auto_init_multi_out(codec);
 		stac92xx_auto_init_hp_out(codec);
 	}
+	for (i = 0; i < cfg->line_outs; i++) {
+		err = stac92xx_add_jack(codec,
+				cfg->line_out_pins[i], SND_JACK_LINEOUT);
+		if (err < 0)
+			return err;
+	}
 	for (i = 0; i < AUTO_PIN_LAST; i++) {
 		hda_nid_t nid = cfg->input_pins[i];
 		if (nid) {
@@ -3656,6 +3738,11 @@ static int stac92xx_init(struct hda_codec *codec)
 			if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC)
 				pinctl |= stac92xx_get_vref(codec, nid);
 			stac92xx_auto_set_pinctl(codec, nid, pinctl);
+			err = stac92xx_add_jack(codec, nid,
+				SND_JACK_MICROPHONE);
+			if (err < 0)
+				return err;
+			enable_pin_detect(codec, nid, STAC_INSERT_EVENT | nid);
 		}
 	}
 	for (i = 0; i < spec->num_dmics; i++)
@@ -3697,6 +3784,18 @@ static int stac92xx_init(struct hda_codec *codec)
 	return 0;
 }

+static void stac92xx_free_jacks(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	if (spec->jacks.list) {
+		struct sigmatel_jack *jacks = spec->jacks.list;
+		int i;
+		for (i = 0; i < spec->jacks.used; i++)
+			snd_device_free(codec->bus->card, &jacks[i].jack);
+	}
+	snd_array_free(&spec->jacks);
+}
+
 static void stac92xx_free_kctls(struct hda_codec *codec)
 {
 	struct sigmatel_spec *spec = codec->spec;
@@ -3717,11 +3816,10 @@ static void stac92xx_free(struct hda_codec *codec)
 	if (! spec)
 		return;

-	if (spec->jack)
-		snd_device_free(codec->bus->card, spec->jack);
-
 	if (spec->bios_pin_configs)
 		kfree(spec->bios_pin_configs);
+	stac92xx_free_jacks(codec);
+	snd_array_free(&spec->events);

 	kfree(spec);
 	snd_hda_detach_beep_device(codec);
@@ -3804,8 +3902,6 @@ static void stac92xx_hp_detect(struct hda_codec *codec,
unsigned int res)
 			break;
 		presence = get_hp_pin_presence(codec, cfg->hp_pins[i]);
 	}
-	snd_jack_report(spec->jack,
-		presence ? SND_JACK_HEADPHONE : 0);

 	if (presence) {
 		/* disable lineouts, enable hp */
@@ -3862,24 +3958,56 @@ static void stac92xx_pin_sense(struct hda_codec *codec,
int idx)

 	/* power down unused output ports */
 	snd_hda_codec_write(codec, codec->afg, 0, 0x7ec, val);
-};
+}
+
+static void stac92xx_report_jack(struct hda_codec *codec, hda_nid_t nid)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	struct sigmatel_jack *jacks = spec->jacks.list;
+
+	if (jacks) {
+		int i;
+		for (i = 0; i < spec->jacks.used; i++) {
+			if (jacks->nid == nid) {
+				unsigned int pin_ctl =
+					snd_hda_codec_read(codec, nid,
+					0, AC_VERB_GET_PIN_WIDGET_CONTROL,
+					 0x00);
+				int type = jacks->type;
+				if (type == SND_JACK_SWITCH)
+					type = (pin_ctl & AC_PINCTL_HP_EN)
+					? SND_JACK_HEADPHONE : SND_JACK_LINEOUT;
+				snd_jack_report(jacks->jack,
+					get_hp_pin_presence(codec, nid)
+					? type : 0);
+			}
+			jacks++;
+		}
+	}
+}

 static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
 {
 	struct sigmatel_spec *spec = codec->spec;
-	int idx = res >> 26 & 0x0f;
+	int event = (res >> 26) & 0x70;
+	int nid = res >> 26 & 0x0f;

-	switch ((res >> 26) & 0x70) {
+	switch (event) {
 	case STAC_HP_EVENT:
 		stac92xx_hp_detect(codec, res);
 		/* fallthru */
+	case STAC_INSERT_EVENT:
 	case STAC_PWR_EVENT:
-		if (spec->num_pwrs > 0)
-			stac92xx_pin_sense(codec, idx);
+		if (nid) {
+			if (spec->num_pwrs > 0)
+				stac92xx_pin_sense(codec, nid);
+			stac92xx_report_jack(codec, nid);
+		}
 		break;
 	case STAC_VREF_EVENT: {
 		int data = snd_hda_codec_read(codec, codec->afg, 0,
 			AC_VERB_GET_GPIO_DATA, 0);
+		int idx = stac92xx_event_data(codec, nid);
 		/* toggle VREF state based on GPIOx status */
 		snd_hda_codec_write(codec, codec->afg, 0, 0x7e0,
 			!!(data & (1 << idx)));
@@ -4402,8 +4530,11 @@ again:
 			snd_hda_codec_write(codec, codec->afg, 0,
 				AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02);
 			snd_hda_codec_write_cache(codec, codec->afg, 0,
-					AC_VERB_SET_UNSOLICITED_ENABLE,
-					(AC_USRSP_EN | STAC_VREF_EVENT | 0x01));
+				AC_VERB_SET_UNSOLICITED_ENABLE,
+				(AC_USRSP_EN | STAC_VREF_EVENT | codec->afg));
+			err = stac92xx_add_event(spec, codec->afg, 0x02);
+			if (err < 0)
+				return err;
 			spec->gpio_mask |= 0x02;
 			break;
 		}
@@ -4802,8 +4933,11 @@ static int patch_stac9205(struct hda_codec *codec)
 		snd_hda_codec_write(codec, codec->afg, 0,
 			AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10);
 		snd_hda_codec_write_cache(codec, codec->afg, 0,
-					  AC_VERB_SET_UNSOLICITED_ENABLE,
-					  (AC_USRSP_EN | STAC_HP_EVENT));
+			AC_VERB_SET_UNSOLICITED_ENABLE,
+			(AC_USRSP_EN | STAC_VREF_EVENT | codec->afg));
+		err = stac92xx_add_event(spec, codec->afg, 0x01);
+		if (err < 0)
+			return err;

 		spec->gpio_dir = 0x0b;
 		spec->eapd_mask = 0x01;



More information about the Alsa-devel mailing list