Commit eeecd9d1 authored by Takashi Iwai's avatar Takashi Iwai

ALSA: hda - Use regmap for amp accesses

This patch converts the amp access functions to the regmap helpers.
The amp values were formerly cached in the own hash table.  Now it's
dropped by the regmap's cache.

The only tricky conversion is snd_hda_codec_amp_init().  This function
shouldn't do anything if the amp was already initialized.  For
achieving this behavior, a value is read once at first temporarily in
the cache-only mode.  Only if it returns an error,  i.e. the item
still doesn't exist in the cache, it proceeds to the update.
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 9ba17b4d
......@@ -1543,139 +1543,6 @@ int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid,
}
EXPORT_SYMBOL_GPL(snd_hda_override_pin_caps);
/* read or sync the hash value with the current value;
* call within hash_mutex
*/
static struct hda_amp_info *
update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch,
int direction, int index, bool init_only)
{
struct hda_amp_info *info;
unsigned int parm, val = 0;
bool val_read = false;
retry:
info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index));
if (!info)
return NULL;
if (!(info->head.val & INFO_AMP_VOL(ch))) {
if (!val_read) {
mutex_unlock(&codec->hash_mutex);
parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT;
parm |= direction == HDA_OUTPUT ?
AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
parm |= index;
val = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_AMP_GAIN_MUTE, parm);
val &= 0xff;
val_read = true;
mutex_lock(&codec->hash_mutex);
goto retry;
}
info->vol[ch] = val;
info->head.val |= INFO_AMP_VOL(ch);
} else if (init_only)
return NULL;
return info;
}
/*
* write the current volume in info to the h/w
*/
static void put_vol_mute(struct hda_codec *codec, unsigned int amp_caps,
hda_nid_t nid, int ch, int direction, int index,
int val)
{
u32 parm;
parm = ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT;
parm |= direction == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT;
parm |= index << AC_AMP_SET_INDEX_SHIFT;
if ((val & HDA_AMP_MUTE) && !(amp_caps & AC_AMPCAP_MUTE) &&
(amp_caps & AC_AMPCAP_MIN_MUTE))
; /* set the zero value as a fake mute */
else
parm |= val;
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, parm);
}
/**
* snd_hda_codec_amp_read - Read AMP value
* @codec: HD-audio codec
* @nid: NID to read the AMP value
* @ch: channel (left=0 or right=1)
* @direction: #HDA_INPUT or #HDA_OUTPUT
* @index: the index value (only for input direction)
*
* Read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit.
*/
int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
int direction, int index)
{
struct hda_amp_info *info;
unsigned int val = 0;
mutex_lock(&codec->hash_mutex);
info = update_amp_hash(codec, nid, ch, direction, index, false);
if (info)
val = info->vol[ch];
mutex_unlock(&codec->hash_mutex);
return val;
}
EXPORT_SYMBOL_GPL(snd_hda_codec_amp_read);
static int codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
int direction, int idx, int mask, int val,
bool init_only, bool cache_only)
{
struct hda_amp_info *info;
unsigned int caps;
if (snd_BUG_ON(mask & ~0xff))
mask &= 0xff;
val &= mask;
mutex_lock(&codec->hash_mutex);
info = update_amp_hash(codec, nid, ch, direction, idx, init_only);
if (!info) {
mutex_unlock(&codec->hash_mutex);
return 0;
}
val |= info->vol[ch] & ~mask;
if (info->vol[ch] == val) {
mutex_unlock(&codec->hash_mutex);
return 0;
}
info->vol[ch] = val;
info->head.dirty |= cache_only;
caps = info->amp_caps;
mutex_unlock(&codec->hash_mutex);
if (!cache_only)
put_vol_mute(codec, caps, nid, ch, direction, idx, val);
return 1;
}
/**
* snd_hda_codec_amp_update - update the AMP value
* @codec: HD-audio codec
* @nid: NID to read the AMP value
* @ch: channel (left=0 or right=1)
* @direction: #HDA_INPUT or #HDA_OUTPUT
* @idx: the index value (only for input direction)
* @mask: bit mask to set
* @val: the bits value to set
*
* Update the AMP value with a bit mask.
* Returns 0 if the value is unchanged, 1 if changed.
*/
int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
int direction, int idx, int mask, int val)
{
return codec_amp_update(codec, nid, ch, direction, idx, mask, val,
false, codec->cached_write);
}
EXPORT_SYMBOL_GPL(snd_hda_codec_amp_update);
/**
* snd_hda_codec_amp_stereo - update the AMP stereo values
* @codec: HD-audio codec
......@@ -1719,8 +1586,16 @@ EXPORT_SYMBOL_GPL(snd_hda_codec_amp_stereo);
int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch,
int dir, int idx, int mask, int val)
{
return codec_amp_update(codec, nid, ch, dir, idx, mask, val, true,
codec->cached_write);
int orig;
if (!codec->core.regmap)
return -EINVAL;
regcache_cache_only(codec->core.regmap, true);
orig = snd_hda_codec_amp_read(codec, nid, ch, dir, idx);
regcache_cache_only(codec->core.regmap, false);
if (orig >= 0)
return 0;
return snd_hda_codec_amp_update(codec, nid, ch, dir, idx, mask, val);
}
EXPORT_SYMBOL_GPL(snd_hda_codec_amp_init);
......@@ -1749,49 +1624,6 @@ int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid,
}
EXPORT_SYMBOL_GPL(snd_hda_codec_amp_init_stereo);
/**
* snd_hda_codec_resume_amp - Resume all AMP commands from the cache
* @codec: HD-audio codec
*
* Resume the all amp commands from the cache.
*/
void snd_hda_codec_resume_amp(struct hda_codec *codec)
{
int i;
mutex_lock(&codec->hash_mutex);
codec->cached_write = 0;
for (i = 0; i < codec->amp_cache.buf.used; i++) {
struct hda_amp_info *buffer;
u32 key;
hda_nid_t nid;
unsigned int idx, dir, ch;
struct hda_amp_info info;
buffer = snd_array_elem(&codec->amp_cache.buf, i);
if (!buffer->head.dirty)
continue;
buffer->head.dirty = 0;
info = *buffer;
key = info.head.key;
if (!key)
continue;
nid = key & 0xff;
idx = (key >> 16) & 0xff;
dir = (key >> 24) & 0xff;
for (ch = 0; ch < 2; ch++) {
if (!(info.head.val & INFO_AMP_VOL(ch)))
continue;
mutex_unlock(&codec->hash_mutex);
put_vol_mute(codec, info.amp_caps, nid, ch, dir, idx,
info.vol[ch]);
mutex_lock(&codec->hash_mutex);
}
}
mutex_unlock(&codec->hash_mutex);
}
EXPORT_SYMBOL_GPL(snd_hda_codec_resume_amp);
static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int ofs)
{
......@@ -1862,8 +1694,8 @@ update_amp_value(struct hda_codec *codec, hda_nid_t nid,
maxval = get_amp_max_value(codec, nid, dir, 0);
if (val > maxval)
val = maxval;
return codec_amp_update(codec, nid, ch, dir, idx, HDA_AMP_VOLMASK, val,
false, !hda_codec_is_power_on(codec));
return snd_hda_codec_amp_update(codec, nid, ch, dir, idx,
HDA_AMP_VOLMASK, val);
}
/**
......@@ -2546,17 +2378,15 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
int change = 0;
if (chs & 1) {
change = codec_amp_update(codec, nid, 0, dir, idx,
HDA_AMP_MUTE,
*valp ? 0 : HDA_AMP_MUTE, false,
!hda_codec_is_power_on(codec));
change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx,
HDA_AMP_MUTE,
*valp ? 0 : HDA_AMP_MUTE);
valp++;
}
if (chs & 2)
change |= codec_amp_update(codec, nid, 1, dir, idx,
HDA_AMP_MUTE,
*valp ? 0 : HDA_AMP_MUTE, false,
!hda_codec_is_power_on(codec));
change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx,
HDA_AMP_MUTE,
*valp ? 0 : HDA_AMP_MUTE);
hda_call_check_power_status(codec, nid);
return change;
}
......@@ -3417,7 +3247,8 @@ EXPORT_SYMBOL_GPL(snd_hda_sequence_write_cache);
*/
void snd_hda_codec_flush_cache(struct hda_codec *codec)
{
snd_hda_codec_resume_amp(codec);
if (codec->core.regmap)
regcache_sync(codec->core.regmap);
snd_hda_codec_resume_cache(codec);
}
EXPORT_SYMBOL_GPL(snd_hda_codec_flush_cache);
......@@ -3645,6 +3476,9 @@ static void hda_call_codec_resume(struct hda_codec *codec)
{
atomic_inc(&codec->core.in_pm);
if (codec->core.regmap)
regcache_mark_dirty(codec->core.regmap);
hda_mark_cmd_cache_dirty(codec);
codec->power_jiffies = jiffies;
......@@ -3658,7 +3492,8 @@ static void hda_call_codec_resume(struct hda_codec *codec)
else {
if (codec->patch_ops.init)
codec->patch_ops.init(codec);
snd_hda_codec_resume_amp(codec);
if (codec->core.regmap)
regcache_sync(codec->core.regmap);
snd_hda_codec_resume_cache(codec);
}
......
......@@ -127,18 +127,16 @@ int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
#endif
/* lowlevel accessor with caching; use carefully */
int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
int direction, int index);
int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
int direction, int idx, int mask, int val);
#define snd_hda_codec_amp_read(codec, nid, ch, dir, idx) \
snd_hdac_regmap_get_amp(&(codec)->core, nid, ch, dir, idx)
#define snd_hda_codec_amp_update(codec, nid, ch, dir, idx, mask, val) \
snd_hdac_regmap_update_amp(&(codec)->core, nid, ch, dir, idx, mask, val)
int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
int dir, int idx, int mask, int val);
int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch,
int direction, int idx, int mask, int val);
int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid,
int dir, int idx, int mask, int val);
void snd_hda_codec_resume_amp(struct hda_codec *codec);
void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int *tlv);
struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
......
......@@ -2211,7 +2211,7 @@ static int generic_hdmi_resume(struct hda_codec *codec)
int pin_idx;
codec->patch_ops.init(codec);
snd_hda_codec_resume_amp(codec);
regcache_sync(codec->core.regmap);
snd_hda_codec_resume_cache(codec);
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
......
......@@ -799,7 +799,7 @@ static int alc_resume(struct hda_codec *codec)
if (!spec->no_depop_delay)
msleep(150); /* to avoid pop noise */
codec->patch_ops.init(codec);
snd_hda_codec_resume_amp(codec);
regcache_sync(codec->core.regmap);
snd_hda_codec_resume_cache(codec);
hda_call_check_power_status(codec, 0x01);
return 0;
......@@ -3058,7 +3058,7 @@ static int alc269_resume(struct hda_codec *codec)
msleep(200);
}
snd_hda_codec_resume_amp(codec);
regcache_sync(codec->core.regmap);
snd_hda_codec_resume_cache(codec);
hda_call_check_power_status(codec, 0x01);
......
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