/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
+/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
/* */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#define IS_VT1708S_VENDORID(x) ((x) >= 0x11060397 && (x) <= 0x11067397)
#define IS_VT1702_VENDORID(x) ((x) >= 0x11060398 && (x) <= 0x11067398)
+#define VIA_HP_EVENT 0x01
+#define VIA_GPIO_EVENT 0x02
+
enum {
VIA_CTL_WIDGET_VOL,
VIA_CTL_WIDGET_MUTE,
struct snd_kcontrol_new *mixers[3];
unsigned int num_mixers;
- struct hda_verb *init_verbs;
+ struct hda_verb *init_verbs[5];
+ unsigned int num_iverbs;
char *stream_name_analog;
struct hda_pcm_stream *stream_analog_playback;
kfree(codec->spec);
}
+/* mute internal speaker if HP is plugged */
+static void via_hp_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+ struct via_spec *spec = codec->spec;
+
+ present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0],
+ HDA_OUTPUT, 0, HDA_AMP_MUTE,
+ present ? HDA_AMP_MUTE : 0);
+}
+
+static void via_gpio_control(struct hda_codec *codec)
+{
+ unsigned int gpio_data;
+ unsigned int vol_counter;
+ unsigned int vol;
+ unsigned int master_vol;
+
+ struct via_spec *spec = codec->spec;
+
+ gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
+ AC_VERB_GET_GPIO_DATA, 0) & 0x03;
+
+ vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
+ 0xF84, 0) & 0x3F0000) >> 16;
+
+ vol = vol_counter & 0x1F;
+ master_vol = snd_hda_codec_read(codec, 0x1A, 0,
+ AC_VERB_GET_AMP_GAIN_MUTE,
+ AC_AMP_GET_INPUT);
+
+ if (gpio_data == 0x02) {
+ /* unmute line out */
+ snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0],
+ HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
+
+ if (vol_counter & 0x20) {
+ /* decrease volume */
+ if (vol > master_vol)
+ vol = master_vol;
+ snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
+ 0, HDA_AMP_VOLMASK,
+ master_vol-vol);
+ } else {
+ /* increase volume */
+ snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
+ HDA_AMP_VOLMASK,
+ ((master_vol+vol) > 0x2A) ? 0x2A :
+ (master_vol+vol));
+ }
+ } else if (!(gpio_data & 0x02)) {
+ /* mute line out */
+ snd_hda_codec_amp_stereo(codec,
+ spec->autocfg.line_out_pins[0],
+ HDA_OUTPUT, 0, HDA_AMP_MUTE,
+ HDA_AMP_MUTE);
+ }
+}
+
+/* unsolicited event for jack sensing */
+static void via_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ res >>= 26;
+ if (res == VIA_HP_EVENT)
+ via_hp_automute(codec);
+ else if (res == VIA_GPIO_EVENT)
+ via_gpio_control(codec);
+}
+
static int via_init(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
- snd_hda_sequence_write(codec, spec->init_verbs);
+ int i;
+ for (i = 0; i < spec->num_iverbs; i++)
+ snd_hda_sequence_write(codec, spec->init_verbs[i]);
+
/* Lydia Add for EAPD enable */
if (!spec->dig_in_nid) { /* No Digital In connection */
if (IS_VT1708_VENDORID(codec->vendor_id)) {
if (spec->kctl_alloc)
spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
- spec->init_verbs = vt1708_volume_init_verbs;
+ spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs;
spec->input_mux = &spec->private_imux;
{ } /* end */
};
+static struct hda_verb vt1709_uniwill_init_verbs[] = {
+ {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT},
+ { }
+};
+
/*
* generic initialization of ADC, input mixers and output mixers
*/
"Using genenic mode...\n");
}
- spec->init_verbs = vt1709_10ch_volume_init_verbs;
+ spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs;
+ spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
spec->stream_name_analog = "VT1709 Analog";
spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback;
codec->patch_ops = via_patch_ops;
codec->patch_ops.init = via_auto_init;
+ codec->patch_ops.unsol_event = via_unsol_event;
#ifdef CONFIG_SND_HDA_POWER_SAVE
spec->loopback.amplist = vt1709_loopbacks;
#endif
"Using genenic mode...\n");
}
- spec->init_verbs = vt1709_6ch_volume_init_verbs;
+ spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs;
+ spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
spec->stream_name_analog = "VT1709 Analog";
spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback;
codec->patch_ops = via_patch_ops;
codec->patch_ops.init = via_auto_init;
+ codec->patch_ops.unsol_event = via_unsol_event;
#ifdef CONFIG_SND_HDA_POWER_SAVE
spec->loopback.amplist = vt1709_loopbacks;
#endif
{ }
};
+static struct hda_verb vt1708B_uniwill_init_verbs[] = {
+ {0x1D, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT},
+ { }
+};
+
static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = {
.substreams = 1,
.channels_min = 2,
"from BIOS. Using genenic mode...\n");
}
- spec->init_verbs = vt1708B_8ch_volume_init_verbs;
+ spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs;
+ spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
spec->stream_name_analog = "VT1708B Analog";
spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback;
codec->patch_ops = via_patch_ops;
codec->patch_ops.init = via_auto_init;
+ codec->patch_ops.unsol_event = via_unsol_event;
#ifdef CONFIG_SND_HDA_POWER_SAVE
spec->loopback.amplist = vt1708B_loopbacks;
#endif
"from BIOS. Using genenic mode...\n");
}
- spec->init_verbs = vt1708B_4ch_volume_init_verbs;
+ spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs;
+ spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
spec->stream_name_analog = "VT1708B Analog";
spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback;
codec->patch_ops = via_patch_ops;
codec->patch_ops.init = via_auto_init;
+ codec->patch_ops.unsol_event = via_unsol_event;
#ifdef CONFIG_SND_HDA_POWER_SAVE
spec->loopback.amplist = vt1708B_loopbacks;
#endif
{ }
};
+static struct hda_verb vt1708S_uniwill_init_verbs[] = {
+ {0x1D, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT},
+ { }
+};
+
static struct hda_pcm_stream vt1708S_pcm_analog_playback = {
.substreams = 2,
.channels_min = 2,
"from BIOS. Using genenic mode...\n");
}
- spec->init_verbs = vt1708S_volume_init_verbs;
+ spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs;
+ spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs;
spec->stream_name_analog = "VT1708S Analog";
spec->stream_analog_playback = &vt1708S_pcm_analog_playback;
codec->patch_ops = via_patch_ops;
codec->patch_ops.init = via_auto_init;
-
+ codec->patch_ops.unsol_event = via_unsol_event;
#ifdef CONFIG_SND_HDA_POWER_SAVE
spec->loopback.amplist = vt1708S_loopbacks;
#endif
{ }
};
+static struct hda_verb vt1702_uniwill_init_verbs[] = {
+ {0x01, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_GPIO_EVENT},
+ {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT},
+ { }
+};
+
static struct hda_pcm_stream vt1702_pcm_analog_playback = {
.substreams = 1,
.channels_min = 2,
"from BIOS. Using genenic mode...\n");
}
- spec->init_verbs = vt1702_volume_init_verbs;
+ spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs;
+ spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs;
spec->stream_name_analog = "VT1702 Analog";
spec->stream_analog_playback = &vt1702_pcm_analog_playback;
codec->patch_ops = via_patch_ops;
codec->patch_ops.init = via_auto_init;
-
+ codec->patch_ops.unsol_event = via_unsol_event;
#ifdef CONFIG_SND_HDA_POWER_SAVE
spec->loopback.amplist = vt1702_loopbacks;
#endif