|
@@ -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);
|
|
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
|
|
* snd_hda_codec_amp_stereo - update the AMP stereo values
|
|
* @codec: HD-audio codec
|
|
* @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 snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch,
|
|
int dir, int idx, int mask, int val)
|
|
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);
|
|
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);
|
|
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,
|
|
static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir,
|
|
unsigned int ofs)
|
|
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);
|
|
maxval = get_amp_max_value(codec, nid, dir, 0);
|
|
if (val > maxval)
|
|
if (val > maxval)
|
|
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;
|
|
int change = 0;
|
|
|
|
|
|
if (chs & 1) {
|
|
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++;
|
|
valp++;
|
|
}
|
|
}
|
|
if (chs & 2)
|
|
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);
|
|
hda_call_check_power_status(codec, nid);
|
|
return change;
|
|
return change;
|
|
}
|
|
}
|
|
@@ -3417,7 +3247,8 @@ EXPORT_SYMBOL_GPL(snd_hda_sequence_write_cache);
|
|
*/
|
|
*/
|
|
void snd_hda_codec_flush_cache(struct hda_codec *codec)
|
|
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);
|
|
snd_hda_codec_resume_cache(codec);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(snd_hda_codec_flush_cache);
|
|
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);
|
|
atomic_inc(&codec->core.in_pm);
|
|
|
|
|
|
|
|
+ if (codec->core.regmap)
|
|
|
|
+ regcache_mark_dirty(codec->core.regmap);
|
|
|
|
+
|
|
hda_mark_cmd_cache_dirty(codec);
|
|
hda_mark_cmd_cache_dirty(codec);
|
|
|
|
|
|
codec->power_jiffies = jiffies;
|
|
codec->power_jiffies = jiffies;
|
|
@@ -3658,7 +3492,8 @@ static void hda_call_codec_resume(struct hda_codec *codec)
|
|
else {
|
|
else {
|
|
if (codec->patch_ops.init)
|
|
if (codec->patch_ops.init)
|
|
codec->patch_ops.init(codec);
|
|
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);
|
|
snd_hda_codec_resume_cache(codec);
|
|
}
|
|
}
|
|
|
|
|