Commit ee8e765b authored by Takashi Iwai's avatar Takashi Iwai

ALSA: hda - Revive snd_hda_get_conn_list()

Manage the connection list cache using linked lists instead of
snd_array, and revive snd_hda_get_conn_list() again, so that we don't
have to keep the expanded values locally.
This will reduce the stack usage by recursive call of
snd_hda_get_conn_index() or parse_nid_path() of the generic parser.

The list management doesn't include any mutex protection, thus the
caller needs to take care of race appropriately.
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 9cc159c6
...@@ -334,20 +334,51 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, ...@@ -334,20 +334,51 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid,
} }
EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes); EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes);
/* connection list element */
struct hda_conn_list {
struct list_head list;
int len;
hda_nid_t nid;
hda_nid_t conns[0];
};
/* look up the cached results */ /* look up the cached results */
static hda_nid_t *lookup_conn_list(struct snd_array *array, hda_nid_t nid) static struct hda_conn_list *
lookup_conn_list(struct hda_codec *codec, hda_nid_t nid)
{ {
int i, len; struct hda_conn_list *p;
for (i = 0; i < array->used; ) { list_for_each_entry(p, &codec->conn_list, list) {
hda_nid_t *p = snd_array_elem(array, i); if (p->nid == nid)
if (nid == *p)
return p; return p;
len = p[1];
i += len + 2;
} }
return NULL; return NULL;
} }
static int add_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
const hda_nid_t *list)
{
struct hda_conn_list *p;
p = kmalloc(sizeof(*p) + len * sizeof(hda_nid_t), GFP_KERNEL);
if (!p)
return -ENOMEM;
p->len = len;
p->nid = nid;
memcpy(p->conns, list, len * sizeof(hda_nid_t));
list_add(&p->list, &codec->conn_list);
return 0;
}
static void remove_conn_list(struct hda_codec *codec)
{
while (!list_empty(&codec->conn_list)) {
struct hda_conn_list *p;
p = list_first_entry(&codec->conn_list, typeof(*p), list);
list_del(&p->list);
kfree(p);
}
}
/* read the connection and add to the cache */ /* read the connection and add to the cache */
static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid) static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid)
{ {
...@@ -360,6 +391,49 @@ static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid) ...@@ -360,6 +391,49 @@ static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid)
return snd_hda_override_conn_list(codec, nid, len, list); return snd_hda_override_conn_list(codec, nid, len, list);
} }
/**
* snd_hda_get_conn_list - get connection list
* @codec: the HDA codec
* @nid: NID to parse
* @len: number of connection list entries
* @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(struct hda_codec *codec, hda_nid_t nid,
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);
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);
if (err < 0)
return err;
added = true;
}
}
EXPORT_SYMBOL_HDA(snd_hda_get_conn_list);
/** /**
* snd_hda_get_connections - copy connection list * snd_hda_get_connections - copy connection list
* @codec: the HDA codec * @codec: the HDA codec
...@@ -375,39 +449,20 @@ static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid) ...@@ -375,39 +449,20 @@ static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid)
int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, 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; const hda_nid_t *list;
int len; int len = snd_hda_get_conn_list(codec, nid, &list);
hda_nid_t *p;
bool added = false;
again: if (len > 0 && conn_list) {
mutex_lock(&codec->hash_mutex); if (len > max_conns) {
len = -1;
/* if the connection-list is already cached, read it */
p = lookup_conn_list(array, nid);
if (p) {
len = p[1];
if (conn_list && len > max_conns) {
snd_printk(KERN_ERR "hda_codec: " snd_printk(KERN_ERR "hda_codec: "
"Too many connections %d for NID 0x%x\n", "Too many connections %d for NID 0x%x\n",
len, nid); len, nid);
mutex_unlock(&codec->hash_mutex);
return -EINVAL; return -EINVAL;
} }
if (conn_list && len) memcpy(conn_list, list, len * sizeof(hda_nid_t));
memcpy(conn_list, p + 2, len * sizeof(hda_nid_t));
} }
mutex_unlock(&codec->hash_mutex);
if (len >= 0)
return len;
if (snd_BUG_ON(added))
return -EINVAL;
len = read_and_add_raw_conns(codec, nid); return len;
if (len < 0)
return len;
added = true;
goto again;
} }
EXPORT_SYMBOL_HDA(snd_hda_get_connections); EXPORT_SYMBOL_HDA(snd_hda_get_connections);
...@@ -519,15 +574,6 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, ...@@ -519,15 +574,6 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid,
return conns; return conns;
} }
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;
}
/** /**
* snd_hda_override_conn_list - add/modify the connection-list to cache * snd_hda_override_conn_list - add/modify the connection-list to cache
* @codec: the HDA codec * @codec: the HDA codec
...@@ -543,28 +589,15 @@ static bool add_conn_list(struct snd_array *array, hda_nid_t nid) ...@@ -543,28 +589,15 @@ static bool add_conn_list(struct snd_array *array, hda_nid_t nid)
int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len, int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
const hda_nid_t *list) const hda_nid_t *list)
{ {
struct snd_array *array = &codec->conn_lists; struct hda_conn_list *p;
hda_nid_t *p;
int i, old_used;
mutex_lock(&codec->hash_mutex); p = lookup_conn_list(codec, nid);
p = lookup_conn_list(array, nid); if (p) {
if (p) list_del(&p->list);
*p = -1; /* invalidate the old entry */ kfree(p);
}
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;
mutex_unlock(&codec->hash_mutex);
return 0;
error_add: return add_conn_list(codec, nid, len, list);
array->used = old_used;
mutex_unlock(&codec->hash_mutex);
return -ENOMEM;
} }
EXPORT_SYMBOL_HDA(snd_hda_override_conn_list); EXPORT_SYMBOL_HDA(snd_hda_override_conn_list);
...@@ -582,10 +615,10 @@ EXPORT_SYMBOL_HDA(snd_hda_override_conn_list); ...@@ -582,10 +615,10 @@ EXPORT_SYMBOL_HDA(snd_hda_override_conn_list);
int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
hda_nid_t nid, int recursive) hda_nid_t nid, int recursive)
{ {
hda_nid_t conn[HDA_MAX_NUM_INPUTS]; const hda_nid_t *conn;
int i, nums; int i, nums;
nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); nums = snd_hda_get_conn_list(codec, mux, &conn);
for (i = 0; i < nums; i++) for (i = 0; i < nums; i++)
if (conn[i] == nid) if (conn[i] == nid)
return i; return i;
...@@ -1186,8 +1219,8 @@ static void snd_hda_codec_free(struct hda_codec *codec) ...@@ -1186,8 +1219,8 @@ static void snd_hda_codec_free(struct hda_codec *codec)
snd_array_free(&codec->mixers); snd_array_free(&codec->mixers);
snd_array_free(&codec->nids); snd_array_free(&codec->nids);
snd_array_free(&codec->cvt_setups); snd_array_free(&codec->cvt_setups);
snd_array_free(&codec->conn_lists);
snd_array_free(&codec->spdif_out); snd_array_free(&codec->spdif_out);
remove_conn_list(codec);
codec->bus->caddr_tbl[codec->addr] = NULL; codec->bus->caddr_tbl[codec->addr] = NULL;
if (codec->patch_ops.free) if (codec->patch_ops.free)
codec->patch_ops.free(codec); codec->patch_ops.free(codec);
...@@ -1257,10 +1290,11 @@ int snd_hda_codec_new(struct hda_bus *bus, ...@@ -1257,10 +1290,11 @@ int snd_hda_codec_new(struct hda_bus *bus,
snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16); 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->driver_pins, sizeof(struct hda_pincfg), 16);
snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8); snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8);
snd_array_init(&codec->conn_lists, sizeof(hda_nid_t), 64);
snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16); snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16);
snd_array_init(&codec->jacktbl, sizeof(struct hda_jack_tbl), 16); snd_array_init(&codec->jacktbl, sizeof(struct hda_jack_tbl), 16);
snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8); snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8);
INIT_LIST_HEAD(&codec->conn_list);
INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work); INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work);
#ifdef CONFIG_PM #ifdef CONFIG_PM
......
...@@ -831,7 +831,7 @@ struct hda_codec { ...@@ -831,7 +831,7 @@ struct hda_codec {
struct hda_cache_rec amp_cache; /* cache for amp access */ struct hda_cache_rec amp_cache; /* cache for amp access */
struct hda_cache_rec cmd_cache; /* cache for other commands */ struct hda_cache_rec cmd_cache; /* cache for other commands */
struct snd_array conn_lists; /* connection-list array */ struct list_head conn_list; /* linked-list of connection-list */
struct mutex spdif_mutex; struct mutex spdif_mutex;
struct mutex control_mutex; struct mutex control_mutex;
...@@ -944,6 +944,8 @@ snd_hda_get_num_conns(struct hda_codec *codec, hda_nid_t nid) ...@@ -944,6 +944,8 @@ snd_hda_get_num_conns(struct hda_codec *codec, hda_nid_t nid)
} }
int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, int snd_hda_get_raw_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);
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, int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums,
const hda_nid_t *list); const hda_nid_t *list);
int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
......
...@@ -208,7 +208,7 @@ static bool __parse_nid_path(struct hda_codec *codec, ...@@ -208,7 +208,7 @@ static bool __parse_nid_path(struct hda_codec *codec,
int with_aa_mix, struct nid_path *path, int depth) int with_aa_mix, struct nid_path *path, int depth)
{ {
struct hda_gen_spec *spec = codec->spec; struct hda_gen_spec *spec = codec->spec;
hda_nid_t conn[16]; const hda_nid_t *conn;
int i, nums; int i, nums;
if (to_nid == spec->mixer_nid) { if (to_nid == spec->mixer_nid) {
...@@ -217,7 +217,7 @@ static bool __parse_nid_path(struct hda_codec *codec, ...@@ -217,7 +217,7 @@ static bool __parse_nid_path(struct hda_codec *codec,
with_aa_mix = HDA_PARSE_ALL; /* mark aa-mix is included */ with_aa_mix = HDA_PARSE_ALL; /* mark aa-mix is included */
} }
nums = snd_hda_get_connections(codec, to_nid, conn, ARRAY_SIZE(conn)); nums = snd_hda_get_conn_list(codec, to_nid, &conn);
for (i = 0; i < nums; i++) { for (i = 0; i < nums; i++) {
if (conn[i] != from_nid) { if (conn[i] != from_nid) {
/* special case: when from_nid is 0, /* special case: when from_nid is 0,
...@@ -481,12 +481,12 @@ static void activate_amp_in(struct hda_codec *codec, struct nid_path *path, ...@@ -481,12 +481,12 @@ static void activate_amp_in(struct hda_codec *codec, struct nid_path *path,
int i, bool enable, bool add_aamix) int i, bool enable, bool add_aamix)
{ {
struct hda_gen_spec *spec = codec->spec; struct hda_gen_spec *spec = codec->spec;
hda_nid_t conn[16]; const hda_nid_t *conn;
int n, nums, idx; int n, nums, idx;
int type; int type;
hda_nid_t nid = path->path[i]; hda_nid_t nid = path->path[i];
nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); nums = snd_hda_get_conn_list(codec, nid, &conn);
type = get_wcaps_type(get_wcaps(codec, nid)); type = get_wcaps_type(get_wcaps(codec, nid));
if (type == AC_WID_PIN || if (type == AC_WID_PIN ||
(type == AC_WID_AUD_IN && codec->single_adc_amp)) { (type == AC_WID_AUD_IN && codec->single_adc_amp)) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment