All of lore.kernel.org
 help / color / mirror / Atom feed
* [patch] patch_cm9880.c to choose multi-channel jacks automatically
@ 2005-04-04 23:04 "Tien,  C.L. - 田承禮"
  2005-04-05 16:27 ` Takashi Iwai
  0 siblings, 1 reply; 4+ messages in thread
From: "Tien,  C.L. - 田承禮" @ 2005-04-04 23:04 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel

[-- Attachment #1: Type: text/plain, Size: 3787 bytes --]


Takashi,

1. I found the current code has basic_init, which already includes necessary controls for 6-stack initialization, so I don't need another model.

2. I add a new model "auto" to let the driver find a. if there are option real panel/front panel, b. the jacks to be used for multichannel.

Because the jack color are based on MS's channel sequence, so the "auto" model will pick the same jacks for multichannel MS uses. I did this to hope to minimize users questions. These code can also be applied to other codecs but I don't have any to test.

Sincerely,
ChenLi Tien

-----Original Message-----
From:	Takashi Iwai [mailto:tiwai@suse.de]
Sent:	2005/3/29 [星期二] 上午 07:18
To:	Tien,  C.L. - 田承禮
Cc:	alsa-devel@alsa-project.org
Subject:	Re: [Alsa-devel] [patch] patch_cm9880.c to support multi-channel
At Sun, 27 Mar 2005 23:19:02 +0800,
Tien, C.L. wrote:
> 
> I got configuration info from Windows driver's .INI file, there are
> totally 28 configuration so I think it is impractical to use parameter
> to fits user's needs. 
> 
> I want to add configuration for 6-stack first since most of the M/Bs
> have them. 

OK, that's fine.  A patch would be appreciated as always ;)

> Then I plan to add code to probe the jacks for multi-channel
> playback, so driver can find which pin for front/rear/side/LFE by 
> itself without parameter help. The line-out/line-in/mic etc. should 
> also can be detected without a fixed table. 

That's exactly what I wanted to implement after 1.0.9, too.

The basic infrastructure for unsolicited events is already implemented
as a callback to driver, but it's never tested yet.

My another plan for HD-audio is to add the hwdep interface to allow
users reconfigure the mixer interface (like .INI file of windows).
This will require a relatively big rewrite...


Takashi

> 
> Sincerely,
> ChenLi Tien
> 
> 
> -----Original Message-----
> From:	Takashi Iwai [mailto:tiwai@suse.de]
> Sent:	2005/3/24 [星期四] 下午 03:40
> To:	Tien,  C.L. - 田承禮
> Cc:	alsa-devel@alsa-project.org
> Subject:	Re: [Alsa-devel] [patch] patch_cm9880.c to support multi-channel
> At Fri, 25 Mar 2005 01:53:06 +0800,
> Tien, C.L. wrote:
> > 
> > I also added controls in the allout_init array. I wonder what this
> > model for because it is the same as basic_init except the line-in is
> > used as output in allout_init. 
> 
> It was coded so, just based on the example configuration described in 
> the datasheet of CMI9880, in case any hardware is implemented in that
> way.  Surely we can clean up these models.
> 
> > I plan to add controls for 5/6 stacks ,  which lacks front panel but
> > has rear 5/6 jacks. Any idea? 
> 
> Go ahead, let's discuss together with the code :)
> 
> 
> Takashi
> 
> 
> 
> 
> 
> -------------------------------------------------------
> SF email is sponsored by - The IT Product Guide
> Read honest & candid reviews on hundreds of IT Products from real users.
> Discover which products truly live up to the hype. Start reading now.
> http://ads.osdn.com/?ad_ide95&alloc_id\x14396&op=click
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/alsa-devel
> 


-------------------------------------------------------
SF email is sponsored by - The IT Product Guide
Read honest & candid reviews on hundreds of IT Products from real users.
Discover which products truly live up to the hype. Start reading now.
http://ads.osdn.com/?ad_ide95&alloc_id\x14396&op=ick
_______________________________________________
Alsa-devel mailing list
Alsa-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/alsa-devel






[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: patch_cmedia.c.patch --]
[-- Type: text/x-patch; name="patch_cmedia.c.patch", Size: 9919 bytes --]

Index: alsa-cvs/alsa-kernel/pci/hda/patch_cmedia.c
===================================================================
RCS file: /cvsroot/alsa/alsa-kernel/pci/hda/patch_cmedia.c,v
retrieving revision 1.7
diff -a -u -r1.7 patch_cmedia.c
--- alsa-cvs/alsa-kernel/pci/hda/patch_cmedia.c	24 Mar 2005 20:47:35 -0000	1.7
+++ alsa-cvs/alsa-kernel/pci/hda/patch_cmedia.c	4 Apr 2005 22:52:38 -0000
@@ -29,6 +29,7 @@
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#define NUM_PINS	11
 
 
 /* board config type */
@@ -38,6 +39,7 @@
 	CMI_FULL,	/* back 6-jack + front-panel 2-jack */
 	CMI_FULL_DIG,	/* back 6-jack + front-panel 2-jack + digital I/O */
 	CMI_ALLOUT,	/* back 5-jack + front-panel 2-jack + digital out */
+	CMI_AUTO,	/* let driver guess it */
 };
 
 struct cmi_spec {
@@ -63,6 +65,14 @@
 	const struct cmi_channel_mode *channel_modes;
 
 	struct hda_pcm pcm_rec[2];	/* PCM information */
+
+	/* pin deafault configuration */
+	hda_nid_t pin_nid[NUM_PINS];
+	unsigned int def_conf[NUM_PINS];
+	unsigned int pin_def_confs;
+
+	/* multichannel pins */
+	hda_nid_t multich_pin[4];	/* max 8-channel */
 };
 
 /*
@@ -254,6 +264,10 @@
 };
 
 /* front, rear, clfe, rear_surr */
+static hda_nid_t cmi9880_multi_dac_nids[4] = {
+	0x03, 0x04, 0x05, 0x06
+};
+/* front, rear, clfe, rear_surr */
 static hda_nid_t cmi9880_dac_nids[4] = {
 	0x03, 0x04, 0x05, 0x06
 };
@@ -267,6 +281,22 @@
 
 /*
  */
+static struct hda_verb cmi9880_multi_init[] = {
+	/* port-E for PCM (front panel) */
+	{ 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+	{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
+	/* port-F for side (rear panel) */
+	{ 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+	{ 0x10, AC_VERB_SET_CONNECT_SEL, 0x01 },
+	/* port-G for CLFE (rear panel) */
+	{ 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+	{ 0x1f, AC_VERB_SET_CONNECT_SEL, 0x02 },
+	/* port-H for side (rear panel) */
+	{ 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+	{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x01 },
+	{} /* terminator */
+};
+
 static struct hda_verb cmi9880_basic_init[] = {
 	/* port-D for line out (rear panel) */
 	{ 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
@@ -353,6 +383,175 @@
 	return 0;
 }
 
+#define AC_DEFCFG_ASSOC_SHIFT		4
+#define get_defcfg_connect(cfg) ((cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT)
+#define get_defcfg_association(cfg) ((cfg & AC_DEFCFG_DEF_ASSOC) >> AC_DEFCFG_ASSOC_SHIFT)
+#define get_defcfg_sequence(cfg) (cfg & AC_DEFCFG_SEQUENCE)
+
+/* get all pin default configuration in def_conf */
+static int cmi9880_get_pin_def_config(struct hda_codec *codec)
+{
+	struct cmi_spec *spec = codec->spec;
+	hda_nid_t nid, nid_start;
+	int i = 0, nodes;
+
+	nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid_start);
+	for (nid = nid_start; nid < nodes + nid_start; nid++) {
+		unsigned int wid_caps = snd_hda_param_read(codec, nid,
+						   AC_PAR_AUDIO_WIDGET_CAP);
+		unsigned int wid_type = (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+		/* read all default configuration for pin complex */
+		if (wid_type == AC_WID_PIN) {
+			spec->pin_nid[i] = nid;
+			spec->def_conf[i] = 
+				snd_hda_codec_read(codec, nid, 0,
+					AC_VERB_GET_CONFIG_DEFAULT, 0);
+			i++;
+		}
+	}
+	spec->pin_def_confs = i;
+	return 0;
+}
+
+/* get a pin default configuration of nid in def_conf */
+static unsigned int cmi9880_get_def_config(struct hda_codec *codec, hda_nid_t nid)
+{
+	struct cmi_spec *spec = codec->spec;
+	int i = 0;
+
+	while (spec->pin_nid[i] != nid && i < spec->pin_def_confs)
+		i++;
+	if (i == spec->pin_def_confs)
+		return (unsigned int) -1;
+	else
+		return spec->def_conf[i];
+}
+
+/* decide what pins to use for multichannel playback */
+static int cmi9880_get_multich_pins(struct hda_codec *codec)
+{
+	struct cmi_spec *spec = codec->spec;
+	int i, j, pins, seq[4];
+	int max_channel = 0;
+	unsigned int def_conf, sequence;
+	hda_nid_t nid;
+
+	memset(spec->multich_pin, 0, sizeof(spec->multich_pin));
+	for (pins = 0, i = 0; i < spec->pin_def_confs && pins < 4; i++) {
+		def_conf = spec->def_conf[i];
+		/* skip pin not connected */
+		if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
+			continue;
+		/* get the sequence if association == 1 */
+		/* the other pins have association = 0, incorrect in spec 1.0 */
+		if (get_defcfg_association(def_conf) == 1) {
+			sequence = get_defcfg_sequence(def_conf);
+			seq[pins] = sequence;
+			spec->multich_pin[pins] = spec->pin_nid[i];
+			pins++;	// ready for next slot
+			max_channel += 2;
+		}
+	}
+	/* sort by sequence, data collected here will be for Windows */ 
+	for (i = 0; i < pins; i++) {
+		for (j = i + 1; j < pins; j++) {
+			if (seq[j] < seq[i]) {
+				sequence = seq[j];
+				nid = spec->multich_pin[j];
+				seq[j] = seq[i];
+				spec->multich_pin[j] = spec->multich_pin[i];
+				seq[i] = sequence;
+				spec->multich_pin[i] = nid;
+			}
+		}
+	}
+	/* the pin assignment is for front, C/LFE, surround and back */
+	if (max_channel >= 6) {
+		hda_nid_t temp;
+		/* exchange pin of C/LFE and surround */
+		temp = spec->multich_pin[1];
+		spec->multich_pin[1] = spec->multich_pin[2];
+		spec->multich_pin[2] = temp;
+	}
+	return max_channel;
+}
+
+/* fill in the multi_dac_nids table, which will decide
+   which audio widget to use for each channel */
+static int cmi9880_fill_multi_dac_nids(struct hda_codec *codec)
+{
+	struct cmi_spec *spec = codec->spec;
+	hda_nid_t nid;
+	int assigned[4];
+	int i, j;
+
+	/* clear the table, only one c-media dac assumed here */
+	memset(cmi9880_multi_dac_nids, 0, sizeof(cmi9880_multi_dac_nids));
+	memset(assigned, 0, sizeof(assigned));
+	/* check the pins we found */
+	for (i = 0; i < spec->multiout.max_channels / 2; i++) {
+		nid = spec->multich_pin[i];
+		/* nid 0x0b~0x0e is hardwired to audio widget 0x3~0x6 */
+		if (nid <= 0x0e && nid >= 0x0b) {
+			cmi9880_multi_dac_nids[i] = nid - 0x08;
+			assigned[nid - 0x0b] = 1;
+		}
+	}
+	/* left pin can be connect to any audio widget */
+	for (i = 0; i < spec->multiout.max_channels / 2; i++) {
+		if (!assigned[i]) {
+			/* search for an empty channel */
+			/* I should also check the pin type */
+			for (j = 0; j < ARRAY_SIZE(cmi9880_multi_dac_nids); j++)
+				if (!cmi9880_multi_dac_nids[j]) {
+					cmi9880_multi_dac_nids[j] = i + 3;
+					assigned[i] = 1;
+					break;
+				}
+		}
+	}
+	return 0;
+}
+
+/* create multi_init table, which is used for multichannel initialization */
+static int cmi9880_fill_multi_init(struct hda_codec *codec)
+{
+	struct cmi_spec *spec = codec->spec;
+	hda_nid_t nid;
+	int i, j, k, len;
+
+	/* clear the table, only one c-media dac assumed here */
+	memset(cmi9880_multi_init, 0, sizeof(cmi9880_multi_init));
+	for (j = 0, i = 0; i < spec->multiout.max_channels / 2; i++) {
+		hda_nid_t conn[4];
+		nid = spec->multich_pin[i];
+		/* set as output */
+		cmi9880_multi_init[j].nid = nid;
+		cmi9880_multi_init[j].verb = AC_VERB_SET_PIN_WIDGET_CONTROL;
+		cmi9880_multi_init[j].param = 0xc0;
+		j++;
+		/* nid 0x0f,0x10,0x1f,0x20 are needed to set connection */
+		switch (nid) {
+		case 0x0f:
+		case 0x10:
+		case 0x1f:
+		case 0x20:
+			/* set connection */
+			cmi9880_multi_init[j].nid = nid;
+			cmi9880_multi_init[j].verb = AC_VERB_SET_CONNECT_SEL;
+			/* find the index in connect list */
+			len = snd_hda_get_connections(codec, nid, conn, 4);
+			for (k = 0; k < len; k++)
+				if (conn[k] == cmi9880_multi_dac_nids[i])
+					break;
+			cmi9880_multi_init[j].param = k < len ? k : 0;
+			j++;
+			break;
+		}
+	}
+	return 0;
+}
+
 static int cmi9880_init(struct hda_codec *codec)
 {
 	struct cmi_spec *spec = codec->spec;
@@ -360,6 +559,8 @@
 		snd_hda_sequence_write(codec, cmi9880_allout_init);
 	else
 		snd_hda_sequence_write(codec, cmi9880_basic_init);
+	if (spec->board_config == CMI_AUTO)
+		snd_hda_sequence_write(codec, cmi9880_multi_init);
 	return 0;
 }
 
@@ -546,6 +747,7 @@
 	{ .modelname = "full", .config = CMI_FULL },
 	{ .modelname = "full_dig", .config = CMI_FULL_DIG },
 	{ .modelname = "allout", .config = CMI_ALLOUT },
+	{ .modelname = "auto", .config = CMI_AUTO },
 	{} /* terminator */
 };
 
@@ -605,6 +807,54 @@
 		spec->input_mux = &cmi9880_no_line_mux;
 		spec->multiout.dig_out_nid = CMI_DIG_OUT_NID;
 		break;
+	case CMI_AUTO:
+		{
+		unsigned int port_e, port_f, port_g, port_h;
+		unsigned int port_spdifi, port_spdifo;
+		/* collect pin default configuration */
+		cmi9880_get_pin_def_config(codec);
+		port_e = cmi9880_get_def_config(codec, 0x0f);
+		port_f = cmi9880_get_def_config(codec, 0x10);
+		port_g = cmi9880_get_def_config(codec, 0x1f);
+		port_h = cmi9880_get_def_config(codec, 0x20);
+		port_spdifi = cmi9880_get_def_config(codec, 0x13);
+		port_spdifo = cmi9880_get_def_config(codec, 0x12);
+		spec->front_panel = 1;
+		if ((get_defcfg_connect(port_e) == AC_JACK_PORT_NONE)
+		|| (get_defcfg_connect(port_f) == AC_JACK_PORT_NONE)) {
+			spec->surr_switch = 1;
+			/* no front panel */
+			if ((get_defcfg_connect(port_g) == AC_JACK_PORT_NONE)
+			|| (get_defcfg_connect(port_h) == AC_JACK_PORT_NONE)) {
+				/* no optional rear panel */
+				spec->board_config = CMI_MINIMAL;
+				spec->front_panel = 0;
+				spec->num_ch_modes = 2;
+			} else
+				spec->board_config = CMI_MIN_FP;
+				spec->num_ch_modes = 3;
+			spec->channel_modes = cmi9880_channel_modes;
+			spec->input_mux = &cmi9880_basic_mux;
+		} else {
+			spec->input_mux = &cmi9880_basic_mux;
+			if (get_defcfg_connect(port_spdifo) != 1)
+				spec->multiout.dig_out_nid = CMI_DIG_OUT_NID;
+			if (get_defcfg_connect(port_spdifi) != 1)
+				spec->dig_in_nid = CMI_DIG_IN_NID;
+		}
+		spec->multiout.max_channels = cmi9880_get_multich_pins(codec);
+		cmi9880_fill_multi_dac_nids(codec);
+		cmi9880_fill_multi_init(codec);
+		spec->multiout.num_dacs = 4;
+		spec->multiout.dac_nids = cmi9880_multi_dac_nids;
+
+		spec->adc_nids = cmi9880_adc_nids;
+
+		codec->patch_ops = cmi9880_patch_ops;
+
+		return 0;
+		}
+		break;
 	}
 
 	spec->multiout.num_dacs = 4;

^ permalink raw reply	[flat|nested] 4+ messages in thread
* RE: [patch] patch_cm9880.c to choose multi-channel jacks automatically
@ 2005-04-08  0:32 "Tien,  C.L. - 田承禮"
  2005-04-08 14:07 ` Takashi Iwai
  0 siblings, 1 reply; 4+ messages in thread
