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: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
@@ -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)
|
|||||||
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)
|
|||||||
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);
|
|
||||||
if (len < 0)
|
|
||||||
return len;
|
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,
|
|||||||
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)
|
|||||||
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;
|
return add_conn_list(codec, nid, len, list);
|
||||||
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:
|
|
||||||
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);
|
|||||||
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)
|
|||||||
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,
|
|||||||
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 {
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user