List Info

Thread: Finalize HDA restructuring for sound/pci/hda/hda_*.{h, c}




Finalize HDA restructuring for sound/pci/hda/hda_*.{h, c}
user name
2006-03-10 11:34:17
UpstreamStatus: Merged upstream

This patch finalizes the changes from upstream CVS adding
audible
functionality for a variety of HDA chipsets not reliant on
individual
patch_*.c codecs. The important fixes include falling back
to a single
mode when autoprobing fails (preventing azx_get_response
floods),
adding helper routines to reenable sound on select Fujitsu
laptops, and
fixing PM support on HDA chipsets.

Signed-off-by: Daniel T Chen <crimsunubuntu.com>

---

 sound/pci/hda/hda_codec.c   |  271
+++++++++++++++++++++++++++++++++++--------
 sound/pci/hda/hda_codec.h   |   18 ++-
 sound/pci/hda/hda_generic.c |   23 +++-
 sound/pci/hda/hda_intel.c   |  169
+++++++++++++++------------
 sound/pci/hda/hda_local.h   |   56 ++++++++-
 sound/pci/hda/hda_proc.c    |    1 
 6 files changed, 403 insertions(+), 135 deletions(-)

a5f42db12d572b3aae9fbdb0c65e8a06ed5de86c
diff --git a/sound/pci/hda/hda_codec.c
b/sound/pci/hda/hda_codec.c
index 0dbeeaf..a537397 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
 -155,8
