Alsa-Devel Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Takashi Iwai <tiwai@suse.de>
To: "Tien, C.L." <cltien@cmedia.com.tw>
Cc: alsa-devel@alsa-project.org
Subject: Re: [patch] patch_cm9880.c to choose multi-channel jacks automatically
Date: Tue, 05 Apr 2005 18:27:12 +0200	[thread overview]
Message-ID: <s5hoectxk7z.wl@alsa2.suse.de> (raw)
In-Reply-To: <92C0412E07F63549B2A2F2345D3DB51501E4B057@cm-msg-02.cmedia.com.tw>

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

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.

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?


Thanks,

Takashi

[-- Attachment #2: Type: text/plain, Size: 10279 bytes --]

Index: alsa-kernel/pci/hda/patch_cmedia.c
===================================================================
RCS file: /home/iwai/cvs/alsa/alsa-kernel/pci/hda/patch_cmedia.c,v
retrieving revision 1.7
diff -u -r1.7 patch_cmedia.c
--- alsa-kernel/pci/hda/patch_cmedia.c	24 Mar 2005 20:46:02 -0000	1.7
+++ alsa-kernel/pci/hda/patch_cmedia.c	5 Apr 2005 16:22: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 {
@@ -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,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 */
 };
 
 /*
@@ -267,6 +278,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 +380,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(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] == spec->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 +556,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 +744,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 +769,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 +807,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;
 

  reply	other threads:[~2005-04-05 16:27 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 [this message]
  -- strict thread matches above, loose matches on Subject: below --
2005-04-08  0:32 "Tien,  C.L. - 田承禮"
2005-04-08 14:07 ` Takashi Iwai

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=s5hoectxk7z.wl@alsa2.suse.de \
    --to=tiwai@suse.de \
    --cc=alsa-devel@alsa-project.org \
    --cc=cltien@cmedia.com.tw \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox