ALSA: hda - Use regmap for parameter caches, too

The amp hash table was used for recording the cached reads of some
capability values like pin caps or amp caps.  Now all these are moved
to regmap as well.

One addition to the regmap helper is codec->caps_overwriting flag.
This is set in snd_hdac_override_parm(), and the regmap helper accepts
any register while this flag is set, so that it can overwrite even the
read-only verb like AC_VERB_PARAMETERS.  The flag is cleared
immediately in snd_hdac_override_parm(), as it's a once-off flag.

Along with these changes, the no longer needed amp hash and relevant
fields are removed from hda_codec struct now.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2015-02-26 08:54:56 +01:00
parent eeecd9d10d
commit faa75f8a2e
6 changed files with 82 additions and 141 deletions

View file

@ -76,6 +76,7 @@ struct hdac_device {
/* regmap */
struct regmap *regmap;
bool lazy_cache:1; /* don't wake up for writes */
bool caps_overwriting:1; /* caps overwrite being in process */
};
/* device/driver type used for matching */
@ -109,6 +110,8 @@ int _snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm,
unsigned int *res);
int snd_hdac_read_parm_uncached(struct hdac_device *codec, hda_nid_t nid,
int parm);
int snd_hdac_override_parm(struct hdac_device *codec, hda_nid_t nid,
unsigned int parm, unsigned int val);
int snd_hdac_get_connections(struct hdac_device *codec, hda_nid_t nid,
hda_nid_t *conn_list, int max_conns);
int snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid,

View file