From: "Tien,  C.L. - 田承禮" @ 2005-04-08  0:32 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel

[-- Attachment #1: Type: text/plain, Size: 1822 bytes --]

Hi,

I move the cm9880_multi_init array too.


-----Original Message-----
From:	Takashi Iwai [mailto:tiwai@suse.de]
Sent:	2005/4/5 [星期二] 下午 12:27
To:	Tien,  C.L. - 田承禮
Cc:	alsa-devel@alsa-project.org
Subject:	Re: [patch] patch_cm9880.c to choose multi-channel jacks automatically
Hi,

thanks for the patch!

At Tue, 5 Apr 2005 07:04:47 +0800,
Tien, C.L. wrote:
> 
> Takashi,
> 
> 1. I found the current code has basic_init, which already includes
> necessary controls for 6-stack initialization, so I don't need
> another model. 
> 
> 2. I add a new model "auto" to let the driver find a. if there are
> option real panel/front panel, b. the jacks to be used for
> multichannel. 
> 
> Because the jack color are based on MS's channel sequence, so the
> "auto" model will pick the same jacks for multichannel MS uses. I
> did this to hope to minimize users questions. These code can also be
> applied to other codecs but I don't have any to test. 

Yes, this looks quite generic.  I'll try to test the similar code on
ALC882 on my machine later.

I found that you're modifying the static cmi9880_dac_nids[], which I
don't like well.  I put it to spec->dac_nids[], so it can be safely
modified.

*I didn't change cmi9880_dac_nids, what I modified is the array cmi988_multi_dac_nids I added. But indeed it should be inside the spec.
I also move cm9880_multi_init array to multi_init array inside spec structure. The initial value in the old patch is for explain how the array size is decided.

The below is the modified patch.  In addition to the above, I changed
the default mode to AUTO, since this looks more convenient than
FULL_DIG.  Could you check it?

*Yes, it works the same way as my old patch, thank you!

Thanks,

Takashi


Sincerely,
ChenLi Tien


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: patch_cmedia.c.patch --]
[-- Type: text/x-patch; name="patch_cmedia.c.patch", Size: 9562 bytes --]

