[alsa-devel] [RFC PATCH 1/4] ALSA: hda - codec add DP MST support for connection list
libin.yang at linux.intel.com
libin.yang at linux.intel.com
Mon Mar 7 15:57:43 CET 2016
From: Libin Yang <libin.yang at linux.intel.com>
This patches adds the support of connection list for DP MST.
With this, hdmi driver in DP MST mode can easily reuse
the connection list mechanism.
Signed-off-by: Libin Yang <libin.yang at linux.intel.com>
---
sound/pci/hda/hda_codec.c | 134 ++++++++++++++++++++++++++++++++++++++++++----
sound/pci/hda/hda_codec.h | 5 ++
2 files changed, 128 insertions(+), 11 deletions(-)
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 8374188..d4c81f7 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -110,23 +110,24 @@ struct hda_conn_list {
struct list_head list;
int len;
hda_nid_t nid;
+ int dev_id;
hda_nid_t conns[0];
};
/* look up the cached results */
static struct hda_conn_list *
-lookup_conn_list(struct hda_codec *codec, hda_nid_t nid)
+lookup_conn_list(struct hda_codec *codec, hda_nid_t nid, int dev_id)
{
struct hda_conn_list *p;
list_for_each_entry(p, &codec->conn_list, list) {
- if (p->nid == nid)
+ if ((p->nid == nid) && (p->dev_id == dev_id))
return p;
}
return NULL;
}
-static int add_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
- const hda_nid_t *list)
+static int add_conn_list(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id, int len, const hda_nid_t *list)
{
struct hda_conn_list *p;
@@ -135,6 +136,7 @@ static int add_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
return -ENOMEM;
p->len = len;
p->nid = nid;
+ p->dev_id = dev_id;
memcpy(p->conns, list, len * sizeof(hda_nid_t));
list_add(&p->list, &codec->conn_list);
return 0;
@@ -150,8 +152,13 @@ static void remove_conn_list(struct hda_codec *codec)
}
}
-/* read the connection and add to the cache */
-static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid)
+/*
+ * read the connection and add to the cache
+ * the caller should select the device entry by sending the
+ * corresponding verb if necessary before calling this function
+ */
+static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id)
{
hda_nid_t list[32];
hda_nid_t *result = list;
@@ -166,7 +173,8 @@ static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid)
len = snd_hda_get_raw_connections(codec, nid, result, len);
}
if (len >= 0)
- len = snd_hda_override_conn_list(codec, nid, len, result);
+ len = snd_hda_override_conn_list_mst(codec, nid,
+ dev_id, len, result);
if (result != list)
kfree(result);
return len;
@@ -197,7 +205,7 @@ int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
const struct hda_conn_list *p;
/* if the connection-list is already cached, read it */
- p = lookup_conn_list(codec, nid);
+ p = lookup_conn_list(codec, nid, 0);
if (p) {
if (listp)
*listp = p->conns;
@@ -206,7 +214,7 @@ int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
if (snd_BUG_ON(added))
return -EINVAL;
- err = read_and_add_raw_conns(codec, nid);
+ err = read_and_add_raw_conns(codec, nid, 0);
if (err < 0)
return err;
added = true;
@@ -215,6 +223,49 @@ int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
EXPORT_SYMBOL_GPL(snd_hda_get_conn_list);
/**
+ * snd_hda_get_conn_list_mst - get connection list in mst mode
+ * @codec: the HDA codec
+ * @nid: NID to parse
+ * @dev_id: device entry id
+ * @listp: the pointer to store NID list
+ *
+ * Parses the connection list of the given widget and stores the pointer
+ * to the list of NIDs.
+ *
+ * Returns the number of connections, or a negative error code.
+ *
+ * Note that the returned pointer isn't protected against the list
+ * modification. If snd_hda_override_conn_list() might be called
+ * concurrently, protect with a mutex appropriately.
+ */
+int snd_hda_get_conn_list_mst(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id, const hda_nid_t **listp)
+{
+ bool added = false;
+
+ for (;;) {
+ int err;
+ const struct hda_conn_list *p;
+
+ /* if the connection-list is already cached, read it */
+ p = lookup_conn_list(codec, nid, dev_id);
+ if (p) {
+ if (listp)
+ *listp = p->conns;
+ return p->len;
+ }
+ if (snd_BUG_ON(added))
+ return -EINVAL;
+
+ err = read_and_add_raw_conns(codec, nid, dev_id);
+ if (err < 0)
+ return err;
+ added = true;
+ }
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_conn_list_mst);
+
+/**
* snd_hda_get_connections - copy connection list
* @codec: the HDA codec
* @nid: NID to parse
@@ -246,6 +297,39 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
EXPORT_SYMBOL_GPL(snd_hda_get_connections);
/**
+ * snd_hda_get_connections_mst - copy connection list in mst mode
+ * @codec: the HDA codec
+ * @nid: NID to parse
+ * @dev_id: device entry id
+ * @conn_list: connection list array; when NULL, checks only the size
+ * @max_conns: max. number of connections to store
+ *
+ * Parses the connection list of the given widget and stores the list
+ * of NIDs.
+ *
+ * Returns the number of connections, or a negative error code.
+ */
+int snd_hda_get_connections_mst(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id, hda_nid_t *conn_list,
+ int max_conns)
+{
+ const hda_nid_t *list;
+ int len = snd_hda_get_conn_list_mst(codec, nid, dev_id, &list);
+
+ if (len > 0 && conn_list) {
+ if (len > max_conns) {
+ codec_err(codec, "Too many connections %d for NID 0x%x\n",
+ len, nid);
+ return -EINVAL;
+ }
+ memcpy(conn_list, list, len * sizeof(hda_nid_t));
+ }
+
+ return len;
+}
+EXPORT_SYMBOL_GPL(snd_hda_get_connections_mst);
+
+/**
* snd_hda_override_conn_list - add/modify the connection-list to cache
* @codec: the HDA codec
* @nid: NID to parse
@@ -262,17 +346,45 @@ int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
{
struct hda_conn_list *p;
- p = lookup_conn_list(codec, nid);
+ p = lookup_conn_list(codec, nid, 0);
if (p) {
list_del(&p->list);
kfree(p);
}
- return add_conn_list(codec, nid, len, list);
+ return add_conn_list(codec, nid, 0, len, list);
}
EXPORT_SYMBOL_GPL(snd_hda_override_conn_list);
/**
+ * snd_hda_override_conn_list_mst - add/modify the connection-list to cache
+ * @codec: the HDA codec
+ * @nid: NID to parse
+ * @dev_id: device entry id
+ * @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_mst(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id, int len, const hda_nid_t *list)
+{
+ struct hda_conn_list *p;
+
+ p = lookup_conn_list(codec, nid, dev_id);
+ if (p) {
+ list_del(&p->list);
+ kfree(p);
+ }
+
+ return add_conn_list(codec, nid, dev_id, len, list);
+}
+EXPORT_SYMBOL_GPL(snd_hda_override_conn_list_mst);
+
+/**
* snd_hda_get_conn_index - get the connection index of the given NID
* @codec: the HDA codec
* @mux: NID containing the list
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 373fcad..deeed35 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -330,6 +330,9 @@ snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags,
snd_hdac_get_sub_nodes(&(codec)->core, nid, start_nid)
int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
hda_nid_t *conn_list, int max_conns);
+int snd_hda_get_connections_mst(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id, hda_nid_t *conn_list,
+ int max_conns);
static inline int
snd_hda_get_num_conns(struct hda_codec *codec, hda_nid_t nid)
{
@@ -345,6 +348,8 @@ int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
const hda_nid_t **listp);
int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums,
const hda_nid_t *list);
+int snd_hda_override_conn_list_mst(struct hda_codec *codec, hda_nid_t nid,
+ int dev_id, int len, const hda_nid_t *list);
int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
hda_nid_t nid, int recursive);
int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid,
--
1.9.1
More information about the Alsa-devel
mailing list