ALSA: hda - Remember connection lists
Takashi Iwai [Thu, 7 Apr 2011 13:55:15 +0000 (15:55 +0200)]
The connection lists are static and we can reuse the previous results
instead of querying via verb at each time.  This will reduce the I/O
in the runtime especially for some codec auto-parsers.

Signed-off-by: Takashi Iwai <tiwai@suse.de>

sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h

index 2c79e96..11ead15 100644 (file)
@@ -307,6 +307,12 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid,
 }
 EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes);
 
+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);
+static int copy_conn_list(hda_nid_t nid, hda_nid_t *dst, int max_dst,
+                         hda_nid_t *src, int len);
+
 /**
  * snd_hda_get_connections - get connection list
  * @codec: the HDA codec
@@ -320,7 +326,44 @@ EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes);
  * Returns the number of connections, or a negative error code.
  */
 int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
-                           hda_nid_t *conn_list, int max_conns)
+                            hda_nid_t *conn_list, int max_conns)
+{
+       struct snd_array *array = &codec->conn_lists;
+       int i, j, len, old_used;
+       hda_nid_t list[HDA_MAX_CONNECTIONS];
+
+       /* look up the cached results */
+       for (i = 0; i < array->used; ) {
+               hda_nid_t *p = snd_array_elem(array, i);
+               len = p[1];
+               if (nid == *p)
+                       return copy_conn_list(nid, conn_list, max_conns,
+                                             p + 2, len);
+               i += len + 2;
+       }
+
+       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;
+
+       return copy_conn_list(nid, conn_list, max_conns, list, len);
+               
+ error_add:
+       array->used = old_used;
+       return -ENOMEM;
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_connections);
+
+static int _hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
+                            hda_nid_t *conn_list, int max_conns)
 {
        unsigned int parm;
        int i, conn_len, conns;
@@ -417,8 +460,28 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
        }
        return conns;
 }
-EXPORT_SYMBOL_HDA(snd_hda_get_connections);
 
+static bool add_conn_list(struct snd_array *array, hda_nid_t nid)
+{
+       hda_nid_t *p = snd_array_new(array);
+       if (!p)
+               return false;
+       *p = nid;
+       return true;
+}
+
+static int copy_conn_list(hda_nid_t nid, hda_nid_t *dst, int max_dst,
+                         hda_nid_t *src, int len)
+{
+       if (len > max_dst) {
+               snd_printk(KERN_ERR "hda_codec: "
+                          "Too many connections %d for NID 0x%x\n",
+                          len, nid);
+               return -EINVAL;
+       }
+       memcpy(dst, src, len * sizeof(hda_nid_t));
+       return len;
+}
 
 /**
  * snd_hda_queue_unsol_event - add an unsolicited event to queue
@@ -1017,6 +1080,7 @@ static void snd_hda_codec_free(struct hda_codec *codec)
        list_del(&codec->list);
        snd_array_free(&codec->mixers);
        snd_array_free(&codec->nids);
+       snd_array_free(&codec->conn_lists);
        codec->bus->caddr_tbl[codec->addr] = NULL;
        if (codec->patch_ops.free)
                codec->patch_ops.free(codec);
@@ -1077,6 +1141,7 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
        snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16);
        snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16);
        snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8);
+       snd_array_init(&codec->conn_lists, sizeof(hda_nid_t), 64);
        if (codec->bus->modelname) {
                codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
                if (!codec->modelname) {
index e46d542..7d57a66 100644 (file)
@@ -825,6 +825,8 @@ struct hda_codec {
        struct hda_cache_rec amp_cache; /* cache for amp access */
        struct hda_cache_rec cmd_cache; /* cache for other commands */
 
+       struct snd_array conn_lists;    /* connection-list array */
+
        struct mutex spdif_mutex;
        struct mutex control_mutex;
        unsigned int spdif_status;      /* IEC958 status bits */