diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-01-31 23:16:28 (GMT) |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-01-31 23:16:28 (GMT) |
commit | e1a9c9872dd004617555dff079b357a6ffd945e9 (patch) | |
tree | c34779e59712ff345f8e4ee97e74086a85b34974 /sound/pci/hda/patch_sigmatel.c | |
parent | fcc3ff4f9d695a80dc6e6058e0d631a3026ed4c3 (diff) | |
parent | 2ecba4ffbbc6c85fce8c3878514be415edace413 (diff) | |
download | linux-fsl-qoriq-e1a9c9872dd004617555dff079b357a6ffd945e9.tar.xz |
Merge branch 'linus' of git://git.kernel.org/pub/scm/linux/kernel/git/perex/alsa
* 'linus' of git://git.kernel.org/pub/scm/linux/kernel/git/perex/alsa: (299 commits)
[ALSA] version 1.0.16rc2
[ALSA] hda: fix Mic in as output
[ALSA] emu10k1 - Another EMU0404 Board ID
[ALSA] emu10k1 - Fix kthread handling at resume
[ALSA] emu10k1: General cleanup, add new locks, fix alsa bug#3501, kernel bug#9304.
[ALSA] emu10k1 - Use enum for emu_model types
[ALSA] emu10k1 - Don't create emu1010 controls for non-emu boards
[ALSA] emu10k1 - 1616(M) cardbus improvements
[ALSA] snd:emu10k1: E-Mu updates. Fixes to firmware loading and support for 0404.
[ALSA] emu10k1: Add comments regarding E-Mu ins and outs.
[ALSA] oxygen: revert SPI clock frequency change for AK4396/WM8785
[ALSA] es1938 - improve capture hw pointer reads
[ALSA] HDA-Intel - Add support for Intel SCH
[ALSA] hda: Add GPIO mute support to STAC9205
[ALSA] hda-codec - Add Dell T3400 support
[ALSA] hda-codec - Add model for HP DV9553EG laptop
[ALSA] hda-codec - Control SPDIF as slave
[ALSA] hda_intel: ALSA HD Audio patch for Intel ICH10 DeviceID's
[ALSA] Fix Oops with PCM OSS sync
[ALSA] hda-codec - Add speaker automute to ALC262 HP models
...
Diffstat (limited to 'sound/pci/hda/patch_sigmatel.c')
-rw-r--r-- | sound/pci/hda/patch_sigmatel.c | 1217 |
1 files changed, 1081 insertions, 136 deletions
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 0401223..caf48ed 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -24,7 +24,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sound/driver.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/slab.h> @@ -35,7 +34,8 @@ #include "hda_local.h" #define NUM_CONTROL_ALLOC 32 -#define STAC_HP_EVENT 0x37 +#define STAC_PWR_EVENT 0x20 +#define STAC_HP_EVENT 0x30 enum { STAC_REF, @@ -62,6 +62,16 @@ enum { }; enum { + STAC_92HD73XX_REF, + STAC_92HD73XX_MODELS +}; + +enum { + STAC_92HD71BXX_REF, + STAC_92HD71BXX_MODELS +}; + +enum { STAC_925x_REF, STAC_M2_2, STAC_MA6, @@ -97,6 +107,7 @@ enum { STAC_D965_3ST, STAC_D965_5ST, STAC_DELL_3ST, + STAC_DELL_BIOS, STAC_927X_MODELS }; @@ -110,11 +121,24 @@ struct sigmatel_spec { unsigned int mic_switch: 1; unsigned int alt_switch: 1; unsigned int hp_detect: 1; - unsigned int gpio_mute: 1; - unsigned int gpio_mask, gpio_data; + /* gpio lines */ + unsigned int gpio_mask; + unsigned int gpio_dir; + unsigned int gpio_data; + unsigned int gpio_mute; + + /* analog loopback */ + unsigned char aloopback_mask; + unsigned char aloopback_shift; + + /* power management */ + unsigned int num_pwrs; + hda_nid_t *pwr_nids; /* playback */ + struct hda_input_mux *mono_mux; + unsigned int cur_mmux; struct hda_multi_out multiout; hda_nid_t dac_nids[5]; @@ -125,8 +149,10 @@ struct sigmatel_spec { unsigned int num_muxes; hda_nid_t *dmic_nids; unsigned int num_dmics; - hda_nid_t dmux_nid; + hda_nid_t *dmux_nids; + unsigned int num_dmuxes; hda_nid_t dig_in_nid; + hda_nid_t mono_nid; /* pin widgets */ hda_nid_t *pin_nids; @@ -140,7 +166,7 @@ struct sigmatel_spec { /* capture source */ struct hda_input_mux *dinput_mux; - unsigned int cur_dmux; + unsigned int cur_dmux[2]; struct hda_input_mux *input_mux; unsigned int cur_mux[3]; @@ -157,6 +183,10 @@ struct sigmatel_spec { struct snd_kcontrol_new *kctl_alloc; struct hda_input_mux private_dimux; struct hda_input_mux private_imux; + struct hda_input_mux private_mono_mux; + + /* virtual master */ + unsigned int vmaster_tlv[4]; }; static hda_nid_t stac9200_adc_nids[1] = { @@ -171,6 +201,58 @@ static hda_nid_t stac9200_dac_nids[1] = { 0x02, }; +static hda_nid_t stac92hd73xx_pwr_nids[8] = { + 0x0a, 0x0b, 0x0c, 0xd, 0x0e, + 0x0f, 0x10, 0x11 +}; + +static hda_nid_t stac92hd73xx_adc_nids[2] = { + 0x1a, 0x1b +}; + +#define STAC92HD73XX_NUM_DMICS 2 +static hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = { + 0x13, 0x14, 0 +}; + +#define STAC92HD73_DAC_COUNT 5 +static hda_nid_t stac92hd73xx_dac_nids[STAC92HD73_DAC_COUNT] = { + 0x15, 0x16, 0x17, 0x18, 0x19, +}; + +static hda_nid_t stac92hd73xx_mux_nids[4] = { + 0x28, 0x29, 0x2a, 0x2b, +}; + +static hda_nid_t stac92hd73xx_dmux_nids[2] = { + 0x20, 0x21, +}; + +static hda_nid_t stac92hd71bxx_pwr_nids[3] = { + 0x0a, 0x0d, 0x0f +}; + +static hda_nid_t stac92hd71bxx_adc_nids[2] = { + 0x12, 0x13, +}; + +static hda_nid_t stac92hd71bxx_mux_nids[2] = { + 0x1a, 0x1b +}; + +static hda_nid_t stac92hd71bxx_dmux_nids[1] = { + 0x1c, +}; + +static hda_nid_t stac92hd71bxx_dac_nids[2] = { + 0x10, /*0x11, */ +}; + +#define STAC92HD71BXX_NUM_DMICS 2 +static hda_nid_t stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS + 1] = { + 0x18, 0x19, 0 +}; + static hda_nid_t stac925x_adc_nids[1] = { 0x03, }; @@ -188,6 +270,10 @@ static hda_nid_t stac925x_dmic_nids[STAC925X_NUM_DMICS + 1] = { 0x15, 0 }; +static hda_nid_t stac925x_dmux_nids[1] = { + 0x14, +}; + static hda_nid_t stac922x_adc_nids[2] = { 0x06, 0x07, }; @@ -204,6 +290,15 @@ static hda_nid_t stac927x_mux_nids[3] = { 0x15, 0x16, 0x17 }; +static hda_nid_t stac927x_dmux_nids[1] = { + 0x1b, +}; + +#define STAC927X_NUM_DMICS 2 +static hda_nid_t stac927x_dmic_nids[STAC927X_NUM_DMICS + 1] = { + 0x13, 0x14, 0 +}; + static hda_nid_t stac9205_adc_nids[2] = { 0x12, 0x13 }; @@ -212,6 +307,10 @@ static hda_nid_t stac9205_mux_nids[2] = { 0x19, 0x1a }; +static hda_nid_t stac9205_dmux_nids[1] = { + 0x1d, +}; + #define STAC9205_NUM_DMICS 2 static hda_nid_t stac9205_dmic_nids[STAC9205_NUM_DMICS + 1] = { 0x17, 0x18, 0 @@ -232,6 +331,17 @@ static hda_nid_t stac922x_pin_nids[10] = { 0x0f, 0x10, 0x11, 0x15, 0x1b, }; +static hda_nid_t stac92hd73xx_pin_nids[12] = { + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x10, 0x11, 0x12, 0x13, + 0x14, 0x22 +}; + +static hda_nid_t stac92hd71bxx_pin_nids[10] = { + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x14, 0x18, 0x19, 0x1e, +}; + static hda_nid_t stac927x_pin_nids[14] = { 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, @@ -257,8 +367,9 @@ static int stac92xx_dmux_enum_get(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; + unsigned int dmux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - ucontrol->value.enumerated.item[0] = spec->cur_dmux; + ucontrol->value.enumerated.item[0] = spec->cur_dmux[dmux_idx]; return 0; } @@ -267,9 +378,10 @@ static int stac92xx_dmux_enum_put(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; + unsigned int dmux_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); return snd_hda_input_mux_put(codec, spec->dinput_mux, ucontrol, - spec->dmux_nid, &spec->cur_dmux); + spec->dmux_nids[dmux_idx], &spec->cur_dmux[dmux_idx]); } static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) @@ -299,15 +411,45 @@ static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e spec->mux_nids[adc_idx], &spec->cur_mux[adc_idx]); } +static int stac92xx_mono_mux_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + return snd_hda_input_mux_info(spec->mono_mux, uinfo); +} + +static int stac92xx_mono_mux_enum_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; + + ucontrol->value.enumerated.item[0] = spec->cur_mmux; + return 0; +} + +static int stac92xx_mono_mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct sigmatel_spec *spec = codec->spec; + + return snd_hda_input_mux_put(codec, spec->mono_mux, ucontrol, + spec->mono_nid, &spec->cur_mmux); +} + #define stac92xx_aloopback_info snd_ctl_boolean_mono_info static int stac92xx_aloopback_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); struct sigmatel_spec *spec = codec->spec; - ucontrol->value.integer.value[0] = spec->aloopback; + ucontrol->value.integer.value[0] = !!(spec->aloopback & + (spec->aloopback_mask << idx)); return 0; } @@ -316,23 +458,33 @@ static int stac92xx_aloopback_put(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; + unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); unsigned int dac_mode; + unsigned int val, idx_val; - if (spec->aloopback == ucontrol->value.integer.value[0]) + idx_val = spec->aloopback_mask << idx; + if (ucontrol->value.integer.value[0]) + val = spec->aloopback | idx_val; + else + val = spec->aloopback & ~idx_val; + if (spec->aloopback == val) return 0; - spec->aloopback = ucontrol->value.integer.value[0]; - + spec->aloopback = val; + /* Only return the bits defined by the shift value of the + * first two bytes of the mask + */ dac_mode = snd_hda_codec_read(codec, codec->afg, 0, - kcontrol->private_value & 0xFFFF, 0x0); + kcontrol->private_value & 0xFFFF, 0x0); + dac_mode >>= spec->aloopback_shift; - if (spec->aloopback) { + if (spec->aloopback & idx_val) { snd_hda_power_up(codec); - dac_mode |= 0x40; + dac_mode |= idx_val; } else { snd_hda_power_down(codec); - dac_mode &= ~0x40; + dac_mode &= ~idx_val; } snd_hda_codec_write_cache(codec, codec->afg, 0, @@ -354,6 +506,107 @@ static struct hda_verb stac9200_eapd_init[] = { {} }; +static struct hda_verb stac92hd73xx_6ch_core_init[] = { + /* set master volume and direct control */ + { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + /* setup audio connections */ + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00}, + { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* setup adcs to point to mixer */ + { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b}, + { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + /* setup import muxs */ + { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x00}, + {} +}; + +static struct hda_verb stac92hd73xx_8ch_core_init[] = { + /* set master volume and direct control */ + { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + /* setup audio connections */ + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00}, + { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02}, + /* connect hp ports to dac3 */ + { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x03}, + { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x03}, + /* setup adcs to point to mixer */ + { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b}, + { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + /* setup import muxs */ + { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x03}, + {} +}; + +static struct hda_verb stac92hd73xx_10ch_core_init[] = { + /* set master volume and direct control */ + { 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + /* setup audio connections */ + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 }, + { 0x10, AC_VERB_SET_CONNECT_SEL, 0x01 }, + { 0x11, AC_VERB_SET_CONNECT_SEL, 0x02 }, + /* dac3 is connected to import3 mux */ + { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb07f}, + /* connect hp ports to dac4 */ + { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x04}, + { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x04}, + /* setup adcs to point to mixer */ + { 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b}, + { 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b}, + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, + /* setup import muxs */ + { 0x28, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x29, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x2b, AC_VERB_SET_CONNECT_SEL, 0x03}, + {} +}; + +static struct hda_verb stac92hd71bxx_core_init[] = { + /* set master volume and direct control */ + { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + /* connect headphone jack to dac1 */ + { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01}, + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */ + /* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */ + { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, +}; + +static struct hda_verb stac92hd71bxx_analog_core_init[] = { + /* set master volume and direct control */ + { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, + /* connect headphone jack to dac1 */ + { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01}, + /* connect ports 0d and 0f to audio mixer */ + { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x2}, + { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2}, + { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */ + /* unmute dac0 input in audio mixer */ + { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0x701f}, + /* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */ + { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, + {} +}; + static struct hda_verb stac925x_core_init[] = { /* set dac0mux for dac converter */ { 0x06, AC_VERB_SET_CONNECT_SEL, 0x00}, @@ -388,6 +641,16 @@ static struct hda_verb stac9205_core_init[] = { {} }; +#define STAC_MONO_MUX \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = "Mono Mux", \ + .count = 1, \ + .info = stac92xx_mono_mux_enum_info, \ + .get = stac92xx_mono_mux_enum_get, \ + .put = stac92xx_mono_mux_enum_put, \ + } + #define STAC_INPUT_SOURCE(cnt) \ { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ @@ -398,11 +661,11 @@ static struct hda_verb stac9205_core_init[] = { .put = stac92xx_mux_enum_put, \ } -#define STAC_ANALOG_LOOPBACK(verb_read,verb_write) \ +#define STAC_ANALOG_LOOPBACK(verb_read, verb_write, cnt) \ { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = "Analog Loopback", \ - .count = 1, \ + .count = cnt, \ .info = stac92xx_aloopback_info, \ .get = stac92xx_aloopback_get, \ .put = stac92xx_aloopback_put, \ @@ -419,6 +682,114 @@ static struct snd_kcontrol_new stac9200_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new stac92hd73xx_6ch_mixer[] = { + 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), + + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT), + + HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT), + + HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT), + HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT), + + HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT), + HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT), + + HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT), + HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT), + { } /* end */ +}; + +static struct snd_kcontrol_new stac92hd73xx_8ch_mixer[] = { + STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 4), + + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT), + + HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT), + + HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT), + HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT), + + HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT), + HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT), + + HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT), + HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT), + { } /* end */ +}; + +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), + + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT), + HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT), + + HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT), + HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT), + + HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT), + HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT), + + HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT), + HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT), + + HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT), + HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT), + { } /* end */ +}; + +static struct snd_kcontrol_new stac92hd71bxx_analog_mixer[] = { + STAC_INPUT_SOURCE(2), + + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x0, 0x1a, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT), + + HDA_CODEC_MUTE("Analog Loopback 1", 0x17, 0x3, HDA_INPUT), + HDA_CODEC_MUTE("Analog Loopback 2", 0x17, 0x4, HDA_INPUT), + { } /* end */ +}; + +static struct snd_kcontrol_new stac92hd71bxx_mixer[] = { + STAC_INPUT_SOURCE(2), + 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), + HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x0, 0x1a, 0x0, HDA_OUTPUT), + + HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME_IDX("Capture Mux Volume", 0x1, 0x1b, 0x0, HDA_OUTPUT), + { } /* end */ +}; + static struct snd_kcontrol_new stac925x_mixer[] = { STAC_INPUT_SOURCE(1), HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_OUTPUT), @@ -428,16 +799,8 @@ static struct snd_kcontrol_new stac925x_mixer[] = { }; static struct snd_kcontrol_new stac9205_mixer[] = { - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Digital Input Source", - .count = 1, - .info = stac92xx_dmux_enum_info, - .get = stac92xx_dmux_enum_get, - .put = stac92xx_dmux_enum_put, - }, STAC_INPUT_SOURCE(2), - STAC_ANALOG_LOOPBACK(0xFE0, 0x7E0), + 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), @@ -466,7 +829,7 @@ static struct snd_kcontrol_new stac922x_mixer[] = { static struct snd_kcontrol_new stac927x_mixer[] = { STAC_INPUT_SOURCE(3), - STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB), + 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), @@ -482,6 +845,44 @@ static struct snd_kcontrol_new stac927x_mixer[] = { { } /* end */ }; +static struct snd_kcontrol_new stac_dmux_mixer = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Digital Input Source", + /* count set later */ + .info = stac92xx_dmux_enum_info, + .get = stac92xx_dmux_enum_get, + .put = stac92xx_dmux_enum_put, +}; + +static const char *slave_vols[] = { + "Front Playback Volume", + "Surround Playback Volume", + "Center Playback Volume", + "LFE Playback Volume", + "Side Playback Volume", + "Headphone Playback Volume", + "Headphone Playback Volume", + "Speaker Playback Volume", + "External Speaker Playback Volume", + "Speaker2 Playback Volume", + NULL +}; + +static const char *slave_sws[] = { + "Front Playback Switch", + "Surround Playback Switch", + "Center Playback Switch", + "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 +}; + static int stac92xx_build_controls(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; @@ -497,6 +898,13 @@ static int stac92xx_build_controls(struct hda_codec *codec) if (err < 0) return err; } + if (spec->num_dmuxes > 0) { + stac_dmux_mixer.count = spec->num_dmuxes; + err = snd_ctl_add(codec->bus->card, + snd_ctl_new1(&stac_dmux_mixer, codec)); + if (err < 0) + return err; + } if (spec->multiout.dig_out_nid) { err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid); @@ -508,6 +916,23 @@ static int stac92xx_build_controls(struct hda_codec *codec) if (err < 0) return err; } + + /* if we have no master control, let's create it */ + if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { + snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0], + HDA_OUTPUT, spec->vmaster_tlv); + err = snd_hda_add_vmaster(codec, "Master Playback Volume", + spec->vmaster_tlv, slave_vols); + if (err < 0) + return err; + } + if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { + err = snd_hda_add_vmaster(codec, "Master Playback Switch", + NULL, slave_sws); + if (err < 0) + return err; + } + return 0; } @@ -733,7 +1158,7 @@ static struct snd_pci_quirk stac9200_cfg_tbl[] = { static unsigned int ref925x_pin_configs[8] = { 0x40c003f0, 0x424503f2, 0x01813022, 0x02a19021, - 0x90a70320, 0x02214210, 0x400003f1, 0x9033032e, + 0x90a70320, 0x02214210, 0x01019020, 0x9033032e, }; static unsigned int stac925x_MA6_pin_configs[8] = { @@ -777,6 +1202,48 @@ static struct snd_pci_quirk stac925x_cfg_tbl[] = { {} /* terminator */ }; +static unsigned int ref92hd73xx_pin_configs[12] = { + 0x02214030, 0x02a19040, 0x01a19020, 0x02214030, + 0x0181302e, 0x01014010, 0x01014020, 0x01014030, + 0x02319040, 0x90a000f0, 0x90a000f0, 0x01452050, +}; + +static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = { + [STAC_92HD73XX_REF] = ref92hd73xx_pin_configs, +}; + +static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = { + [STAC_92HD73XX_REF] = "ref", +}; + +static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = { + /* SigmaTel reference board */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, + "DFI LanParty", STAC_92HD73XX_REF), + {} /* terminator */ +}; + +static unsigned int ref92hd71bxx_pin_configs[10] = { + 0x02214030, 0x02a19040, 0x01a19020, 0x01014010, + 0x0181302e, 0x01114010, 0x01019020, 0x90a000f0, + 0x90a000f0, 0x01452050, +}; + +static unsigned int *stac92hd71bxx_brd_tbl[STAC_92HD71BXX_MODELS] = { + [STAC_92HD71BXX_REF] = ref92hd71bxx_pin_configs, +}; + +static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = { + [STAC_92HD71BXX_REF] = "ref", +}; + +static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = { + /* SigmaTel reference board */ + SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668, + "DFI LanParty", STAC_92HD71BXX_REF), + {} /* terminator */ +}; + static unsigned int ref922x_pin_configs[10] = { 0x01014010, 0x01016011, 0x01012012, 0x0221401f, 0x01813122, 0x01011014, 0x01441030, 0x01c41030, @@ -823,8 +1290,8 @@ static unsigned int dell_922x_m81_pin_configs[10] = { 102801D7 (Dell XPS M1210) */ static unsigned int dell_922x_m82_pin_configs[10] = { - 0x0221121f, 0x408103ff, 0x02111212, 0x90100310, - 0x408003f1, 0x02111211, 0x03451340, 0x40c003f2, + 0x02211211, 0x408103ff, 0x02a1123e, 0x90100310, + 0x408003f1, 0x0221121f, 0x03451340, 0x40c003f2, 0x508003f3, 0x405003f4, }; @@ -1022,22 +1489,24 @@ static unsigned int d965_5st_pin_configs[14] = { static unsigned int dell_3st_pin_configs[14] = { 0x02211230, 0x02a11220, 0x01a19040, 0x01114210, 0x01111212, 0x01116211, 0x01813050, 0x01112214, - 0x403003fa, 0x40000100, 0x40000100, 0x404003fb, + 0x403003fa, 0x90a60040, 0x90a60040, 0x404003fb, 0x40c003fc, 0x40000100 }; static unsigned int *stac927x_brd_tbl[STAC_927X_MODELS] = { - [STAC_D965_REF] = ref927x_pin_configs, - [STAC_D965_3ST] = d965_3st_pin_configs, - [STAC_D965_5ST] = d965_5st_pin_configs, - [STAC_DELL_3ST] = dell_3st_pin_configs, + [STAC_D965_REF] = ref927x_pin_configs, + [STAC_D965_3ST] = d965_3st_pin_configs, + [STAC_D965_5ST] = d965_5st_pin_configs, + [STAC_DELL_3ST] = dell_3st_pin_configs, + [STAC_DELL_BIOS] = NULL, }; static const char *stac927x_models[STAC_927X_MODELS] = { - [STAC_D965_REF] = "ref", - [STAC_D965_3ST] = "3stack", - [STAC_D965_5ST] = "5stack", - [STAC_DELL_3ST] = "dell-3stack", + [STAC_D965_REF] = "ref", + [STAC_D965_3ST] = "3stack", + [STAC_D965_5ST] = "5stack", + [STAC_DELL_3ST] = "dell-3stack", + [STAC_DELL_BIOS] = "dell-bios", }; static struct snd_pci_quirk stac927x_cfg_tbl[] = { @@ -1064,13 +1533,21 @@ static struct snd_pci_quirk stac927x_cfg_tbl[] = { SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2003, "Intel D965", STAC_D965_3ST), SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2002, "Intel D965", STAC_D965_3ST), SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2001, "Intel D965", STAC_D965_3ST), - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f3, "Dell Inspiron 1420", STAC_D965_3ST), /* Dell 3 stack systems */ + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f7, "Dell XPS M1730", STAC_DELL_3ST), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01dd, "Dell Dimension E520", STAC_DELL_3ST), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01ed, "Dell ", STAC_DELL_3ST), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x01f4, "Dell ", STAC_DELL_3ST), + /* Dell 3 stack systems with verb table in 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, 0x022f, "Dell ", STAC_DELL_BIOS), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x022e, "Dell ", 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), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0209, "Dell XPS 1330", STAC_DELL_BIOS), /* 965 based 5 stack systems */ - SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0209, "Dell XPS 1330", STAC_D965_5ST), SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2301, "Intel D965", STAC_D965_5ST), SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2302, "Intel D965", STAC_D965_5ST), SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2303, "Intel D965", STAC_D965_5ST), @@ -1085,7 +1562,7 @@ static struct snd_pci_quirk stac927x_cfg_tbl[] = { static unsigned int ref9205_pin_configs[12] = { 0x40000100, 0x40000100, 0x01016011, 0x01014010, - 0x01813122, 0x01a19021, 0x40000100, 0x40000100, + 0x01813122, 0x01a19021, 0x01019020, 0x40000100, 0x90a000f0, 0x90a000f0, 0x01441030, 0x01c41030 }; @@ -1097,6 +1574,7 @@ static unsigned int ref9205_pin_configs[12] = { 102801FD 10280204 1028021F + 10280228 (Dell Vostro 1500) */ static unsigned int dell_9205_m42_pin_configs[12] = { 0x0321101F, 0x03A11020, 0x400003FA, 0x90170310, @@ -1180,6 +1658,8 @@ static struct snd_pci_quirk stac9205_cfg_tbl[] = { "unknown Dell", STAC_9205_DELL_M42), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x021f, "Dell Inspiron", STAC_9205_DELL_M44), + SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0228, + "Dell Vostro 1500", STAC_9205_DELL_M42), {} /* terminator */ }; @@ -1245,22 +1725,6 @@ static void stac92xx_set_config_regs(struct hda_codec *codec) spec->pin_configs[i]); } -static void stac92xx_enable_gpio_mask(struct hda_codec *codec) -{ - struct sigmatel_spec *spec = codec->spec; - /* Configure GPIOx as output */ - snd_hda_codec_write_cache(codec, codec->afg, 0, - AC_VERB_SET_GPIO_DIRECTION, spec->gpio_mask); - /* Configure GPIOx as CMOS */ - snd_hda_codec_write_cache(codec, codec->afg, 0, 0x7e7, 0x00000000); - /* Assert GPIOx */ - snd_hda_codec_write_cache(codec, codec->afg, 0, - AC_VERB_SET_GPIO_DATA, spec->gpio_data); - /* Enable GPIOx */ - snd_hda_codec_write_cache(codec, codec->afg, 0, - AC_VERB_SET_GPIO_MASK, spec->gpio_mask); -} - /* * Analog playback callbacks */ @@ -1479,7 +1943,7 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ 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]; + unsigned short val = !!ucontrol->value.integer.value[0]; spec->io_switch[io_idx] = val; @@ -1491,6 +1955,13 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ pinctl |= stac92xx_get_vref(codec, nid); stac92xx_auto_set_pinctl(codec, nid, pinctl); } + + /* check the auto-mute again: we need to mute/unmute the speaker + * appropriately according to the pin direction + */ + if (spec->hp_detect) + codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26); + return 1; } @@ -1512,11 +1983,12 @@ static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol, struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; hda_nid_t nid = kcontrol->private_value & 0xff; + unsigned int val = !!ucontrol->value.integer.value[0]; - if (spec->clfe_swap == ucontrol->value.integer.value[0]) + if (spec->clfe_swap == val) return 0; - spec->clfe_swap = ucontrol->value.integer.value[0]; + spec->clfe_swap = val; snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE, spec->clfe_swap ? 0x4 : 0x0); @@ -1547,6 +2019,7 @@ static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol, enum { STAC_CTL_WIDGET_VOL, STAC_CTL_WIDGET_MUTE, + STAC_CTL_WIDGET_MONO_MUX, STAC_CTL_WIDGET_IO_SWITCH, STAC_CTL_WIDGET_CLFE_SWITCH }; @@ -1554,6 +2027,7 @@ enum { static struct snd_kcontrol_new stac92xx_control_templates[] = { HDA_CODEC_VOLUME(NULL, 0, 0, 0), HDA_CODEC_MUTE(NULL, 0, 0, 0), + STAC_MONO_MUX, STAC_CODEC_IO_SWITCH(NULL, 0), STAC_CODEC_CLFE_SWITCH(NULL, 0), }; @@ -1598,6 +2072,7 @@ static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cf for (i = 0; i < codec->num_nodes; i++) { wcaps = codec->wcaps[i]; wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; + if (wtype == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL)) num_dacs++; } @@ -1685,7 +2160,6 @@ static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec, wcaps = snd_hda_param_read(codec, conn[j], AC_PAR_AUDIO_WIDGET_CAP); wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; - if (wtype != AC_WID_AUD_OUT || (wcaps & AC_WCAP_DIGITAL)) continue; @@ -1759,7 +2233,7 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, int i, err; struct sigmatel_spec *spec = codec->spec; - unsigned int wid_caps; + unsigned int wid_caps, pincap; for (i = 0; i < cfg->line_outs; i++) { @@ -1795,13 +2269,39 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, } } - if (spec->line_switch) - if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, "Line In as Output Switch", cfg->input_pins[AUTO_PIN_LINE] << 8)) < 0) - return err; + if (spec->line_switch) { + nid = cfg->input_pins[AUTO_PIN_LINE]; + pincap = snd_hda_param_read(codec, nid, + AC_PAR_PIN_CAP); + if (pincap & AC_PINCAP_OUT) { + err = stac92xx_add_control(spec, + STAC_CTL_WIDGET_IO_SWITCH, + "Line In as Output Switch", nid << 8); + if (err < 0) + return err; + } + } - if (spec->mic_switch) - if ((err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH, "Mic as Output Switch", (cfg->input_pins[AUTO_PIN_MIC] << 8) | 1)) < 0) - return err; + if (spec->mic_switch) { + unsigned int def_conf; + nid = cfg->input_pins[AUTO_PIN_MIC]; + def_conf = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_CONFIG_DEFAULT, 0); + + /* 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); + if (pincap & AC_PINCAP_OUT) { + err = stac92xx_add_control(spec, + STAC_CTL_WIDGET_IO_SWITCH, + "Mic as Output Switch", (nid << 8) | 1); + if (err < 0) + return err; + } + } + } return 0; } @@ -1891,6 +2391,37 @@ static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec, return 0; } +/* labels for mono mux outputs */ +static const char *stac92xx_mono_labels[3] = { + "DAC0", "DAC1", "Mixer" +}; + +/* create mono mux for mono out on capable codecs */ +static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec) +{ + struct sigmatel_spec *spec = codec->spec; + struct hda_input_mux *mono_mux = &spec->private_mono_mux; + int i, num_cons; + hda_nid_t con_lst[ARRAY_SIZE(stac92xx_mono_labels)]; + + num_cons = snd_hda_get_connections(codec, + spec->mono_nid, + con_lst, + HDA_MAX_NUM_INPUTS); + if (!num_cons || num_cons > ARRAY_SIZE(stac92xx_mono_labels)) + return -EINVAL; + + for (i = 0; i < num_cons; i++) { + mono_mux->items[mono_mux->num_items].label = + stac92xx_mono_labels[i]; + mono_mux->items[mono_mux->num_items].index = i; + mono_mux->num_items++; + } + + return stac92xx_add_control(spec, STAC_CTL_WIDGET_MONO_MUX, + "Mono Mux", spec->mono_nid); +} + /* labels for dmic mux inputs */ static const char *stac92xx_dmic_labels[5] = { "Analog Inputs", "Digital Mic 1", "Digital Mic 2", @@ -1904,15 +2435,18 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec, struct sigmatel_spec *spec = codec->spec; struct hda_input_mux *dimux = &spec->private_dimux; hda_nid_t con_lst[HDA_MAX_NUM_INPUTS]; - int i, j; + int err, i, j; + char name[32]; dimux->items[dimux->num_items].label = stac92xx_dmic_labels[0]; dimux->items[dimux->num_items].index = 0; dimux->num_items++; for (i = 0; i < spec->num_dmics; i++) { + hda_nid_t nid; int index; int num_cons; + unsigned int wcaps; unsigned int def_conf; def_conf = snd_hda_codec_read(codec, @@ -1923,17 +2457,32 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec, if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) continue; + nid = spec->dmic_nids[i]; num_cons = snd_hda_get_connections(codec, - spec->dmux_nid, + spec->dmux_nids[0], con_lst, HDA_MAX_NUM_INPUTS); for (j = 0; j < num_cons; j++) - if (con_lst[j] == spec->dmic_nids[i]) { + if (con_lst[j] == nid) { index = j; goto found; } continue; found: + wcaps = get_wcaps(codec, nid); + + if (wcaps & AC_WCAP_OUT_AMP) { + sprintf(name, "%s Capture Volume", + stac92xx_dmic_labels[dimux->num_items]); + + err = stac92xx_add_control(spec, + STAC_CTL_WIDGET_VOL, + name, + HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); + if (err < 0) + return err; + } + dimux->items[dimux->num_items].label = stac92xx_dmic_labels[dimux->num_items]; dimux->items[dimux->num_items].index = index; @@ -2026,6 +2575,7 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out { struct sigmatel_spec *spec = codec->spec; int err; + int hp_speaker_swap = 0; if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, @@ -2034,6 +2584,68 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out if (! spec->autocfg.line_outs) return 0; /* can't find valid pin config */ + /* If we have no real line-out pin and multiple hp-outs, HPs should + * be set up as multi-channel outputs. + */ + if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT && + spec->autocfg.hp_outs > 1) { + /* Copy hp_outs to line_outs, backup line_outs in + * speaker_outs so that the following routines can handle + * HP pins as primary outputs. + */ + memcpy(spec->autocfg.speaker_pins, spec->autocfg.line_out_pins, + sizeof(spec->autocfg.line_out_pins)); + spec->autocfg.speaker_outs = spec->autocfg.line_outs; + memcpy(spec->autocfg.line_out_pins, spec->autocfg.hp_pins, + sizeof(spec->autocfg.hp_pins)); + spec->autocfg.line_outs = spec->autocfg.hp_outs; + hp_speaker_swap = 1; + } + if (spec->autocfg.mono_out_pin) { + int dir = (get_wcaps(codec, spec->autocfg.mono_out_pin) + & AC_WCAP_OUT_AMP) ? HDA_OUTPUT : HDA_INPUT; + u32 caps = query_amp_caps(codec, + spec->autocfg.mono_out_pin, dir); + hda_nid_t conn_list[1]; + + /* get the mixer node and then the mono mux if it exists */ + if (snd_hda_get_connections(codec, + spec->autocfg.mono_out_pin, conn_list, 1) && + snd_hda_get_connections(codec, conn_list[0], + conn_list, 1)) { + + int wcaps = get_wcaps(codec, conn_list[0]); + int wid_type = (wcaps & AC_WCAP_TYPE) + >> AC_WCAP_TYPE_SHIFT; + /* LR swap check, some stac925x have a mux that + * changes the DACs output path instead of the + * mono-mux path. + */ + if (wid_type == AC_WID_AUD_SEL && + !(wcaps & AC_WCAP_LR_SWAP)) + spec->mono_nid = conn_list[0]; + } + /* all mono outs have a least a mute/unmute switch */ + err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE, + "Mono Playback Switch", + HDA_COMPOSE_AMP_VAL(spec->autocfg.mono_out_pin, + 1, 0, dir)); + if (err < 0) + return err; + /* check to see if there is volume support for the amp */ + if ((caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT) { + err = stac92xx_add_control(spec, STAC_CTL_WIDGET_VOL, + "Mono Playback Volume", + HDA_COMPOSE_AMP_VAL(spec->autocfg.mono_out_pin, + 1, 0, dir)); + if (err < 0) + return err; + } + + stac92xx_auto_set_pinctl(codec, spec->autocfg.mono_out_pin, + AC_PINCTL_OUT_EN); + } + if ((err = stac92xx_add_dyn_out_pins(codec, &spec->autocfg)) < 0) return err; if (spec->multiout.num_dacs == 0) @@ -2045,6 +2657,19 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out if (err < 0) return err; + if (hp_speaker_swap == 1) { + /* Restore the hp_outs and line_outs */ + memcpy(spec->autocfg.hp_pins, spec->autocfg.line_out_pins, + sizeof(spec->autocfg.line_out_pins)); + spec->autocfg.hp_outs = spec->autocfg.line_outs; + memcpy(spec->autocfg.line_out_pins, spec->autocfg.speaker_pins, + sizeof(spec->autocfg.speaker_pins)); + spec->autocfg.line_outs = spec->autocfg.speaker_outs; + memset(spec->autocfg.speaker_pins, 0, + sizeof(spec->autocfg.speaker_pins)); + spec->autocfg.speaker_outs = 0; + } + err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg); if (err < 0) @@ -2055,6 +2680,12 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out if (err < 0) return err; + if (spec->mono_nid > 0) { + err = stac92xx_auto_create_mono_output_ctls(codec); + if (err < 0) + return err; + } + if (spec->num_dmics > 0) if ((err = stac92xx_auto_create_dmic_input_ctls(codec, &spec->autocfg)) < 0) @@ -2073,7 +2704,9 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out spec->mixers[spec->num_mixers++] = spec->kctl_alloc; spec->input_mux = &spec->private_imux; - spec->dinput_mux = &spec->private_dimux; + if (!spec->dinput_mux) + spec->dinput_mux = &spec->private_dimux; + spec->mono_mux = &spec->private_mono_mux; return 1; } @@ -2183,38 +2816,35 @@ static int stac9200_parse_auto_config(struct hda_codec *codec) * funky external mute control using GPIO pins. */ -static void stac922x_gpio_mute(struct hda_codec *codec, int pin, int muted) +static void stac_gpio_set(struct hda_codec *codec, unsigned int mask, + unsigned int dir_mask, unsigned int data) { unsigned int gpiostate, gpiomask, gpiodir; gpiostate = snd_hda_codec_read(codec, codec->afg, 0, AC_VERB_GET_GPIO_DATA, 0); - - if (!muted) - gpiostate |= (1 << pin); - else - gpiostate &= ~(1 << pin); + gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask); gpiomask = snd_hda_codec_read(codec, codec->afg, 0, AC_VERB_GET_GPIO_MASK, 0); - gpiomask |= (1 << pin); + gpiomask |= mask; gpiodir = snd_hda_codec_read(codec, codec->afg, 0, AC_VERB_GET_GPIO_DIRECTION, 0); - gpiodir |= (1 << pin); + gpiodir |= dir_mask; - /* AppleHDA seems to do this -- WTF is this verb?? */ + /* Configure GPIOx as CMOS */ snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0); snd_hda_codec_write(codec, codec->afg, 0, AC_VERB_SET_GPIO_MASK, gpiomask); - snd_hda_codec_write(codec, codec->afg, 0, - AC_VERB_SET_GPIO_DIRECTION, gpiodir); + snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_SET_GPIO_DIRECTION, gpiodir); /* sync */ msleep(1); - snd_hda_codec_write(codec, codec->afg, 0, - AC_VERB_SET_GPIO_DATA, gpiostate); + snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */ } static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid, @@ -2226,6 +2856,16 @@ static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid, (AC_USRSP_EN | event)); } +static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid) +{ + int i; + for (i = 0; i < cfg->hp_outs; i++) + if (cfg->hp_pins[i] == nid) + return 1; /* nid is a HP-Out */ + + return 0; /* nid is not a HP-Out */ +}; + static int stac92xx_init(struct hda_codec *codec) { struct sigmatel_spec *spec = codec->spec; @@ -2261,10 +2901,23 @@ static int stac92xx_init(struct hda_codec *codec) stac92xx_auto_set_pinctl(codec, nid, pinctl); } } - if (spec->num_dmics > 0) - for (i = 0; i < spec->num_dmics; i++) - stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i], - AC_PINCTL_IN_EN); + for (i = 0; i < spec->num_dmics; i++) + stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i], + AC_PINCTL_IN_EN); + for (i = 0; i < spec->num_pwrs; i++) { + int event = is_nid_hp_pin(cfg, spec->pwr_nids[i]) + ? STAC_HP_EVENT : STAC_PWR_EVENT; + int pinctl = snd_hda_codec_read(codec, spec->pwr_nids[i], + 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + /* outputs are only ports capable of power management + * any attempts on powering down a input port cause the + * referenced VREF to act quirky. + */ + if (pinctl & AC_PINCTL_IN_EN) + continue; + enable_pin_detect(codec, spec->pwr_nids[i], event | i); + codec->patch_ops.unsol_event(codec, (event | i) << 26); + } if (cfg->dig_out_pin) stac92xx_auto_set_pinctl(codec, cfg->dig_out_pin, @@ -2273,10 +2926,8 @@ static int stac92xx_init(struct hda_codec *codec) stac92xx_auto_set_pinctl(codec, cfg->dig_in_pin, AC_PINCTL_IN_EN); - if (spec->gpio_mute) { - stac922x_gpio_mute(codec, 0, 0); - stac922x_gpio_mute(codec, 1, 0); - } + stac_gpio_set(codec, spec->gpio_mask, + spec->gpio_dir, spec->gpio_data); return 0; } @@ -2342,13 +2993,20 @@ static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid, pin_ctl & ~flag); } -static int get_pin_presence(struct hda_codec *codec, hda_nid_t nid) +static int get_hp_pin_presence(struct hda_codec *codec, hda_nid_t nid) { if (!nid) return 0; if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0x00) - & (1 << 31)) - return 1; + & (1 << 31)) { + unsigned int pinctl; + pinctl = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + if (pinctl & AC_PINCTL_IN_EN) + return 0; /* mic- or line-input */ + else + return 1; /* HP-output */ + } return 0; } @@ -2359,10 +3017,14 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res) int i, presence; presence = 0; + if (spec->gpio_mute) + presence = !(snd_hda_codec_read(codec, codec->afg, 0, + AC_VERB_GET_GPIO_DATA, 0) & spec->gpio_mute); + for (i = 0; i < cfg->hp_outs; i++) { - presence = get_pin_presence(codec, cfg->hp_pins[i]); if (presence) break; + presence = get_hp_pin_presence(codec, cfg->hp_pins[i]); } if (presence) { @@ -2384,12 +3046,37 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res) } } +static void stac92xx_pin_sense(struct hda_codec *codec, int idx) +{ + struct sigmatel_spec *spec = codec->spec; + hda_nid_t nid = spec->pwr_nids[idx]; + int presence, val; + val = snd_hda_codec_read(codec, codec->afg, 0, 0x0fec, 0x0) + & 0x000000ff; + presence = get_hp_pin_presence(codec, nid); + idx = 1 << idx; + + if (presence) + val &= ~idx; + else + val |= idx; + + /* power down unused output ports */ + snd_hda_codec_write(codec, codec->afg, 0, 0x7ec, val); +}; + static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res) { - switch (res >> 26) { + struct sigmatel_spec *spec = codec->spec; + int idx = res >> 26 & 0x0f; + + switch ((res >> 26) & 0x30) { case STAC_HP_EVENT: stac92xx_hp_detect(codec, res); - break; + /* fallthru */ + case STAC_PWR_EVENT: + if (spec->num_pwrs > 0) + stac92xx_pin_sense(codec, idx); } } @@ -2400,10 +3087,8 @@ static int stac92xx_resume(struct hda_codec *codec) stac92xx_set_config_regs(codec); snd_hda_sequence_write(codec, spec->init); - if (spec->gpio_mute) { - stac922x_gpio_mute(codec, 0, 0); - stac922x_gpio_mute(codec, 1, 0); - } + stac_gpio_set(codec, spec->gpio_mask, + spec->gpio_dir, spec->gpio_data); snd_hda_codec_resume_amp(codec); snd_hda_codec_resume_cache(codec); /* invoke unsolicited event to reset the HP state */ @@ -2460,6 +3145,7 @@ static int patch_stac9200(struct hda_codec *codec) spec->num_muxes = 1; spec->num_dmics = 0; spec->num_adcs = 1; + spec->num_pwrs = 0; if (spec->board_config == STAC_9200_GATEWAY) spec->init = stac9200_eapd_init; @@ -2515,6 +3201,7 @@ static int patch_stac925x(struct hda_codec *codec) spec->mux_nids = stac925x_mux_nids; spec->num_muxes = 1; spec->num_adcs = 1; + spec->num_pwrs = 0; switch (codec->vendor_id) { case 0x83847632: /* STAC9202 */ case 0x83847633: /* STAC9202D */ @@ -2522,6 +3209,8 @@ static int patch_stac925x(struct hda_codec *codec) case 0x83847637: /* STAC9251D */ spec->num_dmics = STAC925X_NUM_DMICS; spec->dmic_nids = stac925x_dmic_nids; + spec->num_dmuxes = ARRAY_SIZE(stac925x_dmux_nids); + spec->dmux_nids = stac925x_dmux_nids; break; default: spec->num_dmics = 0; @@ -2551,6 +3240,204 @@ static int patch_stac925x(struct hda_codec *codec) return 0; } +static struct hda_input_mux stac92hd73xx_dmux = { + .num_items = 4, + .items = { + { "Analog Inputs", 0x0b }, + { "CD", 0x08 }, + { "Digital Mic 1", 0x09 }, + { "Digital Mic 2", 0x0a }, + } +}; + +static int patch_stac92hd73xx(struct hda_codec *codec) +{ + struct sigmatel_spec *spec; + hda_nid_t conn[STAC92HD73_DAC_COUNT + 2]; + int err = 0; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + spec->num_pins = ARRAY_SIZE(stac92hd73xx_pin_nids); + spec->pin_nids = stac92hd73xx_pin_nids; + spec->board_config = snd_hda_check_board_config(codec, + STAC_92HD73XX_MODELS, + stac92hd73xx_models, + stac92hd73xx_cfg_tbl); +again: + 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); + if (err < 0) { + stac92xx_free(codec); + return err; + } + spec->pin_configs = spec->bios_pin_configs; + } else { + spec->pin_configs = stac92hd73xx_brd_tbl[spec->board_config]; + stac92xx_set_config_regs(codec); + } + + spec->multiout.num_dacs = snd_hda_get_connections(codec, 0x0a, + conn, STAC92HD73_DAC_COUNT + 2) - 1; + + if (spec->multiout.num_dacs < 0) { + printk(KERN_WARNING "hda_codec: Could not determine " + "number of channels defaulting to DAC count\n"); + spec->multiout.num_dacs = STAC92HD73_DAC_COUNT; + } + + switch (spec->multiout.num_dacs) { + case 0x3: /* 6 Channel */ + spec->mixer = stac92hd73xx_6ch_mixer; + spec->init = stac92hd73xx_6ch_core_init; + break; + case 0x4: /* 8 Channel */ + spec->multiout.hp_nid = 0x18; + spec->mixer = stac92hd73xx_8ch_mixer; + spec->init = stac92hd73xx_8ch_core_init; + break; + case 0x5: /* 10 Channel */ + spec->multiout.hp_nid = 0x19; + spec->mixer = stac92hd73xx_10ch_mixer; + spec->init = stac92hd73xx_10ch_core_init; + }; + + spec->multiout.dac_nids = stac92hd73xx_dac_nids; + spec->aloopback_mask = 0x01; + spec->aloopback_shift = 8; + + spec->mux_nids = stac92hd73xx_mux_nids; + spec->adc_nids = stac92hd73xx_adc_nids; + spec->dmic_nids = stac92hd73xx_dmic_nids; + spec->dmux_nids = stac92hd73xx_dmux_nids; + + spec->num_muxes = ARRAY_SIZE(stac92hd73xx_mux_nids); + spec->num_adcs = ARRAY_SIZE(stac92hd73xx_adc_nids); + spec->num_dmics = STAC92HD73XX_NUM_DMICS; + spec->num_dmuxes = ARRAY_SIZE(stac92hd73xx_dmux_nids); + spec->dinput_mux = &stac92hd73xx_dmux; + /* GPIO0 High = Enable EAPD */ + spec->gpio_mask = spec->gpio_dir = 0x1; + spec->gpio_data = 0x01; + + spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids); + spec->pwr_nids = stac92hd73xx_pwr_nids; + + err = stac92xx_parse_auto_config(codec, 0x22, 0x24); + + if (!err) { + if (spec->board_config < 0) { + printk(KERN_WARNING "hda_codec: No auto-config is " + "available, default to model=ref\n"); + spec->board_config = STAC_92HD73XX_REF; + goto again; + } + err = -EINVAL; + } + + if (err < 0) { + stac92xx_free(codec); + return err; + } + + codec->patch_ops = stac92xx_patch_ops; + + return 0; +} + +static int patch_stac92hd71bxx(struct hda_codec *codec) +{ + struct sigmatel_spec *spec; + int err = 0; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (spec == NULL) + return -ENOMEM; + + codec->spec = spec; + spec->num_pins = ARRAY_SIZE(stac92hd71bxx_pin_nids); + spec->pin_nids = stac92hd71bxx_pin_nids; + spec->board_config = snd_hda_check_board_config(codec, + STAC_92HD71BXX_MODELS, + stac92hd71bxx_models, + stac92hd71bxx_cfg_tbl); +again: + 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); + if (err < 0) { + stac92xx_free(codec); + return err; + } + spec->pin_configs = spec->bios_pin_configs; + } else { + spec->pin_configs = stac92hd71bxx_brd_tbl[spec->board_config]; + stac92xx_set_config_regs(codec); + } + + switch (codec->vendor_id) { + case 0x111d76b6: /* 4 Port without Analog Mixer */ + case 0x111d76b7: + case 0x111d76b4: /* 6 Port without Analog Mixer */ + case 0x111d76b5: + spec->mixer = stac92hd71bxx_mixer; + spec->init = stac92hd71bxx_core_init; + break; + default: + spec->mixer = stac92hd71bxx_analog_mixer; + spec->init = stac92hd71bxx_analog_core_init; + } + + spec->aloopback_mask = 0x20; + spec->aloopback_shift = 0; + + /* GPIO0 High = EAPD */ + spec->gpio_mask = spec->gpio_dir = spec->gpio_data = 0x1; + + spec->mux_nids = stac92hd71bxx_mux_nids; + spec->adc_nids = stac92hd71bxx_adc_nids; + spec->dmic_nids = stac92hd71bxx_dmic_nids; + spec->dmux_nids = stac92hd71bxx_dmux_nids; + + spec->num_muxes = ARRAY_SIZE(stac92hd71bxx_mux_nids); + spec->num_adcs = ARRAY_SIZE(stac92hd71bxx_adc_nids); + spec->num_dmics = STAC92HD71BXX_NUM_DMICS; + spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids); + + spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids); + spec->pwr_nids = stac92hd71bxx_pwr_nids; + + spec->multiout.num_dacs = 2; + spec->multiout.hp_nid = 0x11; + spec->multiout.dac_nids = stac92hd71bxx_dac_nids; + + err = stac92xx_parse_auto_config(codec, 0x21, 0x23); + if (!err) { + if (spec->board_config < 0) { + printk(KERN_WARNING "hda_codec: No auto-config is " + "available, default to model=ref\n"); + spec->board_config = STAC_92HD71BXX_REF; + goto again; + } + err = -EINVAL; + } + + if (err < 0) { + stac92xx_free(codec); + return err; + } + + codec->patch_ops = stac92xx_patch_ops; + + return 0; +}; + static int patch_stac922x(struct hda_codec *codec) { struct sigmatel_spec *spec; @@ -2567,7 +3454,8 @@ static int patch_stac922x(struct hda_codec *codec) stac922x_models, stac922x_cfg_tbl); if (spec->board_config == STAC_INTEL_MAC_V3) { - spec->gpio_mute = 1; + spec->gpio_mask = spec->gpio_dir = 0x03; + spec->gpio_data = 0x03; /* Intel Macs have all same PCI SSID, so we need to check * codec SSID to distinguish the exact models */ @@ -2620,6 +3508,7 @@ static int patch_stac922x(struct hda_codec *codec) spec->num_muxes = ARRAY_SIZE(stac922x_mux_nids); spec->num_adcs = ARRAY_SIZE(stac922x_adc_nids); spec->num_dmics = 0; + spec->num_pwrs = 0; spec->init = stac922x_core_init; spec->mixer = stac922x_mixer; @@ -2669,53 +3558,70 @@ static int patch_stac927x(struct hda_codec *codec) stac927x_models, stac927x_cfg_tbl); again: - if (spec->board_config < 0) { - snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC927x, using BIOS defaults\n"); + if (spec->board_config < 0 || !stac927x_brd_tbl[spec->board_config]) { + 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); if (err < 0) { stac92xx_free(codec); return err; } spec->pin_configs = spec->bios_pin_configs; - } else if (stac927x_brd_tbl[spec->board_config] != NULL) { + } else { spec->pin_configs = stac927x_brd_tbl[spec->board_config]; stac92xx_set_config_regs(codec); } + spec->adc_nids = stac927x_adc_nids; + spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids); + spec->mux_nids = stac927x_mux_nids; + spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids); + spec->multiout.dac_nids = spec->dac_nids; + switch (spec->board_config) { case STAC_D965_3ST: - spec->adc_nids = stac927x_adc_nids; - spec->mux_nids = stac927x_mux_nids; - spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids); - spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids); + case STAC_D965_5ST: + /* GPIO0 High = Enable EAPD */ + spec->gpio_mask = spec->gpio_dir = 0x01; + spec->gpio_data = 0x01; spec->num_dmics = 0; + spec->init = d965_core_init; spec->mixer = stac927x_mixer; break; - case STAC_D965_5ST: - spec->adc_nids = stac927x_adc_nids; - spec->mux_nids = stac927x_mux_nids; - spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids); - spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids); - spec->num_dmics = 0; + case STAC_DELL_BIOS: + /* correct the front output jack as a hp out */ + stac92xx_set_config_reg(codec, 0x0f, 0x02270110); + /* correct the front input jack as a mic */ + stac92xx_set_config_reg(codec, 0x0e, 0x02a79130); + /* fallthru */ + case STAC_DELL_3ST: + /* GPIO2 High = Enable EAPD */ + spec->gpio_mask = spec->gpio_dir = 0x04; + spec->gpio_data = 0x04; + spec->dmic_nids = stac927x_dmic_nids; + spec->num_dmics = STAC927X_NUM_DMICS; + spec->init = d965_core_init; spec->mixer = stac927x_mixer; + spec->dmux_nids = stac927x_dmux_nids; + spec->num_dmuxes = ARRAY_SIZE(stac927x_dmux_nids); break; default: - spec->adc_nids = stac927x_adc_nids; - spec->mux_nids = stac927x_mux_nids; - spec->num_muxes = ARRAY_SIZE(stac927x_mux_nids); - spec->num_adcs = ARRAY_SIZE(stac927x_adc_nids); + /* GPIO0 High = Enable EAPD */ + spec->gpio_mask = spec->gpio_dir = 0x1; + spec->gpio_data = 0x01; spec->num_dmics = 0; + spec->init = stac927x_core_init; spec->mixer = stac927x_mixer; } - spec->multiout.dac_nids = spec->dac_nids; - /* GPIO0 High = Enable EAPD */ - spec->gpio_mask = spec->gpio_data = 0x00000001; - stac92xx_enable_gpio_mask(codec); - + spec->num_pwrs = 0; + spec->aloopback_mask = 0x40; + spec->aloopback_shift = 0; + err = stac92xx_parse_auto_config(codec, 0x1e, 0x20); if (!err) { if (spec->board_config < 0) { @@ -2733,6 +3639,18 @@ static int patch_stac927x(struct hda_codec *codec) codec->patch_ops = stac92xx_patch_ops; + /* + * !!FIXME!! + * The STAC927x seem to require fairly long delays for certain + * command sequences. With too short delays (even if the answer + * is set to RIRB properly), it results in the silence output + * on some hardwares like Dell. + * + * The below flag enables the longer delay (see get_response + * in hda_intel.c). + */ + codec->bus->needs_damn_long_delay = 1; + return 0; } @@ -2771,11 +3689,15 @@ static int patch_stac9205(struct hda_codec *codec) spec->num_muxes = ARRAY_SIZE(stac9205_mux_nids); spec->dmic_nids = stac9205_dmic_nids; spec->num_dmics = STAC9205_NUM_DMICS; - spec->dmux_nid = 0x1d; + spec->dmux_nids = stac9205_dmux_nids; + spec->num_dmuxes = ARRAY_SIZE(stac9205_dmux_nids); + spec->num_pwrs = 0; spec->init = stac9205_core_init; spec->mixer = stac9205_mixer; + spec->aloopback_mask = 0x40; + spec->aloopback_shift = 0; spec->multiout.dac_nids = spec->dac_nids; switch (spec->board_config){ @@ -2784,19 +3706,28 @@ static int patch_stac9205(struct hda_codec *codec) stac92xx_set_config_reg(codec, 0x1f, 0x01441030); stac92xx_set_config_reg(codec, 0x20, 0x1c410030); - spec->gpio_mask = 0x00000007; /* GPIO0-2 */ - /* GPIO0 High = EAPD, GPIO1 Low = DRM, - * GPIO2 High = Headphone Mute + /* Enable unsol response for GPIO4/Dock HP connection */ + snd_hda_codec_write(codec, codec->afg, 0, + AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10); + snd_hda_codec_write_cache(codec, codec->afg, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, + (AC_USRSP_EN | STAC_HP_EVENT)); + + spec->gpio_dir = 0x0b; + spec->gpio_mask = 0x1b; + spec->gpio_mute = 0x10; + /* GPIO0 High = EAPD, GPIO1 Low = Headphone Mute, + * GPIO3 Low = DRM */ - spec->gpio_data = 0x00000005; + spec->gpio_data = 0x01; break; default: /* GPIO0 High = EAPD */ - spec->gpio_mask = spec->gpio_data = 0x00000001; + spec->gpio_mask = spec->gpio_dir = 0x1; + spec->gpio_data = 0x01; break; } - stac92xx_enable_gpio_mask(codec); err = stac92xx_parse_auto_config(codec, 0x1f, 0x20); if (!err) { if (spec->board_config < 0) { @@ -2950,7 +3881,7 @@ static int stac9872_vaio_init(struct hda_codec *codec) static void stac9872_vaio_hp_detect(struct hda_codec *codec, unsigned int res) { - if (get_pin_presence(codec, 0x0a)) { + if (get_hp_pin_presence(codec, 0x0a)) { stac92xx_reset_pinctl(codec, 0x0f, AC_PINCTL_OUT_EN); stac92xx_set_pinctl(codec, 0x0a, AC_PINCTL_OUT_EN); } else { @@ -3032,6 +3963,7 @@ static int patch_stac9872(struct hda_codec *codec) spec->multiout.hp_nid = VAIO_HP_DAC; spec->num_adcs = ARRAY_SIZE(vaio_adcs); spec->adc_nids = vaio_adcs; + spec->num_pwrs = 0; spec->input_mux = &vaio_mux; spec->mux_nids = vaio_mux_nids; codec->patch_ops = stac9872_vaio_patch_ops; @@ -3045,6 +3977,7 @@ static int patch_stac9872(struct hda_codec *codec) spec->multiout.dac_nids = vaio_dacs; spec->multiout.hp_nid = VAIO_HP_DAC; spec->num_adcs = ARRAY_SIZE(vaio_adcs); + spec->num_pwrs = 0; spec->adc_nids = vaio_adcs; spec->input_mux = &vaio_mux; spec->mux_nids = vaio_mux_nids; @@ -3104,5 +4037,17 @@ struct hda_codec_preset snd_hda_preset_sigmatel[] = { { .id = 0x838476a5, .name = "STAC9255D", .patch = patch_stac9205 }, { .id = 0x838476a6, .name = "STAC9254", .patch = patch_stac9205 }, { .id = 0x838476a7, .name = "STAC9254D", .patch = patch_stac9205 }, + { .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx }, + { .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx }, + { .id = 0x111d7676, .name = "92HD73E1X5", .patch = patch_stac92hd73xx }, + { .id = 0x111d7608, .name = "92HD71BXX", .patch = patch_stac92hd71bxx }, + { .id = 0x111d76b0, .name = "92HD71B8X", .patch = patch_stac92hd71bxx }, + { .id = 0x111d76b1, .name = "92HD71B8X", .patch = patch_stac92hd71bxx }, + { .id = 0x111d76b2, .name = "92HD71B7X", .patch = patch_stac92hd71bxx }, + { .id = 0x111d76b3, .name = "92HD71B7X", .patch = patch_stac92hd71bxx }, + { .id = 0x111d76b4, .name = "92HD71B6X", .patch = patch_stac92hd71bxx }, + { .id = 0x111d76b5, .name = "92HD71B6X", .patch = patch_stac92hd71bxx }, + { .id = 0x111d76b6, .name = "92HD71B5X", .patch = patch_stac92hd71bxx }, + { .id = 0x111d76b7, .name = "92HD71B5X", .patch = patch_stac92hd71bxx }, {} /* terminator */ }; |