};
enum {
+ STAC_AUTO,
STAC_REF,
STAC_9200_OQO,
STAC_9200_DELL_D21,
};
enum {
+ STAC_9205_AUTO,
STAC_9205_REF,
STAC_9205_DELL_M42,
STAC_9205_DELL_M43,
};
enum {
+ STAC_92HD73XX_AUTO,
STAC_92HD73XX_NO_JD, /* no jack-detection */
STAC_92HD73XX_REF,
+ STAC_92HD73XX_INTEL,
STAC_DELL_M6_AMIC,
STAC_DELL_M6_DMIC,
STAC_DELL_M6_BOTH,
};
enum {
+ STAC_92HD83XXX_AUTO,
STAC_92HD83XXX_REF,
STAC_92HD83XXX_PWR_REF,
STAC_DELL_S14,
};
enum {
+ STAC_92HD71BXX_AUTO,
STAC_92HD71BXX_REF,
STAC_DELL_M4_1,
STAC_DELL_M4_2,
STAC_DELL_M4_3,
STAC_HP_M4,
STAC_HP_DV5,
+ STAC_HP_HDX,
+ STAC_HP_DV4_1222NR,
STAC_92HD71BXX_MODELS
};
enum {
+ STAC_925x_AUTO,
STAC_925x_REF,
STAC_M1,
STAC_M1_2,
};
enum {
+ STAC_922X_AUTO,
STAC_D945_REF,
STAC_D945GTP3,
STAC_D945GTP5,
};
enum {
+ STAC_927X_AUTO,
STAC_D965_REF_NO_JD, /* no jack-detection */
STAC_D965_REF,
STAC_D965_3ST,
STAC_D965_5ST,
+ STAC_D965_5ST_NO_FP,
STAC_DELL_3ST,
STAC_DELL_BIOS,
STAC_927X_MODELS
};
+enum {
+ STAC_9872_AUTO,
+ STAC_9872_VAIO,
+ STAC_9872_MODELS
+};
+
struct sigmatel_event {
hda_nid_t nid;
unsigned char type;
unsigned int gpio_dir;
unsigned int gpio_data;
unsigned int gpio_mute;
+ unsigned int gpio_led;
/* stream */
unsigned int stream_delay;
/* analog loopback */
+ struct snd_kcontrol_new *aloopback_ctl;
unsigned char aloopback_mask;
unsigned char aloopback_shift;
/* pin widgets */
hda_nid_t *pin_nids;
unsigned int num_pins;
- unsigned int *pin_configs;
/* codec specific stuff */
struct hda_verb *init;
return 0;
}
+static unsigned int stac92xx_vref_set(struct hda_codec *codec,
+ hda_nid_t nid, unsigned int new_vref)
+{
+ int error;
+ unsigned int pincfg;
+ pincfg = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+
+ pincfg &= 0xff;
+ pincfg &= ~(AC_PINCTL_VREFEN | AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
+ pincfg |= new_vref;
+
+ if (new_vref == AC_PINCTL_VREF_HIZ)
+ pincfg |= AC_PINCTL_OUT_EN;
+ else
+ pincfg |= AC_PINCTL_IN_EN;
+
+ error = snd_hda_codec_write_cache(codec, nid, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, pincfg);
+ if (error < 0)
+ return error;
+ else
+ return 1;
+}
+
+static unsigned int stac92xx_vref_get(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int vref;
+ vref = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ vref &= AC_PINCTL_VREFEN;
+ return vref;
+}
+
static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
.private_value = verb_read | (verb_write << 16), \
}
+#define DC_BIAS(xname, idx, nid) \
+ { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .index = idx, \
+ .info = stac92xx_dc_bias_info, \
+ .get = stac92xx_dc_bias_get, \
+ .put = stac92xx_dc_bias_put, \
+ .private_value = nid, \
+ }
+
static struct snd_kcontrol_new stac9200_mixer[] = {
HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT),
HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT),
HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT),
- STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3),
-
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
{ } /* end */
};
-static struct snd_kcontrol_new stac92hd73xx_8ch_mixer[] = {
+static struct snd_kcontrol_new stac92hd73xx_6ch_loopback[] = {
+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3),
+ {}
+};
+
+static struct snd_kcontrol_new stac92hd73xx_8ch_loopback[] = {
STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 4),
+ {}
+};
+
+static struct snd_kcontrol_new stac92hd73xx_10ch_loopback[] = {
+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5),
+ {}
+};
+static struct snd_kcontrol_new stac92hd73xx_8ch_mixer[] = {
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
};
static struct snd_kcontrol_new stac92hd73xx_10ch_mixer[] = {
- STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 5),
-
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
};
static struct snd_kcontrol_new stac92hd71bxx_analog_mixer[] = {
- STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2),
-
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT),
{ } /* end */
};
-static struct snd_kcontrol_new stac92hd71bxx_mixer[] = {
- STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2),
+static struct snd_kcontrol_new stac92hd71bxx_loopback[] = {
+ STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2)
+};
+static struct snd_kcontrol_new stac92hd71bxx_mixer[] = {
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT),
};
static struct snd_kcontrol_new stac9205_mixer[] = {
- STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0, 1),
-
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1b, 0x0, HDA_INPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1d, 0x0, HDA_OUTPUT),
{ } /* end */
};
+static struct snd_kcontrol_new stac9205_loopback[] = {
+ STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0, 1),
+ {}
+};
+
/* This needs to be generated dynamically based on sequence */
static struct snd_kcontrol_new stac922x_mixer[] = {
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x17, 0x0, HDA_INPUT),
static struct snd_kcontrol_new stac927x_mixer[] = {
- STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1),
-
HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x18, 0x0, HDA_INPUT),
HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1b, 0x0, HDA_OUTPUT),
{ } /* end */
};
+static struct snd_kcontrol_new stac927x_loopback[] = {
+ STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1),
+ {}
+};
+
static struct snd_kcontrol_new stac_dmux_mixer = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Digital Input Source",
"LFE Playback Volume",
"Side Playback Volume",
"Headphone Playback Volume",
- "Headphone Playback Volume",
"Speaker Playback Volume",
- "External Speaker Playback Volume",
- "Speaker2 Playback Volume",
NULL
};
"LFE Playback Switch",
"Side Playback Switch",
"Headphone Playback Switch",
- "Headphone Playback Switch",
"Speaker Playback Switch",
- "External Speaker Playback Switch",
- "Speaker2 Playback Switch",
"IEC958 Playback Switch",
NULL
};
return err;
}
+ if (spec->aloopback_ctl &&
+ snd_hda_get_bool_hint(codec, "loopback") == 1) {
+ err = snd_hda_add_new_ctls(codec, spec->aloopback_ctl);
+ if (err < 0)
+ return err;
+ }
+
stac92xx_free_kctls(codec); /* no longer needed */
/* create jack input elements */
};
static const char *stac9200_models[STAC_9200_MODELS] = {
+ [STAC_AUTO] = "auto",
[STAC_REF] = "ref",
[STAC_9200_OQO] = "oqo",
[STAC_9200_DELL_D21] = "dell-d21",
};
static const char *stac925x_models[STAC_925x_MODELS] = {
+ [STAC_925x_AUTO] = "auto",
[STAC_REF] = "ref",
[STAC_M1] = "m1",
[STAC_M1_2] = "m1-2",
};
static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = {
+ [STAC_92HD73XX_AUTO] = "auto",
[STAC_92HD73XX_NO_JD] = "no-jd",
[STAC_92HD73XX_REF] = "ref",
+ [STAC_92HD73XX_INTEL] = "intel",
[STAC_DELL_M6_AMIC] = "dell-m6-amic",
[STAC_DELL_M6_DMIC] = "dell-m6-dmic",
[STAC_DELL_M6_BOTH] = "dell-m6",
"DFI LanParty", STAC_92HD73XX_REF),
SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
"DFI LanParty", STAC_92HD73XX_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5002,
+ "Intel DG45ID", STAC_92HD73XX_INTEL),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x5003,
+ "Intel DG45FC", STAC_92HD73XX_INTEL),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0254,
"Dell Studio 1535", STAC_DELL_M6_DMIC),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0255,
"Dell Studio 1537", STAC_DELL_M6_DMIC),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02a0,
"Dell Studio 17", STAC_DELL_M6_DMIC),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02be,
+ "Dell Studio 1555", STAC_DELL_M6_DMIC),
{} /* terminator */
};
};
static const char *stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = {
+ [STAC_92HD83XXX_AUTO] = "auto",
[STAC_92HD83XXX_REF] = "ref",
[STAC_92HD83XXX_PWR_REF] = "mic-ref",
[STAC_DELL_S14] = "dell-s14",
[STAC_DELL_M4_3] = dell_m4_3_pin_configs,
[STAC_HP_M4] = NULL,
[STAC_HP_DV5] = NULL,
+ [STAC_HP_HDX] = NULL,
+ [STAC_HP_DV4_1222NR] = NULL,
};
static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = {
+ [STAC_92HD71BXX_AUTO] = "auto",
[STAC_92HD71BXX_REF] = "ref",
[STAC_DELL_M4_1] = "dell-m4-1",
[STAC_DELL_M4_2] = "dell-m4-2",
[STAC_DELL_M4_3] = "dell-m4-3",
[STAC_HP_M4] = "hp-m4",
[STAC_HP_DV5] = "hp-dv5",
+ [STAC_HP_HDX] = "hp-hdx",
+ [STAC_HP_DV4_1222NR] = "hp-dv4-1222nr",
};
static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
"DFI LanParty", STAC_92HD71BXX_REF),
SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
"DFI LanParty", STAC_92HD71BXX_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30fb,
+ "HP dv4-1222nr", STAC_HP_DV4_1222NR),
+ SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3080,
+ "HP", STAC_HP_DV5),
SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x30f0,
"HP dv4-7", STAC_HP_DV5),
SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3600,
"HP dv4-7", STAC_HP_DV5),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x3610,
+ "HP HDX", STAC_HP_HDX), /* HDX18 */
SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361a,
"HP mini 1000", STAC_HP_M4),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361b,
+ "HP HDX", STAC_HP_HDX), /* HDX16 */
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233,
"unknown Dell", STAC_DELL_M4_1),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0234,
};
static const char *stac922x_models[STAC_922X_MODELS] = {
+ [STAC_922X_AUTO] = "auto",
[STAC_D945_REF] = "ref",
[STAC_D945GTP5] = "5stack",
[STAC_D945GTP3] = "3stack",
0x40000100, 0x40000100
};
+static unsigned int d965_5st_no_fp_pin_configs[14] = {
+ 0x40000100, 0x40000100, 0x0181304e, 0x01014010,
+ 0x01a19040, 0x01011012, 0x01016011, 0x40000100,
+ 0x40000100, 0x40000100, 0x40000100, 0x01442070,
+ 0x40000100, 0x40000100
+};
+
static unsigned int dell_3st_pin_configs[14] = {
0x02211230, 0x02a11220, 0x01a19040, 0x01114210,
0x01111212, 0x01116211, 0x01813050, 0x01112214,
[STAC_D965_REF] = ref927x_pin_configs,
[STAC_D965_3ST] = d965_3st_pin_configs,
[STAC_D965_5ST] = d965_5st_pin_configs,
+ [STAC_D965_5ST_NO_FP] = d965_5st_no_fp_pin_configs,
[STAC_DELL_3ST] = dell_3st_pin_configs,
[STAC_DELL_BIOS] = NULL,
};
static const char *stac927x_models[STAC_927X_MODELS] = {
+ [STAC_927X_AUTO] = "auto",
[STAC_D965_REF_NO_JD] = "ref-no-jd",
[STAC_D965_REF] = "ref",
[STAC_D965_3ST] = "3stack",
[STAC_D965_5ST] = "5stack",
+ [STAC_D965_5ST_NO_FP] = "5stack-no-fp",
[STAC_DELL_3ST] = "dell-3stack",
[STAC_DELL_BIOS] = "dell-bios",
};
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f3, "Dell Inspiron 1420", STAC_DELL_BIOS),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0227, "Dell Vostro 1400 ", STAC_DELL_BIOS),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022e, "Dell ", STAC_DELL_BIOS),
- SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022f, "Dell Inspiron 1525", STAC_DELL_3ST),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022f, "Dell Inspiron 1525", STAC_DELL_BIOS),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0242, "Dell ", STAC_DELL_BIOS),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0243, "Dell ", STAC_DELL_BIOS),
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ff, "Dell ", STAC_DELL_BIOS),
};
static const char *stac9205_models[STAC_9205_MODELS] = {
+ [STAC_9205_AUTO] = "auto",
[STAC_9205_REF] = "ref",
[STAC_9205_DELL_M42] = "dell-m42",
[STAC_9205_DELL_M43] = "dell-m43",
/* SigmaTel reference board */
SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
"DFI LanParty", STAC_9205_REF),
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0xfb30,
+ "SigmaTel", STAC_9205_REF),
SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
"DFI LanParty", STAC_9205_REF),
/* Dell */
SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0228,
"Dell Vostro 1500", STAC_9205_DELL_M42),
/* Gateway */
+ SND_PCI_QUIRK(0x107b, 0x0560, "Gateway T6834c", STAC_9205_EAPD),
SND_PCI_QUIRK(0x107b, 0x0565, "Gateway T1616", STAC_9205_EAPD),
{} /* terminator */
};
-static int stac92xx_save_bios_config_regs(struct hda_codec *codec)
-{
- int i;
- struct sigmatel_spec *spec = codec->spec;
-
- kfree(spec->pin_configs);
- spec->pin_configs = kcalloc(spec->num_pins, sizeof(*spec->pin_configs),
- GFP_KERNEL);
- if (!spec->pin_configs)
- return -ENOMEM;
-
- for (i = 0; i < spec->num_pins; i++) {
- hda_nid_t nid = spec->pin_nids[i];
- unsigned int pin_cfg;
-
- if (!nid)
- continue;
- pin_cfg = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONFIG_DEFAULT, 0x00);
- snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x bios pin config %8.8x\n",
- nid, pin_cfg);
- spec->pin_configs[i] = pin_cfg;
- }
-
- return 0;
-}
-
-static void stac92xx_set_config_reg(struct hda_codec *codec,
- hda_nid_t pin_nid, unsigned int pin_config)
-{
- int i;
- snd_hda_codec_write(codec, pin_nid, 0,
- AC_VERB_SET_CONFIG_DEFAULT_BYTES_0,
- pin_config & 0x000000ff);
- snd_hda_codec_write(codec, pin_nid, 0,
- AC_VERB_SET_CONFIG_DEFAULT_BYTES_1,
- (pin_config & 0x0000ff00) >> 8);
- snd_hda_codec_write(codec, pin_nid, 0,
- AC_VERB_SET_CONFIG_DEFAULT_BYTES_2,
- (pin_config & 0x00ff0000) >> 16);
- snd_hda_codec_write(codec, pin_nid, 0,
- AC_VERB_SET_CONFIG_DEFAULT_BYTES_3,
- pin_config >> 24);
- i = snd_hda_codec_read(codec, pin_nid, 0,
- AC_VERB_GET_CONFIG_DEFAULT,
- 0x00);
- snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x pin config %8.8x\n",
- pin_nid, i);
-}
-
-static void stac92xx_set_config_regs(struct hda_codec *codec)
+static void stac92xx_set_config_regs(struct hda_codec *codec,
+ unsigned int *pincfgs)
{
int i;
struct sigmatel_spec *spec = codec->spec;
- if (!spec->pin_configs)
- return;
+ if (!pincfgs)
+ return;
for (i = 0; i < spec->num_pins; i++)
- if (spec->pin_nids[i] && spec->pin_configs[i])
- stac92xx_set_config_reg(codec, spec->pin_nids[i],
- spec->pin_configs[i]);
-}
-
-static int stac_save_pin_cfgs(struct hda_codec *codec, unsigned int *pins)
-{
- struct sigmatel_spec *spec = codec->spec;
-
- if (!pins)
- return stac92xx_save_bios_config_regs(codec);
-
- kfree(spec->pin_configs);
- spec->pin_configs = kmemdup(pins,
- spec->num_pins * sizeof(*pins),
- GFP_KERNEL);
- if (!spec->pin_configs)
- return -ENOMEM;
-
- stac92xx_set_config_regs(codec);
- return 0;
-}
-
-static void stac_change_pin_config(struct hda_codec *codec, hda_nid_t nid,
- unsigned int cfg)
-{
- struct sigmatel_spec *spec = codec->spec;
- int i;
-
- for (i = 0; i < spec->num_pins; i++) {
- if (spec->pin_nids[i] == nid) {
- spec->pin_configs[i] = cfg;
- stac92xx_set_config_reg(codec, nid, cfg);
- break;
- }
- }
+ if (spec->pin_nids[i] && pincfgs[i])
+ snd_hda_codec_set_pincfg(codec, spec->pin_nids[i],
+ pincfgs[i]);
}
/*
return 0;
}
-static unsigned int stac92xx_get_vref(struct hda_codec *codec, hda_nid_t nid)
+static unsigned int stac92xx_get_default_vref(struct hda_codec *codec,
+ hda_nid_t nid)
{
- unsigned int pincap = snd_hda_param_read(codec, nid,
- AC_PAR_PIN_CAP);
+ unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
if (pincap & AC_PINCAP_VREF_100)
return AC_PINCTL_VREF_100;
return 1;
}
-#define stac92xx_io_switch_info snd_ctl_boolean_mono_info
+static int stac92xx_dc_bias_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ int i;
+ static char *texts[] = {
+ "Mic In", "Line In", "Line Out"
+ };
+
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+ hda_nid_t nid = kcontrol->private_value;
+
+ if (nid == spec->mic_switch || nid == spec->line_switch)
+ i = 3;
+ else
+ i = 2;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->value.enumerated.items = i;
+ uinfo->count = 1;
+ if (uinfo->value.enumerated.item >= i)
+ uinfo->value.enumerated.item = i-1;
+ strcpy(uinfo->value.enumerated.name,
+ texts[uinfo->value.enumerated.item]);
+
+ return 0;
+}
+
+static int stac92xx_dc_bias_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 vref = stac92xx_vref_get(codec, nid);
+
+ if (vref == stac92xx_get_default_vref(codec, nid))
+ ucontrol->value.enumerated.item[0] = 0;
+ else if (vref == AC_PINCTL_VREF_GRD)
+ ucontrol->value.enumerated.item[0] = 1;
+ else if (vref == AC_PINCTL_VREF_HIZ)
+ ucontrol->value.enumerated.item[0] = 2;
+
+ return 0;
+}
+
+static int stac92xx_dc_bias_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ unsigned int new_vref = 0;
+ int error;
+ hda_nid_t nid = kcontrol->private_value;
+
+ if (ucontrol->value.enumerated.item[0] == 0)
+ new_vref = stac92xx_get_default_vref(codec, nid);
+ else if (ucontrol->value.enumerated.item[0] == 1)
+ new_vref = AC_PINCTL_VREF_GRD;
+ else if (ucontrol->value.enumerated.item[0] == 2)
+ new_vref = AC_PINCTL_VREF_HIZ;
+ else
+ return 0;
+
+ if (new_vref != stac92xx_vref_get(codec, nid)) {
+ error = stac92xx_vref_set(codec, nid, new_vref);
+ return error;
+ }
+
+ return 0;
+}
+
+static int stac92xx_io_switch_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ static char *texts[2];
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (kcontrol->private_value == spec->line_switch)
+ texts[0] = "Line In";
+ else
+ texts[0] = "Mic In";
+ texts[1] = "Line Out";
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->value.enumerated.items = 2;
+ uinfo->count = 1;
+
+ 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 stac92xx_io_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
- int io_idx = kcontrol-> private_value & 0xff;
+ hda_nid_t nid = kcontrol->private_value;
+ int io_idx = (nid == spec->mic_switch) ? 1 : 0;
- ucontrol->value.integer.value[0] = spec->io_switch[io_idx];
+ ucontrol->value.enumerated.item[0] = spec->io_switch[io_idx];
return 0;
}
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct sigmatel_spec *spec = codec->spec;
- hda_nid_t nid = kcontrol->private_value >> 8;
- int io_idx = kcontrol-> private_value & 0xff;
- unsigned short val = !!ucontrol->value.integer.value[0];
+ hda_nid_t nid = kcontrol->private_value;
+ int io_idx = (nid == spec->mic_switch) ? 1 : 0;
+ unsigned short val = !!ucontrol->value.enumerated.item[0];
spec->io_switch[io_idx] = val;
else {
unsigned int pinctl = AC_PINCTL_IN_EN;
if (io_idx) /* set VREF for mic */
- pinctl |= stac92xx_get_vref(codec, nid);
+ pinctl |= stac92xx_get_default_vref(codec, nid);
stac92xx_auto_set_pinctl(codec, nid, pinctl);
}
STAC_CTL_WIDGET_AMP_VOL,
STAC_CTL_WIDGET_HP_SWITCH,
STAC_CTL_WIDGET_IO_SWITCH,
- STAC_CTL_WIDGET_CLFE_SWITCH
+ STAC_CTL_WIDGET_CLFE_SWITCH,
+ STAC_CTL_WIDGET_DC_BIAS
};
static struct snd_kcontrol_new stac92xx_control_templates[] = {
STAC_CODEC_HP_SWITCH(NULL),
STAC_CODEC_IO_SWITCH(NULL, 0),
STAC_CODEC_CLFE_SWITCH(NULL, 0),
+ DC_BIAS(NULL, 0, 0),
};
/* add dynamic controls */
.put = stac92xx_mux_enum_put,
};
+static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec,
+ hda_nid_t nid, int idx)
+{
+ int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+ int control = 0;
+ struct sigmatel_spec *spec = codec->spec;
+ char name[22];
+
+ if (!((get_defcfg_connect(def_conf)) & AC_JACK_PORT_FIXED)) {
+ if (stac92xx_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD
+ && nid == spec->line_switch)
+ control = STAC_CTL_WIDGET_IO_SWITCH;
+ else if (snd_hda_query_pin_caps(codec, nid)
+ & (AC_PINCAP_VREF_GRD << AC_PINCAP_VREF_SHIFT))
+ control = STAC_CTL_WIDGET_DC_BIAS;
+ else if (nid == spec->mic_switch)
+ control = STAC_CTL_WIDGET_IO_SWITCH;
+ }
+
+ if (control) {
+ strcpy(name, auto_pin_cfg_labels[idx]);
+ return stac92xx_add_control(codec->spec, control,
+ strcat(name, " Jack Mode"), nid);
+ }
+
+ return 0;
+}
+
static int stac92xx_add_input_source(struct sigmatel_spec *spec)
{
struct snd_kcontrol_new *knew;
if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
return 0;
nid = cfg->input_pins[AUTO_PIN_LINE];
- pincap = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+ pincap = snd_hda_query_pin_caps(codec, nid);
if (pincap & AC_PINCAP_OUT)
return nid;
return 0;
mic_pin = AUTO_PIN_MIC;
for (;;) {
hda_nid_t nid = cfg->input_pins[mic_pin];
- def_conf = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONFIG_DEFAULT, 0);
+ def_conf = snd_hda_codec_get_pincfg(codec, nid);
/* some laptops have an internal analog microphone
* which can't be used as a output */
if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) {
- pincap = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+ pincap = snd_hda_query_pin_caps(codec, nid);
if (pincap & AC_PINCAP_OUT)
return nid;
}
conn_len = snd_hda_get_connections(codec, nid, conn,
HDA_MAX_CONNECTIONS);
for (j = 0; j < conn_len; j++) {
- wcaps = snd_hda_param_read(codec, conn[j],
- AC_PAR_AUDIO_WIDGET_CAP);
+ wcaps = get_wcaps(codec, conn[j]);
wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
/* we check only analog outputs */
if (wtype != AC_WID_AUD_OUT || (wcaps & AC_WCAP_DIGITAL))
return conn[j];
}
}
+ /* if all DACs are already assigned, connect to the primary DAC */
+ if (conn_len > 1) {
+ for (j = 0; j < conn_len; j++) {
+ if (conn[j] == spec->multiout.dac_nids[0]) {
+ snd_hda_codec_write_cache(codec, nid, 0,
+ AC_VERB_SET_CONNECT_SEL, j);
+ break;
+ }
+ }
+ }
return 0;
}
add_spec_dacs(spec, dac);
}
+ for (i = 0; i < cfg->hp_outs; i++) {
+ nid = cfg->hp_pins[i];
+ dac = get_unassigned_dac(codec, nid);
+ if (dac) {
+ if (!spec->multiout.hp_nid)
+ spec->multiout.hp_nid = dac;
+ else
+ add_spec_extra_dacs(spec, dac);
+ }
+ spec->hp_dacs[i] = dac;
+ }
+
+ for (i = 0; i < cfg->speaker_outs; i++) {
+ nid = cfg->speaker_pins[i];
+ dac = get_unassigned_dac(codec, nid);
+ if (dac)
+ add_spec_extra_dacs(spec, dac);
+ spec->speaker_dacs[i] = dac;
+ }
+
/* add line-in as output */
nid = check_line_out_switch(codec);
if (nid) {
}
}
- for (i = 0; i < cfg->hp_outs; i++) {
- nid = cfg->hp_pins[i];
- dac = get_unassigned_dac(codec, nid);
- if (dac) {
- if (!spec->multiout.hp_nid)
- spec->multiout.hp_nid = dac;
- else
- add_spec_extra_dacs(spec, dac);
- }
- spec->hp_dacs[i] = dac;
- }
-
- for (i = 0; i < cfg->speaker_outs; i++) {
- nid = cfg->speaker_pins[i];
- dac = get_unassigned_dac(codec, nid);
- if (dac)
- add_spec_extra_dacs(spec, dac);
- spec->speaker_dacs[i] = dac;
- }
-
snd_printd("stac92xx: dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
spec->multiout.num_dacs,
spec->multiout.dac_nids[0],
}
/* create volume control/switch for the given prefx type */
-static int create_controls(struct hda_codec *codec, const char *pfx,
- hda_nid_t nid, int chs)
+static int create_controls_idx(struct hda_codec *codec, const char *pfx,
+ int idx, hda_nid_t nid, int chs)
{
struct sigmatel_spec *spec = codec->spec;
char name[32];
}
sprintf(name, "%s Playback Volume", pfx);
- err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, name,
+ err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_VOL, idx, name,
HDA_COMPOSE_AMP_VAL_OFS(nid, chs, 0, HDA_OUTPUT,
spec->volume_offset));
if (err < 0)
return err;
sprintf(name, "%s Playback Switch", pfx);
- err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, name,
+ err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_MUTE, idx, name,
HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
if (err < 0)
return err;
return 0;
}
+#define create_controls(codec, pfx, nid, chs) \
+ create_controls_idx(codec, pfx, 0, nid, chs)
+
static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
{
if (spec->multiout.num_dacs > 4) {
return 1;
}
-static int is_unique_dac(struct sigmatel_spec *spec, hda_nid_t nid)
-{
- int i;
-
- if (spec->autocfg.line_outs != 1)
- return 0;
- if (spec->multiout.hp_nid == nid)
- return 0;
- for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++)
- if (spec->multiout.extra_out_nid[i] == nid)
- return 0;
- return 1;
-}
-
-/* add playback controls from the parsed DAC table */
-static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
+/* Create output controls
+ * The mixer elements are named depending on the given type (AUTO_PIN_XXX_OUT)
+ */
+static int create_multi_out_ctls(struct hda_codec *codec, int num_outs,
+ const hda_nid_t *pins,
+ const hda_nid_t *dac_nids,
+ int type)
{
struct sigmatel_spec *spec = codec->spec;
static const char *chname[4] = {
"Front", "Surround", NULL /*CLFE*/, "Side"
};
- hda_nid_t nid = 0;
+ hda_nid_t nid;
int i, err;
unsigned int wid_caps;
- for (i = 0; i < cfg->line_outs && spec->multiout.dac_nids[i]; i++) {
- nid = spec->multiout.dac_nids[i];
- if (i == 2) {
+ for (i = 0; i < num_outs && i < ARRAY_SIZE(chname); i++) {
+ if (type == AUTO_PIN_HP_OUT && !spec->hp_detect) {
+ wid_caps = get_wcaps(codec, pins[i]);
+ if (wid_caps & AC_WCAP_UNSOL_CAP)
+ spec->hp_detect = 1;
+ }
+ nid = dac_nids[i];
+ if (!nid)
+ continue;
+ if (type != AUTO_PIN_HP_OUT && i == 2) {
/* Center/LFE */
err = create_controls(codec, "Center", nid, 1);
if (err < 0)
}
} else {
- const char *name = chname[i];
- /* if it's a single DAC, assign a better name */
- if (!i && is_unique_dac(spec, nid)) {
- switch (cfg->line_out_type) {
- case AUTO_PIN_HP_OUT:
- name = "Headphone";
- break;
- case AUTO_PIN_SPEAKER_OUT:
- name = "Speaker";
- break;
- }
+ const char *name;
+ int idx;
+ switch (type) {
+ case AUTO_PIN_HP_OUT:
+ name = "Headphone";
+ idx = i;
+ break;
+ case AUTO_PIN_SPEAKER_OUT:
+ name = "Speaker";
+ idx = i;
+ break;
+ default:
+ name = chname[i];
+ idx = 0;
+ break;
}
- err = create_controls(codec, name, nid, 3);
+ err = create_controls_idx(codec, name, idx, nid, 3);
if (err < 0)
return err;
}
}
+ return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
+ const struct auto_pin_cfg *cfg)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ hda_nid_t nid;
+ int err;
+ int idx;
+
+ err = create_multi_out_ctls(codec, cfg->line_outs, cfg->line_out_pins,
+ spec->multiout.dac_nids,
+ cfg->line_out_type);
+ if (err < 0)
+ return err;
if (cfg->hp_outs > 1 && cfg->line_out_type == AUTO_PIN_LINE_OUT) {
err = stac92xx_add_control(spec,
return err;
}
- if (spec->line_switch) {
- err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH,
- "Line In as Output Switch",
- spec->line_switch << 8);
- if (err < 0)
- return err;
- }
-
- if (spec->mic_switch) {
- err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH,
- "Mic as Output Switch",
- (spec->mic_switch << 8) | 1);
- if (err < 0)
- return err;
+ for (idx = AUTO_PIN_MIC; idx <= AUTO_PIN_FRONT_LINE; idx++) {
+ nid = cfg->input_pins[idx];
+ if (nid) {
+ err = stac92xx_add_jack_mode_control(codec, nid, idx);
+ if (err < 0)
+ return err;
+ }
}
return 0;
struct auto_pin_cfg *cfg)
{
struct sigmatel_spec *spec = codec->spec;
- hda_nid_t nid;
- int i, err, nums;
+ int err;
+
+ err = create_multi_out_ctls(codec, cfg->hp_outs, cfg->hp_pins,
+ spec->hp_dacs, AUTO_PIN_HP_OUT);
+ if (err < 0)
+ return err;
+
+ err = create_multi_out_ctls(codec, cfg->speaker_outs, cfg->speaker_pins,
+ spec->speaker_dacs, AUTO_PIN_SPEAKER_OUT);
+ if (err < 0)
+ return err;
- nums = 0;
- for (i = 0; i < cfg->hp_outs; i++) {
- static const char *pfxs[] = {
- "Headphone", "Headphone2", "Headphone3",
- };
- unsigned int wid_caps = get_wcaps(codec, cfg->hp_pins[i]);
- if (wid_caps & AC_WCAP_UNSOL_CAP)
- spec->hp_detect = 1;
- if (nums >= ARRAY_SIZE(pfxs))
- continue;
- nid = spec->hp_dacs[i];
- if (!nid)
- continue;
- err = create_controls(codec, pfxs[nums++], nid, 3);
- if (err < 0)
- return err;
- }
- nums = 0;
- for (i = 0; i < cfg->speaker_outs; i++) {
- static const char *pfxs[] = {
- "Speaker", "External Speaker", "Speaker2",
- };
- if (nums >= ARRAY_SIZE(pfxs))
- continue;
- nid = spec->speaker_dacs[i];
- if (!nid)
- continue;
- err = create_controls(codec, pfxs[nums++], nid, 3);
- if (err < 0)
- return err;
- }
return 0;
}
unsigned int wcaps;
unsigned int def_conf;
- def_conf = snd_hda_codec_read(codec,
- spec->dmic_nids[i],
- 0,
- AC_VERB_GET_CONFIG_DEFAULT,
- 0);
+ def_conf = snd_hda_codec_get_pincfg(codec, spec->dmic_nids[i]);
if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
continue;
static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out, hda_nid_t dig_in)
{
struct sigmatel_spec *spec = codec->spec;
+ int hp_swap = 0;
int err;
if ((err = snd_hda_parse_pin_def_config(codec,
spec->autocfg.line_outs = spec->autocfg.hp_outs;
spec->autocfg.line_out_type = AUTO_PIN_HP_OUT;
spec->autocfg.hp_outs = 0;
+ hp_swap = 1;
}
if (spec->autocfg.mono_out_pin) {
int dir = get_wcaps(codec, spec->autocfg.mono_out_pin) &
err = snd_hda_attach_beep_device(codec, nid);
if (err < 0)
return err;
+ /* IDT/STAC codecs have linear beep tone parameter */
+ codec->beep->linear_tone = 1;
/* if no beep switch is available, make its own one */
caps = query_amp_caps(codec, nid, HDA_OUTPUT);
if (codec->beep &&
#endif
err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg);
-
if (err < 0)
return err;
- err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg);
+ /* All output parsing done, now restore the swapped hp pins */
+ if (hp_swap) {
+ memcpy(spec->autocfg.hp_pins, spec->autocfg.line_out_pins,
+ sizeof(spec->autocfg.hp_pins));
+ spec->autocfg.hp_outs = spec->autocfg.line_outs;
+ spec->autocfg.line_out_type = AUTO_PIN_HP_OUT;
+ spec->autocfg.line_outs = 0;
+ }
+ err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
for (i = 0; i < spec->autocfg.line_outs && lfe_pin == 0x0; i++) {
hda_nid_t pin = spec->autocfg.line_out_pins[i];
unsigned int defcfg;
- defcfg = snd_hda_codec_read(codec, pin, 0,
- AC_VERB_GET_CONFIG_DEFAULT,
- 0x00);
+ defcfg = snd_hda_codec_get_pincfg(codec, pin);
if (get_defcfg_device(defcfg) == AC_JACK_SPEAKER) {
unsigned int wcaps = get_wcaps(codec, pin);
wcaps &= (AC_WCAP_STEREO | AC_WCAP_OUT_AMP);
AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */
}
+#ifdef CONFIG_SND_HDA_INPUT_JACK
+static void stac92xx_free_jack_priv(struct snd_jack *jack)
+{
+ struct sigmatel_jack *jacks = jack->private_data;
+ jacks->nid = 0;
+ jacks->jack = NULL;
+}
+#endif
+
static int stac92xx_add_jack(struct hda_codec *codec,
hda_nid_t nid, int type)
{
-#ifdef CONFIG_SND_JACK
+#ifdef CONFIG_SND_HDA_INPUT_JACK
struct sigmatel_spec *spec = codec->spec;
struct sigmatel_jack *jack;
- int def_conf = snd_hda_codec_read(codec, nid,
- 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+ int def_conf = snd_hda_codec_get_pincfg(codec, nid);
int connectivity = get_defcfg_connect(def_conf);
char name[32];
+ int err;
if (connectivity && connectivity != AC_JACK_PORT_FIXED)
return 0;
jack->nid = nid;
jack->type = type;
- sprintf(name, "%s at %s %s Jack",
+ snprintf(name, sizeof(name), "%s at %s %s Jack",
snd_hda_get_jack_type(def_conf),
snd_hda_get_jack_connectivity(def_conf),
snd_hda_get_jack_location(def_conf));
- return snd_jack_new(codec->bus->card, name, type, &jack->jack);
-#else
- return 0;
+ err = snd_jack_new(codec->bus->card, name, type, &jack->jack);
+ if (err < 0) {
+ jack->nid = 0;
+ return err;
+ }
+ jack->jack->private_data = jack;
+ jack->jack->private_free = stac92xx_free_jack_priv;
#endif
+ return 0;
}
static int stac_add_event(struct sigmatel_spec *spec, hda_nid_t nid,
static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid,
int enable);
+/* override some hints from the hwdep entry */
+static void stac_store_hints(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ const char *p;
+ int val;
+
+ val = snd_hda_get_bool_hint(codec, "hp_detect");
+ if (val >= 0)
+ spec->hp_detect = val;
+ p = snd_hda_get_hint(codec, "gpio_mask");
+ if (p) {
+ spec->gpio_mask = simple_strtoul(p, NULL, 0);
+ spec->eapd_mask = spec->gpio_dir = spec->gpio_data =
+ spec->gpio_mask;
+ }
+ p = snd_hda_get_hint(codec, "gpio_dir");
+ if (p)
+ spec->gpio_dir = simple_strtoul(p, NULL, 0) & spec->gpio_mask;
+ p = snd_hda_get_hint(codec, "gpio_data");
+ if (p)
+ spec->gpio_data = simple_strtoul(p, NULL, 0) & spec->gpio_mask;
+ p = snd_hda_get_hint(codec, "eapd_mask");
+ if (p)
+ spec->eapd_mask = simple_strtoul(p, NULL, 0) & spec->gpio_mask;
+ val = snd_hda_get_bool_hint(codec, "eapd_switch");
+ if (val >= 0)
+ spec->eapd_switch = val;
+}
+
static int stac92xx_init(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
spec->adc_nids[i], 0,
AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
+ /* override some hints */
+ stac_store_hints(codec);
+
/* set up GPIO */
gpio = spec->gpio_data;
/* turn on EAPD statically when spec->eapd_switch isn't set.
unsigned int pinctl, conf;
if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC) {
/* for mic pins, force to initialize */
- pinctl = stac92xx_get_vref(codec, nid);
+ pinctl = stac92xx_get_default_vref(codec, nid);
pinctl |= AC_PINCTL_IN_EN;
stac92xx_auto_set_pinctl(codec, nid, pinctl);
} else {
pinctl = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
/* if PINCTL already set then skip */
- if (!(pinctl & AC_PINCTL_IN_EN)) {
+ /* Also, if both INPUT and OUTPUT are set,
+ * it must be a BIOS bug; need to override, too
+ */
+ if (!(pinctl & AC_PINCTL_IN_EN) ||
+ (pinctl & AC_PINCTL_OUT_EN)) {
+ pinctl &= ~AC_PINCTL_OUT_EN;
pinctl |= AC_PINCTL_IN_EN;
stac92xx_auto_set_pinctl(codec, nid,
pinctl);
}
}
- conf = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONFIG_DEFAULT, 0);
+ conf = snd_hda_codec_get_pincfg(codec, nid);
if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) {
enable_pin_detect(codec, nid,
STAC_INSERT_EVENT);
stac_toggle_power_map(codec, nid, 1);
continue;
}
- def_conf = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONFIG_DEFAULT, 0);
+ def_conf = snd_hda_codec_get_pincfg(codec, nid);
def_conf = get_defcfg_connect(def_conf);
/* skip any ports that don't have jacks since presence
* detection is useless */
static void stac92xx_free_jacks(struct hda_codec *codec)
{
-#ifdef CONFIG_SND_JACK
+#ifdef CONFIG_SND_HDA_INPUT_JACK
/* free jack instances manually when clearing/reconfiguring */
struct sigmatel_spec *spec = codec->spec;
if (!codec->bus->shutdown && spec->jacks.list) {
struct sigmatel_jack *jacks = spec->jacks.list;
int i;
- for (i = 0; i < spec->jacks.used; i++)
- snd_device_free(codec->bus->card, &jacks[i].jack);
+ for (i = 0; i < spec->jacks.used; i++, jacks++) {
+ if (jacks->jack)
+ snd_device_free(codec->bus->card, jacks->jack);
+ }
}
snd_array_free(&spec->jacks);
#endif
if (! spec)
return;
- kfree(spec->pin_configs);
stac92xx_free_jacks(codec);
snd_array_free(&spec->events);
if (spec->num_pwrs > 0)
stac92xx_pin_sense(codec, event->nid);
stac92xx_report_jack(codec, event->nid);
+
+ switch (codec->subsystem_id) {
+ case 0x103c308f:
+ if (event->nid == 0xb) {
+ int pin = AC_PINCTL_IN_EN;
+
+ if (get_pin_presence(codec, 0xa)
+ && get_pin_presence(codec, 0xb))
+ pin |= AC_PINCTL_VREF_80;
+ if (!get_pin_presence(codec, 0xb))
+ pin |= AC_PINCTL_VREF_80;
+
+ /* toggle VREF state based on mic + hp pin
+ * status
+ */
+ stac92xx_auto_set_pinctl(codec, 0x0a, pin);
+ }
+ }
break;
case STAC_VREF_EVENT:
data = snd_hda_codec_read(codec, codec->afg, 0,
{
struct sigmatel_spec *spec = codec->spec;
- stac92xx_set_config_regs(codec);
stac92xx_init(codec);
snd_hda_codec_resume_amp(codec);
snd_hda_codec_resume_cache(codec);
return 0;
}
+/*
+ * using power check for controlling mute led of HP notebooks
+ * check for mute state only on Speakers (nid = 0x10)
+ *
+ * For this feature CONFIG_SND_HDA_POWER_SAVE is needed, otherwise
+ * the LED is NOT working properly !
+ *
+ * Changed name to reflect that it now works for any designated
+ * model, not just HP HDX.
+ */
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static int stac92xx_hp_check_power_status(struct hda_codec *codec,
+ hda_nid_t nid)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (nid == 0x10) {
+ if (snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) &
+ HDA_AMP_MUTE)
+ spec->gpio_data &= ~spec->gpio_led; /* orange */
+ else
+ spec->gpio_data |= spec->gpio_led; /* white */
+
+ stac_gpio_set(codec, spec->gpio_mask,
+ spec->gpio_dir,
+ spec->gpio_data);
+ }
+
+ return 0;
+}
+#endif
+
static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
{
struct sigmatel_spec *spec = codec->spec;
spec->board_config = snd_hda_check_board_config(codec, STAC_9200_MODELS,
stac9200_models,
stac9200_cfg_tbl);
- if (spec->board_config < 0) {
+ if (spec->board_config < 0)
snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9200, using BIOS defaults\n");
- err = stac92xx_save_bios_config_regs(codec);
- } else
- err = stac_save_pin_cfgs(codec,
+ else
+ stac92xx_set_config_regs(codec,
stac9200_brd_tbl[spec->board_config]);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
spec->multiout.max_channels = 2;
spec->multiout.num_dacs = 1;
stac925x_models,
stac925x_cfg_tbl);
again:
- if (spec->board_config < 0) {
+ if (spec->board_config < 0)
snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC925x,"
"using BIOS defaults\n");
- err = stac92xx_save_bios_config_regs(codec);
- } else
- err = stac_save_pin_cfgs(codec,
+ else
+ stac92xx_set_config_regs(codec,
stac925x_brd_tbl[spec->board_config]);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
spec->multiout.max_channels = 2;
spec->multiout.num_dacs = 1;
stac92hd73xx_models,
stac92hd73xx_cfg_tbl);
again:
- if (spec->board_config < 0) {
+ if (spec->board_config < 0)
snd_printdd(KERN_INFO "hda_codec: Unknown model for"
" STAC92HD73XX, using BIOS defaults\n");
- err = stac92xx_save_bios_config_regs(codec);
- } else
- err = stac_save_pin_cfgs(codec,
+ else
+ stac92xx_set_config_regs(codec,
stac92hd73xx_brd_tbl[spec->board_config]);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
num_dacs = snd_hda_get_connections(codec, 0x0a,
conn, STAC92HD73_DAC_COUNT + 2) - 1;
case 0x3: /* 6 Channel */
spec->mixer = stac92hd73xx_6ch_mixer;
spec->init = stac92hd73xx_6ch_core_init;
+ spec->aloopback_ctl = stac92hd73xx_6ch_loopback;
break;
case 0x4: /* 8 Channel */
spec->mixer = stac92hd73xx_8ch_mixer;
spec->init = stac92hd73xx_8ch_core_init;
+ spec->aloopback_ctl = stac92hd73xx_8ch_loopback;
break;
case 0x5: /* 10 Channel */
spec->mixer = stac92hd73xx_10ch_mixer;
spec->init = stac92hd73xx_10ch_core_init;
+ spec->aloopback_ctl = stac92hd73xx_10ch_loopback;
+ break;
}
spec->multiout.dac_nids = spec->dac_nids;
spec->init = dell_m6_core_init;
switch (spec->board_config) {
case STAC_DELL_M6_AMIC: /* Analog Mics */
- stac92xx_set_config_reg(codec, 0x0b, 0x90A70170);
+ snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170);
spec->num_dmics = 0;
spec->private_dimux.num_items = 1;
break;
case STAC_DELL_M6_DMIC: /* Digital Mics */
- stac92xx_set_config_reg(codec, 0x13, 0x90A60160);
+ snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160);
spec->num_dmics = 1;
spec->private_dimux.num_items = 2;
break;
case STAC_DELL_M6_BOTH: /* Both */
- stac92xx_set_config_reg(codec, 0x0b, 0x90A70170);
- stac92xx_set_config_reg(codec, 0x13, 0x90A60160);
+ snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170);
+ snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160);
spec->num_dmics = 1;
spec->private_dimux.num_items = 2;
break;
stac92hd83xxx_models,
stac92hd83xxx_cfg_tbl);
again:
- if (spec->board_config < 0) {
+ if (spec->board_config < 0)
snd_printdd(KERN_INFO "hda_codec: Unknown model for"
" STAC92HD83XXX, using BIOS defaults\n");
- err = stac92xx_save_bios_config_regs(codec);
- } else
- err = stac_save_pin_cfgs(codec,
+ else
+ stac92xx_set_config_regs(codec,
stac92hd83xxx_brd_tbl[spec->board_config]);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
switch (codec->vendor_id) {
case 0x111d7604:
case 0x111d7605:
+ case 0x111d76d5:
if (spec->board_config == STAC_92HD83XXX_PWR_REF)
break;
spec->num_pwrs = 0;
}
};
+/* get the pin connection (fixed, none, etc) */
+static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx)
+{
+ struct sigmatel_spec *spec = codec->spec;
+ unsigned int cfg;
+
+ cfg = snd_hda_codec_get_pincfg(codec, spec->pin_nids[idx]);
+ return get_defcfg_connect(cfg);
+}
+
static int stac92hd71bxx_connected_ports(struct hda_codec *codec,
hda_nid_t *nids, int num_nids)
{
break;
if (idx >= spec->num_pins)
break;
- def_conf = get_defcfg_connect(spec->pin_configs[idx]);
+ def_conf = stac_get_defcfg_connect(codec, idx);
if (def_conf == AC_JACK_PORT_NONE)
break;
}
return 0;
/* dig1pin case */
- if (get_defcfg_connect(spec->pin_configs[idx+1]) != AC_JACK_PORT_NONE)
+ if (stac_get_defcfg_connect(codec, idx + 1) != AC_JACK_PORT_NONE)
return 2;
/* dig0pin + dig2pin case */
- if (get_defcfg_connect(spec->pin_configs[idx+2]) != AC_JACK_PORT_NONE)
+ if (stac_get_defcfg_connect(codec, idx + 2) != AC_JACK_PORT_NONE)
return 2;
- if (get_defcfg_connect(spec->pin_configs[idx]) != AC_JACK_PORT_NONE)
+ if (stac_get_defcfg_connect(codec, idx) != AC_JACK_PORT_NONE)
return 1;
else
return 0;
stac92hd71bxx_models,
stac92hd71bxx_cfg_tbl);
again:
- if (spec->board_config < 0) {
+ if (spec->board_config < 0)
snd_printdd(KERN_INFO "hda_codec: Unknown model for"
" STAC92HD71BXX, using BIOS defaults\n");
- err = stac92xx_save_bios_config_regs(codec);
- } else
- err = stac_save_pin_cfgs(codec,
+ else
+ stac92xx_set_config_regs(codec,
stac92hd71bxx_brd_tbl[spec->board_config]);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
if (spec->board_config > STAC_92HD71BXX_REF) {
/* GPIO0 = EAPD */
/* disable VSW */
spec->init = &stac92hd71bxx_analog_core_init[HD_DISABLE_PORTF];
unmute_init++;
- stac_change_pin_config(codec, 0x0f, 0x40f000f0);
- stac_change_pin_config(codec, 0x19, 0x40f000f3);
+ snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0);
+ snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3);
stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS - 1] = 0;
spec->num_dmics = stac92hd71bxx_connected_ports(codec,
stac92hd71bxx_dmic_nids,
if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP)
snd_hda_sequence_write_cache(codec, unmute_init);
+ /* Some HP machines seem to have unstable codec communications
+ * especially with ATI fglrx driver. For recovering from the
+ * CORB/RIRB stall, allow the BUS reset and keep always sync
+ */
+ if (spec->board_config == STAC_HP_DV5) {
+ codec->bus->sync_write = 1;
+ codec->bus->allow_bus_reset = 1;
+ }
+
+ spec->aloopback_ctl = stac92hd71bxx_loopback;
spec->aloopback_mask = 0x50;
spec->aloopback_shift = 0;
switch (spec->board_config) {
case STAC_HP_M4:
/* enable internal microphone */
- stac_change_pin_config(codec, 0x0e, 0x01813040);
+ snd_hda_codec_set_pincfg(codec, 0x0e, 0x01813040);
stac92xx_auto_set_pinctl(codec, 0x0e,
AC_PINCTL_IN_EN | AC_PINCTL_VREF_80);
/* fallthru */
case STAC_DELL_M4_3:
spec->num_dmics = 1;
spec->num_smuxes = 0;
- spec->num_dmuxes = 0;
+ spec->num_dmuxes = 1;
break;
+ case STAC_HP_DV4_1222NR:
+ spec->num_dmics = 1;
+ /* I don't know if it needs 1 or 2 smuxes - will wait for
+ * bug reports to fix if needed
+ */
+ spec->num_smuxes = 1;
+ spec->num_dmuxes = 1;
+ spec->gpio_led = 0x01;
+ /* fallthrough */
case STAC_HP_DV5:
- stac_change_pin_config(codec, 0x0d, 0x90170010);
+ snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010);
stac92xx_auto_set_pinctl(codec, 0x0d, AC_PINCTL_OUT_EN);
break;
- };
+ case STAC_HP_HDX:
+ spec->num_dmics = 1;
+ spec->num_dmuxes = 1;
+ spec->num_smuxes = 1;
+ /* orange/white mute led on GPIO3, orange=0, white=1 */
+ spec->gpio_led = 0x08;
+ break;
+ }
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ if (spec->gpio_led) {
+ spec->gpio_mask |= spec->gpio_led;
+ spec->gpio_dir |= spec->gpio_led;
+ spec->gpio_data |= spec->gpio_led;
+ /* register check_power_status callback. */
+ codec->patch_ops.check_power_status =
+ stac92xx_hp_check_power_status;
+ }
+#endif
spec->multiout.dac_nids = spec->dac_nids;
if (spec->dinput_mux)
codec->proc_widget_hook = stac92hd7x_proc_hook;
return 0;
-};
+}
static int patch_stac922x(struct hda_codec *codec)
{
}
again:
- if (spec->board_config < 0) {
+ if (spec->board_config < 0)
snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, "
"using BIOS defaults\n");
- err = stac92xx_save_bios_config_regs(codec);
- } else
- err = stac_save_pin_cfgs(codec,
+ else
+ stac92xx_set_config_regs(codec,
stac922x_brd_tbl[spec->board_config]);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
spec->adc_nids = stac922x_adc_nids;
spec->mux_nids = stac922x_mux_nids;
stac927x_models,
stac927x_cfg_tbl);
again:
- if (spec->board_config < 0) {
+ if (spec->board_config < 0)
snd_printdd(KERN_INFO "hda_codec: Unknown model for"
"STAC927x, using BIOS defaults\n");
- err = stac92xx_save_bios_config_regs(codec);
- } else
- err = stac_save_pin_cfgs(codec,
+ else
+ stac92xx_set_config_regs(codec,
stac927x_brd_tbl[spec->board_config]);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
spec->digbeep_nid = 0x23;
spec->adc_nids = stac927x_adc_nids;
case 0x10280209:
case 0x1028022e:
/* correct the device field to SPDIF out */
- stac_change_pin_config(codec, 0x21, 0x01442070);
+ snd_hda_codec_set_pincfg(codec, 0x21, 0x01442070);
break;
- };
+ }
/* configure the analog microphone on some laptops */
- stac_change_pin_config(codec, 0x0c, 0x90a79130);
+ snd_hda_codec_set_pincfg(codec, 0x0c, 0x90a79130);
/* correct the front output jack as a hp out */
- stac_change_pin_config(codec, 0x0f, 0x0227011f);
+ snd_hda_codec_set_pincfg(codec, 0x0f, 0x0227011f);
/* correct the front input jack as a mic */
- stac_change_pin_config(codec, 0x0e, 0x02a79130);
+ snd_hda_codec_set_pincfg(codec, 0x0e, 0x02a79130);
/* fallthru */
case STAC_DELL_3ST:
/* GPIO2 High = Enable EAPD */
spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x04;
spec->gpio_data = 0x04;
+ switch (codec->subsystem_id) {
+ case 0x1028022f:
+ /* correct EAPD to be GPIO0 */
+ spec->eapd_mask = spec->gpio_mask = 0x01;
+ spec->gpio_dir = spec->gpio_data = 0x01;
+ break;
+ };
spec->dmic_nids = stac927x_dmic_nids;
spec->num_dmics = STAC927X_NUM_DMICS;
}
spec->num_pwrs = 0;
+ spec->aloopback_ctl = stac927x_loopback;
spec->aloopback_mask = 0x40;
spec->aloopback_shift = 0;
spec->eapd_switch = 1;
stac9205_models,
stac9205_cfg_tbl);
again:
- if (spec->board_config < 0) {
+ if (spec->board_config < 0)
snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9205, using BIOS defaults\n");
- err = stac92xx_save_bios_config_regs(codec);
- } else
- err = stac_save_pin_cfgs(codec,
+ else
+ stac92xx_set_config_regs(codec,
stac9205_brd_tbl[spec->board_config]);
- if (err < 0) {
- stac92xx_free(codec);
- return err;
- }
spec->digbeep_nid = 0x23;
spec->adc_nids = stac9205_adc_nids;
spec->init = stac9205_core_init;
spec->mixer = stac9205_mixer;
+ spec->aloopback_ctl = stac9205_loopback;
spec->aloopback_mask = 0x40;
spec->aloopback_shift = 0;
switch (spec->board_config){
case STAC_9205_DELL_M43:
/* Enable SPDIF in/out */
- stac_change_pin_config(codec, 0x1f, 0x01441030);
- stac_change_pin_config(codec, 0x20, 0x1c410030);
+ snd_hda_codec_set_pincfg(codec, 0x1f, 0x01441030);
+ snd_hda_codec_set_pincfg(codec, 0x20, 0x1c410030);
/* Enable unsol response for GPIO4/Dock HP connection */
err = stac_add_event(spec, codec->afg, STAC_VREF_EVENT, 0x01);
0x15
};
+static unsigned int stac9872_vaio_pin_configs[9] = {
+ 0x03211020, 0x411111f0, 0x411111f0, 0x03a15030,
+ 0x411111f0, 0x90170110, 0x411111f0, 0x411111f0,
+ 0x90a7013e
+};
+
+static const char *stac9872_models[STAC_9872_MODELS] = {
+ [STAC_9872_AUTO] = "auto",
+ [STAC_9872_VAIO] = "vaio",
+};
+
+static unsigned int *stac9872_brd_tbl[STAC_9872_MODELS] = {
+ [STAC_9872_VAIO] = stac9872_vaio_pin_configs,
+};
+
+static struct snd_pci_quirk stac9872_cfg_tbl[] = {
+ SND_PCI_QUIRK_MASK(0x104d, 0xfff0, 0x81e0,
+ "Sony VAIO F/S", STAC_9872_VAIO),
+ {} /* terminator */
+};
+
static int patch_stac9872(struct hda_codec *codec)
{
struct sigmatel_spec *spec;
if (spec == NULL)
return -ENOMEM;
codec->spec = spec;
+ spec->num_pins = ARRAY_SIZE(stac9872_pin_nids);
+ spec->pin_nids = stac9872_pin_nids;
-#if 0 /* no model right now */
spec->board_config = snd_hda_check_board_config(codec, STAC_9872_MODELS,
stac9872_models,
stac9872_cfg_tbl);
-#endif
+ if (spec->board_config < 0)
+ snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9872, "
+ "using BIOS defaults\n");
+ else
+ stac92xx_set_config_regs(codec,
+ stac9872_brd_tbl[spec->board_config]);
- spec->num_pins = ARRAY_SIZE(stac9872_pin_nids);
- spec->pin_nids = stac9872_pin_nids;
spec->multiout.dac_nids = spec->dac_nids;
spec->num_adcs = ARRAY_SIZE(stac9872_adc_nids);
spec->adc_nids = stac9872_adc_nids;
{ .id = 0x83847661, .name = "CXD9872RD/K", .patch = patch_stac9872 },
{ .id = 0x83847662, .name = "STAC9872AK", .patch = patch_stac9872 },
{ .id = 0x83847664, .name = "CXD9872AKD", .patch = patch_stac9872 },
+ { .id = 0x83847698, .name = "STAC9205", .patch = patch_stac9205 },
{ .id = 0x838476a0, .name = "STAC9205", .patch = patch_stac9205 },
{ .id = 0x838476a1, .name = "STAC9205D", .patch = patch_stac9205 },
{ .id = 0x838476a2, .name = "STAC9204", .patch = patch_stac9205 },
{ .id = 0x111d7603, .name = "92HD75B3X5", .patch = patch_stac92hd71bxx},
{ .id = 0x111d7604, .name = "92HD83C1X5", .patch = patch_stac92hd83xxx},
{ .id = 0x111d7605, .name = "92HD81B1X5", .patch = patch_stac92hd83xxx},
+ { .id = 0x111d76d5, .name = "92HD81B1C5", .patch = patch_stac92hd83xxx},
{ .id = 0x111d7608, .name = "92HD75B2X5", .patch = patch_stac92hd71bxx},
{ .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx },
{ .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx },