提交 a551d914 编写于 作者: T Takashi Iwai

ALSA: hda - Use regmap for command verb caches, too

Like the previous patches, this patch converts also to the regmap, at
this time, the cached verb writes are the target.  But this conversion
needs a bit more caution than before.

- In the old code, we just record any verbs as is, and restore them at
  resume.  For the regmap scheme, this doesn't work, since a few verbs
  like AMP or DIGI_CONVERT are asymmetrical.  Such verbs are converted
  either to the dedicated function (snd_hda_regmap_xxx_amp()) or
  changed to the unified verb.

- Some verbs have to be declared as vendor-specific ones before
  accessing via regmap.

Also, the minor optimization with codec->cached_write flag is dropped
in a few places, as this would confuse the operation.  Further
optimizations will be brought in the later patches, if any.

This conversion ends up with a drop of significant amount of codes,
mostly the helper codes that are no longer used.
Signed-off-by: NTakashi Iwai <tiwai@suse.de>
上级 5e56bcea
......@@ -807,10 +807,6 @@ static void hda_jackpoll_work(struct work_struct *work)
codec->jackpoll_interval);
}
static void init_hda_cache(struct hda_cache_rec *cache,
unsigned int record_size);
static void free_hda_cache(struct hda_cache_rec *cache);
/* release all pincfg lists */
static void free_init_pincfgs(struct hda_codec *codec)
{
......@@ -929,9 +925,6 @@ void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec)
codec->proc_widget_hook = NULL;
codec->spec = NULL;
free_hda_cache(&codec->cmd_cache);
init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
/* free only driver_pins so that init_pins + user_pins are restored */
snd_array_free(&codec->driver_pins);
snd_array_free(&codec->cvt_setups);
......@@ -994,7 +987,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->cmd_cache);
kfree(codec->modelname);
kfree(codec->wcaps);
kfree(codec);
......@@ -1047,8 +1039,6 @@ int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card,
codec->addr = codec_addr;
mutex_init(&codec->spdif_mutex);
mutex_init(&codec->control_mutex);
mutex_init(&codec->hash_mutex);
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);
snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16);
......@@ -1316,66 +1306,6 @@ static void hda_cleanup_all_streams(struct hda_codec *codec)
* amp access functions
*/
/* FIXME: more better hash key? */
#define HDA_HASH_KEY(nid, dir, idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24))
#define HDA_HASH_PINCAP_KEY(nid) (u32)((nid) + (0x02 << 24))
#define HDA_HASH_PARPCM_KEY(nid) (u32)((nid) + (0x03 << 24))
#define HDA_HASH_PARSTR_KEY(nid) (u32)((nid) + (0x04 << 24))
#define INFO_AMP_CAPS (1<<0)
#define INFO_AMP_VOL(ch) (1 << (1 + (ch)))
/* initialize the hash table */
static void init_hda_cache(struct hda_cache_rec *cache,
unsigned int record_size)
{
memset(cache, 0, sizeof(*cache));
memset(cache->hash, 0xff, sizeof(cache->hash));
snd_array_init(&cache->buf, record_size, 64);
}
static void free_hda_cache(struct hda_cache_rec *cache)
{
snd_array_free(&cache->buf);
}
/* query the hash. allocate an entry if not found. */
static struct hda_cache_head *get_hash(struct hda_cache_rec *cache, u32 key)
{
u16 idx = key % (u16)ARRAY_SIZE(cache->hash);
u16 cur = cache->hash[idx];
struct hda_cache_head *info;
while (cur != 0xffff) {
info = snd_array_elem(&cache->buf, cur);
if (info->key == key)
return info;
cur = info->next;
}
return NULL;
}
/* query the hash. allocate an entry if not found. */
static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache,
u32 key)
{
struct hda_cache_head *info = get_hash(cache, key);
if (!info) {
u16 idx, cur;
/* add a new hash entry */
info = snd_array_new(&cache->buf);
if (!info)
return NULL;
cur = snd_array_index(&cache->buf, info);
info->key = key;
info->val = 0;
info->dirty = 0;
idx = key % (u16)ARRAY_SIZE(cache->hash);
info->next = cache->hash[idx];
cache->hash[idx] = cur;
}
return info;
}
/**
* query_amp_caps - query AMP capabilities
* @codec: the HD-auio codec
......@@ -2589,25 +2519,35 @@ static unsigned int convert_to_spdif_status(unsigned short val)
/* set digital convert verbs both for the given NID and its slaves */
static void set_dig_out(struct hda_codec *codec, hda_nid_t nid,
int verb, int val)
int mask, int val)
{
const hda_nid_t *d;
snd_hda_codec_write_cache(codec, nid, 0, verb, val);
snd_hdac_regmap_update(&codec->core, nid, AC_VERB_SET_DIGI_CONVERT_1,
mask, val);
d = codec->slave_dig_outs;
if (!d)
return;
for (; *d; d++)
snd_hda_codec_write_cache(codec, *d, 0, verb, val);
snd_hdac_regmap_update(&codec->core, nid,
AC_VERB_SET_DIGI_CONVERT_1, mask, val);
}
static inline void set_dig_out_convert(struct hda_codec *codec, hda_nid_t nid,
int dig1, int dig2)
{
if (dig1 != -1)
set_dig_out(codec, nid, AC_VERB_SET_DIGI_CONVERT_1, dig1);
if (dig2 != -1)
set_dig_out(codec, nid, AC_VERB_SET_DIGI_CONVERT_2, dig2);
unsigned int mask = 0;
unsigned int val = 0;
if (dig1 != -1) {
mask |= 0xff;
val = dig1;
}
if (dig2 != -1) {
mask |= 0xff00;
val |= dig2 << 8;
}
set_dig_out(codec, nid, mask, val);
}
static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
......@@ -2740,6 +2680,7 @@ int snd_hda_create_dig_out_ctls(struct hda_codec *codec,
struct snd_kcontrol *kctl;
struct snd_kcontrol_new *dig_mix;
int idx = 0;
int val = 0;
const int spdif_index = 16;
struct hda_spdif_out *spdif;
struct hda_bus *bus = codec->bus;
......@@ -2780,8 +2721,9 @@ int snd_hda_create_dig_out_ctls(struct hda_codec *codec,
return err;
}
spdif->nid = cvt_nid;
spdif->ctls = snd_hda_codec_read(codec, cvt_nid, 0,
AC_VERB_GET_DIGI_CONVERT_1, 0);
snd_hdac_regmap_read(&codec->core, cvt_nid,
AC_VERB_GET_DIGI_CONVERT_1, &val);
spdif->ctls = val;
spdif->status = convert_to_spdif_status(spdif->ctls);
return 0;
}
......@@ -2925,8 +2867,8 @@ static int snd_hda_spdif_in_switch_put(struct snd_kcontrol *kcontrol,
change = codec->spdif_in_enable != val;
if (change) {
codec->spdif_in_enable = val;
snd_hda_codec_write_cache(codec, nid, 0,
AC_VERB_SET_DIGI_CONVERT_1, val);
snd_hdac_regmap_write(&codec->core, nid,
AC_VERB_SET_DIGI_CONVERT_1, val);
}
mutex_unlock(&codec->spdif_mutex);
return change;
......@@ -2937,10 +2879,11 @@ static int snd_hda_spdif_in_status_get(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
hda_nid_t nid = kcontrol->private_value;
unsigned short val;
unsigned int val;
unsigned int sbits;
val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT_1, 0);
snd_hdac_regmap_read(&codec->core, nid,
AC_VERB_GET_DIGI_CONVERT_1, &val);
sbits = convert_to_spdif_status(val);
ucontrol->value.iec958.status[0] = sbits;
ucontrol->value.iec958.status[1] = sbits >> 8;
......@@ -3006,154 +2949,6 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
}
EXPORT_SYMBOL_GPL(snd_hda_create_spdif_in_ctls);
/*
* command cache
*/
/* build a 31bit cache key with the widget id and the command parameter */
#define build_cmd_cache_key(nid, verb) ((verb << 8) | nid)
#define get_cmd_cache_nid(key) ((key) & 0xff)
#define get_cmd_cache_cmd(key) (((key) >> 8) & 0xffff)
/**
* snd_hda_codec_write_cache - send a single command with caching
* @codec: the HDA codec
* @nid: NID to send the command
* @flags: optional bit flags
* @verb: the verb to send
* @parm: the parameter for the verb
*
* Send a single command without waiting for response.
*
* Returns 0 if successful, or a negative error code.
*/
int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
int flags, unsigned int verb, unsigned int parm)
{
int err;
struct hda_cache_head *c;
u32 key;
unsigned int cache_only;
cache_only = codec->cached_write;
if (!cache_only) {
err = snd_hda_codec_write(codec, nid, flags, verb, parm);
if (err < 0)
return err;
}
/* parm may contain the verb stuff for get/set amp */
verb = verb | (parm >> 8);
parm &= 0xff;
key = build_cmd_cache_key(nid, verb);
mutex_lock(&codec->bus->core.cmd_mutex);
c = get_alloc_hash(&codec->cmd_cache, key);
if (c) {
c->val = parm;
c->dirty = cache_only;
}
mutex_unlock(&codec->bus->core.cmd_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(snd_hda_codec_write_cache);
/**
* snd_hda_codec_update_cache - check cache and write the cmd only when needed
* @codec: the HDA codec
* @nid: NID to send the command
* @flags: optional bit flags
* @verb: the verb to send
* @parm: the parameter for the verb
*
* This function works like snd_hda_codec_write_cache(), but it doesn't send
* command if the parameter is already identical with the cached value.
* If not, it sends the command and refreshes the cache.
*
* Returns 0 if successful, or a negative error code.
*/
int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid,
int flags, unsigned int verb, unsigned int parm)
{
struct hda_cache_head *c;
u32 key;
/* parm may contain the verb stuff for get/set amp */
verb = verb | (parm >> 8);
parm &= 0xff;
key = build_cmd_cache_key(nid, verb);
mutex_lock(&codec->bus->core.cmd_mutex);
c = get_hash(&codec->cmd_cache, key);
if (c && c->val == parm) {
mutex_unlock(&codec->bus->core.cmd_mutex);
return 0;
}
mutex_unlock(&codec->bus->core.cmd_mutex);
return snd_hda_codec_write_cache(codec, nid, flags, verb, parm);
}
EXPORT_SYMBOL_GPL(snd_hda_codec_update_cache);
/**
* snd_hda_codec_resume_cache - Resume the all commands from the cache
* @codec: HD-audio codec
*
* Execute all verbs recorded in the command caches to resume.
*/
void snd_hda_codec_resume_cache(struct hda_codec *codec)
{
int i;
mutex_lock(&codec->hash_mutex);
codec->cached_write = 0;
for (i = 0; i < codec->cmd_cache.buf.used; i++) {
struct hda_cache_head *buffer;
u32 key;
buffer = snd_array_elem(&codec->cmd_cache.buf, i);
key = buffer->key;
if (!key)
continue;
if (!buffer->dirty)
continue;
buffer->dirty = 0;
mutex_unlock(&codec->hash_mutex);
snd_hda_codec_write(codec, get_cmd_cache_nid(key), 0,
get_cmd_cache_cmd(key), buffer->val);
mutex_lock(&codec->hash_mutex);
}
mutex_unlock(&codec->hash_mutex);
}
EXPORT_SYMBOL_GPL(snd_hda_codec_resume_cache);
/**
* snd_hda_sequence_write_cache - sequence writes with caching
* @codec: the HDA codec
* @seq: VERB array to send
*
* Send the commands sequentially from the given array.
* Thte commands are recorded on cache for power-save and resume.
* The array must be terminated with NID=0.
*/
void snd_hda_sequence_write_cache(struct hda_codec *codec,
const struct hda_verb *seq)
{
for (; seq->nid; seq++)
snd_hda_codec_write_cache(codec, seq->nid, 0, seq->verb,
seq->param);
}
EXPORT_SYMBOL_GPL(snd_hda_sequence_write_cache);
/**
* snd_hda_codec_flush_cache - Execute all pending (cached) amps / verbs
* @codec: HD-audio codec
*/
void snd_hda_codec_flush_cache(struct hda_codec *codec)
{
if (codec->core.regmap)
regcache_sync(codec->core.regmap);
snd_hda_codec_resume_cache(codec);
}
EXPORT_SYMBOL_GPL(snd_hda_codec_flush_cache);
/**
* snd_hda_codec_set_power_to_all - Set the power state to all widgets
* @codec: the HDA codec
......@@ -3354,17 +3149,6 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec)
return state;
}
/* mark all entries of cmd and amp caches dirty */
static void hda_mark_cmd_cache_dirty(struct hda_codec *codec)
{
int i;
for (i = 0; i < codec->cmd_cache.buf.used; i++) {
struct hda_cache_head *cmd;
cmd = snd_array_elem(&codec->cmd_cache.buf, i);
cmd->dirty = 1;
}
}
/*
* kick up codec; used both from PM and power-save
*/
......@@ -3375,8 +3159,6 @@ static void hda_call_codec_resume(struct hda_codec *codec)
if (codec->core.regmap)
regcache_mark_dirty(codec->core.regmap);
hda_mark_cmd_cache_dirty(codec);
codec->power_jiffies = jiffies;
hda_set_power_state(codec, AC_PWRST_D0);
......@@ -3390,7 +3172,6 @@ static void hda_call_codec_resume(struct hda_codec *codec)
codec->patch_ops.init(codec);
if (codec->core.regmap)
regcache_sync(codec->core.regmap);
snd_hda_codec_resume_cache(codec);
}
if (codec->jackpoll_interval)
......
......@@ -155,19 +155,6 @@ struct hda_codec_ops {
void (*stream_pm)(struct hda_codec *codec, hda_nid_t nid, bool on);
};
/* record for amp information cache */
struct hda_cache_head {
u32 key:31; /* hash key */
u32 dirty:1;
u16 val; /* assigned value */
u16 next;
};
struct hda_cache_rec {
u16 hash[64]; /* hash table for index */
struct snd_array buf; /* record entries */
};
/* PCM callbacks */
struct hda_pcm_ops {
int (*open)(struct hda_pcm_stream *info, struct hda_codec *codec,
......@@ -251,13 +238,10 @@ 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 cmd_cache; /* cache for other commands */
struct list_head conn_list; /* linked-list of connection-list */
struct mutex spdif_mutex;
struct mutex control_mutex;
struct mutex hash_mutex;
struct snd_array spdif_out;
unsigned int spdif_in_enable; /* SPDIF input enable? */
const hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
......@@ -406,15 +390,15 @@ snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex)
}
/* cached write */
int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
int flags, unsigned int verb, unsigned int parm);
void snd_hda_sequence_write_cache(struct hda_codec *codec,
const struct hda_verb *seq);
int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid,
int flags, unsigned int verb, unsigned int parm);
void snd_hda_codec_resume_cache(struct hda_codec *codec);
/* both for cmd & amp caches */
void snd_hda_codec_flush_cache(struct hda_codec *codec);
static inline int
snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
int flags, unsigned int verb, unsigned int parm)
{
return snd_hdac_regmap_write(&codec->core, nid, verb, parm);
}
#define snd_hda_codec_update_cache(codec, nid, flags, verb, parm) \
snd_hda_codec_write_cache(codec, nid, flags, verb, parm)
/* the struct for codec->pin_configs */
struct hda_pincfg {
......
......@@ -3381,11 +3381,6 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol,
imux = &spec->input_mux;
adc_idx = kcontrol->id.index;
mutex_lock(&codec->control_mutex);
/* we use the cache-only update at first since multiple input paths
* may shared the same amp; by updating only caches, the redundant
* writes to hardware can be reduced.
*/
codec->cached_write = 1;
for (i = 0; i < imux->num_items; i++) {
path = get_input_path(codec, adc_idx, i);
if (!path || !path->ctls[type])
......@@ -3393,12 +3388,9 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol,
kcontrol->private_value = path->ctls[type];
err = func(kcontrol, ucontrol);
if (err < 0)
goto error;
break;
}
error:
codec->cached_write = 0;
mutex_unlock(&codec->control_mutex);
snd_hda_codec_flush_cache(codec); /* flush the updates */
if (err >= 0 && spec->cap_sync_hook)
spec->cap_sync_hook(codec, kcontrol, ucontrol);
return err;
......@@ -5760,8 +5752,6 @@ int snd_hda_gen_init(struct hda_codec *codec)
snd_hda_apply_verbs(codec);
codec->cached_write = 1;
init_multi_out(codec);
init_extra_out(codec);
init_multi_io(codec);
......@@ -5777,7 +5767,7 @@ int snd_hda_gen_init(struct hda_codec *codec)
/* call init functions of standard auto-mute helpers */
update_automute_all(codec);
snd_hda_codec_flush_cache(codec);
regcache_sync(codec->core.regmap);
if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook)
snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
......
......@@ -777,7 +777,6 @@ static int ad1988_auto_smux_enum_put(struct snd_kcontrol *kcontrol,
return 0;
mutex_lock(&codec->control_mutex);
codec->cached_write = 1;
path = snd_hda_get_path_from_idx(codec,
spec->smux_paths[spec->cur_smux]);
if (path)
......@@ -786,9 +785,7 @@ static int ad1988_auto_smux_enum_put(struct snd_kcontrol *kcontrol,
if (path)
snd_hda_activate_path(codec, path, true, true);
spec->cur_smux = val;
codec->cached_write = 0;
mutex_unlock(&codec->control_mutex);
snd_hda_codec_flush_cache(codec); /* flush the updates */
return 1;
}
......@@ -1004,18 +1001,17 @@ static void ad1884_fixup_hp_eapd(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
struct ad198x_spec *spec = codec->spec;
static const struct hda_verb gpio_init_verbs[] = {
{0x01, AC_VERB_SET_GPIO_MASK, 0x02},
{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
{0x01, AC_VERB_SET_GPIO_DATA, 0x02},
{},
};
switch (action) {
case HDA_FIXUP_ACT_PRE_PROBE:
spec->gen.vmaster_mute.hook = ad1884_vmaster_hp_gpio_hook;
spec->gen.own_eapd_ctl = 1;
snd_hda_sequence_write_cache(codec, gpio_init_verbs);
snd_hda_codec_write_cache(codec, 0x01, 0,
AC_VERB_SET_GPIO_MASK, 0x02);
snd_hda_codec_write_cache(codec, 0x01, 0,
AC_VERB_SET_GPIO_DIRECTION, 0x02);
snd_hda_codec_write_cache(codec, 0x01, 0,
AC_VERB_SET_GPIO_DATA, 0x02);
break;
case HDA_FIXUP_ACT_PROBE:
if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
......
......@@ -302,6 +302,7 @@ static void cxt_fixup_headphone_mic(struct hda_codec *codec,
switch (action) {
case HDA_FIXUP_ACT_PRE_PROBE:
spec->parse_flags |= HDA_PINCFG_HEADPHONE_MIC;
snd_hdac_regmap_add_vendor_verb(&codec->core, 0x410);
break;
case HDA_FIXUP_ACT_PROBE:
spec->gen.cap_sync_hook = cxt_update_headset_mode_hook;
......@@ -409,15 +410,11 @@ static void olpc_xo_automic(struct hda_codec *codec,
struct hda_jack_callback *jack)
{
struct conexant_spec *spec = codec->spec;
int saved_cached_write = codec->cached_write;
codec->cached_write = 1;
/* in DC mode, we don't handle automic */
if (!spec->dc_enable)
snd_hda_gen_mic_autoswitch(codec, jack);
olpc_xo_update_mic_pins(codec);
snd_hda_codec_flush_cache(codec);
codec->cached_write = saved_cached_write;
if (spec->dc_enable)
olpc_xo_update_mic_boost(codec);
}
......
......@@ -2212,7 +2212,6 @@ static int generic_hdmi_resume(struct hda_codec *codec)
codec->patch_ops.init(codec);
regcache_sync(codec->core.regmap);
snd_hda_codec_resume_cache(codec);
for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
......@@ -2299,6 +2298,7 @@ static void intel_haswell_fixup_enable_dp12(struct hda_codec *codec)
/* enable DP1.2 mode */
vendor_param |= INTEL_EN_DP12;
snd_hdac_regmap_add_vendor_verb(&codec->core, INTEL_SET_VENDOR_VERB);
snd_hda_codec_write_cache(codec, INTEL_VENDOR_NID, 0,
INTEL_SET_VENDOR_VERB, vendor_param);
}
......
......@@ -800,7 +800,6 @@ static int alc_resume(struct hda_codec *codec)
msleep(150); /* to avoid pop noise */
codec->patch_ops.init(codec);
regcache_sync(codec->core.regmap);
snd_hda_codec_resume_cache(codec);
hda_call_check_power_status(codec, 0x01);
return 0;
}
......@@ -3059,7 +3058,6 @@ static int alc269_resume(struct hda_codec *codec)
}
regcache_sync(codec->core.regmap);
snd_hda_codec_resume_cache(codec);
hda_call_check_power_status(codec, 0x01);
/* on some machine, the BIOS will clear the codec gpio data when enter
......
......@@ -222,6 +222,10 @@ static int si3054_init(struct hda_codec *codec)
unsigned wait_count;
u16 val;
if (snd_hdac_regmap_add_vendor_verb(&codec->core,
SI3054_VERB_WRITE_NODE))
return -ENOMEM;
snd_hda_codec_write(codec, AC_NODE_ROOT, 0, AC_VERB_SET_CODEC_RESET, 0);
snd_hda_codec_write(codec, codec->core.mfg, 0, AC_VERB_SET_STREAM_FORMAT, 0);
SET_REG(codec, SI3054_LINE_RATE, 9600);
......
......@@ -1050,12 +1050,9 @@ static const struct hda_verb stac92hd71bxx_core_init[] = {
{}
};
static const struct hda_verb stac92hd71bxx_unmute_core_init[] = {
static const hda_nid_t stac92hd71bxx_unmute_nids[] = {
/* unmute right and left channels for nodes 0x0f, 0xa, 0x0d */
{ 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{ 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{ 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{}
0x0f, 0x0a, 0x0d, 0
};
static const struct hda_verb stac925x_core_init[] = {
......@@ -4269,6 +4266,10 @@ static int stac_parse_auto_config(struct hda_codec *codec)
if (spec->aloopback_ctl &&
snd_hda_get_bool_hint(codec, "loopback") == 1) {
unsigned int wr_verb =
spec->aloopback_ctl->private_value >> 16;
if (snd_hdac_regmap_add_vendor_verb(&codec->core, wr_verb))
return -ENOMEM;
if (!snd_hda_gen_add_kctl(&spec->gen, NULL, spec->aloopback_ctl))
return -ENOMEM;
}
......@@ -4688,7 +4689,7 @@ static int patch_stac92hd95(struct hda_codec *codec)
static int patch_stac92hd71bxx(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
const struct hda_verb *unmute_init = stac92hd71bxx_unmute_core_init;
const hda_nid_t *unmute_nids = stac92hd71bxx_unmute_nids;
int err;
err = alloc_stac_spec(codec);
......@@ -4713,7 +4714,7 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
switch (codec->core.vendor_id) {
case 0x111d76b6: /* 4 Port without Analog Mixer */
case 0x111d76b7:
unmute_init++;
unmute_nids++;
break;
case 0x111d7608: /* 5 Port with Analog Mixer */
if ((codec->core.revision_id & 0xf) == 0 ||
......@@ -4721,7 +4722,7 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
spec->stream_delay = 40; /* 40 milliseconds */
/* disable VSW */
unmute_init++;
unmute_nids++;
snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0);
snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3);
break;
......@@ -4735,8 +4736,12 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
if (get_wcaps_type(get_wcaps(codec, 0x28)) == AC_WID_VOL_KNB)
snd_hda_add_verbs(codec, stac92hd71bxx_core_init);
if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP)
snd_hda_sequence_write_cache(codec, unmute_init);
if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP) {
const hda_nid_t *p;
for (p = unmute_nids; *p; p++)
snd_hda_codec_amp_init_stereo(codec, *p, HDA_INPUT, 0,
0xff, 0x00);
}
spec->aloopback_ctl = &stac92hd71bxx_loopback;
spec->aloopback_mask = 0x50;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册