Merge branch 'topic/via-cleanup' into topic/hda
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index 0ea5cc6..85217bd 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -171,6 +171,19 @@
snd-hda-codec-ca0110.
This module is automatically loaded at probing.
+config SND_HDA_CODEC_CA0132
+ bool "Build Creative CA0132 codec support"
+ depends on SND_HDA_INTEL
+ default y
+ help
+ Say Y here to include Creative CA0132 codec support in
+ snd-hda-intel driver.
+
+ When the HD-audio driver is built as a module, the codec
+ support code is also built as another module,
+ snd-hda-codec-ca0132.
+ This module is automatically loaded at probing.
+
config SND_HDA_CODEC_CMEDIA
bool "Build C-Media HD-audio codec support"
default y
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index 17ef365..87365d5 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -13,6 +13,7 @@
snd-hda-codec-si3054-objs := patch_si3054.o
snd-hda-codec-cirrus-objs := patch_cirrus.o
snd-hda-codec-ca0110-objs := patch_ca0110.o
+snd-hda-codec-ca0132-objs := patch_ca0132.o
snd-hda-codec-conexant-objs := patch_conexant.o
snd-hda-codec-via-objs := patch_via.o
snd-hda-codec-hdmi-objs := patch_hdmi.o hda_eld.o
@@ -42,6 +43,9 @@
ifdef CONFIG_SND_HDA_CODEC_CA0110
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-ca0110.o
endif
+ifdef CONFIG_SND_HDA_CODEC_CA0132
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-ca0132.o
+endif
ifdef CONFIG_SND_HDA_CODEC_CONEXANT
obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-conexant.o
endif
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index a2388fc..26c420d 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -311,35 +311,35 @@
static int _hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
hda_nid_t *conn_list, int max_conns);
static bool add_conn_list(struct snd_array *array, hda_nid_t nid);
-static int copy_conn_list(hda_nid_t nid, hda_nid_t *dst, int max_dst,
- hda_nid_t *src, int len);
/**
* snd_hda_get_connections - get connection list
* @codec: the HDA codec
* @nid: NID to parse
- * @conn_list: connection list array
- * @max_conns: max. number of connections to store
+ * @listp: the pointer to store NID list
*
* Parses the connection list of the given widget and stores the list
* of NIDs.
*
* Returns the number of connections, or a negative error code.
*/
-int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
- hda_nid_t *conn_list, int max_conns)
+int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
+ const hda_nid_t **listp)
{
struct snd_array *array = &codec->conn_lists;
int i, len, old_used;
hda_nid_t list[HDA_MAX_CONNECTIONS];
+ hda_nid_t *p;
/* look up the cached results */
for (i = 0; i < array->used; ) {
- hda_nid_t *p = snd_array_elem(array, i);
+ p = snd_array_elem(array, i);
len = p[1];
- if (nid == *p)
- return copy_conn_list(nid, conn_list, max_conns,
- p + 2, len);
+ if (nid == *p) {
+ if (listp)
+ *listp = p + 2;
+ return len;
+ }
i += len + 2;
}
@@ -355,12 +355,46 @@
if (!add_conn_list(array, list[i]))
goto error_add;
- return copy_conn_list(nid, conn_list, max_conns, list, len);
+ p = snd_array_elem(array, old_used);
+ if (listp)
+ *listp = p + 2;
+ return len;
error_add:
array->used = old_used;
return -ENOMEM;
}
+EXPORT_SYMBOL_HDA(snd_hda_get_conn_list);
+
+/**
+ * snd_hda_get_connections - copy connection list
+ * @codec: the HDA codec
+ * @nid: NID to parse
+ * @conn_list: connection list array
+ * @max_conns: max. number of connections to store
+ *
+ * Parses the connection list of the given widget and stores the list
+ * of NIDs.
+ *
+ * Returns the number of connections, or a negative error code.
+ */
+int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
+ hda_nid_t *conn_list, int max_conns)
+{
+ const hda_nid_t *list;
+ int len = snd_hda_get_conn_list(codec, nid, &list);
+
+ if (len <= 0)
+ return len;
+ if (len > max_conns) {
+ snd_printk(KERN_ERR "hda_codec: "
+ "Too many connections %d for NID 0x%x\n",
+ len, nid);
+ return -EINVAL;
+ }
+ memcpy(conn_list, list, len * sizeof(hda_nid_t));
+ return len;
+}
EXPORT_SYMBOL_HDA(snd_hda_get_connections);
static int _hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
@@ -471,19 +505,6 @@
return true;
}
-static int copy_conn_list(hda_nid_t nid, hda_nid_t *dst, int max_dst,
- hda_nid_t *src, int len)
-{
- if (len > max_dst) {
- snd_printk(KERN_ERR "hda_codec: "
- "Too many connections %d for NID 0x%x\n",
- len, nid);
- return -EINVAL;
- }
- memcpy(dst, src, len * sizeof(hda_nid_t));
- return len;
-}
-
/**
* snd_hda_queue_unsol_event - add an unsolicited event to queue
* @bus: the BUS
@@ -4590,7 +4611,7 @@
unsigned int wid_caps = get_wcaps(codec, nid);
unsigned int wid_type = get_wcaps_type(wid_caps);
unsigned int def_conf;
- short assoc, loc;
+ short assoc, loc, conn, dev;
/* read all default configuration for pin complex */
if (wid_type != AC_WID_PIN)
@@ -4600,10 +4621,19 @@
continue;
def_conf = snd_hda_codec_get_pincfg(codec, nid);
- if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
+ conn = get_defcfg_connect(def_conf);
+ if (conn == AC_JACK_PORT_NONE)
continue;
loc = get_defcfg_location(def_conf);
- switch (get_defcfg_device(def_conf)) {
+ dev = get_defcfg_device(def_conf);
+
+ /* workaround for buggy BIOS setups */
+ if (dev == AC_JACK_LINE_OUT) {
+ if (conn == AC_JACK_PORT_FIXED)
+ dev = AC_JACK_SPEAKER;
+ }
+
+ switch (dev) {
case AC_JACK_LINE_OUT:
seq = get_defcfg_sequence(def_conf);
assoc = get_defcfg_association(def_conf);
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 070efac..c71cd7f 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -903,6 +903,8 @@
hda_nid_t *start_id);
int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
hda_nid_t *conn_list, int max_conns);
+int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
+ const hda_nid_t **listp);
int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
u32 *ratesp, u64 *formatsp, unsigned int *bpsp);
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
new file mode 100644
index 0000000..d9a2254
--- /dev/null
+++ b/sound/pci/hda/patch_ca0132.c
@@ -0,0 +1,1097 @@
+/*
+ * HD audio interface patch for Creative CA0132 chip
+ *
+ * Copyright (c) 2011, Creative Technology Ltd.
+ *
+ * Based on patch_ca0110.c
+ * Copyright (c) 2008 Takashi Iwai <tiwai@suse.de>
+ *
+ * This driver 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/mutex.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+#define WIDGET_CHIP_CTRL 0x15
+#define WIDGET_DSP_CTRL 0x16
+
+#define WUH_MEM_CONNID 10
+#define DSP_MEM_CONNID 16
+
+enum hda_cmd_vendor_io {
+ /* for DspIO node */
+ VENDOR_DSPIO_SCP_WRITE_DATA_LOW = 0x000,
+ VENDOR_DSPIO_SCP_WRITE_DATA_HIGH = 0x100,
+
+ VENDOR_DSPIO_STATUS = 0xF01,
+ VENDOR_DSPIO_SCP_POST_READ_DATA = 0x702,
+ VENDOR_DSPIO_SCP_READ_DATA = 0xF02,
+ VENDOR_DSPIO_DSP_INIT = 0x703,
+ VENDOR_DSPIO_SCP_POST_COUNT_QUERY = 0x704,
+ VENDOR_DSPIO_SCP_READ_COUNT = 0xF04,
+
+ /* for ChipIO node */
+ VENDOR_CHIPIO_ADDRESS_LOW = 0x000,
+ VENDOR_CHIPIO_ADDRESS_HIGH = 0x100,
+ VENDOR_CHIPIO_STREAM_FORMAT = 0x200,
+ VENDOR_CHIPIO_DATA_LOW = 0x300,
+ VENDOR_CHIPIO_DATA_HIGH = 0x400,
+
+ VENDOR_CHIPIO_GET_PARAMETER = 0xF00,
+ VENDOR_CHIPIO_STATUS = 0xF01,
+ VENDOR_CHIPIO_HIC_POST_READ = 0x702,
+ VENDOR_CHIPIO_HIC_READ_DATA = 0xF03,
+
+ VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE = 0x70A,
+
+ VENDOR_CHIPIO_PLL_PMU_WRITE = 0x70C,
+ VENDOR_CHIPIO_PLL_PMU_READ = 0xF0C,
+ VENDOR_CHIPIO_8051_ADDRESS_LOW = 0x70D,
+ VENDOR_CHIPIO_8051_ADDRESS_HIGH = 0x70E,
+ VENDOR_CHIPIO_FLAG_SET = 0x70F,
+ VENDOR_CHIPIO_FLAGS_GET = 0xF0F,
+ VENDOR_CHIPIO_PARAMETER_SET = 0x710,
+ VENDOR_CHIPIO_PARAMETER_GET = 0xF10,
+
+ VENDOR_CHIPIO_PORT_ALLOC_CONFIG_SET = 0x711,
+ VENDOR_CHIPIO_PORT_ALLOC_SET = 0x712,
+ VENDOR_CHIPIO_PORT_ALLOC_GET = 0xF12,
+ VENDOR_CHIPIO_PORT_FREE_SET = 0x713,
+
+ VENDOR_CHIPIO_PARAMETER_EX_ID_GET = 0xF17,
+ VENDOR_CHIPIO_PARAMETER_EX_ID_SET = 0x717,
+ VENDOR_CHIPIO_PARAMETER_EX_VALUE_GET = 0xF18,
+ VENDOR_CHIPIO_PARAMETER_EX_VALUE_SET = 0x718
+};
+
+/*
+ * Control flag IDs
+ */
+enum control_flag_id {
+ /* Connection manager stream setup is bypassed/enabled */
+ CONTROL_FLAG_C_MGR = 0,
+ /* DSP DMA is bypassed/enabled */
+ CONTROL_FLAG_DMA = 1,
+ /* 8051 'idle' mode is disabled/enabled */
+ CONTROL_FLAG_IDLE_ENABLE = 2,
+ /* Tracker for the SPDIF-in path is bypassed/enabled */
+ CONTROL_FLAG_TRACKER = 3,
+ /* DigitalOut to Spdif2Out connection is disabled/enabled */
+ CONTROL_FLAG_SPDIF2OUT = 4,
+ /* Digital Microphone is disabled/enabled */
+ CONTROL_FLAG_DMIC = 5,
+ /* ADC_B rate is 48 kHz/96 kHz */
+ CONTROL_FLAG_ADC_B_96KHZ = 6,
+ /* ADC_C rate is 48 kHz/96 kHz */
+ CONTROL_FLAG_ADC_C_96KHZ = 7,
+ /* DAC rate is 48 kHz/96 kHz (affects all DACs) */
+ CONTROL_FLAG_DAC_96KHZ = 8,
+ /* DSP rate is 48 kHz/96 kHz */
+ CONTROL_FLAG_DSP_96KHZ = 9,
+ /* SRC clock is 98 MHz/196 MHz (196 MHz forces rate to 96 KHz) */
+ CONTROL_FLAG_SRC_CLOCK_196MHZ = 10,
+ /* SRC rate is 48 kHz/96 kHz (48 kHz disabled when clock is 196 MHz) */
+ CONTROL_FLAG_SRC_RATE_96KHZ = 11,
+ /* Decode Loop (DSP->SRC->DSP) is disabled/enabled */
+ CONTROL_FLAG_DECODE_LOOP = 12,
+ /* De-emphasis filter on DAC-1 disabled/enabled */
+ CONTROL_FLAG_DAC1_DEEMPHASIS = 13,
+ /* De-emphasis filter on DAC-2 disabled/enabled */
+ CONTROL_FLAG_DAC2_DEEMPHASIS = 14,
+ /* De-emphasis filter on DAC-3 disabled/enabled */
+ CONTROL_FLAG_DAC3_DEEMPHASIS = 15,
+ /* High-pass filter on ADC_B disabled/enabled */
+ CONTROL_FLAG_ADC_B_HIGH_PASS = 16,
+ /* High-pass filter on ADC_C disabled/enabled */
+ CONTROL_FLAG_ADC_C_HIGH_PASS = 17,
+ /* Common mode on Port_A disabled/enabled */
+ CONTROL_FLAG_PORT_A_COMMON_MODE = 18,
+ /* Common mode on Port_D disabled/enabled */
+ CONTROL_FLAG_PORT_D_COMMON_MODE = 19,
+ /* Impedance for ramp generator on Port_A 16 Ohm/10K Ohm */
+ CONTROL_FLAG_PORT_A_10KOHM_LOAD = 20,
+ /* Impedance for ramp generator on Port_D, 16 Ohm/10K Ohm */
+ CONTROL_FLAG_PORT_D_10K0HM_LOAD = 21,
+ /* ASI rate is 48kHz/96kHz */
+ CONTROL_FLAG_ASI_96KHZ = 22,
+ /* DAC power settings able to control attached ports no/yes */
+ CONTROL_FLAG_DACS_CONTROL_PORTS = 23,
+ /* Clock Stop OK reporting is disabled/enabled */
+ CONTROL_FLAG_CONTROL_STOP_OK_ENABLE = 24,
+ /* Number of control flags */
+ CONTROL_FLAGS_MAX = (CONTROL_FLAG_CONTROL_STOP_OK_ENABLE+1)
+};
+
+/*
+ * Control parameter IDs
+ */
+enum control_parameter_id {
+ /* 0: force HDA, 1: allow DSP if HDA Spdif1Out stream is idle */
+ CONTROL_PARAM_SPDIF1_SOURCE = 2,
+
+ /* Stream Control */
+
+ /* Select stream with the given ID */
+ CONTROL_PARAM_STREAM_ID = 24,
+ /* Source connection point for the selected stream */
+ CONTROL_PARAM_STREAM_SOURCE_CONN_POINT = 25,
+ /* Destination connection point for the selected stream */
+ CONTROL_PARAM_STREAM_DEST_CONN_POINT = 26,
+ /* Number of audio channels in the selected stream */
+ CONTROL_PARAM_STREAMS_CHANNELS = 27,
+ /*Enable control for the selected stream */
+ CONTROL_PARAM_STREAM_CONTROL = 28,
+
+ /* Connection Point Control */
+
+ /* Select connection point with the given ID */
+ CONTROL_PARAM_CONN_POINT_ID = 29,
+ /* Connection point sample rate */
+ CONTROL_PARAM_CONN_POINT_SAMPLE_RATE = 30,
+
+ /* Node Control */
+
+ /* Select HDA node with the given ID */
+ CONTROL_PARAM_NODE_ID = 31
+};
+
+/*
+ * Dsp Io Status codes
+ */
+enum hda_vendor_status_dspio {
+ /* Success */
+ VENDOR_STATUS_DSPIO_OK = 0x00,
+ /* Busy, unable to accept new command, the host must retry */
+ VENDOR_STATUS_DSPIO_BUSY = 0x01,
+ /* SCP command queue is full */
+ VENDOR_STATUS_DSPIO_SCP_COMMAND_QUEUE_FULL = 0x02,
+ /* SCP response queue is empty */
+ VENDOR_STATUS_DSPIO_SCP_RESPONSE_QUEUE_EMPTY = 0x03
+};
+
+/*
+ * Chip Io Status codes
+ */
+enum hda_vendor_status_chipio {
+ /* Success */
+ VENDOR_STATUS_CHIPIO_OK = 0x00,
+ /* Busy, unable to accept new command, the host must retry */
+ VENDOR_STATUS_CHIPIO_BUSY = 0x01
+};
+
+/*
+ * CA0132 sample rate
+ */
+enum ca0132_sample_rate {
+ SR_6_000 = 0x00,
+ SR_8_000 = 0x01,
+ SR_9_600 = 0x02,
+ SR_11_025 = 0x03,
+ SR_16_000 = 0x04,
+ SR_22_050 = 0x05,
+ SR_24_000 = 0x06,
+ SR_32_000 = 0x07,
+ SR_44_100 = 0x08,
+ SR_48_000 = 0x09,
+ SR_88_200 = 0x0A,
+ SR_96_000 = 0x0B,
+ SR_144_000 = 0x0C,
+ SR_176_400 = 0x0D,
+ SR_192_000 = 0x0E,
+ SR_384_000 = 0x0F,
+
+ SR_COUNT = 0x10,
+
+ SR_RATE_UNKNOWN = 0x1F
+};
+
+/*
+ * Scp Helper function
+ */
+enum get_set {
+ IS_SET = 0,
+ IS_GET = 1,
+};
+
+/*
+ * Duplicated from ca0110 codec
+ */
+
+static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
+{
+ if (pin) {
+ snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
+ if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
+ snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_UNMUTE);
+ }
+ if (dac)
+ snd_hda_codec_write(codec, dac, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO);
+}
+
+static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
+{
+ if (pin) {
+ snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL,
+ PIN_VREF80);
+ if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
+ snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_UNMUTE(0));
+ }
+ if (adc)
+ snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_UNMUTE(0));
+}
+
+static char *dirstr[2] = { "Playback", "Capture" };
+
+static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
+ int chan, int dir)
+{
+ char namestr[44];
+ int type = dir ? HDA_INPUT : HDA_OUTPUT;
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type);
+ sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
+ return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
+ int chan, int dir)
+{
+ char namestr[44];
+ int type = dir ? HDA_INPUT : HDA_OUTPUT;
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type);
+ sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]);
+ return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+#define add_out_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 0)
+#define add_out_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 0)
+#define add_in_switch(codec, nid, pfx) _add_switch(codec, nid, pfx, 3, 1)
+#define add_in_volume(codec, nid, pfx) _add_volume(codec, nid, pfx, 3, 1)
+#define add_mono_switch(codec, nid, pfx, chan) \
+ _add_switch(codec, nid, pfx, chan, 0)
+#define add_mono_volume(codec, nid, pfx, chan) \
+ _add_volume(codec, nid, pfx, chan, 0)
+#define add_in_mono_switch(codec, nid, pfx, chan) \
+ _add_switch(codec, nid, pfx, chan, 1)
+#define add_in_mono_volume(codec, nid, pfx, chan) \
+ _add_volume(codec, nid, pfx, chan, 1)
+
+
+/*
+ * CA0132 specific
+ */
+
+struct ca0132_spec {
+ struct auto_pin_cfg autocfg;
+ struct hda_multi_out multiout;
+ hda_nid_t out_pins[AUTO_CFG_MAX_OUTS];
+ hda_nid_t dacs[AUTO_CFG_MAX_OUTS];
+ hda_nid_t hp_dac;
+ hda_nid_t input_pins[AUTO_PIN_LAST];
+ hda_nid_t adcs[AUTO_PIN_LAST];
+ hda_nid_t dig_out;
+ hda_nid_t dig_in;
+ unsigned int num_inputs;
+ long curr_hp_switch;
+ long curr_hp_volume[2];
+ long curr_speaker_switch;
+ struct mutex chipio_mutex;
+ const char *input_labels[AUTO_PIN_LAST];
+ struct hda_pcm pcm_rec[2]; /* PCM information */
+};
+
+/* Chip access helper function */
+static int chipio_send(struct hda_codec *codec,
+ unsigned int reg,
+ unsigned int data)
+{
+ unsigned int res;
+ int retry = 50;
+
+ /* send bits of data specified by reg */
+ do {
+ res = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0,
+ reg, data);
+ if (res == VENDOR_STATUS_CHIPIO_OK)
+ return 0;
+ } while (--retry);
+ return -EIO;
+}
+
+/*
+ * Write chip address through the vendor widget -- NOT protected by the Mutex!
+ */
+static int chipio_write_address(struct hda_codec *codec,
+ unsigned int chip_addx)
+{
+ int res;
+
+ /* send low 16 bits of the address */
+ res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_LOW,
+ chip_addx & 0xffff);
+
+ if (res != -EIO) {
+ /* send high 16 bits of the address */
+ res = chipio_send(codec, VENDOR_CHIPIO_ADDRESS_HIGH,
+ chip_addx >> 16);
+ }
+
+ return res;
+}
+
+/*
+ * Write data through the vendor widget -- NOT protected by the Mutex!
+ */
+
+static int chipio_write_data(struct hda_codec *codec, unsigned int data)
+{
+ int res;
+
+ /* send low 16 bits of the data */
+ res = chipio_send(codec, VENDOR_CHIPIO_DATA_LOW, data & 0xffff);
+
+ if (res != -EIO) {
+ /* send high 16 bits of the data */
+ res = chipio_send(codec, VENDOR_CHIPIO_DATA_HIGH,
+ data >> 16);
+ }
+
+ return res;
+}
+
+/*
+ * Read data through the vendor widget -- NOT protected by the Mutex!
+ */
+static int chipio_read_data(struct hda_codec *codec, unsigned int *data)
+{
+ int res;
+
+ /* post read */
+ res = chipio_send(codec, VENDOR_CHIPIO_HIC_POST_READ, 0);
+
+ if (res != -EIO) {
+ /* read status */
+ res = chipio_send(codec, VENDOR_CHIPIO_STATUS, 0);
+ }
+
+ if (res != -EIO) {
+ /* read data */
+ *data = snd_hda_codec_read(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_HIC_READ_DATA,
+ 0);
+ }
+
+ return res;
+}
+
+/*
+ * Write given value to the given address through the chip I/O widget.
+ * protected by the Mutex
+ */
+static int chipio_write(struct hda_codec *codec,
+ unsigned int chip_addx, const unsigned int data)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int err;
+
+ mutex_lock(&spec->chipio_mutex);
+
+ /* write the address, and if successful proceed to write data */
+ err = chipio_write_address(codec, chip_addx);
+ if (err < 0)
+ goto exit;
+
+ err = chipio_write_data(codec, data);
+ if (err < 0)
+ goto exit;
+
+exit:
+ mutex_unlock(&spec->chipio_mutex);
+ return err;
+}
+
+/*
+ * Read the given address through the chip I/O widget
+ * protected by the Mutex
+ */
+static int chipio_read(struct hda_codec *codec,
+ unsigned int chip_addx, unsigned int *data)
+{
+ struct ca0132_spec *spec = codec->spec;
+ int err;
+
+ mutex_lock(&spec->chipio_mutex);
+
+ /* write the address, and if successful proceed to write data */
+ err = chipio_write_address(codec, chip_addx);
+ if (err < 0)
+ goto exit;
+
+ err = chipio_read_data(codec, data);
+ if (err < 0)
+ goto exit;
+
+exit:
+ mutex_unlock(&spec->chipio_mutex);
+ return err;
+}
+
+/*
+ * PCM stuffs
+ */
+static void ca0132_setup_stream(struct hda_codec *codec, hda_nid_t nid,
+ u32 stream_tag,
+ int channel_id, int format)
+{
+ unsigned int oldval, newval;
+
+ if (!nid)
+ return;
+
+ snd_printdd("ca0132_setup_stream: "
+ "NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n",
+ nid, stream_tag, channel_id, format);
+
+ /* update the format-id if changed */
+ oldval = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_STREAM_FORMAT,
+ 0);
+ if (oldval != format) {
+ msleep(20);
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_STREAM_FORMAT,
+ format);
+ }
+
+ oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
+ newval = (stream_tag << 4) | channel_id;
+ if (oldval != newval) {
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_CHANNEL_STREAMID,
+ newval);
+ }
+}
+
+static void ca0132_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
+{
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0);
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
+}
+
+/*
+ * PCM callbacks
+ */
+static int ca0132_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ ca0132_setup_stream(codec, spec->dacs[0], stream_tag, 0, format);
+
+ return 0;
+}
+
+static int ca0132_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ ca0132_cleanup_stream(codec, spec->dacs[0]);
+
+ return 0;
+}
+
+/*
+ * Digital out
+ */
+static int ca0132_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ unsigned int stream_tag,
+ unsigned int format,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ ca0132_setup_stream(codec, spec->dig_out, stream_tag, 0, format);
+
+ return 0;
+}
+
+static int ca0132_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ ca0132_cleanup_stream(codec, spec->dig_out);
+
+ return 0;
+}
+
+/*
+ * Analog capture
+ */
+static int ca0132_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 ca0132_spec *spec = codec->spec;
+
+ ca0132_setup_stream(codec, spec->adcs[substream->number],
+ stream_tag, 0, format);
+
+ return 0;
+}
+
+static int ca0132_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ ca0132_cleanup_stream(codec, spec->adcs[substream->number]);
+
+ return 0;
+}
+
+/*
+ * Digital capture
+ */
+static int ca0132_dig_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 ca0132_spec *spec = codec->spec;
+
+ ca0132_setup_stream(codec, spec->dig_in, stream_tag, 0, format);
+
+ return 0;
+}
+
+static int ca0132_dig_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ ca0132_cleanup_stream(codec, spec->dig_in);
+
+ return 0;
+}
+
+/*
+ */
+static struct hda_pcm_stream ca0132_pcm_analog_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .ops = {
+ .prepare = ca0132_playback_pcm_prepare,
+ .cleanup = ca0132_playback_pcm_cleanup
+ },
+};
+
+static struct hda_pcm_stream ca0132_pcm_analog_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .ops = {
+ .prepare = ca0132_capture_pcm_prepare,
+ .cleanup = ca0132_capture_pcm_cleanup
+ },
+};
+
+static struct hda_pcm_stream ca0132_pcm_digital_playback = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .ops = {
+ .prepare = ca0132_dig_playback_pcm_prepare,
+ .cleanup = ca0132_dig_playback_pcm_cleanup
+ },
+};
+
+static struct hda_pcm_stream ca0132_pcm_digital_capture = {
+ .substreams = 1,
+ .channels_min = 2,
+ .channels_max = 2,
+ .ops = {
+ .prepare = ca0132_dig_capture_pcm_prepare,
+ .cleanup = ca0132_dig_capture_pcm_cleanup
+ },
+};
+
+static int ca0132_build_pcms(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ struct hda_pcm *info = spec->pcm_rec;
+
+ codec->pcm_info = info;
+ codec->num_pcms = 0;
+
+ info->name = "CA0132 Analog";
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0132_pcm_analog_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0];
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
+ spec->multiout.max_channels;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_inputs;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0];
+ codec->num_pcms++;
+
+ if (!spec->dig_out && !spec->dig_in)
+ return 0;
+
+ info++;
+ info->name = "CA0132 Digital";
+ info->pcm_type = HDA_PCM_TYPE_SPDIF;
+ if (spec->dig_out) {
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+ ca0132_pcm_digital_playback;
+ info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out;
+ }
+ if (spec->dig_in) {
+ info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+ ca0132_pcm_digital_capture;
+ info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in;
+ }
+ codec->num_pcms++;
+
+ return 0;
+}
+
+#define REG_CODEC_MUTE 0x18b014
+#define REG_CODEC_HP_VOL_L 0x18b070
+#define REG_CODEC_HP_VOL_R 0x18b074
+
+static int ca0132_hp_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ long *valp = ucontrol->value.integer.value;
+
+ *valp = spec->curr_hp_switch;
+ return 0;
+}
+
+static int ca0132_hp_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ long *valp = ucontrol->value.integer.value;
+ unsigned int data;
+ int err;
+
+ /* any change? */
+ if (spec->curr_hp_switch == *valp)
+ return 0;
+
+ snd_hda_power_up(codec);
+
+ err = chipio_read(codec, REG_CODEC_MUTE, &data);
+ if (err < 0)
+ return err;
+
+ /* *valp 0 is mute, 1 is unmute */
+ data = (data & 0x7f) | (*valp ? 0 : 0x80);
+ chipio_write(codec, REG_CODEC_MUTE, data);
+ if (err < 0)
+ return err;
+
+ spec->curr_hp_switch = *valp;
+
+ snd_hda_power_down(codec);
+ return 1;
+}
+
+static int ca0132_speaker_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ long *valp = ucontrol->value.integer.value;
+
+ *valp = spec->curr_speaker_switch;
+ return 0;
+}
+
+static int ca0132_speaker_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ long *valp = ucontrol->value.integer.value;
+ unsigned int data;
+ int err;
+
+ /* any change? */
+ if (spec->curr_speaker_switch == *valp)
+ return 0;
+
+ snd_hda_power_up(codec);
+
+ err = chipio_read(codec, REG_CODEC_MUTE, &data);
+ if (err < 0)
+ return err;
+
+ /* *valp 0 is mute, 1 is unmute */
+ data = (data & 0xef) | (*valp ? 0 : 0x10);
+ chipio_write(codec, REG_CODEC_MUTE, data);
+ if (err < 0)
+ return err;
+
+ spec->curr_speaker_switch = *valp;
+
+ snd_hda_power_down(codec);
+ return 1;
+}
+
+static int ca0132_hp_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ long *valp = ucontrol->value.integer.value;
+
+ *valp++ = spec->curr_hp_volume[0];
+ *valp = spec->curr_hp_volume[1];
+ return 0;
+}
+
+static int ca0132_hp_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct ca0132_spec *spec = codec->spec;
+ long *valp = ucontrol->value.integer.value;
+ long left_vol, right_vol;
+ unsigned int data;
+ int val;
+ int err;
+
+ left_vol = *valp++;
+ right_vol = *valp;
+
+ /* any change? */
+ if ((spec->curr_hp_volume[0] == left_vol) &&
+ (spec->curr_hp_volume[1] == right_vol))
+ return 0;
+
+ snd_hda_power_up(codec);
+
+ err = chipio_read(codec, REG_CODEC_HP_VOL_L, &data);
+ if (err < 0)
+ return err;
+
+ val = 31 - left_vol;
+ data = (data & 0xe0) | val;
+ chipio_write(codec, REG_CODEC_HP_VOL_L, data);
+ if (err < 0)
+ return err;
+
+ val = 31 - right_vol;
+ data = (data & 0xe0) | val;
+ chipio_write(codec, REG_CODEC_HP_VOL_R, data);
+ if (err < 0)
+ return err;
+
+ spec->curr_hp_volume[0] = left_vol;
+ spec->curr_hp_volume[1] = right_vol;
+
+ snd_hda_power_down(codec);
+ return 1;
+}
+
+static int add_hp_switch(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO("Headphone Playback Switch",
+ nid, 1, 0, HDA_OUTPUT);
+ knew.get = ca0132_hp_switch_get;
+ knew.put = ca0132_hp_switch_put;
+ return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+static int add_hp_volume(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_VOLUME_MONO("Headphone Playback Volume",
+ nid, 3, 0, HDA_OUTPUT);
+ knew.get = ca0132_hp_volume_get;
+ knew.put = ca0132_hp_volume_put;
+ return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+static int add_speaker_switch(struct hda_codec *codec, hda_nid_t nid)
+{
+ struct snd_kcontrol_new knew =
+ HDA_CODEC_MUTE_MONO("Speaker Playback Switch",
+ nid, 1, 0, HDA_OUTPUT);
+ knew.get = ca0132_speaker_switch_get;
+ knew.put = ca0132_speaker_switch_put;
+ return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
+}
+
+static void ca0132_fix_hp_caps(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ unsigned int caps;
+
+ /* set mute-capable, 1db step, 32 steps, ofs 6 */
+ caps = 0x80031f06;
+ snd_hda_override_amp_caps(codec, cfg->hp_pins[0], HDA_OUTPUT, caps);
+}
+
+static int ca0132_build_controls(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i, err;
+
+ if (spec->multiout.num_dacs) {
+ err = add_speaker_switch(codec, spec->out_pins[0]);
+ if (err < 0)
+ return err;
+ }
+
+ if (cfg->hp_outs) {
+ ca0132_fix_hp_caps(codec);
+ err = add_hp_switch(codec, cfg->hp_pins[0]);
+ if (err < 0)
+ return err;
+ err = add_hp_volume(codec, cfg->hp_pins[0]);
+ if (err < 0)
+ return err;
+ }
+
+ for (i = 0; i < spec->num_inputs; i++) {
+ const char *label = spec->input_labels[i];
+
+ err = add_in_switch(codec, spec->adcs[i], label);
+ if (err < 0)
+ return err;
+ err = add_in_volume(codec, spec->adcs[i], label);
+ if (err < 0)
+ return err;
+ if (cfg->inputs[i].type == AUTO_PIN_MIC) {
+ /* add Mic-Boost */
+ err = add_in_mono_volume(codec, spec->input_pins[i],
+ "Mic Boost", 1);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ if (spec->dig_out) {
+ err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out,
+ spec->dig_out);
+ if (err < 0)
+ return err;
+ err = add_out_volume(codec, spec->dig_out, "IEC958");
+ if (err < 0)
+ return err;
+ }
+
+ if (spec->dig_in) {
+ err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
+ if (err < 0)
+ return err;
+ err = add_in_volume(codec, spec->dig_in, "IEC958");
+ }
+ return 0;
+}
+
+
+static void ca0132_set_ct_ext(struct hda_codec *codec, int enable)
+{
+ /* Set Creative extension */
+ snd_printdd("SET CREATIVE EXTENSION\n");
+ snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0,
+ VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE,
+ enable);
+ msleep(20);
+}
+
+
+static void ca0132_config(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+
+ /* line-outs */
+ cfg->line_outs = 1;
+ cfg->line_out_pins[0] = 0x0b; /* front */
+ cfg->line_out_type = AUTO_PIN_LINE_OUT;
+
+ spec->dacs[0] = 0x02;
+ spec->out_pins[0] = 0x0b;
+ spec->multiout.dac_nids = spec->dacs;
+ spec->multiout.num_dacs = 1;
+ spec->multiout.max_channels = 2;
+
+ /* headphone */
+ cfg->hp_outs = 1;
+ cfg->hp_pins[0] = 0x0f;
+
+ spec->hp_dac = 0;
+ spec->multiout.hp_nid = 0;
+
+ /* inputs */
+ cfg->num_inputs = 2; /* Mic-in and line-in */
+ cfg->inputs[0].pin = 0x12;
+ cfg->inputs[0].type = AUTO_PIN_MIC;
+ cfg->inputs[1].pin = 0x11;
+ cfg->inputs[1].type = AUTO_PIN_LINE_IN;
+
+ /* Mic-in */
+ spec->input_pins[0] = 0x12;
+ spec->input_labels[0] = "Mic-In";
+ spec->adcs[0] = 0x07;
+
+ /* Line-In */
+ spec->input_pins[1] = 0x11;
+ spec->input_labels[1] = "Line-In";
+ spec->adcs[1] = 0x08;
+ spec->num_inputs = 2;
+}
+
+static void ca0132_init_chip(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+
+ mutex_init(&spec->chipio_mutex);
+}
+
+static void ca0132_exit_chip(struct hda_codec *codec)
+{
+ /* put any chip cleanup stuffs here. */
+}
+
+static int ca0132_init(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec = codec->spec;
+ struct auto_pin_cfg *cfg = &spec->autocfg;
+ int i;
+
+ for (i = 0; i < spec->multiout.num_dacs; i++) {
+ init_output(codec, spec->out_pins[i],
+ spec->multiout.dac_nids[i]);
+ }
+ init_output(codec, cfg->hp_pins[0], spec->hp_dac);
+ init_output(codec, cfg->dig_out_pins[0], spec->dig_out);
+
+ for (i = 0; i < spec->num_inputs; i++)
+ init_input(codec, spec->input_pins[i], spec->adcs[i]);
+
+ init_input(codec, cfg->dig_in_pin, spec->dig_in);
+
+ ca0132_set_ct_ext(codec, 1);
+
+ return 0;
+}
+
+
+static void ca0132_free(struct hda_codec *codec)
+{
+ ca0132_set_ct_ext(codec, 0);
+ ca0132_exit_chip(codec);
+ kfree(codec->spec);
+}
+
+static struct hda_codec_ops ca0132_patch_ops = {
+ .build_controls = ca0132_build_controls,
+ .build_pcms = ca0132_build_pcms,
+ .init = ca0132_init,
+ .free = ca0132_free,
+};
+
+
+
+static int patch_ca0132(struct hda_codec *codec)
+{
+ struct ca0132_spec *spec;
+
+ snd_printdd("patch_ca0132\n");
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return -ENOMEM;
+ codec->spec = spec;
+
+ ca0132_init_chip(codec);
+
+ ca0132_config(codec);
+
+ codec->patch_ops = ca0132_patch_ops;
+
+ return 0;
+}
+
+/*
+ * patch entries
+ */
+static struct hda_codec_preset snd_hda_preset_ca0132[] = {
+ { .id = 0x11020011, .name = "CA0132", .patch = patch_ca0132 },
+ {} /* terminator */
+};
+
+MODULE_ALIAS("snd-hda-codec-id:11020011");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Creative CA0132, CA0132 HD-audio codec");
+
+static struct hda_codec_preset_list ca0132_list = {
+ .preset = snd_hda_preset_ca0132,
+ .owner = THIS_MODULE,
+};
+
+static int __init patch_ca0132_init(void)
+{
+ return snd_hda_add_codec_preset(&ca0132_list);
+}
+
+static void __exit patch_ca0132_exit(void)
+{
+ snd_hda_delete_codec_preset(&ca0132_list);
+}
+
+module_init(patch_ca0132_init)
+module_exit(patch_ca0132_exit)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 4adf9e2..5e4efb7 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -348,6 +348,7 @@
const hda_nid_t *adc_nids;
const hda_nid_t *capsrc_nids;
hda_nid_t dig_in_nid; /* digital-in NID; optional */
+ hda_nid_t mixer_nid; /* analog-mixer NID */
/* capture setup for dynamic dual-adc switch */
unsigned int cur_adc_idx;
@@ -1759,6 +1760,15 @@
return 0;
}
+static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
+{
+ int i;
+ for (i = 0; i < nums; i++)
+ if (list[i] == nid)
+ return true;
+ return false;
+}
+
/* check subsystem ID and set up device-specific initialization;
* return 1 if initialized, 0 if invalid SSID
*/
@@ -1868,9 +1878,9 @@
nid = porti;
else
return 1;
- for (i = 0; i < spec->autocfg.line_outs; i++)
- if (spec->autocfg.line_out_pins[i] == nid)
- return 1;
+ if (found_in_nid_list(nid, spec->autocfg.line_out_pins,
+ spec->autocfg.line_outs))
+ return 1;
spec->autocfg.hp_pins[0] = nid;
}
return 1;
@@ -2061,15 +2071,23 @@
{
struct alc_spec *spec = codec->spec;
int i;
- hda_nid_t pin;
+ hda_nid_t pin, dac;
for (i = 0; i < spec->autocfg.dig_outs; i++) {
pin = spec->autocfg.dig_out_pins[i];
- if (pin) {
- snd_hda_codec_write(codec, pin, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- PIN_OUT);
- }
+ if (!pin)
+ continue;
+ snd_hda_codec_write(codec, pin, 0,
+ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ if (!i)
+ dac = spec->multiout.dig_out_nid;
+ else
+ dac = spec->slave_dig_outs[i - 1];
+ if (!dac || !(get_wcaps(codec, dac) & AC_WCAP_OUT_AMP))
+ continue;
+ snd_hda_codec_write(codec, dac, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_UNMUTE);
}
pin = spec->autocfg.dig_in_pin;
if (pin)
@@ -5321,9 +5339,10 @@
#define ALC880_PIN_CD_NID 0x1c
/* fill in the dac_nids table from the parsed pin configuration */
-static int alc880_auto_fill_dac_nids(struct alc_spec *spec,
- const struct auto_pin_cfg *cfg)
+static int alc880_auto_fill_dac_nids(struct hda_codec *codec)
{
+ struct alc_spec *spec = codec->spec;
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
hda_nid_t nid;
int assigned[4];
int i, j;
@@ -5359,11 +5378,15 @@
return 0;
}
-static const char *alc_get_line_out_pfx(struct alc_spec *spec,
- bool can_be_master)
+static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch,
+ bool can_be_master, int *index)
{
struct auto_pin_cfg *cfg = &spec->autocfg;
+ static const char * const chname[4] = {
+ "Front", "Surround", NULL /*CLFE*/, "Side"
+ };
+ *index = 0;
if (cfg->line_outs == 1 && !spec->multi_ios &&
!cfg->hp_outs && !cfg->speaker_outs && can_be_master)
return "Master";
@@ -5374,23 +5397,23 @@
return "Speaker";
break;
case AUTO_PIN_HP_OUT:
+ /* for multi-io case, only the primary out */
+ if (ch && spec->multi_ios)
+ break;
+ *index = ch;
return "Headphone";
default:
if (cfg->line_outs == 1 && !spec->multi_ios)
return "PCM";
break;
}
- return NULL;
+ return chname[ch];
}
/* add playback controls from the parsed DAC table */
static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec,
const struct auto_pin_cfg *cfg)
{
- static const char * const chname[4] = {
- "Front", "Surround", NULL /*CLFE*/, "Side"
- };
- const char *pfx = alc_get_line_out_pfx(spec, false);
hda_nid_t nid;
int i, err, noutputs;
@@ -5399,10 +5422,13 @@
noutputs += spec->multi_ios;
for (i = 0; i < noutputs; i++) {
+ const char *name;
+ int index;
if (!spec->multiout.dac_nids[i])
continue;
nid = alc880_idx_to_mixer(alc880_dac_to_idx(spec->multiout.dac_nids[i]));
- if (!pfx && i == 2) {
+ name = alc_get_line_out_pfx(spec, i, false, &index);
+ if (!name) {
/* Center/LFE */
err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
"Center",
@@ -5429,12 +5455,6 @@
if (err < 0)
return err;
} else {
- const char *name = pfx;
- int index = i;
- if (!name) {
- name = chname[i];
- index = 0;
- }
err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
name, index,
HDA_COMPOSE_AMP_VAL(nid, 3, 0,
@@ -5601,6 +5621,19 @@
return PIN_OUT;
}
+static void alc880_auto_init_dac(struct hda_codec *codec, hda_nid_t nid)
+{
+ if (!nid)
+ return;
+ nid = alc880_idx_to_mixer(alc880_dac_to_idx(nid));
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_ZERO);
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_MUTE(0));
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_MUTE(1));
+}
+
static void alc880_auto_init_multi_out(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
@@ -5611,12 +5644,16 @@
int pin_type = get_pin_type(spec->autocfg.line_out_type);
alc880_auto_set_output_and_unmute(codec, nid, pin_type, i);
}
+ /* mute DACs */
+ for (i = 0; i < spec->multiout.num_dacs; i++)
+ alc880_auto_init_dac(codec, spec->multiout.dac_nids[i]);
}
static void alc880_auto_init_extra_out(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
hda_nid_t pin;
+ int i;
pin = spec->autocfg.speaker_pins[0];
if (pin) /* connect to front */
@@ -5624,6 +5661,10 @@
pin = spec->autocfg.hp_pins[0];
if (pin) /* connect to front */
alc880_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
+ /* mute DACs */
+ alc880_auto_init_dac(codec, spec->multiout.hp_nid);
+ for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++)
+ alc880_auto_init_dac(codec, spec->multiout.extra_out_nid[i]);
}
static void alc880_auto_init_analog_input(struct hda_codec *codec)
@@ -5636,13 +5677,21 @@
hda_nid_t nid = cfg->inputs[i].pin;
if (alc_is_input_pin(codec, nid)) {
alc_set_input_pin(codec, nid, cfg->inputs[i].type);
- if (nid != ALC880_PIN_CD_NID &&
- (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
+ if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE,
AMP_OUT_MUTE);
}
}
+
+ /* mute all loopback inputs */
+ if (spec->mixer_nid) {
+ int nums = snd_hda_get_conn_list(codec, spec->mixer_nid, NULL);
+ for (i = 0; i < nums; i++)
+ snd_hda_codec_write(codec, spec->mixer_nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_MUTE(i));
+ }
}
static void alc880_auto_init_input_src(struct hda_codec *codec)
@@ -5661,10 +5710,14 @@
snd_hda_codec_write(codec, spec->adc_nids[c], 0,
AC_VERB_SET_CONNECT_SEL,
imux->items[0].index);
+ snd_hda_codec_write(codec, spec->adc_nids[c], 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_MUTE(0));
}
}
-static int alc_auto_add_multi_channel_mode(struct hda_codec *codec);
+static int alc_auto_add_multi_channel_mode(struct hda_codec *codec,
+ int (*fill_dac)(struct hda_codec *));
/* parse the BIOS configuration and set up the alc_spec */
/* return 1 if successful, 0 if the proper config is not found,
@@ -5683,10 +5736,10 @@
if (!spec->autocfg.line_outs)
return 0; /* can't find valid BIOS pin config */
- err = alc880_auto_fill_dac_nids(spec, &spec->autocfg);
+ err = alc880_auto_fill_dac_nids(codec);
if (err < 0)
return err;
- err = alc_auto_add_multi_channel_mode(codec);
+ err = alc_auto_add_multi_channel_mode(codec, alc880_auto_fill_dac_nids);
if (err < 0)
return err;
err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg);
@@ -5712,8 +5765,6 @@
if (spec->kctls.list)
add_mixer(spec, spec->kctls.list);
- add_verb(spec, alc880_volume_init_verbs);
-
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux[0];
@@ -5983,6 +6034,8 @@
codec->spec = spec;
+ spec->mixer_nid = 0x0b;
+
board_config = snd_hda_check_board_config(codec, ALC880_MODEL_LAST,
alc880_models,
alc880_cfg_tbl);
@@ -7183,12 +7236,8 @@
nid = cfg->line_out_pins[0];
if (nid) {
const char *pfx;
- if (!cfg->speaker_pins[0] && !cfg->hp_pins[0])
- pfx = "Master";
- else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
- pfx = "Speaker";
- else
- pfx = "Front";
+ int index;
+ pfx = alc_get_line_out_pfx(spec, 0, true, &index);
err = alc260_add_playback_controls(spec, nid, pfx, &vols);
if (err < 0)
return err;
@@ -7231,10 +7280,24 @@
}
}
+static void alc260_auto_init_dac(struct hda_codec *codec, hda_nid_t nid)
+{
+ if (!nid)
+ return;
+ nid += 0x06; /* DAC -> MIX */
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_ZERO);
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_MUTE(0));
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_MUTE(1));
+}
+
static void alc260_auto_init_multi_out(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
hda_nid_t nid;
+ int i;
nid = spec->autocfg.line_out_pins[0];
if (nid) {
@@ -7249,74 +7312,17 @@
nid = spec->autocfg.hp_pins[0];
if (nid)
alc260_auto_set_output_and_unmute(codec, nid, PIN_HP, 0);
+
+ /* mute DACs */
+ for (i = 0; i < spec->multiout.num_dacs; i++)
+ alc260_auto_init_dac(codec, spec->multiout.dac_nids[i]);
+ alc260_auto_init_dac(codec, spec->multiout.extra_out_nid[0]);
+ alc260_auto_init_dac(codec, spec->multiout.hp_nid);
}
-#define ALC260_PIN_CD_NID 0x16
-static void alc260_auto_init_analog_input(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- if (nid >= 0x12) {
- alc_set_input_pin(codec, nid, cfg->inputs[i].type);
- if (nid != ALC260_PIN_CD_NID &&
- (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_MUTE);
- }
- }
-}
-
+#define alc260_auto_init_analog_input alc880_auto_init_analog_input
#define alc260_auto_init_input_src alc880_auto_init_input_src
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static const struct hda_verb alc260_volume_init_verbs[] = {
- /*
- * Unmute ADC0-1 and set the default input to mic-in
- */
- {0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x05, 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
- * 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 */
- /* mute analog inputs */
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
- /*
- * Set up output mixers (0x08 - 0x0a)
- */
- /* set vol=0 to output mixers */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- /* set up input amps for analog loopback */
- /* Amp Indices: DAC = 0, mixer = 1 */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- { }
-};
-
static int alc260_parse_auto_config(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
@@ -7343,8 +7349,6 @@
if (spec->kctls.list)
add_mixer(spec, spec->kctls.list);
- add_verb(spec, alc260_volume_init_verbs);
-
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux[0];
@@ -7591,6 +7595,8 @@
codec->spec = spec;
+ spec->mixer_nid = 0x07;
+
board_config = snd_hda_check_board_config(codec, ALC260_MODEL_LAST,
alc260_models,
alc260_cfg_tbl);
@@ -9038,48 +9044,6 @@
alc_hp_automute(codec);
}
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static const struct hda_verb alc883_auto_init_verbs[] = {
- /*
- * Unmute ADC0-2 and set the default input to mic-in
- */
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /*
- * Set up output mixers (0x0c - 0x0f)
- */
- /* set vol=0 to output mixers */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0f, 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)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* FIXME: use matrix-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
- /* Input mixer2 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- /* Input mixer3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- { }
-};
-
/* 2ch mode (Speaker:front, Subwoofer:CLFE, Line:input, Headphones:front) */
static const struct hda_verb alc889A_mb31_ch2_init[] = {
{0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP as front */
@@ -11039,6 +11003,9 @@
/* set as output */
alc_set_pin_output(codec, nid, pin_type);
+ if (snd_hda_get_conn_list(codec, nid, NULL) < 2)
+ return;
+
if (dac == 0x25)
idx = 4;
else if (dac >= 0x02 && dac <= 0x05)
@@ -11048,6 +11015,8 @@
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx);
}
+#define alc882_auto_init_dac alc880_auto_init_dac
+
static void alc882_auto_init_multi_out(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
@@ -11060,6 +11029,9 @@
alc882_auto_set_output_and_unmute(codec, nid, pin_type,
spec->multiout.dac_nids[i]);
}
+ /* mute DACs */
+ for (i = 0; i < spec->multiout.num_dacs; i++)
+ alc882_auto_init_dac(codec, spec->multiout.dac_nids[i]);
}
static void alc882_auto_init_hp_out(struct hda_codec *codec)
@@ -11091,23 +11063,14 @@
alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac);
}
}
+
+ /* mute DACs */
+ alc882_auto_init_dac(codec, spec->multiout.hp_nid);
+ for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++)
+ alc882_auto_init_dac(codec, spec->multiout.extra_out_nid[i]);
}
-static void alc882_auto_init_analog_input(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- alc_set_input_pin(codec, nid, cfg->inputs[i].type);
- if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_MUTE);
- }
-}
+#define alc882_auto_init_analog_input alc880_auto_init_analog_input
static void alc882_auto_init_input_src(struct hda_codec *codec)
{
@@ -11115,7 +11078,6 @@
int c;
for (c = 0; c < spec->num_adc_nids; c++) {
- hda_nid_t conn_list[HDA_MAX_NUM_INPUTS];
hda_nid_t nid = spec->capsrc_nids[c];
unsigned int mux_idx;
const struct hda_input_mux *imux;
@@ -11126,9 +11088,8 @@
AC_VERB_SET_AMP_GAIN_MUTE,
AMP_IN_MUTE(0));
- conns = snd_hda_get_connections(codec, nid, conn_list,
- ARRAY_SIZE(conn_list));
- if (conns < 0)
+ conns = snd_hda_get_conn_list(codec, nid, NULL);
+ if (conns <= 0)
continue;
mux_idx = c >= spec->num_mux_defs ? 0 : c;
imux = &spec->input_mux[mux_idx];
@@ -11215,10 +11176,10 @@
if (!spec->autocfg.line_outs)
return 0; /* can't find valid BIOS pin config */
- err = alc880_auto_fill_dac_nids(spec, &spec->autocfg);
+ err = alc880_auto_fill_dac_nids(codec);
if (err < 0)
return err;
- err = alc_auto_add_multi_channel_mode(codec);
+ err = alc_auto_add_multi_channel_mode(codec, alc880_auto_fill_dac_nids);
if (err < 0)
return err;
err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg);
@@ -11244,7 +11205,6 @@
if (spec->kctls.list)
add_mixer(spec, spec->kctls.list);
- add_verb(spec, alc883_auto_init_verbs);
/* if ADC 0x07 is available, initialize it, too */
if (get_wcaps_type(get_wcaps(codec, 0x07)) == AC_WID_AUD_IN)
add_verb(spec, alc882_adc1_init_verbs);
@@ -11285,6 +11245,8 @@
codec->spec = spec;
+ spec->mixer_nid = 0x0b;
+
switch (codec->vendor_id) {
case 0x10ec0882:
case 0x10ec0885:
@@ -11356,7 +11318,6 @@
for (i = 0; i < ARRAY_SIZE(alc882_adc_nids); i++) {
const struct hda_input_mux *imux = spec->input_mux;
hda_nid_t cap;
- hda_nid_t items[16];
hda_nid_t nid = alc882_adc_nids[i];
unsigned int wcap = get_wcaps(codec, nid);
/* get type */
@@ -11367,8 +11328,7 @@
err = snd_hda_get_connections(codec, nid, &cap, 1);
if (err < 0)
continue;
- err = snd_hda_get_connections(codec, cap, items,
- ARRAY_SIZE(items));
+ err = snd_hda_get_conn_list(codec, cap, NULL);
if (err < 0)
continue;
for (j = 0; j < imux->num_items; j++)
@@ -12257,17 +12217,18 @@
{
const char *pfx;
int vbits;
- int i, err;
+ int i, index, err;
spec->multiout.num_dacs = 1; /* only use one dac */
spec->multiout.dac_nids = spec->private_dac_nids;
spec->private_dac_nids[0] = 2;
- pfx = alc_get_line_out_pfx(spec, true);
- if (!pfx)
- pfx = "Front";
for (i = 0; i < 2; i++) {
- err = alc262_add_out_sw_ctl(spec, cfg->line_out_pins[i], pfx, i);
+ pfx = alc_get_line_out_pfx(spec, i, true, &index);
+ if (!pfx)
+ pfx = "PCM";
+ err = alc262_add_out_sw_ctl(spec, cfg->line_out_pins[i], pfx,
+ index);
if (err < 0)
return err;
if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
@@ -12287,10 +12248,11 @@
vbits = alc262_check_volbit(cfg->line_out_pins[0]) |
alc262_check_volbit(cfg->speaker_pins[0]) |
alc262_check_volbit(cfg->hp_pins[0]);
- if (vbits == 1 || vbits == 2)
- pfx = "Master"; /* only one mixer is used */
vbits = 0;
for (i = 0; i < 2; i++) {
+ pfx = alc_get_line_out_pfx(spec, i, true, &index);
+ if (!pfx)
+ pfx = "PCM";
err = alc262_add_out_vol_ctl(spec, cfg->line_out_pins[i], pfx,
&vbits, i);
if (err < 0)
@@ -12314,70 +12276,6 @@
#define alc262_auto_create_input_ctls \
alc882_auto_create_input_ctls
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static const struct hda_verb alc262_volume_init_verbs[] = {
- /*
- * Unmute ADC0-2 and set the default input to mic-in
- */
- {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
- {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Mute input amps (CD, 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 - 0x0f)
- */
- /* set vol=0 to output mixers */
- {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
- {0x0e, 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)},
-
- /* FIXME: use matrix-type input source selection */
- /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
- /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
- /* Input mixer2 */
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
- /* Input mixer3 */
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
- {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
-
- { }
-};
-
static const struct hda_verb alc262_HP_BPC_init_verbs[] = {
/*
* Unmute ADC0-2 and set the default input to mic-in
@@ -12600,6 +12498,7 @@
*/
enum {
PINFIX_FSC_H270,
+ PINFIX_HP_Z200,
};
static const struct alc_fixup alc262_fixups[] = {
@@ -12612,9 +12511,17 @@
{ }
}
},
+ [PINFIX_HP_Z200] = {
+ .type = ALC_FIXUP_PINS,
+ .v.pins = (const struct alc_pincfg[]) {
+ { 0x16, 0x99130120 }, /* internal speaker */
+ { }
+ }
+ },
};
static const struct snd_pci_quirk alc262_fixup_tbl[] = {
+ SND_PCI_QUIRK(0x103c, 0x170b, "HP Z200", PINFIX_HP_Z200),
SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", PINFIX_FSC_H270),
{}
};
@@ -12666,7 +12573,6 @@
if (spec->kctls.list)
add_mixer(spec, spec->kctls.list);
- add_verb(spec, alc262_volume_init_verbs);
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux[0];
@@ -12731,6 +12637,8 @@
ALC262_HP_BPC),
SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1500, "HP z series",
ALC262_HP_BPC),
+ SND_PCI_QUIRK(0x103c, 0x170b, "HP Z200",
+ ALC262_AUTO),
SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x1700, "HP xw series",
ALC262_HP_BPC),
SND_PCI_QUIRK(0x103c, 0x2800, "HP D7000", ALC262_HP_BPC_D7000_WL),
@@ -13024,6 +12932,9 @@
return -ENOMEM;
codec->spec = spec;
+
+ spec->mixer_nid = 0x0b;
+
#if 0
/* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is
* under-run
@@ -13440,6 +13351,8 @@
{ }
};
+/* only for model=test */
+#ifdef CONFIG_SND_DEBUG
/*
* generic initialization of ADC, input mixers and output mixers
*/
@@ -13460,12 +13373,15 @@
{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+ { }
+};
+#endif /* CONFIG_SND_DEBUG */
- /* set PCBEEP vol = 0, mute connections */
+/* set PCBEEP vol = 0, mute connections */
+static const struct hda_verb alc268_beep_init_verbs[] = {
{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
{ }
};
@@ -13625,10 +13541,8 @@
nid = cfg->line_out_pins[0];
if (nid) {
const char *name;
- if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
- name = "Speaker";
- else
- name = "Front";
+ int index;
+ name = alc_get_line_out_pfx(spec, 0, true, &index);
err = alc268_new_analog_output(spec, nid, name, 0);
if (err < 0)
return err;
@@ -13682,6 +13596,14 @@
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx);
}
+static void alc268_auto_init_dac(struct hda_codec *codec, hda_nid_t nid)
+{
+ if (!nid)
+ return;
+ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_OUT_MUTE);
+}
+
static void alc268_auto_init_multi_out(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
@@ -13692,6 +13614,9 @@
int pin_type = get_pin_type(spec->autocfg.line_out_type);
alc268_auto_set_output_and_unmute(codec, nid, pin_type);
}
+ /* mute DACs */
+ for (i = 0; i < spec->multiout.num_dacs; i++)
+ alc268_auto_init_dac(codec, spec->multiout.dac_nids[i]);
}
static void alc268_auto_init_hp_out(struct hda_codec *codec)
@@ -13711,6 +13636,10 @@
if (spec->autocfg.mono_out_pin)
snd_hda_codec_write(codec, spec->autocfg.mono_out_pin, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+ /* mute DACs */
+ alc268_auto_init_dac(codec, spec->multiout.hp_nid);
+ for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++)
+ alc268_auto_init_dac(codec, spec->multiout.extra_out_nid[i]);
}
static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec)
@@ -13804,7 +13733,7 @@
if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d)
add_mixer(spec, alc268_beep_mixer);
- add_verb(spec, alc268_volume_init_verbs);
+ add_verb(spec, alc268_beep_init_verbs);
spec->num_mux_defs = 2;
spec->input_mux = &spec->private_imux[0];
@@ -14029,7 +13958,8 @@
[ALC268_TEST] = {
.mixers = { alc268_test_mixer, alc268_capture_mixer },
.init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
- alc268_volume_init_verbs },
+ alc268_volume_init_verbs,
+ alc268_beep_init_verbs },
.num_dacs = ARRAY_SIZE(alc268_dac_nids),
.dac_nids = alc268_dac_nids,
.num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
@@ -14056,6 +13986,8 @@
codec->spec = spec;
+ /* ALC268 has no aa-loopback mixer */
+
board_config = snd_hda_check_board_config(codec, ALC268_MODEL_LAST,
alc268_models,
alc268_cfg_tbl);
@@ -14767,13 +14699,10 @@
if (spec->kctls.list)
add_mixer(spec, spec->kctls.list);
- if (spec->codec_variant != ALC269_TYPE_NORMAL) {
- add_verb(spec, alc269vb_init_verbs);
+ if (spec->codec_variant != ALC269_TYPE_NORMAL)
alc_ssid_check(codec, 0, 0x1b, 0x14, 0x21);
- } else {
- add_verb(spec, alc269_init_verbs);
+ else
alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
- }
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux[0];
@@ -15223,6 +15152,8 @@
codec->spec = spec;
+ spec->mixer_nid = 0x0b;
+
alc_auto_parse_customize_define(codec);
if (codec->vendor_id == 0x10ec0269) {
@@ -15850,58 +15781,6 @@
{ }
};
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static const struct hda_verb alc861_auto_init_verbs[] = {
- /*
- * Unmute ADC0 and set the default input to mic-in
- */
- /* {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, */
- {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
- /* Unmute DAC0~3 & spdif out*/
- {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
- {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-
- /* Unmute Mixer 14 (mic) 1c (Line in)*/
- {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x014, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x01c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
- /* Unmute Stereo Mixer 15 */
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
- {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c},
-
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
- {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-
- {0x08, AC_VERB_SET_CONNECT_SEL, 0x00}, /* set Mic 1 */
-
- { }
-};
-
static const struct hda_verb alc861_toshiba_init_verbs[] = {
{0x0f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
@@ -15969,7 +15848,7 @@
{
struct alc_spec *spec = codec->spec;
hda_nid_t mix, srcs[5];
- int i, j, num;
+ int i, num;
if (snd_hda_get_connections(codec, pin, &mix, 1) != 1)
return 0;
@@ -15981,20 +15860,18 @@
type = get_wcaps_type(get_wcaps(codec, srcs[i]));
if (type != AC_WID_AUD_OUT)
continue;
- for (j = 0; j < spec->multiout.num_dacs; j++)
- if (spec->multiout.dac_nids[j] == srcs[i])
- break;
- if (j >= spec->multiout.num_dacs)
+ if (!found_in_nid_list(srcs[i], spec->multiout.dac_nids,
+ spec->multiout.num_dacs))
return srcs[i];
}
return 0;
}
/* fill in the dac_nids table from the parsed pin configuration */
-static int alc861_auto_fill_dac_nids(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
+static int alc861_auto_fill_dac_nids(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
int i;
hda_nid_t nid, dac;
@@ -16024,10 +15901,6 @@
const struct auto_pin_cfg *cfg)
{
struct alc_spec *spec = codec->spec;
- static const char * const chname[4] = {
- "Front", "Surround", NULL /*CLFE*/, "Side"
- };
- const char *pfx = alc_get_line_out_pfx(spec, true);
hda_nid_t nid;
int i, err, noutputs;
@@ -16036,10 +15909,13 @@
noutputs += spec->multi_ios;
for (i = 0; i < noutputs; i++) {
+ const char *name;
+ int index;
nid = spec->multiout.dac_nids[i];
if (!nid)
continue;
- if (!pfx && i == 2) {
+ name = alc_get_line_out_pfx(spec, i, true, &index);
+ if (!name) {
/* Center/LFE */
err = alc861_create_out_sw(codec, "Center", nid, 1);
if (err < 0)
@@ -16048,12 +15924,6 @@
if (err < 0)
return err;
} else {
- const char *name = pfx;
- int index = i;
- if (!name) {
- name = chname[i];
- index = 0;
- }
err = __alc861_create_out_sw(codec, name, nid, index, 3);
if (err < 0)
return err;
@@ -16122,7 +15992,7 @@
struct alc_spec *spec = codec->spec;
int i;
- for (i = 0; i < spec->autocfg.line_outs; i++) {
+ for (i = 0; i < spec->autocfg.line_outs + spec->multi_ios; i++) {
hda_nid_t nid = spec->autocfg.line_out_pins[i];
int pin_type = get_pin_type(spec->autocfg.line_out_type);
if (nid)
@@ -16147,18 +16017,7 @@
spec->multiout.dac_nids[0]);
}
-static void alc861_auto_init_analog_input(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- if (nid >= 0x0c && nid <= 0x11)
- alc_set_input_pin(codec, nid, cfg->inputs[i].type);
- }
-}
+#define alc861_auto_init_analog_input alc880_auto_init_analog_input
/* parse the BIOS configuration and set up the alc_spec */
/* return 1 if successful, 0 if the proper config is not found,
@@ -16177,10 +16036,10 @@
if (!spec->autocfg.line_outs)
return 0; /* can't find valid BIOS pin config */
- err = alc861_auto_fill_dac_nids(codec, &spec->autocfg);
+ err = alc861_auto_fill_dac_nids(codec);
if (err < 0)
return err;
- err = alc_auto_add_multi_channel_mode(codec);
+ err = alc_auto_add_multi_channel_mode(codec, alc861_auto_fill_dac_nids);
if (err < 0)
return err;
err = alc861_auto_create_multi_out_ctls(codec, &spec->autocfg);
@@ -16200,8 +16059,6 @@
if (spec->kctls.list)
add_mixer(spec, spec->kctls.list);
- add_verb(spec, alc861_auto_init_verbs);
-
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux[0];
@@ -16416,6 +16273,8 @@
codec->spec = spec;
+ spec->mixer_nid = 0x15;
+
board_config = snd_hda_check_board_config(codec, ALC861_MODEL_LAST,
alc861_models,
alc861_cfg_tbl);
@@ -17101,61 +16960,9 @@
}
-static void alc861vd_auto_set_output_and_unmute(struct hda_codec *codec,
- hda_nid_t nid, int pin_type, int dac_idx)
-{
- alc_set_pin_output(codec, nid, pin_type);
-}
-
-static void alc861vd_auto_init_multi_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- int i;
-
- 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);
- if (nid)
- alc861vd_auto_set_output_and_unmute(codec, nid,
- pin_type, i);
- }
-}
-
-
-static void alc861vd_auto_init_hp_out(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- hda_nid_t pin;
-
- pin = spec->autocfg.hp_pins[0];
- if (pin) /* connect to front and use dac 0 */
- alc861vd_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
- pin = spec->autocfg.speaker_pins[0];
- if (pin)
- alc861vd_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
-}
-
-#define ALC861VD_PIN_CD_NID ALC880_PIN_CD_NID
-
-static void alc861vd_auto_init_analog_input(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- if (alc_is_input_pin(codec, nid)) {
- alc_set_input_pin(codec, nid, cfg->inputs[i].type);
- if (nid != ALC861VD_PIN_CD_NID &&
- (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_MUTE);
- }
- }
-}
-
+#define alc861vd_auto_init_multi_out alc882_auto_init_multi_out
+#define alc861vd_auto_init_hp_out alc882_auto_init_hp_out
+#define alc861vd_auto_init_analog_input alc882_auto_init_analog_input
#define alc861vd_auto_init_input_src alc882_auto_init_input_src
#define alc861vd_idx_to_mixer_vol(nid) ((nid) + 0x02)
@@ -17167,10 +16974,6 @@
static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
const struct auto_pin_cfg *cfg)
{
- static const char * const chname[4] = {
- "Front", "Surround", "CLFE", "Side"
- };
- const char *pfx = alc_get_line_out_pfx(spec, true);
hda_nid_t nid_v, nid_s;
int i, err, noutputs;
@@ -17179,6 +16982,8 @@
noutputs += spec->multi_ios;
for (i = 0; i < noutputs; i++) {
+ const char *name;
+ int index;
if (!spec->multiout.dac_nids[i])
continue;
nid_v = alc861vd_idx_to_mixer_vol(
@@ -17188,7 +16993,8 @@
alc880_dac_to_idx(
spec->multiout.dac_nids[i]));
- if (!pfx && i == 2) {
+ name = alc_get_line_out_pfx(spec, i, true, &index);
+ if (!name) {
/* Center/LFE */
err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
"Center",
@@ -17215,12 +17021,6 @@
if (err < 0)
return err;
} else {
- const char *name = pfx;
- int index = i;
- if (!name) {
- name = chname[i];
- index = 0;
- }
err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
name, index,
HDA_COMPOSE_AMP_VAL(nid_v, 3, 0,
@@ -17300,10 +17100,10 @@
if (!spec->autocfg.line_outs)
return 0; /* can't find valid BIOS pin config */
- err = alc880_auto_fill_dac_nids(spec, &spec->autocfg);
+ err = alc880_auto_fill_dac_nids(codec);
if (err < 0)
return err;
- err = alc_auto_add_multi_channel_mode(codec);
+ err = alc_auto_add_multi_channel_mode(codec, alc880_auto_fill_dac_nids);
if (err < 0)
return err;
err = alc861vd_auto_create_multi_out_ctls(spec, &spec->autocfg);
@@ -17330,8 +17130,6 @@
if (spec->kctls.list)
add_mixer(spec, spec->kctls.list);
- add_verb(spec, alc861vd_volume_init_verbs);
-
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux[0];
@@ -17390,6 +17188,8 @@
codec->spec = spec;
+ spec->mixer_nid = 0x0b;
+
board_config = snd_hda_check_board_config(codec, ALC861VD_MODEL_LAST,
alc861vd_models,
alc861vd_cfg_tbl);
@@ -18955,7 +18755,7 @@
{
struct alc_spec *spec = codec->spec;
hda_nid_t srcs[5];
- int i, j, num;
+ int i, num;
pin = alc_go_down_to_selector(codec, pin);
num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs));
@@ -18963,30 +18763,78 @@
hda_nid_t nid = alc_auto_mix_to_dac(codec, srcs[i]);
if (!nid)
continue;
- for (j = 0; j < spec->multiout.num_dacs; j++)
- if (spec->multiout.dac_nids[j] == nid)
- break;
- if (j >= spec->multiout.num_dacs)
- return nid;
+ if (found_in_nid_list(nid, spec->multiout.dac_nids,
+ spec->multiout.num_dacs))
+ continue;
+ if (spec->multiout.hp_nid == nid)
+ continue;
+ if (found_in_nid_list(nid, spec->multiout.extra_out_nid,
+ ARRAY_SIZE(spec->multiout.extra_out_nid)))
+ continue;
+ return nid;
}
return 0;
}
+static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin)
+{
+ hda_nid_t sel = alc_go_down_to_selector(codec, pin);
+ if (snd_hda_get_conn_list(codec, sel, NULL) == 1)
+ return alc_auto_look_for_dac(codec, pin);
+ return 0;
+}
+
/* fill in the dac_nids table from the parsed pin configuration */
-static int alc662_auto_fill_dac_nids(struct hda_codec *codec,
- const struct auto_pin_cfg *cfg)
+static int alc662_auto_fill_dac_nids(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
+ bool redone;
int i;
- hda_nid_t dac;
+ again:
+ spec->multiout.num_dacs = 0;
+ spec->multiout.hp_nid = 0;
+ spec->multiout.extra_out_nid[0] = 0;
+ memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids));
spec->multiout.dac_nids = spec->private_dac_nids;
- for (i = 0; i < cfg->line_outs; i++) {
- dac = alc_auto_look_for_dac(codec, cfg->line_out_pins[i]);
- if (!dac)
- continue;
- spec->private_dac_nids[spec->multiout.num_dacs++] = dac;
+
+ /* fill hard-wired DACs first */
+ if (!redone) {
+ for (i = 0; i < cfg->line_outs; i++)
+ spec->private_dac_nids[i] =
+ get_dac_if_single(codec, cfg->line_out_pins[i]);
+ if (cfg->hp_outs)
+ spec->multiout.hp_nid =
+ get_dac_if_single(codec, cfg->hp_pins[0]);
+ if (cfg->speaker_outs)
+ spec->multiout.extra_out_nid[0] =
+ get_dac_if_single(codec, cfg->speaker_pins[0]);
}
+
+ for (i = 0; i < cfg->line_outs; i++) {
+ hda_nid_t pin = cfg->line_out_pins[i];
+ if (spec->private_dac_nids[i])
+ continue;
+ spec->private_dac_nids[i] = alc_auto_look_for_dac(codec, pin);
+ if (!spec->private_dac_nids[i] && !redone) {
+ /* if we can't find primary DACs, re-probe without
+ * checking the hard-wired DACs
+ */
+ redone = true;
+ goto again;
+ }
+ }
+
+ for (i = 0; i < cfg->line_outs; i++) {
+ if (spec->private_dac_nids[i])
+ spec->multiout.num_dacs++;
+ else
+ memmove(spec->private_dac_nids + i,
+ spec->private_dac_nids + i + 1,
+ sizeof(hda_nid_t) * (cfg->line_outs - i - 1));
+ }
+
return 0;
}
@@ -19018,10 +18866,6 @@
const struct auto_pin_cfg *cfg)
{
struct alc_spec *spec = codec->spec;
- static const char * const chname[4] = {
- "Front", "Surround", NULL /*CLFE*/, "Side"
- };
- const char *pfx = alc_get_line_out_pfx(spec, true);
hda_nid_t nid, mix, pin;
int i, err, noutputs;
@@ -19030,6 +18874,8 @@
noutputs += spec->multi_ios;
for (i = 0; i < noutputs; i++) {
+ const char *name;
+ int index;
nid = spec->multiout.dac_nids[i];
if (!nid)
continue;
@@ -19040,7 +18886,8 @@
mix = alc_auto_dac_to_mix(codec, pin, nid);
if (!mix)
continue;
- if (!pfx && i == 2) {
+ name = alc_get_line_out_pfx(spec, i, true, &index);
+ if (!name) {
/* Center/LFE */
err = alc662_add_vol_ctl(spec, "Center", nid, 1);
if (err < 0)
@@ -19055,12 +18902,6 @@
if (err < 0)
return err;
} else {
- const char *name = pfx;
- int index = i;
- if (!name) {
- name = chname[i];
- index = 0;
- }
err = __alc662_add_vol_ctl(spec, name, nid, index, 3);
if (err < 0)
return err;
@@ -19073,18 +18914,16 @@
}
/* add playback controls for speaker and HP outputs */
-/* return DAC nid if any new DAC is assigned */
static int alc662_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
- const char *pfx)
+ hda_nid_t dac, const char *pfx)
{
struct alc_spec *spec = codec->spec;
- hda_nid_t nid, mix;
+ hda_nid_t mix;
int err;
if (!pin)
return 0;
- nid = alc_auto_look_for_dac(codec, pin);
- if (!nid) {
+ if (!dac) {
/* the corresponding DAC is already occupied */
if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP))
return 0; /* no way */
@@ -19093,16 +18932,16 @@
HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
}
- mix = alc_auto_dac_to_mix(codec, pin, nid);
+ mix = alc_auto_dac_to_mix(codec, pin, dac);
if (!mix)
return 0;
- err = alc662_add_vol_ctl(spec, pfx, nid, 3);
+ err = alc662_add_vol_ctl(spec, pfx, dac, 3);
if (err < 0)
return err;
err = alc662_add_sw_ctl(spec, pfx, mix, 3);
if (err < 0)
return err;
- return nid;
+ return 0;
}
/* create playback/capture controls for input pins */
@@ -19165,27 +19004,7 @@
spec->multiout.extra_out_nid[0]);
}
-#define ALC662_PIN_CD_NID ALC880_PIN_CD_NID
-
-static void alc662_auto_init_analog_input(struct hda_codec *codec)
-{
- struct alc_spec *spec = codec->spec;
- struct auto_pin_cfg *cfg = &spec->autocfg;
- int i;
-
- for (i = 0; i < cfg->num_inputs; i++) {
- hda_nid_t nid = cfg->inputs[i].pin;
- if (alc_is_input_pin(codec, nid)) {
- alc_set_input_pin(codec, nid, cfg->inputs[i].type);
- if (nid != ALC662_PIN_CD_NID &&
- (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_MUTE);
- }
- }
-}
-
+#define alc662_auto_init_analog_input alc882_auto_init_analog_input
#define alc662_auto_init_input_src alc882_auto_init_input_src
/*
@@ -19308,15 +19127,29 @@
.put = alc_auto_ch_mode_put,
};
-static int alc_auto_add_multi_channel_mode(struct hda_codec *codec)
+static int alc_auto_add_multi_channel_mode(struct hda_codec *codec,
+ int (*fill_dac)(struct hda_codec *))
{
struct alc_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
unsigned int location, defcfg;
int num_pins;
+ if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && cfg->hp_outs == 1) {
+ /* use HP as primary out */
+ cfg->speaker_outs = cfg->line_outs;
+ memcpy(cfg->speaker_pins, cfg->line_out_pins,
+ sizeof(cfg->speaker_pins));
+ cfg->line_outs = cfg->hp_outs;
+ memcpy(cfg->line_out_pins, cfg->hp_pins, sizeof(cfg->hp_pins));
+ cfg->hp_outs = 0;
+ memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
+ cfg->line_out_type = AUTO_PIN_HP_OUT;
+ if (fill_dac)
+ fill_dac(codec);
+ }
if (cfg->line_outs != 1 ||
- cfg->line_out_type != AUTO_PIN_LINE_OUT)
+ cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
return 0;
defcfg = snd_hda_codec_get_pincfg(codec, cfg->line_out_pins[0]);
@@ -19354,10 +19187,10 @@
if (!spec->autocfg.line_outs)
return 0; /* can't find valid BIOS pin config */
- err = alc662_auto_fill_dac_nids(codec, &spec->autocfg);
+ err = alc662_auto_fill_dac_nids(codec);
if (err < 0)
return err;
- err = alc_auto_add_multi_channel_mode(codec);
+ err = alc_auto_add_multi_channel_mode(codec, alc662_auto_fill_dac_nids);
if (err < 0)
return err;
err = alc662_auto_create_multi_out_ctls(codec, &spec->autocfg);
@@ -19365,17 +19198,15 @@
return err;
err = alc662_auto_create_extra_out(codec,
spec->autocfg.speaker_pins[0],
+ spec->multiout.extra_out_nid[0],
"Speaker");
if (err < 0)
return err;
- if (err)
- spec->multiout.extra_out_nid[0] = err;
err = alc662_auto_create_extra_out(codec, spec->autocfg.hp_pins[0],
+ spec->multiout.hp_nid,
"Headphone");
if (err < 0)
return err;
- if (err)
- spec->multiout.hp_nid = err;
err = alc662_auto_create_input_ctls(codec, &spec->autocfg);
if (err < 0)
return err;
@@ -19499,6 +19330,8 @@
codec->spec = spec;
+ spec->mixer_nid = 0x0b;
+
alc_auto_parse_customize_define(codec);
alc_fix_pll_init(codec, 0x20, 0x04, 15);
@@ -19865,10 +19698,8 @@
nid = cfg->line_out_pins[0];
if (nid) {
const char *name;
- if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
- name = "Speaker";
- else
- name = "Front";
+ int index;
+ name = alc_get_line_out_pfx(spec, 0, true, &index);
err = alc680_new_analog_output(spec, nid, name, 0);
if (err < 0)
return err;
@@ -19960,8 +19791,6 @@
if (spec->kctls.list)
add_mixer(spec, spec->kctls.list);
- add_verb(spec, alc680_init_verbs);
-
err = alc_auto_add_mic_boost(codec);
if (err < 0)
return err;
@@ -20025,6 +19854,8 @@
codec->spec = spec;
+ /* ALC680 has no aa-loopback mixer */
+
board_config = snd_hda_check_board_config(codec, ALC680_MODEL_LAST,
alc680_models,
alc680_cfg_tbl);