Index: alsa-kernel/pci/hda/patch_cmedia.c
===================================================================
RCS file: /cvsroot/alsa/alsa-kernel/pci/hda/patch_cmedia.c,v
retrieving revision 1.7
diff -a -u -r1.7 patch_cmedia.c
--- alsa-kernel/pci/hda/patch_cmedia.c	24 Mar 2005 20:47:35 -0000	1.7
+++ alsa-kernel/pci/hda/patch_cmedia.c	8 Apr 2005 00:17:21 -0000
@@ -29,6 +29,7 @@
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#define NUM_PINS	11
 
 
 /* board config type */
@@ -38,6 +39,7 @@
 	CMI_FULL,	/* back 6-jack + front-panel 2-jack */
 	CMI_FULL_DIG,	/* back 6-jack + front-panel 2-jack + digital I/O */
 	CMI_ALLOUT,	/* back 5-jack + front-panel 2-jack + digital out */
+	CMI_AUTO,	/* let driver guess it */
 };
 
 struct cmi_spec {
@@ -48,6 +50,7 @@
 
 	/* playback */
 	struct hda_multi_out multiout;
+	hda_nid_t dac_nids[4];		/* NID for each DAC */
 
 	/* capture */
 	hda_nid_t *adc_nids;
@@ -63,6 +66,15 @@
 	const struct cmi_channel_mode *channel_modes;
 
 	struct hda_pcm pcm_rec[2];	/* PCM information */
+
+	/* pin deafault configuration */
+	hda_nid_t pin_nid[NUM_PINS];
+	unsigned int def_conf[NUM_PINS];
+	unsigned int pin_def_confs;
+
+	/* multichannel pins */
+	hda_nid_t multich_pin[4];	/* max 8-channel */
+	struct hda_verb multi_init[9];	/* 2 verbs for each pin + terminator */
 };
 
 /*
@@ -353,6 +365,175 @@
 	return 0;
 }
 
+#define AC_DEFCFG_ASSOC_SHIFT		4
+#define get_defcfg_connect(cfg) ((cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT)
+#define get_defcfg_association(cfg) ((cfg & AC_DEFCFG_DEF_ASSOC) >> AC_DEFCFG_ASSOC_SHIFT)
+#define get_defcfg_sequence(cfg) (cfg & AC_DEFCFG_SEQUENCE)
+
+/* get all pin default configuration in def_conf */
+static int cmi9880_get_pin_def_config(struct hda_codec *codec)
+{
+	struct cmi_spec *spec = codec->spec;
+	hda_nid_t nid, nid_start;
+	int i = 0, nodes;
+
+	nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid_start);
+	for (nid = nid_start; nid < nodes + nid_start; nid++) {
+		unsigned int wid_caps = snd_hda_param_read(codec, nid,
+						   AC_PAR_AUDIO_WIDGET_CAP);
+		unsigned int wid_type = (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+		/* read all default configuration for pin complex */
+		if (wid_type == AC_WID_PIN) {
+			spec->pin_nid[i] = nid;
+			spec->def_conf[i] = 
+				snd_hda_codec_read(codec, nid, 0,
+					AC_VERB_GET_CONFIG_DEFAULT, 0);
+			i++;
+		}
+	}
+	spec->pin_def_confs = i;
+	return 0;
+}
+
+/* get a pin default configuration of nid in def_conf */
+static unsigned int cmi9880_get_def_config(struct hda_codec *codec, hda_nid_t nid)
+{
+	struct cmi_spec *spec = codec->spec;
+	int i = 0;
+
+	while (spec->pin_nid[i] != nid && i < spec->pin_def_confs)
+		i++;
+	if (i == spec->pin_def_confs)
+		return (unsigned int) -1;
+	else
+		return spec->def_conf[i];
+}
+
+/* decide what pins to use for multichannel playback */
+static int cmi9880_get_multich_pins(struct hda_codec *codec)
+{
+	struct cmi_spec *spec = codec->spec;
+	int i, j, pins, seq[4];
+	int max_channel = 0;
+	unsigned int def_conf, sequence;
+	hda_nid_t nid;
+
+	memset(spec->multich_pin, 0, sizeof(spec->multich_pin));
+	for (pins = 0, i = 0; i < spec->pin_def_confs && pins < 4; i++) {
+		def_conf = spec->def_conf[i];
+		/* skip pin not connected */
+		if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
+			continue;
+		/* get the sequence if association == 1 */
+		/* the other pins have association = 0, incorrect in spec 1.0 */
+		if (get_defcfg_association(def_conf) == 1) {
+			sequence = get_defcfg_sequence(def_conf);
+			seq[pins] = sequence;
+			spec->multich_pin[pins] = spec->pin_nid[i];
+			pins++;	// ready for next slot
+			max_channel += 2;
+		}
+	}
+	/* sort by sequence, data collected here will be for Windows */ 
+	for (i = 0; i < pins; i++) {
+		for (j = i + 1; j < pins; j++) {
+			if (seq[j] < seq[i]) {
+				sequence = seq[j];
+				nid = spec->multich_pin[j];
+				seq[j] = seq[i];
+				spec->multich_pin[j] = spec->multich_pin[i];
+				seq[i] = sequence;
+				spec->multich_pin[i] = nid;
+			}
+		}
+	}
+	/* the pin assignment is for front, C/LFE, surround and back */
+	if (max_channel >= 6) {
+		hda_nid_t temp;
+		/* exchange pin of C/LFE and surround */
+		temp = spec->multich_pin[1];
+		spec->multich_pin[1] = spec->multich_pin[2];
+		spec->multich_pin[2] = temp;
+	}
+	return max_channel;
+}
+
+/* fill in the multi_dac_nids table, which will decide
+   which audio widget to use for each channel */
+static int cmi9880_fill_multi_dac_nids(struct hda_codec *codec)
+{
+	struct cmi_spec *spec = codec->spec;
+	hda_nid_t nid;
+	int assigned[4];
+	int i, j;
+
+	/* clear the table, only one c-media dac assumed here */
+	memset(spec->dac_nids, 0, sizeof(spec->dac_nids));
+	memset(assigned, 0, sizeof(assigned));
+	/* check the pins we found */
+	for (i = 0; i < spec->multiout.max_channels / 2; i++) {
+		nid = spec->multich_pin[i];
+		/* nid 0x0b~0x0e is hardwired to audio widget 0x3~0x6 */
+		if (nid <= 0x0e && nid >= 0x0b) {
+			spec->dac_nids[i] = nid - 0x08;
+			assigned[nid - 0x0b] = 1;
+		}
+	}
+	/* left pin can be connect to any audio widget */
+	for (i = 0; i < spec->multiout.max_channels / 2; i++) {
+		if (!assigned[i]) {
+			/* search for an empty channel */
+			/* I should also check the pin type */
+			for (j = 0; j < ARRAY_SIZE(spec->dac_nids); j++)
+				if (! spec->dac_nids[j]) {
+					spec->dac_nids[j] = i + 3;
+					assigned[i] = 1;
+					break;
+				}
+		}
+	}
+	return 0;
+}
+
+/* create multi_init table, which is used for multichannel initialization */
+static int cmi9880_fill_multi_init(struct hda_codec *codec)
+{
+	struct cmi_spec *spec = codec->spec;
+	hda_nid_t nid;
+	int i, j, k, len;
+
+	/* clear the table, only one c-media dac assumed here */
+	memset(spec->multi_init, 0, sizeof(spec->multi_init));
+	for (j = 0, i = 0; i < spec->multiout.max_channels / 2; i++) {
+		hda_nid_t conn[4];
+		nid = spec->multich_pin[i];
+		/* set as output */
+		spec->multi_init[j].nid = nid;
+		spec->multi_init[j].verb = AC_VERB_SET_PIN_WIDGET_CONTROL;
+		spec->multi_init[j].param = 0xc0;
+		j++;
+		/* nid 0x0f,0x10,0x1f,0x20 are needed to set connection */
+		switch (nid) {
+		case 0x0f:
+		case 0x10:
+		case 0x1f:
+		case 0x20:
+			/* set connection */
+			spec->multi_init[j].nid = nid;
+			spec->multi_init[j].verb = AC_VERB_SET_CONNECT_SEL;
+			/* find the index in connect list */
+			len = snd_hda_get_connections(codec, nid, conn, 4);
+			for (k = 0; k < len; k++)
+				if (conn[k] == spec->dac_nids[i])
+					break;
+			spec->multi_init[j].param = k < len ? k : 0;
+			j++;
+			break;
+		}
+	}
+	return 0;
+}
+
 static int cmi9880_init(struct hda_codec *codec)
 {
 	struct cmi_spec *spec = codec->spec;
@@ -360,6 +541,8 @@
 		snd_hda_sequence_write(codec, cmi9880_allout_init);
 	else
 		snd_hda_sequence_write(codec, cmi9880_basic_init);
+	if (spec->board_config == CMI_AUTO)
+		snd_hda_sequence_write(codec, spec->multi_init);
 	return 0;
 }
 