+155,9  int snd_hda_get_connections(struct hda_c
 			    hda_nid_t *conn_list, int max_conns)
 {
 	unsigned int parm;
-	int i, j, conn_len, num_tupples, conns;
+	int i, conn_len, conns;
 	unsigned int shift, num_elems, mask;
+	hda_nid_t prev_nid;
 
 	snd_assert(conn_list && max_conns > 0, return
-EINVAL);
 
 -171,7
+172,6  int snd_hda_get_connections(struct hda_c
 		num_elems = 4;
 	}
 	conn_len = parm & AC_CLIST_LENGTH;
-	num_tupples = num_elems / 2;
 	mask = (1 << (shift-1)) - 1;
 
 	if (! conn_len)
 -186,40
+186,38  int snd_hda_get_connections(struct hda_c
 
 	/* multi connection */
 	conns = 0;
-	for (i = 0; i < conn_len; i += num_elems) {
-		parm = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_CONNECT_LIST, i);
-		for (j = 0; j < num_tupples; j++) {
-			int range_val;
-			hda_nid_t val1, val2, n;
-			range_val = parm & (1 << (shift-1)); /* ranges
*/
-			val1 = parm & mask;
-			parm >>= shift;
-			val2 = parm & mask;
-			parm >>= shift;
-			if (range_val) {
-				/* ranges between val1 and val2 */
-				if (val1 > val2) {
-					snd_printk(KERN_WARNING "hda_codec: invalid
dep_range_val %x:%x\n", val1, val2);
-					continue;
-				}
-				for (n = val1; n <= val2; n++) {
-					if (conns >= max_conns)
-						return -EINVAL;
-					conn_list[conns++] = n;
-				}
-			} else {
-				if (! val1)
-					break;
-				if (conns >= max_conns)
-					return -EINVAL;
-				conn_list[conns++] = val1;
-				if (! val2)
-					break;
-				if (conns >= max_conns)
+	prev_nid = 0;
+	for (i = 0; i < conn_len; i++) {
+		int range_val;
+		hda_nid_t val, n;
+
+		if (i % num_elems == 0)
+			parm = snd_hda_codec_read(codec, nid, 0,
+						  AC_VERB_GET_CONNECT_LIST, i);
+		range_val = !! (parm & (1 << (shift-1))); /*
ranges */
+		val = parm & mask;
+		parm >>= shift;
+		if (range_val) {
+			/* ranges between the previous and this one */
+			if (! prev_nid || prev_nid >= val) {
+				snd_printk(KERN_WARNING "hda_codec: invalid
dep_range_val %x:%x\n", prev_nid, val);
+				continue;
+			}
+			for (n = prev_nid + 1; n <= val; n++) {
+				if (conns >= max_conns) {
+					snd_printk(KERN_ERR "Too many
connections\n");
 					return -EINVAL;
-				conn_list[conns++] = val2;
+				}
+				conn_list[conns++] = n;
 			}
+		} else {
+			if (conns >= max_conns) {
+				snd_printk(KERN_ERR "Too many
connections\n");
+				return -EINVAL;
+			}
+			conn_list[conns++] = val;
 		}
+		prev_nid = val;
 	}
 	return conns;
 }
 -288,6
+286,9  static int init_unsol_queue(struct hda_b
 {
 	struct hda_bus_unsolicited *unsol;
 
+	if (bus->unsol) /* already initialized */
+		return 0;
+
 	unsol = kzalloc(sizeof(*unsol), GFP_KERNEL);
 	if (! unsol) {
 		snd_printk(KERN_ERR "hda_codec: can't allocate
unsolicited queue\n");
 -373,8
+374,6  int snd_hda_bus_new(snd_card_t *card, co
 	init_MUTEX(&bus->cmd_mutex);
 	INIT_LIST_HEAD(&bus->codec_list);
 
-	init_unsol_queue(bus);
-
 	if ((err = snd_device_new(card, SNDRV_DEV_BUS, bus,
&dev_ops)) < 0) {
 		snd_hda_bus_free(bus);
 		return err;
 -455,6
+454,27  static void setup_fg_nodes(struct hda_co
 }
 
 /*
+ * read widget caps for each widget and store in cache
+ */
+static int read_widget_caps(struct hda_codec *codec,
hda_nid_t fg_node)
+{
+	int i;
+	hda_nid_t nid;
+
+	codec->num_nodes = snd_hda_get_sub_nodes(codec,
fg_node,
+						 &codec->start_nid);
+	codec->wcaps = kmalloc(codec->num_nodes * 4,
GFP_KERNEL);
+	if (! codec->wcaps)
+		return -ENOMEM;
+	nid = codec->start_nid;
+	for (i = 0; i < codec->num_nodes; i++, nid++)
+		codec->wcaps[i] = snd_hda_param_read(codec, nid,
+						     AC_PAR_AUDIO_WIDGET_CAP);
+	return 0;
+}
+
+
+/*
  * codec destructor
  */
 static void snd_hda_codec_free(struct hda_codec *codec)
 -465,6
+485,8  static void snd_hda_codec_free(struct hd
 	codec->bus->caddr_tbl[codec->addr] = NULL;
 	if (codec->patch_ops.free)
 		codec->patch_ops.free(codec);
+	kfree(codec->amp_info);
+	kfree(codec->wcaps);
 	kfree(codec);
 }
 
 -508,6
+530,12  int snd_hda_codec_new(struct hda_bus *bu
 	bus->caddr_tbl[codec_addr] = codec;
 
 	codec->vendor_id = snd_hda_param_read(codec,
AC_NODE_ROOT, AC_PAR_VENDOR_ID);
+	if (codec->vendor_id == -1)
+		/* read again, hopefully the access method was corrected
+		 * in the last read...
+		 */
+		codec->vendor_id = snd_hda_param_read(codec,
AC_NODE_ROOT,
+						      AC_PAR_VENDOR_ID);
 	codec->subsystem_id = snd_hda_param_read(codec,
AC_NODE_ROOT, AC_PAR_SUBSYSTEM_ID);
 	codec->revision_id = snd_hda_param_read(codec,
AC_NODE_ROOT, AC_PAR_REV_ID);
 
 -518,6
+546,12  int snd_hda_codec_new(struct hda_bus *bu
 		return -ENODEV;
 	}
 
+	if (read_widget_caps(codec, codec->afg ? codec->afg
: codec->mfg) < 0) {
+		snd_printk(KERN_ERR "hda_codec: cannot
malloc\n");
+		snd_hda_codec_free(codec);
+		return -ENOMEM;
+	}
+
 	if (! codec->subsystem_id) {
 		hda_nid_t nid = codec->afg ? codec->afg :
codec->mfg;
 		codec->subsystem_id = snd_hda_codec_read(codec, nid,
0,
 -539,6
+573,9  int snd_hda_codec_new(struct hda_bus *bu
 		return err;
 	}
 
+	if (codec->patch_ops.unsol_event)
+		init_unsol_queue(bus);
+
 	snd_hda_codec_proc_new(codec);
 
 	sprintf(component, "HDA:%08x",
codec->vendor_id);
 -586,6
+623,8  static void init_amp_hash(struct hda_cod
 {
 	memset(codec->amp_hash, 0xff,
sizeof(codec->amp_hash));
 	codec->num_amp_entries = 0;
+	codec->amp_info_size = 0;
+	codec->amp_info = NULL;
 }
 
 /* query the hash.  allocate an entry if not found. */
 -603,9
+642,22  static struct hda_amp_info *get_alloc_am
 	}
 
 	/* add a new hash entry */
-	if (codec->num_amp_entries >=
ARRAY_SIZE(codec->amp_info)) {
-		snd_printk(KERN_ERR "hda_codec: Tooooo many
amps!\n");
-		return NULL;
+	if (codec->num_amp_entries >=
codec->amp_info_size) {
+		/* reallocate the array */
+		int new_size = codec->amp_info_size + 64;
+		struct hda_amp_info *new_info = kcalloc(new_size,
sizeof(struct hda_amp_info),
+							GFP_KERNEL);
+		if (! new_info) {
+			snd_printk(KERN_ERR "hda_codec: can't malloc
amp_info\n");
+			return NULL;
+		}
+		if (codec->amp_info) {
+			memcpy(new_info, codec->amp_info,
+			       codec->amp_info_size * sizeof(struct
hda_amp_info));
+			kfree(codec->amp_info);
+		}
+		codec->amp_info_size = new_size;
+		codec->amp_info = new_info;
 	}
 	cur = codec->num_amp_entries++;
 	info = &codec->amp_info[cur];
 -627,7
+679,7  static u32 query_amp_caps(struct hda_cod
 	if (! info)
 		return 0;
 	if (! (info->status & INFO_AMP_CAPS)) {
-		if (!(snd_hda_param_read(codec, nid,
AC_PAR_AUDIO_WIDGET_CAP) & AC_WCAP_AMP_OVRD))
+		if (! (get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD))
 			nid = codec->afg;
 		info->amp_caps = snd_hda_param_read(codec, nid,
direction == HDA_OUTPUT ?
 						    AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
 -676,7
+728,8  static void put_vol_mute(struct hda_code
 /*
  * read AMP value.  The volume is between 0 to 0x7f, 0x80 =
mute bit.
  */
-static int snd_hda_codec_amp_read(struct hda_codec *codec,
hda_nid_t nid, int ch, int direction, int index)
+int snd_hda_codec_amp_read(struct hda_codec *codec,
hda_nid_t nid, int ch,
+			   int direction, int index)
 {
 	struct hda_amp_info *info = get_alloc_amp_hash(codec,
HDA_HASH_KEY(nid, direction, index));
 	if (! info)
 -687,7
+740,8  static int snd_hda_codec_amp_read(struct
 /*
  * update the AMP value, mask = bit mask to set, val = the
value
  */
-static int snd_hda_codec_amp_update(struct hda_codec
*codec, hda_nid_t nid, int ch, int direction, int idx, int
mask, int val)
+int snd_hda_codec_amp_update(struct hda_codec *codec,
hda_nid_t nid, int ch,
+			     int direction, int idx, int mask, int val)
 {
 	struct hda_amp_info *info = get_alloc_amp_hash(codec,
HDA_HASH_KEY(nid, direction, idx));
 
 -1175,6
+1229,31  int snd_hda_create_spdif_in_ctls(struct 
 }
 
 
+/*
+ * set power state of the codec
+ */
+static void hda_set_power_state(struct hda_codec *codec,
hda_nid_t fg,
+				unsigned int power_state)
+{
+	hda_nid_t nid, nid_start;
+	int nodes;
+
+	snd_hda_codec_write(codec, fg, 0, AC_VERB_SET_POWER_STATE,
+			    power_state);
+
+	nodes = snd_hda_get_sub_nodes(codec, fg, &nid_start);
+	for (nid = nid_start; nid < nodes + nid_start; nid++) {
+		if (get_wcaps(codec, nid) & AC_WCAP_POWER)
+			snd_hda_codec_write(codec, nid, 0,
+					    AC_VERB_SET_POWER_STATE,
+					    power_state);
+	}
+
+	if (power_state == AC_PWRST_D0)
+		msleep(10);
+}
+
+
 /**
  * snd_hda_build_controls - build mixer controls
  * bus: the BUS
 -1202,6
+1281,9  int snd_hda_build_controls(struct hda_bu
 	list_for_each(p, &bus->codec_list) {
 		struct hda_codec *codec = list_entry(p, struct hda_codec,
list);
 		int err;
+		hda_set_power_state(codec,
+				    codec->afg ? codec->afg : codec->mfg,
+				    AC_PWRST_D0);
 		if (! codec->patch_ops.init)
 			continue;
 		err = codec->patch_ops.init(codec);
 -1320,7
+1402,7  int snd_hda_query_supported_pcm(struct h
 
 	val = 0;
 	if (nid != codec->afg &&
-	    snd_hda_param_read(codec, nid,
AC_PAR_AUDIO_WIDGET_CAP) & AC_WCAP_FORMAT_OVRD) {
+	    (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) {
 		val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
 		if (val == -1)
 			return -EIO;
 -1342,7
+1424,7  int snd_hda_query_supported_pcm(struct h
 		unsigned int bps;
 		unsigned int wcaps;
 
-		wcaps = snd_hda_param_read(codec, nid,
AC_PAR_AUDIO_WIDGET_CAP);
+		wcaps = get_wcaps(codec, nid);
 		streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
 		if (streams == -1)
 			return -EIO;
 -1412,7
+1494,7  int snd_hda_is_supported_format(struct h
 	unsigned int val = 0, rate, stream;
 
 	if (nid != codec->afg &&
-	    snd_hda_param_read(codec, nid,
AC_PAR_AUDIO_WIDGET_CAP) & AC_WCAP_FORMAT_OVRD) {
+	    (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) {
 		val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
 		if (val == -1)
 			return 0;
 -1638,14
+1720,74  int snd_hda_add_new_ctls(struct hda_code
 	int err;
 
 	for (; knew->name; knew++) {
-		err = snd_ctl_add(codec->bus->card,
snd_ctl_new1(knew, codec));
-		if (err < 0)
-			return err;
+		snd_kcontrol_t *kctl;
+		kctl = snd_ctl_new1(knew, codec);
+		if (! kctl)
+			return -ENOMEM;
+		err = snd_ctl_add(codec->bus->card, kctl);
+		if (err < 0) {
+			if (! codec->addr)
+				return err;
+			kctl = snd_ctl_new1(knew, codec);
+			if (! kctl)
+				return -ENOMEM;
+			kctl->id.device = codec->addr;
+			if ((err = snd_ctl_add(codec->bus->card, kctl))
< 0)
+				return err;
+		}
 	}
 	return 0;
 }
 
 
+ /*
+ * Channel mode helper
+ */
+int snd_hda_ch_mode_info(struct hda_codec *codec,
snd_ctl_elem_info_t *uinfo,
+			 const struct hda_channel_mode *chmode, int num_chmodes)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = num_chmodes;
+	if (uinfo->value.enumerated.item >= num_chmodes)
+		uinfo->value.enumerated.item = num_chmodes - 1;
+	sprintf(uinfo->value.enumerated.name,
"%dch",
+		chmode[uinfo->value.enumerated.item].channels);
+	return 0;
+}
+
+int snd_hda_ch_mode_get(struct hda_codec *codec,
snd_ctl_elem_value_t *ucontrol,
+			const struct hda_channel_mode *chmode, int num_chmodes,
+			int max_channels)
+{
+	int i;
+
+	for (i = 0; i < num_chmodes; i++) {
+		if (max_channels == chmode[i].channels) {
+			ucontrol->value.enumerated.item[0] = i;
+			break;
+		}
+	}
+	return 0;
+}
+
+int snd_hda_ch_mode_put(struct hda_codec *codec,
snd_ctl_elem_value_t *ucontrol,
+			const struct hda_channel_mode *chmode, int num_chmodes,
+			int *max_channelsp)
+{
+	unsigned int mode;
+
+	mode = ucontrol->value.enumerated.item[0];
+	snd_assert(mode < num_chmodes, return -EINVAL);
+	if (*max_channelsp == chmode[mode].channels && !
codec->in_resume)
+		return 0;
+	/* change the current channel setting */
+	*max_channelsp = chmode[mode].channels;
+	if (chmode[mode].sequence)
+		snd_hda_sequence_write(codec, chmode[mode].sequence);
+	return 1;
+}
+
 /*
  * input MUX helper
  */
 -1792,8
+1934,18  int snd_hda_multi_out_analog_cleanup(str
 /*
  * Helper for automatic ping configuration
  */
+
+static int is_in_nid_list(hda_nid_t nid, hda_nid_t *list)
+{
+	for (; *list; list++)
+		if (*list == nid)
+			return 1;
+	return 0;
+}
+
 /* parse all pin widgets and store the useful pin nids to
cfg */
-int snd_hda_parse_pin_def_config(struct hda_codec *codec,
struct auto_pin_cfg *cfg)
+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;
 -1806,8
+1958,7  int snd_hda_parse_pin_def_config(struct 
 
 	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_caps = get_wcaps(codec, nid);
 		unsigned int wid_type = (wid_caps & AC_WCAP_TYPE)
>> AC_WCAP_TYPE_SHIFT;
 		unsigned int def_conf;
 		short assoc, loc;
 -1815,13
+1966,16  int snd_hda_parse_pin_def_config(struct 
 		/* read all default configuration for pin complex */
 		if (wid_type != AC_WID_PIN)
 			continue;
+		/* ignore the given nids (e.g. pc-beep returns error) */
+		if (ignore_nids && is_in_nid_list(nid,
ignore_nids))
+			continue;
+
 		def_conf = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_CONFIG_DEFAULT, 0);
 		if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
 			continue;
 		loc = get_defcfg_location(def_conf);
 		switch (get_defcfg_device(def_conf)) {
 		case AC_JACK_LINE_OUT:
-		case AC_JACK_SPEAKER:
 			seq = get_defcfg_sequence(def_conf);
 			assoc = get_defcfg_association(def_conf);
 			if (! assoc)
 -1836,6
+1990,9  int snd_hda_parse_pin_def_config(struct 
 			sequences[cfg->line_outs] = seq;
 			cfg->line_outs++;
 			break;
+		case AC_JACK_SPEAKER:
+			cfg->speaker_pin = nid;
+			break;
 		case AC_JACK_HP_OUT:
 			cfg->hp_pin = nid;
 			break;
 -1902,6
+2059,12  int snd_hda_parse_pin_def_config(struct 
 	return 0;
 }
 
+/* labels for input pins */
+const char *auto_pin_cfg_labels[AUTO_PIN_LAST] = {
+	"Mic", "Front Mic",
"Line", "Front Line",
"CD", "Aux"
+};
+
+
 #ifdef CONFIG_PM
 /*
  * power management
 -1923,6
+2086,9  int snd_hda_suspend(struct hda_bus *bus,
 		struct hda_codec *codec = list_entry(p, struct hda_codec,
list);
 		if (codec->patch_ops.suspend)
 			codec->patch_ops.suspend(codec, state);
+		hda_set_power_state(codec,
+				    codec->afg ? codec->afg : codec->mfg,
+				    AC_PWRST_D3);
 	}
 	return 0;
 }
 -1940,6
+2106,9  int snd_hda_resume(struct hda_bus *bus)
 
 	list_for_each(p, &bus->codec_list) {
 		struct hda_codec *codec = list_entry(p, struct hda_codec,
list);
+		hda_set_power_state(codec,
+				    codec->afg ? codec->afg : codec->mfg,
+				    AC_PWRST_D0);
 		if (codec->patch_ops.resume)
 			codec->patch_ops.resume(codec);
 	}
diff --git a/sound/pci/hda/hda_codec.h
b/sound/pci/hda/hda_codec.h
index 1179d6c..fc5cbfe 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
 -98,7
+98,7  enum {
 #define AC_VERB_SET_UNSOLICITED_ENABLE		0x708
 #define AC_VERB_SET_PIN_SENSE			0x709
 #define AC_VERB_SET_BEEP_CONTROL		0x70a
-#define AC_VERB_SET_EAPD_BTLENALBE		0x70c
+#define AC_VERB_SET_EAPD_BTLENABLE		0x70c
 #define AC_VERB_SET_DIGI_CONVERT_1		0x70d
 #define AC_VERB_SET_DIGI_CONVERT_2		0x70e
 #define AC_VERB_SET_VOLUME_KNOB_CONTROL		0x70f
 -214,6
+214,12  enum {
 #define AC_PWRST_D2SUP			(1<<2)
 #define AC_PWRST_D3SUP			(1<<3)
 
+/* Power state values */
+#define AC_PWRST_D0			0x00
+#define AC_PWRST_D1			0x01
+#define AC_PWRST_D2			0x02
+#define AC_PWRST_D3			0x03
+
 /* Processing capabilies */
 #define AC_PCAP_BENIGN			(1<<0)
 #define AC_PCAP_NUM_COEF		(0xff<<8)
 -376,7
+382,7  enum {
 };
 
 /* max. connections to a widget */
-#define HDA_MAX_CONNECTIONS	16
+#define HDA_MAX_CONNECTIONS	32
 
 /* max. codec address */
 #define HDA_MAX_CODEC_ADDRESS	0x0f
 -542,10
+548,16  struct hda_codec {
 	/* codec specific info */
 	void *spec;
 
+	/* widget capabilities cache */
+	unsigned int num_nodes;
+	hda_nid_t start_nid;
+	u32 *wcaps;
+
 	/* hash for amp access */
 	u16 amp_hash[32];
 	int num_amp_entries;
-	struct hda_amp_info amp_info[128]; /* big enough? */
+	int amp_info_size;
+	struct hda_amp_info *amp_info;
 
 	struct semaphore spdif_mutex;
 	unsigned int spdif_status;	/* IEC958 status bits */
diff --git a/sound/pci/hda/hda_generic.c
b/sound/pci/hda/hda_generic.c
index d0eb9f2..00ead66 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
 -32,7
+32,8 
 struct hda_gnode {
 	hda_nid_t nid;		/* NID of this widget */
 	unsigned short nconns;	/* number of input connections */
-	hda_nid_t conn_list[HDA_MAX_CONNECTIONS]; /* input
connections */
+	hda_nid_t *conn_list;
+	hda_nid_t slist[2];	/* temporay list */
 	unsigned int wid_caps;	/* widget capabilities */
 	unsigned char type;	/* widget type */
 	unsigned char pin_ctl;	/* pin controls */
 -84,6
+85,8  static void snd_hda_generic_free(struct 
 	/* free all widgets */
 	list_for_each_safe(p, n, &spec->nid_list) {
 		struct hda_gnode *node = list_entry(p, struct hda_gnode,
list);
+		if (node->conn_list != node->slist)
+			kfree(node->conn_list);
 		kfree(node);
 	}
 	kfree(spec);
 -97,18
+100,32  static int add_new_node(struct hda_codec
 {
 	struct hda_gnode *node;
 	int nconns;
+	hda_nid_t conn_list[HDA_MAX_CONNECTIONS];
 
 	node = kzalloc(sizeof(*node), GFP_KERNEL);
 	if (node == NULL)
 		return -ENOMEM;
 	node->nid = nid;
-	nconns = snd_hda_get_connections(codec, nid,
node->conn_list, HDA_MAX_CONNECTIONS);
+	nconns = snd_hda_get_connections(codec, nid, conn_list,
+					 HDA_MAX_CONNECTIONS);
 	if (nconns < 0) {
 		kfree(node);
 		return nconns;
 	}
+	if (nconns <= ARRAY_SIZE(node->slist))
+		node->conn_list = node->slist;
+	else {
+		node->conn_list = kmalloc(sizeof(hda_nid_t) * nconns,
+					  GFP_KERNEL);
+		if (! node->conn_list) {
+			snd_printk(KERN_ERR "hda-generic: cannot
malloc\n");
+			kfree(node);
+			return -ENOMEM;
+		}
+	}
+	memcpy(node->conn_list, conn_list, nconns);
 	node->nconns = nconns;
-	node->wid_caps = snd_hda_param_read(codec, nid,
AC_PAR_AUDIO_WIDGET_CAP);
+	node->wid_caps = get_wcaps(codec, nid);
 	node->type = (node->wid_caps & AC_WCAP_TYPE)
>> AC_WCAP_TYPE_SHIFT;
 
 	if (node->type == AC_WID_PIN) {
diff --git a/sound/pci/hda/hda_intel.c
b/sound/pci/hda/hda_intel.c
index 646b23f..93f4cff 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
 -37,6
+37,7 
 #include <asm/io.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
+#include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/init.h>
 -51,6
+52,8  static int index = SNDRV_DEFAULT_IDX1;
 static char *id = SNDRV_DEFAULT_STR1;
 static char *model;
 static int position_fix;
+static int probe_mask = -1;
+static int single_cmd;
 
 module_param(index, int, 0444);
 MODULE_PARM_DESC(index, "Index value for Intel HD
audio interface.");
 -60,6
+63,11  module_param(model, charp, 0444);
 MODULE_PARM_DESC(model, "Use the given board
model.");
 module_param(position_fix, int, 0444);
 MODULE_PARM_DESC(position_fix, "Fix DMA pointer (0 =
auto, 1 = none, 2 = POSBUF, 3 = FIFO size).");
+module_param(probe_mask, int, 0444);
+MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs
(default = -1).");
+module_param(single_cmd, bool, 0444);
+MODULE_PARM_DESC(single_cmd, "Use single command to
communicate with codecs (for debugging only).");
+
 
 /* just for backward compatibility */
 static int enable;
 -230,12
+238,6  enum {
 #define NVIDIA_HDA_ENABLE_COHBITS     0x0f
 
 /*
- * Use CORB/RIRB for communication from/to codecs.
- * This is the way recommended by Intel (see below).
- */
-#define USE_CORB_RIRB
-
-/*
  */
 
 typedef struct snd_azx azx_t;
 -251,7
+253,6  struct snd_azx_dev {
 	unsigned int fragsize;		/* size of each period in bytes */
 	unsigned int frags;		/* number for period in the play
buffer */
 	unsigned int fifo_size;		/* FIFO size */
-	unsigned int last_pos;		/* last updated period position */
 
 	void __iomem *sd_addr;		/* stream descriptor pointer */
 
 -262,10
+263,11  struct snd_azx_dev {
 	unsigned int format_val;	/* format value to be set in the
controller and the codec */
 	unsigned char stream_tag;	/* assigned stream */
 	unsigned char index;		/* stream index */
+	/* for sanity check of position buffer */
+	unsigned int period_intr;
 
 	unsigned int opened: 1;
 	unsigned int running: 1;
-	unsigned int period_updating: 1;
 };
 
 /* CORB/RIRB */
 -324,6
+326,7  struct snd_azx {
 	/* flags */
 	int position_fix;
 	unsigned int initialized: 1;
+	unsigned int single_cmd: 1;
 };
 
 /* driver types */
 -387,7
+390,6  static char *driver_short_names[] __devi
  * Interface for HD codec
  */
 
-#ifdef USE_CORB_RIRB
 /*
  * CORB / RIRB interface
  */
 -435,11
+437,7  static void azx_init_cmd_io(azx_t *chip)
 	/* set N=1, get RIRB response interrupt for new entry */
 	azx_writew(chip, RINTCNT, 1);
 	/* enable rirb dma and response irq */
-#ifdef USE_CORB_RIRB
 	azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN |
ICH6_RBCTL_IRQ_EN);
-#else
-	azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN);
-#endif
 	chip->rirb.rp = chip->rirb.cmds = 0;
 }
 
 -451,8
+449,8  static void azx_free_cmd_io(azx_t *chip)
 }
 
 /* send a command */
-static int azx_send_cmd(struct hda_codec *codec, hda_nid_t
nid, int direct,
-			unsigned int verb, unsigned int para)
+static int azx_corb_send_cmd(struct hda_codec *codec,
hda_nid_t nid, int direct,
+			     unsigned int verb, unsigned int para)
 {
 	azx_t *chip = codec->bus->private_data;
 	unsigned int wp;
 -508,16
+506,21  static void azx_update_rirb(azx_t *chip)
 }
 
 /* receive a response */
-static unsigned int azx_get_response(struct hda_codec
*codec)
+static unsigned int azx_rirb_get_response(struct hda_codec
*codec)
 {
 	azx_t *chip = codec->bus->private_data;
 	int timeout = 50;
 
 	while (chip->rirb.cmds) {
 		if (! --timeout) {
-			snd_printk(KERN_ERR "azx_get_response
timeout\n");
+			snd_printk(KERN_ERR
+					"hda_intel: azx_get_response timeout, "
+					"switching to single_cmd mode...\n");
 			chip->rirb.rp = azx_readb(chip, RIRBWP);
 			chip->rirb.cmds = 0;
+			/* switch to single_cmd mode */
+			chip->single_cmd = 1;
+			azx_free_cmd_io(chip);
 			return -1;
 		}
 		msleep(1);
 -525,7
+528,6  static unsigned int azx_get_response(str
 	return chip->rirb.res; /* the last value */
 }
 
-#else
 /*
  * Use the single immediate command instead of CORB/RIRB
for simplicity
  *
 -536,13
+538,10  static unsigned int azx_get_response(str
  *       I left the codes, however, for debugging/testing
purposes.
  */
 
-#define azx_alloc_cmd_io(chip)	0
-#define azx_init_cmd_io(chip)
-#define azx_free_cmd_io(chip)
-
 /* send a command */
-static int azx_send_cmd(struct hda_codec *codec, hda_nid_t
nid, int direct,
-			unsigned int verb, unsigned int para)
+static int azx_single_send_cmd(struct hda_codec *codec,
hda_nid_t nid,
+			       int direct, unsigned int verb,
+			       unsigned int para)
 {
 	azx_t *chip = codec->bus->private_data;
 	u32 val;
 -570,7
+569,7  static int azx_send_cmd(struct hda_codec
 }
 
 /* receive a response */
-static unsigned int azx_get_response(struct hda_codec
*codec)
+static unsigned int azx_single_get_response(struct
hda_codec *codec)
 {
 	azx_t *chip = codec->bus->private_data;
 	int timeout = 50;
 -585,9
+584,35  static unsigned int azx_get_response(str
 	return (unsigned int)-1;
 }
 
-#define azx_update_rirb(chip)
+/*
+ * The below are the main callbacks from hda_codec.
+ *
+ * They are just the skeleton to call sub-callbacks
according to the
+ * current setting of chip->single_cmd.
+ */
+
+/* send a command */
+static int azx_send_cmd(struct hda_codec *codec, hda_nid_t
nid,
+			int direct, unsigned int verb,
+			unsigned int para)
+{
+	struct azx *chip = codec->bus->private_data;
+	if (chip->single_cmd)
+		return azx_single_send_cmd(codec, nid, direct, verb,
para);
+	else
+		return azx_corb_send_cmd(codec, nid, direct, verb, para);
+}
+
+/* get a response */
+static unsigned int azx_get_response(struct hda_codec
*codec)
+{
+	struct azx *chip = codec->bus->private_data;
+	if (chip->single_cmd)
+		return azx_single_get_response(codec);
+	else
+		return azx_rirb_get_response(codec);
+}
 
-#endif /* USE_CORB_RIRB */
 
 /* reset codec link */
 static int azx_reset(azx_t *chip)
 -734,7
+759,8  static void azx_init_chip(azx_t *chip)
 	azx_int_enable(chip);
 
 	/* initialize the codec command I/O */
-	azx_init_cmd_io(chip);
+	if (! chip->single_cmd)
+		azx_init_cmd_io(chip);
 
 	/* program the position buffer */
 	azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr);
 -781,11
+807,10  static irqreturn_t azx_interrupt(int irq
 		if (status & azx_dev->sd_int_sta_mask) {
 			azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK);
 			if (azx_dev->substream &&
azx_dev->running) {
-				azx_dev->period_updating = 1;
+				azx_dev->period_intr++;
 				spin_unlock(&chip->reg_lock);
 				snd_pcm_period_elapsed(azx_dev->substream);
 				spin_lock(&chip->reg_lock);
-				azx_dev->period_updating = 0;
 			}
 		}
 	}
 -793,7
+818,7  static irqreturn_t azx_interrupt(int irq
 	/* clear rirb int */
 	status = azx_readb(chip, RIRBSTS);
 	if (status & RIRB_INT_MASK) {
-		if (status & RIRB_INT_RESPONSE)
+		if (! chip->single_cmd && (status &
RIRB_INT_RESPONSE))
 			azx_update_rirb(chip);
 		azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
 	}
 -918,7
+943,7  static int __devinit azx_codec_create(az
 
 	codecs = 0;
 	for (c = 0; c < AZX_MAX_CODECS; c++) {
-		if (chip->codec_mask & (1 << c)) {
+		if ((chip->codec_mask & (1 << c)) &
probe_mask) {
 			err = snd_hda_codec_new(chip->bus, c, NULL);
 			if (err < 0)
 				continue;
 -1096,7
+1121,6  static int azx_pcm_prepare(snd_pcm_subst
 		azx_dev->fifo_size = azx_sd_readw(azx_dev,
SD_FIFOSIZE) + 1;
 	else
 		azx_dev->fifo_size = 0;
-	azx_dev->last_pos = 0;
 
 	return hinfo->ops.prepare(hinfo, apcm->codec,
azx_dev->stream_tag,
 				  azx_dev->format_val, substream);
 -1144,39
+1168,24  static snd_pcm_uframes_t azx_pcm_pointer
 	azx_dev_t *azx_dev = get_azx_dev(substream);
 	unsigned int pos;
 
-	if (chip->position_fix == POS_FIX_POSBUF) {
+	if (chip->position_fix == POS_FIX_POSBUF ||
+	    chip->position_fix == POS_FIX_AUTO) {
 		/* use the position buffer */
 		pos = *azx_dev->posbuf;
+		if (chip->position_fix == POS_FIX_AUTO &&
+		    azx_dev->period_intr == 1 && ! pos) {
+			printk(KERN_WARNING
+			       "hda-intel: Invalid position buffer,
"
+			       "using LPIB read method
instead.\n");
+			chip->position_fix = POS_FIX_NONE;
+			goto read_lpib;
+		}
 	} else {
+	read_lpib:
 		/* read LPIB */
 		pos = azx_sd_readl(azx_dev, SD_LPIB);
 		if (chip->position_fix == POS_FIX_FIFO)
 			pos += azx_dev->fifo_size;
-#if 0 /* disabled temprarily, auto-correction doesn't work
well... */
-		else if (chip->position_fix == POS_FIX_AUTO &&
azx_dev->period_updating) {
-			/* check the validity of DMA position */
-			unsigned int diff = 0;
-			azx_dev->last_pos += azx_dev->fragsize;
-			if (azx_dev->last_pos > pos)
-				diff = azx_dev->last_pos - pos;
-			if (azx_dev->last_pos >= azx_dev->bufsize) {
-				if (pos < azx_dev->fragsize)
-					diff = 0;
-				azx_dev->last_pos = 0;
-			}
-			if (diff > 0 && diff <=
azx_dev->fifo_size)
-				pos += azx_dev->fifo_size;
-			else {
-				snd_printdd(KERN_INFO "hda_intel: DMA position
fix %d, switching to posbuf\n", diff);
-				chip->position_fix = POS_FIX_POSBUF;
-				pos = *azx_dev->posbuf;
-			}
-			azx_dev->period_updating = 0;
-		}
-#else
-		else if (chip->position_fix == POS_FIX_AUTO)
-			pos += azx_dev->fifo_size;
-#endif
 	}
 	if (pos >= azx_dev->bufsize)
 		pos = 0;
 -1328,28
+1337,33  static int __devinit azx_init_stream(azx
 /*
  * power management
  */
-static int azx_suspend(snd_card_t *card, pm_message_t
state)
+static int azx_suspend(struct pci_dev *pci, pm_message_t
state)
 {
-	azx_t *chip = card->pm_private_data;
+	snd_card_t *card = pci_get_drvdata(pci);
+	azx_t *chip = card->private_data;
 	int i;
 
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
 	for (i = 0; i < chip->pcm_devs; i++)
-		if (chip->pcm[i])
-			snd_pcm_suspend_all(chip->pcm[i]);
+		snd_pcm_suspend_all(chip->pcm[i]);
 	snd_hda_suspend(chip->bus, state);
 	azx_free_cmd_io(chip);
-	pci_disable_device(chip->pci);
+	pci_disable_device(pci);
+	pci_save_state(pci);
 	return 0;
 }
 
-static int azx_resume(snd_card_t *card)
+static int azx_resume(struct pci_dev *pci)
 {
-	azx_t *chip = card->pm_private_data;
+	snd_card_t *card = pci_get_drvdata(pci);
+	azx_t *chip = card->private_data;
 
-	pci_enable_device(chip->pci);
-	pci_set_master(chip->pci);
+	pci_restore_state(pci);
+	pci_enable_device(pci);
+	pci_set_master(pci);
 	azx_init_chip(chip);
 	snd_hda_resume(chip->bus);
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
 	return 0;
 }
 #endif /* CONFIG_PM */
 -1409,7
+1423,7  static int azx_dev_free(snd_device_t *de
  * constructor
  */
 static int __devinit azx_create(snd_card_t *card, struct
pci_dev *pci,
-				int posfix, int driver_type,
+				int driver_type,
 				azx_t **rchip)
 {
 	azx_t *chip;
 -1438,7
+1452,8  static int __devinit azx_create(snd_card
 	chip->irq = -1;
 	chip->driver_type = driver_type;
 
-	chip->position_fix = posfix;
+	chip->position_fix = position_fix;
+	chip->single_cmd = single_cmd;
 
 #if BITS_PER_LONG != 64
 	/* Fix up base address on ULI M5461 */
 -1509,8
+1524,9  static int __devinit azx_create(snd_card
 		goto errout;
 	}
 	/* allocate CORB/RIRB */
-	if ((err = azx_alloc_cmd_io(chip)) < 0)
-		goto errout;
+	if (! chip->single_cmd)
+		if ((err = azx_alloc_cmd_io(chip)) < 0)
+			goto errout;
 
 	/* initialize streams */
 	azx_init_stream(chip);
 -1556,11
+1572,12  static int __devinit azx_probe(struct pc
 		return -ENOMEM;
 	}
 
-	if ((err = azx_create(card, pci, position_fix,
pci_id->driver_data,
+	if ((err = azx_create(card, pci, pci_id->driver_data,
 			      &chip)) < 0) {
 		snd_card_free(card);
 		return err;
 	}
+	card->private_data = chip;
 
 	/* create codec instances */
 	if ((err = azx_codec_create(chip, model)) < 0) {
 -1580,7
+1597,6  static int __devinit azx_probe(struct pc
 		return err;
 	}
 
-	snd_card_set_pm_callback(card, azx_suspend, azx_resume,
chip);
 	snd_card_set_dev(card, &pci->dev);
 
 	if ((err = snd_card_register(card)) < 0) {
 -1621,7
+1637,10  static struct pci_driver driver = {
 	.id_table = azx_ids,
 	.probe = azx_probe,
 	.remove = __devexit_p(azx_remove),
-	SND_PCI_PM_CALLBACKS
+#ifdef CONFIG_PM
+	.suspend = azx_suspend,
+	.resume = azx_resume,
+#endif
 };
 
 static int __init alsa_card_azx_init(void)
diff --git a/sound/pci/hda/hda_local.h
b/sound/pci/hda/hda_local.h
index f51a56f..5597cd4 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
 -66,6
+66,9  int snd_hda_mixer_amp_volume_put(snd_kco
 int snd_hda_mixer_amp_switch_info(snd_kcontrol_t *kcontrol,
snd_ctl_elem_info_t *uinfo);
 int snd_hda_mixer_amp_switch_get(snd_kcontrol_t *kcontrol,
snd_ctl_elem_value_t *ucontrol);
 int snd_hda_mixer_amp_switch_put(snd_kcontrol_t *kcontrol,
snd_ctl_elem_value_t *ucontrol);
+/* lowlevel accessor with caching; use carefully */
+int snd_hda_codec_amp_read(struct hda_codec *codec,
hda_nid_t nid, int ch, int direction, int index);
+int snd_hda_codec_amp_update(struct hda_codec *codec,
hda_nid_t nid, int ch, int direction, int idx, int mask, int
val);
 
 /* mono switch binding multiple inputs */
 #define HDA_BIND_MUTE_MONO(xname, nid, channel, indices,
direction) \
 -87,7
+90,7  int snd_hda_create_spdif_in_ctls(struct 
 /*
  * input MUX helper
  */
-#define HDA_MAX_NUM_INPUTS	8
+#define HDA_MAX_NUM_INPUTS	16
 struct hda_input_mux_item {
 	const char *label;
 	unsigned int index;
 -102,6
+105,23  int snd_hda_input_mux_put(struct hda_cod
 			  snd_ctl_elem_value_t *ucontrol, hda_nid_t nid,
 			  unsigned int *cur_val);
 
+ /*
+ * Channel mode helper
+ */
+struct hda_channel_mode {
+	int channels;
+	const struct hda_verb *sequence;
+};
+
+int snd_hda_ch_mode_info(struct hda_codec *codec,
snd_ctl_elem_info_t *uinfo,
+			 const struct hda_channel_mode *chmode, int
num_chmodes);
+int snd_hda_ch_mode_get(struct hda_codec *codec,
snd_ctl_elem_value_t *ucontrol,
+			const struct hda_channel_mode *chmode, int num_chmodes,
+			int max_channels);
+int snd_hda_ch_mode_put(struct hda_codec *codec,
snd_ctl_elem_value_t *ucontrol,
+			const struct hda_channel_mode *chmode, int num_chmodes,
+			int *max_channelsp);
+
 /*
  * Multi-channel / digital-out PCM helper
  */
 -194,9
+214,12  enum ;
 
+extern const char *auto_pin_cfg_labels[AUTO_PIN_LAST];
+
 struct auto_pin_cfg {
 	int line_outs;
-	hda_nid_t line_out_pins[4]; /* sorted in the order of
Front/Surr/CLFE/Side */
+	hda_nid_t line_out_pins[5]; /* sorted in the order of
Front/Surr/CLFE/Side */
+	hda_nid_t speaker_pin;
 	hda_nid_t hp_pin;
 	hda_nid_t input_pins[AUTO_PIN_LAST];
 	hda_nid_t dig_out_pin;
 -209,6
+232,33  struct auto_pin_cfg {
 #define get_defcfg_sequence(cfg) (cfg &
AC_DEFCFG_SEQUENCE)
 #define get_defcfg_device(cfg) ((cfg &
AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT)
 
-int snd_hda_parse_pin_def_config(struct hda_codec *codec,
struct auto_pin_cfg *cfg);
+int snd_hda_parse_pin_def_config(struct hda_codec *codec,
struct auto_pin_cfg *cfg,
+				 hda_nid_t *ignore_nids);
+
+/* amp values */
+#define AMP_IN_MUTE(idx)	(0x7080 | ((idx)<<8))
+#define AMP_IN_UNMUTE(idx)	(0x7000 | ((idx)<<8))
+#define AMP_OUT_MUTE	0xb080
+#define AMP_OUT_UNMUTE	0xb000
+#define AMP_OUT_ZERO	0xb000
+/* pinctl values */
+#define PIN_IN		0x20
+#define PIN_VREF80	0x24
+#define PIN_VREF50	0x21
+#define PIN_OUT		0x40
+#define PIN_HP		0xc0
+#define PIN_HP_AMP	0x80
+
+/*
+ * get widget capabilities
+ */
+static inline u32 get_wcaps(struct hda_codec *codec,
hda_nid_t nid)
+{
+	if (nid < codec->start_nid ||
+	    nid >= codec->start_nid + codec->num_nodes)
+		return snd_hda_param_read(codec, nid,
AC_PAR_AUDIO_WIDGET_CAP);
+	return codec->wcaps[nid - codec->start_nid];
+}
+
 
 #endif /* __SOUND_HDA_LOCAL_H */
diff --git a/sound/pci/hda/hda_proc.c
b/sound/pci/hda/hda_proc.c
index 39ddf1c..d5693e6 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
 -26,6
+26,7 
 #include <linux/pci.h>
 #include <sound/core.h>
 #include "hda_codec.h"
+#include "hda_local.h"
 
 static const char *get_wid_type_name(unsigned int
wid_value)
 {
-- 
1.1.3

-- 
Daniel T. Chen            crimsunubuntu.com
GPG key:   www.sh.nu/~crimsun/pubkey.gpg.asc
-- 
kernel-team mailing list
kernel-teamlists.ubuntu.com
https://lists.ubuntu.com/mailman/listinfo/kernel-team
[1]

about | contact  Other archives ( Real Estate discussion Medical topics )