diff options
-rw-r--r-- | sound/pci/hda/Kconfig | 13 | ||||
-rw-r--r-- | sound/pci/hda/Makefile | 4 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.c | 84 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.h | 2 | ||||
-rw-r--r-- | sound/pci/hda/patch_ca0132.c | 1097 | ||||
-rw-r--r-- | sound/pci/hda/patch_realtek.c | 787 |
6 files changed, 1482 insertions, 505 deletions
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 0ea5cc60ac78..85217bd96d85 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -171,6 +171,19 @@ config SND_HDA_CODEC_CA0110 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 17ef3658f34b..87365d5ea2a9 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -13,6 +13,7 @@ snd-hda-codec-idt-objs := patch_sigmatel.o 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 @@ endif 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 a2388fc23e39..26c420de91c3 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -311,35 +311,35 @@ EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes); 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 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, 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 @@ static bool add_conn_list(struct snd_array *array, hda_nid_t nid) 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 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, 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 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec, 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 070efac7e207..c71cd7fb6d11 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -903,6 +903,8 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, 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 000000000000..d9a2254ceef6 --- /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 <[email protected]> + * + * 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 4adf9e23f049..5e4efb75879e 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -348,6 +348,7 @@ struct alc_spec { 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 @@ do_sku: 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 @@ do_sku: 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 @@ static void alc_auto_init_digital(struct hda_codec *codec) { 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 @@ static int add_control_with_pfx(struct alc_spec *spec, int type, #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 @@ static int alc880_auto_fill_dac_nids(struct alc_spec *spec, 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 @@ static const char *alc_get_line_out_pfx(struct alc_spec *spec, 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 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec, 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 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec, 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 @@ static int get_pin_type(int line_out_type) 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 @@ static void alc880_auto_init_multi_out(struct hda_codec *codec) 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 @@ static void alc880_auto_init_extra_out(struct hda_codec *codec) 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 @@ static void alc880_auto_init_analog_input(struct hda_codec *codec) 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 @@ static void alc880_auto_init_input_src(struct hda_codec *codec) 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 @@ static int alc880_parse_auto_config(struct hda_codec *codec) 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 @@ static int alc880_parse_auto_config(struct hda_codec *codec) 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 @@ static int patch_alc880(struct hda_codec *codec) 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 @@ static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec, 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_set_output_and_unmute(struct hda_codec *codec, } } +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 @@ static void alc260_auto_init_multi_out(struct hda_codec *codec) nid = spec->autocfg.hp_pins[0]; if (nid) alc260_auto_set_output_and_unmute(codec, nid, PIN_HP, 0); -} -#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); - } - } + /* 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_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 @@ static int alc260_parse_auto_config(struct hda_codec *codec) 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 @@ static int patch_alc260(struct hda_codec *codec) 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 @@ static void alc885_imac24_init_hook(struct hda_codec *codec) 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 @@ static void alc882_auto_set_output_and_unmute(struct hda_codec *codec, /* 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 @@ static void alc882_auto_set_output_and_unmute(struct hda_codec *codec, 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 @@ static void alc882_auto_init_multi_out(struct hda_codec *codec) 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,31 +11063,21 @@ static void alc882_auto_init_hp_out(struct hda_codec *codec) alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac); } } -} - -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); - } + /* 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]); } +#define alc882_auto_init_analog_input alc880_auto_init_analog_input + static void alc882_auto_init_input_src(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; 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 @@ static void alc882_auto_init_input_src(struct hda_codec *codec) 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 @@ static int alc882_parse_auto_config(struct hda_codec *codec) 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 @@ static int alc882_parse_auto_config(struct hda_codec *codec) 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 @@ static int patch_alc882(struct hda_codec *codec) codec->spec = spec; + spec->mixer_nid = 0x0b; + switch (codec->vendor_id) { case 0x10ec0882: case 0x10ec0885: @@ -11356,7 +11318,6 @@ static int patch_alc882(struct hda_codec *codec) 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 @@ static int patch_alc882(struct hda_codec *codec) 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 @@ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, { 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 @@ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, 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 @@ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, #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 @@ static const struct hda_verb alc262_toshiba_rx1_unsol_verbs[] = { */ enum { PINFIX_FSC_H270, + PINFIX_HP_Z200, }; static const struct alc_fixup alc262_fixups[] = { @@ -12612,9 +12511,17 @@ static const struct alc_fixup alc262_fixups[] = { { } } }, + [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 @@ static int alc262_parse_auto_config(struct hda_codec *codec) 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 @@ static const struct snd_pci_quirk alc262_cfg_tbl[] = { 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 @@ static int patch_alc262(struct hda_codec *codec) 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 @@ static const struct hda_verb alc268_base_init_verbs[] = { { } }; +/* only for model=test */ +#ifdef CONFIG_SND_DEBUG /* * generic initialization of ADC, input mixers and output mixers */ @@ -13460,12 +13373,15 @@ static const struct hda_verb alc268_volume_init_verbs[] = { {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 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec, 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 @@ static void alc268_auto_set_output_and_unmute(struct hda_codec *codec, 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 @@ static void alc268_auto_init_multi_out(struct hda_codec *codec) 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 @@ static void alc268_auto_init_hp_out(struct hda_codec *codec) 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 @@ static int alc268_parse_auto_config(struct hda_codec *codec) 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 @@ static const struct alc_config_preset alc268_presets[] = { [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 @@ static int patch_alc268(struct hda_codec *codec) 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 @@ static int alc269_parse_auto_config(struct hda_codec *codec) 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 @@ static int patch_alc269(struct hda_codec *codec) codec->spec = spec; + spec->mixer_nid = 0x0b; + alc_auto_parse_customize_define(codec); if (codec->vendor_id == 0x10ec0269) { @@ -15850,58 +15781,6 @@ static const struct hda_verb alc861_asus_laptop_init_verbs[] = { { } }; -/* - * 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 @@ static hda_nid_t alc861_look_for_dac(struct hda_codec *codec, hda_nid_t pin) { 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 @@ static hda_nid_t alc861_look_for_dac(struct hda_codec *codec, hda_nid_t pin) 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 @@ static int alc861_auto_create_multi_out_ctls(struct hda_codec *codec, 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 @@ static int alc861_auto_create_multi_out_ctls(struct hda_codec *codec, 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 @@ static int alc861_auto_create_multi_out_ctls(struct hda_codec *codec, 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 @@ static void alc861_auto_init_multi_out(struct hda_codec *codec) 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 @@ static void alc861_auto_init_hp_out(struct hda_codec *codec) 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 @@ static int alc861_parse_auto_config(struct hda_codec *codec) 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 @@ static int alc861_parse_auto_config(struct hda_codec *codec) 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 @@ static int patch_alc861(struct hda_codec *codec) 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 int alc861vd_auto_create_input_ctls(struct hda_codec *codec, } -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 void alc861vd_auto_init_analog_input(struct hda_codec *codec) 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 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec, 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 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec, 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 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec, 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 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec) 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 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec) 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 @@ static int patch_alc861vd(struct hda_codec *codec) 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 @@ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin) { 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 @@ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin) 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; + + /* 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++) { - dac = alc_auto_look_for_dac(codec, cfg->line_out_pins[i]); - if (!dac) + hda_nid_t pin = cfg->line_out_pins[i]; + if (spec->private_dac_nids[i]) continue; - spec->private_dac_nids[spec->multiout.num_dacs++] = dac; + 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 @@ static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec, 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 @@ static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec, 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 @@ static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec, 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 @@ static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec, 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 @@ static int alc662_auto_create_multi_out_ctls(struct hda_codec *codec, } /* 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 @@ static int alc662_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, 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 @@ static void alc662_auto_init_hp_out(struct hda_codec *codec) 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 @@ static const struct snd_kcontrol_new alc_auto_channel_mode_enum = { .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 @@ static int alc662_parse_auto_config(struct hda_codec *codec) 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 @@ static int alc662_parse_auto_config(struct hda_codec *codec) 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 @@ static int patch_alc662(struct hda_codec *codec) 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 @@ static int alc680_auto_create_multi_out_ctls(struct alc_spec *spec, 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 @@ static int alc680_parse_auto_config(struct hda_codec *codec) 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 @@ static int patch_alc680(struct hda_codec *codec) 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); |