summaryrefslogtreecommitdiff
path: root/sound/pci/hda
diff options
context:
space:
mode:
Diffstat (limited to 'sound/pci/hda')
-rw-r--r--sound/pci/hda/Makefile2
-rw-r--r--sound/pci/hda/hda_codec.c117
-rw-r--r--sound/pci/hda/hda_codec.h60
-rw-r--r--sound/pci/hda/hda_generic.c1
-rw-r--r--sound/pci/hda/hda_hwdep.c1
-rw-r--r--sound/pci/hda/hda_intel.c164
-rw-r--r--sound/pci/hda/hda_local.h18
-rw-r--r--sound/pci/hda/hda_proc.c361
-rw-r--r--sound/pci/hda/patch_analog.c190
-rw-r--r--sound/pci/hda/patch_atihdmi.c3
-rw-r--r--sound/pci/hda/patch_cmedia.c2
-rw-r--r--sound/pci/hda/patch_conexant.c445
-rw-r--r--sound/pci/hda/patch_realtek.c1830
-rw-r--r--sound/pci/hda/patch_si3054.c2
-rw-r--r--sound/pci/hda/patch_sigmatel.c1217
-rw-r--r--sound/pci/hda/patch_via.c679
-rw-r--r--sound/pci/hda/vmaster.c364
17 files changed, 4821 insertions, 635 deletions
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index ab0c726..9e0d8a1 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -2,7 +2,7 @@ snd-hda-intel-y := hda_intel.o
# since snd-hda-intel is the only driver using hda-codec,
# merge it into a single module although it was originally
# designed to be individual modules
-snd-hda-intel-y += hda_codec.o
+snd-hda-intel-y += hda_codec.o vmaster.o
snd-hda-intel-$(CONFIG_PROC_FS) += hda_proc.o
snd-hda-intel-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
snd-hda-intel-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 8cbe3bf..26812dc 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -19,7 +19,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>
@@ -55,6 +54,7 @@ static struct hda_vendor_id hda_vendor_ids[] = {
{ 0x10ec, "Realtek" },
{ 0x1057, "Motorola" },
{ 0x1106, "VIA" },
+ { 0x111d, "IDT" },
{ 0x11d4, "Analog Devices" },
{ 0x13f6, "C-Media" },
{ 0x14f1, "Conexant" },
@@ -429,6 +429,10 @@ find_codec_preset(struct hda_codec *codec)
for (tbl = hda_preset_tables; *tbl; tbl++) {
for (preset = *tbl; preset->id; preset++) {
u32 mask = preset->mask;
+ if (preset->afg && preset->afg != codec->afg)
+ continue;
+ if (preset->mfg && preset->mfg != codec->mfg)
+ continue;
if (!mask)
mask = ~0;
if (preset->id == (codec->vendor_id & mask) &&
@@ -765,7 +769,7 @@ get_alloc_amp_hash(struct hda_codec *codec, u32 key)
/*
* query AMP capabilities for the given widget and direction
*/
-static u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
+u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
{
struct hda_amp_info *info;
@@ -933,7 +937,8 @@ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
caps = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
if (!caps) {
printk(KERN_WARNING "hda_codec: "
- "num_steps = 0 for NID=0x%x\n", nid);
+ "num_steps = 0 for NID=0x%x (ctl = %s)\n", nid,
+ kcontrol->id.name);
return -EINVAL;
}
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
@@ -1012,6 +1017,66 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
return 0;
}
+/*
+ * set (static) TLV for virtual master volume; recalculated as max 0dB
+ */
+void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
+ unsigned int *tlv)
+{
+ u32 caps;
+ int nums, step;
+
+ caps = query_amp_caps(codec, nid, dir);
+ nums = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+ step = (caps & AC_AMPCAP_STEP_SIZE) >> AC_AMPCAP_STEP_SIZE_SHIFT;
+ step = (step + 1) * 25;
+ tlv[0] = SNDRV_CTL_TLVT_DB_SCALE;
+ tlv[1] = 2 * sizeof(unsigned int);
+ tlv[2] = -nums * step;
+ tlv[3] = step;
+}
+
+/* find a mixer control element with the given name */
+struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
+ const char *name)
+{
+ struct snd_ctl_elem_id id;
+ memset(&id, 0, sizeof(id));
+ id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ strcpy(id.name, name);
+ return snd_ctl_find_id(codec->bus->card, &id);
+}
+
+/* create a virtual master control and add slaves */
+int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
+ unsigned int *tlv, const char **slaves)
+{
+ struct snd_kcontrol *kctl;
+ const char **s;
+ int err;
+
+ kctl = snd_ctl_make_virtual_master(name, tlv);
+ if (!kctl)
+ return -ENOMEM;
+ err = snd_ctl_add(codec->bus->card, kctl);
+ if (err < 0)
+ return err;
+
+ for (s = slaves; *s; s++) {
+ struct snd_kcontrol *sctl;
+
+ sctl = snd_hda_find_mixer_ctl(codec, *s);
+ if (!sctl) {
+ snd_printdd("Cannot find slave %s, skipped\n", *s);
+ continue;
+ }
+ err = snd_ctl_add_slave(kctl, sctl);
+ if (err < 0)
+ return err;
+ }
+ return 0;
+}
+
/* switch */
int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
@@ -1434,7 +1499,8 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
return err;
}
codec->spdif_ctls =
- snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0);
+ snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_DIGI_CONVERT_1, 0);
codec->spdif_status = convert_to_spdif_status(codec->spdif_ctls);
return 0;
}
@@ -1481,7 +1547,7 @@ static int snd_hda_spdif_in_status_get(struct snd_kcontrol *kcontrol,
unsigned short val;
unsigned int sbits;
- val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0);
+ val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT_1, 0);
sbits = convert_to_spdif_status(val);
ucontrol->value.iec958.status[0] = sbits;
ucontrol->value.iec958.status[1] = sbits >> 8;
@@ -1532,7 +1598,8 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
return err;
}
codec->spdif_in_enable =
- snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT, 0) &
+ snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_DIGI_CONVERT_1, 0) &
AC_DIG1_ENABLE;
return 0;
}
@@ -1622,6 +1689,7 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
snd_hda_codec_write(codec, fg, 0, AC_VERB_SET_POWER_STATE,
power_state);
+ msleep(10); /* partial workaround for "azx_get_response timeout" */
nid = codec->start_nid;
for (i = 0; i < codec->num_nodes; i++, nid++) {
@@ -2336,7 +2404,8 @@ int snd_hda_ch_mode_put(struct hda_codec *codec,
unsigned int mode;
mode = ucontrol->value.enumerated.item[0];
- snd_assert(mode < num_chmodes, return -EINVAL);
+ if (mode >= num_chmodes)
+ return -EINVAL;
if (*max_channelsp == chmode[mode].channels)
return 0;
/* change the current channel setting */
@@ -2602,20 +2671,21 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
struct auto_pin_cfg *cfg,
hda_nid_t *ignore_nids)
{
- hda_nid_t nid, nid_start;
- int nodes;
+ hda_nid_t nid, end_nid;
short seq, assoc_line_out, assoc_speaker;
short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)];
short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)];
+ short sequences_hp[ARRAY_SIZE(cfg->hp_pins)];
memset(cfg, 0, sizeof(*cfg));
memset(sequences_line_out, 0, sizeof(sequences_line_out));
memset(sequences_speaker, 0, sizeof(sequences_speaker));
+ memset(sequences_hp, 0, sizeof(sequences_hp));
assoc_line_out = assoc_speaker = 0;
- nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid_start);
- for (nid = nid_start; nid < nodes + nid_start; nid++) {
+ end_nid = codec->start_nid + codec->num_nodes;
+ for (nid = codec->start_nid; nid < end_nid; nid++) {
unsigned int wid_caps = get_wcaps(codec, nid);
unsigned int wid_type =
(wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
@@ -2638,6 +2708,10 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
case AC_JACK_LINE_OUT:
seq = get_defcfg_sequence(def_conf);
assoc = get_defcfg_association(def_conf);
+
+ if (!(wid_caps & AC_WCAP_STEREO))
+ if (!cfg->mono_out_pin)
+ cfg->mono_out_pin = nid;
if (!assoc)
continue;
if (!assoc_line_out)
@@ -2666,9 +2740,12 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
cfg->speaker_outs++;
break;
case AC_JACK_HP_OUT:
+ seq = get_defcfg_sequence(def_conf);
+ assoc = get_defcfg_association(def_conf);
if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins))
continue;
cfg->hp_pins[cfg->hp_outs] = nid;
+ sequences_hp[cfg->hp_outs] = (assoc << 4) | seq;
cfg->hp_outs++;
break;
case AC_JACK_MIC_IN: {
@@ -2712,7 +2789,24 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
cfg->line_outs);
sort_pins_by_sequence(cfg->speaker_pins, sequences_speaker,
cfg->speaker_outs);
+ sort_pins_by_sequence(cfg->hp_pins, sequences_hp,
+ cfg->hp_outs);
+ /* if we have only one mic, make it AUTO_PIN_MIC */
+ if (!cfg->input_pins[AUTO_PIN_MIC] &&
+ cfg->input_pins[AUTO_PIN_FRONT_MIC]) {
+ cfg->input_pins[AUTO_PIN_MIC] =
+ cfg->input_pins[AUTO_PIN_FRONT_MIC];
+ cfg->input_pins[AUTO_PIN_FRONT_MIC] = 0;
+ }
+ /* ditto for line-in */
+ if (!cfg->input_pins[AUTO_PIN_LINE] &&
+ cfg->input_pins[AUTO_PIN_FRONT_LINE]) {
+ cfg->input_pins[AUTO_PIN_LINE] =
+ cfg->input_pins[AUTO_PIN_FRONT_LINE];
+ cfg->input_pins[AUTO_PIN_FRONT_LINE] = 0;
+ }
+
/*
* FIX-UP: if no line-outs are detected, try to use speaker or HP pin
* as a primary output
@@ -2766,6 +2860,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
cfg->hp_outs, cfg->hp_pins[0],
cfg->hp_pins[1], cfg->hp_pins[2],
cfg->hp_pins[3], cfg->hp_pins[4]);
+ snd_printd(" mono: mono_out=0x%x\n", cfg->mono_out_pin);
snd_printd(" inputs: mic=0x%x, fmic=0x%x, line=0x%x, fline=0x%x,"
" cd=0x%x, aux=0x%x\n",
cfg->input_pins[AUTO_PIN_MIC],
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 2bce925..f148711 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -77,12 +77,16 @@ enum {
#define AC_VERB_GET_PIN_SENSE 0x0f09
#define AC_VERB_GET_BEEP_CONTROL 0x0f0a
#define AC_VERB_GET_EAPD_BTLENABLE 0x0f0c
-#define AC_VERB_GET_DIGI_CONVERT 0x0f0d
+#define AC_VERB_GET_DIGI_CONVERT_1 0x0f0d
+#define AC_VERB_GET_DIGI_CONVERT_2 0x0f0e
#define AC_VERB_GET_VOLUME_KNOB_CONTROL 0x0f0f
/* f10-f1a: GPIO */
#define AC_VERB_GET_GPIO_DATA 0x0f15
#define AC_VERB_GET_GPIO_MASK 0x0f16
#define AC_VERB_GET_GPIO_DIRECTION 0x0f17
+#define AC_VERB_GET_GPIO_WAKE_MASK 0x0f18
+#define AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK 0x0f19
+#define AC_VERB_GET_GPIO_STICKY_MASK 0x0f1a
#define AC_VERB_GET_CONFIG_DEFAULT 0x0f1c
/* f20: AFG/MFG */
#define AC_VERB_GET_SUBSYSTEM_ID 0x0f20
@@ -110,6 +114,9 @@ enum {
#define AC_VERB_SET_GPIO_DATA 0x715
#define AC_VERB_SET_GPIO_MASK 0x716
#define AC_VERB_SET_GPIO_DIRECTION 0x717
+#define AC_VERB_SET_GPIO_WAKE_MASK 0x718
+#define AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK 0x719
+#define AC_VERB_SET_GPIO_STICKY_MASK 0x71a
#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 0x71c
#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_1 0x71d
#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_2 0x71e
@@ -135,6 +142,7 @@ enum {
#define AC_PAR_PROC_CAP 0x10
#define AC_PAR_GPIO_CAP 0x11
#define AC_PAR_AMP_OUT_CAP 0x12
+#define AC_PAR_VOL_KNB_CAP 0x13
/*
* AC_VERB_PARAMETERS results (32bit)
@@ -181,6 +189,27 @@ enum {
#define AC_SUPFMT_FLOAT32 (1<<1)
#define AC_SUPFMT_AC3 (1<<2)
+/* GP I/O count */
+#define AC_GPIO_IO_COUNT (0xff<<0)
+#define AC_GPIO_O_COUNT (0xff<<8)
+#define AC_GPIO_O_COUNT_SHIFT 8
+#define AC_GPIO_I_COUNT (0xff<<16)
+#define AC_GPIO_I_COUNT_SHIFT 16
+#define AC_GPIO_UNSOLICITED (1<<30)
+#define AC_GPIO_WAKE (1<<31)
+
+/* Converter stream, channel */
+#define AC_CONV_CHANNEL (0xf<<0)
+#define AC_CONV_STREAM (0xf<<4)
+#define AC_CONV_STREAM_SHIFT 4
+
+/* Input converter SDI select */
+#define AC_SDI_SELECT (0xf<<0)
+
+/* Unsolicited response */
+#define AC_UNSOL_TAG (0x3f<<0)
+#define AC_UNSOL_ENABLED (1<<7)
+
/* Pin widget capabilies */
#define AC_PINCAP_IMP_SENSE (1<<0) /* impedance sense capable */
#define AC_PINCAP_TRIG_REQ (1<<1) /* trigger required */
@@ -189,6 +218,10 @@ enum {
#define AC_PINCAP_OUT (1<<4) /* output capable */
#define AC_PINCAP_IN (1<<5) /* input capable */
#define AC_PINCAP_BALANCE (1<<6) /* balanced I/O capable */
+/* Note: This LR_SWAP pincap is defined in the Realtek ALC883 specification,
+ * but is marked reserved in the Intel HDA specification.
+ */
+#define AC_PINCAP_LR_SWAP (1<<7) /* L/R swap */
#define AC_PINCAP_VREF (0x37<<8)
#define AC_PINCAP_VREF_SHIFT 8
#define AC_PINCAP_EAPD (1<<16) /* EAPD capable */
@@ -222,6 +255,9 @@ enum {
#define AC_PWRST_D3SUP (1<<3)
/* Power state values */
+#define AC_PWRST_SETTING (0xf<<0)
+#define AC_PWRST_ACTUAL (0xf<<4)
+#define AC_PWRST_ACTUAL_SHIFT 4
#define AC_PWRST_D0 0x00
#define AC_PWRST_D1 0x01
#define AC_PWRST_D2 0x02
@@ -230,10 +266,11 @@ enum {
/* Processing capabilies */
#define AC_PCAP_BENIGN (1<<0)
#define AC_PCAP_NUM_COEF (0xff<<8)
+#define AC_PCAP_NUM_COEF_SHIFT 8
/* Volume knobs capabilities */
#define AC_KNBCAP_NUM_STEPS (0x7f<<0)
-#define AC_KNBCAP_DELTA (1<<8)
+#define AC_KNBCAP_DELTA (1<<7)
/*
* Control Parameters
@@ -266,6 +303,9 @@ enum {
#define AC_DIG1_PROFESSIONAL (1<<6)
#define AC_DIG1_LEVEL (1<<7)
+/* DIGITAL2 bits */
+#define AC_DIG2_CC (0x7f<<0)
+
/* Pin widget control - 8bit */
#define AC_PINCTL_VREFEN (0x7<<0)
#define AC_PINCTL_VREF_HIZ 0 /* Hi-Z */
@@ -280,12 +320,22 @@ enum {
/* Unsolicited response - 8bit */
#define AC_USRSP_EN (1<<7)
+/* Pin sense - 32bit */
+#define AC_PINSENSE_IMPEDANCE_MASK (0x7fffffff)
+#define AC_PINSENSE_PRESENCE (1<<31)
+
+/* EAPD/BTL enable - 32bit */
+#define AC_EAPDBTL_BALANCED (1<<0)
+#define AC_EAPDBTL_EAPD (1<<1)
+#define AC_EAPDBTL_LR_SWAP (1<<2)
+
/* configuration default - 32bit */
#define AC_DEFCFG_SEQUENCE (0xf<<0)
#define AC_DEFCFG_DEF_ASSOC (0xf<<4)
#define AC_DEFCFG_ASSOC_SHIFT 4
#define AC_DEFCFG_MISC (0xf<<8)
#define AC_DEFCFG_MISC_SHIFT 8
+#define AC_DEFCFG_MISC_NO_PRESENCE (1<<0)
#define AC_DEFCFG_COLOR (0xf<<12)
#define AC_DEFCFG_COLOR_SHIFT 12
#define AC_DEFCFG_CONN_TYPE (0xf<<16)
@@ -417,7 +467,7 @@ struct hda_bus_ops {
/* free the private data */
void (*private_free)(struct hda_bus *);
#ifdef CONFIG_SND_HDA_POWER_SAVE
- /* notify power-up/down from codec to contoller */
+ /* notify power-up/down from codec to controller */
void (*pm_notify)(struct hda_codec *codec);
#endif
};
@@ -456,6 +506,9 @@ struct hda_bus {
struct hda_bus_unsolicited *unsol;
struct snd_info_entry *proc;
+
+ /* misc op flags */
+ unsigned int needs_damn_long_delay :1;
};
/*
@@ -470,6 +523,7 @@ struct hda_codec_preset {
unsigned int subs;
unsigned int subs_mask;
unsigned int rev;
+ hda_nid_t afg, mfg;
const char *name;
int (*patch)(struct hda_codec *codec);
};
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index c957eb5..f9de7c4 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -20,7 +20,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <sound/core.h>
diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c
index bafb7b0..2177d9a 100644
--- a/sound/pci/hda/hda_hwdep.c
+++ b/sound/pci/hda/hda_hwdep.c
@@ -18,7 +18,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/pci.h>
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 3fa0f97..56f8a30 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -34,7 +34,6 @@
*
*/
-#include <sound/driver.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
@@ -50,29 +49,32 @@
#include "hda_codec.h"
-static int index = SNDRV_DEFAULT_IDX1;
-static char *id = SNDRV_DEFAULT_STR1;
-static char *model;
-static int position_fix;
-static int probe_mask = -1;
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static char *model[SNDRV_CARDS];
+static int position_fix[SNDRV_CARDS];
+static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
static int single_cmd;
static int enable_msi;
-module_param(index, int, 0444);
+module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
-module_param(id, charp, 0444);
+module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for Intel HD audio interface.");
-module_param(model, charp, 0444);
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Intel HD audio interface.");
+module_param_array(model, charp, NULL, 0444);
MODULE_PARM_DESC(model, "Use the given board model.");
-module_param(position_fix, int, 0444);
+module_param_array(position_fix, int, NULL, 0444);
MODULE_PARM_DESC(position_fix, "Fix DMA pointer "
"(0 = auto, 1 = none, 2 = POSBUF, 3 = FIFO size).");
-module_param(probe_mask, int, 0444);
+module_param_array(probe_mask, int, NULL, 0444);
MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1).");
module_param(single_cmd, bool, 0444);
MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs "
"(for debugging only).");
-module_param(enable_msi, int, 0);
+module_param(enable_msi, int, 0444);
MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)");
#ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -87,10 +89,6 @@ module_param(power_save_controller, bool, 0644);
MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode.");
#endif
-/* just for backward compatibility */
-static int enable;
-module_param(enable, bool, 0444);
-
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
"{Intel, ICH6M},"
@@ -98,12 +96,20 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
"{Intel, ESB2},"
"{Intel, ICH8},"
"{Intel, ICH9},"
+ "{Intel, ICH10},"
+ "{Intel, SCH},"
"{ATI, SB450},"
"{ATI, SB600},"
"{ATI, RS600},"
"{ATI, RS690},"
"{ATI, RS780},"
"{ATI, R600},"
+ "{ATI, RV630},"
+ "{ATI, RV610},"
+ "{ATI, RV670},"
+ "{ATI, RV635},"
+ "{ATI, RV620},"
+ "{ATI, RV770},"
"{VIA, VT8251},"
"{VIA, VT8237A},"
"{SiS, SIS966},"
@@ -370,6 +376,7 @@ struct azx {
/* driver types */
enum {
AZX_DRIVER_ICH,
+ AZX_DRIVER_SCH,
AZX_DRIVER_ATI,
AZX_DRIVER_ATIHDMI,
AZX_DRIVER_VIA,
@@ -380,6 +387,7 @@ enum {
static char *driver_short_names[] __devinitdata = {
[AZX_DRIVER_ICH] = "HDA Intel",
+ [AZX_DRIVER_SCH] = "HDA Intel MID",
[AZX_DRIVER_ATI] = "HDA ATI SB",
[AZX_DRIVER_ATIHDMI] = "HDA ATI HDMI",
[AZX_DRIVER_VIA] = "HDA VIA VT82xx",
@@ -547,7 +555,7 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
again:
timeout = jiffies + msecs_to_jiffies(1000);
- do {
+ for (;;) {
if (chip->polling_mode) {
spin_lock_irq(&chip->reg_lock);
azx_update_rirb(chip);
@@ -555,8 +563,15 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
}
if (!chip->rirb.cmds)
return chip->rirb.res; /* the last value */
- schedule_timeout_uninterruptible(1);
- } while (time_after_eq(timeout, jiffies));
+ if (time_after(jiffies, timeout))
+ break;
+ if (codec->bus->needs_damn_long_delay)
+ msleep(2); /* temporary workaround */
+ else {
+ udelay(10);
+ cond_resched();
+ }
+ }
if (chip->msi) {
snd_printk(KERN_WARNING "hda_intel: No response from codec, "
@@ -618,8 +633,9 @@ static int azx_single_send_cmd(struct hda_codec *codec, u32 val)
}
udelay(1);
}
- snd_printd(SFX "send_cmd timeout: IRS=0x%x, val=0x%x\n",
- azx_readw(chip, IRS), val);
+ if (printk_ratelimit())
+ snd_printd(SFX "send_cmd timeout: IRS=0x%x, val=0x%x\n",
+ azx_readw(chip, IRS), val);
return -EIO;
}
@@ -635,8 +651,9 @@ static unsigned int azx_single_get_response(struct hda_codec *codec)
return azx_readl(chip, IR);
udelay(1);
}
- snd_printd(SFX "get_response timeout: IRS=0x%x\n",
- azx_readw(chip, IRS));
+ if (printk_ratelimit())
+ snd_printd(SFX "get_response timeout: IRS=0x%x\n",
+ azx_readw(chip, IRS));
return (unsigned int)-1;
}
@@ -1031,7 +1048,8 @@ static unsigned int azx_max_codecs[] __devinitdata = {
[AZX_DRIVER_NVIDIA] = 3, /* FIXME: correct? */
};
-static int __devinit azx_codec_create(struct azx *chip, const char *model)
+static int __devinit azx_codec_create(struct azx *chip, const char *model,
+ unsigned int codec_probe_mask)
{
struct hda_bus_template bus_temp;
int c, codecs, audio_codecs, err;
@@ -1052,7 +1070,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model)
codecs = audio_codecs = 0;
for (c = 0; c < AZX_MAX_CODECS; c++) {
- if ((chip->codec_mask & (1 << c)) & probe_mask) {
+ if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
struct hda_codec *codec;
err = snd_hda_codec_new(chip->bus, c, &codec);
if (err < 0)
@@ -1065,7 +1083,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model)
if (!audio_codecs) {
/* probe additional slots if no codec is found */
for (; c < azx_max_codecs[chip->driver_type]; c++) {
- if ((chip->codec_mask & (1 << c)) & probe_mask) {
+ if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
err = snd_hda_codec_new(chip->bus, c, NULL);
if (err < 0)
continue;
@@ -1676,18 +1694,18 @@ static struct snd_pci_quirk probe_mask_list[] __devinitdata = {
{}
};
-static void __devinit check_probe_mask(struct azx *chip)
+static void __devinit check_probe_mask(struct azx *chip, int dev)
{
const struct snd_pci_quirk *q;
- if (probe_mask == -1) {
+ if (probe_mask[dev] == -1) {
q = snd_pci_quirk_lookup(chip->pci, probe_mask_list);
if (q) {
printk(KERN_INFO
"hda_intel: probe_mask set to 0x%x "
"for device %04x:%04x\n",
q->value, q->subvendor, q->subdevice);
- probe_mask = q->value;
+ probe_mask[dev] = q->value;
}
}
}
@@ -1697,17 +1715,18 @@ static void __devinit check_probe_mask(struct azx *chip)
* constructor
*/
static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
- int driver_type,
+ int dev, int driver_type,
struct azx **rchip)
{
struct azx *chip;
int err;
+ unsigned short gcap;
static struct snd_device_ops ops = {
.dev_free = azx_dev_free,
};
*rchip = NULL;
-
+
err = pci_enable_device(pci);
if (err < 0)
return err;
@@ -1727,8 +1746,8 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
chip->driver_type = driver_type;
chip->msi = enable_msi;
- chip->position_fix = check_position_fix(chip, position_fix);
- check_probe_mask(chip);
+ chip->position_fix = check_position_fix(chip, position_fix[dev]);
+ check_probe_mask(chip, dev);
chip->single_cmd = single_cmd;
@@ -1769,25 +1788,40 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
pci_set_master(pci);
synchronize_irq(chip->irq);
- switch (chip->driver_type) {
- case AZX_DRIVER_ULI:
- chip->playback_streams = ULI_NUM_PLAYBACK;
- chip->capture_streams = ULI_NUM_CAPTURE;
- chip->playback_index_offset = ULI_PLAYBACK_INDEX;
- chip->capture_index_offset = ULI_CAPTURE_INDEX;
- break;
- case AZX_DRIVER_ATIHDMI:
- chip->playback_streams = ATIHDMI_NUM_PLAYBACK;
- chip->capture_streams = ATIHDMI_NUM_CAPTURE;
- chip->playback_index_offset = ATIHDMI_PLAYBACK_INDEX;
- chip->capture_index_offset = ATIHDMI_CAPTURE_INDEX;
- break;
- default:
- chip->playback_streams = ICH6_NUM_PLAYBACK;
- chip->capture_streams = ICH6_NUM_CAPTURE;
- chip->playback_index_offset = ICH6_PLAYBACK_INDEX;
- chip->capture_index_offset = ICH6_CAPTURE_INDEX;
- break;
+ gcap = azx_readw(chip, GCAP);
+ snd_printdd("chipset global capabilities = 0x%x\n", gcap);
+
+ if (gcap) {
+ /* read number of streams from GCAP register instead of using
+ * hardcoded value
+ */
+ chip->playback_streams = (gcap & (0xF << 12)) >> 12;
+ chip->capture_streams = (gcap & (0xF << 8)) >> 8;
+ chip->playback_index_offset = (gcap & (0xF << 12)) >> 12;
+ chip->capture_index_offset = 0;
+ } else {
+ /* gcap didn't give any info, switching to old method */
+
+ switch (chip->driver_type) {
+ case AZX_DRIVER_ULI:
+ chip->playback_streams = ULI_NUM_PLAYBACK;
+ chip->capture_streams = ULI_NUM_CAPTURE;
+ chip->playback_index_offset = ULI_PLAYBACK_INDEX;
+ chip->capture_index_offset = ULI_CAPTURE_INDEX;
+ break;
+ case AZX_DRIVER_ATIHDMI:
+ chip->playback_streams = ATIHDMI_NUM_PLAYBACK;
+ chip->capture_streams = ATIHDMI_NUM_CAPTURE;
+ chip->playback_index_offset = ATIHDMI_PLAYBACK_INDEX;
+ chip->capture_index_offset = ATIHDMI_CAPTURE_INDEX;
+ break;
+ default:
+ chip->playback_streams = ICH6_NUM_PLAYBACK;
+ chip->capture_streams = ICH6_NUM_CAPTURE;
+ chip->playback_index_offset = ICH6_PLAYBACK_INDEX;
+ chip->capture_index_offset = ICH6_CAPTURE_INDEX;
+ break;
+ }
}
chip->num_streams = chip->playback_streams + chip->capture_streams;
chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev),
@@ -1869,17 +1903,25 @@ static void power_down_all_codecs(struct azx *chip)
static int __devinit azx_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
+ static int dev;
struct snd_card *card;
struct azx *chip;
int err;
- card = snd_card_new(index, id, THIS_MODULE, 0);
+ if (dev >= SNDRV_CARDS)
+ return -ENODEV;
+ if (!enable[dev]) {
+ dev++;
+ return -ENOENT;
+ }
+
+ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
if (!card) {
snd_printk(KERN_ERR SFX "Error creating card!\n");
return -ENOMEM;
}
- err = azx_create(card, pci, pci_id->driver_data, &chip);
+ err = azx_create(card, pci, dev, pci_id->driver_data, &chip);
if (err < 0) {
snd_card_free(card);
return err;
@@ -1887,7 +1929,7 @@ static int __devinit azx_probe(struct pci_dev *pci,
card->private_data = chip;
/* create codec instances */
- err = azx_codec_create(chip, model);
+ err = azx_codec_create(chip, model[dev], probe_mask[dev]);
if (err < 0) {
snd_card_free(card);
return err;
@@ -1919,6 +1961,7 @@ static int __devinit azx_probe(struct pci_dev *pci,
chip->running = 1;
power_down_all_codecs(chip);
+ dev++;
return err;
}
@@ -1936,12 +1979,21 @@ static struct pci_device_id azx_ids[] = {
{ 0x8086, 0x284b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH8 */
{ 0x8086, 0x293e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH9 */
{ 0x8086, 0x293f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH9 */
+ { 0x8086, 0x3a3e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH10 */
+ { 0x8086, 0x3a6e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH10 */
+ { 0x8086, 0x811b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_SCH }, /* SCH*/
{ 0x1002, 0x437b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB450 */
{ 0x1002, 0x4383, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB600 */
{ 0x1002, 0x793b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS600 HDMI */
{ 0x1002, 0x7919, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS690 HDMI */
- { 0x1002, 0x960c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS780 HDMI */
+ { 0x1002, 0x960f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS780 HDMI */
{ 0x1002, 0xaa00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI R600 HDMI */
+ { 0x1002, 0xaa08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV630 HDMI */
+ { 0x1002, 0xaa10, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV610 HDMI */
+ { 0x1002, 0xaa18, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV670 HDMI */
+ { 0x1002, 0xaa20, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV635 HDMI */
+ { 0x1002, 0xaa28, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV620 HDMI */
+ { 0x1002, 0xaa30, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RV770 HDMI */
{ 0x1106, 0x3288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_VIA }, /* VIA VT8251/VT8237A */
{ 0x1039, 0x7502, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_SIS }, /* SIS966 */
{ 0x10b9, 0x5461, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ULI }, /* ULI M5461 */
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 8c56c9c..ad0014a 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -90,6 +90,13 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
void snd_hda_codec_resume_amp(struct hda_codec *codec);
#endif
+void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
+ unsigned int *tlv);
+struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
+ const char *name);
+int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
+ unsigned int *tlv, const char **slaves);
+
/* amp value bits */
#define HDA_AMP_MUTE 0x80
#define HDA_AMP_UNMUTE 0x00
@@ -325,6 +332,7 @@ struct auto_pin_cfg {
hda_nid_t input_pins[AUTO_PIN_LAST];
hda_nid_t dig_out_pin;
hda_nid_t dig_in_pin;
+ hda_nid_t mono_out_pin;
};
#define get_defcfg_connect(cfg) \
@@ -363,10 +371,11 @@ static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid)
{
if (nid < codec->start_nid ||
nid >= codec->start_nid + codec->num_nodes)
- return snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
+ return 0;
return codec->wcaps[nid - codec->start_nid];
}
+u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction);
int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int caps);
@@ -398,4 +407,11 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
hda_nid_t nid);
#endif /* CONFIG_SND_HDA_POWER_SAVE */
+/*
+ * virtual master control
+ */
+struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
+ const unsigned int *tlv);
+int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave);
+
#endif /* __SOUND_HDA_LOCAL_H */
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index e94944f..35a630d 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -21,7 +21,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <sound/driver.h>
#include <linux/init.h>
#include <sound/core.h>
#include "hda_codec.h"
@@ -203,7 +202,8 @@ static const char *get_jack_color(u32 cfg)
}
static void print_pin_caps(struct snd_info_buffer *buffer,
- struct hda_codec *codec, hda_nid_t nid)
+ struct hda_codec *codec, hda_nid_t nid,
+ int *supports_vref)
{
static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" };
static char *jack_types[16] = {
@@ -213,7 +213,7 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
"SPDIF In", "Digitial In", "Reserved", "Other"
};
static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" };
- unsigned int caps;
+ unsigned int caps, val;
caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
snd_iprintf(buffer, " Pincap 0x08%x:", caps);
@@ -227,7 +227,45 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
snd_iprintf(buffer, " EAPD");
if (caps & AC_PINCAP_PRES_DETECT)
snd_iprintf(buffer, " Detect");
+ if (caps & AC_PINCAP_BALANCE)
+ snd_iprintf(buffer, " Balanced");
+ if (caps & AC_PINCAP_LR_SWAP)
+ snd_iprintf(buffer, " R/L");
+ if (caps & AC_PINCAP_TRIG_REQ)
+ snd_iprintf(buffer, " Trigger");
+ if (caps & AC_PINCAP_IMP_SENSE)
+ snd_iprintf(buffer, " ImpSense");
snd_iprintf(buffer, "\n");
+ if (caps & AC_PINCAP_VREF) {
+ unsigned int vref =
+ (caps & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
+ snd_iprintf(buffer, " Vref caps:");
+ if (vref & AC_PINCAP_VREF_HIZ)
+ snd_iprintf(buffer, " HIZ");
+ if (vref & AC_PINCAP_VREF_50)
+ snd_iprintf(buffer, " 50");
+ if (vref & AC_PINCAP_VREF_GRD)
+ snd_iprintf(buffer, " GRD");
+ if (vref & AC_PINCAP_VREF_80)
+ snd_iprintf(buffer, " 80");
+ if (vref & AC_PINCAP_VREF_100)
+ snd_iprintf(buffer, " 100");
+ snd_iprintf(buffer, "\n");
+ *supports_vref = 1;
+ } else
+ *supports_vref = 0;
+ if (caps & AC_PINCAP_EAPD) {
+ val = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_EAPD_BTLENABLE, 0);
+ snd_iprintf(buffer, " EAPD 0x%x:", val);
+ if (val & AC_EAPDBTL_BALANCED)
+ snd_iprintf(buffer, " BALANCED");
+ if (val & AC_EAPDBTL_EAPD)
+ snd_iprintf(buffer, " EAPD");
+ if (val & AC_EAPDBTL_LR_SWAP)
+ snd_iprintf(buffer, " R/L");
+ snd_iprintf(buffer, "\n");
+ }
caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
snd_iprintf(buffer, " Pin Default 0x%08x: [%s] %s at %s %s\n", caps,
jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT],
@@ -237,8 +275,233 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
snd_iprintf(buffer, " Conn = %s, Color = %s\n",
get_jack_connection(caps),
get_jack_color(caps));
+ /* Default association and sequence values refer to default grouping
+ * of pin complexes and their sequence within the group. This is used
+ * for priority and resource allocation.
+ */
+ snd_iprintf(buffer, " DefAssociation = 0x%x, Sequence = 0x%x\n",
+ (caps & AC_DEFCFG_DEF_ASSOC) >> AC_DEFCFG_ASSOC_SHIFT,
+ caps & AC_DEFCFG_SEQUENCE);
+ if (((caps & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT) &
+ AC_DEFCFG_MISC_NO_PRESENCE) {
+ /* Miscellaneous bit indicates external hardware does not
+ * support presence detection even if the pin complex
+ * indicates it is supported.
+ */
+ snd_iprintf(buffer, " Misc = NO_PRESENCE\n");
+ }
+}
+
+static void print_pin_ctls(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid,
+ int supports_vref)
+{
+ unsigned int pinctls;
+
+ pinctls = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+ snd_iprintf(buffer, " Pin-ctls: 0x%02x:", pinctls);
+ if (pinctls & AC_PINCTL_IN_EN)
+ snd_iprintf(buffer, " IN");
+ if (pinctls & AC_PINCTL_OUT_EN)
+ snd_iprintf(buffer, " OUT");
+ if (pinctls & AC_PINCTL_HP_EN)
+ snd_iprintf(buffer, " HP");
+ if (supports_vref) {
+ int vref = pinctls & AC_PINCTL_VREFEN;
+ switch (vref) {
+ case AC_PINCTL_VREF_HIZ:
+ snd_iprintf(buffer, " VREF_HIZ");
+ break;
+ case AC_PINCTL_VREF_50:
+ snd_iprintf(buffer, " VREF_50");
+ break;
+ case AC_PINCTL_VREF_GRD:
+ snd_iprintf(buffer, " VREF_GRD");
+ break;
+ case AC_PINCTL_VREF_80:
+ snd_iprintf(buffer, " VREF_80");
+ break;
+ case AC_PINCTL_VREF_100:
+ snd_iprintf(buffer, " VREF_100");
+ break;
+ }
+ }
+ snd_iprintf(buffer, "\n");
+}
+
+static void print_vol_knob(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int cap = snd_hda_param_read(codec, nid,
+ AC_PAR_VOL_KNB_CAP);
+ snd_iprintf(buffer, " Volume-Knob: delta=%d, steps=%d, ",
+ (cap >> 7) & 1, cap & 0x7f);
+ cap = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_VOLUME_KNOB_CONTROL, 0);
+ snd_iprintf(buffer, "direct=%d, val=%d\n",
+ (cap >> 7) & 1, cap & 0x7f);
+}
+
+static void print_audio_io(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid,
+ unsigned int wid_type)
+{
+ int conv = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
+ snd_iprintf(buffer,
+ " Converter: stream=%d, channel=%d\n",
+ (conv & AC_CONV_STREAM) >> AC_CONV_STREAM_SHIFT,
+ conv & AC_CONV_CHANNEL);
+
+ if (wid_type == AC_WID_AUD_IN && (conv & AC_CONV_CHANNEL) == 0) {
+ int sdi = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_SDI_SELECT, 0);
+ snd_iprintf(buffer, " SDI-Select: %d\n",
+ sdi & AC_SDI_SELECT);
+ }
+}
+
+static void print_digital_conv(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int digi1 = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_DIGI_CONVERT_1, 0);
+ unsigned int digi2 = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_DIGI_CONVERT_2, 0);
+ snd_iprintf(buffer, " Digital:");
+ if (digi1 & AC_DIG1_ENABLE)
+ snd_iprintf(buffer, " Enabled");
+ if (digi1 & AC_DIG1_V)
+ snd_iprintf(buffer, " Validity");
+ if (digi1 & AC_DIG1_VCFG)
+ snd_iprintf(buffer, " ValidityCfg");
+ if (digi1 & AC_DIG1_EMPHASIS)
+ snd_iprintf(buffer, " Preemphasis");
+ if (digi1 & AC_DIG1_COPYRIGHT)
+ snd_iprintf(buffer, " Copyright");
+ if (digi1 & AC_DIG1_NONAUDIO)
+ snd_iprintf(buffer, " Non-Audio");
+ if (digi1 & AC_DIG1_PROFESSIONAL)
+ snd_iprintf(buffer, " Pro");
+ if (digi1 & AC_DIG1_LEVEL)
+ snd_iprintf(buffer, " GenLevel");
+ snd_iprintf(buffer, "\n");
+ snd_iprintf(buffer, " Digital category: 0x%x\n", digi2 & AC_DIG2_CC);
+}
+
+static const char *get_pwr_state(u32 state)
+{
+ static const char *buf[4] = {
+ "D0", "D1", "D2", "D3"
+ };
+ if (state < 4)
+ return buf[state];
+ return "UNKNOWN";
+}
+
+static void print_power_state(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ int pwr = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_POWER_STATE, 0);
+ snd_iprintf(buffer, " Power: setting=%s, actual=%s\n",
+ get_pwr_state(pwr & AC_PWRST_SETTING),
+ get_pwr_state((pwr & AC_PWRST_ACTUAL) >>
+ AC_PWRST_ACTUAL_SHIFT));
+}
+
+static void print_unsol_cap(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ int unsol = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_UNSOLICITED_RESPONSE, 0);
+ snd_iprintf(buffer,
+ " Unsolicited: tag=%02x, enabled=%d\n",
+ unsol & AC_UNSOL_TAG,
+ (unsol & AC_UNSOL_ENABLED) ? 1 : 0);
+}
+
+static void print_proc_caps(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int proc_caps = snd_hda_param_read(codec, nid,
+ AC_PAR_PROC_CAP);
+ snd_iprintf(buffer, " Processing caps: benign=%d, ncoeff=%d\n",
+ proc_caps & AC_PCAP_BENIGN,
+ (proc_caps & AC_PCAP_NUM_COEF) >> AC_PCAP_NUM_COEF_SHIFT);
}
+static void print_conn_list(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid,
+ unsigned int wid_type, hda_nid_t *conn,
+ int conn_len)
+{
+ int c, curr = -1;
+
+ if (conn_len > 1 && wid_type != AC_WID_AUD_MIX)
+ curr = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_CONNECT_SEL, 0);
+ snd_iprintf(buffer, " Connection: %d\n", conn_len);
+ if (conn_len > 0) {
+ snd_iprintf(buffer, " ");
+ for (c = 0; c < conn_len; c++) {
+ snd_iprintf(buffer, " 0x%02x", conn[c]);
+ if (c == curr)
+ snd_iprintf(buffer, "*");
+ }
+ snd_iprintf(buffer, "\n");
+ }
+}
+
+static void print_realtek_coef(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ int coeff = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_PROC_COEF, 0);
+ snd_iprintf(buffer, " Processing Coefficient: 0x%02x\n", coeff);
+ coeff = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_COEF_INDEX, 0);
+ snd_iprintf(buffer, " Coefficient Index: 0x%02x\n", coeff);
+}
+
+static void print_gpio(struct snd_info_buffer *buffer,
+ struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int gpio =
+ snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP);
+ unsigned int enable, direction, wake, unsol, sticky, data;
+ int i, max;
+ snd_iprintf(buffer, "GPIO: io=%d, o=%d, i=%d, "
+ "unsolicited=%d, wake=%d\n",
+ gpio & AC_GPIO_IO_COUNT,
+ (gpio & AC_GPIO_O_COUNT) >> AC_GPIO_O_COUNT_SHIFT,
+ (gpio & AC_GPIO_I_COUNT) >> AC_GPIO_I_COUNT_SHIFT,
+ (gpio & AC_GPIO_UNSOLICITED) ? 1 : 0,
+ (gpio & AC_GPIO_WAKE) ? 1 : 0);
+ max = gpio & AC_GPIO_IO_COUNT;
+ enable = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_GPIO_MASK, 0);
+ direction = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_GPIO_DIRECTION, 0);
+ wake = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_GPIO_WAKE_MASK, 0);
+ unsol = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK, 0);
+ sticky = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_GPIO_STICKY_MASK, 0);
+ data = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_GPIO_DATA, 0);
+ for (i = 0; i < max; ++i)
+ snd_iprintf(buffer,
+ " IO[%d]: enable=%d, dir=%d, wake=%d, "
+ "sticky=%d, data=%d\n", i,
+ (enable & (1<<i)) ? 1 : 0,
+ (direction & (1<<i)) ? 1 : 0,
+ (wake & (1<<i)) ? 1 : 0,
+ (sticky & (1<<i)) ? 1 : 0,
+ (data & (1<<i)) ? 1 : 0);
+ /* FIXME: add GPO and GPI pin information */
+}
static void print_codec_info(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
@@ -276,14 +539,17 @@ static void print_codec_info(struct snd_info_entry *entry,
snd_hda_power_down(codec);
return;
}
+
+ print_gpio(buffer, codec, codec->afg);
+
for (i = 0; i < nodes; i++, nid++) {
unsigned int wid_caps =
snd_hda_param_read(codec, nid,
AC_PAR_AUDIO_WIDGET_CAP);
unsigned int wid_type =
(wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
- int conn_len = 0;
hda_nid_t conn[HDA_MAX_CONNECTIONS];
+ int conn_len = 0;
snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid,
get_wid_type_name(wid_type), wid_caps);
@@ -297,8 +563,18 @@ static void print_codec_info(struct snd_info_entry *entry,
snd_iprintf(buffer, " Amp-In");
if (wid_caps & AC_WCAP_OUT_AMP)
snd_iprintf(buffer, " Amp-Out");
+ if (wid_caps & AC_WCAP_STRIPE)
+ snd_iprintf(buffer, " Stripe");
+ if (wid_caps & AC_WCAP_LR_SWAP)
+ snd_iprintf(buffer, " R/L");
snd_iprintf(buffer, "\n");
+ /* volume knob is a special widget that always have connection
+ * list
+ */
+ if (wid_type == AC_WID_VOL_KNB)
+ wid_caps |= AC_WCAP_CONN_LIST;
+
if (wid_caps & AC_WCAP_CONN_LIST)
conn_len = snd_hda_get_connections(codec, nid, conn,
HDA_MAX_CONNECTIONS);
@@ -318,48 +594,49 @@ static void print_codec_info(struct snd_info_entry *entry,
wid_caps & AC_WCAP_STEREO, 1);
}
- if (wid_type == AC_WID_PIN) {
- unsigned int pinctls;
- print_pin_caps(buffer, codec, nid);
- pinctls = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_PIN_WIDGET_CONTROL,
- 0);
- snd_iprintf(buffer, " Pin-ctls: 0x%02x:", pinctls);
- if (pinctls & AC_PINCTL_IN_EN)
- snd_iprintf(buffer, " IN");
- if (pinctls & AC_PINCTL_OUT_EN)
- snd_iprintf(buffer, " OUT");
- if (pinctls & AC_PINCTL_HP_EN)
- snd_iprintf(buffer, " HP");
- snd_iprintf(buffer, "\n");
+ switch (wid_type) {
+ case AC_WID_PIN: {
+ int supports_vref;
+ print_pin_caps(buffer, codec, nid, &supports_vref);
+ print_pin_ctls(buffer, codec, nid, supports_vref);
+ break;
}
-
- if ((wid_type == AC_WID_AUD_OUT || wid_type == AC_WID_AUD_IN) &&
- (wid_caps & AC_WCAP_FORMAT_OVRD)) {
- snd_iprintf(buffer, " PCM:\n");
- print_pcm_caps(buffer, codec, nid);
+ case AC_WID_VOL_KNB:
+ print_vol_knob(buffer, codec, nid);
+ break;
+ case AC_WID_AUD_OUT:
+ case AC_WID_AUD_IN:
+ print_audio_io(buffer, codec, nid, wid_type);
+ if (wid_caps & AC_WCAP_DIGITAL)
+ print_digital_conv(buffer, codec, nid);
+ if (wid_caps & AC_WCAP_FORMAT_OVRD) {
+ snd_iprintf(buffer, " PCM:\n");
+ print_pcm_caps(buffer, codec, nid);
+ }
+ break;
}
+ if (wid_caps & AC_WCAP_UNSOL_CAP)
+ print_unsol_cap(buffer, codec, nid);
+
if (wid_caps & AC_WCAP_POWER)
- snd_iprintf(buffer, " Power: 0x%x\n",
- snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_POWER_STATE,
- 0));
-
- if (wid_caps & AC_WCAP_CONN_LIST) {
- int c, curr = -1;
- if (conn_len > 1 && wid_type != AC_WID_AUD_MIX)
- curr = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_CONNECT_SEL, 0);
- snd_iprintf(buffer, " Connection: %d\n", conn_len);
- snd_iprintf(buffer, " ");
- for (c = 0; c < conn_len; c++) {
- snd_iprintf(buffer, " 0x%02x", conn[c]);
- if (c == curr)
- snd_iprintf(buffer, "*");
- }
- snd_iprintf(buffer, "\n");
- }
+ print_power_state(buffer, codec, nid);
+
+ if (wid_caps & AC_WCAP_DELAY)
+ snd_iprintf(buffer, " Delay: %d samples\n",
+ (wid_caps & AC_WCAP_DELAY) >>
+ AC_WCAP_DELAY_SHIFT);
+
+ if (wid_caps & AC_WCAP_CONN_LIST)
+ print_conn_list(buffer, codec, nid, wid_type,
+ conn, conn_len);
+
+ if (wid_caps & AC_WCAP_PROC_WID)
+ print_proc_caps(buffer, codec, nid);
+
+ /* NID 0x20 == Realtek Define Registers */
+ if (codec->vendor_id == 0x10ec && nid == 0x20)
+ print_realtek_coef(buffer, codec, nid);
}
snd_hda_power_down(codec);
}
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index 196ad3c..19f0884 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -19,7 +19,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>
@@ -79,6 +78,11 @@ struct ad198x_spec {
#ifdef CONFIG_SND_HDA_POWER_SAVE
struct hda_loopback_check loopback;
#endif
+ /* for virtual master */
+ hda_nid_t vmaster_nid;
+ u32 vmaster_tlv[4];
+ const char **slave_vols;
+ const char **slave_sws;
};
/*
@@ -126,6 +130,32 @@ static int ad198x_init(struct hda_codec *codec)
return 0;
}
+static const char *ad_slave_vols[] = {
+ "Front Playback Volume",
+ "Surround Playback Volume",
+ "Center Playback Volume",
+ "LFE Playback Volume",
+ "Side Playback Volume",
+ "Headphone Playback Volume",
+ "Mono Playback Volume",
+ "Speaker Playback Volume",
+ "IEC958 Playback Volume",
+ NULL
+};
+
+static const char *ad_slave_sws[] = {
+ "Front Playback Switch",
+ "Surround Playback Switch",
+ "Center Playback Switch",
+ "LFE Playback Switch",
+ "Side Playback Switch",
+ "Headphone Playback Switch",
+ "Mono Playback Switch",
+ "Speaker Playback Switch",
+ "IEC958 Playback Switch",
+ NULL
+};
+
static int ad198x_build_controls(struct hda_codec *codec)
{
struct ad198x_spec *spec = codec->spec;
@@ -147,6 +177,27 @@ static int ad198x_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->vmaster_nid,
+ HDA_OUTPUT, spec->vmaster_tlv);
+ err = snd_hda_add_vmaster(codec, "Master Playback Volume",
+ spec->vmaster_tlv,
+ (spec->slave_vols ?
+ spec->slave_vols : ad_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,
+ (spec->slave_sws ?
+ spec->slave_sws : ad_slave_sws));
+ if (err < 0)
+ return err;
+ }
+
return 0;
}
@@ -370,7 +421,7 @@ static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
int invert = (kcontrol->private_value >> 8) & 1;
hda_nid_t nid = kcontrol->private_value & 0xff;
unsigned int eapd;
- eapd = ucontrol->value.integer.value[0];
+ eapd = !!ucontrol->value.integer.value[0];
if (invert)
eapd = !eapd;
if (eapd == spec->cur_eapd)
@@ -833,27 +884,29 @@ static const char *ad1986a_models[AD1986A_MODELS] = {
static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
+ SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
+ SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
+ SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
+ SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD),
SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_LAPTOP_EAPD),
SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_LAPTOP_EAPD),
SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
+ SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
@@ -872,6 +925,13 @@ static struct hda_amp_list ad1986a_loopbacks[] = {
};
#endif
+static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
+{
+ unsigned int conf = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_CONFIG_DEFAULT, 0);
+ return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
+}
+
static int patch_ad1986a(struct hda_codec *codec)
{
struct ad198x_spec *spec;
@@ -898,6 +958,7 @@ static int patch_ad1986a(struct hda_codec *codec)
#ifdef CONFIG_SND_HDA_POWER_SAVE
spec->loopback.amplist = ad1986a_loopbacks;
#endif
+ spec->vmaster_nid = 0x1b;
codec->patch_ops = ad198x_patch_ops;
@@ -930,7 +991,8 @@ static int patch_ad1986a(struct hda_codec *codec)
spec->multiout.max_channels = 2;
spec->multiout.num_dacs = 1;
spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
- spec->multiout.dig_out_nid = 0;
+ if (!is_jack_available(codec, 0x25))
+ spec->multiout.dig_out_nid = 0;
spec->input_mux = &ad1986a_laptop_eapd_capture_source;
break;
case AD1986A_LAPTOP_AUTOMUTE:
@@ -941,7 +1003,8 @@ static int patch_ad1986a(struct hda_codec *codec)
spec->multiout.max_channels = 2;
spec->multiout.num_dacs = 1;
spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
- spec->multiout.dig_out_nid = 0;
+ if (!is_jack_available(codec, 0x25))
+ spec->multiout.dig_out_nid = 0;
spec->input_mux = &ad1986a_laptop_eapd_capture_source;
codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
codec->patch_ops.init = ad1986a_hp_init;
@@ -1020,6 +1083,8 @@ static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ad198x_spec *spec = codec->spec;
+ if (ucontrol->value.enumerated.item[0] > 1)
+ return -EINVAL;
if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
spec->spdif_route = ucontrol->value.enumerated.item[0];
snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
@@ -1138,6 +1203,7 @@ static int patch_ad1983(struct hda_codec *codec)
#ifdef CONFIG_SND_HDA_POWER_SAVE
spec->loopback.amplist = ad1983_loopbacks;
#endif
+ spec->vmaster_nid = 0x05;
codec->patch_ops = ad198x_patch_ops;
@@ -1496,14 +1562,14 @@ static const char *ad1981_models[AD1981_MODELS] = {
};
static struct snd_pci_quirk ad1981_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
/* All HP models */
SND_PCI_QUIRK(0x103c, 0, "HP nx", AD1981_HP),
- /* HP nx6320 (reversed SSID, H/W bug) */
- SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
+ SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
/* Lenovo Thinkpad T60/X60/Z6xx */
SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1981_THINKPAD),
- SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
- SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
+ /* HP nx6320 (reversed SSID, H/W bug) */
+ SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
{}
};
@@ -1534,6 +1600,7 @@ static int patch_ad1981(struct hda_codec *codec)
#ifdef CONFIG_SND_HDA_POWER_SAVE
spec->loopback.amplist = ad1981_loopbacks;
#endif
+ spec->vmaster_nid = 0x05;
codec->patch_ops = ad198x_patch_ops;
@@ -1908,7 +1975,6 @@ static struct snd_kcontrol_new ad1988_capture_mixers[] = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* The multiple "Capture Source" controls confuse alsamixer
* So call somewhat different..
- * FIXME: the controls appear in the "playback" view!
*/
/* .name = "Capture Source", */
.name = "Input Source",
@@ -1965,6 +2031,8 @@ static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
int change;
val = ucontrol->value.enumerated.item[0];
+ if (val > 3)
+ return -EINVAL;
if (!val) {
sel = snd_hda_codec_read(codec, 0x1d, 0,
AC_VERB_GET_AMP_GAIN_MUTE,
@@ -2079,6 +2147,8 @@ static struct hda_verb ad1988_6stack_init_verbs[] = {
{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
{0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
{0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
+ /* Analog CD Input */
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
{ }
};
@@ -2720,8 +2790,8 @@ static const char *ad1988_models[AD1988_MODEL_LAST] = {
};
static struct snd_pci_quirk ad1988_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
+ SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
{}
};
@@ -2843,6 +2913,7 @@ static int patch_ad1988(struct hda_codec *codec)
#ifdef CONFIG_SND_HDA_POWER_SAVE
spec->loopback.amplist = ad1988_loopbacks;
#endif
+ spec->vmaster_nid = 0x04;
return 0;
}
@@ -2919,7 +2990,6 @@ static struct snd_kcontrol_new ad1884_base_mixers[] = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* The multiple "Capture Source" controls confuse alsamixer
* So call somewhat different..
- * FIXME: the controls appear in the "playback" view!
*/
/* .name = "Capture Source", */
.name = "Input Source",
@@ -3009,6 +3079,20 @@ static struct hda_amp_list ad1884_loopbacks[] = {
};
#endif
+static const char *ad1884_slave_vols[] = {
+ "PCM Playback Volume",
+ "Mic Playback Volume",
+ "Mono Playback Volume",
+ "Front Mic Playback Volume",
+ "Mic Playback Volume",
+ "CD Playback Volume",
+ "Internal Mic Playback Volume",
+ "Docking Mic Playback Volume"
+ "Beep Playback Volume",
+ "IEC958 Playback Volume",
+ NULL
+};
+
static int patch_ad1884(struct hda_codec *codec)
{
struct ad198x_spec *spec;
@@ -3036,6 +3120,9 @@ static int patch_ad1884(struct hda_codec *codec)
#ifdef CONFIG_SND_HDA_POWER_SAVE
spec->loopback.amplist = ad1884_loopbacks;
#endif
+ spec->vmaster_nid = 0x04;
+ /* we need to cover all playback volumes */
+ spec->slave_vols = ad1884_slave_vols;
codec->patch_ops = ad198x_patch_ops;
@@ -3054,6 +3141,20 @@ static struct hda_input_mux ad1984_thinkpad_capture_source = {
},
};
+
+/*
+ * Dell Precision T3400
+ */
+static struct hda_input_mux ad1984_dell_desktop_capture_source = {
+ .num_items = 3,
+ .items = {
+ { "Front Mic", 0x0 },
+ { "Line-In", 0x1 },
+ { "Mix", 0x3 },
+ },
+};
+
+
static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
/* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
@@ -3078,7 +3179,6 @@ static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* The multiple "Capture Source" controls confuse alsamixer
* So call somewhat different..
- * FIXME: the controls appear in the "playback" view!
*/
/* .name = "Capture Source", */
.name = "Input Source",
@@ -3087,6 +3187,16 @@ static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
.get = ad198x_mux_enum_get,
.put = ad198x_mux_enum_put,
},
+ /* SPDIF controls */
+ HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
+ /* identical with ad1983 */
+ .info = ad1983_spdif_route_info,
+ .get = ad1983_spdif_route_get,
+ .put = ad1983_spdif_route_put,
+ },
{ } /* end */
};
@@ -3104,6 +3214,44 @@ static struct hda_verb ad1984_thinkpad_init_verbs[] = {
{ } /* end */
};
+/*
+ * Dell Precision T3400
+ */
+static struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
+ HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
+ /*
+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT),
+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT),
+ */
+ HDA_CODEC_VOLUME("Line-In Boost", 0x15, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* The multiple "Capture Source" controls confuse alsamixer
+ * So call somewhat different..
+ */
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 2,
+ .info = ad198x_mux_enum_info,
+ .get = ad198x_mux_enum_get,
+ .put = ad198x_mux_enum_put,
+ },
+ { } /* end */
+};
+
/* Digial MIC ADC NID 0x05 + 0x06 */
static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
@@ -3157,17 +3305,20 @@ static int ad1984_build_pcms(struct hda_codec *codec)
enum {
AD1984_BASIC,
AD1984_THINKPAD,
+ AD1984_DELL_DESKTOP,
AD1984_MODELS
};
static const char *ad1984_models[AD1984_MODELS] = {
[AD1984_BASIC] = "basic",
[AD1984_THINKPAD] = "thinkpad",
+ [AD1984_DELL_DESKTOP] = "dell_desktop",
};
static struct snd_pci_quirk ad1984_cfg_tbl[] = {
/* Lenovo Thinkpad T61/X61 */
SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1984_THINKPAD),
+ SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
{}
};
@@ -3189,11 +3340,16 @@ static int patch_ad1984(struct hda_codec *codec)
codec->patch_ops.build_pcms = ad1984_build_pcms;
break;
case AD1984_THINKPAD:
- spec->multiout.dig_out_nid = 0;
+ spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
spec->input_mux = &ad1984_thinkpad_capture_source;
spec->mixers[0] = ad1984_thinkpad_mixers;
spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
break;
+ case AD1984_DELL_DESKTOP:
+ spec->multiout.dig_out_nid = 0;
+ spec->input_mux = &ad1984_dell_desktop_capture_source;
+ spec->mixers[0] = ad1984_dell_desktop_mixers;
+ break;
}
return 0;
}
@@ -3267,7 +3423,6 @@ static struct snd_kcontrol_new ad1882_base_mixers[] = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* The multiple "Capture Source" controls confuse alsamixer
* So call somewhat different..
- * FIXME: the controls appear in the "playback" view!
*/
/* .name = "Capture Source", */
.name = "Input Source",
@@ -3468,6 +3623,7 @@ static int patch_ad1882(struct hda_codec *codec)
#ifdef CONFIG_SND_HDA_POWER_SAVE
spec->loopback.amplist = ad1882_loopbacks;
#endif
+ spec->vmaster_nid = 0x04;
codec->patch_ops = ad198x_patch_ops;
diff --git a/sound/pci/hda/patch_atihdmi.c b/sound/pci/hda/patch_atihdmi.c
index fbb8969..9a8bb4c 100644
--- a/sound/pci/hda/patch_atihdmi.c
+++ b/sound/pci/hda/patch_atihdmi.c
@@ -21,7 +21,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>
@@ -158,6 +157,6 @@ struct hda_codec_preset snd_hda_preset_atihdmi[] = {
{ .id = 0x1002793c, .name = "ATI RS600 HDMI", .patch = patch_atihdmi },
{ .id = 0x10027919, .name = "ATI RS600 HDMI", .patch = patch_atihdmi },
{ .id = 0x1002791a, .name = "ATI RS690/780 HDMI", .patch = patch_atihdmi },
- { .id = 0x1002aa01, .name = "ATI R600 HDMI", .patch = patch_atihdmi },
+ { .id = 0x1002aa01, .name = "ATI R6xx HDMI", .patch = patch_atihdmi },
{} /* terminator */
};
diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c
index 6c54793..3d6097b 100644
--- a/sound/pci/hda/patch_cmedia.c
+++ b/sound/pci/hda/patch_cmedia.c
@@ -21,7 +21,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>
@@ -186,7 +185,6 @@ static struct snd_kcontrol_new cmi9880_basic_mixer[] = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* The multiple "Capture Source" controls confuse alsamixer
* So call somewhat different..
- * FIXME: the controls appear in the "playback" view!
*/
/* .name = "Capture Source", */
.name = "Input Source",
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 6aa0739..f6dd51c 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -20,7 +20,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>
@@ -65,6 +64,11 @@ struct conexant_spec {
hda_nid_t *adc_nids;
hda_nid_t dig_in_nid; /* digital-in NID; optional */
+ unsigned int cur_adc_idx;
+ hda_nid_t cur_adc;
+ unsigned int cur_adc_stream_tag;
+ unsigned int cur_adc_format;
+
/* capture source */
const struct hda_input_mux *input_mux;
hda_nid_t *capsrc_nids;
@@ -218,6 +222,41 @@ static struct hda_pcm_stream conexant_pcm_digital_capture = {
/* NID is set in alc_build_pcms */
};
+static int cx5051_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct conexant_spec *spec = codec->spec;
+ spec->cur_adc = spec->adc_nids[spec->cur_adc_idx];
+ spec->cur_adc_stream_tag = stream_tag;
+ spec->cur_adc_format = format;
+ snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
+ return 0;
+}
+
+static int cx5051_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct conexant_spec *spec = codec->spec;
+ snd_hda_codec_setup_stream(codec, spec->cur_adc, 0, 0, 0);
+ spec->cur_adc = 0;
+ return 0;
+}
+
+static struct hda_pcm_stream cx5051_pcm_analog_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = 0, /* fill later */
+ .ops = {
+ .prepare = cx5051_capture_pcm_prepare,
+ .cleanup = cx5051_capture_pcm_cleanup
+ },
+};
+
static int conexant_build_pcms(struct hda_codec *codec)
{
struct conexant_spec *spec = codec->spec;
@@ -232,7 +271,12 @@ static int conexant_build_pcms(struct hda_codec *codec)
spec->multiout.max_channels;
info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
spec->multiout.dac_nids[0];
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = conexant_pcm_analog_capture;
+ if (codec->vendor_id == 0x14f15051)
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+ cx5051_pcm_analog_capture;
+ else
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+ conexant_pcm_analog_capture;
info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
@@ -373,7 +417,7 @@ static int cxt_eapd_put(struct snd_kcontrol *kcontrol,
hda_nid_t nid = kcontrol->private_value & 0xff;
unsigned int eapd;
- eapd = ucontrol->value.integer.value[0];
+ eapd = !!ucontrol->value.integer.value[0];
if (invert)
eapd = !eapd;
if (eapd == spec->cur_eapd)
@@ -454,7 +498,16 @@ static struct hda_input_mux cxt5045_capture_source = {
.num_items = 2,
.items = {
{ "IntMic", 0x1 },
- { "LineIn", 0x2 },
+ { "ExtMic", 0x2 },
+ }
+};
+
+static struct hda_input_mux cxt5045_capture_source_benq = {
+ .num_items = 3,
+ .items = {
+ { "IntMic", 0x1 },
+ { "ExtMic", 0x2 },
+ { "LineIn", 0x3 },
}
};
@@ -577,6 +630,15 @@ static struct snd_kcontrol_new cxt5045_mixers[] = {
{}
};
+static struct snd_kcontrol_new cxt5045_benq_mixers[] = {
+ HDA_CODEC_VOLUME("Line In Capture Volume", 0x1a, 0x03, HDA_INPUT),
+ HDA_CODEC_MUTE("Line In Capture Switch", 0x1a, 0x03, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line In Playback Volume", 0x17, 0x3, HDA_INPUT),
+ HDA_CODEC_MUTE("Line In Playback Switch", 0x17, 0x3, HDA_INPUT),
+
+ {}
+};
+
static struct hda_verb cxt5045_init_verbs[] = {
/* Line in, Mic */
{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
@@ -602,6 +664,30 @@ static struct hda_verb cxt5045_init_verbs[] = {
{ } /* end */
};
+static struct hda_verb cxt5045_benq_init_verbs[] = {
+ /* Int Mic, Mic */
+ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN|AC_PINCTL_VREF_80 },
+ /* Line In,HP, Amp */
+ {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x10, AC_VERB_SET_CONNECT_SEL, 0x1},
+ {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x11, AC_VERB_SET_CONNECT_SEL, 0x1},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+ /* Record selector: Int mic */
+ {0x1a, AC_VERB_SET_CONNECT_SEL, 0x1},
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE,
+ AC_AMP_SET_INPUT|AC_AMP_SET_RIGHT|AC_AMP_SET_LEFT|0x17},
+ /* SPDIF route: PCM */
+ {0x13, AC_VERB_SET_CONNECT_SEL, 0x0},
+ /* EAPD */
+ {0x10, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
+ { } /* end */
+};
static struct hda_verb cxt5045_hp_sense_init_verbs[] = {
/* pin sensing on HP jack */
@@ -740,8 +826,10 @@ static int cxt5045_init(struct hda_codec *codec)
enum {
- CXT5045_LAPTOP, /* Laptops w/ EAPD support */
- CXT5045_FUJITSU, /* Laptops w/ EAPD support */
+ CXT5045_LAPTOP_HPSENSE,
+ CXT5045_LAPTOP_MICSENSE,
+ CXT5045_LAPTOP_HPMICSENSE,
+ CXT5045_BENQ,
#ifdef CONFIG_SND_DEBUG
CXT5045_TEST,
#endif
@@ -749,23 +837,35 @@ enum {
};
static const char *cxt5045_models[CXT5045_MODELS] = {
- [CXT5045_LAPTOP] = "laptop",
- [CXT5045_FUJITSU] = "fujitsu",
+ [CXT5045_LAPTOP_HPSENSE] = "laptop-hpsense",
+ [CXT5045_LAPTOP_MICSENSE] = "laptop-micsense",
+ [CXT5045_LAPTOP_HPMICSENSE] = "laptop-hpmicsense",
+ [CXT5045_BENQ] = "benq",
#ifdef CONFIG_SND_DEBUG
[CXT5045_TEST] = "test",
#endif
};
static struct snd_pci_quirk cxt5045_cfg_tbl[] = {
- SND_PCI_QUIRK(0x103c, 0x30b7, "HP DV6000Z", CXT5045_LAPTOP),
- SND_PCI_QUIRK(0x103c, 0x30bb, "HP DV8000", CXT5045_LAPTOP),
- SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV Series", CXT5045_LAPTOP),
- SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2120", CXT5045_LAPTOP),
- SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP),
- SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP),
- SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_FUJITSU),
- SND_PCI_QUIRK(0x1734, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP),
- SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP),
+ SND_PCI_QUIRK(0x103c, 0x30a5, "HP", CXT5045_LAPTOP_HPSENSE),
+ SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV Series", CXT5045_LAPTOP_HPSENSE),
+ SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2120", CXT5045_LAPTOP_HPSENSE),
+ SND_PCI_QUIRK(0x103c, 0x30b7, "HP DV6000Z", CXT5045_LAPTOP_HPSENSE),
+ SND_PCI_QUIRK(0x103c, 0x30bb, "HP DV8000", CXT5045_LAPTOP_HPSENSE),
+ SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP_HPSENSE),
+ SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV9533EG", CXT5045_LAPTOP_HPSENSE),
+ SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HPSENSE),
+ SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP_HPSENSE),
+ SND_PCI_QUIRK(0x152d, 0x0753, "Benq R55E", CXT5045_BENQ),
+ SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_LAPTOP_MICSENSE),
+ SND_PCI_QUIRK(0x1734, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP_HPMICSENSE),
+ SND_PCI_QUIRK(0x1734, 0x110e, "Fujitsu V5505", CXT5045_LAPTOP_HPSENSE),
+ SND_PCI_QUIRK(0x1509, 0x1e40, "FIC", CXT5045_LAPTOP_HPMICSENSE),
+ SND_PCI_QUIRK(0x1509, 0x2f05, "FIC", CXT5045_LAPTOP_HPMICSENSE),
+ SND_PCI_QUIRK(0x1509, 0x2f06, "FIC", CXT5045_LAPTOP_HPMICSENSE),
+ SND_PCI_QUIRK(0x1631, 0xc106, "Packard Bell", CXT5045_LAPTOP_HPMICSENSE),
+ SND_PCI_QUIRK(0x1631, 0xc107, "Packard Bell", CXT5045_LAPTOP_HPMICSENSE),
+ SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP_HPSENSE),
{}
};
@@ -803,7 +903,7 @@ static int patch_cxt5045(struct hda_codec *codec)
cxt5045_models,
cxt5045_cfg_tbl);
switch (board_config) {
- case CXT5045_LAPTOP:
+ case CXT5045_LAPTOP_HPSENSE:
codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
spec->input_mux = &cxt5045_capture_source;
spec->num_init_verbs = 2;
@@ -811,20 +911,53 @@ static int patch_cxt5045(struct hda_codec *codec)
spec->mixers[0] = cxt5045_mixers;
codec->patch_ops.init = cxt5045_init;
break;
- case CXT5045_FUJITSU:
+ case CXT5045_LAPTOP_MICSENSE:
spec->input_mux = &cxt5045_capture_source;
spec->num_init_verbs = 2;
spec->init_verbs[1] = cxt5045_mic_sense_init_verbs;
spec->mixers[0] = cxt5045_mixers;
codec->patch_ops.init = cxt5045_init;
break;
+ default:
+ case CXT5045_LAPTOP_HPMICSENSE:
+ codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
+ spec->input_mux = &cxt5045_capture_source;
+ spec->num_init_verbs = 3;
+ spec->init_verbs[1] = cxt5045_hp_sense_init_verbs;
+ spec->init_verbs[2] = cxt5045_mic_sense_init_verbs;
+ spec->mixers[0] = cxt5045_mixers;
+ codec->patch_ops.init = cxt5045_init;
+ break;
+ case CXT5045_BENQ:
+ codec->patch_ops.unsol_event = cxt5045_hp_unsol_event;
+ spec->input_mux = &cxt5045_capture_source_benq;
+ spec->num_init_verbs = 1;
+ spec->init_verbs[0] = cxt5045_benq_init_verbs;
+ spec->mixers[0] = cxt5045_mixers;
+ spec->mixers[1] = cxt5045_benq_mixers;
+ spec->num_mixers = 2;
+ codec->patch_ops.init = cxt5045_init;
+ break;
#ifdef CONFIG_SND_DEBUG
case CXT5045_TEST:
spec->input_mux = &cxt5045_test_capture_source;
spec->mixers[0] = cxt5045_test_mixer;
spec->init_verbs[0] = cxt5045_test_init_verbs;
+ break;
+
#endif
}
+
+ /*
+ * Fix max PCM level to 0 dB
+ * (originall it has 0x2b steps with 0dB offset 0x14)
+ */
+ snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT,
+ (0x14 << AC_AMPCAP_OFFSET_SHIFT) |
+ (0x14 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+ (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+ (1 << AC_AMPCAP_MUTE_SHIFT));
+
return 0;
}
@@ -933,13 +1066,13 @@ static void cxt5047_hp2_automute(struct hda_codec *codec)
static void cxt5047_hp_automic(struct hda_codec *codec)
{
static struct hda_verb mic_jack_on[] = {
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
{}
};
static struct hda_verb mic_jack_off[] = {
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
{}
};
unsigned int present;
@@ -956,8 +1089,7 @@ static void cxt5047_hp_automic(struct hda_codec *codec)
static void cxt5047_hp_unsol_event(struct hda_codec *codec,
unsigned int res)
{
- res >>= 26;
- switch (res) {
+ switch (res >> 26) {
case CONEXANT_HP_EVENT:
cxt5047_hp_automute(codec);
break;
@@ -1166,6 +1298,17 @@ static struct snd_kcontrol_new cxt5047_test_mixer[] = {
.get = conexant_mux_enum_get,
.put = conexant_mux_enum_put,
},
+ HDA_CODEC_VOLUME("Input-1 Volume", 0x1a, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Input-1 Switch", 0x1a, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Input-2 Volume", 0x1a, 0x1, HDA_INPUT),
+ HDA_CODEC_MUTE("Input-2 Switch", 0x1a, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("Input-3 Volume", 0x1a, 0x2, HDA_INPUT),
+ HDA_CODEC_MUTE("Input-3 Switch", 0x1a, 0x2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Input-4 Volume", 0x1a, 0x3, HDA_INPUT),
+ HDA_CODEC_MUTE("Input-4 Switch", 0x1a, 0x3, HDA_INPUT),
+ HDA_CODEC_VOLUME("Input-5 Volume", 0x1a, 0x4, HDA_INPUT),
+ HDA_CODEC_MUTE("Input-5 Switch", 0x1a, 0x4, HDA_INPUT),
+
{ } /* end */
};
@@ -1255,9 +1398,9 @@ static const char *cxt5047_models[CXT5047_MODELS] = {
static struct snd_pci_quirk cxt5047_cfg_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x30a0, "HP DV1000", CXT5047_LAPTOP),
+ SND_PCI_QUIRK(0x103c, 0x30a5, "HP DV5200T/DV8000T", CXT5047_LAPTOP_HP),
SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV2000T/DV3000T", CXT5047_LAPTOP),
SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2000Z", CXT5047_LAPTOP),
- SND_PCI_QUIRK(0x103c, 0x30a5, "HP DV5200T/DV8000T", CXT5047_LAPTOP_HP),
SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P100", CXT5047_LAPTOP_EAPD),
{}
};
@@ -1324,10 +1467,260 @@ static int patch_cxt5047(struct hda_codec *codec)
return 0;
}
+/* Conexant 5051 specific */
+static hda_nid_t cxt5051_dac_nids[1] = { 0x10 };
+static hda_nid_t cxt5051_adc_nids[2] = { 0x14, 0x15 };
+#define CXT5051_SPDIF_OUT 0x1C
+#define CXT5051_PORTB_EVENT 0x38
+#define CXT5051_PORTC_EVENT 0x39
+
+static struct hda_channel_mode cxt5051_modes[1] = {
+ { 2, NULL },
+};
+
+static void cxt5051_update_speaker(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ unsigned int pinctl;
+ pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0;
+ snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ pinctl);
+}
+
+/* turn on/off EAPD (+ mute HP) as a master switch */
+static int cxt5051_hp_master_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ if (!cxt_eapd_put(kcontrol, ucontrol))
+ return 0;
+ cxt5051_update_speaker(codec);
+ return 1;
+}
+
+/* toggle input of built-in and mic jack appropriately */
+static void cxt5051_portb_automic(struct hda_codec *codec)
+{
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x17, 0,
+ AC_VERB_GET_PIN_SENSE, 0) &
+ AC_PINSENSE_PRESENCE;
+ snd_hda_codec_write(codec, 0x14, 0,
+ AC_VERB_SET_CONNECT_SEL,
+ present ? 0x01 : 0x00);
+}
+
+/* switch the current ADC according to the jack state */
+static void cxt5051_portc_automic(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+ unsigned int present;
+ hda_nid_t new_adc;
+
+ present = snd_hda_codec_read(codec, 0x18, 0,
+ AC_VERB_GET_PIN_SENSE, 0) &
+ AC_PINSENSE_PRESENCE;
+ if (present)
+ spec->cur_adc_idx = 1;
+ else
+ spec->cur_adc_idx = 0;
+ new_adc = spec->adc_nids[spec->cur_adc_idx];
+ if (spec->cur_adc && spec->cur_adc != new_adc) {
+ /* stream is running, let's swap the current ADC */
+ snd_hda_codec_setup_stream(codec, spec->cur_adc, 0, 0, 0);
+ spec->cur_adc = new_adc;
+ snd_hda_codec_setup_stream(codec, new_adc,
+ spec->cur_adc_stream_tag, 0,
+ spec->cur_adc_format);
+ }
+}
+
+/* mute internal speaker if HP is plugged */
+static void cxt5051_hp_automute(struct hda_codec *codec)
+{
+ struct conexant_spec *spec = codec->spec;
+
+ spec->hp_present = snd_hda_codec_read(codec, 0x16, 0,
+ AC_VERB_GET_PIN_SENSE, 0) &
+ AC_PINSENSE_PRESENCE;
+ cxt5051_update_speaker(codec);
+}
+
+/* unsolicited event for HP jack sensing */
+static void cxt5051_hp_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ switch (res >> 26) {
+ case CONEXANT_HP_EVENT:
+ cxt5051_hp_automute(codec);
+ break;
+ case CXT5051_PORTB_EVENT:
+ cxt5051_portb_automic(codec);
+ break;
+ case CXT5051_PORTC_EVENT:
+ cxt5051_portc_automic(codec);
+ break;
+ }
+}
+
+static struct snd_kcontrol_new cxt5051_mixers[] = {
+ HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
+ HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
+ HDA_CODEC_VOLUME("External Mic Volume", 0x14, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("External Mic Switch", 0x14, 0x01, HDA_INPUT),
+ HDA_CODEC_VOLUME("Docking Mic Volume", 0x15, 0x00, HDA_INPUT),
+ HDA_CODEC_MUTE("Docking Mic Switch", 0x15, 0x00, HDA_INPUT),
+ HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = cxt_eapd_info,
+ .get = cxt_eapd_get,
+ .put = cxt5051_hp_master_sw_put,
+ .private_value = 0x1a,
+ },
+
+ {}
+};
+
+static struct snd_kcontrol_new cxt5051_hp_mixers[] = {
+ HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
+ HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
+ HDA_CODEC_VOLUME("External Mic Volume", 0x15, 0x00, HDA_INPUT),
+ HDA_CODEC_MUTE("External Mic Switch", 0x15, 0x00, HDA_INPUT),
+ HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = cxt_eapd_info,
+ .get = cxt_eapd_get,
+ .put = cxt5051_hp_master_sw_put,
+ .private_value = 0x1a,
+ },
+
+ {}
+};
+
+static struct hda_verb cxt5051_init_verbs[] = {
+ /* Line in, Mic */
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
+ /* SPK */
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* HP, Amp */
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
+ /* DAC1 */
+ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ /* Record selector: Int mic */
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x44},
+ /* SPDIF route: PCM */
+ {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
+ /* EAPD */
+ {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
+ {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
+ {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT},
+ {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTC_EVENT},
+ { } /* end */
+};
+
+/* initialize jack-sensing, too */
+static int cxt5051_init(struct hda_codec *codec)
+{
+ conexant_init(codec);
+ if (codec->patch_ops.unsol_event) {
+ cxt5051_hp_automute(codec);
+ cxt5051_portb_automic(codec);
+ cxt5051_portc_automic(codec);
+ }
+ return 0;
+}
+
+
+enum {
+ CXT5051_LAPTOP, /* Laptops w/ EAPD support */
+ CXT5051_HP, /* no docking */
+ CXT5051_MODELS
+};
+
+static const char *cxt5051_models[CXT5051_MODELS] = {
+ [CXT5051_LAPTOP] = "laptop",
+ [CXT5051_HP] = "hp",
+};
+
+static struct snd_pci_quirk cxt5051_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
+ CXT5051_LAPTOP),
+ SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP),
+ {}
+};
+
+static int patch_cxt5051(struct hda_codec *codec)
+{
+ struct conexant_spec *spec;
+ int board_config;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ mutex_init(&spec->amp_mutex);
+ codec->spec = spec;
+
+ codec->patch_ops = conexant_patch_ops;
+ codec->patch_ops.init = cxt5051_init;
+
+ spec->multiout.max_channels = 2;
+ spec->multiout.num_dacs = ARRAY_SIZE(cxt5051_dac_nids);
+ spec->multiout.dac_nids = cxt5051_dac_nids;
+ spec->multiout.dig_out_nid = CXT5051_SPDIF_OUT;
+ spec->num_adc_nids = 1; /* not 2; via auto-mic switch */
+ spec->adc_nids = cxt5051_adc_nids;
+ spec->num_mixers = 1;
+ spec->mixers[0] = cxt5051_mixers;
+ spec->num_init_verbs = 1;
+ spec->init_verbs[0] = cxt5051_init_verbs;
+ spec->spdif_route = 0;
+ spec->num_channel_mode = ARRAY_SIZE(cxt5051_modes);
+ spec->channel_mode = cxt5051_modes;
+ spec->cur_adc = 0;
+ spec->cur_adc_idx = 0;
+
+ board_config = snd_hda_check_board_config(codec, CXT5051_MODELS,
+ cxt5051_models,
+ cxt5051_cfg_tbl);
+ switch (board_config) {
+ case CXT5051_HP:
+ codec->patch_ops.unsol_event = cxt5051_hp_unsol_event;
+ spec->mixers[0] = cxt5051_hp_mixers;
+ break;
+ default:
+ case CXT5051_LAPTOP:
+ codec->patch_ops.unsol_event = cxt5051_hp_unsol_event;
+ break;
+ }
+
+ return 0;
+}
+
+
+/*
+ */
+
struct hda_codec_preset snd_hda_preset_conexant[] = {
{ .id = 0x14f15045, .name = "CX20549 (Venice)",
.patch = patch_cxt5045 },
{ .id = 0x14f15047, .name = "CX20551 (Waikiki)",
.patch = patch_cxt5047 },
+ { .id = 0x14f15051, .name = "CX20561 (Hermosa)",
+ .patch = patch_cxt5051 },
{} /* terminator */
};
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 1c50278..586d98f 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -23,7 +23,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>
@@ -92,9 +91,12 @@ enum {
ALC262_HP_BPC,
ALC262_HP_BPC_D7000_WL,
ALC262_HP_BPC_D7000_WF,
+ ALC262_HP_TC_T5735,
+ ALC262_HP_RP5700,
ALC262_BENQ_ED8,
ALC262_SONY_ASSAMD,
ALC262_BENQ_T31,
+ ALC262_ULTRA,
ALC262_AUTO,
ALC262_MODEL_LAST /* last tag */
};
@@ -104,10 +106,21 @@ enum {
ALC268_3ST,
ALC268_TOSHIBA,
ALC268_ACER,
+ ALC268_DELL,
+#ifdef CONFIG_SND_DEBUG
+ ALC268_TEST,
+#endif
ALC268_AUTO,
ALC268_MODEL_LAST /* last tag */
};
+/* ALC269 models */
+enum {
+ ALC269_BASIC,
+ ALC269_AUTO,
+ ALC269_MODEL_LAST /* last tag */
+};
+
/* ALC861 models */
enum {
ALC861_3ST,
@@ -144,6 +157,7 @@ enum {
ALC662_5ST_DIG,
ALC662_LENOVO_101E,
ALC662_ASUS_EEEPC_P701,
+ ALC662_ASUS_EEEPC_EP20,
ALC662_AUTO,
ALC662_MODEL_LAST,
};
@@ -183,6 +197,8 @@ enum {
ALC883_HAIER_W66,
ALC888_6ST_HP,
ALC888_3ST_HP,
+ ALC888_6ST_DELL,
+ ALC883_MITAC,
ALC883_AUTO,
ALC883_MODEL_LAST,
};
@@ -204,6 +220,8 @@ struct alc_spec {
char *stream_name_analog; /* analog PCM stream */
struct hda_pcm_stream *stream_analog_playback;
struct hda_pcm_stream *stream_analog_capture;
+ struct hda_pcm_stream *stream_analog_alt_playback;
+ struct hda_pcm_stream *stream_analog_alt_capture;
char *stream_name_digital; /* digital PCM stream */
struct hda_pcm_stream *stream_digital_playback;
@@ -214,6 +232,7 @@ struct alc_spec {
* max_channels, dacs must be set
* dig_out_nid and hp_nid are optional
*/
+ hda_nid_t alt_dac_nid;
/* capture */
unsigned int num_adc_nids;
@@ -247,7 +266,11 @@ struct alc_spec {
/* for pin sensing */
unsigned int sense_updated: 1;
unsigned int jack_present: 1;
+ unsigned int master_sw: 1;
+ /* for virtual master */
+ hda_nid_t vmaster_nid;
+ u32 vmaster_tlv[4];
#ifdef CONFIG_SND_HDA_POWER_SAVE
struct hda_loopback_check loopback;
#endif
@@ -562,7 +585,7 @@ static int alc_spdif_ctrl_get(struct snd_kcontrol *kcontrol,
unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
long *valp = ucontrol->value.integer.value;
unsigned int val = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_DIGI_CONVERT, 0x00);
+ AC_VERB_GET_DIGI_CONVERT_1, 0x00);
*valp = (val & mask) != 0;
return 0;
@@ -576,7 +599,7 @@ static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol,
unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
long val = *ucontrol->value.integer.value;
unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_DIGI_CONVERT,
+ AC_VERB_GET_DIGI_CONVERT_1,
0x00);
/* Set/unset the masked control bit(s) as needed */
@@ -598,6 +621,59 @@ static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol,
.private_value = nid | (mask<<16) }
#endif /* CONFIG_SND_DEBUG */
+/* A switch control to allow the enabling EAPD digital outputs on the ALC26x.
+ * Again, this is only used in the ALC26x test models to help identify when
+ * the EAPD line must be asserted for features to work.
+ */
+#ifdef CONFIG_SND_DEBUG
+#define alc_eapd_ctrl_info snd_ctl_boolean_mono_info
+
+static int alc_eapd_ctrl_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 & 0xffff;
+ unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
+ long *valp = ucontrol->value.integer.value;
+ unsigned int val = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_EAPD_BTLENABLE, 0x00);
+
+ *valp = (val & mask) != 0;
+ return 0;
+}
+
+static int alc_eapd_ctrl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int change;
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ hda_nid_t nid = kcontrol->private_value & 0xffff;
+ unsigned char mask = (kcontrol->private_value >> 16) & 0xff;
+ long val = *ucontrol->value.integer.value;
+ unsigned int ctrl_data = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_EAPD_BTLENABLE,
+ 0x00);
+
+ /* Set/unset the masked control bit(s) as needed */
+ change = (!val ? 0 : mask) != (ctrl_data & mask);
+ if (!val)
+ ctrl_data &= ~mask;
+ else
+ ctrl_data |= mask;
+ snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_EAPD_BTLENABLE,
+ ctrl_data);
+
+ return change;
+}
+
+#define ALC_EAPD_CTRL_SWITCH(xname, nid, mask) \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
+ .info = alc_eapd_ctrl_info, \
+ .get = alc_eapd_ctrl_get, \
+ .put = alc_eapd_ctrl_put, \
+ .private_value = nid | (mask<<16) }
+#endif /* CONFIG_SND_DEBUG */
+
/*
* set up from the preset table
*/
@@ -739,7 +815,7 @@ static void alc_subsystem_id(struct hda_codec *codec,
/* check sum */
tmp = 0;
for (i = 1; i < 16; i++) {
- if ((ass >> i) && 1)
+ if ((ass >> i) & 1)
tmp++;
}
if (((ass >> 16) & 0xf) != tmp)
@@ -828,10 +904,10 @@ do_sku:
break;
}
- /* is laptop and enable the function "Mute internal speaker
+ /* is laptop or Desktop and enable the function "Mute internal speaker
* when the external headphone out jack is plugged"
*/
- if (!(ass & 0x4) || !(ass & 0x8000))
+ if (!(ass & 0x8000))
return;
/*
* 10~8 : Jack location
@@ -841,9 +917,9 @@ do_sku:
* when the external headphone out jack is plugged"
*/
if (!spec->autocfg.speaker_pins[0]) {
- if (spec->multiout.dac_nids[0])
+ if (spec->autocfg.line_out_pins[0])
spec->autocfg.speaker_pins[0] =
- spec->multiout.dac_nids[0];
+ spec->autocfg.line_out_pins[0];
else
return;
}
@@ -1009,7 +1085,6 @@ static struct snd_kcontrol_new alc880_capture_mixer[] = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* The multiple "Capture Source" controls confuse alsamixer
* So call somewhat different..
- * FIXME: the controls appear in the "playback" view!
*/
/* .name = "Capture Source", */
.name = "Input Source",
@@ -1031,7 +1106,6 @@ static struct snd_kcontrol_new alc880_capture_alt_mixer[] = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* The multiple "Capture Source" controls confuse alsamixer
* So call somewhat different..
- * FIXME: the controls appear in the "playback" view!
*/
/* .name = "Capture Source", */
.name = "Input Source",
@@ -1226,7 +1300,6 @@ static struct snd_kcontrol_new alc880_z71v_mixer[] = {
};
-/* FIXME! */
/*
* ALC880 F1734 model
*
@@ -1242,8 +1315,8 @@ static hda_nid_t alc880_f1734_dac_nids[1] = {
static struct snd_kcontrol_new alc880_f1734_mixer[] = {
HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("Internal Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Internal Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
@@ -1252,7 +1325,6 @@ static struct snd_kcontrol_new alc880_f1734_mixer[] = {
};
-/* FIXME! */
/*
* ALC880 ASUS model
*
@@ -1289,7 +1361,6 @@ static struct snd_kcontrol_new alc880_asus_mixer[] = {
{ } /* end */
};
-/* FIXME! */
/*
* ALC880 ASUS W1V model
*
@@ -1327,7 +1398,6 @@ static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* The multiple "Capture Source" controls confuse alsamixer
* So call somewhat different..
- * FIXME: the controls appear in the "playback" view!
*/
/* .name = "Capture Source", */
.name = "Input Source",
@@ -1341,10 +1411,10 @@ static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = {
/* Uniwill */
static struct snd_kcontrol_new alc880_uniwill_mixer[] = {
- HDA_CODEC_VOLUME("HPhone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("HPhone Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("iSpeaker Playback Switch", 0x0d, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
@@ -1384,16 +1454,49 @@ static struct snd_kcontrol_new alc880_fujitsu_mixer[] = {
};
static struct snd_kcontrol_new alc880_uniwill_p53_mixer[] = {
- HDA_CODEC_VOLUME("HPhone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("HPhone Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("iSpeaker Playback Switch", 0x0d, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
{ } /* end */
};
/*
+ * virtual master controls
+ */
+
+/*
+ * slave controls for virtual master
+ */
+static const char *alc_slave_vols[] = {
+ "Front Playback Volume",
+ "Surround Playback Volume",
+ "Center Playback Volume",
+ "LFE Playback Volume",
+ "Side Playback Volume",
+ "Headphone Playback Volume",
+ "Speaker Playback Volume",
+ "Mono Playback Volume",
+ "Line-Out Playback Volume",
+ NULL,
+};
+
+static const char *alc_slave_sws[] = {
+ "Front Playback Switch",
+ "Surround Playback Switch",
+ "Center Playback Switch",
+ "LFE Playback Switch",
+ "Side Playback Switch",
+ "Headphone Playback Switch",
+ "Speaker Playback Switch",
+ "Mono Playback Switch",
+ "IEC958 Playback Switch",
+ NULL,
+};
+
+/*
* build control elements
*/
static int alc_build_controls(struct hda_codec *codec)
@@ -1419,6 +1522,23 @@ static int alc_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->vmaster_nid,
+ HDA_OUTPUT, spec->vmaster_tlv);
+ err = snd_hda_add_vmaster(codec, "Master Playback Volume",
+ spec->vmaster_tlv, alc_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, alc_slave_sws);
+ if (err < 0)
+ return err;
+ }
+
return 0;
}
@@ -1790,7 +1910,6 @@ static void alc880_uniwill_p53_unsol_event(struct hda_codec *codec,
alc880_uniwill_p53_dcvol_automute(codec);
}
-/* FIXME! */
/*
* F1734 pin configuration:
* HP = 0x14, speaker-out = 0x15, mic = 0x18
@@ -1819,7 +1938,6 @@ static struct hda_verb alc880_pin_f1734_init_verbs[] = {
{ }
};
-/* FIXME! */
/*
* ASUS pin configuration:
* HP/front = 0x14, surr = 0x15, clfe = 0x16, mic = 0x18, line = 0x1a
@@ -1966,9 +2084,8 @@ static struct hda_channel_mode alc880_lg_ch_modes[3] = {
};
static struct snd_kcontrol_new alc880_lg_mixer[] = {
- /* FIXME: it's not really "master" but front channels */
- HDA_CODEC_VOLUME("Master Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("Master Playback Switch", 0x0f, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Front Playback Switch", 0x0f, 2, HDA_INPUT),
HDA_CODEC_VOLUME("Surround Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Surround Playback Switch", 0x0c, 2, HDA_INPUT),
HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
@@ -2256,7 +2373,7 @@ static int alc880_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
/*
* Analog capture
*/
-static int alc880_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+static int alc880_alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
unsigned int format,
@@ -2264,18 +2381,18 @@ static int alc880_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
{
struct alc_spec *spec = codec->spec;
- snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
+ snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1],
stream_tag, 0, format);
return 0;
}
-static int alc880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+static int alc880_alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
{
struct alc_spec *spec = codec->spec;
- snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
+ snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1],
0, 0, 0);
return 0;
}
@@ -2296,13 +2413,27 @@ static struct hda_pcm_stream alc880_pcm_analog_playback = {
};
static struct hda_pcm_stream alc880_pcm_analog_capture = {
- .substreams = 2,
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in alc_build_pcms */
+};
+
+static struct hda_pcm_stream alc880_pcm_analog_alt_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in alc_build_pcms */
+};
+
+static struct hda_pcm_stream alc880_pcm_analog_alt_capture = {
+ .substreams = 2, /* can be overridden */
.channels_min = 2,
.channels_max = 2,
/* NID is set in alc_build_pcms */
.ops = {
- .prepare = alc880_capture_pcm_prepare,
- .cleanup = alc880_capture_pcm_cleanup
+ .prepare = alc880_alt_capture_pcm_prepare,
+ .cleanup = alc880_alt_capture_pcm_cleanup
},
};
@@ -2326,7 +2457,7 @@ static struct hda_pcm_stream alc880_pcm_digital_capture = {
};
/* Used by alc_build_pcms to flag that a PCM has no playback stream */
-static struct hda_pcm_stream alc_pcm_null_playback = {
+static struct hda_pcm_stream alc_pcm_null_stream = {
.substreams = 0,
.channels_min = 0,
.channels_max = 0,
@@ -2383,17 +2514,32 @@ static int alc_build_pcms(struct hda_codec *codec)
* model, configure a second analog capture-only PCM.
*/
/* Additional Analaog capture for index #2 */
- if (spec->num_adc_nids > 1 && spec->stream_analog_capture &&
- spec->adc_nids) {
+ if ((spec->alt_dac_nid && spec->stream_analog_alt_playback) ||
+ (spec->num_adc_nids > 1 && spec->stream_analog_alt_capture)) {
codec->num_pcms = 3;
info = spec->pcm_rec + 2;
info->name = spec->stream_name_analog;
- /* No playback stream for second PCM */
- info->stream[SNDRV_PCM_STREAM_PLAYBACK] = alc_pcm_null_playback;
- info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0;
- if (spec->stream_analog_capture) {
- info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
- info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[1];
+ if (spec->alt_dac_nid) {
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+ *spec->stream_analog_alt_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
+ spec->alt_dac_nid;
+ } else {
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+ alc_pcm_null_stream;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0;
+ }
+ if (spec->num_adc_nids > 1) {
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+ *spec->stream_analog_alt_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
+ spec->adc_nids[1];
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
+ spec->num_adc_nids - 1;
+ } else {
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+ alc_pcm_null_stream;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 0;
}
}
@@ -2723,23 +2869,17 @@ static const char *alc880_models[ALC880_MODEL_LAST] = {
};
static struct snd_pci_quirk alc880_cfg_tbl[] = {
- /* Broken BIOS configuration */
- SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_6ST_DIG),
- SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_6ST_DIG),
-
+ SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_W810),
SND_PCI_QUIRK(0x1019, 0xa880, "ECS", ALC880_5ST_DIG),
SND_PCI_QUIRK(0x1019, 0xa884, "Acer APFV", ALC880_6ST),
- SND_PCI_QUIRK(0x1019, 0x0f69, "Coeus G610P", ALC880_W810),
SND_PCI_QUIRK(0x1025, 0x0070, "ULI", ALC880_3ST_DIG),
SND_PCI_QUIRK(0x1025, 0x0077, "ULI", ALC880_6ST_DIG),
SND_PCI_QUIRK(0x1025, 0x0078, "ULI", ALC880_6ST_DIG),
SND_PCI_QUIRK(0x1025, 0x0087, "ULI", ALC880_6ST_DIG),
SND_PCI_QUIRK(0x1025, 0xe309, "ULI", ALC880_3ST_DIG),
SND_PCI_QUIRK(0x1025, 0xe310, "ULI", ALC880_3ST),
-
SND_PCI_QUIRK(0x1039, 0x1234, NULL, ALC880_6ST_DIG),
SND_PCI_QUIRK(0x103c, 0x2a09, "HP", ALC880_5ST),
-
SND_PCI_QUIRK(0x1043, 0x10b3, "ASUS W1V", ALC880_ASUS_W1V),
SND_PCI_QUIRK(0x1043, 0x10c2, "ASUS W6A", ALC880_ASUS_DIG),
SND_PCI_QUIRK(0x1043, 0x10c3, "ASUS Wxx", ALC880_ASUS_DIG),
@@ -2754,54 +2894,50 @@ static struct snd_pci_quirk alc880_cfg_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x8181, "ASUS P4GPL", ALC880_ASUS_DIG),
SND_PCI_QUIRK(0x1043, 0x8196, "ASUS P5GD1", ALC880_6ST),
SND_PCI_QUIRK(0x1043, 0x81b4, "ASUS", ALC880_6ST),
- SND_PCI_QUIRK(0x1043, 0, "ASUS", ALC880_ASUS),
-
- SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_3ST),
+ SND_PCI_QUIRK(0x1043, 0, "ASUS", ALC880_ASUS), /* default ASUS */
SND_PCI_QUIRK(0x104d, 0x81a0, "Sony", ALC880_3ST),
+ SND_PCI_QUIRK(0x104d, 0x81d6, "Sony", ALC880_3ST),
+ SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_5ST),
SND_PCI_QUIRK(0x107b, 0x3033, "Gateway", ALC880_5ST),
SND_PCI_QUIRK(0x107b, 0x4039, "Gateway", ALC880_5ST),
- SND_PCI_QUIRK(0x107b, 0x3032, "Gateway", ALC880_5ST),
- SND_PCI_QUIRK(0x1558, 0x0520, "Clevo m520G", ALC880_CLEVO),
- SND_PCI_QUIRK(0x1558, 0x0660, "Clevo m655n", ALC880_CLEVO),
- SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_W810),
- SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_TCL_S700),
- SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_6ST_DIG),
SND_PCI_QUIRK(0x1297, 0xc790, "Shuttle ST20G5", ALC880_6ST_DIG),
SND_PCI_QUIRK(0x1458, 0xa102, "Gigabyte K8", ALC880_6ST_DIG),
SND_PCI_QUIRK(0x1462, 0x1150, "MSI", ALC880_6ST_DIG),
SND_PCI_QUIRK(0x1509, 0x925d, "FIC P4M", ALC880_6ST_DIG),
+ SND_PCI_QUIRK(0x1558, 0x0520, "Clevo m520G", ALC880_CLEVO),
+ SND_PCI_QUIRK(0x1558, 0x0660, "Clevo m655n", ALC880_CLEVO),
SND_PCI_QUIRK(0x1558, 0x5401, "ASUS", ALC880_ASUS_DIG2),
-
+ SND_PCI_QUIRK(0x1565, 0x8202, "Biostar", ALC880_5ST_DIG),
SND_PCI_QUIRK(0x1584, 0x9050, "Uniwill", ALC880_UNIWILL_DIG),
+ SND_PCI_QUIRK(0x1584, 0x9054, "Uniwlll", ALC880_F1734),
SND_PCI_QUIRK(0x1584, 0x9070, "Uniwill", ALC880_UNIWILL),
SND_PCI_QUIRK(0x1584, 0x9077, "Uniwill P53", ALC880_UNIWILL_P53),
- SND_PCI_QUIRK(0x1584, 0x9054, "Uniwlll", ALC880_F1734),
-
+ SND_PCI_QUIRK(0x161f, 0x203d, "W810", ALC880_W810),
+ SND_PCI_QUIRK(0x1695, 0x400d, "EPoX", ALC880_5ST_DIG),
SND_PCI_QUIRK(0x1695, 0x4012, "EPox EP-5LDA", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x1734, 0x10ac, "FSC", ALC880_UNIWILL),
SND_PCI_QUIRK(0x1734, 0x107c, "FSC F1734", ALC880_F1734),
+ SND_PCI_QUIRK(0x1734, 0x10ac, "FSC", ALC880_UNIWILL),
SND_PCI_QUIRK(0x1734, 0x10b0, "Fujitsu", ALC880_FUJITSU),
-
+ SND_PCI_QUIRK(0x1854, 0x0018, "LG LW20", ALC880_LG_LW),
SND_PCI_QUIRK(0x1854, 0x003b, "LG", ALC880_LG),
SND_PCI_QUIRK(0x1854, 0x0068, "LG w1", ALC880_LG),
- SND_PCI_QUIRK(0x1854, 0x0018, "LG LW20", ALC880_LG_LW),
SND_PCI_QUIRK(0x1854, 0x0077, "LG LW25", ALC880_LG_LW),
-
- SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_3ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_3ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_3ST_DIG),
+ SND_PCI_QUIRK(0x19db, 0x4188, "TCL S700", ALC880_TCL_S700),
+ SND_PCI_QUIRK(0x2668, 0x8086, NULL, ALC880_6ST_DIG), /* broken BIOS */
+ SND_PCI_QUIRK(0x8086, 0x2668, NULL, ALC880_6ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_5ST_DIG),
SND_PCI_QUIRK(0x8086, 0xd400, "Intel mobo", ALC880_5ST_DIG),
SND_PCI_QUIRK(0x8086, 0xd401, "Intel mobo", ALC880_5ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xd402, "Intel mobo", ALC880_3ST_DIG),
SND_PCI_QUIRK(0x8086, 0xe224, "Intel mobo", ALC880_5ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xe305, "Intel mobo", ALC880_3ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xe308, "Intel mobo", ALC880_3ST_DIG),
SND_PCI_QUIRK(0x8086, 0xe400, "Intel mobo", ALC880_5ST_DIG),
SND_PCI_QUIRK(0x8086, 0xe401, "Intel mobo", ALC880_5ST_DIG),
SND_PCI_QUIRK(0x8086, 0xe402, "Intel mobo", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x8086, 0xa100, "Intel mobo", ALC880_5ST_DIG),
- SND_PCI_QUIRK(0x8086, 0, "Intel mobo", ALC880_3ST),
-
+ SND_PCI_QUIRK(0x8086, 0, "Intel mobo", ALC880_3ST), /* default Intel */
+ SND_PCI_QUIRK(0xa0a0, 0x0560, "AOpen i915GMm-HFS", ALC880_5ST_DIG),
+ SND_PCI_QUIRK(0xe803, 0x1019, NULL, ALC880_6ST_DIG),
{}
};
@@ -3511,6 +3647,7 @@ static int patch_alc880(struct hda_codec *codec)
spec->stream_name_analog = "ALC880 Analog";
spec->stream_analog_playback = &alc880_pcm_analog_playback;
spec->stream_analog_capture = &alc880_pcm_analog_capture;
+ spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture;
spec->stream_name_digital = "ALC880 Digital";
spec->stream_digital_playback = &alc880_pcm_digital_playback;
@@ -3535,6 +3672,8 @@ static int patch_alc880(struct hda_codec *codec)
}
}
+ spec->vmaster_nid = 0x0c;
+
codec->patch_ops = alc_patch_ops;
if (board_config == ALC880_AUTO)
spec->init_hook = alc880_auto_init;
@@ -3691,18 +3830,135 @@ static struct snd_kcontrol_new alc260_pc_beep_mixer[] = {
{ } /* end */
};
+/* update HP, line and mono out pins according to the master switch */
+static void alc260_hp_master_update(struct hda_codec *codec,
+ hda_nid_t hp, hda_nid_t line,
+ hda_nid_t mono)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int val = spec->master_sw ? PIN_HP : 0;
+ /* change HP and line-out pins */
+ snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ val);
+ snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ val);
+ /* mono (speaker) depending on the HP jack sense */
+ val = (val && !spec->jack_present) ? PIN_OUT : 0;
+ snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+ val);
+}
+
+static int alc260_hp_master_sw_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+ *ucontrol->value.integer.value = spec->master_sw;
+ return 0;
+}
+
+static int alc260_hp_master_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+ int val = !!*ucontrol->value.integer.value;
+ hda_nid_t hp, line, mono;
+
+ if (val == spec->master_sw)
+ return 0;
+ spec->master_sw = val;
+ hp = (kcontrol->private_value >> 16) & 0xff;
+ line = (kcontrol->private_value >> 8) & 0xff;
+ mono = kcontrol->private_value & 0xff;
+ alc260_hp_master_update(codec, hp, line, mono);
+ return 1;
+}
+
+static struct snd_kcontrol_new alc260_hp_output_mixer[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = alc260_hp_master_sw_get,
+ .put = alc260_hp_master_sw_put,
+ .private_value = (0x0f << 16) | (0x10 << 8) | 0x11
+ },
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Front Playback Switch", 0x08, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Headphone Playback Switch", 0x09, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0,
+ HDA_OUTPUT),
+ HDA_BIND_MUTE_MONO("Speaker Playback Switch", 0x0a, 1, 2, HDA_INPUT),
+ { } /* end */
+};
+
+static struct hda_verb alc260_hp_unsol_verbs[] = {
+ {0x10, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {},
+};
+
+static void alc260_hp_automute(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x10, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+ spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0;
+ alc260_hp_master_update(codec, 0x0f, 0x10, 0x11);
+}
+
+static void alc260_hp_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ if ((res >> 26) == ALC880_HP_EVENT)
+ alc260_hp_automute(codec);
+}
+
static struct snd_kcontrol_new alc260_hp_3013_mixer[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = alc260_hp_master_sw_get,
+ .put = alc260_hp_master_sw_put,
+ .private_value = (0x10 << 16) | (0x15 << 8) | 0x11
+ },
HDA_CODEC_VOLUME("Front Playback Volume", 0x09, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Front Playback Switch", 0x10, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Aux-In Playback Volume", 0x07, 0x06, HDA_INPUT),
HDA_CODEC_MUTE("Aux-In Playback Switch", 0x07, 0x06, HDA_INPUT),
HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("iSpeaker Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("iSpeaker Playback Switch", 0x11, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x11, 1, 0x0, HDA_OUTPUT),
{ } /* end */
};
+static struct hda_verb alc260_hp_3013_unsol_verbs[] = {
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {},
+};
+
+static void alc260_hp_3013_automute(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x15, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+ spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0;
+ alc260_hp_master_update(codec, 0x10, 0x15, 0x11);
+}
+
+static void alc260_hp_3013_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if ((res >> 26) == ALC880_HP_EVENT)
+ alc260_hp_3013_automute(codec);
+}
+
/* Fujitsu S702x series laptops. ALC260 pin usage: Mic/Line jack = 0x12,
* HP jack = 0x14, CD audio = 0x16, internal speaker = 0x10.
*/
@@ -3812,7 +4068,6 @@ static struct snd_kcontrol_new alc260_capture_mixer[] = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* The multiple "Capture Source" controls confuse alsamixer
* So call somewhat different..
- * FIXME: the controls appear in the "playback" view!
*/
/* .name = "Capture Source", */
.name = "Input Source",
@@ -3831,7 +4086,6 @@ static struct snd_kcontrol_new alc260_capture_alt_mixer[] = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* The multiple "Capture Source" controls confuse alsamixer
* So call somewhat different..
- * FIXME: the controls appear in the "playback" view!
*/
/* .name = "Capture Source", */
.name = "Input Source",
@@ -4332,6 +4586,12 @@ static struct snd_kcontrol_new alc260_test_mixer[] = {
ALC_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x03, 0x01),
ALC_SPDIF_CTRL_SWITCH("SPDIF Capture Switch", 0x06, 0x01),
+ /* A switch allowing EAPD to be enabled. Some laptops seem to use
+ * this output to turn on an external amplifier.
+ */
+ ALC_EAPD_CTRL_SWITCH("LINE-OUT EAPD Enable Switch", 0x0f, 0x02),
+ ALC_EAPD_CTRL_SWITCH("HP-OUT EAPD Enable Switch", 0x10, 0x02),
+
{ } /* end */
};
static struct hda_verb alc260_test_init_verbs[] = {
@@ -4417,17 +4677,8 @@ static struct hda_verb alc260_test_init_verbs[] = {
};
#endif
-static struct hda_pcm_stream alc260_pcm_analog_playback = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
-};
-
-static struct hda_pcm_stream alc260_pcm_analog_capture = {
- .substreams = 1,
- .channels_min = 2,
- .channels_max = 2,
-};
+#define alc260_pcm_analog_playback alc880_pcm_analog_alt_playback
+#define alc260_pcm_analog_capture alc880_pcm_analog_capture
#define alc260_pcm_digital_playback alc880_pcm_digital_playback
#define alc260_pcm_digital_capture alc880_pcm_digital_capture
@@ -4744,8 +4995,8 @@ static struct snd_pci_quirk alc260_cfg_tbl[] = {
SND_PCI_QUIRK(0x104d, 0x81cd, "Sony VAIO", ALC260_BASIC),
SND_PCI_QUIRK(0x10cf, 0x1326, "Fujitsu S702X", ALC260_FUJITSU_S702X),
SND_PCI_QUIRK(0x152d, 0x0729, "CTL U553W", ALC260_BASIC),
- SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_WILL),
SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_REPLACER_672V),
+ SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_WILL),
{}
};
@@ -4765,10 +5016,11 @@ static struct alc_config_preset alc260_presets[] = {
.input_mux = &alc260_capture_source,
},
[ALC260_HP] = {
- .mixers = { alc260_base_output_mixer,
+ .mixers = { alc260_hp_output_mixer,
alc260_input_mixer,
alc260_capture_alt_mixer },
- .init_verbs = { alc260_init_verbs },
+ .init_verbs = { alc260_init_verbs,
+ alc260_hp_unsol_verbs },
.num_dacs = ARRAY_SIZE(alc260_dac_nids),
.dac_nids = alc260_dac_nids,
.num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
@@ -4776,12 +5028,15 @@ static struct alc_config_preset alc260_presets[] = {
.num_channel_mode = ARRAY_SIZE(alc260_modes),
.channel_mode = alc260_modes,
.input_mux = &alc260_capture_source,
+ .unsol_event = alc260_hp_unsol_event,
+ .init_hook = alc260_hp_automute,
},
[ALC260_HP_3013] = {
.mixers = { alc260_hp_3013_mixer,
alc260_input_mixer,
alc260_capture_alt_mixer },
- .init_verbs = { alc260_hp_3013_init_verbs },
+ .init_verbs = { alc260_hp_3013_init_verbs,
+ alc260_hp_3013_unsol_verbs },
.num_dacs = ARRAY_SIZE(alc260_dac_nids),
.dac_nids = alc260_dac_nids,
.num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
@@ -4789,6 +5044,8 @@ static struct alc_config_preset alc260_presets[] = {
.num_channel_mode = ARRAY_SIZE(alc260_modes),
.channel_mode = alc260_modes,
.input_mux = &alc260_capture_source,
+ .unsol_event = alc260_hp_3013_unsol_event,
+ .init_hook = alc260_hp_3013_automute,
},
[ALC260_FUJITSU_S702X] = {
.mixers = { alc260_fujitsu_mixer,
@@ -4906,6 +5163,8 @@ static int patch_alc260(struct hda_codec *codec)
spec->stream_digital_playback = &alc260_pcm_digital_playback;
spec->stream_digital_capture = &alc260_pcm_digital_capture;
+ spec->vmaster_nid = 0x08;
+
codec->patch_ops = alc_patch_ops;
if (board_config == ALC260_AUTO)
spec->init_hook = alc260_auto_init;
@@ -5106,15 +5365,15 @@ static struct snd_kcontrol_new alc882_base_mixer[] = {
};
static struct snd_kcontrol_new alc885_mbp3_mixer[] = {
- HDA_CODEC_VOLUME("Master Volume", 0x0c, 0x00, HDA_OUTPUT),
- HDA_BIND_MUTE ("Master Switch", 0x0c, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE ("Speaker Switch", 0x14, 0x00, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line Out Volume", 0x0d,0x00, HDA_OUTPUT),
- HDA_CODEC_VOLUME("Line In Playback Volume", 0x0b, 0x02, HDA_INPUT),
- HDA_CODEC_MUTE ("Line In Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
+ HDA_BIND_MUTE ("Front Playback Switch", 0x0c, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE ("Speaker Playback Switch", 0x14, 0x00, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Line-Out Playback Volume", 0x0d, 0x00, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE ("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x00, HDA_INPUT),
HDA_CODEC_MUTE ("Mic Playback Switch", 0x0b, 0x00, HDA_INPUT),
- HDA_CODEC_VOLUME("Line In Boost", 0x1a, 0x00, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Boost", 0x1a, 0x00, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x00, HDA_INPUT),
{ } /* end */
};
@@ -5679,7 +5938,6 @@ static struct snd_kcontrol_new alc882_capture_alt_mixer[] = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* The multiple "Capture Source" controls confuse alsamixer
* So call somewhat different..
- * FIXME: the controls appear in the "playback" view!
*/
/* .name = "Capture Source", */
.name = "Input Source",
@@ -5702,7 +5960,6 @@ static struct snd_kcontrol_new alc882_capture_mixer[] = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* The multiple "Capture Source" controls confuse alsamixer
* So call somewhat different..
- * FIXME: the controls appear in the "playback" view!
*/
/* .name = "Capture Source", */
.name = "Input Source",
@@ -5743,16 +6000,17 @@ static const char *alc882_models[ALC882_MODEL_LAST] = {
static struct snd_pci_quirk alc882_cfg_tbl[] = {
SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC882_6ST_DIG),
- SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8 */
- SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA),
SND_PCI_QUIRK(0x1043, 0x060d, "Asus A7J", ALC882_ASUS_A7J),
SND_PCI_QUIRK(0x1043, 0x1243, "Asus A7J", ALC882_ASUS_A7J),
SND_PCI_QUIRK(0x1043, 0x13c2, "Asus A7M", ALC882_ASUS_A7M),
+ SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_W2JC),
SND_PCI_QUIRK(0x1043, 0x817f, "Asus P5LD2", ALC882_6ST_DIG),
SND_PCI_QUIRK(0x1043, 0x81d8, "Asus P5WD", ALC882_6ST_DIG),
- SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_W2JC),
+ SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG),
+ SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P35 DS3R", ALC882_6ST_DIG),
+ SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8 */
+ SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG),
+ SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA),
{}
};
@@ -5990,7 +6248,7 @@ static int alc_auto_add_mic_boost(struct hda_codec *codec)
hda_nid_t nid;
nid = spec->autocfg.input_pins[AUTO_PIN_MIC];
- if (nid) {
+ if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) {
err = add_control(spec, ALC_CTL_WIDGET_VOL,
"Mic Boost",
HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
@@ -5998,7 +6256,7 @@ static int alc_auto_add_mic_boost(struct hda_codec *codec)
return err;
}
nid = spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC];
- if (nid) {
+ if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) {
err = add_control(spec, ALC_CTL_WIDGET_VOL,
"Front Mic Boost",
HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
@@ -6061,6 +6319,7 @@ static int patch_alc882(struct hda_codec *codec)
case 0x106b1000: /* iMac 24 */
board_config = ALC885_IMAC24;
break;
+ case 0x106b00a1: /* Macbook */
case 0x106b2c00: /* Macbook Pro rev3 */
board_config = ALC885_MBP3;
break;
@@ -6093,6 +6352,9 @@ static int patch_alc882(struct hda_codec *codec)
spec->stream_name_analog = "ALC882 Analog";
spec->stream_analog_playback = &alc882_pcm_analog_playback;
spec->stream_analog_capture = &alc882_pcm_analog_capture;
+ /* FIXME: setup DAC5 */
+ /*spec->stream_analog_alt_playback = &alc880_pcm_analog_alt_playback;*/
+ spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture;
spec->stream_name_digital = "ALC882 Digital";
spec->stream_digital_playback = &alc882_pcm_digital_playback;
@@ -6117,6 +6379,8 @@ static int patch_alc882(struct hda_codec *codec)
}
}
+ spec->vmaster_nid = 0x0c;
+
codec->patch_ops = alc_patch_ops;
if (board_config == ALC882_AUTO)
spec->init_hook = alc882_auto_init;
@@ -6340,6 +6604,36 @@ static struct snd_kcontrol_new alc883_base_mixer[] = {
{ } /* end */
};
+static struct snd_kcontrol_new alc883_mitac_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 2,
+ .info = alc883_mux_enum_info,
+ .get = alc883_mux_enum_get,
+ .put = alc883_mux_enum_put,
+ },
+ { } /* end */
+};
+
static struct snd_kcontrol_new alc883_3ST_2ch_mixer[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
@@ -6508,8 +6802,8 @@ static struct snd_kcontrol_new alc883_tagra_2ch_mixer[] = {
static struct snd_kcontrol_new alc883_lenovo_101e_2ch_mixer[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("iSpeaker Playback Switch", 0x0d, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
@@ -6658,6 +6952,46 @@ static struct snd_kcontrol_new alc888_3st_hp_mixer[] = {
{ } /* end */
};
+static struct snd_kcontrol_new alc888_6st_dell_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0e, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT),
+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 2,
+ .info = alc883_mux_enum_info,
+ .get = alc883_mux_enum_get,
+ .put = alc883_mux_enum_put,
+ },
+ { } /* end */
+};
+
static struct snd_kcontrol_new alc883_acer_aspire_mixer[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
@@ -6772,6 +7106,67 @@ static struct hda_verb alc883_init_verbs[] = {
{ }
};
+/* toggle speaker-output according to the hp-jack state */
+static void alc883_mitac_hp_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x15, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+ snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+}
+
+/* auto-toggle front mic */
+/*
+static void alc883_mitac_mic_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+ unsigned char bits;
+
+ present = snd_hda_codec_read(codec, 0x18, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ bits = present ? HDA_AMP_MUTE : 0;
+ snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits);
+}
+*/
+
+static void alc883_mitac_automute(struct hda_codec *codec)
+{
+ alc883_mitac_hp_automute(codec);
+ /* alc883_mitac_mic_automute(codec); */
+}
+
+static void alc883_mitac_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ switch (res >> 26) {
+ case ALC880_HP_EVENT:
+ alc883_mitac_hp_automute(codec);
+ break;
+ case ALC880_MIC_EVENT:
+ /* alc883_mitac_mic_automute(codec); */
+ break;
+ }
+}
+
+static struct hda_verb alc883_mitac_verbs[] = {
+ /* HP */
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ /* Subwoofer */
+ {0x17, AC_VERB_SET_CONNECT_SEL, 0x02},
+ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+
+ /* enable unsolicited event */
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+ /* {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, */
+
+ { } /* end */
+};
+
static struct hda_verb alc883_tagra_verbs[] = {
{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
@@ -6843,6 +7238,15 @@ static struct hda_verb alc888_3st_hp_verbs[] = {
{ }
};
+static struct hda_verb alc888_6st_dell_verbs[] = {
+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x02}, /* Rear : output 1 (0x0e) */
+ {0x16, AC_VERB_SET_CONNECT_SEL, 0x01}, /* CLFE : output 2 (0x0d) */
+ {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, /* Side : output 3 (0x0f) */
+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+ { }
+};
+
static struct hda_verb alc888_3st_hp_2ch_init[] = {
{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
@@ -7038,6 +7442,33 @@ static struct hda_verb alc883_acer_eapd_verbs[] = {
{ }
};
+static void alc888_6st_dell_front_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+
+ present = snd_hda_codec_read(codec, 0x1b, 0,
+ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+ snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+ snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+ snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+}
+
+static void alc888_6st_dell_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ switch (res >> 26) {
+ case ALC880_HP_EVENT:
+ printk("hp_event\n");
+ alc888_6st_dell_front_automute(codec);
+ break;
+ }
+}
+
/*
* generic initialization of ADC, input mixers and output mixers
*/
@@ -7096,7 +7527,7 @@ static struct hda_verb alc883_auto_init_verbs[] = {
{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
/* {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
{ }
};
@@ -7111,7 +7542,6 @@ static struct snd_kcontrol_new alc883_capture_mixer[] = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* The multiple "Capture Source" controls confuse alsamixer
* So call somewhat different..
- * FIXME: the controls appear in the "playback" view!
*/
/* .name = "Capture Source", */
.name = "Input Source",
@@ -7130,6 +7560,7 @@ static struct snd_kcontrol_new alc883_capture_mixer[] = {
/* pcm configuration: identiacal with ALC880 */
#define alc883_pcm_analog_playback alc880_pcm_analog_playback
#define alc883_pcm_analog_capture alc880_pcm_analog_capture
+#define alc883_pcm_analog_alt_capture alc880_pcm_analog_alt_capture
#define alc883_pcm_digital_playback alc880_pcm_digital_playback
#define alc883_pcm_digital_capture alc880_pcm_digital_capture
@@ -7154,53 +7585,58 @@ static const char *alc883_models[ALC883_MODEL_LAST] = {
[ALC883_HAIER_W66] = "haier-w66",
[ALC888_6ST_HP] = "6stack-hp",
[ALC888_3ST_HP] = "3stack-hp",
+ [ALC888_6ST_DELL] = "6stack-dell",
+ [ALC883_MITAC] = "mitac",
[ALC883_AUTO] = "auto",
};
static struct snd_pci_quirk alc883_cfg_tbl[] = {
SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC883_3ST_6ch_DIG),
+ SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE),
+ SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_ACER_ASPIRE),
+ SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_ACER_ASPIRE),
+ SND_PCI_QUIRK(0x1025, 0, "Acer laptop", ALC883_ACER), /* default Acer */
+ SND_PCI_QUIRK(0x1028, 0x020d, "Dell Inspiron 530", ALC888_6ST_DELL),
SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavillion", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch),
- SND_PCI_QUIRK(0x1558, 0, "Clevo laptop", ALC883_LAPTOP_EAPD),
+ SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP),
+ SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP),
+ SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC888_6ST_HP),
+ SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG),
SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG),
+ SND_PCI_QUIRK(0x1071, 0x8253, "Mitac 8252d", ALC883_MITAC),
+ SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD),
+ SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch),
SND_PCI_QUIRK(0x1458, 0xa002, "MSI", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG),
- SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG),
SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG),
+ SND_PCI_QUIRK(0x1462, 0x040d, "MSI", ALC883_TARGA_2ch_DIG),
SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG),
SND_PCI_QUIRK(0x1462, 0x3729, "MSI S420", ALC883_TARGA_DIG),
- SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG),
SND_PCI_QUIRK(0x1462, 0x3b7f, "MSI", ALC883_TARGA_2ch_DIG),
- SND_PCI_QUIRK(0x1462, 0x3fcc, "MSI", ALC883_TARGA_DIG),
+ SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG),
SND_PCI_QUIRK(0x1462, 0x3fc1, "MSI", ALC883_TARGA_DIG),
SND_PCI_QUIRK(0x1462, 0x3fc3, "MSI", ALC883_TARGA_DIG),
+ SND_PCI_QUIRK(0x1462, 0x3fcc, "MSI", ALC883_TARGA_DIG),
SND_PCI_QUIRK(0x1462, 0x3fdf, "MSI", ALC883_TARGA_DIG),
SND_PCI_QUIRK(0x1462, 0x4314, "MSI", ALC883_TARGA_DIG),
SND_PCI_QUIRK(0x1462, 0x4319, "MSI", ALC883_TARGA_DIG),
SND_PCI_QUIRK(0x1462, 0x4324, "MSI", ALC883_TARGA_DIG),
+ SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG),
+ SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG),
+ SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG),
+ SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG),
+ SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG),
SND_PCI_QUIRK(0x1462, 0xa422, "MSI", ALC883_TARGA_2ch_DIG),
- SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE),
- SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_ACER_ASPIRE),
- SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_ACER_ASPIRE),
- SND_PCI_QUIRK(0x1025, 0, "Acer laptop", ALC883_ACER),
+ SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG),
+ SND_PCI_QUIRK(0x1558, 0, "Clevo laptop", ALC883_LAPTOP_EAPD),
SND_PCI_QUIRK(0x15d9, 0x8780, "Supermicro PDSBA", ALC883_3ST_6ch),
SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION),
- SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD),
- SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch),
SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo 101e", ALC883_LENOVO_101E_2ch),
- SND_PCI_QUIRK(0x17aa, 0x3bfd, "Lenovo NB0763", ALC883_LENOVO_NB0763),
SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763),
- SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC888_6ST_HP),
- SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP),
- SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP),
+ SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763),
+ SND_PCI_QUIRK(0x17aa, 0x3bfd, "Lenovo NB0763", ALC883_LENOVO_NB0763),
SND_PCI_QUIRK(0x17c0, 0x4071, "MEDION MD2", ALC883_MEDION_MD2),
SND_PCI_QUIRK(0x1991, 0x5625, "Haier W66", ALC883_HAIER_W66),
- SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763),
- SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG),
- SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG),
+ SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch),
{}
};
@@ -7435,6 +7871,34 @@ static struct alc_config_preset alc883_presets[] = {
.need_dac_fix = 1,
.input_mux = &alc883_capture_source,
},
+ [ALC888_6ST_DELL] = {
+ .mixers = { alc888_6st_dell_mixer, alc883_chmode_mixer },
+ .init_verbs = { alc883_init_verbs, alc888_6st_dell_verbs },
+ .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+ .dac_nids = alc883_dac_nids,
+ .dig_out_nid = ALC883_DIGOUT_NID,
+ .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+ .adc_nids = alc883_adc_nids,
+ .dig_in_nid = ALC883_DIGIN_NID,
+ .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
+ .channel_mode = alc883_sixstack_modes,
+ .input_mux = &alc883_capture_source,
+ .unsol_event = alc888_6st_dell_unsol_event,
+ .init_hook = alc888_6st_dell_front_automute,
+ },
+ [ALC883_MITAC] = {
+ .mixers = { alc883_mitac_mixer },
+ .init_verbs = { alc883_init_verbs, alc883_mitac_verbs },
+ .num_dacs = ARRAY_SIZE(alc883_dac_nids),
+ .dac_nids = alc883_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+ .adc_nids = alc883_adc_nids,
+ .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
+ .channel_mode = alc883_3ST_2ch_modes,
+ .input_mux = &alc883_capture_source,
+ .unsol_event = alc883_mitac_unsol_event,
+ .init_hook = alc883_mitac_automute,
+ },
};
@@ -7582,6 +8046,7 @@ static int patch_alc883(struct hda_codec *codec)
spec->stream_name_analog = "ALC883 Analog";
spec->stream_analog_playback = &alc883_pcm_analog_playback;
spec->stream_analog_capture = &alc883_pcm_analog_capture;
+ spec->stream_analog_alt_capture = &alc883_pcm_analog_alt_capture;
spec->stream_name_digital = "ALC883 Digital";
spec->stream_digital_playback = &alc883_pcm_digital_playback;
@@ -7592,6 +8057,8 @@ static int patch_alc883(struct hda_codec *codec)
spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids);
}
+ spec->vmaster_nid = 0x0c;
+
codec->patch_ops = alc_patch_ops;
if (board_config == ALC883_AUTO)
spec->init_hook = alc883_auto_init;
@@ -7659,13 +8126,99 @@ static struct snd_kcontrol_new alc262_hippo1_mixer[] = {
{ } /* end */
};
+/* update HP, line and mono-out pins according to the master switch */
+static void alc262_hp_master_update(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int val = spec->master_sw;
+
+ /* HP & line-out */
+ snd_hda_codec_write_cache(codec, 0x1b, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ val ? PIN_HP : 0);
+ snd_hda_codec_write_cache(codec, 0x15, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ val ? PIN_HP : 0);
+ /* mono (speaker) depending on the HP jack sense */
+ val = val && !spec->jack_present;
+ snd_hda_codec_write_cache(codec, 0x16, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ val ? PIN_OUT : 0);
+}
+
+static void alc262_hp_bpc_automute(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int presence;
+ presence = snd_hda_codec_read(codec, 0x1b, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+ spec->jack_present = !!(presence & AC_PINSENSE_PRESENCE);
+ alc262_hp_master_update(codec);
+}
+
+static void alc262_hp_bpc_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+ if ((res >> 26) != ALC880_HP_EVENT)
+ return;
+ alc262_hp_bpc_automute(codec);
+}
+
+static void alc262_hp_wildwest_automute(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int presence;
+ presence = snd_hda_codec_read(codec, 0x15, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+ spec->jack_present = !!(presence & AC_PINSENSE_PRESENCE);
+ alc262_hp_master_update(codec);
+}
+
+static void alc262_hp_wildwest_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if ((res >> 26) != ALC880_HP_EVENT)
+ return;
+ alc262_hp_wildwest_automute(codec);
+}
+
+static int alc262_hp_master_sw_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+ *ucontrol->value.integer.value = spec->master_sw;
+ return 0;
+}
+
+static int alc262_hp_master_sw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct alc_spec *spec = codec->spec;
+ int val = !!*ucontrol->value.integer.value;
+
+ if (val == spec->master_sw)
+ return 0;
+ spec->master_sw = val;
+ alc262_hp_master_update(codec);
+ return 1;
+}
+
static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = alc262_hp_master_sw_get,
+ .put = alc262_hp_master_sw_put,
+ },
HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
-
+ HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 2, 0x0,
+ HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 2, 0x0,
+ HDA_OUTPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
@@ -7684,12 +8237,21 @@ static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = {
};
static struct snd_kcontrol_new alc262_HP_BPC_WildWest_mixer[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = snd_ctl_boolean_mono_info,
+ .get = alc262_hp_master_sw_get,
+ .put = alc262_hp_master_sw_put,
+ },
HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
- HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
- HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0e, 2, 0x0,
+ HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Speaker Playback Switch", 0x16, 2, 0x0,
+ HDA_OUTPUT),
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x02, HDA_INPUT),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x02, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Boost", 0x1a, 0, HDA_INPUT),
@@ -7709,6 +8271,85 @@ static struct snd_kcontrol_new alc262_HP_BPC_WildWest_option_mixer[] = {
{ } /* end */
};
+/* mute/unmute internal speaker according to the hp jack and mute state */
+static void alc262_hp_t5735_automute(struct hda_codec *codec, int force)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (force || !spec->sense_updated) {
+ unsigned int present;
+ present = snd_hda_codec_read(codec, 0x15, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+ spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0;
+ spec->sense_updated = 1;
+ }
+ snd_hda_codec_amp_stereo(codec, 0x0c, HDA_OUTPUT, 0, HDA_AMP_MUTE,
+ spec->jack_present ? HDA_AMP_MUTE : 0);
+}
+
+static void alc262_hp_t5735_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if ((res >> 26) != ALC880_HP_EVENT)
+ return;
+ alc262_hp_t5735_automute(codec, 1);
+}
+
+static void alc262_hp_t5735_init_hook(struct hda_codec *codec)
+{
+ alc262_hp_t5735_automute(codec, 1);
+}
+
+static struct snd_kcontrol_new alc262_hp_t5735_mixer[] = {
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+ { } /* end */
+};
+
+static struct hda_verb alc262_hp_t5735_verbs[] = {
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+ { }
+};
+
+static struct snd_kcontrol_new alc262_hp_rp5700_mixer[] = {
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0e, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x16, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT),
+ { } /* end */
+};
+
+static struct hda_verb alc262_hp_rp5700_verbs[] = {
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x00 << 8))},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x00 << 8))},
+ {}
+};
+
+static struct hda_input_mux alc262_hp_rp5700_capture_source = {
+ .num_items = 1,
+ .items = {
+ { "Line", 0x1 },
+ },
+};
+
/* bind hp and internal speaker mute (with plug check) */
static int alc262_sony_master_sw_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -8082,6 +8723,72 @@ static struct hda_verb alc262_benq_t31_EAPD_verbs[] = {
{}
};
+/* Samsung Q1 Ultra Vista model setup */
+static struct snd_kcontrol_new alc262_ultra_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x19, 0, HDA_INPUT),
+ { } /* end */
+};
+
+static struct hda_verb alc262_ultra_verbs[] = {
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ /* Mic is on Node 0x19 */
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+ {0x22, AC_VERB_SET_CONNECT_SEL, 0x01},
+ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ {0x23, AC_VERB_SET_CONNECT_SEL, 0x01},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ {0x24, AC_VERB_SET_CONNECT_SEL, 0x01},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+ {}
+};
+
+static struct hda_input_mux alc262_ultra_capture_source = {
+ .num_items = 1,
+ .items = {
+ { "Mic", 0x1 },
+ },
+};
+
+/* mute/unmute internal speaker according to the hp jack and mute state */
+static void alc262_ultra_automute(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ unsigned int mute;
+ unsigned int present;
+
+ /* need to execute and sync at first */
+ snd_hda_codec_read(codec, 0x15, 0, AC_VERB_SET_PIN_SENSE, 0);
+ present = snd_hda_codec_read(codec, 0x15, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+ spec->jack_present = (present & 0x80000000) != 0;
+ if (spec->jack_present) {
+ /* mute internal speaker */
+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, HDA_AMP_MUTE);
+ } else {
+ /* unmute internal speaker if necessary */
+ mute = snd_hda_codec_amp_read(codec, 0x15, 0, HDA_OUTPUT, 0);
+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, mute);
+ }
+}
+
+/* unsolicited event for HP jack sensing */
+static void alc262_ultra_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if ((res >> 26) != ALC880_HP_EVENT)
+ return;
+ alc262_ultra_automute(codec);
+}
+
/* add playback controls from the parsed DAC table */
static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec,
const struct auto_pin_cfg *cfg)
@@ -8269,7 +8976,7 @@ static struct hda_verb alc262_HP_BPC_init_verbs[] = {
{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
@@ -8311,6 +9018,8 @@ static struct hda_verb alc262_HP_BPC_init_verbs[] = {
{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+ {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+
{ }
};
@@ -8405,6 +9114,8 @@ static struct hda_verb alc262_HP_BPC_WildWest_init_verbs[] = {
/* {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x06 << 8))}, */
{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x07 << 8))},
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+
{ }
};
@@ -8484,39 +9195,49 @@ static const char *alc262_models[ALC262_MODEL_LAST] = {
[ALC262_FUJITSU] = "fujitsu",
[ALC262_HP_BPC] = "hp-bpc",
[ALC262_HP_BPC_D7000_WL]= "hp-bpc-d7000",
+ [ALC262_HP_TC_T5735] = "hp-tc-t5735",
+ [ALC262_HP_RP5700] = "hp-rp5700",
[ALC262_BENQ_ED8] = "benq",
[ALC262_BENQ_T31] = "benq-t31",
[ALC262_SONY_ASSAMD] = "sony-assamd",
+ [ALC262_ULTRA] = "ultra",
[ALC262_AUTO] = "auto",
};
static struct snd_pci_quirk alc262_cfg_tbl[] = {
SND_PCI_QUIRK(0x1002, 0x437b, "Hippo", ALC262_HIPPO),
SND_PCI_QUIRK(0x103c, 0x12fe, "HP xw9400", ALC262_HP_BPC),
- SND_PCI_QUIRK(0x103c, 0x280c, "HP xw4400", ALC262_HP_BPC),
SND_PCI_QUIRK(0x103c, 0x12ff, "HP xw4550", ALC262_HP_BPC),
- SND_PCI_QUIRK(0x103c, 0x1308, "HP xw4600", ALC262_HP_BPC),
- SND_PCI_QUIRK(0x103c, 0x3014, "HP xw6400", ALC262_HP_BPC),
- SND_PCI_QUIRK(0x103c, 0x1307, "HP xw6600", ALC262_HP_BPC),
- SND_PCI_QUIRK(0x103c, 0x3015, "HP xw8400", ALC262_HP_BPC),
SND_PCI_QUIRK(0x103c, 0x1306, "HP xw8600", ALC262_HP_BPC),
+ SND_PCI_QUIRK(0x103c, 0x1307, "HP xw6600", ALC262_HP_BPC),
+ SND_PCI_QUIRK(0x103c, 0x1308, "HP xw4600", ALC262_HP_BPC),
+ SND_PCI_QUIRK(0x103c, 0x1309, "HP xw4*00", ALC262_HP_BPC),
+ SND_PCI_QUIRK(0x103c, 0x130a, "HP xw6*00", ALC262_HP_BPC),
+ SND_PCI_QUIRK(0x103c, 0x130b, "HP xw8*00", ALC262_HP_BPC),
SND_PCI_QUIRK(0x103c, 0x2800, "HP D7000", ALC262_HP_BPC_D7000_WL),
- SND_PCI_QUIRK(0x103c, 0x2802, "HP D7000", ALC262_HP_BPC_D7000_WL),
- SND_PCI_QUIRK(0x103c, 0x2804, "HP D7000", ALC262_HP_BPC_D7000_WL),
- SND_PCI_QUIRK(0x103c, 0x2806, "HP D7000", ALC262_HP_BPC_D7000_WL),
SND_PCI_QUIRK(0x103c, 0x2801, "HP D7000", ALC262_HP_BPC_D7000_WF),
+ SND_PCI_QUIRK(0x103c, 0x2802, "HP D7000", ALC262_HP_BPC_D7000_WL),
SND_PCI_QUIRK(0x103c, 0x2803, "HP D7000", ALC262_HP_BPC_D7000_WF),
+ SND_PCI_QUIRK(0x103c, 0x2804, "HP D7000", ALC262_HP_BPC_D7000_WL),
SND_PCI_QUIRK(0x103c, 0x2805, "HP D7000", ALC262_HP_BPC_D7000_WF),
+ SND_PCI_QUIRK(0x103c, 0x2806, "HP D7000", ALC262_HP_BPC_D7000_WL),
SND_PCI_QUIRK(0x103c, 0x2807, "HP D7000", ALC262_HP_BPC_D7000_WF),
+ SND_PCI_QUIRK(0x103c, 0x280c, "HP xw4400", ALC262_HP_BPC),
+ SND_PCI_QUIRK(0x103c, 0x3014, "HP xw6400", ALC262_HP_BPC),
+ SND_PCI_QUIRK(0x103c, 0x3015, "HP xw8400", ALC262_HP_BPC),
+ SND_PCI_QUIRK(0x103c, 0x302f, "HP Thin Client T5735",
+ ALC262_HP_TC_T5735),
+ SND_PCI_QUIRK(0x103c, 0x2817, "HP RP5700", ALC262_HP_RP5700),
+ SND_PCI_QUIRK(0x104d, 0x1f00, "Sony ASSAMD", ALC262_SONY_ASSAMD),
SND_PCI_QUIRK(0x104d, 0x8203, "Sony UX-90", ALC262_HIPPO),
+ SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD),
+ SND_PCI_QUIRK(0x104d, 0x900e, "Sony ASSAMD", ALC262_SONY_ASSAMD),
+ SND_PCI_QUIRK(0x104d, 0x9015, "Sony 0x9015", ALC262_SONY_ASSAMD),
SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU),
- SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1),
+ SND_PCI_QUIRK(0x144d, 0xc032, "Samsung Q1 Ultra", ALC262_ULTRA),
SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_BENQ_ED8),
SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_BENQ_T31),
- SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD),
- SND_PCI_QUIRK(0x104d, 0x9015, "Sony 0x9015", ALC262_SONY_ASSAMD),
- SND_PCI_QUIRK(0x104d, 0x900e, "Sony ASSAMD", ALC262_SONY_ASSAMD),
- SND_PCI_QUIRK(0x104d, 0x1f00, "Sony ASSAMD", ALC262_SONY_ASSAMD),
+ SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1),
{}
};
@@ -8579,6 +9300,8 @@ static struct alc_config_preset alc262_presets[] = {
.num_channel_mode = ARRAY_SIZE(alc262_modes),
.channel_mode = alc262_modes,
.input_mux = &alc262_HP_capture_source,
+ .unsol_event = alc262_hp_bpc_unsol_event,
+ .init_hook = alc262_hp_bpc_automute,
},
[ALC262_HP_BPC_D7000_WF] = {
.mixers = { alc262_HP_BPC_WildWest_mixer },
@@ -8589,6 +9312,8 @@ static struct alc_config_preset alc262_presets[] = {
.num_channel_mode = ARRAY_SIZE(alc262_modes),
.channel_mode = alc262_modes,
.input_mux = &alc262_HP_D7000_capture_source,
+ .unsol_event = alc262_hp_wildwest_unsol_event,
+ .init_hook = alc262_hp_wildwest_automute,
},
[ALC262_HP_BPC_D7000_WL] = {
.mixers = { alc262_HP_BPC_WildWest_mixer,
@@ -8600,7 +9325,30 @@ static struct alc_config_preset alc262_presets[] = {
.num_channel_mode = ARRAY_SIZE(alc262_modes),
.channel_mode = alc262_modes,
.input_mux = &alc262_HP_D7000_capture_source,
+ .unsol_event = alc262_hp_wildwest_unsol_event,
+ .init_hook = alc262_hp_wildwest_automute,
+ },
+ [ALC262_HP_TC_T5735] = {
+ .mixers = { alc262_hp_t5735_mixer },
+ .init_verbs = { alc262_init_verbs, alc262_hp_t5735_verbs },
+ .num_dacs = ARRAY_SIZE(alc262_dac_nids),
+ .dac_nids = alc262_dac_nids,
+ .hp_nid = 0x03,
+ .num_channel_mode = ARRAY_SIZE(alc262_modes),
+ .channel_mode = alc262_modes,
+ .input_mux = &alc262_capture_source,
+ .unsol_event = alc262_hp_t5735_unsol_event,
+ .init_hook = alc262_hp_t5735_init_hook,
},
+ [ALC262_HP_RP5700] = {
+ .mixers = { alc262_hp_rp5700_mixer },
+ .init_verbs = { alc262_init_verbs, alc262_hp_rp5700_verbs },
+ .num_dacs = ARRAY_SIZE(alc262_dac_nids),
+ .dac_nids = alc262_dac_nids,
+ .num_channel_mode = ARRAY_SIZE(alc262_modes),
+ .channel_mode = alc262_modes,
+ .input_mux = &alc262_hp_rp5700_capture_source,
+ },
[ALC262_BENQ_ED8] = {
.mixers = { alc262_base_mixer },
.init_verbs = { alc262_init_verbs, alc262_EAPD_verbs },
@@ -8635,6 +9383,19 @@ static struct alc_config_preset alc262_presets[] = {
.unsol_event = alc262_hippo_unsol_event,
.init_hook = alc262_hippo_automute,
},
+ [ALC262_ULTRA] = {
+ .mixers = { alc262_ultra_mixer },
+ .init_verbs = { alc262_init_verbs, alc262_ultra_verbs },
+ .num_dacs = ARRAY_SIZE(alc262_dac_nids),
+ .dac_nids = alc262_dac_nids,
+ .hp_nid = 0x03,
+ .dig_out_nid = ALC262_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc262_modes),
+ .channel_mode = alc262_modes,
+ .input_mux = &alc262_ultra_capture_source,
+ .unsol_event = alc262_ultra_unsol_event,
+ .init_hook = alc262_ultra_automute,
+ },
};
static int patch_alc262(struct hda_codec *codec)
@@ -8716,6 +9477,8 @@ static int patch_alc262(struct hda_codec *codec)
}
}
+ spec->vmaster_nid = 0x0c;
+
codec->patch_ops = alc_patch_ops;
if (board_config == ALC262_AUTO)
spec->init_hook = alc262_auto_init;
@@ -8873,6 +9636,49 @@ static void alc268_acer_init_hook(struct hda_codec *codec)
alc268_acer_automute(codec, 1);
}
+static struct snd_kcontrol_new alc268_dell_mixer[] = {
+ /* output mixer control */
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Internal Mic Boost", 0x19, 0, HDA_INPUT),
+ { }
+};
+
+static struct hda_verb alc268_dell_verbs[] = {
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+ { }
+};
+
+/* mute/unmute internal speaker according to the hp jack and mute state */
+static void alc268_dell_automute(struct hda_codec *codec)
+{
+ unsigned int present;
+ unsigned int mute;
+
+ present = snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_PIN_SENSE, 0);
+ if (present & 0x80000000)
+ mute = HDA_AMP_MUTE;
+ else
+ mute = snd_hda_codec_amp_read(codec, 0x15, 0, HDA_OUTPUT, 0);
+ snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, mute);
+}
+
+static void alc268_dell_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if ((res >> 26) != ALC880_HP_EVENT)
+ return;
+ alc268_dell_automute(codec);
+}
+
+#define alc268_dell_init_hook alc268_dell_automute
+
/*
* generic initialization of ADC, input mixers and output mixers
*/
@@ -8915,19 +9721,13 @@ static struct hda_verb alc268_base_init_verbs[] = {
{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- /* FIXME: use matrix-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1c, 14, 15, 0b */
- /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
- /* Input mixer2 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
+ /* Unmute Selector 23h,24h and set the default input to mic-in */
+
+ {0x23, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x24, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
{ }
};
@@ -8972,29 +9772,14 @@ static int alc268_mux_enum_put(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
- const struct hda_input_mux *imux = spec->input_mux;
+
unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
static hda_nid_t capture_mixers[3] = { 0x23, 0x24 };
hda_nid_t nid = capture_mixers[adc_idx];
- unsigned int *cur_val = &spec->cur_mux[adc_idx];
- unsigned int i, idx;
- idx = ucontrol->value.enumerated.item[0];
- if (idx >= imux->num_items)
- idx = imux->num_items - 1;
- if (*cur_val == idx)
- return 0;
- for (i = 0; i < imux->num_items; i++) {
- unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE;
- snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT,
- imux->items[i].index,
- HDA_AMP_MUTE, v);
- snd_hda_codec_write_cache(codec, nid, 0,
- AC_VERB_SET_CONNECT_SEL,
- idx );
- }
- *cur_val = idx;
- return 1;
+ return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
+ nid,
+ &spec->cur_mux[adc_idx]);
}
static struct snd_kcontrol_new alc268_capture_alt_mixer[] = {
@@ -9004,7 +9789,6 @@ static struct snd_kcontrol_new alc268_capture_alt_mixer[] = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* The multiple "Capture Source" controls confuse alsamixer
* So call somewhat different..
- * FIXME: the controls appear in the "playback" view!
*/
/* .name = "Capture Source", */
.name = "Input Source",
@@ -9025,7 +9809,6 @@ static struct snd_kcontrol_new alc268_capture_mixer[] = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* The multiple "Capture Source" controls confuse alsamixer
* So call somewhat different..
- * FIXME: the controls appear in the "playback" view!
*/
/* .name = "Capture Source", */
.name = "Input Source",
@@ -9047,6 +9830,61 @@ static struct hda_input_mux alc268_capture_source = {
},
};
+#ifdef CONFIG_SND_DEBUG
+static struct snd_kcontrol_new alc268_test_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+
+ /* Volume widgets */
+ HDA_CODEC_VOLUME("LOUT1 Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("LOUT2 Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE_MONO("Mono sum Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+ HDA_BIND_MUTE("LINE-OUT sum Playback Switch", 0x0f, 2, HDA_INPUT),
+ HDA_BIND_MUTE("HP-OUT sum Playback Switch", 0x10, 2, HDA_INPUT),
+ HDA_BIND_MUTE("LINE-OUT Playback Switch", 0x14, 2, HDA_OUTPUT),
+ HDA_BIND_MUTE("HP-OUT Playback Switch", 0x15, 2, HDA_OUTPUT),
+ HDA_BIND_MUTE("Mono Playback Switch", 0x16, 2, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("MIC1 Capture Volume", 0x18, 0x0, HDA_INPUT),
+ HDA_BIND_MUTE("MIC1 Capture Switch", 0x18, 2, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("MIC2 Capture Volume", 0x19, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("LINE1 Capture Volume", 0x1a, 0x0, HDA_INPUT),
+ HDA_BIND_MUTE("LINE1 Capture Switch", 0x1a, 2, HDA_OUTPUT),
+ /* The below appears problematic on some hardwares */
+ /*HDA_CODEC_VOLUME("PCBEEP Playback Volume", 0x1d, 0x0, HDA_INPUT),*/
+ HDA_CODEC_VOLUME("PCM-IN1 Capture Volume", 0x23, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("PCM-IN1 Capture Switch", 0x23, 2, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("PCM-IN2 Capture Volume", 0x24, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("PCM-IN2 Capture Switch", 0x24, 2, HDA_OUTPUT),
+
+ /* Modes for retasking pin widgets */
+ ALC_PIN_MODE("LINE-OUT pin mode", 0x14, ALC_PIN_DIR_INOUT),
+ ALC_PIN_MODE("HP-OUT pin mode", 0x15, ALC_PIN_DIR_INOUT),
+ ALC_PIN_MODE("MIC1 pin mode", 0x18, ALC_PIN_DIR_INOUT),
+ ALC_PIN_MODE("LINE1 pin mode", 0x1a, ALC_PIN_DIR_INOUT),
+
+ /* Controls for GPIO pins, assuming they are configured as outputs */
+ ALC_GPIO_DATA_SWITCH("GPIO pin 0", 0x01, 0x01),
+ ALC_GPIO_DATA_SWITCH("GPIO pin 1", 0x01, 0x02),
+ ALC_GPIO_DATA_SWITCH("GPIO pin 2", 0x01, 0x04),
+ ALC_GPIO_DATA_SWITCH("GPIO pin 3", 0x01, 0x08),
+
+ /* Switches to allow the digital SPDIF output pin to be enabled.
+ * The ALC268 does not have an SPDIF input.
+ */
+ ALC_SPDIF_CTRL_SWITCH("SPDIF Playback Switch", 0x06, 0x01),
+
+ /* A switch allowing EAPD to be enabled. Some laptops seem to use
+ * this output to turn on an external amplifier.
+ */
+ ALC_EAPD_CTRL_SWITCH("LINE-OUT EAPD Enable Switch", 0x0f, 0x02),
+ ALC_EAPD_CTRL_SWITCH("HP-OUT EAPD Enable Switch", 0x10, 0x02),
+
+ { } /* end */
+};
+#endif
+
/* create input playback/capture controls for the given pin */
static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
const char *ctlname, int idx)
@@ -9194,6 +10032,7 @@ static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec)
/* pcm configuration: identiacal with ALC880 */
#define alc268_pcm_analog_playback alc880_pcm_analog_playback
#define alc268_pcm_analog_capture alc880_pcm_analog_capture
+#define alc268_pcm_analog_alt_capture alc880_pcm_analog_alt_capture
#define alc268_pcm_digital_playback alc880_pcm_digital_playback
/*
@@ -9259,16 +10098,23 @@ static const char *alc268_models[ALC268_MODEL_LAST] = {
[ALC268_3ST] = "3stack",
[ALC268_TOSHIBA] = "toshiba",
[ALC268_ACER] = "acer",
+ [ALC268_DELL] = "dell",
+#ifdef CONFIG_SND_DEBUG
+ [ALC268_TEST] = "test",
+#endif
[ALC268_AUTO] = "auto",
};
static struct snd_pci_quirk alc268_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x1025, 0x0126, "Acer", ALC268_ACER),
+ SND_PCI_QUIRK(0x1025, 0x012e, "Acer Aspire 5310", ALC268_ACER),
+ SND_PCI_QUIRK(0x1025, 0x0130, "Acer Extensa 5210", ALC268_ACER),
+ SND_PCI_QUIRK(0x1025, 0x0136, "Acer Aspire 5315", ALC268_ACER),
+ SND_PCI_QUIRK(0x1028, 0x0253, "Dell OEM", ALC268_DELL),
+ SND_PCI_QUIRK(0x103c, 0x30cc, "TOSHIBA", ALC268_TOSHIBA),
SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST),
SND_PCI_QUIRK(0x1179, 0xff10, "TOSHIBA A205", ALC268_TOSHIBA),
SND_PCI_QUIRK(0x1179, 0xff50, "TOSHIBA A305", ALC268_TOSHIBA),
- SND_PCI_QUIRK(0x103c, 0x30cc, "TOSHIBA", ALC268_TOSHIBA),
- SND_PCI_QUIRK(0x1025, 0x0126, "Acer", ALC268_ACER),
- SND_PCI_QUIRK(0x1025, 0x0130, "Acer Extensa 5210", ALC268_ACER),
SND_PCI_QUIRK(0x152d, 0x0763, "Diverse (CPR2000)", ALC268_ACER),
{}
};
@@ -9317,6 +10163,35 @@ static struct alc_config_preset alc268_presets[] = {
.unsol_event = alc268_acer_unsol_event,
.init_hook = alc268_acer_init_hook,
},
+ [ALC268_DELL] = {
+ .mixers = { alc268_dell_mixer },
+ .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
+ alc268_dell_verbs },
+ .num_dacs = ARRAY_SIZE(alc268_dac_nids),
+ .dac_nids = alc268_dac_nids,
+ .hp_nid = 0x02,
+ .num_channel_mode = ARRAY_SIZE(alc268_modes),
+ .channel_mode = alc268_modes,
+ .unsol_event = alc268_dell_unsol_event,
+ .init_hook = alc268_dell_init_hook,
+ .input_mux = &alc268_capture_source,
+ },
+#ifdef CONFIG_SND_DEBUG
+ [ALC268_TEST] = {
+ .mixers = { alc268_test_mixer, alc268_capture_mixer },
+ .init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
+ alc268_volume_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc268_dac_nids),
+ .dac_nids = alc268_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
+ .adc_nids = alc268_adc_nids_alt,
+ .hp_nid = 0x03,
+ .dig_out_nid = ALC268_DIGOUT_NID,
+ .num_channel_mode = ARRAY_SIZE(alc268_modes),
+ .channel_mode = alc268_modes,
+ .input_mux = &alc268_capture_source,
+ },
+#endif
};
static int patch_alc268(struct hda_codec *codec)
@@ -9361,34 +10236,34 @@ static int patch_alc268(struct hda_codec *codec)
spec->stream_name_analog = "ALC268 Analog";
spec->stream_analog_playback = &alc268_pcm_analog_playback;
spec->stream_analog_capture = &alc268_pcm_analog_capture;
+ spec->stream_analog_alt_capture = &alc268_pcm_analog_alt_capture;
spec->stream_name_digital = "ALC268 Digital";
spec->stream_digital_playback = &alc268_pcm_digital_playback;
- if (board_config == ALC268_AUTO) {
- if (!spec->adc_nids && spec->input_mux) {
- /* check whether NID 0x07 is valid */
- unsigned int wcap = get_wcaps(codec, 0x07);
-
- /* get type */
- wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
- if (wcap != AC_WID_AUD_IN) {
- spec->adc_nids = alc268_adc_nids_alt;
- spec->num_adc_nids =
- ARRAY_SIZE(alc268_adc_nids_alt);
- spec->mixers[spec->num_mixers] =
+ if (!spec->adc_nids && spec->input_mux) {
+ /* check whether NID 0x07 is valid */
+ unsigned int wcap = get_wcaps(codec, 0x07);
+
+ /* get type */
+ wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+ if (wcap != AC_WID_AUD_IN) {
+ spec->adc_nids = alc268_adc_nids_alt;
+ spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt);
+ spec->mixers[spec->num_mixers] =
alc268_capture_alt_mixer;
- spec->num_mixers++;
- } else {
- spec->adc_nids = alc268_adc_nids;
- spec->num_adc_nids =
- ARRAY_SIZE(alc268_adc_nids);
- spec->mixers[spec->num_mixers] =
- alc268_capture_mixer;
- spec->num_mixers++;
- }
+ spec->num_mixers++;
+ } else {
+ spec->adc_nids = alc268_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids);
+ spec->mixers[spec->num_mixers] =
+ alc268_capture_mixer;
+ spec->num_mixers++;
}
}
+
+ spec->vmaster_nid = 0x02;
+
codec->patch_ops = alc_patch_ops;
if (board_config == ALC268_AUTO)
spec->init_hook = alc268_auto_init;
@@ -9397,6 +10272,360 @@ static int patch_alc268(struct hda_codec *codec)
}
/*
+ * ALC269 channel source setting (2 channel)
+ */
+#define ALC269_DIGOUT_NID ALC880_DIGOUT_NID
+
+#define alc269_dac_nids alc260_dac_nids
+
+static hda_nid_t alc269_adc_nids[1] = {
+ /* ADC1 */
+ 0x07,
+};
+
+#define alc269_modes alc260_modes
+#define alc269_capture_source alc880_lg_lw_capture_source
+
+static struct snd_kcontrol_new alc269_base_mixer[] = {
+ HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+ HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
+ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
+ { } /* end */
+};
+
+/* capture mixer elements */
+static struct snd_kcontrol_new alc269_capture_mixer[] = {
+ HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* The multiple "Capture Source" controls confuse alsamixer
+ * So call somewhat different..
+ */
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 1,
+ .info = alc_mux_enum_info,
+ .get = alc_mux_enum_get,
+ .put = alc_mux_enum_put,
+ },
+ { } /* end */
+};
+
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb alc269_init_verbs[] = {
+ /*
+ * Unmute ADC0 and set the default input to mic-in
+ */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+ /* Mute input amps (PCBeep, Line In, Mic 1 & Mic 2) of the
+ * analog-loopback mixer widget
+ * Note: PASD motherboards uses the Line In 2 as the input for
+ * front panel mic (mic 2)
+ */
+ /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+
+ /*
+ * Set up output mixers (0x0c - 0x0e)
+ */
+ /* set vol=0 to output mixers */
+ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+
+ /* set up input amps for analog loopback */
+ /* Amp Indices: DAC = 0, mixer = 1 */
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+
+ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+ /* FIXME: use matrix-type input source selection */
+ /* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */
+ /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+
+ /* set EAPD */
+ {0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
+ {0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
+ { }
+};
+
+/* add playback controls from the parsed DAC table */
+static int alc269_auto_create_multi_out_ctls(struct alc_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ hda_nid_t nid;
+ int err;
+
+ spec->multiout.num_dacs = 1; /* only use one dac */
+ spec->multiout.dac_nids = spec->private_dac_nids;
+ spec->multiout.dac_nids[0] = 2;
+
+ nid = cfg->line_out_pins[0];
+ if (nid) {
+ err = add_control(spec, ALC_CTL_WIDGET_VOL,
+ "Front Playback Volume",
+ HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ err = add_control(spec, ALC_CTL_WIDGET_MUTE,
+ "Front Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ }
+
+ nid = cfg->speaker_pins[0];
+ if (nid) {
+ if (!cfg->line_out_pins[0]) {
+ err = add_control(spec, ALC_CTL_WIDGET_VOL,
+ "Speaker Playback Volume",
+ HDA_COMPOSE_AMP_VAL(0x02, 3, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ }
+ if (nid == 0x16) {
+ err = add_control(spec, ALC_CTL_WIDGET_MUTE,
+ "Speaker Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 2, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ } else {
+ err = add_control(spec, ALC_CTL_WIDGET_MUTE,
+ "Speaker Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ }
+ }
+ nid = cfg->hp_pins[0];
+ if (nid) {
+ /* spec->multiout.hp_nid = 2; */
+ if (!cfg->line_out_pins[0] && !cfg->speaker_pins[0]) {
+ err = add_control(spec, ALC_CTL_WIDGET_VOL,
+ "Headphone Playback Volume",
+ HDA_COMPOSE_AMP_VAL(0x02, 3, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ }
+ if (nid == 0x16) {
+ err = add_control(spec, ALC_CTL_WIDGET_MUTE,
+ "Headphone Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 2, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ } else {
+ err = add_control(spec, ALC_CTL_WIDGET_MUTE,
+ "Headphone Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ }
+ }
+ return 0;
+}
+
+#define alc269_auto_create_analog_input_ctls \
+ alc880_auto_create_analog_input_ctls
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+#define alc269_loopbacks alc880_loopbacks
+#endif
+
+/* pcm configuration: identiacal with ALC880 */
+#define alc269_pcm_analog_playback alc880_pcm_analog_playback
+#define alc269_pcm_analog_capture alc880_pcm_analog_capture
+#define alc269_pcm_digital_playback alc880_pcm_digital_playback
+#define alc269_pcm_digital_capture alc880_pcm_digital_capture
+
+/*
+ * BIOS auto configuration
+ */
+static int alc269_parse_auto_config(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+ int err;
+ static hda_nid_t alc269_ignore[] = { 0x1d, 0 };
+
+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
+ alc269_ignore);
+ if (err < 0)
+ return err;
+
+ err = alc269_auto_create_multi_out_ctls(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+ err = alc269_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+
+ spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+
+ if (spec->autocfg.dig_out_pin)
+ spec->multiout.dig_out_nid = ALC269_DIGOUT_NID;
+
+ if (spec->kctl_alloc)
+ spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+
+ spec->init_verbs[spec->num_init_verbs++] = alc269_init_verbs;
+ spec->num_mux_defs = 1;
+ spec->input_mux = &spec->private_imux;
+
+ err = alc_auto_add_mic_boost(codec);
+ if (err < 0)
+ return err;
+
+ return 1;
+}
+
+#define alc269_auto_init_multi_out alc882_auto_init_multi_out
+#define alc269_auto_init_hp_out alc882_auto_init_hp_out
+#define alc269_auto_init_analog_input alc882_auto_init_analog_input
+
+
+/* init callback for auto-configuration model -- overriding the default init */
+static void alc269_auto_init(struct hda_codec *codec)
+{
+ alc269_auto_init_multi_out(codec);
+ alc269_auto_init_hp_out(codec);
+ alc269_auto_init_analog_input(codec);
+}
+
+/*
+ * configuration and preset
+ */
+static const char *alc269_models[ALC269_MODEL_LAST] = {
+ [ALC269_BASIC] = "basic",
+};
+
+static struct snd_pci_quirk alc269_cfg_tbl[] = {
+ {}
+};
+
+static struct alc_config_preset alc269_presets[] = {
+ [ALC269_BASIC] = {
+ .mixers = { alc269_base_mixer },
+ .init_verbs = { alc269_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc269_dac_nids),
+ .dac_nids = alc269_dac_nids,
+ .hp_nid = 0x03,
+ .num_channel_mode = ARRAY_SIZE(alc269_modes),
+ .channel_mode = alc269_modes,
+ .input_mux = &alc269_capture_source,
+ },
+};
+
+static int patch_alc269(struct hda_codec *codec)
+{
+ struct alc_spec *spec;
+ int board_config;
+ int err;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST,
+ alc269_models,
+ alc269_cfg_tbl);
+
+ if (board_config < 0) {
+ printk(KERN_INFO "hda_codec: Unknown model for ALC269, "
+ "trying auto-probe from BIOS...\n");
+ board_config = ALC269_AUTO;
+ }
+
+ if (board_config == ALC269_AUTO) {
+ /* automatic parse from the BIOS config */
+ err = alc269_parse_auto_config(codec);
+ if (err < 0) {
+ alc_free(codec);
+ return err;
+ } else if (!err) {
+ printk(KERN_INFO
+ "hda_codec: Cannot set up configuration "
+ "from BIOS. Using base mode...\n");
+ board_config = ALC269_BASIC;
+ }
+ }
+
+ if (board_config != ALC269_AUTO)
+ setup_preset(spec, &alc269_presets[board_config]);
+
+ spec->stream_name_analog = "ALC269 Analog";
+ spec->stream_analog_playback = &alc269_pcm_analog_playback;
+ spec->stream_analog_capture = &alc269_pcm_analog_capture;
+
+ spec->stream_name_digital = "ALC269 Digital";
+ spec->stream_digital_playback = &alc269_pcm_digital_playback;
+ spec->stream_digital_capture = &alc269_pcm_digital_capture;
+
+ spec->adc_nids = alc269_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids);
+ spec->mixers[spec->num_mixers] = alc269_capture_mixer;
+ spec->num_mixers++;
+
+ codec->patch_ops = alc_patch_ops;
+ if (board_config == ALC269_AUTO)
+ spec->init_hook = alc269_auto_init;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ if (!spec->loopback.amplist)
+ spec->loopback.amplist = alc269_loopbacks;
+#endif
+
+ return 0;
+}
+
+/*
* ALC861 channel source setting (2/6 channel selection for 3-stack)
*/
@@ -10213,7 +11442,6 @@ static struct snd_kcontrol_new alc861_capture_mixer[] = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* The multiple "Capture Source" controls confuse alsamixer
* So call somewhat different..
- *FIXME: the controls appear in the "playback" view!
*/
/* .name = "Capture Source", */
.name = "Input Source",
@@ -10369,22 +11597,23 @@ static struct snd_pci_quirk alc861_cfg_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC861_3ST),
SND_PCI_QUIRK(0x1043, 0x1335, "ASUS F2/3", ALC861_ASUS_LAPTOP),
SND_PCI_QUIRK(0x1043, 0x1338, "ASUS F2/3", ALC861_ASUS_LAPTOP),
- SND_PCI_QUIRK(0x1043, 0x13d7, "ASUS A9rp", ALC861_ASUS_LAPTOP),
- SND_PCI_QUIRK(0x1584, 0x9075, "Airis Praxis N1212", ALC861_ASUS_LAPTOP),
SND_PCI_QUIRK(0x1043, 0x1393, "ASUS", ALC861_ASUS),
+ SND_PCI_QUIRK(0x1043, 0x13d7, "ASUS A9rp", ALC861_ASUS_LAPTOP),
SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS P1-AH2", ALC861_3ST_DIG),
SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba", ALC861_TOSHIBA),
/* FIXME: the entry below breaks Toshiba A100 (model=auto works!)
* Any other models that need this preset?
*/
/* SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba", ALC861_TOSHIBA), */
- SND_PCI_QUIRK(0x1584, 0x9072, "Uniwill m31", ALC861_UNIWILL_M31),
- SND_PCI_QUIRK(0x1584, 0x9075, "Uniwill", ALC861_UNIWILL_M31),
+ SND_PCI_QUIRK(0x1462, 0x7254, "HP dx2200 (MSI MS-7254)", ALC861_3ST),
+ SND_PCI_QUIRK(0x1462, 0x7297, "HP dx2250 (MSI MS-7297)", ALC861_3ST),
SND_PCI_QUIRK(0x1584, 0x2b01, "Uniwill X40AIx", ALC861_UNIWILL_M31),
+ SND_PCI_QUIRK(0x1584, 0x9072, "Uniwill m31", ALC861_UNIWILL_M31),
+ SND_PCI_QUIRK(0x1584, 0x9075, "Airis Praxis N1212", ALC861_ASUS_LAPTOP),
+ /* FIXME: the below seems conflict */
+ /* SND_PCI_QUIRK(0x1584, 0x9075, "Uniwill", ALC861_UNIWILL_M31), */
SND_PCI_QUIRK(0x1849, 0x0660, "Asrock 939SLI32", ALC660_3ST),
SND_PCI_QUIRK(0x8086, 0xd600, "Intel", ALC861_3ST),
- SND_PCI_QUIRK(0x1462, 0x7254, "HP dx2200 (MSI MS-7254)", ALC861_3ST),
- SND_PCI_QUIRK(0x1462, 0x7297, "HP dx2250 (MSI MS-7297)", ALC861_3ST),
{}
};
@@ -10543,6 +11772,8 @@ static int patch_alc861(struct hda_codec *codec)
spec->stream_digital_playback = &alc861_pcm_digital_playback;
spec->stream_digital_capture = &alc861_pcm_digital_capture;
+ spec->vmaster_nid = 0x03;
+
codec->patch_ops = alc_patch_ops;
if (board_config == ALC861_AUTO)
spec->init_hook = alc861_auto_init;
@@ -10697,7 +11928,6 @@ static struct snd_kcontrol_new alc861vd_capture_mixer[] = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* The multiple "Capture Source" controls confuse alsamixer
* So call somewhat different..
- *FIXME: the controls appear in the "playback" view!
*/
/* .name = "Capture Source", */
.name = "Input Source",
@@ -11102,21 +12332,20 @@ static const char *alc861vd_models[ALC861VD_MODEL_LAST] = {
};
static struct snd_pci_quirk alc861vd_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x1019, 0xa88d, "Realtek ALC660 demo", ALC660VD_3ST),
+ SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_HP),
SND_PCI_QUIRK(0x1043, 0x12e2, "Asus z35m", ALC660VD_3ST),
SND_PCI_QUIRK(0x1043, 0x1339, "Asus G1", ALC660VD_3ST),
SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660VD_3ST_DIG),
SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST),
- SND_PCI_QUIRK(0x1019, 0xa88d, "Realtek ALC660 demo", ALC660VD_3ST),
-
+ SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO),
/*SND_PCI_QUIRK(0x1179, 0xff00, "DALLAS", ALC861VD_DALLAS),*/ /*lenovo*/
SND_PCI_QUIRK(0x1179, 0xff01, "DALLAS", ALC861VD_DALLAS),
- SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo 3000 C200", ALC861VD_LENOVO),
- SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo", ALC861VD_LENOVO),
- SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO),
SND_PCI_QUIRK(0x1179, 0xff03, "Toshiba P205", ALC861VD_LENOVO),
SND_PCI_QUIRK(0x1565, 0x820d, "Biostar NF61S SE", ALC861VD_6ST_DIG),
+ SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo", ALC861VD_LENOVO),
+ SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo 3000 C200", ALC861VD_LENOVO),
SND_PCI_QUIRK(0x1849, 0x0862, "ASRock K8NF6G-VSTA", ALC861VD_6ST_DIG),
- SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_HP),
{}
};
@@ -11520,6 +12749,8 @@ static int patch_alc861vd(struct hda_codec *codec)
spec->mixers[spec->num_mixers] = alc861vd_capture_mixer;
spec->num_mixers++;
+ spec->vmaster_nid = 0x02;
+
codec->patch_ops = alc_patch_ops;
if (board_config == ALC861VD_AUTO)
@@ -11699,18 +12930,6 @@ static struct snd_kcontrol_new alc662_base_mixer[] = {
HDA_CODEC_MUTE("Mic Playback Switch", 0xb, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Front Mic Playback Volume", 0xb, 0x01, HDA_INPUT),
HDA_CODEC_MUTE("Front Mic Playback Switch", 0xb, 0x01, HDA_INPUT),
-
- /* Capture mixer control */
- HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Capture Source",
- .count = 1,
- .info = alc_mux_enum_info,
- .get = alc_mux_enum_get,
- .put = alc_mux_enum_put,
- },
{ } /* end */
};
@@ -11728,17 +12947,6 @@ static struct snd_kcontrol_new alc662_3ST_2ch_mixer[] = {
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = alc662_mux_enum_info,
- .get = alc662_mux_enum_get,
- .put = alc662_mux_enum_put,
- },
{ } /* end */
};
@@ -11762,46 +12970,24 @@ static struct snd_kcontrol_new alc662_3ST_6ch_mixer[] = {
HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = alc662_mux_enum_info,
- .get = alc662_mux_enum_get,
- .put = alc662_mux_enum_put,
- },
{ } /* end */
};
static struct snd_kcontrol_new alc662_lenovo_101e_mixer[] = {
HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Front Playback Switch", 0x02, 2, HDA_INPUT),
- HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x03, 0x0, HDA_OUTPUT),
- HDA_BIND_MUTE("iSpeaker Playback Switch", 0x03, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Speaker Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Speaker Playback Switch", 0x03, 2, HDA_INPUT),
HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
- HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
- HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
- {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- /* .name = "Capture Source", */
- .name = "Input Source",
- .count = 1,
- .info = alc662_mux_enum_info,
- .get = alc662_mux_enum_get,
- .put = alc662_mux_enum_put,
- },
{ } /* end */
};
static struct snd_kcontrol_new alc662_eeepc_p701_mixer[] = {
- HDA_CODEC_MUTE("iSpeaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
HDA_CODEC_VOLUME("LineOut Playback Volume", 0x02, 0x0, HDA_OUTPUT),
HDA_CODEC_MUTE("LineOut Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
@@ -11816,6 +13002,24 @@ static struct snd_kcontrol_new alc662_eeepc_p701_mixer[] = {
{ } /* end */
};
+static struct snd_kcontrol_new alc662_eeepc_ep20_mixer[] = {
+ HDA_CODEC_VOLUME("LineOut Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+ HDA_CODEC_MUTE("LineOut Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("Surround Playback Switch", 0x03, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT),
+ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x04, 1, 2, HDA_INPUT),
+ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x04, 2, 2, HDA_INPUT),
+ HDA_CODEC_MUTE("Speaker Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+ HDA_BIND_MUTE("MuteCtrl Playback Switch", 0x0c, 2, HDA_INPUT),
+ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ { } /* end */
+};
+
static struct snd_kcontrol_new alc662_chmode_mixer[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -11901,6 +13105,13 @@ static struct hda_verb alc662_eeepc_sue_init_verbs[] = {
{}
};
+/* Set Unsolicited Event*/
+static struct hda_verb alc662_eeepc_ep20_sue_init_verbs[] = {
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+ {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+ {}
+};
+
/*
* generic initialization of ADC, input mixers and output mixers
*/
@@ -11957,14 +13168,13 @@ static struct snd_kcontrol_new alc662_capture_mixer[] = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* The multiple "Capture Source" controls confuse alsamixer
* So call somewhat different..
- * FIXME: the controls appear in the "playback" view!
*/
/* .name = "Capture Source", */
.name = "Input Source",
.count = 1,
- .info = alc882_mux_enum_info,
- .get = alc882_mux_enum_get,
- .put = alc882_mux_enum_put,
+ .info = alc662_mux_enum_info,
+ .get = alc662_mux_enum_get,
+ .put = alc662_mux_enum_put,
},
{ } /* end */
};
@@ -12037,6 +13247,40 @@ static void alc662_eeepc_inithook(struct hda_codec *codec)
alc662_eeepc_mic_automute(codec);
}
+static void alc662_eeepc_ep20_automute(struct hda_codec *codec)
+{
+ unsigned int mute;
+ unsigned int present;
+
+ snd_hda_codec_read(codec, 0x14, 0, AC_VERB_SET_PIN_SENSE, 0);
+ present = snd_hda_codec_read(codec, 0x14, 0,
+ AC_VERB_GET_PIN_SENSE, 0);
+ present = (present & 0x80000000) != 0;
+ if (present) {
+ /* mute internal speaker */
+ snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, HDA_AMP_MUTE);
+ } else {
+ /* unmute internal speaker if necessary */
+ mute = snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0);
+ snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
+ HDA_AMP_MUTE, mute);
+ }
+}
+
+/* unsolicited event for HP jack sensing */
+static void alc662_eeepc_ep20_unsol_event(struct hda_codec *codec,
+ unsigned int res)
+{
+ if ((res >> 26) == ALC880_HP_EVENT)
+ alc662_eeepc_ep20_automute(codec);
+}
+
+static void alc662_eeepc_ep20_inithook(struct hda_codec *codec)
+{
+ alc662_eeepc_ep20_automute(codec);
+}
+
#ifdef CONFIG_SND_HDA_POWER_SAVE
#define alc662_loopbacks alc880_loopbacks
#endif
@@ -12057,12 +13301,15 @@ static const char *alc662_models[ALC662_MODEL_LAST] = {
[ALC662_3ST_6ch] = "3stack-6ch",
[ALC662_5ST_DIG] = "6stack-dig",
[ALC662_LENOVO_101E] = "lenovo-101e",
+ [ALC662_ASUS_EEEPC_P701] = "eeepc-p701",
+ [ALC662_ASUS_EEEPC_EP20] = "eeepc-ep20",
[ALC662_AUTO] = "auto",
};
static struct snd_pci_quirk alc662_cfg_tbl[] = {
- SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E),
SND_PCI_QUIRK(0x1043, 0x82a1, "ASUS Eeepc", ALC662_ASUS_EEEPC_P701),
+ SND_PCI_QUIRK(0x1043, 0x82d1, "ASUS Eeepc EP20", ALC662_ASUS_EEEPC_EP20),
+ SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E),
{}
};
@@ -12149,6 +13396,21 @@ static struct alc_config_preset alc662_presets[] = {
.unsol_event = alc662_eeepc_unsol_event,
.init_hook = alc662_eeepc_inithook,
},
+ [ALC662_ASUS_EEEPC_EP20] = {
+ .mixers = { alc662_eeepc_ep20_mixer, alc662_capture_mixer,
+ alc662_chmode_mixer },
+ .init_verbs = { alc662_init_verbs,
+ alc662_eeepc_ep20_sue_init_verbs },
+ .num_dacs = ARRAY_SIZE(alc662_dac_nids),
+ .dac_nids = alc662_dac_nids,
+ .num_adc_nids = ARRAY_SIZE(alc662_adc_nids),
+ .adc_nids = alc662_adc_nids,
+ .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes),
+ .channel_mode = alc662_3ST_6ch_modes,
+ .input_mux = &alc662_lenovo_101e_capture_source,
+ .unsol_event = alc662_eeepc_ep20_unsol_event,
+ .init_hook = alc662_eeepc_ep20_inithook,
+ },
};
@@ -12308,6 +13570,7 @@ static void alc662_auto_init_multi_out(struct hda_codec *codec)
struct alc_spec *spec = codec->spec;
int i;
+ alc_subsystem_id(codec, 0x15, 0x1b, 0x14);
for (i = 0; i <= HDA_SIDE; i++) {
hda_nid_t nid = spec->autocfg.line_out_pins[i];
int pin_type = get_pin_type(spec->autocfg.line_out_type);
@@ -12458,6 +13721,8 @@ static int patch_alc662(struct hda_codec *codec)
spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids);
}
+ spec->vmaster_nid = 0x02;
+
codec->patch_ops = alc_patch_ops;
if (board_config == ALC662_AUTO)
spec->init_hook = alc662_auto_init;
@@ -12475,7 +13740,9 @@ static int patch_alc662(struct hda_codec *codec)
struct hda_codec_preset snd_hda_preset_realtek[] = {
{ .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 },
{ .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 },
+ { .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 },
{ .id = 0x10ec0268, .name = "ALC268", .patch = patch_alc268 },
+ { .id = 0x10ec0269, .name = "ALC269", .patch = patch_alc269 },
{ .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
.patch = patch_alc861 },
{ .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd },
@@ -12490,5 +13757,6 @@ struct hda_codec_preset snd_hda_preset_realtek[] = {
{ .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc883 },
{ .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 },
{ .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc883 },
+ { .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc883 },
{} /* terminator */
};
diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c
index 2a4b960..d22f5a6 100644
--- a/sound/pci/hda/patch_si3054.c
+++ b/sound/pci/hda/patch_si3054.c
@@ -22,7 +22,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>
@@ -287,7 +286,6 @@ static int patch_si3054(struct hda_codec *codec)
struct hda_codec_preset snd_hda_preset_si3054[] = {
{ .id = 0x163c3055, .name = "Si3054", .patch = patch_si3054 },
{ .id = 0x163c3155, .name = "Si3054", .patch = patch_si3054 },
- { .id = 0x11c11040, .name = "Si3054", .patch = patch_si3054 },
{ .id = 0x11c13026, .name = "Si3054", .patch = patch_si3054 },
{ .id = 0x11c13055, .name = "Si3054", .patch = patch_si3054 },
{ .id = 0x11c13155, .name = "Si3054", .patch = patch_si3054 },
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 */
};
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index 4cdf3e6..4e5dd4c 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -27,11 +27,12 @@
/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
+/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
+/* 2007-09-17 Lydia Wang Add VT1708B codec support */
/* */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-#include <sound/driver.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
@@ -51,14 +52,23 @@
#define VT1708_HP_NID 0x13
#define VT1708_DIGOUT_NID 0x14
#define VT1708_DIGIN_NID 0x16
+#define VT1708_DIGIN_PIN 0x26
#define VT1709_HP_DAC_NID 0x28
#define VT1709_DIGOUT_NID 0x13
#define VT1709_DIGIN_NID 0x17
+#define VT1709_DIGIN_PIN 0x25
+
+#define VT1708B_HP_NID 0x25
+#define VT1708B_DIGOUT_NID 0x12
+#define VT1708B_DIGIN_NID 0x15
+#define VT1708B_DIGIN_PIN 0x21
#define IS_VT1708_VENDORID(x) ((x) >= 0x11061708 && (x) <= 0x1106170b)
#define IS_VT1709_10CH_VENDORID(x) ((x) >= 0x1106e710 && (x) <= 0x1106e713)
#define IS_VT1709_6CH_VENDORID(x) ((x) >= 0x1106e714 && (x) <= 0x1106e717)
+#define IS_VT1708B_8CH_VENDORID(x) ((x) >= 0x1106e720 && (x) <= 0x1106e723)
+#define IS_VT1708B_4CH_VENDORID(x) ((x) >= 0x1106e724 && (x) <= 0x1106e727)
enum {
@@ -131,6 +141,11 @@ static hda_nid_t vt1709_adc_nids[3] = {
0x14, 0x15, 0x16
};
+static hda_nid_t vt1708B_adc_nids[2] = {
+ /* ADC1-2 */
+ 0x13, 0x14
+};
+
/* add dynamic controls */
static int via_add_control(struct via_spec *spec, int type, const char *name,
unsigned long val)
@@ -268,9 +283,13 @@ static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
0x18, &spec->cur_mux[adc_idx]);
else if ((IS_VT1709_10CH_VENDORID(vendor_id) ||
- IS_VT1709_6CH_VENDORID(vendor_id)) && (adc_idx == 0) )
+ IS_VT1709_6CH_VENDORID(vendor_id)) && adc_idx == 0)
return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
0x19, &spec->cur_mux[adc_idx]);
+ else if ((IS_VT1708B_8CH_VENDORID(vendor_id) ||
+ IS_VT1708B_4CH_VENDORID(vendor_id)) && adc_idx == 0)
+ return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
+ 0x17, &spec->cur_mux[adc_idx]);
else
return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
spec->adc_nids[adc_idx],
@@ -287,7 +306,6 @@ static struct snd_kcontrol_new vt1708_capture_mixer[] = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* The multiple "Capture Source" controls confuse alsamixer
* So call somewhat different..
- * FIXME: the controls appear in the "playback" view!
*/
/* .name = "Capture Source", */
.name = "Input Source",
@@ -309,15 +327,15 @@ static struct hda_verb vt1708_volume_init_verbs[] = {
{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+ /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
* mixer widget
*/
/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* master */
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
/*
* Set up output mixers (0x19 - 0x1b)
@@ -329,10 +347,9 @@ static struct hda_verb vt1708_volume_init_verbs[] = {
/* Setup default input to PW4 */
{0x20, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* Set mic as default input of sw0 */
- {0x18, AC_VERB_SET_CONNECT_SEL, 0x2},
/* PW9 Output enable */
{0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ { }
};
static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
@@ -544,6 +561,33 @@ static int via_init(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
snd_hda_sequence_write(codec, spec->init_verbs);
+ /* Lydia Add for EAPD enable */
+ if (!spec->dig_in_nid) { /* No Digital In connection */
+ if (IS_VT1708_VENDORID(codec->vendor_id)) {
+ snd_hda_codec_write(codec, VT1708_DIGIN_PIN, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ PIN_OUT);
+ snd_hda_codec_write(codec, VT1708_DIGIN_PIN, 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x02);
+ } else if (IS_VT1709_10CH_VENDORID(codec->vendor_id) ||
+ IS_VT1709_6CH_VENDORID(codec->vendor_id)) {
+ snd_hda_codec_write(codec, VT1709_DIGIN_PIN, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ PIN_OUT);
+ snd_hda_codec_write(codec, VT1709_DIGIN_PIN, 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x02);
+ } else if (IS_VT1708B_8CH_VENDORID(codec->vendor_id) ||
+ IS_VT1708B_4CH_VENDORID(codec->vendor_id)) {
+ snd_hda_codec_write(codec, VT1708B_DIGIN_PIN, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ PIN_OUT);
+ snd_hda_codec_write(codec, VT1708B_DIGIN_PIN, 0,
+ AC_VERB_SET_EAPD_BTLENABLE, 0x02);
+ }
+ } else /* enable SPDIF-input pin */
+ snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
+
return 0;
}
@@ -623,58 +667,68 @@ static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
if (i == AUTO_SEQ_CENLFE) {
/* Center/LFE */
err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
- "Center Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT));
+ "Center Playback Volume",
+ HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
"LFE Playback Volume",
- HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
"Center Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
"LFE Playback Switch",
- HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
} else if (i == AUTO_SEQ_FRONT){
/* add control to mixer index 0 */
err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
"Master Front Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_INPUT));
+ HDA_COMPOSE_AMP_VAL(0x17, 3, 0,
+ HDA_INPUT));
if (err < 0)
return err;
err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
"Master Front Playback Switch",
- HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_INPUT));
+ HDA_COMPOSE_AMP_VAL(0x17, 3, 0,
+ HDA_INPUT));
if (err < 0)
return err;
/* add control to PW3 */
sprintf(name, "%s Playback Volume", chname[i]);
err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
sprintf(name, "%s Playback Switch", chname[i]);
err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
} else {
sprintf(name, "%s Playback Volume", chname[i]);
err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
sprintf(name, "%s Playback Switch", chname[i]);
err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
}
@@ -875,7 +929,6 @@ static struct snd_kcontrol_new vt1709_capture_mixer[] = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* The multiple "Capture Source" controls confuse alsamixer
* So call somewhat different..
- * FIXME: the controls appear in the "playback" view!
*/
/* .name = "Capture Source", */
.name = "Input Source",
@@ -899,15 +952,15 @@ static struct hda_verb vt1709_10ch_volume_init_verbs[] = {
{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+ /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
* mixer widget
*/
/* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* unmute master */
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
/*
* Set up output selector (0x1a, 0x1b, 0x29)
@@ -925,8 +978,6 @@ static struct hda_verb vt1709_10ch_volume_init_verbs[] = {
/* Set input of PW4 as AOW4 */
{0x20, AC_VERB_SET_CONNECT_SEL, 0x1},
- /* Set mic as default input of sw0 */
- {0x19, AC_VERB_SET_CONNECT_SEL, 0x2},
/* PW9 Output enable */
{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
{ }
@@ -1073,68 +1124,80 @@ static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec,
/* Center/LFE */
err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
"Center Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(0x1b, 1, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
"LFE Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(0x1b, 2, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
"Center Playback Switch",
- HDA_COMPOSE_AMP_VAL(0x1b, 1, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(0x1b, 1, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
"LFE Playback Switch",
- HDA_COMPOSE_AMP_VAL(0x1b, 2, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(0x1b, 2, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
} else if (i == AUTO_SEQ_FRONT){
/* add control to mixer index 0 */
err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
"Master Front Playback Volume",
- HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT));
+ HDA_COMPOSE_AMP_VAL(0x18, 3, 0,
+ HDA_INPUT));
if (err < 0)
return err;
err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
"Master Front Playback Switch",
- HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT));
+ HDA_COMPOSE_AMP_VAL(0x18, 3, 0,
+ HDA_INPUT));
if (err < 0)
return err;
/* add control to PW3 */
sprintf(name, "%s Playback Volume", chname[i]);
err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
sprintf(name, "%s Playback Switch", chname[i]);
err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
} else if (i == AUTO_SEQ_SURROUND) {
sprintf(name, "%s Playback Volume", chname[i]);
err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(0x29, 3, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(0x29, 3, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
sprintf(name, "%s Playback Switch", chname[i]);
err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(0x29, 3, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(0x29, 3, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
} else if (i == AUTO_SEQ_SIDE) {
sprintf(name, "%s Playback Volume", chname[i]);
err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
- HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(0x1a, 3, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
sprintf(name, "%s Playback Switch", chname[i]);
err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
- HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT));
+ HDA_COMPOSE_AMP_VAL(0x1a, 3, 0,
+ HDA_OUTPUT));
if (err < 0)
return err;
}
@@ -1351,8 +1414,6 @@ static struct hda_verb vt1709_6ch_volume_init_verbs[] = {
/* Set input of PW4 as MW0 */
{0x20, AC_VERB_SET_CONNECT_SEL, 0},
- /* Set mic as default input of sw0 */
- {0x19, AC_VERB_SET_CONNECT_SEL, 0x2},
/* PW9 Output enable */
{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
{ }
@@ -1403,6 +1464,494 @@ static int patch_vt1709_6ch(struct hda_codec *codec)
#ifdef CONFIG_SND_HDA_POWER_SAVE
spec->loopback.amplist = vt1709_loopbacks;
#endif
+ return 0;
+}
+
+/* capture mixer elements */
+static struct snd_kcontrol_new vt1708B_capture_mixer[] = {
+ HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ /* The multiple "Capture Source" controls confuse alsamixer
+ * So call somewhat different..
+ */
+ /* .name = "Capture Source", */
+ .name = "Input Source",
+ .count = 1,
+ .info = via_mux_enum_info,
+ .get = via_mux_enum_get,
+ .put = via_mux_enum_put,
+ },
+ { } /* end */
+};
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb vt1708B_8ch_volume_init_verbs[] = {
+ /*
+ * Unmute ADC0-1 and set the default input to mic-in
+ */
+ {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+
+ /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+ * mixer widget
+ */
+ /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+ /*
+ * Set up output mixers
+ */
+ /* set vol=0 to output mixers */
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+
+ /* Setup default input to PW4 */
+ {0x1d, AC_VERB_SET_CONNECT_SEL, 0x1},
+ /* PW9 Output enable */
+ {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ /* PW10 Input enable */
+ {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+ { }
+};
+
+static struct hda_verb vt1708B_4ch_volume_init_verbs[] = {
+ /*
+ * Unmute ADC0-1 and set the default input to mic-in
+ */
+ {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+
+ /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+ * mixer widget
+ */
+ /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+ /*
+ * Set up output mixers
+ */
+ /* set vol=0 to output mixers */
+ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+ {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+
+ /* Setup default input of PW4 to MW0 */
+ {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
+ /* PW9 Output enable */
+ {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+ /* PW10 Input enable */
+ {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+ { }
+};
+
+static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 8,
+ .nid = 0x10, /* NID to query formats and rates */
+ .ops = {
+ .open = via_playback_pcm_open,
+ .prepare = via_playback_pcm_prepare,
+ .cleanup = via_playback_pcm_cleanup
+ },
+};
+
+static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 4,
+ .nid = 0x10, /* NID to query formats and rates */
+ .ops = {
+ .open = via_playback_pcm_open,
+ .prepare = via_playback_pcm_prepare,
+ .cleanup = via_playback_pcm_cleanup
+ },
+};
+
+static struct hda_pcm_stream vt1708B_pcm_analog_capture = {
+ .substreams = 2,
+ .channels_min = 2,
+ .channels_max = 2,
+ .nid = 0x13, /* NID to query formats and rates */
+ .ops = {
+ .prepare = via_capture_pcm_prepare,
+ .cleanup = via_capture_pcm_cleanup
+ },
+};
+
+static struct hda_pcm_stream vt1708B_pcm_digital_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ /* NID is set in via_build_pcms */
+ .ops = {
+ .open = via_dig_playback_pcm_open,
+ .close = via_dig_playback_pcm_close,
+ .prepare = via_dig_playback_pcm_prepare
+ },
+};
+
+static struct hda_pcm_stream vt1708B_pcm_digital_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+};
+
+/* fill in the dac_nids table from the parsed pin configuration */
+static int vt1708B_auto_fill_dac_nids(struct via_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ int i;
+ hda_nid_t nid;
+
+ spec->multiout.num_dacs = cfg->line_outs;
+
+ spec->multiout.dac_nids = spec->private_dac_nids;
+
+ for (i = 0; i < 4; i++) {
+ nid = cfg->line_out_pins[i];
+ if (nid) {
+ /* config dac list */
+ switch (i) {
+ case AUTO_SEQ_FRONT:
+ spec->multiout.dac_nids[i] = 0x10;
+ break;
+ case AUTO_SEQ_CENLFE:
+ spec->multiout.dac_nids[i] = 0x24;
+ break;
+ case AUTO_SEQ_SURROUND:
+ spec->multiout.dac_nids[i] = 0x25;
+ break;
+ case AUTO_SEQ_SIDE:
+ spec->multiout.dac_nids[i] = 0x11;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ char name[32];
+ static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
+ hda_nid_t nid_vols[] = {0x16, 0x27, 0x26, 0x18};
+ hda_nid_t nid, nid_vol = 0;
+ int i, err;
+
+ for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
+ nid = cfg->line_out_pins[i];
+
+ if (!nid)
+ continue;
+
+ nid_vol = nid_vols[i];
+
+ if (i == AUTO_SEQ_CENLFE) {
+ /* Center/LFE */
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ "Center Playback Volume",
+ HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ "LFE Playback Volume",
+ HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+ "Center Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+ "LFE Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ } else if (i == AUTO_SEQ_FRONT) {
+ /* add control to mixer index 0 */
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ "Master Front Playback Volume",
+ HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
+ HDA_INPUT));
+ if (err < 0)
+ return err;
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+ "Master Front Playback Switch",
+ HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
+ HDA_INPUT));
+ if (err < 0)
+ return err;
+
+ /* add control to PW3 */
+ sprintf(name, "%s Playback Volume", chname[i]);
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ sprintf(name, "%s Playback Switch", chname[i]);
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
+ HDA_COMPOSE_AMP_VAL(nid, 3, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ } else {
+ sprintf(name, "%s Playback Volume", chname[i]);
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
+ HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ sprintf(name, "%s Playback Switch", chname[i]);
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
+ HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
+ HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
+{
+ int err;
+
+ if (!pin)
+ return 0;
+
+ spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */
+
+ err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
+ "Headphone Playback Volume",
+ HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+ err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
+ "Headphone Playback Switch",
+ HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+/* create playback/capture controls for input pins */
+static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec,
+ const struct auto_pin_cfg *cfg)
+{
+ static char *labels[] = {
+ "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
+ };
+ struct hda_input_mux *imux = &spec->private_imux;
+ int i, err, idx = 0;
+
+ /* for internal loopback recording select */
+ imux->items[imux->num_items].label = "Stereo Mixer";
+ imux->items[imux->num_items].index = idx;
+ imux->num_items++;
+
+ for (i = 0; i < AUTO_PIN_LAST; i++) {
+ if (!cfg->input_pins[i])
+ continue;
+
+ switch (cfg->input_pins[i]) {
+ case 0x1a: /* Mic */
+ idx = 2;
+ break;
+
+ case 0x1b: /* Line In */
+ idx = 3;
+ break;
+
+ case 0x1e: /* Front Mic */
+ idx = 4;
+ break;
+
+ case 0x1f: /* CD */
+ idx = 1;
+ break;
+ }
+ err = via_new_analog_input(spec, cfg->input_pins[i], labels[i],
+ idx, 0x16);
+ if (err < 0)
+ return err;
+ imux->items[imux->num_items].label = labels[i];
+ imux->items[imux->num_items].index = idx;
+ imux->num_items++;
+ }
+ return 0;
+}
+
+static int vt1708B_parse_auto_config(struct hda_codec *codec)
+{
+ struct via_spec *spec = codec->spec;
+ int err;
+
+ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
+ if (err < 0)
+ return err;
+ err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+ if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
+ return 0; /* can't find valid BIOS pin config */
+
+ err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+ err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
+ if (err < 0)
+ return err;
+ err = vt1708B_auto_create_analog_input_ctls(spec, &spec->autocfg);
+ if (err < 0)
+ return err;
+
+ spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+
+ if (spec->autocfg.dig_out_pin)
+ spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID;
+ if (spec->autocfg.dig_in_pin)
+ spec->dig_in_nid = VT1708B_DIGIN_NID;
+
+ if (spec->kctl_alloc)
+ spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+
+ spec->input_mux = &spec->private_imux;
+
+ return 1;
+}
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static struct hda_amp_list vt1708B_loopbacks[] = {
+ { 0x16, HDA_INPUT, 1 },
+ { 0x16, HDA_INPUT, 2 },
+ { 0x16, HDA_INPUT, 3 },
+ { 0x16, HDA_INPUT, 4 },
+ { } /* end */
+};
+#endif
+
+static int patch_vt1708B_8ch(struct hda_codec *codec)
+{
+ struct via_spec *spec;
+ int err;
+
+ /* create a codec specific record */
+ spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ /* automatic parse from the BIOS config */
+ err = vt1708B_parse_auto_config(codec);
+ if (err < 0) {
+ via_free(codec);
+ return err;
+ } else if (!err) {
+ printk(KERN_INFO "hda_codec: Cannot set up configuration "
+ "from BIOS. Using genenic mode...\n");
+ }
+
+ spec->init_verbs = vt1708B_8ch_volume_init_verbs;
+
+ spec->stream_name_analog = "VT1708B Analog";
+ spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback;
+ spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
+
+ spec->stream_name_digital = "VT1708B Digital";
+ spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
+ spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
+
+ if (!spec->adc_nids && spec->input_mux) {
+ spec->adc_nids = vt1708B_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
+ spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
+ spec->num_mixers++;
+ }
+
+ codec->patch_ops = via_patch_ops;
+
+ codec->patch_ops.init = via_auto_init;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ spec->loopback.amplist = vt1708B_loopbacks;
+#endif
+
+ return 0;
+}
+
+static int patch_vt1708B_4ch(struct hda_codec *codec)
+{
+ struct via_spec *spec;
+ int err;
+
+ /* create a codec specific record */
+ spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+
+ /* automatic parse from the BIOS config */
+ err = vt1708B_parse_auto_config(codec);
+ if (err < 0) {
+ via_free(codec);
+ return err;
+ } else if (!err) {
+ printk(KERN_INFO "hda_codec: Cannot set up configuration "
+ "from BIOS. Using genenic mode...\n");
+ }
+
+ spec->init_verbs = vt1708B_4ch_volume_init_verbs;
+
+ spec->stream_name_analog = "VT1708B Analog";
+ spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback;
+ spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
+
+ spec->stream_name_digital = "VT1708B Digital";
+ spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
+ spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
+
+ if (!spec->adc_nids && spec->input_mux) {
+ spec->adc_nids = vt1708B_adc_nids;
+ spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
+ spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
+ spec->num_mixers++;
+ }
+
+ codec->patch_ops = via_patch_ops;
+
+ codec->patch_ops.init = via_auto_init;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+ spec->loopback.amplist = vt1708B_loopbacks;
+#endif
return 0;
}
@@ -1415,13 +1964,37 @@ struct hda_codec_preset snd_hda_preset_via[] = {
{ .id = 0x11061709, .name = "VIA VT1708", .patch = patch_vt1708},
{ .id = 0x1106170A, .name = "VIA VT1708", .patch = patch_vt1708},
{ .id = 0x1106170B, .name = "VIA VT1708", .patch = patch_vt1708},
- { .id = 0x1106E710, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch},
- { .id = 0x1106E711, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch},
- { .id = 0x1106E712, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch},
- { .id = 0x1106E713, .name = "VIA VT1709 10-Ch", .patch = patch_vt1709_10ch},
- { .id = 0x1106E714, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch},
- { .id = 0x1106E715, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch},
- { .id = 0x1106E716, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch},
- { .id = 0x1106E717, .name = "VIA VT1709 6-Ch", .patch = patch_vt1709_6ch},
+ { .id = 0x1106E710, .name = "VIA VT1709 10-Ch",
+ .patch = patch_vt1709_10ch},
+ { .id = 0x1106E711, .name = "VIA VT1709 10-Ch",
+ .patch = patch_vt1709_10ch},
+ { .id = 0x1106E712, .name = "VIA VT1709 10-Ch",
+ .patch = patch_vt1709_10ch},
+ { .id = 0x1106E713, .name = "VIA VT1709 10-Ch",
+ .patch = patch_vt1709_10ch},
+ { .id = 0x1106E714, .name = "VIA VT1709 6-Ch",
+ .patch = patch_vt1709_6ch},
+ { .id = 0x1106E715, .name = "VIA VT1709 6-Ch",
+ .patch = patch_vt1709_6ch},
+ { .id = 0x1106E716, .name = "VIA VT1709 6-Ch",
+ .patch = patch_vt1709_6ch},
+ { .id = 0x1106E717, .name = "VIA VT1709 6-Ch",
+ .patch = patch_vt1709_6ch},
+ { .id = 0x1106E720, .name = "VIA VT1708B 8-Ch",
+ .patch = patch_vt1708B_8ch},
+ { .id = 0x1106E721, .name = "VIA VT1708B 8-Ch",
+ .patch = patch_vt1708B_8ch},
+ { .id = 0x1106E722, .name = "VIA VT1708B 8-Ch",
+ .patch = patch_vt1708B_8ch},
+ { .id = 0x1106E723, .name = "VIA VT1708B 8-Ch",
+ .patch = patch_vt1708B_8ch},
+ { .id = 0x1106E724, .name = "VIA VT1708B 4-Ch",
+ .patch = patch_vt1708B_4ch},
+ { .id = 0x1106E725, .name = "VIA VT1708B 4-Ch",
+ .patch = patch_vt1708B_4ch},
+ { .id = 0x1106E726, .name = "VIA VT1708B 4-Ch",
+ .patch = patch_vt1708B_4ch},
+ { .id = 0x1106E727, .name = "VIA VT1708B 4-Ch",
+ .patch = patch_vt1708B_4ch},
{} /* terminator */
};
diff --git a/sound/pci/hda/vmaster.c b/sound/pci/hda/vmaster.c
new file mode 100644
index 0000000..2da49d2
--- /dev/null
+++ b/sound/pci/hda/vmaster.c
@@ -0,0 +1,364 @@
+/*
+ * Virtual master and slave controls
+ *
+ * Copyright (c) 2008 by Takashi Iwai <tiwai@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/control.h>
+
+/*
+ * a subset of information returned via ctl info callback
+ */
+struct link_ctl_info {
+ int type; /* value type */
+ int count; /* item count */
+ int min_val, max_val; /* min, max values */
+};
+
+/*
+ * link master - this contains a list of slave controls that are
+ * identical types, i.e. info returns the same value type and value
+ * ranges, but may have different number of counts.
+ *
+ * The master control is so far only mono volume/switch for simplicity.
+ * The same value will be applied to all slaves.
+ */
+struct link_master {
+ struct list_head slaves;
+ struct link_ctl_info info;
+ int val; /* the master value */
+};
+
+/*
+ * link slave - this contains a slave control element
+ *
+ * It fakes the control callbacsk with additional attenuation by the
+ * master control. A slave may have either one or two channels.
+ */
+
+struct link_slave {
+ struct list_head list;
+ struct link_master *master;
+ struct link_ctl_info info;
+ int vals[2]; /* current values */
+ struct snd_kcontrol slave; /* the copy of original control entry */
+};
+
+/* get the slave ctl info and save the initial values */
+static int slave_init(struct link_slave *slave)
+{
+ struct snd_ctl_elem_info *uinfo;
+ struct snd_ctl_elem_value *uctl;
+ int err, ch;
+
+ if (slave->info.count)
+ return 0; /* already initialized */
+
+ uinfo = kmalloc(sizeof(*uinfo), GFP_KERNEL);
+ if (!uinfo)
+ return -ENOMEM;
+ uinfo->id = slave->slave.id;
+ err = slave->slave.info(&slave->slave, uinfo);
+ if (err < 0) {
+ kfree(uinfo);
+ return err;
+ }
+ slave->info.type = uinfo->type;
+ slave->info.count = uinfo->count;
+ if (slave->info.count > 2 ||
+ (slave->info.type != SNDRV_CTL_ELEM_TYPE_INTEGER &&
+ slave->info.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN)) {
+ snd_printk(KERN_ERR "invalid slave element\n");
+ kfree(uinfo);
+ return -EINVAL;
+ }
+ slave->info.min_val = uinfo->value.integer.min;
+ slave->info.max_val = uinfo->value.integer.max;
+ kfree(uinfo);
+
+ uctl = kmalloc(sizeof(*uctl), GFP_KERNEL);
+ if (!uctl)
+ return -ENOMEM;
+ uctl->id = slave->slave.id;
+ err = slave->slave.get(&slave->slave, uctl);
+ for (ch = 0; ch < slave->info.count; ch++)
+ slave->vals[ch] = uctl->value.integer.value[ch];
+ kfree(uctl);
+ return 0;
+}
+
+/* initialize master volume */
+static int master_init(struct link_master *master)
+{
+ struct link_slave *slave;
+
+ if (master->info.count)
+ return 0; /* already initialized */
+
+ list_for_each_entry(slave, &master->slaves, list) {
+ int err = slave_init(slave);
+ if (err < 0)
+ return err;
+ master->info = slave->info;
+ master->info.count = 1; /* always mono */
+ /* set full volume as default (= no attenuation) */
+ master->val = master->info.max_val;
+ return 0;
+ }
+ return -ENOENT;
+}
+
+static int slave_get_val(struct link_slave *slave,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int err, ch;
+
+ err = slave_init(slave);
+ if (err < 0)
+ return err;
+ for (ch = 0; ch < slave->info.count; ch++)
+ ucontrol->value.integer.value[ch] = slave->vals[ch];
+ return 0;
+}
+
+static int slave_put_val(struct link_slave *slave,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int err, ch, vol;
+
+ err = master_init(slave->master);
+ if (err < 0)
+ return err;
+
+ switch (slave->info.type) {
+ case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
+ for (ch = 0; ch < slave->info.count; ch++)
+ ucontrol->value.integer.value[ch] &=
+ !!slave->master->val;
+ break;
+ case SNDRV_CTL_ELEM_TYPE_INTEGER:
+ for (ch = 0; ch < slave->info.count; ch++) {
+ /* max master volume is supposed to be 0 dB */
+ vol = ucontrol->value.integer.value[ch];
+ vol += slave->master->val - slave->master->info.max_val;
+ if (vol < slave->info.min_val)
+ vol = slave->info.min_val;
+ else if (vol > slave->info.max_val)
+ vol = slave->info.max_val;
+ ucontrol->value.integer.value[ch] = vol;
+ }
+ break;
+ }
+ return slave->slave.put(&slave->slave, ucontrol);
+}
+
+/*
+ * ctl callbacks for slaves
+ */
+static int slave_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct link_slave *slave = snd_kcontrol_chip(kcontrol);
+ return slave->slave.info(&slave->slave, uinfo);
+}
+
+static int slave_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct link_slave *slave = snd_kcontrol_chip(kcontrol);
+ return slave_get_val(slave, ucontrol);
+}
+
+static int slave_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct link_slave *slave = snd_kcontrol_chip(kcontrol);
+ int err, ch, changed = 0;
+
+ err = slave_init(slave);
+ if (err < 0)
+ return err;
+ for (ch = 0; ch < slave->info.count; ch++) {
+ if (slave->vals[ch] != ucontrol->value.integer.value[ch]) {
+ changed = 1;
+ slave->vals[ch] = ucontrol->value.integer.value[ch];
+ }
+ }
+ if (!changed)
+ return 0;
+ return slave_put_val(slave, ucontrol);
+}
+
+static int slave_tlv_cmd(struct snd_kcontrol *kcontrol,
+ int op_flag, unsigned int size,
+ unsigned int __user *tlv)
+{
+ struct link_slave *slave = snd_kcontrol_chip(kcontrol);
+ /* FIXME: this assumes that the max volume is 0 dB */
+ return slave->slave.tlv.c(&slave->slave, op_flag, size, tlv);
+}
+
+static void slave_free(struct snd_kcontrol *kcontrol)
+{
+ struct link_slave *slave = snd_kcontrol_chip(kcontrol);
+ if (slave->slave.private_free)
+ slave->slave.private_free(&slave->slave);
+ if (slave->master)
+ list_del(&slave->list);
+ kfree(slave);
+}
+
+/*
+ * Add a slave control to the group with the given master control
+ *
+ * All slaves must be the same type (returning the same information
+ * via info callback). The fucntion doesn't check it, so it's your
+ * responsibility.
+ *
+ * Also, some additional limitations:
+ * - at most two channels
+ * - logarithmic volume control (dB level), no linear volume
+ * - master can only attenuate the volume, no gain
+ */
+int snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave)
+{
+ struct link_master *master_link = snd_kcontrol_chip(master);
+ struct link_slave *srec;
+
+ srec = kzalloc(sizeof(*srec) +
+ slave->count * sizeof(*slave->vd), GFP_KERNEL);
+ if (!srec)
+ return -ENOMEM;
+ srec->slave = *slave;
+ memcpy(srec->slave.vd, slave->vd, slave->count * sizeof(*slave->vd));
+ srec->master = master_link;
+
+ /* override callbacks */
+ slave->info = slave_info;
+ slave->get = slave_get;
+ slave->put = slave_put;
+ if (slave->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)
+ slave->tlv.c = slave_tlv_cmd;
+ slave->private_data = srec;
+ slave->private_free = slave_free;
+
+ list_add_tail(&srec->list, &master_link->slaves);
+ return 0;
+}
+
+/*
+ * ctl callbacks for master controls
+ */
+static int master_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct link_master *master = snd_kcontrol_chip(kcontrol);
+ int ret;
+
+ ret = master_init(master);
+ if (ret < 0)
+ return ret;
+ uinfo->type = master->info.type;
+ uinfo->count = master->info.count;
+ uinfo->value.integer.min = master->info.min_val;
+ uinfo->value.integer.max = master->info.max_val;
+ return 0;
+}
+
+static int master_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct link_master *master = snd_kcontrol_chip(kcontrol);
+ int err = master_init(master);
+ if (err < 0)
+ return err;
+ ucontrol->value.integer.value[0] = master->val;
+ return 0;
+}
+
+static int master_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct link_master *master = snd_kcontrol_chip(kcontrol);
+ struct link_slave *slave;
+ struct snd_ctl_elem_value *uval;
+ int err, old_val;
+
+ err = master_init(master);
+ if (err < 0)
+ return err;
+ old_val = master->val;
+ if (ucontrol->value.integer.value[0] == old_val)
+ return 0;
+
+ uval = kmalloc(sizeof(*uval), GFP_KERNEL);
+ if (!uval)
+ return -ENOMEM;
+ list_for_each_entry(slave, &master->slaves, list) {
+ master->val = old_val;
+ uval->id = slave->slave.id;
+ slave_get_val(slave, uval);
+ master->val = ucontrol->value.integer.value[0];
+ slave_put_val(slave, uval);
+ }
+ kfree(uval);
+ return 1;
+}
+
+static void master_free(struct snd_kcontrol *kcontrol)
+{
+ struct link_master *master = snd_kcontrol_chip(kcontrol);
+ struct link_slave *slave;
+
+ list_for_each_entry(slave, &master->slaves, list)
+ slave->master = NULL;
+ kfree(master);
+}
+
+
+/*
+ * Create a virtual master control with the given name
+ */
+struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
+ const unsigned int *tlv)
+{
+ struct link_master *master;
+ struct snd_kcontrol *kctl;
+ struct snd_kcontrol_new knew;
+
+ memset(&knew, 0, sizeof(knew));
+ knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ knew.name = name;
+ knew.info = master_info;
+
+ master = kzalloc(sizeof(*master), GFP_KERNEL);
+ if (!master)
+ return NULL;
+ INIT_LIST_HEAD(&master->slaves);
+
+ kctl = snd_ctl_new1(&knew, master);
+ if (!kctl) {
+ kfree(master);
+ return NULL;
+ }
+ /* override some callbacks */
+ kctl->info = master_info;
+ kctl->get = master_get;
+ kctl->put = master_put;
+ kctl->private_free = master_free;
+
+ /* additional (constant) TLV read */
+ if (tlv) {
+ /* FIXME: this assumes that the max volume is 0 dB */
+ kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ kctl->tlv.p = tlv;
+ }
+ return kctl;
+}