ALSA: hda - Add snd_hda_override_conn_list() helper function

Add a function to add/modify the connection-list cache entry.
It'll be useful to fix a buggy hardware result.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 7f85023..d0deab1 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -310,10 +310,23 @@
 
 static int _hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
 				hda_nid_t *conn_list, int max_conns);
-static bool add_conn_list(struct snd_array *array, hda_nid_t nid);
+
+/* look up the cached results */
+static hda_nid_t *lookup_conn_list(struct snd_array *array, hda_nid_t nid)
+{
+	int i, len;
+	for (i = 0; i < array->used; ) {
+		hda_nid_t *p = snd_array_elem(array, i);
+		if (nid == *p)
+			return p;
+		len = p[1];
+		i += len + 2;
+	}
+	return NULL;
+}
 
 /**
- * snd_hda_get_connections - get connection list
+ * snd_hda_get_conn_list - get connection list
  * @codec: the HDA codec
  * @nid: NID to parse
  * @listp: the pointer to store NID list
@@ -327,42 +340,31 @@
 			  const hda_nid_t **listp)
 {
 	struct snd_array *array = &codec->conn_lists;
-	int i, len, old_used;
+	int len, err;
 	hda_nid_t list[HDA_MAX_CONNECTIONS];
 	hda_nid_t *p;
+	bool added = false;
 
-	/* look up the cached results */
-	for (i = 0; i < array->used; ) {
-		p = snd_array_elem(array, i);
-		len = p[1];
-		if (nid == *p) {
-			if (listp)
-				*listp = p + 2;
-			return len;
-		}
-		i += len + 2;
+ again:
+	/* if the connection-list is already cached, read it */
+	p = lookup_conn_list(array, nid);
+	if (p) {
+		if (listp)
+			*listp = p + 2;
+		return p[1];
 	}
+	if (snd_BUG_ON(added))
+		return -EINVAL;
 
+	/* read the connection and add to the cache */
 	len = _hda_get_connections(codec, nid, list, HDA_MAX_CONNECTIONS);
 	if (len < 0)
 		return len;
-
-	/* add to the cache */
-	old_used = array->used;
-	if (!add_conn_list(array, nid) || !add_conn_list(array, len))
-		goto error_add;
-	for (i = 0; i < len; i++)
-		if (!add_conn_list(array, list[i]))
-			goto error_add;
-
-	p = snd_array_elem(array, old_used);
-	if (listp)
-		*listp = p + 2;
-	return len;
-		
- error_add:
-	array->used = old_used;
-	return -ENOMEM;
+	err = snd_hda_override_conn_list(codec, nid, len, list);
+	if (err < 0)
+		return err;
+	added = true;
+	goto again;
 }
 EXPORT_SYMBOL_HDA(snd_hda_get_conn_list);
 
@@ -503,6 +505,43 @@
 }
 
 /**
+ * snd_hda_override_conn_list - add/modify the connection-list to cache
+ * @codec: the HDA codec
+ * @nid: NID to parse
+ * @len: number of connection list entries
+ * @list: the list of connection entries
+ *
+ * Add or modify the given connection-list to the cache.  If the corresponding
+ * cache already exists, invalidate it and append a new one.
+ *
+ * Returns zero or a negative error code.
+ */
+int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
+			       const hda_nid_t *list)
+{
+	struct snd_array *array = &codec->conn_lists;
+	hda_nid_t *p;
+	int i, old_used;
+
+	p = lookup_conn_list(array, nid);
+	if (p)
+		*p = -1; /* invalidate the old entry */
+
+	old_used = array->used;
+	if (!add_conn_list(array, nid) || !add_conn_list(array, len))
+		goto error_add;
+	for (i = 0; i < len; i++)
+		if (!add_conn_list(array, list[i]))
+			goto error_add;
+	return 0;
+
+ error_add:
+	array->used = old_used;
+	return -ENOMEM;
+}
+EXPORT_SYMBOL_HDA(snd_hda_override_conn_list);
+
+/**
  * snd_hda_get_conn_index - get the connection index of the given NID
  * @codec: the HDA codec
  * @mux: NID containing the list