@ -272,6 +272,29 @@ int snd_hdac_read_parm_uncached(struct hdac_device *codec, hda_nid_t nid,
}
EXPORT_SYMBOL_GPL(snd_hdac_read_parm_uncached);
/**
* snd_hdac_override_parm - override read-only parameters
* @codec: the codec object
* @nid: NID for the parameter
* @parm: the parameter to change
* @val: the parameter value to overwrite
*/
int snd_hdac_override_parm(struct hdac_device *codec, hda_nid_t nid,
unsigned int parm, unsigned int val)
{
unsigned int verb = (AC_VERB_PARAMETERS << 8) | (nid << 20) | parm;
int err;
if (!codec->regmap)
return -EINVAL;
codec->caps_overwriting = true;
err = snd_hdac_regmap_write_raw(codec, verb, val);
codec->caps_overwriting = false;
return err;
}
EXPORT_SYMBOL_GPL(snd_hdac_override_parm);
/**
* snd_hdac_get_sub_nodes - get start NID and number of subtree nodes
* @codec: the codec object

View file

@ -58,8 +58,12 @@ static bool hda_volatile_reg(struct device *dev, unsigned int reg)
static bool hda_writeable_reg(struct device *dev, unsigned int reg)
{
struct hdac_device *codec = dev_to_hdac_dev(dev);
unsigned int verb = get_verb(reg);
if (codec->caps_overwriting)
return true;
switch (verb & 0xf00) {
case AC_VERB_GET_STREAM_FORMAT:
case AC_VERB_GET_AMP_GAIN_MUTE:
@ -97,8 +101,12 @@ static bool hda_writeable_reg(struct device *dev, unsigned int reg)
static bool hda_readable_reg(struct device *dev, unsigned int reg)
{
struct hdac_device *codec = dev_to_hdac_dev(dev);
unsigned int verb = get_verb(reg);
if (codec->caps_overwriting)
return true;
switch (verb) {
case AC_VERB_PARAMETERS:
case AC_VERB_GET_CONNECT_LIST:

View file

@ -929,9 +929,7 @@ void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec)
codec->proc_widget_hook = NULL;
codec->spec = NULL;
free_hda_cache(&codec->amp_cache);
free_hda_cache(&codec->cmd_cache);
init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
/* free only driver_pins so that init_pins + user_pins are restored */
@ -996,7 +994,6 @@ static void snd_hda_codec_dev_release(struct device *dev)
free_init_pincfgs(codec);
snd_hdac_device_exit(&codec->core);
snd_hda_sysfs_clear(codec);
free_hda_cache(&codec->amp_cache);
free_hda_cache(&codec->cmd_cache);
kfree(codec->modelname);
kfree(codec->wcaps);
@ -1051,7 +1048,6 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card,
mutex_init(&codec->spdif_mutex);
mutex_init(&codec->control_mutex);
mutex_init(&codec->hash_mutex);
init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32);
snd_array_init(&codec->nids, sizeof(struct hda_nid_item), 32);
@ -1380,67 +1376,6 @@ static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache,
return info;
}
/* query and allocate an amp hash entry */
static inline struct hda_amp_info *
get_alloc_amp_hash(struct hda_codec *codec, u32 key)
{
return (struct hda_amp_info *)get_alloc_hash(&codec->amp_cache, key);
}
/* overwrite the value with the key in the caps hash */
static int write_caps_hash(struct hda_codec *codec, u32 key, unsigned int val)
{
struct hda_amp_info *info;
mutex_lock(&codec->hash_mutex);
info = get_alloc_amp_hash(codec, key);
if (!info) {
mutex_unlock(&codec->hash_mutex);
return -EINVAL;
}
info->amp_caps = val;
info->head.val |= INFO_AMP_CAPS;
mutex_unlock(&codec->hash_mutex);
return 0;
}
/* query the value from the caps hash; if not found, fetch the current
* value from the given function and store in the hash
*/
static unsigned int
query_caps_hash(struct hda_codec *codec, hda_nid_t nid, int dir, u32 key,
unsigned int (*func)(struct hda_codec *, hda_nid_t, int))
{
struct hda_amp_info *info;
unsigned int val;
mutex_lock(&codec->hash_mutex);
info = get_alloc_amp_hash(codec, key);
if (!info) {
mutex_unlock(&codec->hash_mutex);
return 0;
}
if (!(info->head.val & INFO_AMP_CAPS)) {
mutex_unlock(&codec->hash_mutex); /* for reentrance */
val = func(codec, nid, dir);
write_caps_hash(codec, key, val);
} else {
val = info->amp_caps;
mutex_unlock(&codec->hash_mutex);
}
return val;
}
static unsigned int read_amp_cap(struct hda_codec *codec, hda_nid_t nid,
int direction)
{
if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD))
nid = codec->core.afg;
return snd_hda_param_read(codec, nid,
direction == HDA_OUTPUT ?
AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
}
/**
* query_amp_caps - query AMP capabilities
* @codec: the HD-auio codec
@ -1455,9 +1390,11 @@ static unsigned int read_amp_cap(struct hda_codec *codec, hda_nid_t nid,
*/
u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
{
return query_caps_hash(codec, nid, direction,
HDA_HASH_KEY(nid, direction, 0),
read_amp_cap);
if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD))
nid = codec->core.afg;
return snd_hda_param_read(codec, nid,
direction == HDA_OUTPUT ?
AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
}
EXPORT_SYMBOL_GPL(query_amp_caps);
@ -1498,51 +1435,15 @@ EXPORT_SYMBOL_GPL(snd_hda_check_amp_caps);
int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int caps)
{
return write_caps_hash(codec, HDA_HASH_KEY(nid, dir, 0), caps);
unsigned int parm;
snd_hda_override_wcaps(codec, nid,
get_wcaps(codec, nid) | AC_WCAP_AMP_OVRD);
parm = dir == HDA_OUTPUT ? AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP;
return snd_hdac_override_parm(&codec->core, nid, parm, caps);
}
EXPORT_SYMBOL_GPL(snd_hda_override_amp_caps);
static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid,
int dir)
{
return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
}
/**
* snd_hda_query_pin_caps - Query PIN capabilities
* @codec: the HD-auio codec
* @nid: the NID to query
*
* Query PIN capabilities for the given widget.
* Returns the obtained capability bits.
*
* When cap bits have been already read, this doesn't read again but
* returns the cached value.
*/
u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
{
return query_caps_hash(codec, nid, 0, HDA_HASH_PINCAP_KEY(nid),
read_pin_cap);
}
EXPORT_SYMBOL_GPL(snd_hda_query_pin_caps);
/**
* snd_hda_override_pin_caps - Override the pin capabilities
* @codec: the CODEC
* @nid: the NID to override
* @caps: the capability bits to set
*
* Override the cached PIN capabilitiy bits value by the given one.
*
* Returns zero if successful or a negative error code.
*/
int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid,
unsigned int caps)
{
return write_caps_hash(codec, HDA_HASH_PINCAP_KEY(nid), caps);
}
EXPORT_SYMBOL_GPL(snd_hda_override_pin_caps);
/**
* snd_hda_codec_amp_stereo - update the AMP stereo values
* @codec: HD-audio codec
@ -3462,11 +3363,6 @@ static void hda_mark_cmd_cache_dirty(struct hda_codec *codec)
cmd = snd_array_elem(&codec->cmd_cache.buf, i);
cmd->dirty = 1;
}
for (i = 0; i < codec->amp_cache.buf.used; i++) {
struct hda_amp_info *amp;
amp = snd_array_elem(&codec->amp_cache.buf, i);
amp->head.dirty = 1;
}
}
/*
@ -3714,8 +3610,7 @@ unsigned int snd_hda_calc_stream_format(struct hda_codec *codec,
}
EXPORT_SYMBOL_GPL(snd_hda_calc_stream_format);
static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid,
int dir)
static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid)
{
unsigned int val = 0;
if (nid != codec->core.afg &&
@ -3728,14 +3623,7 @@ static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid,
return val;
}
static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid)
{
return query_caps_hash(codec, nid, 0, HDA_HASH_PARPCM_KEY(nid),
get_pcm_param);
}
static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid,
int dir)
static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid)
{
unsigned int streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
if (!streams || streams == -1)
@ -3745,12 +3633,6 @@ static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid,
return streams;
}
static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid)
{
return query_caps_hash(codec, nid, 0, HDA_HASH_PARSTR_KEY(nid),
get_stream_param);
}
/**
* snd_hda_query_supported_pcm - query the supported PCM rates and formats
* @codec: the HDA codec

View file

@ -163,12 +163,6 @@ struct hda_cache_head {
u16 next;
};
struct hda_amp_info {
struct hda_cache_head head;
u32 amp_caps; /* amp capabilities */
u16 vol[2]; /* current volume & mute */
};
struct hda_cache_rec {
u16 hash[64]; /* hash table for index */
struct snd_array buf; /* record entries */
@ -257,7 +251,6 @@ struct hda_codec {
struct snd_array mixers; /* list of assigned mixer elements */
struct snd_array nids; /* list of mapped mixer elements */
struct hda_cache_rec amp_cache; /* cache for amp access */
struct hda_cache_rec cmd_cache; /* cache for other commands */
struct list_head conn_list; /* linked-list of connection-list */

View file

@ -557,9 +557,41 @@ static inline void snd_hda_override_wcaps(struct hda_codec *codec,
u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction);
int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int caps);
u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid);
int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid,
unsigned int caps);
/**
* snd_hda_query_pin_caps - Query PIN capabilities
* @codec: the HD-auio codec
* @nid: the NID to query
*
* Query PIN capabilities for the given widget.
* Returns the obtained capability bits.
*
* When cap bits have been already read, this doesn't read again but
* returns the cached value.
*/
static inline u32
snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
{
return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
}
/**
* snd_hda_override_pin_caps - Override the pin capabilities
* @codec: the CODEC
* @nid: the NID to override
* @caps: the capability bits to set
*
* Override the cached PIN capabilitiy bits value by the given one.
*
* Returns zero if successful or a negative error code.
*/
static inline int
snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid,
unsigned int caps)
{
return snd_hdac_override_parm(&codec->core, nid, AC_PAR_PIN_CAP, caps);
}
bool snd_hda_check_amp_caps(struct hda_codec *codec, hda_nid_t nid,
int dir, unsigned int bits);