@@ -546,6 +729,7 @@
 	{ .modelname = "full", .config = CMI_FULL },
 	{ .modelname = "full_dig", .config = CMI_FULL_DIG },
 	{ .modelname = "allout", .config = CMI_ALLOUT },
+	{ .modelname = "auto", .config = CMI_AUTO },
 	{} /* terminator */
 };
 
@@ -570,10 +754,13 @@
 	codec->spec = spec;
 	spec->board_config = snd_hda_check_board_config(codec, cmi9880_cfg_tbl);
 	if (spec->board_config < 0) {
-		snd_printd(KERN_INFO "hda_codec: Unknown model for CMI9880\n");
-		spec->board_config = CMI_FULL_DIG; /* try everything */
+		snd_printdd(KERN_INFO "hda_codec: Unknown model for CMI9880\n");
+		spec->board_config = CMI_AUTO; /* try everything */
 	}
 
+	/* copy default DAC NIDs */
+	memcpy(spec->dac_nids, cmi9880_dac_nids, sizeof(spec->dac_nids));
+
 	switch (spec->board_config) {
 	case CMI_MINIMAL:
 	case CMI_MIN_FP:
@@ -605,10 +792,50 @@
 		spec->input_mux = &cmi9880_no_line_mux;
 		spec->multiout.dig_out_nid = CMI_DIG_OUT_NID;
 		break;
+	case CMI_AUTO:
+		{
+		unsigned int port_e, port_f, port_g, port_h;
+		unsigned int port_spdifi, port_spdifo;
+		/* collect pin default configuration */
+		cmi9880_get_pin_def_config(codec);
+		port_e = cmi9880_get_def_config(codec, 0x0f);
+		port_f = cmi9880_get_def_config(codec, 0x10);
+		port_g = cmi9880_get_def_config(codec, 0x1f);
+		port_h = cmi9880_get_def_config(codec, 0x20);
+		port_spdifi = cmi9880_get_def_config(codec, 0x13);
+		port_spdifo = cmi9880_get_def_config(codec, 0x12);
+		spec->front_panel = 1;
+		if ((get_defcfg_connect(port_e) == AC_JACK_PORT_NONE)
+		|| (get_defcfg_connect(port_f) == AC_JACK_PORT_NONE)) {
+			spec->surr_switch = 1;
+			/* no front panel */
+			if ((get_defcfg_connect(port_g) == AC_JACK_PORT_NONE)
+			|| (get_defcfg_connect(port_h) == AC_JACK_PORT_NONE)) {
+				/* no optional rear panel */
+				spec->board_config = CMI_MINIMAL;
+				spec->front_panel = 0;
+				spec->num_ch_modes = 2;
+			} else
+				spec->board_config = CMI_MIN_FP;
+				spec->num_ch_modes = 3;
+			spec->channel_modes = cmi9880_channel_modes;
+			spec->input_mux = &cmi9880_basic_mux;
+		} else {
+			spec->input_mux = &cmi9880_basic_mux;
+			if (get_defcfg_connect(port_spdifo) != 1)
+				spec->multiout.dig_out_nid = CMI_DIG_OUT_NID;
+			if (get_defcfg_connect(port_spdifi) != 1)
+				spec->dig_in_nid = CMI_DIG_IN_NID;
+		}
+		spec->multiout.max_channels = cmi9880_get_multich_pins(codec);
+		cmi9880_fill_multi_dac_nids(codec);
+		cmi9880_fill_multi_init(codec);
+		}
+		break;
 	}
 
 	spec->multiout.num_dacs = 4;
-	spec->multiout.dac_nids = cmi9880_dac_nids;
+	spec->multiout.dac_nids = spec->dac_nids;
 
 	spec->adc_nids = cmi9880_adc_nids;
 

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2005-04-08 14:07 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-04-04 23:04 [patch] patch_cm9880.c to choose multi-channel jacks automatically "Tien,  C.L. - 田承禮"
2005-04-05 16:27 ` Takashi Iwai
  -- strict thread matches above, loose matches on Subject: below --
2005-04-08  0:32 "Tien,  C.L. - 田承禮"
2005-04-08 14:07 ` Takashi Iwai

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.