aboutsummaryrefslogtreecommitdiff
path: root/sound/pci/hda/patch_via.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci/hda/patch_via.c')
-rw-r--r--sound/pci/hda/patch_via.c5860
1 files changed, 1600 insertions, 4260 deletions
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index f43bb0eaed8..0da4f8ff542 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -54,36 +54,10 @@
#include "hda_codec.h"
#include "hda_local.h"
-#define NID_MAPPING (-1)
-
-/* amp values */
-#define AMP_VAL_IDX_SHIFT 19
-#define AMP_VAL_IDX_MASK (0x0f<<19)
-
/* Pin Widget NID */
-#define VT1708_HP_NID 0x13
-#define VT1708_DIGOUT_NID 0x14
-#define VT1708_DIGIN_NID 0x16
-#define VT1708_DIGIN_PIN 0x26
#define VT1708_HP_PIN_NID 0x20
#define VT1708_CD_PIN_NID 0x24
-#define VT1709_HP_DAC_NID 0x28
-#define VT1709_DIGOUT_NID 0x13
-#define VT1709_DIGIN_NID 0x17
-#define VT1709_DIGIN_PIN 0x25
-
-#define VT1708B_HP_NID 0x25
-#define VT1708B_DIGOUT_NID 0x12
-#define VT1708B_DIGIN_NID 0x15
-#define VT1708B_DIGIN_PIN 0x21
-
-#define VT1708S_HP_NID 0x25
-#define VT1708S_DIGOUT_NID 0x12
-
-#define VT1702_HP_NID 0x17
-#define VT1702_DIGOUT_NID 0x11
-
enum VIA_HDA_CODEC {
UNKNOWN = -1,
VT1708,
@@ -107,6 +81,34 @@ enum VIA_HDA_CODEC {
(spec)->codec_type == VT1812 ||\
(spec)->codec_type == VT1802)
+#define MAX_NID_PATH_DEPTH 5
+
+/* output-path: DAC -> ... -> pin
+ * idx[] contains the source index number of the next widget;
+ * e.g. idx[0] is the index of the DAC selected by path[1] widget
+ * multi[] indicates whether it's a selector widget with multi-connectors
+ * (i.e. the connection selection is mandatory)
+ * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
+ */
+struct nid_path {
+ int depth;
+ hda_nid_t path[MAX_NID_PATH_DEPTH];
+ unsigned char idx[MAX_NID_PATH_DEPTH];
+ unsigned char multi[MAX_NID_PATH_DEPTH];
+ unsigned int vol_ctl;
+ unsigned int mute_ctl;
+};
+
+/* input-path */
+struct via_input {
+ hda_nid_t pin; /* input-pin or aa-mix */
+ int adc_idx; /* ADC index to be used */
+ int mux_idx; /* MUX index (if any) */
+ const char *label; /* input-source label */
+};
+
+#define VIA_MAX_ADCS 3
+
struct via_spec {
/* codec parameterization */
const struct snd_kcontrol_new *mixers[6];
@@ -115,28 +117,45 @@ struct via_spec {
const struct hda_verb *init_verbs[5];
unsigned int num_iverbs;
- char *stream_name_analog;
+ char stream_name_analog[32];
+ char stream_name_hp[32];
const struct hda_pcm_stream *stream_analog_playback;
const struct hda_pcm_stream *stream_analog_capture;
- char *stream_name_digital;
+ char stream_name_digital[32];
const struct hda_pcm_stream *stream_digital_playback;
const struct hda_pcm_stream *stream_digital_capture;
/* playback */
struct hda_multi_out multiout;
hda_nid_t slave_dig_outs[2];
+ hda_nid_t hp_dac_nid;
+ bool hp_indep_shared; /* indep HP-DAC is shared with side ch */
+ int num_active_streams;
+ int dac_mixer_idx;
+
+ struct nid_path out_path[HDA_SIDE + 1];
+ struct nid_path hp_path;
+ struct nid_path hp_dep_path;
+ struct nid_path speaker_path;
/* capture */
unsigned int num_adc_nids;
- const hda_nid_t *adc_nids;
- hda_nid_t mux_nids[3];
+ hda_nid_t adc_nids[VIA_MAX_ADCS];
+ hda_nid_t mux_nids[VIA_MAX_ADCS];
+ hda_nid_t aa_mix_nid;
hda_nid_t dig_in_nid;
- hda_nid_t dig_in_pin;
/* capture source */
- const struct hda_input_mux *input_mux;
- unsigned int cur_mux[3];
+ bool dyn_adc_switch;
+ int num_inputs;
+ struct via_input inputs[AUTO_CFG_MAX_INS + 1];
+ unsigned int cur_mux[VIA_MAX_ADCS];
+
+ /* dynamic ADC switching */
+ hda_nid_t cur_adc;
+ unsigned int cur_adc_stream_tag;
+ unsigned int cur_adc_format;
/* PCM information */
struct hda_pcm pcm_rec[3];
@@ -144,28 +163,36 @@ struct via_spec {
/* dynamic controls, init_verbs and input_mux */
struct auto_pin_cfg autocfg;
struct snd_array kctls;
- struct hda_input_mux private_imux[2];
hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
/* HP mode source */
- const struct hda_input_mux *hp_mux;
unsigned int hp_independent_mode;
- unsigned int hp_independent_mode_index;
- unsigned int smart51_enabled;
unsigned int dmic_enabled;
+ unsigned int no_pin_power_ctl;
enum VIA_HDA_CODEC codec_type;
+ /* smart51 setup */
+ unsigned int smart51_nums;
+ hda_nid_t smart51_pins[2];
+ int smart51_idxs[2];
+ const char *smart51_labels[2];
+ unsigned int smart51_enabled;
+
/* work to check hp jack state */
struct hda_codec *codec;
struct delayed_work vt1708_hp_work;
- int vt1708_jack_detectect;
+ int vt1708_jack_detect;
int vt1708_hp_present;
void (*set_widgets_power_state)(struct hda_codec *codec);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
struct hda_loopback_check loopback;
-#endif
+ int num_loopbacks;
+ struct hda_amp_list loopback_list[8];
+
+ /* bind capture-volume */
+ struct hda_bind_ctls *bind_cap_vol;
+ struct hda_bind_ctls *bind_cap_sw;
};
static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
@@ -237,33 +264,23 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
#define VIA_JACK_EVENT 0x20
#define VIA_HP_EVENT 0x01
#define VIA_GPIO_EVENT 0x02
-#define VIA_MONO_EVENT 0x03
-#define VIA_SPEAKER_EVENT 0x04
-#define VIA_BIND_HP_EVENT 0x05
+#define VIA_LINE_EVENT 0x03
enum {
VIA_CTL_WIDGET_VOL,
VIA_CTL_WIDGET_MUTE,
VIA_CTL_WIDGET_ANALOG_MUTE,
- VIA_CTL_WIDGET_BIND_PIN_MUTE,
-};
-
-enum {
- AUTO_SEQ_FRONT = 0,
- AUTO_SEQ_SURROUND,
- AUTO_SEQ_CENLFE,
- AUTO_SEQ_SIDE
};
-static void analog_low_current_mode(struct hda_codec *codec, int stream_idle);
-static int is_aa_path_mute(struct hda_codec *codec);
+static void analog_low_current_mode(struct hda_codec *codec);
+static bool is_aa_path_mute(struct hda_codec *codec);
static void vt1708_start_hp_work(struct via_spec *spec)
{
if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
return;
snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
- !spec->vt1708_jack_detectect);
+ !spec->vt1708_jack_detect);
if (!delayed_work_pending(&spec->vt1708_hp_work))
schedule_delayed_work(&spec->vt1708_hp_work,
msecs_to_jiffies(100));
@@ -277,7 +294,7 @@ static void vt1708_stop_hp_work(struct via_spec *spec)
&& !is_aa_path_mute(spec->codec))
return;
snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
- !spec->vt1708_jack_detectect);
+ !spec->vt1708_jack_detect);
cancel_delayed_work_sync(&spec->vt1708_hp_work);
}
@@ -295,7 +312,7 @@ static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
set_widgets_power_state(codec);
- analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1);
+ analog_low_current_mode(snd_kcontrol_chip(kcontrol));
if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
if (is_aa_path_mute(codec))
vt1708_start_hp_work(codec->spec);
@@ -315,168 +332,44 @@ static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
.put = analog_input_switch_put, \
.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
-static void via_hp_bind_automute(struct hda_codec *codec);
-
-static int bind_pin_switch_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct via_spec *spec = codec->spec;
- int i;
- int change = 0;
-
- long *valp = ucontrol->value.integer.value;
- int lmute, rmute;
- if (strstr(kcontrol->id.name, "Switch") == NULL) {
- snd_printd("Invalid control!\n");
- return change;
- }
- change = snd_hda_mixer_amp_switch_put(kcontrol,
- ucontrol);
- /* Get mute value */
- lmute = *valp ? 0 : HDA_AMP_MUTE;
- valp++;
- rmute = *valp ? 0 : HDA_AMP_MUTE;
-
- /* Set hp pins */
- if (!spec->hp_independent_mode) {
- for (i = 0; i < spec->autocfg.hp_outs; i++) {
- snd_hda_codec_amp_update(
- codec, spec->autocfg.hp_pins[i],
- 0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
- lmute);
- snd_hda_codec_amp_update(
- codec, spec->autocfg.hp_pins[i],
- 1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
- rmute);
- }
- }
-
- if (!lmute && !rmute) {
- /* Line Outs */
- for (i = 0; i < spec->autocfg.line_outs; i++)
- snd_hda_codec_amp_stereo(
- codec, spec->autocfg.line_out_pins[i],
- HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
- /* Speakers */
- for (i = 0; i < spec->autocfg.speaker_outs; i++)
- snd_hda_codec_amp_stereo(
- codec, spec->autocfg.speaker_pins[i],
- HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
- /* unmute */
- via_hp_bind_automute(codec);
-
- } else {
- if (lmute) {
- /* Mute all left channels */
- for (i = 1; i < spec->autocfg.line_outs; i++)
- snd_hda_codec_amp_update(
- codec,
- spec->autocfg.line_out_pins[i],
- 0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
- lmute);
- for (i = 0; i < spec->autocfg.speaker_outs; i++)
- snd_hda_codec_amp_update(
- codec,
- spec->autocfg.speaker_pins[i],
- 0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
- lmute);
- }
- if (rmute) {
- /* mute all right channels */
- for (i = 1; i < spec->autocfg.line_outs; i++)
- snd_hda_codec_amp_update(
- codec,
- spec->autocfg.line_out_pins[i],
- 1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
- rmute);
- for (i = 0; i < spec->autocfg.speaker_outs; i++)
- snd_hda_codec_amp_update(
- codec,
- spec->autocfg.speaker_pins[i],
- 1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
- rmute);
- }
- }
- return change;
-}
-
-#define BIND_PIN_MUTE \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .name = NULL, \
- .index = 0, \
- .info = snd_hda_mixer_amp_switch_info, \
- .get = snd_hda_mixer_amp_switch_get, \
- .put = bind_pin_switch_put, \
- .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
-
static const struct snd_kcontrol_new via_control_templates[] = {
HDA_CODEC_VOLUME(NULL, 0, 0, 0),
HDA_CODEC_MUTE(NULL, 0, 0, 0),
ANALOG_INPUT_MUTE,
- BIND_PIN_MUTE,
-};
-
-static const hda_nid_t vt1708_adc_nids[2] = {
- /* ADC1-2 */
- 0x15, 0x27
-};
-
-static const hda_nid_t vt1709_adc_nids[3] = {
- /* ADC1-2 */
- 0x14, 0x15, 0x16
-};
-
-static const hda_nid_t vt1708B_adc_nids[2] = {
- /* ADC1-2 */
- 0x13, 0x14
-};
-
-static const hda_nid_t vt1708S_adc_nids[2] = {
- /* ADC1-2 */
- 0x13, 0x14
-};
-
-static const hda_nid_t vt1702_adc_nids[3] = {
- /* ADC1-2 */
- 0x12, 0x20, 0x1F
-};
-
-static const hda_nid_t vt1718S_adc_nids[2] = {
- /* ADC1-2 */
- 0x10, 0x11
-};
-
-static const hda_nid_t vt1716S_adc_nids[2] = {
- /* ADC1-2 */
- 0x13, 0x14
};
-static const hda_nid_t vt2002P_adc_nids[2] = {
- /* ADC1-2 */
- 0x10, 0x11
-};
-static const hda_nid_t vt1812_adc_nids[2] = {
- /* ADC1-2 */
- 0x10, 0x11
-};
+/* add dynamic controls */
+static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
+ const struct snd_kcontrol_new *tmpl,
+ const char *name)
+{
+ struct snd_kcontrol_new *knew;
+ snd_array_init(&spec->kctls, sizeof(*knew), 32);
+ knew = snd_array_new(&spec->kctls);
+ if (!knew)
+ return NULL;
+ *knew = *tmpl;
+ if (!name)
+ name = tmpl->name;
+ if (name) {
+ knew->name = kstrdup(name, GFP_KERNEL);
+ if (!knew->name)
+ return NULL;
+ }
+ return knew;
+}
-/* add dynamic controls */
static int __via_add_control(struct via_spec *spec, int type, const char *name,
int idx, unsigned long val)
{
struct snd_kcontrol_new *knew;
- snd_array_init(&spec->kctls, sizeof(*knew), 32);
- knew = snd_array_new(&spec->kctls);
+ knew = __via_clone_ctl(spec, &via_control_templates[type], name);
if (!knew)
return -ENOMEM;
- *knew = via_control_templates[type];
- knew->name = kstrdup(name, GFP_KERNEL);
- if (!knew->name)
- return -ENOMEM;
+ knew->index = idx;
if (get_amp_nid_(val))
knew->subdevice = HDA_SUBDEV_AMP_FLAG;
knew->private_value = val;
@@ -486,21 +379,7 @@ static int __via_add_control(struct via_spec *spec, int type, const char *name,
#define via_add_control(spec, type, name, val) \
__via_add_control(spec, type, name, 0, val)
-static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec,
- const struct snd_kcontrol_new *tmpl)
-{
- struct snd_kcontrol_new *knew;
-
- snd_array_init(&spec->kctls, sizeof(*knew), 32);
- knew = snd_array_new(&spec->kctls);
- if (!knew)
- return NULL;
- *knew = *tmpl;
- knew->name = kstrdup(tmpl->name, GFP_KERNEL);
- if (!knew->name)
- return NULL;
- return knew;
-}
+#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
static void via_free_kctls(struct hda_codec *codec)
{
@@ -535,58 +414,227 @@ static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
return 0;
}
-static void via_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid, int pin_type,
- int dac_idx)
+#define get_connection_index(codec, mux, nid) \
+ snd_hda_get_conn_index(codec, mux, nid, 0)
+
+static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
+ unsigned int mask)
+{
+ unsigned int caps;
+ if (!nid)
+ return false;
+ caps = get_wcaps(codec, nid);
+ if (dir == HDA_INPUT)
+ caps &= AC_WCAP_IN_AMP;
+ else
+ caps &= AC_WCAP_OUT_AMP;
+ if (!caps)
+ return false;
+ if (query_amp_caps(codec, nid, dir) & mask)
+ return true;
+ return false;
+}
+
+#define have_mute(codec, nid, dir) \
+ check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
+
+static bool is_node_in_path(struct nid_path *path, hda_nid_t nid)
+{
+ int i;
+ if (!nid)
+ return false;
+ for (i = 0; i < path->depth; i++) {
+ if (path->path[i] == nid)
+ return true;
+ }
+ return false;
+}
+
+/* enable/disable the output-route mixers */
+static void activate_output_mix(struct hda_codec *codec, struct nid_path *path,
+ hda_nid_t mix_nid, int aa_mix_idx, bool enable)
+{
+ int i, num, val;
+ bool hp_path, front_path;
+ struct via_spec *spec = codec->spec;
+
+ if (!path)
+ return;
+ num = snd_hda_get_conn_list(codec, mix_nid, NULL);
+ hp_path = is_node_in_path(path, spec->hp_dac_nid);
+ front_path = is_node_in_path(path, spec->multiout.dac_nids[0]);
+
+ for (i = 0; i < num; i++) {
+ if (i == aa_mix_idx) {
+ if (hp_path)
+ val = enable ? AMP_IN_MUTE(i) :
+ AMP_IN_UNMUTE(i);
+ else if (front_path)
+ val = AMP_IN_UNMUTE(i);
+ else
+ val = AMP_IN_MUTE(i);
+ } else {
+ if (hp_path)
+ val = enable ? AMP_IN_UNMUTE(i) :
+ AMP_IN_MUTE(i);
+ else if (front_path)
+ val = AMP_IN_MUTE(i);
+ else
+ val = AMP_IN_UNMUTE(i);
+ }
+ snd_hda_codec_write(codec, mix_nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, val);
+ }
+}
+
+/* enable/disable the output-route */
+static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
+ bool enable, bool force)
+{
+ int i, val;
+ struct via_spec *spec = codec->spec;
+ hda_nid_t aa_mix_nid = spec->aa_mix_nid;
+ for (i = 0; i < path->depth; i++) {
+ hda_nid_t src, dst;
+ int idx = path->idx[i];
+ src = path->path[i];
+ if (i < path->depth - 1)
+ dst = path->path[i + 1];
+ else
+ dst = 0;
+ if (enable && path->multi[i])
+ snd_hda_codec_write(codec, dst, 0,
+ AC_VERB_SET_CONNECT_SEL, idx);
+ if (!force
+ && get_wcaps_type(get_wcaps(codec, src)) == AC_WID_AUD_OUT
+ && get_wcaps_type(get_wcaps(codec, dst)) == AC_WID_AUD_MIX)
+ continue;
+ if (have_mute(codec, dst, HDA_INPUT)) {
+ if (dst == aa_mix_nid) {
+ val = enable ? AMP_IN_UNMUTE(idx) :
+ AMP_IN_MUTE(idx);
+ snd_hda_codec_write(codec, dst, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, val);
+ } else {
+ idx = get_connection_index(codec, dst,
+ aa_mix_nid);
+ if (idx >= 0) {
+ activate_output_mix(codec, path,
+ dst, idx, enable);
+ }
+ }
+ }
+ if (!force && (src == path->vol_ctl || src == path->mute_ctl))
+ continue;
+ if (have_mute(codec, src, HDA_OUTPUT)) {
+ int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
+ snd_hda_codec_write(codec, src, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, val);
+ }
+ }
+}
+
+/* set the given pin as output */
+static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
+ int pin_type)
{
- /* set as output */
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ if (!pin)
+ return;
+ snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
pin_type);
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
- if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)
- snd_hda_codec_write(codec, nid, 0,
+ if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
+ snd_hda_codec_write(codec, pin, 0,
AC_VERB_SET_EAPD_BTLENABLE, 0x02);
}
+static void via_auto_init_output(struct hda_codec *codec,
+ struct nid_path *path, int pin_type,
+ bool with_aa_mix, bool force)
+{
+ struct via_spec *spec = codec->spec;
+ unsigned int caps;
+ hda_nid_t pin;
+
+ if (!path->depth)
+ return;
+ pin = path->path[path->depth - 1];
+
+ init_output_pin(codec, pin, pin_type);
+ caps = query_amp_caps(codec, pin, HDA_OUTPUT);
+ if (caps & AC_AMPCAP_MUTE) {
+ unsigned int val;
+ val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
+ snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_MUTE | val);
+ }
+
+ /* initialize the AA-path */
+ if (!spec->aa_mix_nid)
+ return;
+ activate_output_path(codec, path, true, force);
+}
static void via_auto_init_multi_out(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
int i;
- for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
- hda_nid_t nid = spec->autocfg.line_out_pins[i];
- if (nid)
- via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
- }
+ for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++)
+ /* enable aa-mute only for the front channel */
+ via_auto_init_output(codec, &spec->out_path[i], PIN_OUT,
+ i == 0, true);
}
static void via_auto_init_hp_out(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
- hda_nid_t pin;
- int i;
- for (i = 0; i < spec->autocfg.hp_outs; i++) {
- pin = spec->autocfg.hp_pins[i];
- if (pin) /* connect to front */
- via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
+ if (!spec->hp_dac_nid) {
+ via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP,
+ true, true);
+ return;
+ }
+ if (spec->hp_independent_mode) {
+ activate_output_path(codec, &spec->hp_dep_path, false, false);
+ via_auto_init_output(codec, &spec->hp_path, PIN_HP,
+ true, true);
+ } else {
+ activate_output_path(codec, &spec->hp_path, false, false);
+ via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP,
+ true, true);
}
}
-static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
+static void via_auto_init_speaker_out(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+
+ if (spec->autocfg.speaker_outs)
+ via_auto_init_output(codec, &spec->speaker_path, PIN_OUT,
+ true, true);
+}
+
+static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
static void via_auto_init_analog_input(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
const struct auto_pin_cfg *cfg = &spec->autocfg;
+ hda_nid_t conn[HDA_MAX_CONNECTIONS];
unsigned int ctl;
- int i;
+ int i, num_conns;
+
+ /* init ADCs */
+ for (i = 0; i < spec->num_adc_nids; i++) {
+ snd_hda_codec_write(codec, spec->adc_nids[i], 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_UNMUTE(0));
+ }
+ /* init pins */
for (i = 0; i < cfg->num_inputs; i++) {
hda_nid_t nid = cfg->inputs[i].pin;
- if (spec->smart51_enabled && is_smart51_pins(spec, nid))
+ if (spec->smart51_enabled && is_smart51_pins(codec, nid))
ctl = PIN_OUT;
else if (cfg->inputs[i].type == AUTO_PIN_MIC)
ctl = PIN_VREF50;
@@ -595,6 +643,32 @@ static void via_auto_init_analog_input(struct hda_codec *codec)
snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
}
+
+ /* init input-src */
+ for (i = 0; i < spec->num_adc_nids; i++) {
+ int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
+ if (spec->mux_nids[adc_idx]) {
+ int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
+ snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
+ AC_VERB_SET_CONNECT_SEL,
+ mux_idx);
+ }
+ if (spec->dyn_adc_switch)
+ break; /* only one input-src */
+ }
+
+ /* init aa-mixer */
+ if (!spec->aa_mix_nid)
+ return;
+ num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
+ ARRAY_SIZE(conn));
+ for (i = 0; i < num_conns; i++) {
+ unsigned int caps = get_wcaps(codec, conn[i]);
+ if (get_wcaps_type(caps) == AC_WID_PIN)
+ snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_MUTE(i));
+ }
}
static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
@@ -605,9 +679,13 @@ static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
>> AC_DEFCFG_MISC_SHIFT
& AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
- unsigned present = snd_hda_jack_detect(codec, nid);
struct via_spec *spec = codec->spec;
- if ((spec->smart51_enabled && is_smart51_pins(spec, nid))
+ unsigned present = 0;
+
+ no_presence |= spec->no_pin_power_ctl;
+ if (!no_presence)
+ present = snd_hda_jack_detect(codec, nid);
+ if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
|| ((no_presence || present)
&& get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
*affected_parm = AC_PWRST_D0; /* if it's connected */
@@ -618,123 +696,77 @@ static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
}
-/*
- * input MUX handling
- */
-static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
+static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct via_spec *spec = codec->spec;
- return snd_hda_input_mux_info(spec->input_mux, uinfo);
+ static const char * const texts[] = {
+ "Disabled", "Enabled"
+ };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 2;
+ if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+ uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+ strcpy(uinfo->value.enumerated.name,
+ texts[uinfo->value.enumerated.item]);
+ return 0;
}
-static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct via_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
- ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
+ ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
return 0;
}
-static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct via_spec *spec = codec->spec;
- unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
- int ret;
-
- if (!spec->mux_nids[adc_idx])
- return -EINVAL;
- /* switch to D0 beofre change index */
- if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
- AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
- snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
- AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+ unsigned int val = !ucontrol->value.enumerated.item[0];
- ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
- spec->mux_nids[adc_idx],
- &spec->cur_mux[adc_idx]);
- /* update jack power state */
+ if (val == spec->no_pin_power_ctl)
+ return 0;
+ spec->no_pin_power_ctl = val;
set_widgets_power_state(codec);
-
- return ret;
+ return 1;
}
+static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Dynamic Power-Control",
+ .info = via_pin_power_ctl_info,
+ .get = via_pin_power_ctl_get,
+ .put = via_pin_power_ctl_put,
+};
+
+
static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
- struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- struct via_spec *spec = codec->spec;
- return snd_hda_input_mux_info(spec->hp_mux, uinfo);
+ static const char * const texts[] = { "OFF", "ON" };
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = 2;
+ if (uinfo->value.enumerated.item >= 2)
+ uinfo->value.enumerated.item = 1;
+ strcpy(uinfo->value.enumerated.name,
+ texts[uinfo->value.enumerated.item]);
+ return 0;
}
static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
- hda_nid_t nid = kcontrol->private_value;
- unsigned int pinsel;
-
- /* use !! to translate conn sel 2 for VT1718S */
- pinsel = !!snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONNECT_SEL,
- 0x00);
- ucontrol->value.enumerated.item[0] = pinsel;
-
- return 0;
-}
-
-static void activate_ctl(struct hda_codec *codec, const char *name, int active)
-{
- struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name);
- if (ctl) {
- ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
- ctl->vd[0].access |= active
- ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE;
- snd_ctl_notify(codec->bus->card,
- SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id);
- }
-}
-
-static hda_nid_t side_mute_channel(struct via_spec *spec)
-{
- switch (spec->codec_type) {
- case VT1708: return 0x1b;
- case VT1709_10CH: return 0x29;
- case VT1708B_8CH: /* fall thru */
- case VT1708S: return 0x27;
- case VT2002P: return 0x19;
- case VT1802: return 0x15;
- case VT1812: return 0x15;
- default: return 0;
- }
-}
-
-static int update_side_mute_status(struct hda_codec *codec)
-{
- /* mute side channel */
struct via_spec *spec = codec->spec;
- unsigned int parm;
- hda_nid_t sw3 = side_mute_channel(spec);
- if (sw3) {
- if (VT2002P_COMPATIBLE(spec))
- parm = spec->hp_independent_mode ?
- AMP_IN_MUTE(1) : AMP_IN_UNMUTE(1);
- else
- parm = spec->hp_independent_mode ?
- AMP_OUT_MUTE : AMP_OUT_UNMUTE;
- snd_hda_codec_write(codec, sw3, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, parm);
- if (spec->codec_type == VT1812)
- snd_hda_codec_write(codec, 0x1d, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, parm);
- }
+ ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
return 0;
}
@@ -743,66 +775,41 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct via_spec *spec = codec->spec;
- hda_nid_t nid = kcontrol->private_value;
- unsigned int pinsel = ucontrol->value.enumerated.item[0];
- unsigned int parm0, parm1;
- /* Get Independent Mode index of headphone pin widget */
- spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
- ? 1 : 0;
- if (spec->codec_type == VT1718S) {
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_CONNECT_SEL, pinsel ? 2 : 0);
- /* Set correct mute switch for MW3 */
- parm0 = spec->hp_independent_mode ?
- AMP_IN_UNMUTE(0) : AMP_IN_MUTE(0);
- parm1 = spec->hp_independent_mode ?
- AMP_IN_MUTE(1) : AMP_IN_UNMUTE(1);
- snd_hda_codec_write(codec, 0x1b, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, parm0);
- snd_hda_codec_write(codec, 0x1b, 0,
- AC_VERB_SET_AMP_GAIN_MUTE, parm1);
- }
- else
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_CONNECT_SEL, pinsel);
+ int cur;
- if (spec->codec_type == VT1812)
- snd_hda_codec_write(codec, 0x35, 0,
- AC_VERB_SET_CONNECT_SEL, pinsel);
- if (spec->multiout.hp_nid && spec->multiout.hp_nid
- != spec->multiout.dac_nids[HDA_FRONT])
- snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid,
- 0, 0, 0);
-
- update_side_mute_status(codec);
- /* update HP volume/swtich active state */
- if (spec->codec_type == VT1708S
- || spec->codec_type == VT1702
- || spec->codec_type == VT1718S
- || spec->codec_type == VT1716S
- || VT2002P_COMPATIBLE(spec)) {
- activate_ctl(codec, "Headphone Playback Volume",
- spec->hp_independent_mode);
- activate_ctl(codec, "Headphone Playback Switch",
- spec->hp_independent_mode);
+ /* no independent-hp status change during PCM playback is running */
+ if (spec->num_active_streams)
+ return -EBUSY;
+
+ cur = !!ucontrol->value.enumerated.item[0];
+ if (spec->hp_independent_mode == cur)
+ return 0;
+ spec->hp_independent_mode = cur;
+ if (cur) {
+ activate_output_path(codec, &spec->hp_dep_path, false, false);
+ activate_output_path(codec, &spec->hp_path, true, false);
+ if (spec->hp_indep_shared)
+ activate_output_path(codec, &spec->out_path[HDA_SIDE],
+ false, false);
+ } else {
+ activate_output_path(codec, &spec->hp_path, false, false);
+ activate_output_path(codec, &spec->hp_dep_path, true, false);
+ if (spec->hp_indep_shared)
+ activate_output_path(codec, &spec->out_path[HDA_SIDE],
+ true, false);
}
+
/* update jack power state */
set_widgets_power_state(codec);
- return 0;
+ return 1;
}
-static const struct snd_kcontrol_new via_hp_mixer[2] = {
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Independent HP",
- .info = via_independent_hp_info,
- .get = via_independent_hp_get,
- .put = via_independent_hp_put,
- },
- {
- .iface = NID_MAPPING,
- .name = "Independent HP",
- },
+static const struct snd_kcontrol_new via_hp_mixer = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Independent HP",
+ .info = via_independent_hp_info,
+ .get = via_independent_hp_get,
+ .put = via_independent_hp_put,
};
static int via_hp_build(struct hda_codec *codec)
@@ -810,61 +817,28 @@ static int via_hp_build(struct hda_codec *codec)
struct via_spec *spec = codec->spec;
struct snd_kcontrol_new *knew;
hda_nid_t nid;
- int nums;
- hda_nid_t conn[HDA_MAX_CONNECTIONS];
-
- switch (spec->codec_type) {
- case VT1718S:
- nid = 0x34;
- break;
- case VT2002P:
- case VT1802:
- nid = 0x35;
- break;
- case VT1812:
- nid = 0x3d;
- break;
- default:
- nid = spec->autocfg.hp_pins[0];
- break;
- }
-
- if (spec->codec_type != VT1708) {
- nums = snd_hda_get_connections(codec, nid,
- conn, HDA_MAX_CONNECTIONS);
- if (nums <= 1)
- return 0;
- }
- knew = via_clone_control(spec, &via_hp_mixer[0]);
+ nid = spec->autocfg.hp_pins[0];
+ knew = via_clone_control(spec, &via_hp_mixer);
if (knew == NULL)
return -ENOMEM;
knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
- knew->private_value = nid;
-
- nid = side_mute_channel(spec);
- if (nid) {
- knew = via_clone_control(spec, &via_hp_mixer[1]);
- if (knew == NULL)
- return -ENOMEM;
- knew->subdevice = nid;
- }
return 0;
}
static void notify_aa_path_ctls(struct hda_codec *codec)
{
+ struct via_spec *spec = codec->spec;
int i;
- struct snd_ctl_elem_id id;
- const char *labels[] = {"Mic", "Front Mic", "Line", "Rear Mic"};
- struct snd_kcontrol *ctl;
-
- memset(&id, 0, sizeof(id));
- id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- for (i = 0; i < ARRAY_SIZE(labels); i++) {
- sprintf(id.name, "%s Playback Volume", labels[i]);
+
+ for (i = 0; i < spec->smart51_nums; i++) {
+ struct snd_kcontrol *ctl;
+ struct snd_ctl_elem_id id;
+ memset(&id, 0, sizeof(id));
+ id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
ctl = snd_hda_find_mixer_ctl(codec, id.name);
if (ctl)
snd_ctl_notify(codec->bus->card,
@@ -876,66 +850,28 @@ static void notify_aa_path_ctls(struct hda_codec *codec)
static void mute_aa_path(struct hda_codec *codec, int mute)
{
struct via_spec *spec = codec->spec;
- hda_nid_t nid_mixer;
- int start_idx;
- int end_idx;
+ int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
int i;
- /* get nid of MW0 and start & end index */
- switch (spec->codec_type) {
- case VT1708:
- nid_mixer = 0x17;
- start_idx = 2;
- end_idx = 4;
- break;
- case VT1709_10CH:
- case VT1709_6CH:
- nid_mixer = 0x18;
- start_idx = 2;
- end_idx = 4;
- break;
- case VT1708B_8CH:
- case VT1708B_4CH:
- case VT1708S:
- case VT1716S:
- nid_mixer = 0x16;
- start_idx = 2;
- end_idx = 4;
- break;
- case VT1718S:
- nid_mixer = 0x21;
- start_idx = 1;
- end_idx = 3;
- break;
- default:
- return;
- }
+
/* check AA path's mute status */
- for (i = start_idx; i <= end_idx; i++) {
- int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
- snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i,
+ for (i = 0; i < spec->smart51_nums; i++) {
+ if (spec->smart51_idxs[i] < 0)
+ continue;
+ snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
+ HDA_INPUT, spec->smart51_idxs[i],
HDA_AMP_MUTE, val);
}
}
-static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin)
+
+static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
{
- const struct auto_pin_cfg *cfg = &spec->autocfg;
+ struct via_spec *spec = codec->spec;
int i;
- for (i = 0; i < cfg->num_inputs; i++) {
- if (pin == cfg->inputs[i].pin)
- return cfg->inputs[i].type <= AUTO_PIN_LINE_IN;
- }
- return 0;
-}
-
-static int via_smart51_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEA