Merge branch 'topic/hda' into for-linus
Takashi Iwai [Fri, 4 Dec 2009 15:22:45 +0000 (16:22 +0100)]
48 files changed:
Documentation/sound/alsa/ALSA-Configuration.txt
Documentation/sound/alsa/HD-Audio-Models.txt
include/sound/ak4113.h [new file with mode: 0644]
include/sound/ak4114.h
include/sound/ak4xxx-adda.h
sound/i2c/other/Makefile
sound/i2c/other/ak4113.c [new file with mode: 0644]
sound/i2c/other/ak4xxx-adda.c
sound/pci/hda/Kconfig
sound/pci/hda/hda_beep.c
sound/pci/hda/hda_beep.h
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h
sound/pci/hda/hda_eld.c
sound/pci/hda/hda_generic.c
sound/pci/hda/hda_hwdep.c
sound/pci/hda/hda_intel.c
sound/pci/hda/hda_local.h
sound/pci/hda/hda_proc.c
sound/pci/hda/patch_analog.c
sound/pci/hda/patch_ca0110.c
sound/pci/hda/patch_cirrus.c
sound/pci/hda/patch_conexant.c
sound/pci/hda/patch_intelhdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_sigmatel.c
sound/pci/hda/patch_via.c
sound/pci/ice1712/Makefile
sound/pci/ice1712/ice1712.c
sound/pci/ice1712/ice1712.h
sound/pci/ice1712/ice1724.c
sound/pci/ice1712/juli.c
sound/pci/ice1712/quartet.c [new file with mode: 0644]
sound/pci/ice1712/quartet.h [new file with mode: 0644]
sound/pci/oxygen/Makefile
sound/pci/oxygen/cs2000.h [new file with mode: 0644]
sound/pci/oxygen/hifier.c
sound/pci/oxygen/oxygen.c
sound/pci/oxygen/oxygen.h
sound/pci/oxygen/oxygen_lib.c
sound/pci/oxygen/oxygen_mixer.c
sound/pci/oxygen/oxygen_pcm.c
sound/pci/oxygen/virtuoso.c
sound/pci/oxygen/xonar.h [new file with mode: 0644]
sound/pci/oxygen/xonar_cs43xx.c [new file with mode: 0644]
sound/pci/oxygen/xonar_hdmi.c [new file with mode: 0644]
sound/pci/oxygen/xonar_lib.c [new file with mode: 0644]
sound/pci/oxygen/xonar_pcm179x.c [new file with mode: 0644]

index 50a5986..8923597 100644 (file)
@@ -798,6 +798,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
                setup before initializing the codecs.  This option is
                available only when CONFIG_SND_HDA_PATCH_LOADER=y is set.
                See HD-Audio.txt for details.
+    beep_mode  - Selects the beep registration mode (0=off, 1=on, 2=
+               dynamic registration via mute switch on/off); the default
+               value is set via CONFIG_SND_HDA_INPUT_BEEP_MODE kconfig.
     
     [Single (global) options]
     single_cmd  - Use single immediate commands to communicate with
index 4c7f9ae..9000cd8 100644 (file)
@@ -391,6 +391,7 @@ STAC92HD83*
   ref          Reference board
   mic-ref      Reference board with power management for ports
   dell-s14     Dell laptop
+  hp           HP laptops with (inverted) mute-LED
   auto         BIOS setup (default)
 
 STAC9872
diff --git a/include/sound/ak4113.h b/include/sound/ak4113.h
new file mode 100644 (file)
index 0000000..8988eda
--- /dev/null
@@ -0,0 +1,321 @@
+#ifndef __SOUND_AK4113_H
+#define __SOUND_AK4113_H
+
+/*
+ *  Routines for Asahi Kasei AK4113
+ *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>,
+ *  Copyright (c) by Pavel Hofman <pavel.hofman@ivitera.com>,
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program 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
+ *
+ */
+
+/* AK4113 registers */
+/* power down */
+#define AK4113_REG_PWRDN       0x00
+/* format control */
+#define AK4113_REG_FORMAT      0x01
+/* input/output control */
+#define AK4113_REG_IO0         0x02
+/* input/output control */
+#define AK4113_REG_IO1         0x03
+/* interrupt0 mask */
+#define AK4113_REG_INT0_MASK   0x04
+/* interrupt1 mask */
+#define AK4113_REG_INT1_MASK   0x05
+/* DAT mask & DTS select */
+#define AK4113_REG_DATDTS      0x06
+/* receiver status 0 */
+#define AK4113_REG_RCS0                0x07
+/* receiver status 1 */
+#define AK4113_REG_RCS1                0x08
+/* receiver status 2 */
+#define AK4113_REG_RCS2                0x09
+/* RX channel status byte 0 */
+#define AK4113_REG_RXCSB0      0x0a
+/* RX channel status byte 1 */
+#define AK4113_REG_RXCSB1      0x0b
+/* RX channel status byte 2 */
+#define AK4113_REG_RXCSB2      0x0c
+/* RX channel status byte 3 */
+#define AK4113_REG_RXCSB3      0x0d
+/* RX channel status byte 4 */
+#define AK4113_REG_RXCSB4      0x0e
+/* burst preamble Pc byte 0 */
+#define AK4113_REG_Pc0         0x0f
+/* burst preamble Pc byte 1 */
+#define AK4113_REG_Pc1         0x10
+/* burst preamble Pd byte 0 */
+#define AK4113_REG_Pd0         0x11
+/* burst preamble Pd byte 1 */
+#define AK4113_REG_Pd1         0x12
+/* Q-subcode address + control */
+#define AK4113_REG_QSUB_ADDR   0x13
+/* Q-subcode track */
+#define AK4113_REG_QSUB_TRACK  0x14
+/* Q-subcode index */
+#define AK4113_REG_QSUB_INDEX  0x15
+/* Q-subcode minute */
+#define AK4113_REG_QSUB_MINUTE 0x16
+/* Q-subcode second */
+#define AK4113_REG_QSUB_SECOND 0x17
+/* Q-subcode frame */
+#define AK4113_REG_QSUB_FRAME  0x18
+/* Q-subcode zero */
+#define AK4113_REG_QSUB_ZERO   0x19
+/* Q-subcode absolute minute */
+#define AK4113_REG_QSUB_ABSMIN 0x1a
+/* Q-subcode absolute second */
+#define AK4113_REG_QSUB_ABSSEC 0x1b
+/* Q-subcode absolute frame */
+#define AK4113_REG_QSUB_ABSFRM 0x1c
+
+/* sizes */
+#define AK4113_REG_RXCSB_SIZE  ((AK4113_REG_RXCSB4-AK4113_REG_RXCSB0)+1)
+#define AK4113_REG_QSUB_SIZE   ((AK4113_REG_QSUB_ABSFRM-AK4113_REG_QSUB_ADDR)\
+               +1)
+
+#define AK4113_WRITABLE_REGS   (AK4113_REG_DATDTS + 1)
+
+/* AK4113_REG_PWRDN bits */
+/* Channel Status Select */
+#define AK4113_CS12            (1<<7)
+/* Block Start & C/U Output Mode */
+#define AK4113_BCU             (1<<6)
+/* Master Clock Operation Select */
+#define AK4113_CM1             (1<<5)
+/* Master Clock Operation Select */
+#define AK4113_CM0             (1<<4)
+/* Master Clock Frequency Select */
+#define AK4113_OCKS1           (1<<3)
+/* Master Clock Frequency Select */
+#define AK4113_OCKS0           (1<<2)
+/* 0 = power down, 1 = normal operation */
+#define AK4113_PWN             (1<<1)
+/* 0 = reset & initialize (except thisregister), 1 = normal operation */
+#define AK4113_RST             (1<<0)
+
+/* AK4113_REQ_FORMAT bits */
+/* V/TX Output select: 0 = Validity Flag Output, 1 = TX */
+#define AK4113_VTX             (1<<7)
+/* Audio Data Control */
+#define AK4113_DIF2            (1<<6)
+/* Audio Data Control */
+#define AK4113_DIF1            (1<<5)
+/* Audio Data Control */
+#define AK4113_DIF0            (1<<4)
+/* Deemphasis Autodetect Enable (1 = enable) */
+#define AK4113_DEAU            (1<<3)
+/* 32kHz-48kHz Deemphasis Control */
+#define AK4113_DEM1            (1<<2)
+/* 32kHz-48kHz Deemphasis Control */
+#define AK4113_DEM0            (1<<1)
+#define AK4113_DEM_OFF         (AK4113_DEM0)
+#define AK4113_DEM_44KHZ       (0)
+#define AK4113_DEM_48KHZ       (AK4113_DEM1)
+#define AK4113_DEM_32KHZ       (AK4113_DEM0|AK4113_DEM1)
+/* STDO: 16-bit, right justified */
+#define AK4113_DIF_16R         (0)
+/* STDO: 18-bit, right justified */
+#define AK4113_DIF_18R         (AK4113_DIF0)
+/* STDO: 20-bit, right justified */
+#define AK4113_DIF_20R         (AK4113_DIF1)
+/* STDO: 24-bit, right justified */
+#define AK4113_DIF_24R         (AK4113_DIF1|AK4113_DIF0)
+/* STDO: 24-bit, left justified */
+#define AK4113_DIF_24L         (AK4113_DIF2)
+/* STDO: I2S */
+#define AK4113_DIF_24I2S       (AK4113_DIF2|AK4113_DIF0)
+/* STDO: 24-bit, left justified; LRCLK, BICK = Input */
+#define AK4113_DIF_I24L                (AK4113_DIF2|AK4113_DIF1)
+/* STDO: I2S;  LRCLK, BICK = Input */
+#define AK4113_DIF_I24I2S      (AK4113_DIF2|AK4113_DIF1|AK4113_DIF0)
+
+/* AK4113_REG_IO0 */
+/* XTL1=0,XTL0=0 -> 11.2896Mhz; XTL1=0,XTL0=1 -> 12.288Mhz */
+#define AK4113_XTL1            (1<<6)
+/* XTL1=1,XTL0=0 -> 24.576Mhz; XTL1=1,XTL0=1 -> use channel status */
+#define AK4113_XTL0            (1<<5)
+/* Block Start Signal Output: 0 = U-bit, 1 = C-bit (req. BCU = 1) */
+#define AK4113_UCE             (1<<4)
+/* TX Output Enable (1 = enable) */
+#define AK4113_TXE             (1<<3)
+/* Output Through Data Selector for TX pin */
+#define AK4113_OPS2            (1<<2)
+/* Output Through Data Selector for TX pin */
+#define AK4113_OPS1            (1<<1)
+/* Output Through Data Selector for TX pin */
+#define AK4113_OPS0            (1<<0)
+/* 11.2896 MHz ref. Xtal freq. */
+#define AK4113_XTL_11_2896M    (0)
+/* 12.288 MHz ref. Xtal freq. */
+#define AK4113_XTL_12_288M     (AK4113_XTL0)
+/* 24.576 MHz ref. Xtal freq. */
+#define AK4113_XTL_24_576M     (AK4113_XTL1)
+
+/* AK4113_REG_IO1 */
+/* Interrupt 0 pin Hold */
+#define AK4113_EFH1            (1<<7)
+/* Interrupt 0 pin Hold */
+#define AK4113_EFH0            (1<<6)
+#define AK4113_EFH_512LRCLK    (0)
+#define AK4113_EFH_1024LRCLK   (AK4113_EFH0)
+#define AK4113_EFH_2048LRCLK   (AK4113_EFH1)
+#define AK4113_EFH_4096LRCLK   (AK4113_EFH1|AK4113_EFH0)
+/* PLL Lock Time: 0 = 384/fs, 1 = 1/fs */
+#define AK4113_FAST            (1<<5)
+/* MCKO2 Output Select: 0 = CMx/OCKSx, 1 = Xtal */
+#define AK4113_XMCK            (1<<4)
+/* MCKO2 Output Freq. Select: 0 = x1, 1 = x0.5  (req. XMCK = 1) */
+#define AK4113_DIV             (1<<3)
+/* Input Recovery Data Select */
+#define AK4113_IPS2            (1<<2)
+/* Input Recovery Data Select */
+#define AK4113_IPS1            (1<<1)
+/* Input Recovery Data Select */
+#define AK4113_IPS0            (1<<0)
+#define AK4113_IPS(x)          ((x)&7)
+
+/* AK4113_REG_INT0_MASK && AK4113_REG_INT1_MASK*/
+/* mask enable for QINT bit */
+#define AK4113_MQI             (1<<7)
+/* mask enable for AUTO bit */
+#define AK4113_MAUT            (1<<6)
+/* mask enable for CINT bit */
+#define AK4113_MCIT            (1<<5)
+/* mask enable for UNLOCK bit */
+#define AK4113_MULK            (1<<4)
+/* mask enable for V bit */
+#define AK4113_V               (1<<3)
+/* mask enable for STC bit */
+#define AK4113_STC             (1<<2)
+/* mask enable for AUDN bit */
+#define AK4113_MAN             (1<<1)
+/* mask enable for PAR bit */
+#define AK4113_MPR             (1<<0)
+
+/* AK4113_REG_DATDTS */
+/* DAT Start ID Counter */
+#define AK4113_DCNT            (1<<4)
+/* DTS-CD 16-bit Sync Word Detect */
+#define AK4113_DTS16           (1<<3)
+/* DTS-CD 14-bit Sync Word Detect */
+#define AK4113_DTS14           (1<<2)
+/* mask enable for DAT bit (if 1, no INT1 effect */
+#define AK4113_MDAT1           (1<<1)
+/* mask enable for DAT bit (if 1, no INT0 effect */
+#define AK4113_MDAT0           (1<<0)
+
+/* AK4113_REG_RCS0 */
+/* Q-subcode buffer interrupt, 0 = no change, 1 = changed */
+#define AK4113_QINT            (1<<7)
+/* Non-PCM or DTS stream auto detection, 0 = no detect, 1 = detect */
+#define AK4113_AUTO            (1<<6)
+/* channel status buffer interrupt, 0 = no change, 1 = change */
+#define AK4113_CINT            (1<<5)
+/* PLL lock status, 0 = lock, 1 = unlock */
+#define AK4113_UNLCK           (1<<4)
+/* Validity bit, 0 = valid, 1 = invalid */
+#define AK4113_V               (1<<3)
+/* sampling frequency or Pre-emphasis change, 0 = no detect, 1 = detect */
+#define AK4113_STC             (1<<2)
+/* audio bit output, 0 = audio, 1 = non-audio */
+#define AK4113_AUDION          (1<<1)
+/* parity error or biphase error status, 0 = no error, 1 = error */
+#define AK4113_PAR             (1<<0)
+
+/* AK4113_REG_RCS1 */
+/* sampling frequency detection */
+#define AK4113_FS3             (1<<7)
+#define AK4113_FS2             (1<<6)
+#define AK4113_FS1             (1<<5)
+#define AK4113_FS0             (1<<4)
+/* Pre-emphasis detect, 0 = OFF, 1 = ON */
+#define AK4113_PEM             (1<<3)
+/* DAT Start ID Detect, 0 = no detect, 1 = detect */
+#define AK4113_DAT             (1<<2)
+/* DTS-CD bit audio stream detect, 0 = no detect, 1 = detect */
+#define AK4113_DTSCD           (1<<1)
+/* Non-PCM bit stream detection, 0 = no detect, 1 = detect */
+#define AK4113_NPCM            (1<<0)
+#define AK4113_FS_8000HZ       (AK4113_FS3|AK4113_FS0)
+#define AK4113_FS_11025HZ      (AK4113_FS2|AK4113_FS0)
+#define AK4113_FS_16000HZ      (AK4113_FS2|AK4113_FS1|AK4113_FS0)
+#define AK4113_FS_22050HZ      (AK4113_FS2)
+#define AK4113_FS_24000HZ      (AK4113_FS2|AK4113_FS1)
+#define AK4113_FS_32000HZ      (AK4113_FS1|AK4113_FS0)
+#define AK4113_FS_44100HZ      (0)
+#define AK4113_FS_48000HZ      (AK4113_FS1)
+#define AK4113_FS_64000HZ      (AK4113_FS3|AK4113_FS1|AK4113_FS0)
+#define AK4113_FS_88200HZ      (AK4113_FS3)
+#define AK4113_FS_96000HZ      (AK4113_FS3|AK4113_FS1)
+#define AK4113_FS_176400HZ     (AK4113_FS3|AK4113_FS2)
+#define AK4113_FS_192000HZ     (AK4113_FS3|AK4113_FS2|AK4113_FS1)
+
+/* AK4113_REG_RCS2 */
+/* CRC for Q-subcode, 0 = no error, 1 = error */
+#define AK4113_QCRC            (1<<1)
+/* CRC for channel status, 0 = no error, 1 = error */
+#define AK4113_CCRC            (1<<0)
+
+/* flags for snd_ak4113_check_rate_and_errors() */
+#define AK4113_CHECK_NO_STAT   (1<<0)  /* no statistics */
+#define AK4113_CHECK_NO_RATE   (1<<1)  /* no rate check */
+
+#define AK4113_CONTROLS                13
+
+typedef void (ak4113_write_t)(void *private_data, unsigned char addr,
+               unsigned char data);
+typedef unsigned char (ak4113_read_t)(void *private_data, unsigned char addr);
+
+struct ak4113 {
+       struct snd_card *card;
+       ak4113_write_t *write;
+       ak4113_read_t *read;
+       void *private_data;
+       unsigned int init:1;
+       spinlock_t lock;
+       unsigned char regmap[AK4113_WRITABLE_REGS];
+       struct snd_kcontrol *kctls[AK4113_CONTROLS];
+       struct snd_pcm_substream *substream;
+       unsigned long parity_errors;
+       unsigned long v_bit_errors;
+       unsigned long qcrc_errors;
+       unsigned long ccrc_errors;
+       unsigned char rcs0;
+       unsigned char rcs1;
+       unsigned char rcs2;
+       struct delayed_work work;
+       unsigned int check_flags;
+       void *change_callback_private;
+       void (*change_callback)(struct ak4113 *ak4113, unsigned char c0,
+                       unsigned char c1);
+};
+
+int snd_ak4113_create(struct snd_card *card, ak4113_read_t *read,
+               ak4113_write_t *write,
+               const unsigned char pgm[AK4113_WRITABLE_REGS],
+               void *private_data, struct ak4113 **r_ak4113);
+void snd_ak4113_reg_write(struct ak4113 *ak4113, unsigned char reg,
+               unsigned char mask, unsigned char val);
+void snd_ak4113_reinit(struct ak4113 *ak4113);
+int snd_ak4113_build(struct ak4113 *ak4113,
+               struct snd_pcm_substream *capture_substream);
+int snd_ak4113_external_rate(struct ak4113 *ak4113);
+int snd_ak4113_check_rate_and_errors(struct ak4113 *ak4113, unsigned int flags);
+
+#endif /* __SOUND_AK4113_H */
+
index d293d36..3ce69fd 100644 (file)
 
 /* AK4114_REG_IO0 */
 #define AK4114_TX1E            (1<<7)  /* TX1 Output Enable (1 = enable) */
-#define AK4114_OPS12           (1<<2)  /* Output Though Data Selector for TX1 pin */
-#define AK4114_OPS11           (1<<1)  /* Output Though Data Selector for TX1 pin */
-#define AK4114_OPS10           (1<<0)  /* Output Though Data Selector for TX1 pin */
+#define AK4114_OPS12           (1<<6)  /* Output Data Selector for TX1 pin */
+#define AK4114_OPS11           (1<<5)  /* Output Data Selector for TX1 pin */
+#define AK4114_OPS10           (1<<4)  /* Output Data Selector for TX1 pin */
 #define AK4114_TX0E            (1<<3)  /* TX0 Output Enable (1 = enable) */
-#define AK4114_OPS02           (1<<2)  /* Output Though Data Selector for TX0 pin */
-#define AK4114_OPS01           (1<<1)  /* Output Though Data Selector for TX0 pin */
-#define AK4114_OPS00           (1<<0)  /* Output Though Data Selector for TX0 pin */
+#define AK4114_OPS02           (1<<2)  /* Output Data Selector for TX0 pin */
+#define AK4114_OPS01           (1<<1)  /* Output Data Selector for TX0 pin */
+#define AK4114_OPS00           (1<<0)  /* Output Data Selector for TX0 pin */
 
 /* AK4114_REG_IO1 */
 #define AK4114_EFH1            (1<<7)  /* Interrupt 0 pin Hold */
index 891cf1a..030b87c 100644 (file)
@@ -68,7 +68,7 @@ struct snd_akm4xxx {
        enum {
                SND_AK4524, SND_AK4528, SND_AK4529,
                SND_AK4355, SND_AK4358, SND_AK4381,
-               SND_AK5365
+               SND_AK5365, SND_AK4620,
        } type;
 
        /* (array) information of combined codecs */
@@ -76,6 +76,9 @@ struct snd_akm4xxx {
        const struct snd_akm4xxx_adc_channel *adc_info;
 
        struct snd_ak4xxx_ops ops;
+       unsigned int num_chips;
+       unsigned int total_regs;
+       const char *name;
 };
 
 void snd_akm4xxx_write(struct snd_akm4xxx *ak, int chip, unsigned char reg,
index 703d954..2dad40f 100644 (file)
@@ -5,6 +5,7 @@
 
 snd-ak4114-objs := ak4114.o
 snd-ak4117-objs := ak4117.o
+snd-ak4113-objs := ak4113.o
 snd-ak4xxx-adda-objs := ak4xxx-adda.o
 snd-pt2258-objs := pt2258.o
 snd-tea575x-tuner-objs := tea575x-tuner.o
@@ -12,5 +13,5 @@ snd-tea575x-tuner-objs := tea575x-tuner.o
 # Module Dependency
 obj-$(CONFIG_SND_PDAUDIOCF) += snd-ak4117.o
 obj-$(CONFIG_SND_ICE1712) += snd-ak4xxx-adda.o
-obj-$(CONFIG_SND_ICE1724) += snd-ak4114.o snd-ak4xxx-adda.o snd-pt2258.o
+obj-$(CONFIG_SND_ICE1724) += snd-ak4114.o snd-ak4113.o snd-ak4xxx-adda.o snd-pt2258.o
 obj-$(CONFIG_SND_FM801_TEA575X) += snd-tea575x-tuner.o
diff --git a/sound/i2c/other/ak4113.c b/sound/i2c/other/ak4113.c
new file mode 100644 (file)
index 0000000..fff62cc
--- /dev/null
@@ -0,0 +1,639 @@
+/*
+ *  Routines for control of the AK4113 via I2C/4-wire serial interface
+ *  IEC958 (S/PDIF) receiver by Asahi Kasei
+ *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ *  Copyright (c) by Pavel Hofman <pavel.hofman@ivitera.com>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program 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/slab.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/ak4113.h>
+#include <sound/asoundef.h>
+#include <sound/info.h>
+
+MODULE_AUTHOR("Pavel Hofman <pavel.hofman@ivitera.com>");
+MODULE_DESCRIPTION("AK4113 IEC958 (S/PDIF) receiver by Asahi Kasei");
+MODULE_LICENSE("GPL");
+
+#define AK4113_ADDR                    0x00 /* fixed address */
+
+static void ak4113_stats(struct work_struct *work);
+static void ak4113_init_regs(struct ak4113 *chip);
+
+
+static void reg_write(struct ak4113 *ak4113, unsigned char reg,
+               unsigned char val)
+{
+       ak4113->write(ak4113->private_data, reg, val);
+       if (reg < sizeof(ak4113->regmap))
+               ak4113->regmap[reg] = val;
+}
+
+static inline unsigned char reg_read(struct ak4113 *ak4113, unsigned char reg)
+{
+       return ak4113->read(ak4113->private_data, reg);
+}
+
+static void snd_ak4113_free(struct ak4113 *chip)
+{
+       chip->init = 1; /* don't schedule new work */
+       mb();
+       cancel_delayed_work(&chip->work);
+       flush_scheduled_work();
+       kfree(chip);
+}
+
+static int snd_ak4113_dev_free(struct snd_device *device)
+{
+       struct ak4113 *chip = device->device_data;
+       snd_ak4113_free(chip);
+       return 0;
+}
+
+int snd_ak4113_create(struct snd_card *card, ak4113_read_t *read,
+               ak4113_write_t *write, const unsigned char pgm[5],
+               void *private_data, struct ak4113 **r_ak4113)
+{
+       struct ak4113 *chip;
+       int err = 0;
+       unsigned char reg;
+       static struct snd_device_ops ops = {
+               .dev_free =     snd_ak4113_dev_free,
+       };
+
+       chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+       if (chip == NULL)
+               return -ENOMEM;
+       spin_lock_init(&chip->lock);
+       chip->card = card;
+       chip->read = read;
+       chip->write = write;
+       chip->private_data = private_data;
+       INIT_DELAYED_WORK(&chip->work, ak4113_stats);
+
+       for (reg = 0; reg < AK4113_WRITABLE_REGS ; reg++)
+               chip->regmap[reg] = pgm[reg];
+       ak4113_init_regs(chip);
+
+       chip->rcs0 = reg_read(chip, AK4113_REG_RCS0) & ~(AK4113_QINT |
+                       AK4113_CINT | AK4113_STC);
+       chip->rcs1 = reg_read(chip, AK4113_REG_RCS1);
+       chip->rcs2 = reg_read(chip, AK4113_REG_RCS2);
+       err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+       if (err < 0)
+               goto __fail;
+
+       if (r_ak4113)
+               *r_ak4113 = chip;
+       return 0;
+
+__fail:
+       snd_ak4113_free(chip);
+       return err < 0 ? err : -EIO;
+}
+EXPORT_SYMBOL_GPL(snd_ak4113_create);
+
+void snd_ak4113_reg_write(struct ak4113 *chip, unsigned char reg,
+               unsigned char mask, unsigned char val)
+{
+       if (reg >= AK4113_WRITABLE_REGS)
+               return;
+       reg_write(chip, reg, (chip->regmap[reg] & ~mask) | val);
+}
+EXPORT_SYMBOL_GPL(snd_ak4113_reg_write);
+
+static void ak4113_init_regs(struct ak4113 *chip)
+{
+       unsigned char old = chip->regmap[AK4113_REG_PWRDN], reg;
+
+       /* bring the chip to reset state and powerdown state */
+       reg_write(chip, AK4113_REG_PWRDN, old & ~(AK4113_RST|AK4113_PWN));
+       udelay(200);
+       /* release reset, but leave powerdown */
+       reg_write(chip, AK4113_REG_PWRDN, (old | AK4113_RST) & ~AK4113_PWN);
+       udelay(200);
+       for (reg = 1; reg < AK4113_WRITABLE_REGS; reg++)
+               reg_write(chip, reg, chip->regmap[reg]);
+       /* release powerdown, everything is initialized now */
+       reg_write(chip, AK4113_REG_PWRDN, old | AK4113_RST | AK4113_PWN);
+}
+
+void snd_ak4113_reinit(struct ak4113 *chip)
+{
+       chip->init = 1;
+       mb();
+       flush_scheduled_work();
+       ak4113_init_regs(chip);
+       /* bring up statistics / event queing */
+       chip->init = 0;
+       if (chip->kctls[0])
+               schedule_delayed_work(&chip->work, HZ / 10);
+}
+EXPORT_SYMBOL_GPL(snd_ak4113_reinit);
+
+static unsigned int external_rate(unsigned char rcs1)
+{
+       switch (rcs1 & (AK4113_FS0|AK4113_FS1|AK4113_FS2|AK4113_FS3)) {
+       case AK4113_FS_8000HZ:
+               return 8000;
+       case AK4113_FS_11025HZ:
+               return 11025;
+       case AK4113_FS_16000HZ:
+               return 16000;
+       case AK4113_FS_22050HZ:
+               return 22050;
+       case AK4113_FS_24000HZ:
+               return 24000;
+       case AK4113_FS_32000HZ:
+               return 32000;
+       case AK4113_FS_44100HZ:
+               return 44100;
+       case AK4113_FS_48000HZ:
+               return 48000;
+       case AK4113_FS_64000HZ:
+               return 64000;
+       case AK4113_FS_88200HZ:
+               return 88200;
+       case AK4113_FS_96000HZ:
+               return 96000;
+       case AK4113_FS_176400HZ:
+               return 176400;
+       case AK4113_FS_192000HZ:
+               return 192000;
+       default:
+               return 0;
+       }
+}
+
+static int snd_ak4113_in_error_info(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = LONG_MAX;
+       return 0;
+}
+
+static int snd_ak4113_in_error_get(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+       long *ptr;
+
+       spin_lock_irq(&chip->lock);
+       ptr = (long *)(((char *)chip) + kcontrol->private_value);
+       ucontrol->value.integer.value[0] = *ptr;
+       *ptr = 0;
+       spin_unlock_irq(&chip->lock);
+       return 0;
+}
+
+#define snd_ak4113_in_bit_info         snd_ctl_boolean_mono_info
+
+static int snd_ak4113_in_bit_get(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+       unsigned char reg = kcontrol->private_value & 0xff;
+       unsigned char bit = (kcontrol->private_value >> 8) & 0xff;
+       unsigned char inv = (kcontrol->private_value >> 31) & 1;
+
+       ucontrol->value.integer.value[0] =
+               ((reg_read(chip, reg) & (1 << bit)) ? 1 : 0) ^ inv;
+       return 0;
+}
+
+static int snd_ak4113_rx_info(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 5;
+       return 0;
+}
+
+static int snd_ak4113_rx_get(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.integer.value[0] =
+               (AK4113_IPS(chip->regmap[AK4113_REG_IO1]));
+       return 0;
+}
+
+static int snd_ak4113_rx_put(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+       int change;
+       u8 old_val;
+
+       spin_lock_irq(&chip->lock);
+       old_val = chip->regmap[AK4113_REG_IO1];
+       change = ucontrol->value.integer.value[0] != AK4113_IPS(old_val);
+       if (change)
+               reg_write(chip, AK4113_REG_IO1,
+                               (old_val & (~AK4113_IPS(0xff))) |
+                               (AK4113_IPS(ucontrol->value.integer.value[0])));
+       spin_unlock_irq(&chip->lock);
+       return change;
+}
+
+static int snd_ak4113_rate_info(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 192000;
+       return 0;
+}
+
+static int snd_ak4113_rate_get(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.integer.value[0] = external_rate(reg_read(chip,
+                               AK4113_REG_RCS1));
+       return 0;
+}
+
+static int snd_ak4113_spdif_info(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+       uinfo->count = 1;
+       return 0;
+}
+
+static int snd_ak4113_spdif_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+       unsigned i;
+
+       for (i = 0; i < AK4113_REG_RXCSB_SIZE; i++)
+               ucontrol->value.iec958.status[i] = reg_read(chip,
+                               AK4113_REG_RXCSB0 + i);
+       return 0;
+}
+
+static int snd_ak4113_spdif_mask_info(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+       uinfo->count = 1;
+       return 0;
+}
+
+static int snd_ak4113_spdif_mask_get(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_value *ucontrol)
+{
+       memset(ucontrol->value.iec958.status, 0xff, AK4113_REG_RXCSB_SIZE);
+       return 0;
+}
+
+static int snd_ak4113_spdif_pinfo(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 0xffff;
+       uinfo->count = 4;
+       return 0;
+}
+
+static int snd_ak4113_spdif_pget(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+       unsigned short tmp;
+
+       ucontrol->value.integer.value[0] = 0xf8f2;
+       ucontrol->value.integer.value[1] = 0x4e1f;
+       tmp = reg_read(chip, AK4113_REG_Pc0) |
+               (reg_read(chip, AK4113_REG_Pc1) << 8);
+       ucontrol->value.integer.value[2] = tmp;
+       tmp = reg_read(chip, AK4113_REG_Pd0) |
+               (reg_read(chip, AK4113_REG_Pd1) << 8);
+       ucontrol->value.integer.value[3] = tmp;
+       return 0;
+}
+
+static int snd_ak4113_spdif_qinfo(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+       uinfo->count = AK4113_REG_QSUB_SIZE;
+       return 0;
+}
+
+static int snd_ak4113_spdif_qget(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct ak4113 *chip = snd_kcontrol_chip(kcontrol);
+       unsigned i;
+
+       for (i = 0; i < AK4113_REG_QSUB_SIZE; i++)
+               ucontrol->value.bytes.data[i] = reg_read(chip,
+                               AK4113_REG_QSUB_ADDR + i);
+       return 0;
+}
+
+/* Don't forget to change AK4113_CONTROLS define!!! */
+static struct snd_kcontrol_new snd_ak4113_iec958_controls[] = {
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         "IEC958 Parity Errors",
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ |
+               SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+       .info =         snd_ak4113_in_error_info,
+       .get =          snd_ak4113_in_error_get,
+       .private_value = offsetof(struct ak4113, parity_errors),
+},
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         "IEC958 V-Bit Errors",
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ |
+               SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+       .info =         snd_ak4113_in_error_info,
+       .get =          snd_ak4113_in_error_get,
+       .private_value = offsetof(struct ak4113, v_bit_errors),
+},
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         "IEC958 C-CRC Errors",
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ |
+               SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+       .info =         snd_ak4113_in_error_info,
+       .get =          snd_ak4113_in_error_get,
+       .private_value = offsetof(struct ak4113, ccrc_errors),
+},
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         "IEC958 Q-CRC Errors",
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ |
+               SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+       .info =         snd_ak4113_in_error_info,
+       .get =          snd_ak4113_in_error_get,
+       .private_value = offsetof(struct ak4113, qcrc_errors),
+},
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         "IEC958 External Rate",
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ |
+               SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+       .info =         snd_ak4113_rate_info,
+       .get =          snd_ak4113_rate_get,
+},
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK),
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ,
+       .info =         snd_ak4113_spdif_mask_info,
+       .get =          snd_ak4113_spdif_mask_get,
+},
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ |
+               SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+       .info =         snd_ak4113_spdif_info,
+       .get =          snd_ak4113_spdif_get,
+},
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         "IEC958 Preample Capture Default",
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ |
+               SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+       .info =         snd_ak4113_spdif_pinfo,
+       .get =          snd_ak4113_spdif_pget,
+},
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         "IEC958 Q-subcode Capture Default",
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ |
+               SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+       .info =         snd_ak4113_spdif_qinfo,
+       .get =          snd_ak4113_spdif_qget,
+},
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         "IEC958 Audio",
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ |
+               SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+       .info =         snd_ak4113_in_bit_info,
+       .get =          snd_ak4113_in_bit_get,
+       .private_value = (1<<31) | (1<<8) | AK4113_REG_RCS0,
+},
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         "IEC958 Non-PCM Bitstream",
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ |
+               SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+       .info =         snd_ak4113_in_bit_info,
+       .get =          snd_ak4113_in_bit_get,
+       .private_value = (0<<8) | AK4113_REG_RCS1,
+},
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         "IEC958 DTS Bitstream",
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ |
+               SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+       .info =         snd_ak4113_in_bit_info,
+       .get =          snd_ak4113_in_bit_get,
+       .private_value = (1<<8) | AK4113_REG_RCS1,
+},
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         "AK4113 Input Select",
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ |
+               SNDRV_CTL_ELEM_ACCESS_WRITE,
+       .info =         snd_ak4113_rx_info,
+       .get =          snd_ak4113_rx_get,
+       .put =          snd_ak4113_rx_put,
+}
+};
+
+static void snd_ak4113_proc_regs_read(struct snd_info_entry *entry,
+               struct snd_info_buffer *buffer)
+{
+       struct ak4113 *ak4113 = entry->private_data;
+       int reg, val;
+       /* all ak4113 registers 0x00 - 0x1c */
+       for (reg = 0; reg < 0x1d; reg++) {
+               val = reg_read(ak4113, reg);
+               snd_iprintf(buffer, "0x%02x = 0x%02x\n", reg, val);
+       }
+}
+
+static void snd_ak4113_proc_init(struct ak4113 *ak4113)
+{
+       struct snd_info_entry *entry;
+       if (!snd_card_proc_new(ak4113->card, "ak4113", &entry))
+               snd_info_set_text_ops(entry, ak4113, snd_ak4113_proc_regs_read);
+}
+
+int snd_ak4113_build(struct ak4113 *ak4113,
+               struct snd_pcm_substream *cap_substream)
+{
+       struct snd_kcontrol *kctl;
+       unsigned int idx;
+       int err;
+
+       if (snd_BUG_ON(!cap_substream))
+               return -EINVAL;
+       ak4113->substream = cap_substream;
+       for (idx = 0; idx < AK4113_CONTROLS; idx++) {
+               kctl = snd_ctl_new1(&snd_ak4113_iec958_controls[idx], ak4113);
+               if (kctl == NULL)
+                       return -ENOMEM;
+               kctl->id.device = cap_substream->pcm->device;
+               kctl->id.subdevice = cap_substream->number;
+               err = snd_ctl_add(ak4113->card, kctl);
+               if (err < 0)
+                       return err;
+               ak4113->kctls[idx] = kctl;
+       }
+       snd_ak4113_proc_init(ak4113);
+       /* trigger workq */
+       schedule_delayed_work(&ak4113->work, HZ / 10);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_ak4113_build);
+
+int snd_ak4113_external_rate(struct ak4113 *ak4113)
+{
+       unsigned char rcs1;
+
+       rcs1 = reg_read(ak4113, AK4113_REG_RCS1);
+       return external_rate(rcs1);
+}
+EXPORT_SYMBOL_GPL(snd_ak4113_external_rate);
+
+int snd_ak4113_check_rate_and_errors(struct ak4113 *ak4113, unsigned int flags)
+{
+       struct snd_pcm_runtime *runtime =
+               ak4113->substream ? ak4113->substream->runtime : NULL;
+       unsigned long _flags;
+       int res = 0;
+       unsigned char rcs0, rcs1, rcs2;
+       unsigned char c0, c1;
+
+       rcs1 = reg_read(ak4113, AK4113_REG_RCS1);
+       if (flags & AK4113_CHECK_NO_STAT)
+               goto __rate;
+       rcs0 = reg_read(ak4113, AK4113_REG_RCS0);
+       rcs2 = reg_read(ak4113, AK4113_REG_RCS2);
+       spin_lock_irqsave(&ak4113->lock, _flags);
+       if (rcs0 & AK4113_PAR)
+               ak4113->parity_errors++;
+       if (rcs0 & AK4113_V)
+               ak4113->v_bit_errors++;
+       if (rcs2 & AK4113_CCRC)
+               ak4113->ccrc_errors++;
+       if (rcs2 & AK4113_QCRC)
+               ak4113->qcrc_errors++;
+       c0 = (ak4113->rcs0 & (AK4113_QINT | AK4113_CINT | AK4113_STC |
+                               AK4113_AUDION | AK4113_AUTO | AK4113_UNLCK)) ^
+               (rcs0 & (AK4113_QINT | AK4113_CINT | AK4113_STC |
+                        AK4113_AUDION | AK4113_AUTO | AK4113_UNLCK));
+       c1 = (ak4113->rcs1 & (AK4113_DTSCD | AK4113_NPCM | AK4113_PEM |
+                               AK4113_DAT | 0xf0)) ^
+               (rcs1 & (AK4113_DTSCD | AK4113_NPCM | AK4113_PEM |
+                        AK4113_DAT | 0xf0));
+       ak4113->rcs0 = rcs0 & ~(AK4113_QINT | AK4113_CINT | AK4113_STC);
+       ak4113->rcs1 = rcs1;
+       ak4113->rcs2 = rcs2;
+       spin_unlock_irqrestore(&ak4113->lock, _flags);
+
+       if (rcs0 & AK4113_PAR)
+               snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                               &ak4113->kctls[0]->id);
+       if (rcs0 & AK4113_V)
+               snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                               &ak4113->kctls[1]->id);
+       if (rcs2 & AK4113_CCRC)
+               snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                               &ak4113->kctls[2]->id);
+       if (rcs2 & AK4113_QCRC)
+               snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                               &ak4113->kctls[3]->id);
+
+       /* rate change */
+       if (c1 & 0xf0)
+               snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                               &ak4113->kctls[4]->id);
+
+       if ((c1 & AK4113_PEM) | (c0 & AK4113_CINT))
+               snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                               &ak4113->kctls[6]->id);
+       if (c0 & AK4113_QINT)
+               snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                               &ak4113->kctls[8]->id);
+
+       if (c0 & AK4113_AUDION)
+               snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                               &ak4113->kctls[9]->id);
+       if (c1 & AK4113_NPCM)
+               snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                               &ak4113->kctls[10]->id);
+       if (c1 & AK4113_DTSCD)
+               snd_ctl_notify(ak4113->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                               &ak4113->kctls[11]->id);
+
+       if (ak4113->change_callback && (c0 | c1) != 0)
+               ak4113->change_callback(ak4113, c0, c1);
+
+__rate:
+       /* compare rate */
+       res = external_rate(rcs1);
+       if (!(flags & AK4113_CHECK_NO_RATE) && runtime &&
+                       (runtime->rate != res)) {
+               snd_pcm_stream_lock_irqsave(ak4113->substream, _flags);
+               if (snd_pcm_running(ak4113->substream)) {
+                       /*printk(KERN_DEBUG "rate changed (%i <- %i)\n",
+                        * runtime->rate, res); */
+                       snd_pcm_stop(ak4113->substream,
+                                       SNDRV_PCM_STATE_DRAINING);
+                       wake_up(&runtime->sleep);
+                       res = 1;
+               }
+               snd_pcm_stream_unlock_irqrestore(ak4113->substream, _flags);
+       }
+       return res;
+}
+EXPORT_SYMBOL_GPL(snd_ak4113_check_rate_and_errors);
+
+static void ak4113_stats(struct work_struct *work)
+{
+       struct ak4113 *chip = container_of(work, struct ak4113, work.work);
+
+       if (!chip->init)
+               snd_ak4113_check_rate_and_errors(chip, chip->check_flags);
+
+       schedule_delayed_work(&chip->work, HZ / 10);
+}
index ee47aba..1adb8a3 100644 (file)
@@ -19,7 +19,7 @@
  *   along with this program; if not, write to the Free Software
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  *
- */      
+ */
 
 #include <asm/io.h>
 #include <linux/delay.h>
@@ -29,6 +29,7 @@
 #include <sound/control.h>
 #include <sound/tlv.h>
 #include <sound/ak4xxx-adda.h>
+#include <sound/info.h>
 
 MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.de>");
 MODULE_DESCRIPTION("Routines for control of AK452x / AK43xx  AD/DA converters");
@@ -52,26 +53,21 @@ EXPORT_SYMBOL(snd_akm4xxx_write);
 static void ak4524_reset(struct snd_akm4xxx *ak, int state)
 {
        unsigned int chip;
-       unsigned char reg, maxreg;
+       unsigned char reg;
 
-       if (ak->type == SND_AK4528)
-               maxreg = 0x06;
-       else
-               maxreg = 0x08;
        for (chip = 0; chip < ak->num_dacs/2; chip++) {
                snd_akm4xxx_write(ak, chip, 0x01, state ? 0x00 : 0x03);
                if (state)
                        continue;
                /* DAC volumes */
-               for (reg = 0x04; reg < maxreg; reg++)
+               for (reg = 0x04; reg < ak->total_regs; reg++)
                        snd_akm4xxx_write(ak, chip, reg,
                                          snd_akm4xxx_get(ak, chip, reg));
        }
 }
 
 /* reset procedure for AK4355 and AK4358 */
-static void ak435X_reset(struct snd_akm4xxx *ak, int state,
-               unsigned char total_regs)
+static void ak435X_reset(struct snd_akm4xxx *ak, int state)
 {
        unsigned char reg;
 
@@ -79,7 +75,7 @@ static void ak435X_reset(struct snd_akm4xxx *ak, int state,
                snd_akm4xxx_write(ak, 0, 0x01, 0x02); /* reset and soft-mute */
                return;
        }
-       for (reg = 0x00; reg < total_regs; reg++)
+       for (reg = 0x00; reg < ak->total_regs; reg++)
                if (reg != 0x01)
                        snd_akm4xxx_write(ak, 0, reg,
                                          snd_akm4xxx_get(ak, 0, reg));
@@ -91,12 +87,11 @@ static void ak4381_reset(struct snd_akm4xxx *ak, int state)
 {
        unsigned int chip;
        unsigned char reg;
-
        for (chip = 0; chip < ak->num_dacs/2; chip++) {
                snd_akm4xxx_write(ak, chip, 0x00, state ? 0x0c : 0x0f);
                if (state)
                        continue;
-               for (reg = 0x01; reg < 0x05; reg++)
+               for (reg = 0x01; reg < ak->total_regs; reg++)
                        snd_akm4xxx_write(ak, chip, reg,
                                          snd_akm4xxx_get(ak, chip, reg));
        }
@@ -113,16 +108,17 @@ void snd_akm4xxx_reset(struct snd_akm4xxx *ak, int state)
        switch (ak->type) {
        case SND_AK4524:
        case SND_AK4528:
+       case SND_AK4620:
                ak4524_reset(ak, state);
                break;
        case SND_AK4529:
                /* FIXME: needed for ak4529? */
                break;
        case SND_AK4355:
-               ak435X_reset(ak, state, 0x0b);
+               ak435X_reset(ak, state);
                break;
        case SND_AK4358:
-               ak435X_reset(ak, state, 0x10);
+               ak435X_reset(ak, state);
                break;
        case SND_AK4381:
                ak4381_reset(ak, state);
@@ -139,7 +135,7 @@ EXPORT_SYMBOL(snd_akm4xxx_reset);
  * Volume conversion table for non-linear volumes
  * from -63.5dB (mute) to 0dB step 0.5dB
  *
- * Used for AK4524 input/ouput attenuation, AK4528, and
+ * Used for AK4524/AK4620 input/ouput attenuation, AK4528, and
  * AK5365 input attenuation
  */
 static const unsigned char vol_cvt_datt[128] = {
@@ -259,8 +255,22 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
                0x00, 0x0f, /* 0: power-up, un-reset */
                0xff, 0xff
        };
+       static const unsigned char inits_ak4620[] = {
+               0x00, 0x07, /* 0: normal */
+               0x01, 0x00, /* 0: reset */
+               0x01, 0x02, /* 1: RSTAD */
+               0x01, 0x03, /* 1: RSTDA */
+               0x01, 0x0f, /* 1: normal */
+               0x02, 0x60, /* 2: 24bit I2S */
+               0x03, 0x01, /* 3: deemphasis off */
+               0x04, 0x00, /* 4: LIN muted */
+               0x05, 0x00, /* 5: RIN muted */
+               0x06, 0x00, /* 6: LOUT muted */
+               0x07, 0x00, /* 7: ROUT muted */
+               0xff, 0xff
+       };
 
-       int chip, num_chips;
+       int chip;
        const unsigned char *ptr, *inits;
        unsigned char reg, data;
 
@@ -270,42 +280,64 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
        switch (ak->type) {
        case SND_AK4524:
                inits = inits_ak4524;
-               num_chips = ak->num_dacs / 2;
+               ak->num_chips = ak->num_dacs / 2;
+               ak->name = "ak4524";
+               ak->total_regs = 0x08;
                break;
        case SND_AK4528:
                inits = inits_ak4528;
-               num_chips = ak->num_dacs / 2;
+               ak->num_chips = ak->num_dacs / 2;
+               ak->name = "ak4528";
+               ak->total_regs = 0x06;
                break;
        case SND_AK4529:
                inits = inits_ak4529;
-               num_chips = 1;
+               ak->num_chips = 1;
+               ak->name = "ak4529";
+               ak->total_regs = 0x0d;
                break;
        case SND_AK4355:
                inits = inits_ak4355;
-               num_chips = 1;
+               ak->num_chips = 1;
+               ak->name = "ak4355";
+               ak->total_regs = 0x0b;
                break;
        case SND_AK4358:
                inits = inits_ak4358;
-               num_chips = 1;
+               ak->num_chips = 1;
+               ak->name = "ak4358";
+               ak->total_regs = 0x10;
                break;
        case SND_AK4381:
                inits = inits_ak4381;
-               num_chips = ak->num_dacs / 2;
+               ak->num_chips = ak->num_dacs / 2;
+               ak->name = "ak4381";
+               ak->total_regs = 0x05;
                break;
        case SND_AK5365:
                /* FIXME: any init sequence? */
+               ak->num_chips = 1;
+               ak->name = "ak5365";
+               ak->total_regs = 0x08;
                return;
+       case SND_AK4620:
+               inits = inits_ak4620;
+               ak->num_chips = ak->num_dacs / 2;
+               ak->name = "ak4620";
+               ak->total_regs = 0x08;
+               break;
        default:
                snd_BUG();
                return;
        }
 
-       for (chip = 0; chip < num_chips; chip++) {
+       for (chip = 0; chip < ak->num_chips; chip++) {
                ptr = inits;
                while (*ptr != 0xff) {
                        reg = *ptr++;
                        data = *ptr++;
                        snd_akm4xxx_write(ak, chip, reg, data);
+                       udelay(10);
                }
        }
 }
@@ -688,6 +720,12 @@ static int build_dac_controls(struct snd_akm4xxx *ak)
                                AK_COMPOSE(idx/2, (idx%2) + 3, 0, 255);
                        knew.tlv.p = db_scale_linear;
                        break;
+               case SND_AK4620:
+                       /* register 6 & 7 */
+                       knew.private_value =
+                               AK_COMPOSE(idx/2, (idx%2) + 6, 0, 255);
+                       knew.tlv.p = db_scale_linear;
+                       break;
                default:
                        return -EINVAL;
                }
@@ -704,10 +742,12 @@ static int build_dac_controls(struct snd_akm4xxx *ak)
 
 static int build_adc_controls(struct snd_akm4xxx *ak)
 {
-       int idx, err, mixer_ch, num_stereo;
+       int idx, err, mixer_ch, num_stereo, max_steps;
        struct snd_kcontrol_new knew;
 
        mixer_ch = 0;
+       if (ak->type == SND_AK4528)
+               return 0;       /* no controls */
        for (idx = 0; idx < ak->num_adcs;) {
                memset(&knew, 0, sizeof(knew));
                if (! ak->adc_info || ! ak->adc_info[mixer_ch].name) {
@@ -733,13 +773,12 @@ static int build_adc_controls(struct snd_akm4xxx *ak)
                }
                /* register 4 & 5 */
                if (ak->type == SND_AK5365)
-                       knew.private_value =
-                               AK_COMPOSE(idx/2, (idx%2) + 4, 0, 151) |
-                               AK_VOL_CVT | AK_IPGA;
+                       max_steps = 152;
                else
-                       knew.private_value =
-                               AK_COMPOSE(idx/2, (idx%2) + 4, 0, 163) |
-                               AK_VOL_CVT | AK_IPGA;
+                       max_steps = 164;
+               knew.private_value =
+                       AK_COMPOSE(idx/2, (idx%2) + 4, 0, max_steps) |
+                       AK_VOL_CVT | AK_IPGA;
                knew.tlv.p = db_scale_vol_datt;
                err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
                if (err < 0)
@@ -808,6 +847,7 @@ static int build_deemphasis(struct snd_akm4xxx *ak, int num_emphs)
                switch (ak->type) {
                case SND_AK4524:
                case SND_AK4528:
+               case SND_AK4620:
                        /* register 3 */
                        knew.private_value = AK_COMPOSE(idx, 3, 0, 0);
                        break;
@@ -834,6 +874,35 @@ static int build_deemphasis(struct snd_akm4xxx *ak, int num_emphs)
        return 0;
 }
 
+#ifdef CONFIG_PROC_FS
+static void proc_regs_read(struct snd_info_entry *entry,
+               struct snd_info_buffer *buffer)
+{
+       struct snd_akm4xxx *ak = (struct snd_akm4xxx *)entry->private_data;
+       int reg, val, chip;
+       for (chip = 0; chip < ak->num_chips; chip++) {
+               for (reg = 0; reg < ak->total_regs; reg++) {
+                       val =  snd_akm4xxx_get(ak, chip, reg);
+                       snd_iprintf(buffer, "chip %d: 0x%02x = 0x%02x\n", chip,
+                                       reg, val);
+               }
+       }
+}
+
+static int proc_init(struct snd_akm4xxx *ak)
+{
+       struct snd_info_entry *entry;
+       int err;
+       err = snd_card_proc_new(ak->card, ak->name, &entry);
+       if (err < 0)
+               return err;
+       snd_info_set_text_ops(entry, ak, proc_regs_read);
+       return 0;
+}
+#else /* !CONFIG_PROC_FS */
+static int proc_init(struct snd_akm4xxx *ak) {}
+#endif
+
 int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
 {
        int err, num_emphs;
@@ -845,18 +914,21 @@ int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
        err = build_adc_controls(ak);
        if (err < 0)
                return err;
-
        if (ak->type == SND_AK4355 || ak->type == SND_AK4358)
                num_emphs = 1;
+       else if (ak->type == SND_AK4620)
+               num_emphs = 0;
        else
                num_emphs = ak->num_dacs / 2;
        err = build_deemphasis(ak, num_emphs);
        if (err < 0)
                return err;
+       err = proc_init(ak);
+       if (err < 0)
+               return err;
 
        return 0;
 }
-       
 EXPORT_SYMBOL(snd_akm4xxx_build_controls);
 
 static int __init alsa_akm4xxx_module_init(void)
index 55545e0..556cff9 100644 (file)
@@ -38,9 +38,20 @@ config SND_HDA_INPUT_BEEP
          Say Y here to build a digital beep interface for HD-audio
          driver. This interface is used to generate digital beeps.
 
+config SND_HDA_INPUT_BEEP_MODE
+       int "Digital beep registration mode (0=off, 1=on, 2=mute sw on/off)"
+       depends on SND_HDA_INPUT_BEEP=y
+       default "1"
+       range 0 2
+       help
+         Set 0 to disable the digital beep interface for HD-audio by default.
+         Set 1 to always enable the digital beep interface for HD-audio by
+         default. Set 2 to control the beep device registration to input
+         layer using a "Beep Switch" in mixer applications.
+
 config SND_HDA_INPUT_JACK
        bool "Support jack plugging notification via input layer"
-       depends on INPUT=y || INPUT=SND_HDA_INTEL
+       depends on INPUT=y || INPUT=SND
        select SND_JACK
        help
          Say Y here to enable the jack plugging notification via
index 3f51a98..5fe34a8 100644 (file)
@@ -113,23 +113,25 @@ static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
        return 0;
 }
 
-int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
+static void snd_hda_do_detach(struct hda_beep *beep)
+{
+       input_unregister_device(beep->dev);
+       beep->dev = NULL;
+       cancel_work_sync(&beep->beep_work);
+       /* turn off beep for sure */
+       snd_hda_codec_write_cache(beep->codec, beep->nid, 0,
+                                 AC_VERB_SET_BEEP_CONTROL, 0);
+}
+
+static int snd_hda_do_attach(struct hda_beep *beep)
 {
        struct input_dev *input_dev;
-       struct hda_beep *beep;
+       struct hda_codec *codec = beep->codec;
        int err;
 
-       if (!snd_hda_get_bool_hint(codec, "beep"))
-               return 0; /* disabled explicitly */
-
-       beep = kzalloc(sizeof(*beep), GFP_KERNEL);
-       if (beep == NULL)
-               return -ENOMEM;
-       snprintf(beep->phys, sizeof(beep->phys),
-               "card%d/codec#%d/beep0", codec->bus->card->number, codec->addr);
        input_dev = input_allocate_device();
        if (!input_dev) {
-               kfree(beep);
+               printk(KERN_INFO "hda_beep: unable to allocate input device\n");
                return -ENOMEM;
        }
 
@@ -151,21 +153,96 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
        err = input_register_device(input_dev);
        if (err < 0) {
                input_free_device(input_dev);
-               kfree(beep);
+               printk(KERN_INFO "hda_beep: unable to register input device\n");
                return err;
        }
+       beep->dev = input_dev;
+       return 0;
+}
+
+static void snd_hda_do_register(struct work_struct *work)
+{
+       struct hda_beep *beep =
+               container_of(work, struct hda_beep, register_work);
+
+       mutex_lock(&beep->mutex);
+       if (beep->enabled && !beep->dev)
+               snd_hda_do_attach(beep);
+       mutex_unlock(&beep->mutex);
+}
+
+static void snd_hda_do_unregister(struct work_struct *work)
+{
+       struct hda_beep *beep =
+               container_of(work, struct hda_beep, unregister_work.work);
+
+       mutex_lock(&beep->mutex);
+       if (!beep->enabled && beep->dev)
+               snd_hda_do_detach(beep);
+       mutex_unlock(&beep->mutex);
+}
 
+int snd_hda_enable_beep_device(struct hda_codec *codec, int enable)
+{
+       struct hda_beep *beep = codec->beep;
+       enable = !!enable;
+       if (beep == NULL)
+               return 0;
+       if (beep->enabled != enable) {
+               beep->enabled = enable;
+               if (!enable) {
+                       /* turn off beep */
+                       snd_hda_codec_write_cache(beep->codec, beep->nid, 0,
+                                                 AC_VERB_SET_BEEP_CONTROL, 0);
+               }
+               if (beep->mode == HDA_BEEP_MODE_SWREG) {
+                       if (enable) {
+                               cancel_delayed_work(&beep->unregister_work);
+                               schedule_work(&beep->register_work);
+                       } else {
+                               schedule_delayed_work(&beep->unregister_work,
+                                                                          HZ);
+                       }
+               }
+               return 1;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_enable_beep_device);
+
+int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
+{
+       struct hda_beep *beep;
+
+       if (!snd_hda_get_bool_hint(codec, "beep"))
+               return 0; /* disabled explicitly by hints */
+       if (codec->beep_mode == HDA_BEEP_MODE_OFF)
+               return 0; /* disabled by module option */
+
+       beep = kzalloc(sizeof(*beep), GFP_KERNEL);
+       if (beep == NULL)
+               return -ENOMEM;
+       snprintf(beep->phys, sizeof(beep->phys),
+               "card%d/codec#%d/beep0", codec->bus->card->number, codec->addr);
        /* enable linear scale */
        snd_hda_codec_write(codec, nid, 0,
                AC_VERB_SET_DIGI_CONVERT_2, 0x01);
 
        beep->nid = nid;
-       beep->dev = input_dev;
        beep->codec = codec;
-       beep->enabled = 1;
+       beep->mode = codec->beep_mode;
        codec->beep = beep;
 
+       INIT_WORK(&beep->register_work, &snd_hda_do_register);
+       INIT_DELAYED_WORK(&beep->unregister_work, &snd_hda_do_unregister);
        INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
+       mutex_init(&beep->mutex);
+
+       if (beep->mode == HDA_BEEP_MODE_ON) {
+               beep->enabled = 1;
+               snd_hda_do_register(&beep->register_work);
+       }
+
        return 0;
 }
 EXPORT_SYMBOL_HDA(snd_hda_attach_beep_device);
@@ -174,11 +251,12 @@ void snd_hda_detach_beep_device(struct hda_codec *codec)
 {
        struct hda_beep *beep = codec->beep;
        if (beep) {
-               cancel_work_sync(&beep->beep_work);
-
-               input_unregister_device(beep->dev);
-               kfree(beep);
+               cancel_work_sync(&beep->register_work);
+               cancel_delayed_work(&beep->unregister_work);
+               if (beep->enabled)
+                       snd_hda_do_detach(beep);
                codec->beep = NULL;
+               kfree(beep);
        }
 }
 EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device);
index 0c3de78..f1de1ba 100644 (file)
 
 #include "hda_codec.h"
 
+#define HDA_BEEP_MODE_OFF      0
+#define HDA_BEEP_MODE_ON       1
+#define HDA_BEEP_MODE_SWREG    2
+
 /* beep information */
 struct hda_beep {
        struct input_dev *dev;
        struct hda_codec *codec;
+       unsigned int mode;
        char phys[32];
        int tone;
        hda_nid_t nid;
        unsigned int enabled:1;
+       unsigned int request_enable:1;
        unsigned int linear_tone:1;     /* linear tone for IDT/STAC codec */
+       struct work_struct register_work; /* registration work */
+       struct delayed_work unregister_work; /* unregistration work */
        struct work_struct beep_work; /* scheduled task for beep event */
+       struct mutex mutex;
 };
 
 #ifdef CONFIG_SND_HDA_INPUT_BEEP
+int snd_hda_enable_beep_device(struct hda_codec *codec, int enable);
 int snd_hda_attach_beep_device(struct hda_codec *codec, int nid);
 void snd_hda_detach_beep_device(struct hda_codec *codec);
 #else
index af989f6..9cfdb77 100644 (file)
@@ -30,6 +30,7 @@
 #include <sound/tlv.h>
 #include <sound/initval.h>
 #include "hda_local.h"
+#include "hda_beep.h"
 #include <sound/hda_hwdep.h>
 
 /*
@@ -93,6 +94,13 @@ static void hda_keep_power_on(struct hda_codec *codec);
 static inline void hda_keep_power_on(struct hda_codec *codec) {}
 #endif
 
+/**
+ * snd_hda_get_jack_location - Give a location string of the jack
+ * @cfg: pin default config value
+ *
+ * Parse the pin default config value and returns the string of the
+ * jack location, e.g. "Rear", "Front", etc.
+ */
 const char *snd_hda_get_jack_location(u32 cfg)
 {
        static char *bases[7] = {
@@ -120,6 +128,13 @@ const char *snd_hda_get_jack_location(u32 cfg)
 }
 EXPORT_SYMBOL_HDA(snd_hda_get_jack_location);
 
+/**
+ * snd_hda_get_jack_connectivity - Give a connectivity string of the jack
+ * @cfg: pin default config value
+ *
+ * Parse the pin default config value and returns the string of the
+ * jack connectivity, i.e. external or internal connection.
+ */
 const char *snd_hda_get_jack_connectivity(u32 cfg)
 {
        static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" };
@@ -128,6 +143,13 @@ const char *snd_hda_get_jack_connectivity(u32 cfg)
 }
 EXPORT_SYMBOL_HDA(snd_hda_get_jack_connectivity);
 
+/**
+ * snd_hda_get_jack_type - Give a type string of the jack
+ * @cfg: pin default config value
+ *
+ * Parse the pin default config value and returns the string of the
+ * jack type, i.e. the purpose of the jack, such as Line-Out or CD.
+ */
 const char *snd_hda_get_jack_type(u32 cfg)
 {
        static char *jack_types[16] = {
@@ -515,6 +537,7 @@ static int snd_hda_bus_dev_register(struct snd_device *device)
        struct hda_codec *codec;
        list_for_each_entry(codec, &bus->codec_list, list) {
                snd_hda_hwdep_add_sysfs(codec);
+               snd_hda_hwdep_add_power_sysfs(codec);
        }
        return 0;
 }
@@ -820,6 +843,16 @@ int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
        return 0;
 }
 
+/**
+ * snd_hda_codec_set_pincfg - Override a pin default configuration
+ * @codec: the HDA codec
+ * @nid: NID to set the pin config
+ * @cfg: the pin default config value
+ *
+ * Override a pin default configuration value in the cache.
+ * This value can be read by snd_hda_codec_get_pincfg() in a higher
+ * priority than the real hardware value.
+ */
 int snd_hda_codec_set_pincfg(struct hda_codec *codec,
                             hda_nid_t nid, unsigned int cfg)
 {
@@ -827,7 +860,15 @@ int snd_hda_codec_set_pincfg(struct hda_codec *codec,
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_set_pincfg);
 
-/* get the current pin config value of the given pin NID */
+/**
+ * snd_hda_codec_get_pincfg - Obtain a pin-default configuration
+ * @codec: the HDA codec
+ * @nid: NID to get the pin config
+ *
+ * Get the current pin config value of the given pin NID.
+ * If the pincfg value is cached or overridden via sysfs or driver,
+ * returns the cached value.
+ */
 unsigned int snd_hda_codec_get_pincfg(struct hda_codec *codec, hda_nid_t nid)
 {
        struct hda_pincfg *pin;
@@ -944,7 +985,7 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
        mutex_init(&codec->control_mutex);
        init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
        init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
-       snd_array_init(&codec->mixers, sizeof(struct snd_kcontrol *), 32);
+       snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 60);
        snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16);
        snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16);
        if (codec->bus->modelname) {
@@ -1026,6 +1067,15 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_new);
 
+/**
+ * snd_hda_codec_configure - (Re-)configure the HD-audio codec
+ * @codec: the HDA codec
+ *
+ * Start parsing of the given codec tree and (re-)initialize the whole
+ * patch instance.
+ *
+ * Returns 0 if successful or a negative error code.
+ */
 int snd_hda_codec_configure(struct hda_codec *codec)
 {
        int err;
@@ -1088,6 +1138,11 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream);
 
+/**
+ * snd_hda_codec_cleanup_stream - clean up the codec for closing
+ * @codec: the CODEC to clean up
+ * @nid: the NID to clean up
+ */
 void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
 {
        if (!nid)
@@ -1163,8 +1218,17 @@ get_alloc_amp_hash(struct hda_codec *codec, u32 key)
        return (struct hda_amp_info *)get_alloc_hash(&codec->amp_cache, key);
 }
 
-/*
- * query AMP capabilities for the given widget and direction
+/**
+ * query_amp_caps - query AMP capabilities
+ * @codec: the HD-auio codec
+ * @nid: the NID to query
+ * @direction: either #HDA_INPUT or #HDA_OUTPUT
+ *
+ * Query AMP capabilities for the given widget and direction.
+ * Returns the obtained capability bits.
+ *
+ * When cap bits have been already read, this doesn't read again but
+ * returns the cached value.
  */
 u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
 {
@@ -1187,6 +1251,19 @@ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
 }
 EXPORT_SYMBOL_HDA(query_amp_caps);
 
+/**
+ * snd_hda_override_amp_caps - Override the AMP capabilities
+ * @codec: the CODEC to clean up
+ * @nid: the NID to clean up
+ * @direction: either #HDA_INPUT or #HDA_OUTPUT
+ * @caps: the capability bits to set
+ *
+ * Override the cached AMP caps bits value by the given one.
+ * This function is useful if the driver needs to adjust the AMP ranges,
+ * e.g. limit to 0dB, etc.
+ *
+ * Returns zero if successful or a negative error code.
+ */
 int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
                              unsigned int caps)
 {
@@ -1222,6 +1299,17 @@ static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid)
        return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
 }
 
+/**
+ * snd_hda_query_pin_caps - Query PIN capabilities
+ * @codec: the HD-auio codec
+ * @nid: the NID to query
+ *
+ * Query PIN capabilities for the given widget.
+ * Returns the obtained capability bits.
+ *
+ * When cap bits have been already read, this doesn't read again but
+ * returns the cached value.
+ */
 u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
 {
        return query_caps_hash(codec, nid, HDA_HASH_PINCAP_KEY(nid),
@@ -1229,6 +1317,40 @@ u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
 }
 EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps);
 
+/**
+ * snd_hda_pin_sense - execute pin sense measurement
+ * @codec: the CODEC to sense
+ * @nid: the pin NID to sense
+ *
+ * Execute necessary pin sense measurement and return its Presence Detect,
+ * Impedance, ELD Valid etc. status bits.
+ */
+u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid)
+{
+       u32 pincap = snd_hda_query_pin_caps(codec, nid);
+
+       if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */
+               snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0);
+
+       return snd_hda_codec_read(codec, nid, 0,
+                                 AC_VERB_GET_PIN_SENSE, 0);
+}
+EXPORT_SYMBOL_HDA(snd_hda_pin_sense);
+
+/**
+ * snd_hda_jack_detect - query pin Presence Detect status
+ * @codec: the CODEC to sense
+ * @nid: the pin NID to sense
+ *
+ * Query and return the pin's Presence Detect status.
+ */
+int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid)
+{
+        u32 sense = snd_hda_pin_sense(codec, nid);
+        return !!(sense & AC_PINSENSE_PRESENCE);
+}
+EXPORT_SYMBOL_HDA(snd_hda_jack_detect);
+
 /*
  * read the current volume to info
  * if the cache exists, read the cache value.
@@ -1269,8 +1391,15 @@ static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
        info->vol[ch] = val;
 }
 
-/*
- * read AMP value.  The volume is between 0 to 0x7f, 0x80 = mute bit.
+/**
+ * snd_hda_codec_amp_read - Read AMP value
+ * @codec: HD-audio codec
+ * @nid: NID to read the AMP value
+ * @ch: channel (left=0 or right=1)
+ * @direction: #HDA_INPUT or #HDA_OUTPUT
+ * @index: the index value (only for input direction)
+ *
+ * Read AMP value.  The volume is between 0 to 0x7f, 0x80 = mute bit.
  */
 int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
                           int direction, int index)
@@ -1283,8 +1412,18 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read);
 
-/*
- * update the AMP value, mask = bit mask to set, val = the value
+/**
+ * snd_hda_codec_amp_update - update the AMP value
+ * @codec: HD-audio codec
+ * @nid: NID to read the AMP value
+ * @ch: channel (left=0 or right=1)
+ * @direction: #HDA_INPUT or #HDA_OUTPUT
+ * @idx: the index value (only for input direction)
+ * @mask: bit mask to set
+ * @val: the bits value to set
+ *
+ * Update the AMP value with a bit mask.
+ * Returns 0 if the value is unchanged, 1 if changed.
  */
 int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
                             int direction, int idx, int mask, int val)
@@ -1303,8 +1442,17 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update);
 
-/*
- * update the AMP stereo with the same mask and value
+/**
+ * snd_hda_codec_amp_stereo - update the AMP stereo values
+ * @codec: HD-audio codec
+ * @nid: NID to read the AMP value
+ * @direction: #HDA_INPUT or #HDA_OUTPUT
+ * @idx: the index value (only for input direction)
+ * @mask: bit mask to set
+ * @val: the bits value to set
+ *
+ * Update the AMP values like snd_hda_codec_amp_update(), but for a
+ * stereo widget with the same mask and value.
  */
 int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
                             int direction, int idx, int mask, int val)
@@ -1318,7 +1466,12 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
 EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo);
 
 #ifdef SND_HDA_NEEDS_RESUME
-/* resume the all amp commands from the cache */
+/**
+ * snd_hda_codec_resume_amp - Resume all AMP commands from the cache
+ * @codec: HD-audio codec
+ *
+ * Resume the all amp commands from the cache.
+ */
 void snd_hda_codec_resume_amp(struct hda_codec *codec)
 {
        struct hda_amp_info *buffer = codec->amp_cache.buf.list;
@@ -1344,7 +1497,12 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec)
 EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp);
 #endif /* SND_HDA_NEEDS_RESUME */
 
-/* volume */
+/**
+ * snd_hda_mixer_amp_volume_info - Info callback for a standard AMP mixer
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
 int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_info *uinfo)
 {
@@ -1400,6 +1558,12 @@ update_amp_value(struct hda_codec *codec, hda_nid_t nid,
                                        HDA_AMP_VOLMASK, val);
 }
 
+/**
+ * snd_hda_mixer_amp_volume_get - Get callback for a standard AMP mixer volume
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
 int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
 {
@@ -1419,6 +1583,12 @@ int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_get);
 
+/**
+ * snd_hda_mixer_amp_volume_put - Put callback for a standard AMP mixer volume
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
 int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
 {
@@ -1443,6 +1613,12 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_put);
 
+/**
+ * snd_hda_mixer_amp_volume_put - TLV callback for a standard AMP mixer volume
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
 int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
                          unsigned int size, unsigned int __user *_tlv)
 {
@@ -1472,8 +1648,16 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_tlv);
 
-/*
- * set (static) TLV for virtual master volume; recalculated as max 0dB
+/**
+ * snd_hda_set_vmaster_tlv - Set TLV for a virtual master control
+ * @codec: HD-audio codec
+ * @nid: NID of a reference widget
+ * @dir: #HDA_INPUT or #HDA_OUTPUT
+ * @tlv: TLV data to be stored, at least 4 elements
+ *
+ * Set (static) TLV data for a virtual master volume using the AMP caps
+ * obtained from the reference NID.
+ * The volume range is recalculated as if the max volume is 0dB.
  */
 void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
                             unsigned int *tlv)
@@ -1507,6 +1691,13 @@ _snd_hda_find_mixer_ctl(struct hda_codec *codec,
        return snd_ctl_find_id(codec->bus->card, &id);
 }
 
+/**
+ * snd_hda_find_mixer_ctl - Find a mixer control element with the given name
+ * @codec: HD-audio codec
+ * @name: ctl id name string
+ *
+ * Get the control element with the given id string and IFACE_MIXER.
+ */
 struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
                                            const char *name)
 {
@@ -1514,30 +1705,57 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
 }
 EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl);
 
-/* Add a control element and assign to the codec */
-int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl)
+/**
+ * snd_hda_ctl-add - Add a control element and assign to the codec
+ * @codec: HD-audio codec
+ * @nid: corresponding NID (optional)
+ * @kctl: the control element to assign
+ *
+ * Add the given control element to an array inside the codec instance.
+ * All control elements belonging to a codec are supposed to be added
+ * by this function so that a proper clean-up works at the free or
+ * reconfiguration time.
+ *
+ * If non-zero @nid is passed, the NID is assigned to the control element.
+ * The assignment is shown in the codec proc file.
+ *
+ * snd_hda_ctl_add() checks the control subdev id field whether
+ * #HDA_SUBDEV_NID_FLAG bit is set.  If set (and @nid is zero), the lower
+ * bits value is taken as the NID to assign.
+ */
+int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid,
+                   struct snd_kcontrol *kctl)
 {
        int err;
-       struct snd_kcontrol **knewp;
+       struct hda_nid_item *item;
 
+       if (kctl->id.subdevice & HDA_SUBDEV_NID_FLAG) {
+               if (nid == 0)
+                       nid = kctl->id.subdevice & 0xffff;
+               kctl->id.subdevice = 0;
+       }
        err = snd_ctl_add(codec->bus->card, kctl);
        if (err < 0)
                return err;
-       knewp = snd_array_new(&codec->mixers);
-       if (!knewp)
+       item = snd_array_new(&codec->mixers);
+       if (!item)
                return -ENOMEM;
-       *knewp = kctl;
+       item->kctl = kctl;
+       item->nid = nid;
        return 0;
 }
 EXPORT_SYMBOL_HDA(snd_hda_ctl_add);
 
-/* Clear all controls assigned to the given codec */
+/**
+ * snd_hda_ctls_clear - Clear all controls assigned to the given codec
+ * @codec: HD-audio codec
+ */
 void snd_hda_ctls_clear(struct hda_codec *codec)
 {
        int i;
-       struct snd_kcontrol **kctls = codec->mixers.list;
+       struct hda_nid_item *items = codec->mixers.list;
        for (i = 0; i < codec->mixers.used; i++)
-               snd_ctl_remove(codec->bus->card, kctls[i]);
+               snd_ctl_remove(codec->bus->card, items[i].kctl);
        snd_array_free(&codec->mixers);
 }
 
@@ -1563,6 +1781,16 @@ static void hda_unlock_devices(struct snd_card *card)
        spin_unlock(&card->files_lock);
 }
 
+/**
+ * snd_hda_codec_reset - Clear all objects assigned to the codec
+ * @codec: HD-audio codec
+ *
+ * This frees the all PCM and control elements assigned to the codec, and
+ * clears the caches and restores the pin default configurations.
+ *
+ * When a device is being used, it returns -EBSY.  If successfully freed,
+ * returns zero.
+ */
 int snd_hda_codec_reset(struct hda_codec *codec)
 {
        struct snd_card *card = codec->bus->card;
@@ -1626,7 +1854,22 @@ int snd_hda_codec_reset(struct hda_codec *codec)
        return 0;
 }
 
-/* create a virtual master control and add slaves */
+/**
+ * snd_hda_add_vmaster - create a virtual master control and add slaves
+ * @codec: HD-audio codec
+ * @name: vmaster control name
+ * @tlv: TLV data (optional)
+ * @slaves: slave control names (optional)
+ *
+ * Create a virtual master control with the given name.  The TLV data
+ * must be either NULL or a valid data.
+ *
+ * @slaves is a NULL-terminated array of strings, each of which is a
+ * slave control name.  All controls with these names are assigned to
+ * the new virtual master control.
+ *
+ * This function returns zero if successful or a negative error code.
+ */
 int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
                        unsigned int *tlv, const char **slaves)
 {
@@ -1643,7 +1886,7 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
        kctl = snd_ctl_make_virtual_master(name, tlv);
        if (!kctl)
                return -ENOMEM;
-       err = snd_hda_ctl_add(codec, kctl);
+       err = snd_hda_ctl_add(codec, 0, kctl);
        if (err < 0)
                return err;
        
@@ -1668,7 +1911,12 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
 }
 EXPORT_SYMBOL_HDA(snd_hda_add_vmaster);
 
-/* switch */
+/**
+ * snd_hda_mixer_amp_switch_info - Info callback for a standard AMP mixer switch
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
 int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_info *uinfo)
 {
@@ -1682,6 +1930,12 @@ int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_info);
 
+/**
+ * snd_hda_mixer_amp_switch_get - Get callback for a standard AMP mixer switch
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
 int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
 {
@@ -1702,6 +1956,12 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_get);
 
+/**
+ * snd_hda_mixer_amp_switch_put - Put callback for a standard AMP mixer switch
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_COMPOSE_AMP_VAL*() or related macros.
+ */
 int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
 {
@@ -1733,6 +1993,25 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put);
 
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+/**
+ * snd_hda_mixer_amp_switch_put_beep - Put callback for a beep AMP switch
+ *
+ * This function calls snd_hda_enable_beep_device(), which behaves differently
+ * depending on beep_mode option.
+ */
+int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       long *valp = ucontrol->value.integer.value;
+
+       snd_hda_enable_beep_device(codec, *valp);
+       return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+}
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put_beep);
+#endif /* CONFIG_SND_HDA_INPUT_BEEP */
+
 /*
  * bound volume controls
  *
@@ -1742,6 +2021,12 @@ EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put);
 #define AMP_VAL_IDX_SHIFT      19
 #define AMP_VAL_IDX_MASK       (0x0f<<19)
 
+/**
+ * snd_hda_mixer_bind_switch_get - Get callback for a bound volume control
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_BIND_MUTE*() macros.
+ */
 int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_value *ucontrol)
 {
@@ -1759,6 +2044,12 @@ int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_get);
 
+/**
+ * snd_hda_mixer_bind_switch_put - Put callback for a bound volume control
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_BIND_MUTE*() macros.
+ */
 int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_value *ucontrol)
 {
@@ -1783,8 +2074,11 @@ int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_put);
 
-/*
- * generic bound volume/swtich controls
+/**
+ * snd_hda_mixer_bind_ctls_info - Info callback for a generic bound control
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros.
  */
 int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_info *uinfo)
@@ -1803,6 +2097,12 @@ int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_info);
 
+/**
+ * snd_hda_mixer_bind_ctls_get - Get callback for a generic bound control
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros.
+ */
 int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
@@ -1820,6 +2120,12 @@ int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_get);
 
+/**
+ * snd_hda_mixer_bind_ctls_put - Put callback for a generic bound control
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_BIND_VOL() or HDA_BIND_SW() macros.
+ */
 int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
@@ -1843,6 +2149,12 @@ int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_put);
 
+/**
+ * snd_hda_mixer_bind_tlv - TLV callback for a generic bound control
+ *
+ * The control element is supposed to have the private_value field
+ * set up via HDA_BIND_VOL() macro.
+ */
 int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag,
                           unsigned int size, unsigned int __user *tlv)
 {
@@ -2126,7 +2438,7 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
                        return -ENOMEM;
                kctl->id.index = idx;
                kctl->private_value = nid;
-               err = snd_hda_ctl_add(codec, kctl);
+               err = snd_hda_ctl_add(codec, nid, kctl);
                if (err < 0)
                        return err;
        }
@@ -2165,14 +2477,19 @@ static struct snd_kcontrol_new spdif_share_sw = {
        .put = spdif_share_sw_put,
 };
 
+/**
+ * snd_hda_create_spdif_share_sw - create Default PCM switch
+ * @codec: the HDA codec
+ * @mout: multi-out instance
+ */
 int snd_hda_create_spdif_share_sw(struct hda_codec *codec,
                                  struct hda_multi_out *mout)
 {
        if (!mout->dig_out_nid)
                return 0;
        /* ATTENTION: here mout is passed as private_data, instead of codec */
-       return snd_hda_ctl_add(codec,
-                          snd_ctl_new1(&spdif_share_sw, mout));
+       return snd_hda_ctl_add(codec, mout->dig_out_nid,
+                             snd_ctl_new1(&spdif_share_sw, mout));
 }
 EXPORT_SYMBOL_HDA(snd_hda_create_spdif_share_sw);
 
@@ -2276,7 +2593,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
                if (!kctl)
                        return -ENOMEM;
                kctl->private_value = nid;
-               err = snd_hda_ctl_add(codec, kctl);
+               err = snd_hda_ctl_add(codec, nid, kctl);
                if (err < 0)
                        return err;
        }
@@ -2332,7 +2649,12 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache);
 
-/* resume the all commands from the cache */
+/**
+ * snd_hda_codec_resume_cache - Resume the all commands from the cache
+ * @codec: HD-audio codec
+ *
+ * Execute all verbs recorded in the command caches to resume.
+ */
 void snd_hda_codec_resume_cache(struct hda_codec *codec)
 {
        struct hda_cache_head *buffer = codec->cmd_cache.buf.list;
@@ -2452,9 +2774,11 @@ static void hda_call_codec_suspend(struct hda_codec *codec)
                            codec->afg ? codec->afg : codec->mfg,
                            AC_PWRST_D3);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
+       snd_hda_update_power_acct(codec);
        cancel_delayed_work(&codec->power_work);
        codec->power_on = 0;
        codec->power_transition = 0;
+       codec->power_jiffies = jiffies;
 #endif
 }
 
@@ -2756,8 +3080,12 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
 }
 
 /**
- * snd_hda_is_supported_format - check whether the given node supports
- * the format val
+ * snd_hda_is_supported_format - Check the validity of the format
+ * @codec: HD-audio codec
+ * @nid: NID to check
+ * @format: the HD-audio format value to check
+ *
+ * Check whether the given node supports the format value.
  *
  * Returns 1 if supported, 0 if not.
  */
@@ -2877,51 +3205,36 @@ static int set_pcm_default_values(struct hda_codec *codec,
        return 0;
 }
 
+/* global */
+const char *snd_hda_pcm_type_name[HDA_PCM_NTYPES] = {
+       "Audio", "SPDIF", "HDMI", "Modem"
+};
+
 /*
  * get the empty PCM device number to assign
  */
 static int get_empty_pcm_device(struct hda_bus *bus, int type)
 {
-       static const char *dev_name[HDA_PCM_NTYPES] = {
-               "Audio", "SPDIF", "HDMI", "Modem"
-       };
-       /* starting device index for each PCM type */
-       static int dev_idx[HDA_PCM_NTYPES] = {
-               [HDA_PCM_TYPE_AUDIO] = 0,
-               [HDA_PCM_TYPE_SPDIF] = 1,
-               [HDA_PCM_TYPE_HDMI] = 3,
-               [HDA_PCM_TYPE_MODEM] = 6
+       /* audio device indices; not linear to keep compatibility */
+       static int audio_idx[HDA_PCM_NTYPES][5] = {
+               [HDA_PCM_TYPE_AUDIO] = { 0, 2, 4, 5, -1 },
+               [HDA_PCM_TYPE_SPDIF] = { 1, -1 },
+               [HDA_PCM_TYPE_HDMI]  = { 3, 7, 8, 9, -1 },
+               [HDA_PCM_TYPE_MODEM] = { 6, -1 },
        };
-       /* normal audio device indices; not linear to keep compatibility */
-       static int audio_idx[4] = { 0, 2, 4, 5 };
-       int i, dev;
-
-       switch (type) {
-       case HDA_PCM_TYPE_AUDIO:
-               for (i = 0; i < ARRAY_SIZE(audio_idx); i++) {
-                       dev = audio_idx[i];
-                       if (!test_bit(dev, bus->pcm_dev_bits))
-                               goto ok;
-               }
-               snd_printk(KERN_WARNING "Too many audio devices\n");
-               return -EAGAIN;
-       case HDA_PCM_TYPE_SPDIF:
-       case HDA_PCM_TYPE_HDMI:
-       case HDA_PCM_TYPE_MODEM:
-               dev = dev_idx[type];
-               if (test_bit(dev, bus->pcm_dev_bits)) {
-                       snd_printk(KERN_WARNING "%s already defined\n",
-                                  dev_name[type]);
-                       return -EAGAIN;
-               }
-               break;
-       default:
+       int i;
+
+       if (type >= HDA_PCM_NTYPES) {
                snd_printk(KERN_WARNING "Invalid PCM type %d\n", type);
                return -EINVAL;
        }
- ok:
-       set_bit(dev, bus->pcm_dev_bits);
-       return dev;
+
+       for (i = 0; audio_idx[type][i] >= 0 ; i++)
+               if (!test_and_set_bit(audio_idx[type][i], bus->pcm_dev_bits))
+                       return audio_idx[type][i];
+
+       snd_printk(KERN_WARNING "Too many %s devices\n", snd_hda_pcm_type_name[type]);
+       return -EAGAIN;
 }
 
 /*
@@ -3159,14 +3472,14 @@ EXPORT_SYMBOL_HDA(snd_hda_check_board_codec_sid_config);
  */
 int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
 {
-       int err;
+       int err;
 
        for (; knew->name; knew++) {
                struct snd_kcontrol *kctl;
                kctl = snd_ctl_new1(knew, codec);
                if (!kctl)
                        return -ENOMEM;
-               err = snd_hda_ctl_add(codec, kctl);
+               err = snd_hda_ctl_add(codec, 0, kctl);
                if (err < 0) {
                        if (!codec->addr)
                                return err;
@@ -3174,7 +3487,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
                        if (!kctl)
                                return -ENOMEM;
                        kctl->id.device = codec->addr;
-                       err = snd_hda_ctl_add(codec, kctl);
+                       err = snd_hda_ctl_add(codec, 0, kctl);
                        if (err < 0)
                                return err;
                }
@@ -3207,8 +3520,27 @@ static void hda_keep_power_on(struct hda_codec *codec)
 {
        codec->power_count++;
        codec->power_on = 1;
+       codec->power_jiffies = jiffies;
 }
 
+/* update the power on/off account with the current jiffies */
+void snd_hda_update_power_acct(struct hda_codec *codec)
+{
+       unsigned long delta = jiffies - codec->power_jiffies;
+       if (codec->power_on)
+               codec->power_on_acct += delta;
+       else
+               codec->power_off_acct += delta;
+       codec->power_jiffies += delta;
+}
+
+/**
+ * snd_hda_power_up - Power-up the codec
+ * @codec: HD-audio codec
+ *
+ * Increment the power-up counter and power up the hardware really when
+ * not turned on yet.
+ */ 
 void snd_hda_power_up(struct hda_codec *codec)
 {
        struct hda_bus *bus = codec->bus;
@@ -3217,7 +3549,9 @@ void snd_hda_power_up(struct hda_codec *codec)
        if (codec->power_on || codec->power_transition)
                return;
 
+       snd_hda_update_power_acct(codec);
        codec->power_on = 1;
+       codec->power_jiffies = jiffies;
        if (bus->ops.pm_notify)
                bus->ops.pm_notify(bus);
        hda_call_codec_resume(codec);
@@ -3229,9 +3563,13 @@ EXPORT_SYMBOL_HDA(snd_hda_power_up);
 #define power_save(codec)      \
        ((codec)->bus->power_save ? *(codec)->bus->power_save : 0)
 
-#define power_save(codec)      \
-       ((codec)->bus->power_save ? *(codec)->bus->power_save : 0)
-
+/**
+ * snd_hda_power_down - Power-down the codec
+ * @codec: HD-audio codec
+ *
+ * Decrement the power-up counter and schedules the power-off work if
+ * the counter rearches to zero.
+ */ 
 void snd_hda_power_down(struct hda_codec *codec)
 {
        --codec->power_count;
@@ -3245,6 +3583,19 @@ void snd_hda_power_down(struct hda_codec *codec)
 }
 EXPORT_SYMBOL_HDA(snd_hda_power_down);
 
+/**
+ * snd_hda_check_amp_list_power - Check the amp list and update the power
+ * @codec: HD-audio codec
+ * @check: the object containing an AMP list and the status
+ * @nid: NID to check / update
+ *
+ * Check whether the given NID is in the amp list.  If it's in the list,
+ * check the current AMP status, and update the the power-status according
+ * to the mute status.
+ *
+ * This function is supposed to be set or called from the check_power_status
+ * patch ops.
+ */ 
 int snd_hda_check_amp_list_power(struct hda_codec *codec,
                                 struct hda_loopback_check *check,
                                 hda_nid_t nid)
@@ -3286,6 +3637,10 @@ EXPORT_SYMBOL_HDA(snd_hda_check_amp_list_power);
 /*
  * Channel mode helper
  */
+
+/**
+ * snd_hda_ch_mode_info - Info callback helper for the channel mode enum
+ */
 int snd_hda_ch_mode_info(struct hda_codec *codec,
                         struct snd_ctl_elem_info *uinfo,
                         const struct hda_channel_mode *chmode,
@@ -3302,6 +3657,9 @@ int snd_hda_ch_mode_info(struct hda_codec *codec,
 }
 EXPORT_SYMBOL_HDA(snd_hda_ch_mode_info);
 
+/**
+ * snd_hda_ch_mode_get - Get callback helper for the channel mode enum
+ */
 int snd_hda_ch_mode_get(struct hda_codec *codec,
                        struct snd_ctl_elem_value *ucontrol,
                        const struct hda_channel_mode *chmode,
@@ -3320,6 +3678,9 @@ int snd_hda_ch_mode_get(struct hda_codec *codec,
 }
 EXPORT_SYMBOL_HDA(snd_hda_ch_mode_get);
 
+/**
+ * snd_hda_ch_mode_put - Put callback helper for the channel mode enum
+ */
 int snd_hda_ch_mode_put(struct hda_codec *codec,
                        struct snd_ctl_elem_value *ucontrol,
                        const struct hda_channel_mode *chmode,
@@ -3344,6 +3705,10 @@ EXPORT_SYMBOL_HDA(snd_hda_ch_mode_put);
 /*
  * input MUX helper
  */
+
+/**
+ * snd_hda_input_mux_info_info - Info callback helper for the input-mux enum
+ */
 int snd_hda_input_mux_info(const struct hda_input_mux *imux,
                           struct snd_ctl_elem_info *uinfo)
 {
@@ -3362,6 +3727,9 @@ int snd_hda_input_mux_info(const struct hda_input_mux *imux,
 }
 EXPORT_SYMBOL_HDA(snd_hda_input_mux_info);
 
+/**
+ * snd_hda_input_mux_info_put - Put callback helper for the input-mux enum
+ */
 int snd_hda_input_mux_put(struct hda_codec *codec,
                          const struct hda_input_mux *imux,
                          struct snd_ctl_elem_value *ucontrol,
@@ -3421,8 +3789,29 @@ static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid)
        }
 }
 
-/*
- * open the digital out in the exclusive mode
+/**
+ * snd_hda_bus_reboot_notify - call the reboot notifier of each codec
+ * @bus: HD-audio bus
+ */
+void snd_hda_bus_reboot_notify(struct hda_bus *bus)
+{
+       struct hda_codec *codec;
+
+       if (!bus)
+               return;
+       list_for_each_entry(codec, &bus->codec_list, list) {
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+               if (!codec->power_on)
+                       continue;
+#endif
+               if (codec->patch_ops.reboot_notify)
+                       codec->patch_ops.reboot_notify(codec);
+       }
+}
+EXPORT_SYMBOL_HDA(snd_hda_bus_reboot_notify);
+
+/**
+ * snd_hda_multi_out_dig_open - open the digital out in the exclusive mode
  */
 int snd_hda_multi_out_dig_open(struct hda_codec *codec,
                               struct hda_multi_out *mout)
@@ -3437,6 +3826,9 @@ int snd_hda_multi_out_dig_open(struct hda_codec *codec,
 }
 EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_open);
 
+/**
+ * snd_hda_multi_out_dig_prepare - prepare the digital out stream
+ */
 int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
                                  struct hda_multi_out *mout,
                                  unsigned int stream_tag,
@@ -3450,6 +3842,9 @@ int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
 }
 EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_prepare);
 
+/**
+ * snd_hda_multi_out_dig_cleanup - clean-up the digital out stream
+ */
 int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec,
                                  struct hda_multi_out *mout)
 {
@@ -3460,8 +3855,8 @@ int snd_hda_multi_out_dig_cleanup(struct hda_codec *codec,
 }
 EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_cleanup);
 
-/*
- * release the digital out
+/**
+ * snd_hda_multi_out_dig_close - release the digital out stream
  */
 int snd_hda_multi_out_dig_close(struct hda_codec *codec,
                                struct hda_multi_out *mout)
@@ -3473,8 +3868,12 @@ int snd_hda_multi_out_dig_close(struct hda_codec *codec,
 }
 EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_close);
 
-/*
- * set up more restrictions for analog out
+/**
+ * snd_hda_multi_out_analog_open - open analog outputs
+ *
+ * Open analog outputs and set up the hw-constraints.
+ * If the digital outputs can be opened as slave, open the digital
+ * outputs, too.
  */
 int snd_hda_multi_out_analog_open(struct hda_codec *codec,
                                  struct hda_multi_out *mout,
@@ -3519,9 +3918,11 @@ int snd_hda_multi_out_analog_open(struct hda_codec *codec,
 }
 EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_open);
 
-/*
- * set up the i/o for analog out
- * when the digital out is available, copy the front out to digital out, too.
+/**
+ * snd_hda_multi_out_analog_prepare - Preapre the analog outputs.
+ *
+ * Set up the i/o for analog out.
+ * When the digital out is available, copy the front out to digital out, too.
  */
 int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
                                     struct hda_multi_out *mout,
@@ -3578,8 +3979,8 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
 }
 EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_prepare);
 
-/*
- * clean up the setting for analog out
+/**
+ * snd_hda_multi_out_analog_cleanup - clean up the setting for analog out
  */
 int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
                                     struct hda_multi_out *mout)
@@ -3965,8 +4366,14 @@ EXPORT_SYMBOL_HDA(snd_hda_resume);
  * generic arrays
  */
 
-/* get a new element from the given array
- * if it exceeds the pre-allocated array size, re-allocate the array
+/**
+ * snd_array_new - get a new element from the given array
+ * @array: the array object
+ * 
+ * Get a new element from the given array.  If it exceeds the
+ * pre-allocated array size, re-allocate the array.
+ *
+ * Returns NULL if allocation failed.
  */
 void *snd_array_new(struct snd_array *array)
 {
@@ -3990,7 +4397,10 @@ void *snd_array_new(struct snd_array *array)
 }
 EXPORT_SYMBOL_HDA(snd_array_new);
 
-/* free the given array elements */
+/**
+ * snd_array_free - free the given array elements
+ * @array: the array object
+ */
 void snd_array_free(struct snd_array *array)
 {
        kfree(array->list);
@@ -4000,7 +4410,12 @@ void snd_array_free(struct snd_array *array)
 }
 EXPORT_SYMBOL_HDA(snd_array_free);
 
-/*
+/**
+ * snd_print_pcm_rates - Print the supported PCM rates to the string buffer
+ * @pcm: PCM caps bits
+ * @buf: the string buffer to write
+ * @buflen: the max buffer length
+ *
  * used by hda_proc.c and hda_eld.c
  */
 void snd_print_pcm_rates(int pcm, char *buf, int buflen)
@@ -4019,6 +4434,14 @@ void snd_print_pcm_rates(int pcm, char *buf, int buflen)
 }
 EXPORT_SYMBOL_HDA(snd_print_pcm_rates);
 
+/**
+ * snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer
+ * @pcm: PCM caps bits
+ * @buf: the string buffer to write
+ * @buflen: the max buffer length
+ *
+ * used by hda_proc.c and hda_eld.c
+ */
 void snd_print_pcm_bits(int pcm, char *buf, int buflen)
 {
        static unsigned int bits[] = { 8, 16, 20, 24, 32 };
index 99552fb..2d62761 100644 (file)
@@ -286,6 +286,10 @@ enum {
 #define AC_PWRST_D1SUP                 (1<<1)
 #define AC_PWRST_D2SUP                 (1<<2)
 #define AC_PWRST_D3SUP                 (1<<3)
+#define AC_PWRST_D3COLDSUP             (1<<4)
+#define AC_PWRST_S3D3COLDSUP           (1<<29)
+#define AC_PWRST_CLKSTOP               (1<<30)
+#define AC_PWRST_EPSS                  (1U<<31)
 
 /* Power state values */
 #define AC_PWRST_SETTING               (0xf<<0)
@@ -674,6 +678,7 @@ struct hda_codec_ops {
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        int (*check_power_status)(struct hda_codec *codec, hda_nid_t nid);
 #endif
+       void (*reboot_notify)(struct hda_codec *codec);
 };
 
 /* record for amp information cache */
@@ -771,6 +776,7 @@ struct hda_codec {
 
        /* beep device */
        struct hda_beep *beep;
+       unsigned int beep_mode;
 
        /* widget capabilities cache */
        unsigned int num_nodes;
@@ -811,6 +817,9 @@ struct hda_codec {
        unsigned int power_transition :1; /* power-state in transition */
        int power_count;        /* current (global) power refcount */
        struct delayed_work power_work; /* delayed task for powerdown */
+       unsigned long power_on_acct;
+       unsigned long power_off_acct;
+       unsigned long power_jiffies;
 #endif
 
        /* codec-specific additional proc output */
@@ -910,6 +919,7 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
  * Misc
  */
 void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen);
+void snd_hda_bus_reboot_notify(struct hda_bus *bus);
 
 /*
  * power management
@@ -933,6 +943,7 @@ const char *snd_hda_get_jack_location(u32 cfg);
 void snd_hda_power_up(struct hda_codec *codec);
 void snd_hda_power_down(struct hda_codec *codec);
 #define snd_hda_codec_needs_resume(codec) codec->power_count
+void snd_hda_update_power_acct(struct hda_codec *codec);
 #else
 static inline void snd_hda_power_up(struct hda_codec *codec) {}
 static inline void snd_hda_power_down(struct hda_codec *codec) {}
index 9446a5a..4228f2f 100644 (file)
@@ -309,17 +309,12 @@ out_fail:
        return -EINVAL;
 }
 
-static int hdmi_present_sense(struct hda_codec *codec, hda_nid_t nid)
-{
-       return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0);
-}
-
 static int hdmi_eld_valid(struct hda_codec *codec, hda_nid_t nid)
 {
        int eldv;
        int present;
 
-       present = hdmi_present_sense(codec, nid);
+       present = snd_hda_pin_sense(codec, nid);
        eldv    = (present & AC_PINSENSE_ELDV);
        present = (present & AC_PINSENSE_PRESENCE);
 
@@ -477,6 +472,8 @@ static void hdmi_print_eld_info(struct snd_info_entry *entry,
                [4 ... 7] = "reserved"
        };
 
+       snd_iprintf(buffer, "monitor_present\t\t%d\n", e->monitor_present);
+       snd_iprintf(buffer, "eld_valid\t\t%d\n", e->eld_valid);
        snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name);
        snd_iprintf(buffer, "connection_type\t\t%s\n",
                                eld_connection_type_names[e->conn_type]);
@@ -518,7 +515,11 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry,
                 *      monitor_name manufacture_id product_id
                 *      eld_version edid_version
                 */
-               if (!strcmp(name, "connection_type"))
+               if (!strcmp(name, "monitor_present"))
+                       e->monitor_present = val;
+               else if (!strcmp(name, "eld_valid"))
+                       e->eld_valid = val;
+               else if (!strcmp(name, "connection_type"))
                        e->conn_type = val;
                else if (!strcmp(name, "port_id"))
                        e->port_id = val;
@@ -560,13 +561,14 @@ static void hdmi_write_eld_info(struct snd_info_entry *entry,
 }
 
 
-int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld)
+int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
+                        int index)
 {
        char name[32];
        struct snd_info_entry *entry;
        int err;
 
-       snprintf(name, sizeof(name), "eld#%d", codec->addr);
+       snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index);
        err = snd_card_proc_new(codec->bus->card, name, &entry);
        if (err < 0)
                return err;
index b36f6c5..092c6a7 100644 (file)
@@ -727,7 +727,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
                if (is_loopback)
                        add_input_loopback(codec, node->nid, HDA_INPUT, index);
                snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
-               err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+               err = snd_hda_ctl_add(codec, node->nid,
+                                       snd_ctl_new1(&knew, codec));
                if (err < 0)
                        return err;
                created = 1;
@@ -737,7 +738,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
                if (is_loopback)
                        add_input_loopback(codec, node->nid, HDA_OUTPUT, 0);
                snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
-               err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+               err = snd_hda_ctl_add(codec, node->nid,
+                                       snd_ctl_new1(&knew, codec));
                if (err < 0)
                        return err;
                created = 1;
@@ -751,7 +753,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
            (node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) {
                knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, index, HDA_INPUT);
                snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
-               err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+               err = snd_hda_ctl_add(codec, node->nid,
+                                       snd_ctl_new1(&knew, codec));
                if (err < 0)
                        return err;
                created = 1;
@@ -759,7 +762,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
                   (node->amp_out_caps & AC_AMPCAP_NUM_STEPS)) {
                knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, 0, HDA_OUTPUT);
                snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
-               err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+               err = snd_hda_ctl_add(codec, node->nid,
+                                       snd_ctl_new1(&knew, codec));
                if (err < 0)
                        return err;
                created = 1;
@@ -857,7 +861,7 @@ static int build_input_controls(struct hda_codec *codec)
        }
 
        /* create input MUX if multiple sources are available */
-       err = snd_hda_ctl_add(codec, snd_ctl_new1(&cap_sel, codec));
+       err = snd_hda_ctl_add(codec, 0, snd_ctl_new1(&cap_sel, codec));
        if (err < 0)
                return err;
 
@@ -875,7 +879,8 @@ static int build_input_controls(struct hda_codec *codec)
                        HDA_CODEC_VOLUME(name, adc_node->nid,
                                         spec->input_mux.items[i].index,
                                         HDA_INPUT);
-               err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+               err = snd_hda_ctl_add(codec, adc_node->nid,
+                                       snd_ctl_new1(&knew, codec));
                if (err < 0)
                        return err;
        }
index cc24e67..d243286 100644 (file)
@@ -154,6 +154,44 @@ int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec)
        return 0;
 }
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+static ssize_t power_on_acct_show(struct device *dev,
+                                 struct device_attribute *attr,
+                                 char *buf)
+{
+       struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+       struct hda_codec *codec = hwdep->private_data;
+       snd_hda_update_power_acct(codec);
+       return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_on_acct));
+}
+
+static ssize_t power_off_acct_show(struct device *dev,
+                                  struct device_attribute *attr,
+                                  char *buf)
+{
+       struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+       struct hda_codec *codec = hwdep->private_data;
+       snd_hda_update_power_acct(codec);
+       return sprintf(buf, "%u\n", jiffies_to_msecs(codec->power_off_acct));
+}
+
+static struct device_attribute power_attrs[] = {
+       __ATTR_RO(power_on_acct),
+       __ATTR_RO(power_off_acct),
+};
+
+int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec)
+{
+       struct snd_hwdep *hwdep = codec->hwdep;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(power_attrs); i++)
+               snd_add_device_sysfs_file(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card,
+                                         hwdep->device, &power_attrs[i]);
+       return 0;
+}
+#endif /* CONFIG_SND_HDA_POWER_SAVE */
+
 #ifdef CONFIG_SND_HDA_RECONFIG
 
 /*
index 6517f58..d822bfc 100644 (file)
@@ -60,10 +60,14 @@ static int bdl_pos_adj[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
 static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
 static int probe_only[SNDRV_CARDS];
 static int single_cmd;
-static int enable_msi;
+static int enable_msi = -1;
 #ifdef CONFIG_SND_HDA_PATCH_LOADER
 static char *patch[SNDRV_CARDS];
 #endif
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+static int beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] =
+                                       CONFIG_SND_HDA_INPUT_BEEP_MODE};
+#endif
 
 module_param_array(index, int, NULL, 0444);
 MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
@@ -91,6 +95,11 @@ MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)");
 module_param_array(patch, charp, NULL, 0444);
 MODULE_PARM_DESC(patch, "Patch file for Intel HD audio interface.");
 #endif
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+module_param_array(beep_mode, int, NULL, 0444);
+MODULE_PARM_DESC(beep_mode, "Select HDA Beep registration mode "
+                           "(0=off, 1=on, 2=mute switch on/off) (default=1).");
+#endif
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
@@ -404,6 +413,7 @@ struct azx {
        unsigned short codec_mask;
        int  codec_probe_mask; /* copied from probe_mask option */
        struct hda_bus *bus;
+       unsigned int beep_mode;
 
        /* CORB/RIRB */
        struct azx_rb corb;
@@ -677,6 +687,14 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus,
                }
        }
 
+       if (!chip->polling_mode) {
+               snd_printk(KERN_WARNING SFX "azx_get_response timeout, "
+                          "switching to polling mode: last cmd=0x%08x\n",
+                          chip->last_cmd[addr]);
+               chip->polling_mode = 1;
+               goto again;
+       }
+
        if (chip->msi) {
                snd_printk(KERN_WARNING SFX "No response from codec, "
                           "disabling MSI: last cmd=0x%08x\n",
@@ -692,14 +710,6 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus,
                goto again;
        }
 
-       if (!chip->polling_mode) {
-               snd_printk(KERN_WARNING SFX "azx_get_response timeout, "
-                          "switching to polling mode: last cmd=0x%08x\n",
-                          chip->last_cmd[addr]);
-               chip->polling_mode = 1;
-               goto again;
-       }
-
        if (chip->probing) {
                /* If this critical timeout happens during the codec probing
                 * phase, this is likely an access to a non-existing codec
@@ -1404,6 +1414,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model)
                        err = snd_hda_codec_new(chip->bus, c, &codec);
                        if (err < 0)
                                continue;
+                       codec->beep_mode = chip->beep_mode;
                        codecs++;
                }
        }
@@ -2154,6 +2165,7 @@ static int azx_resume(struct pci_dev *pci)
 static int azx_halt(struct notifier_block *nb, unsigned long event, void *buf)
 {
        struct azx *chip = container_of(nb, struct azx, reboot_notifier);
+       snd_hda_bus_reboot_notify(chip->bus);
        azx_stop_chip(chip);
        return NOTIFY_OK;
 }
@@ -2221,7 +2233,9 @@ static int azx_dev_free(struct snd_device *device)
 static struct snd_pci_quirk position_fix_list[] __devinitdata = {
        SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_LPIB),
+       SND_PCI_QUIRK(0x103c, 0x306d, "HP dv3", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB),
+       SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", POS_FIX_LPIB),
        {}
 };
 
@@ -2304,11 +2318,9 @@ static void __devinit check_probe_mask(struct azx *chip, int dev)
 }
 
 /*
- * white-list for enable_msi
+ * white/black-list for enable_msi
  */
-static struct snd_pci_quirk msi_white_list[] __devinitdata = {
-       SND_PCI_QUIRK(0x103c, 0x30f7, "HP Pavilion dv4t-1300", 1),
-       SND_PCI_QUIRK(0x103c, 0x3607, "HP Compa CQ40", 1),
+static struct snd_pci_quirk msi_black_list[] __devinitdata = {
        {}
 };
 
@@ -2316,10 +2328,12 @@ static void __devinit check_msi(struct azx *chip)
 {
        const struct snd_pci_quirk *q;
 
-       chip->msi = enable_msi;
-       if (chip->msi)
+       if (enable_msi >= 0) {
+               chip->msi = !!enable_msi;
                return;
-       q = snd_pci_quirk_lookup(chip->pci, msi_white_list);
+       }
+       chip->msi = 1;  /* enable MSI as default */
+       q = snd_pci_quirk_lookup(chip->pci, msi_black_list);
        if (q) {
                printk(KERN_INFO
                       "hda_intel: msi for device %04x:%04x set to %d\n",
@@ -2578,6 +2592,10 @@ static int __devinit azx_probe(struct pci_dev *pci,
                goto out_free;
        card->private_data = chip;
 
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+       chip->beep_mode = beep_mode[dev];
+#endif
+
        /* create codec instances */
        err = azx_codec_create(chip, model[dev]);
        if (err < 0)
index 5f1dcc5..5778ae8 100644 (file)
 #ifndef __SOUND_HDA_LOCAL_H
 #define __SOUND_HDA_LOCAL_H
 
+/* We abuse kcontrol_new.subdev field to pass the NID corresponding to
+ * the given new control.  If id.subdev has a bit flag HDA_SUBDEV_NID_FLAG,
+ * snd_hda_ctl_add() takes the lower-bit subdev value as a valid NID.
+ * 
+ * Note that the subdevice field is cleared again before the real registration
+ * in snd_hda_ctl_add(), so that this value won't appear in the outside.
+ */
+#define HDA_SUBDEV_NID_FLAG    (1U << 31)
+
 /*
  * for mixer controls
  */
@@ -33,6 +42,7 @@
 /* mono volume with index (index=0,1,...) (channel=1,2) */
 #define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
        { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx,  \
+         .subdevice = HDA_SUBDEV_NID_FLAG | (nid), \
          .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
                    SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
                    SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
@@ -53,6 +63,7 @@
 /* mono mute switch with index (index=0,1,...) (channel=1,2) */
 #define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
        { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
+         .subdevice = HDA_SUBDEV_NID_FLAG | (nid), \
          .info = snd_hda_mixer_amp_switch_info, \
          .get = snd_hda_mixer_amp_switch_get, \
          .put = snd_hda_mixer_amp_switch_put, \
 /* stereo mute switch */
 #define HDA_CODEC_MUTE(xname, nid, xindex, direction) \
        HDA_CODEC_MUTE_MONO(xname, nid, 3, xindex, direction)
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+/* special beep mono mute switch with index (index=0,1,...) (channel=1,2) */
+#define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
+       { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
+         .subdevice = HDA_SUBDEV_NID_FLAG | (nid), \
+         .info = snd_hda_mixer_amp_switch_info, \
+         .get = snd_hda_mixer_amp_switch_get, \
+         .put = snd_hda_mixer_amp_switch_put_beep, \
+         .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, xindex, direction) }
+#else
+/* no digital beep - just the standard one */
+#define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, ch, xidx, dir) \
+       HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, ch, xidx, dir)
+#endif /* CONFIG_SND_HDA_INPUT_BEEP */
+/* special beep mono mute switch */
+#define HDA_CODEC_MUTE_BEEP_MONO(xname, nid, channel, xindex, direction) \
+       HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, 0, nid, channel, xindex, direction)
+/* special beep stereo mute switch */
+#define HDA_CODEC_MUTE_BEEP(xname, nid, xindex, direction) \
+       HDA_CODEC_MUTE_BEEP_MONO(xname, nid, 3, xindex, direction)
+
+extern const char *snd_hda_pcm_type_name[];
 
 int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_info *uinfo);
@@ -81,6 +114,10 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol);
 int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol);
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
+int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_value *ucontrol);
+#endif
 /* lowlevel accessor with caching; use carefully */
 int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
                           int direction, int index);
@@ -424,8 +461,16 @@ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction);
 int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
                              unsigned int caps);
 u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid);
+u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid);
+int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid);
 
-int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl);
+struct hda_nid_item {
+       struct snd_kcontrol *kctl;
+       hda_nid_t nid;
+};
+
+int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid,
+                   struct snd_kcontrol *kctl);
 void snd_hda_ctls_clear(struct hda_codec *codec);
 
 /*
@@ -437,6 +482,15 @@ int snd_hda_create_hwdep(struct hda_codec *codec);
 static inline int snd_hda_create_hwdep(struct hda_codec *codec) { return 0; }
 #endif
 
+#if defined(CONFIG_SND_HDA_POWER_SAVE) && defined(CONFIG_SND_HDA_HWDEP)
+int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec);
+#else
+static inline int snd_hda_hwdep_add_power_sysfs(struct hda_codec *codec)
+{
+       return 0;
+}
+#endif
+
 #ifdef CONFIG_SND_HDA_RECONFIG
 int snd_hda_hwdep_add_sysfs(struct hda_codec *codec);
 #else
@@ -490,7 +544,8 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
  * AMP control callbacks
  */
 /* retrieve parameters from private_value */
-#define get_amp_nid(kc)                ((kc)->private_value & 0xffff)
+#define get_amp_nid_(pv)       ((pv) & 0xffff)
+#define get_amp_nid(kc)                get_amp_nid_((kc)->private_value)
 #define get_amp_channels(kc)   (((kc)->private_value >> 16) & 0x3)
 #define get_amp_direction(kc)  (((kc)->private_value >> 18) & 0x1)
 #define get_amp_index(kc)      (((kc)->private_value >> 19) & 0xf)
@@ -516,9 +571,11 @@ struct cea_sad {
  * ELD: EDID Like Data
  */
 struct hdmi_eld {
+       bool    monitor_present;
+       bool    eld_valid;
        int     eld_size;
        int     baseline_len;
-       int     eld_ver;        /* (eld_ver == 0) indicates invalid ELD */
+       int     eld_ver;
        int     cea_edid_ver;
        char    monitor_name[ELD_MAX_MNL + 1];
        int     manufacture_id;
@@ -541,11 +598,13 @@ int snd_hdmi_get_eld(struct hdmi_eld *, struct hda_codec *, hda_nid_t);
 void snd_hdmi_show_eld(struct hdmi_eld *eld);
 
 #ifdef CONFIG_PROC_FS
-int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld);
+int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
+                        int index);
 void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld);
 #else
 static inline int snd_hda_eld_proc_new(struct hda_codec *codec,
-                                      struct hdmi_eld *eld)
+                                      struct hdmi_eld *eld,
+                                      int index)
 {
        return 0;
 }
index 95f24e4..09476fc 100644 (file)
 #include "hda_codec.h"
 #include "hda_local.h"
 
+static char *bits_names(unsigned int bits, char *names[], int size)
+{
+       int i, n;
+       static char buf[128];
+
+       for (i = 0, n = 0; i < size; i++) {
+               if (bits & (1U<<i) && names[i])
+                       n += snprintf(buf + n, sizeof(buf) - n, " %s",
+                                     names[i]);
+       }
+       buf[n] = '\0';
+
+       return buf;
+}
+
 static const char *get_wid_type_name(unsigned int wid_value)
 {
        static char *names[16] = {
@@ -46,6 +61,41 @@ static const char *get_wid_type_name(unsigned int wid_value)
                return "UNKNOWN Widget";
 }
 
+static void print_nid_mixers(struct snd_info_buffer *buffer,
+                            struct hda_codec *codec, hda_nid_t nid)
+{
+       int i;
+       struct hda_nid_item *items = codec->mixers.list;
+       struct snd_kcontrol *kctl;
+       for (i = 0; i < codec->mixers.used; i++) {
+               if (items[i].nid == nid) {
+                       kctl = items[i].kctl;
+                       snd_iprintf(buffer,
+                         "  Control: name=\"%s\", index=%i, device=%i\n",
+                         kctl->id.name, kctl->id.index, kctl->id.device);
+               }
+       }
+}
+
+static void print_nid_pcms(struct snd_info_buffer *buffer,
+                          struct hda_codec *codec, hda_nid_t nid)
+{
+       int pcm, type;
+       struct hda_pcm *cpcm;
+       for (pcm = 0; pcm < codec->num_pcms; pcm++) {
+               cpcm = &codec->pcm_info[pcm];
+               for (type = 0; type < 2; type++) {
+                       if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL)
+                               continue;
+                       snd_iprintf(buffer, "  Device: name=\"%s\", "
+                                   "type=\"%s\", device=%i\n",
+                                   cpcm->name,
+                                   snd_hda_pcm_type_name[cpcm->pcm_type],
+                                   cpcm->pcm->device);
+               }
+       }
+}
+
 static void print_amp_caps(struct snd_info_buffer *buffer,
                           struct hda_codec *codec, hda_nid_t nid, int dir)
 {
@@ -363,8 +413,24 @@ static const char *get_pwr_state(u32 state)
 static void print_power_state(struct snd_info_buffer *buffer,
                              struct hda_codec *codec, hda_nid_t nid)
 {
+       static char *names[] = {
+               [ilog2(AC_PWRST_D0SUP)]         = "D0",
+               [ilog2(AC_PWRST_D1SUP)]         = "D1",
+               [ilog2(AC_PWRST_D2SUP)]         = "D2",
+               [ilog2(AC_PWRST_D3SUP)]         = "D3",
+               [ilog2(AC_PWRST_D3COLDSUP)]     = "D3cold",
+               [ilog2(AC_PWRST_S3D3COLDSUP)]   = "S3D3cold",
+               [ilog2(AC_PWRST_CLKSTOP)]       = "CLKSTOP",
+               [ilog2(AC_PWRST_EPSS)]          = "EPSS",
+       };
+
+       int sup = snd_hda_param_read(codec, nid, AC_PAR_POWER_STATE);
        int pwr = snd_hda_codec_read(codec, nid, 0,
                                     AC_VERB_GET_POWER_STATE, 0);
+       if (sup)
+               snd_iprintf(buffer, "  Power states: %s\n",
+                           bits_names(sup, names, ARRAY_SIZE(names)));
+
        snd_iprintf(buffer, "  Power: setting=%s, actual=%s\n",
                    get_pwr_state(pwr & AC_PWRST_SETTING),
                    get_pwr_state((pwr & AC_PWRST_ACTUAL) >>
@@ -457,6 +523,7 @@ static void print_gpio(struct snd_info_buffer *buffer,
                            (data & (1<<i)) ? 1 : 0,
                            (unsol & (1<<i)) ? 1 : 0);
        /* FIXME: add GPO and GPI pin information */
+       print_nid_mixers(buffer, codec, nid);
 }
 
 static void print_codec_info(struct snd_info_entry *entry,
@@ -536,6 +603,9 @@ static void print_codec_info(struct snd_info_entry *entry,
                        snd_iprintf(buffer, " CP");
                snd_iprintf(buffer, "\n");
 
+               print_nid_mixers(buffer, codec, nid);
+               print_nid_pcms(buffer, codec, nid);
+
                /* volume knob is a special widget that always have connection
                 * list
                 */
index 2d603f6..455a049 100644 (file)
@@ -156,15 +156,19 @@ static const char *ad_slave_sws[] = {
 
 static void ad198x_free_kctls(struct hda_codec *codec);
 
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
 /* additional beep mixers; the actual parameters are overwritten at build */
 static struct snd_kcontrol_new ad_beep_mixer[] = {
        HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Beep Playback Switch", 0, 0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT),
        { } /* end */
 };
 
 #define set_beep_amp(spec, nid, idx, dir) \
        ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
+#else
+#define set_beep_amp(spec, nid, idx, dir) /* NOP */
+#endif
 
 static int ad198x_build_controls(struct hda_codec *codec)
 {
@@ -194,6 +198,7 @@ static int ad198x_build_controls(struct hda_codec *codec)
        }
 
        /* create beep controls if needed */
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
        if (spec->beep_amp) {
                struct snd_kcontrol_new *knew;
                for (knew = ad_beep_mixer; knew->name; knew++) {
@@ -202,11 +207,14 @@ static int ad198x_build_controls(struct hda_codec *codec)
                        if (!kctl)
                                return -ENOMEM;
                        kctl->private_value = spec->beep_amp;
-                       err = snd_hda_ctl_add(codec, kctl);
+                       err = snd_hda_ctl_add(codec,
+                                               get_amp_nid_(spec->beep_amp),
+                                               kctl);
                        if (err < 0)
                                return err;
                }
        }
+#endif
 
        /* if we have no master control, let's create it */
        if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
@@ -712,10 +720,10 @@ static struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = {
 static void ad1986a_automic(struct hda_codec *codec)
 {
        unsigned int present;
-       present = snd_hda_codec_read(codec, 0x1f, 0, AC_VERB_GET_PIN_SENSE, 0);
+       present = snd_hda_jack_detect(codec, 0x1f);
        /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
        snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
-                           (present & AC_PINSENSE_PRESENCE) ? 0 : 2);
+                           present ? 0 : 2);
 }
 
 #define AD1986A_MIC_EVENT              0x36
@@ -754,10 +762,8 @@ static void ad1986a_update_hp(struct hda_codec *codec)
 static void ad1986a_hp_automute(struct hda_codec *codec)
 {
        struct ad198x_spec *spec = codec->spec;
-       unsigned int present;
 
-       present = snd_hda_codec_read(codec, 0x1a, 0, AC_VERB_GET_PIN_SENSE, 0);
-       spec->jack_present = !!(present & 0x80000000);
+       spec->jack_present = snd_hda_jack_detect(codec, 0x1a);
        if (spec->inv_jack_detect)
                spec->jack_present = !spec->jack_present;
        ad1986a_update_hp(codec);
@@ -1547,8 +1553,7 @@ static void ad1981_hp_automute(struct hda_codec *codec)
 {
        unsigned int present;
 
-       present = snd_hda_codec_read(codec, 0x06, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       present = snd_hda_jack_detect(codec, 0x06);
        snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
                                 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
 }
@@ -1568,8 +1573,7 @@ static void ad1981_hp_automic(struct hda_codec *codec)
        };
        unsigned int present;
 
-       present = snd_hda_codec_read(codec, 0x08, 0,
-                                AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       present = snd_hda_jack_detect(codec, 0x08);
        if (present)
                snd_hda_sequence_write(codec, mic_jack_on);
        else
@@ -2524,7 +2528,7 @@ static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
 {
        if ((res >> 26) != AD1988_HP_EVENT)
                return;
-       if (snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0) & (1 << 31))
+       if (snd_hda_jack_detect(codec, 0x11))
                snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
        else
                snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
@@ -2569,6 +2573,8 @@ static int add_control(struct ad198x_spec *spec, int type, const char *name,
        knew->name = kstrdup(name, GFP_KERNEL);
        if (! knew->name)
                return -ENOMEM;
+       if (get_amp_nid_(val))
+               knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val);
        knew->private_value = val;
        return 0;
 }
@@ -3768,8 +3774,7 @@ static void ad1884a_hp_automute(struct hda_codec *codec)
 {
        unsigned int present;
 
-       present = snd_hda_codec_read(codec, 0x11, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       present = snd_hda_jack_detect(codec, 0x11);
        snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
                                 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
        snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
@@ -3781,8 +3786,7 @@ static void ad1884a_hp_automic(struct hda_codec *codec)
 {
        unsigned int present;
 
-       present = snd_hda_codec_read(codec, 0x14, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       present = snd_hda_jack_detect(codec, 0x14);
        snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
                            present ? 0 : 1);
 }
@@ -3817,13 +3821,9 @@ static void ad1884a_laptop_automute(struct hda_codec *codec)
 {
        unsigned int present;
 
-       present = snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0);
-       present &= AC_PINSENSE_PRESENCE;
-       if (!present) {
-               present = snd_hda_codec_read(codec, 0x12, 0,
-                                            AC_VERB_GET_PIN_SENSE, 0);
-               present &= AC_PINSENSE_PRESENCE;
-       }
+       present = snd_hda_jack_detect(codec, 0x11);
+       if (!present)
+               present = snd_hda_jack_detect(codec, 0x12);
        snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
                                 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
        snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
@@ -3835,11 +3835,9 @@ static void ad1884a_laptop_automic(struct hda_codec *codec)
 {
        unsigned int idx;
 
-       if (snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0) &
-           AC_PINSENSE_PRESENCE)
+       if (snd_hda_jack_detect(codec, 0x14))
                idx = 0;
-       else if (snd_hda_codec_read(codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0) &
-                AC_PINSENSE_PRESENCE)
+       else if (snd_hda_jack_detect(codec, 0x1c))
                idx = 4;
        else
                idx = 1;
@@ -4008,8 +4006,7 @@ static void ad1984a_thinkpad_automute(struct hda_codec *codec)
 {
        unsigned int present;
 
-       present = snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0)
-               & AC_PINSENSE_PRESENCE;
+       present = snd_hda_jack_detect(codec, 0x11);
        snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
                                 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
 }
@@ -4117,14 +4114,12 @@ static struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
 /* switch to external mic if plugged */
 static void ad1984a_touchsmart_automic(struct hda_codec *codec)
 {
-       if (snd_hda_codec_read(codec, 0x1c, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000) {
+       if (snd_hda_jack_detect(codec, 0x1c))
                snd_hda_codec_write(codec, 0x0c, 0,
                                     AC_VERB_SET_CONNECT_SEL, 0x4);
-       } else {
+       else
                snd_hda_codec_write(codec, 0x0c, 0,
                                     AC_VERB_SET_CONNECT_SEL, 0x5);
-       }
 }
 
 
index d08353d..af47801 100644 (file)
@@ -144,7 +144,7 @@ static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
        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, snd_ctl_new1(&knew, codec));
+       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,
@@ -155,7 +155,7 @@ static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
        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, snd_ctl_new1(&knew, codec));
+       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)
index 8ba3068..2439e84 100644 (file)
@@ -500,7 +500,7 @@ static int add_mute(struct hda_codec *codec, const char *name, int index,
        knew.private_value = pval;
        snprintf(tmp, sizeof(tmp), "%s %s Switch", name, dir_sfx[dir]);
        *kctlp = snd_ctl_new1(&knew, codec);
-       return snd_hda_ctl_add(codec, *kctlp);
+       return snd_hda_ctl_add(codec, get_amp_nid_(pval), *kctlp);
 }
 
 static int add_volume(struct hda_codec *codec, const char *name,
@@ -513,7 +513,7 @@ static int add_volume(struct hda_codec *codec, const char *name,
        knew.private_value = pval;
        snprintf(tmp, sizeof(tmp), "%s %s Volume", name, dir_sfx[dir]);
        *kctlp = snd_ctl_new1(&knew, codec);
-       return snd_hda_ctl_add(codec, *kctlp);
+       return snd_hda_ctl_add(codec, get_amp_nid_(pval), *kctlp);
 }
 
 static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac)
@@ -536,14 +536,14 @@ static int add_vmaster(struct hda_codec *codec, hda_nid_t dac)
 
        spec->vmaster_sw =
                snd_ctl_make_virtual_master("Master Playback Switch", NULL);
-       err = snd_hda_ctl_add(codec, spec->vmaster_sw);
+       err = snd_hda_ctl_add(codec, dac, spec->vmaster_sw);
        if (err < 0)
                return err;
 
        snd_hda_set_vmaster_tlv(codec, dac, HDA_OUTPUT, tlv);
        spec->vmaster_vol =
                snd_ctl_make_virtual_master("Master Playback Volume", tlv);
-       err = snd_hda_ctl_add(codec, spec->vmaster_vol);
+       err = snd_hda_ctl_add(codec, dac, spec->vmaster_vol);
        if (err < 0)
                return err;
        return 0;
@@ -756,13 +756,13 @@ static int build_input(struct hda_codec *codec)
                if (!kctl)
                        return -ENOMEM;
                kctl->private_value = (long)spec->capture_bind[i];
-               err = snd_hda_ctl_add(codec, kctl);
+               err = snd_hda_ctl_add(codec, 0, kctl);
                if (err < 0)
                        return err;
        }
        
        if (spec->num_inputs > 1 && !spec->mic_detect) {
-               err = snd_hda_ctl_add(codec,
+               err = snd_hda_ctl_add(codec, 0,
                                      snd_ctl_new1(&cs_capture_source, codec));
                if (err < 0)
                        return err;
@@ -807,7 +807,7 @@ static void cs_automute(struct hda_codec *codec)
 {
        struct cs_spec *spec = codec->spec;
        struct auto_pin_cfg *cfg = &spec->autocfg;
-       unsigned int caps, present, hp_present;
+       unsigned int caps, hp_present;
        hda_nid_t nid;
        int i;
 
@@ -817,12 +817,7 @@ static void cs_automute(struct hda_codec *codec)
                caps = snd_hda_query_pin_caps(codec, nid);
                if (!(caps & AC_PINCAP_PRES_DETECT))
                        continue;
-               if (caps & AC_PINCAP_TRIG_REQ)
-                       snd_hda_codec_read(codec, nid, 0,
-                                          AC_VERB_SET_PIN_SENSE, 0);
-               present = snd_hda_codec_read(codec, nid, 0,
-                                            AC_VERB_GET_PIN_SENSE, 0);
-               hp_present |= (present & AC_PINSENSE_PRESENCE) != 0;
+               hp_present = snd_hda_jack_detect(codec, nid);
                if (hp_present)
                        break;
        }
@@ -844,15 +839,11 @@ static void cs_automic(struct hda_codec *codec)
        struct cs_spec *spec = codec->spec;
        struct auto_pin_cfg *cfg = &spec->autocfg;
        hda_nid_t nid;
-       unsigned int caps, present;
+       unsigned int present;
        
        nid = cfg->input_pins[spec->automic_idx];
-       caps = snd_hda_query_pin_caps(codec, nid);
-       if (caps & AC_PINCAP_TRIG_REQ)
-               snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0);
-       present = snd_hda_codec_read(codec, nid, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0);
-       if (present & AC_PINSENSE_PRESENCE)
+       present = snd_hda_jack_detect(codec, nid);
+       if (present)
                change_cur_input(codec, spec->automic_idx, 0);
        else {
                unsigned int imic = (spec->automic_idx == AUTO_PIN_MIC) ?
index 905859d..a09c03c 100644 (file)
@@ -397,9 +397,7 @@ static void conexant_report_jack(struct hda_codec *codec, hda_nid_t nid)
                for (i = 0; i < spec->jacks.used; i++) {
                        if (jacks->nid == nid) {
                                unsigned int present;
-                               present = snd_hda_codec_read(codec, nid, 0,
-                                               AC_VERB_GET_PIN_SENSE, 0) &
-                                       AC_PINSENSE_PRESENCE;
+                               present = snd_hda_jack_detect(codec, nid);
 
                                present = (present) ? jacks->type : 0 ;
 
@@ -750,8 +748,7 @@ static void cxt5045_hp_automic(struct hda_codec *codec)
        };
        unsigned int present;
 
-       present = snd_hda_codec_read(codec, 0x12, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       present = snd_hda_jack_detect(codec, 0x12);
        if (present)
                snd_hda_sequence_write(codec, mic_jack_on);
        else
@@ -765,8 +762,7 @@ static void cxt5045_hp_automute(struct hda_codec *codec)
        struct conexant_spec *spec = codec->spec;
        unsigned int bits;
 
-       spec->hp_present = snd_hda_codec_read(codec, 0x11, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       spec->hp_present = snd_hda_jack_detect(codec, 0x11);
 
        bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0; 
        snd_hda_codec_amp_stereo(codec, 0x10, HDA_OUTPUT, 0,
@@ -1175,9 +1171,10 @@ static int patch_cxt5045(struct hda_codec *codec)
 
        switch (codec->subsystem_id >> 16) {
        case 0x103c:
-               /* HP laptop has a really bad sound over 0dB on NID 0x17.
-                * Fix max PCM level to 0 dB
-                * (originall it has 0x2b steps with 0dB offset 0x14)
+       case 0x1734:
+               /* HP & Fujitsu-Siemens laptops have really bad sound over 0dB
+                * on NID 0x17. Fix max PCM level to 0 dB
+                * (originally it has 0x2b steps with 0dB offset 0x14)
                 */
                snd_hda_override_amp_caps(codec, 0x17, HDA_INPUT,
                                          (0x14 << AC_AMPCAP_OFFSET_SHIFT) |
@@ -1243,8 +1240,7 @@ static void cxt5047_hp_automute(struct hda_codec *codec)
        struct conexant_spec *spec = codec->spec;
        unsigned int bits;
 
-       spec->hp_present = snd_hda_codec_read(codec, 0x13, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       spec->hp_present = snd_hda_jack_detect(codec, 0x13);
 
        bits = (spec->hp_present || !spec->cur_eapd) ? HDA_AMP_MUTE : 0;
        /* See the note in cxt5047_hp_master_sw_put */
@@ -1267,8 +1263,7 @@ static void cxt5047_hp_automic(struct hda_codec *codec)
        };
        unsigned int present;
 
-       present = snd_hda_codec_read(codec, 0x15, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       present = snd_hda_jack_detect(codec, 0x15);
        if (present)
                snd_hda_sequence_write(codec, mic_jack_on);
        else
@@ -1415,16 +1410,7 @@ static struct snd_kcontrol_new cxt5047_test_mixer[] = {
                .get = conexant_mux_enum_get,
                .put = conexant_mux_enum_put,
        },
-       HDA_CODEC_VOLUME("Input-1 Volume", 0x1a, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Input-1 Switch", 0x1a, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Input-2 Volume", 0x1a, 0x1, HDA_INPUT),
-       HDA_CODEC_MUTE("Input-2 Switch", 0x1a, 0x1, HDA_INPUT),
-       HDA_CODEC_VOLUME("Input-3 Volume", 0x1a, 0x2, HDA_INPUT),
-       HDA_CODEC_MUTE("Input-3 Switch", 0x1a, 0x2, HDA_INPUT),
-       HDA_CODEC_VOLUME("Input-4 Volume", 0x1a, 0x3, HDA_INPUT),
-       HDA_CODEC_MUTE("Input-4 Switch", 0x1a, 0x3, HDA_INPUT),
-       HDA_CODEC_VOLUME("Input-5 Volume", 0x1a, 0x4, HDA_INPUT),
-       HDA_CODEC_MUTE("Input-5 Switch", 0x1a, 0x4, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Boost Volume", 0x1a, 0x0, HDA_OUTPUT),
 
        { } /* end */
 };
@@ -1621,9 +1607,7 @@ static void cxt5051_portb_automic(struct hda_codec *codec)
 
        if (spec->no_auto_mic)
                return;
-       present = snd_hda_codec_read(codec, 0x17, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) &
-               AC_PINSENSE_PRESENCE;
+       present = snd_hda_jack_detect(codec, 0x17);
        snd_hda_codec_write(codec, 0x14, 0,
                            AC_VERB_SET_CONNECT_SEL,
                            present ? 0x01 : 0x00);
@@ -1638,9 +1622,7 @@ static void cxt5051_portc_automic(struct hda_codec *codec)
 
        if (spec->no_auto_mic)
                return;
-       present = snd_hda_codec_read(codec, 0x18, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) &
-               AC_PINSENSE_PRESENCE;
+       present = snd_hda_jack_detect(codec, 0x18);
        if (present)
                spec->cur_adc_idx = 1;
        else
@@ -1661,9 +1643,7 @@ static void cxt5051_hp_automute(struct hda_codec *codec)
 {
        struct conexant_spec *spec = codec->spec;
 
-       spec->hp_present = snd_hda_codec_read(codec, 0x16, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) &
-               AC_PINSENSE_PRESENCE;
+       spec->hp_present = snd_hda_jack_detect(codec, 0x16);
        cxt5051_update_speaker(codec);
 }
 
@@ -2011,8 +1991,47 @@ static void cxt5066_automic(struct hda_codec *codec)
        };
        unsigned int present;
 
-       present = snd_hda_codec_read(codec, 0x1a, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       present = snd_hda_jack_detect(codec, 0x1a);
+       if (present) {
+               snd_printdd("CXT5066: external microphone detected\n");
+               snd_hda_sequence_write(codec, ext_mic_present);
+       } else {
+               snd_printdd("CXT5066: external microphone absent\n");
+               snd_hda_sequence_write(codec, ext_mic_absent);
+       }
+}
+
+/* toggle input of built-in digital mic and mic jack appropriately */
+static void cxt5066_vostro_automic(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       unsigned int present;
+
+       struct hda_verb ext_mic_present[] = {
+               /* enable external mic, port B */
+               {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, spec->ext_mic_bias},
+
+               /* switch to external mic input */
+               {0x17, AC_VERB_SET_CONNECT_SEL, 0},
+               {0x14, AC_VERB_SET_CONNECT_SEL, 0},
+
+               /* disable internal digital mic */
+               {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+               {}
+       };
+       static struct hda_verb ext_mic_absent[] = {
+               /* enable internal mic, port C */
+               {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+
+               /* switch to internal mic input */
+               {0x14, AC_VERB_SET_CONNECT_SEL, 2},
+
+               /* disable external mic, port B */
+               {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+               {}
+       };
+
+       present = snd_hda_jack_detect(codec, 0x1a);
        if (present) {
                snd_printdd("CXT5066: external microphone detected\n");
                snd_hda_sequence_write(codec, ext_mic_present);
@@ -2029,12 +2048,10 @@ static void cxt5066_hp_automute(struct hda_codec *codec)
        unsigned int portA, portD;
 
        /* Port A */
-       portA = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_PIN_SENSE, 0)
-               & AC_PINSENSE_PRESENCE;
+       portA = snd_hda_jack_detect(codec, 0x19);
 
        /* Port D */
-       portD = (snd_hda_codec_read(codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0)
-               & AC_PINSENSE_PRESENCE) << 1;
+       portD = snd_hda_jack_detect(codec, 0x1c);
 
        spec->hp_present = !!(portA | portD);
        snd_printdd("CXT5066: hp automute portA=%x portD=%x present=%d\n",
@@ -2056,6 +2073,20 @@ static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res)
        }
 }
 
+/* unsolicited event for jack sensing */
+static void cxt5066_vostro_event(struct hda_codec *codec, unsigned int res)
+{
+       snd_printdd("CXT5066_vostro: unsol event %x (%x)\n", res, res >> 26);
+       switch (res >> 26) {
+       case CONEXANT_HP_EVENT:
+               cxt5066_hp_automute(codec);
+               break;
+       case CONEXANT_MIC_EVENT:
+               cxt5066_vostro_automic(codec);
+               break;
+       }
+}
+
 static const struct hda_input_mux cxt5066_analog_mic_boost = {
        .num_items = 5,
        .items = {
@@ -2297,6 +2328,67 @@ static struct hda_verb cxt5066_init_verbs_olpc[] = {
        { } /* end */
 };
 
+static struct hda_verb cxt5066_init_verbs_vostro[] = {
+       /* Port A: headphones */
+       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+       {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+       /* Port B: external microphone */
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+       /* Port C: unused */
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+       /* Port D: unused */
+       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+       /* Port E: unused, but has primary EAPD */
+       {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+       {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
+
+       /* Port F: unused */
+       {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+       /* Port G: internal speakers */
+       {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+       /* DAC1 */
+       {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+       /* DAC2: unused */
+       {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+
+       /* Digital microphone port */
+       {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+
+       /* Audio input selectors */
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3},
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+
+       /* Disable SPDIF */
+       {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+       {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+       /* enable unsolicited events for Port A and B */
+       {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
+       {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
+       { } /* end */
+};
+
 static struct hda_verb cxt5066_init_verbs_portd_lo[] = {
        {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
        { } /* end */
@@ -2318,6 +2410,7 @@ enum {
        CXT5066_LAPTOP,                 /* Laptops w/ EAPD support */
        CXT5066_DELL_LAPTOP,    /* Dell Laptop */
        CXT5066_OLPC_XO_1_5,    /* OLPC XO 1.5 */
+       CXT5066_DELL_VOSTO,     /* Dell Vostro 1015i */
        CXT5066_MODELS
 };
 
@@ -2325,6 +2418,7 @@ static const char *cxt5066_models[CXT5066_MODELS] = {
        [CXT5066_LAPTOP]                = "laptop",
        [CXT5066_DELL_LAPTOP]   = "dell-laptop",
        [CXT5066_OLPC_XO_1_5]   = "olpc-xo-1_5",
+       [CXT5066_DELL_VOSTO]    = "dell-vostro"
 };
 
 static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
@@ -2333,6 +2427,7 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
        SND_PCI_QUIRK(0x1028, 0x02f5, "Dell",
                      CXT5066_DELL_LAPTOP),
        SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5),
+       SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTO),
        {}
 };
 
@@ -2400,6 +2495,19 @@ static int patch_cxt5066(struct hda_codec *codec)
                /* input source automatically selected */
                spec->input_mux = NULL;
                break;
+       case CXT5066_DELL_VOSTO:
+               codec->patch_ops.unsol_event = cxt5066_vostro_event;
+               spec->init_verbs[0] = cxt5066_init_verbs_vostro;
+               spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc;
+               spec->mixers[spec->num_mixers++] = cxt5066_mixers;
+               spec->port_d_mode = 0;
+
+               /* no S/PDIF out */
+               spec->multiout.dig_out_nid = 0;
+
+               /* input source automatically selected */
+               spec->input_mux = NULL;
+               break;
        }
 
        return 0;
@@ -2417,6 +2525,8 @@ static struct hda_codec_preset snd_hda_preset_conexant[] = {
          .patch = patch_cxt5051 },
        { .id = 0x14f15066, .name = "CX20582 (Pebble)",
          .patch = patch_cxt5066 },
+       { .id = 0x14f15067, .name = "CX20583 (Pebble HSF)",
+         .patch = patch_cxt5066 },
        {} /* terminator */
 };
 
@@ -2424,6 +2534,7 @@ MODULE_ALIAS("snd-hda-codec-id:14f15045");
 MODULE_ALIAS("snd-hda-codec-id:14f15047");
 MODULE_ALIAS("snd-hda-codec-id:14f15051");
 MODULE_ALIAS("snd-hda-codec-id:14f15066");
+MODULE_ALIAS("snd-hda-codec-id:14f15067");
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Conexant HD-audio codec");
index 01a18ed..928df59 100644 (file)
 #include "hda_codec.h"
 #include "hda_local.h"
 
-static hda_nid_t cvt_nid;      /* audio converter */
-static hda_nid_t pin_nid;      /* HDMI output pin */
+/*
+ * The HDMI/DisplayPort configuration can be highly dynamic. A graphics device
+ * could support two independent pipes, each of them can be connected to one or
+ * more ports (DVI, HDMI or DisplayPort).
+ *
+ * The HDA correspondence of pipes/ports are converter/pin nodes.
+ */
+#define INTEL_HDMI_CVTS        2
+#define INTEL_HDMI_PINS        3
 
-#define INTEL_HDMI_EVENT_TAG           0x08
+static char *intel_hdmi_pcm_names[INTEL_HDMI_CVTS] = {
+       "INTEL HDMI 0",
+       "INTEL HDMI 1",
+};
 
 struct intel_hdmi_spec {
-       struct hda_multi_out multiout;
-       struct hda_pcm pcm_rec;
-       struct hdmi_eld sink_eld;
+       int num_cvts;
+       int num_pins;
+       hda_nid_t cvt[INTEL_HDMI_CVTS+1];  /* audio sources */
+       hda_nid_t pin[INTEL_HDMI_PINS+1];  /* audio sinks */
+
+       /*
+        * source connection for each pin
+        */
+       hda_nid_t pin_cvt[INTEL_HDMI_PINS+1];
+
+       /*
+        * HDMI sink attached to each pin
+        */
+       struct hdmi_eld sink_eld[INTEL_HDMI_PINS];
+
+       /*
+        * export one pcm per pipe
+        */
+       struct hda_pcm  pcm_rec[INTEL_HDMI_CVTS];
 };
 
 struct hdmi_audio_infoframe {
@@ -184,40 +210,186 @@ static struct cea_channel_speaker_allocation channel_allocations[] = {
 { .ca_index = 0x31,  .speakers = { FRW,  FLW,  RR,  RL,  FC,  LFE,  FR,  FL } },
 };
 
+
+/*
+ * HDA/HDMI auto parsing
+ */
+
+static int hda_node_index(hda_nid_t *nids, hda_nid_t nid)
+{
+       int i;
+
+       for (i = 0; nids[i]; i++)
+               if (nids[i] == nid)
+                       return i;
+
+       snd_printk(KERN_WARNING "HDMI: nid %d not registered\n", nid);
+       return -EINVAL;
+}
+
+static int intel_hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid)
+{
+       struct intel_hdmi_spec *spec = codec->spec;
+       hda_nid_t conn_list[HDA_MAX_CONNECTIONS];
+       int conn_len, curr;
+       int index;
+
+       if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) {
+               snd_printk(KERN_WARNING
+                          "HDMI: pin %d wcaps %#x "
+                          "does not support connection list\n",
+                          pin_nid, get_wcaps(codec, pin_nid));
+               return -EINVAL;
+       }
+
+       conn_len = snd_hda_get_connections(codec, pin_nid, conn_list,
+                                          HDA_MAX_CONNECTIONS);
+       if (conn_len > 1)
+               curr = snd_hda_codec_read(codec, pin_nid, 0,
+                                         AC_VERB_GET_CONNECT_SEL, 0);
+       else
+               curr = 0;
+
+       index = hda_node_index(spec->pin, pin_nid);
+       if (index < 0)
+               return -EINVAL;
+
+       spec->pin_cvt[index] = conn_list[curr];
+
+       return 0;
+}
+
+static void hdmi_get_show_eld(struct hda_codec *codec, hda_nid_t pin_nid,
+                             struct hdmi_eld *eld)
+{
+       if (!snd_hdmi_get_eld(eld, codec, pin_nid))
+               snd_hdmi_show_eld(eld);
+}
+
+static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid,
+                              struct hdmi_eld *eld)
+{
+       int present = snd_hda_pin_sense(codec, pin_nid);
+
+       eld->monitor_present    = !!(present & AC_PINSENSE_PRESENCE);
+       eld->eld_valid          = !!(present & AC_PINSENSE_ELDV);
+
+       if (present & AC_PINSENSE_ELDV)
+               hdmi_get_show_eld(codec, pin_nid, eld);
+}
+
+static int intel_hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
+{
+       struct intel_hdmi_spec *spec = codec->spec;
+
+       if (spec->num_pins >= INTEL_HDMI_PINS) {
+               snd_printk(KERN_WARNING
+                          "HDMI: no space for pin %d \n", pin_nid);
+               return -EINVAL;
+       }
+
+       hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]);
+
+       spec->pin[spec->num_pins] = pin_nid;
+       spec->num_pins++;
+
+       /*
+        * It is assumed that converter nodes come first in the node list and
+        * hence have been registered and usable now.
+        */
+       return intel_hdmi_read_pin_conn(codec, pin_nid);
+}
+
+static int intel_hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid)
+{
+       struct intel_hdmi_spec *spec = codec->spec;
+
+       if (spec->num_cvts >= INTEL_HDMI_CVTS) {
+               snd_printk(KERN_WARNING
+                          "HDMI: no space for converter %d \n", nid);
+               return -EINVAL;
+       }
+
+       spec->cvt[spec->num_cvts] = nid;
+       spec->num_cvts++;
+
+       return 0;
+}
+
+static int intel_hdmi_parse_codec(struct hda_codec *codec)
+{
+       hda_nid_t nid;
+       int i, nodes;
+
+       nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
+       if (!nid || nodes < 0) {
+               snd_printk(KERN_WARNING "HDMI: failed to get afg sub nodes\n");
+               return -EINVAL;
+       }
+
+       for (i = 0; i < nodes; i++, nid++) {
+               unsigned int caps;
+               unsigned int type;
+
+               caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
+               type = get_wcaps_type(caps);
+
+               if (!(caps & AC_WCAP_DIGITAL))
+                       continue;
+
+               switch (type) {
+               case AC_WID_AUD_OUT:
+                       if (intel_hdmi_add_cvt(codec, nid) < 0)
+                               return -EINVAL;
+                       break;
+               case AC_WID_PIN:
+                       caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+                       if (!(caps & AC_PINCAP_HDMI))
+                               continue;
+                       if (intel_hdmi_add_pin(codec, nid) < 0)
+                               return -EINVAL;
+                       break;
+               }
+       }
+
+       return 0;
+}
+
 /*
  * HDMI routines
  */
 
 #ifdef BE_PARANOID
-static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t nid,
+static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
                                int *packet_index, int *byte_index)
 {
        int val;
 
-       val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_INDEX, 0);
+       val = snd_hda_codec_read(codec, pin_nid, 0,
+                                AC_VERB_GET_HDMI_DIP_INDEX, 0);
 
        *packet_index = val >> 5;
        *byte_index = val & 0x1f;
 }
 #endif
 
-static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t nid,
+static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
                                int packet_index, int byte_index)
 {
        int val;
 
        val = (packet_index << 5) | (byte_index & 0x1f);
 
-       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val);
+       snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val);
 }
 
-static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t nid,
+static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid,
                                unsigned char val)
 {
-       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val);
+       snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val);
 }
 
-static void hdmi_enable_output(struct hda_codec *codec)
+static void hdmi_enable_output(struct hda_codec *codec, hda_nid_t pin_nid)
 {
        /* Unmute */
        if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP)
@@ -231,7 +403,8 @@ static void hdmi_enable_output(struct hda_codec *codec)
 /*
  * Enable Audio InfoFrame Transmission
  */
-static void hdmi_start_infoframe_trans(struct hda_codec *codec)
+static void hdmi_start_infoframe_trans(struct hda_codec *codec,
+                                      hda_nid_t pin_nid)
 {
        hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
        snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
@@ -241,59 +414,49 @@ static void hdmi_start_infoframe_trans(struct hda_codec *codec)
 /*
  * Disable Audio InfoFrame Transmission
  */
-static void hdmi_stop_infoframe_trans(struct hda_codec *codec)
+static void hdmi_stop_infoframe_trans(struct hda_codec *codec,
+                                     hda_nid_t pin_nid)
 {
        hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
        snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
                                                AC_DIPXMIT_DISABLE);
 }
 
-static int hdmi_get_channel_count(struct hda_codec *codec)
+static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t nid)
 {
-       return 1 + snd_hda_codec_read(codec, cvt_nid, 0,
+       return 1 + snd_hda_codec_read(codec, nid, 0,
                                        AC_VERB_GET_CVT_CHAN_COUNT, 0);
 }
 
-static void hdmi_set_channel_count(struct hda_codec *codec, int chs)
+static void hdmi_set_channel_count(struct hda_codec *codec,
+                                  hda_nid_t nid, int chs)
 {
-       snd_hda_codec_write(codec, cvt_nid, 0,
-                                       AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
-
-       if (chs != hdmi_get_channel_count(codec))
-               snd_printd(KERN_INFO "HDMI channel count: expect %d, get %d\n",
-                                       chs, hdmi_get_channel_count(codec));
+       if (chs != hdmi_get_channel_count(codec, nid))
+               snd_hda_codec_write(codec, nid, 0,
+                                   AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
 }
 
-static void hdmi_debug_channel_mapping(struct hda_codec *codec)
+static void hdmi_debug_channel_mapping(struct hda_codec *codec, hda_nid_t nid)
 {
 #ifdef CONFIG_SND_DEBUG_VERBOSE
        int i;
        int slot;
 
        for (i = 0; i < 8; i++) {
-               slot = snd_hda_codec_read(codec, cvt_nid, 0,
+               slot = snd_hda_codec_read(codec, nid, 0,
                                                AC_VERB_GET_HDMI_CHAN_SLOT, i);
                printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
-                                               slot >> 4, slot & 0x7);
+                                               slot >> 4, slot & 0xf);
        }
 #endif
 }
 
-static void hdmi_parse_eld(struct hda_codec *codec)
-{
-       struct intel_hdmi_spec *spec = codec->spec;
-       struct hdmi_eld *eld = &spec->sink_eld;
-
-       if (!snd_hdmi_get_eld(eld, codec, pin_nid))
-               snd_hdmi_show_eld(eld);
-}
-
 
 /*
  * Audio InfoFrame routines
  */
 
-static void hdmi_debug_dip_size(struct hda_codec *codec)
+static void hdmi_debug_dip_size(struct hda_codec *codec, hda_nid_t pin_nid)
 {
 #ifdef CONFIG_SND_DEBUG_VERBOSE
        int i;
@@ -310,7 +473,7 @@ static void hdmi_debug_dip_size(struct hda_codec *codec)
 #endif
 }
 
-static void hdmi_clear_dip_buffers(struct hda_codec *codec)
+static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid)
 {
 #ifdef BE_PARANOID
        int i, j;
@@ -339,23 +502,35 @@ static void hdmi_clear_dip_buffers(struct hda_codec *codec)
 #endif
 }
 
-static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
-                                       struct hdmi_audio_infoframe *ai)
+static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *ai)
 {
-       u8 *params = (u8 *)ai;
+       u8 *bytes = (u8 *)ai;
        u8 sum = 0;
        int i;
 
-       hdmi_debug_dip_size(codec);
-       hdmi_clear_dip_buffers(codec); /* be paranoid */
+       ai->checksum = 0;
+
+       for (i = 0; i < sizeof(*ai); i++)
+               sum += bytes[i];
 
-       for (i = 0; i < sizeof(ai); i++)
-               sum += params[i];
        ai->checksum = - sum;
+}
+
+static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
+                                     hda_nid_t pin_nid,
+                                     struct hdmi_audio_infoframe *ai)
+{
+       u8 *bytes = (u8 *)ai;
+       int i;
+
+       hdmi_debug_dip_size(codec, pin_nid);
+       hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */
+
+       hdmi_checksum_audio_infoframe(ai);
 
        hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
-       for (i = 0; i < sizeof(ai); i++)
-               hdmi_write_dip_byte(codec, pin_nid, params[i]);
+       for (i = 0; i < sizeof(*ai); i++)
+               hdmi_write_dip_byte(codec, pin_nid, bytes[i]);
 }
 
 /*
@@ -386,11 +561,11 @@ static void init_channel_allocations(void)
  *
  * TODO: it could select the wrong CA from multiple candidates.
 */
-static int hdmi_setup_channel_allocation(struct hda_codec *codec,
+static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
                                         struct hdmi_audio_infoframe *ai)
 {
        struct intel_hdmi_spec *spec = codec->spec;
-       struct hdmi_eld *eld = &spec->sink_eld;
+       struct hdmi_eld *eld;
        int i;
        int spk_mask = 0;
        int channels = 1 + (ai->CC02_CT47 & 0x7);
@@ -402,6 +577,11 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec,
        if (channels <= 2)
                return 0;
 
+       i = hda_node_index(spec->pin_cvt, nid);
+       if (i < 0)
+               return 0;
+       eld = &spec->sink_eld[i];
+
        /*
         * HDMI sink's ELD info cannot always be retrieved for now, e.g.
         * in console or for audio devices. Assume the highest speakers
@@ -439,8 +619,8 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec,
        return ai->CA;
 }
 
-static void hdmi_setup_channel_mapping(struct hda_codec *codec,
-                                       struct hdmi_audio_infoframe *ai)
+static void hdmi_setup_channel_mapping(struct hda_codec *codec, hda_nid_t nid,
+                                      struct hdmi_audio_infoframe *ai)
 {
        int i;
 
@@ -453,17 +633,41 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec,
         */
 
        for (i = 0; i < 8; i++)
-               snd_hda_codec_write(codec, cvt_nid, 0,
+               snd_hda_codec_write(codec, nid, 0,
                                    AC_VERB_SET_HDMI_CHAN_SLOT,
                                    (i << 4) | i);
 
-       hdmi_debug_channel_mapping(codec);
+       hdmi_debug_channel_mapping(codec, nid);
 }
 
+static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
+                                   struct hdmi_audio_infoframe *ai)
+{
+       u8 *bytes = (u8 *)ai;
+       u8 val;
+       int i;
+
+       if (snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_XMIT, 0)
+                                                           != AC_DIPXMIT_BEST)
+               return false;
+
+       hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
+       for (i = 0; i < sizeof(*ai); i++) {
+               val = snd_hda_codec_read(codec, pin_nid, 0,
+                                        AC_VERB_GET_HDMI_DIP_DATA, 0);
+               if (val != bytes[i])
+                       return false;
+       }
 
-static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
+       return true;
+}
+
+static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
                                        struct snd_pcm_substream *substream)
 {
+       struct intel_hdmi_spec *spec = codec->spec;
+       hda_nid_t pin_nid;
+       int i;
        struct hdmi_audio_infoframe ai = {
                .type           = 0x84,
                .ver            = 0x01,
@@ -471,11 +675,22 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
                .CC02_CT47      = substream->runtime->channels - 1,
        };
 
-       hdmi_setup_channel_allocation(codec, &ai);
-       hdmi_setup_channel_mapping(codec, &ai);
+       hdmi_setup_channel_allocation(codec, nid, &ai);
+       hdmi_setup_channel_mapping(codec, nid, &ai);
 
-       hdmi_fill_audio_infoframe(codec, &ai);
-       hdmi_start_infoframe_trans(codec);
+       for (i = 0; i < spec->num_pins; i++) {
+               if (spec->pin_cvt[i] != nid)
+                       continue;
+               if (!spec->sink_eld[i].monitor_present)
+                       continue;
+
+               pin_nid = spec->pin[i];
+               if (!hdmi_infoframe_uptodate(codec, pin_nid, &ai)) {
+                       hdmi_stop_infoframe_trans(codec, pin_nid);
+                       hdmi_fill_audio_infoframe(codec, pin_nid, &ai);
+                       hdmi_start_infoframe_trans(codec, pin_nid);
+               }
+       }
 }
 
 
@@ -485,27 +700,39 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
 
 static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
 {
+       struct intel_hdmi_spec *spec = codec->spec;
+       int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
        int pind = !!(res & AC_UNSOL_RES_PD);
        int eldv = !!(res & AC_UNSOL_RES_ELDV);
+       int index;
 
        printk(KERN_INFO
-               "HDMI hot plug event: Presence_Detect=%d ELD_Valid=%d\n",
-               pind, eldv);
+               "HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
+               tag, pind, eldv);
+
+       index = hda_node_index(spec->pin, tag);
+       if (index < 0)
+               return;
+
+       spec->sink_eld[index].monitor_present = pind;
+       spec->sink_eld[index].eld_valid = eldv;
 
        if (pind && eldv) {
-               hdmi_parse_eld(codec);
+               hdmi_get_show_eld(codec, spec->pin[index], &spec->sink_eld[index]);
                /* TODO: do real things about ELD */
        }
 }
 
 static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
 {
+       int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
        int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
        int cp_state = !!(res & AC_UNSOL_RES_CP_STATE);
        int cp_ready = !!(res & AC_UNSOL_RES_CP_READY);
 
        printk(KERN_INFO
-               "HDMI content protection event: SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
+               "HDMI CP event: PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
+               tag,
                subtag,
                cp_state,
                cp_ready);
@@ -520,10 +747,11 @@ static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
 
 static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
 {
+       struct intel_hdmi_spec *spec = codec->spec;
        int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
        int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
 
-       if (tag != INTEL_HDMI_EVENT_TAG) {
+       if (hda_node_index(spec->pin, tag) < 0) {
                snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag);
                return;
        }
@@ -538,24 +766,29 @@ static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
  * Callbacks
  */
 
-static int intel_hdmi_playback_pcm_open(struct hda_pcm_stream *hinfo,
-                                       struct hda_codec *codec,
-                                       struct snd_pcm_substream *substream)
+static void hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid,
+                             u32 stream_tag, int format)
 {
-       struct intel_hdmi_spec *spec = codec->spec;
-
-       return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
+       int tag;
+       int fmt;
 
-static int intel_hdmi_playback_pcm_close(struct hda_pcm_stream *hinfo,
-                                        struct hda_codec *codec,
-                                        struct snd_pcm_substream *substream)
-{
-       struct intel_hdmi_spec *spec = codec->spec;
+       tag = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0) >> 4;
+       fmt = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_STREAM_FORMAT, 0);
 
-       hdmi_stop_infoframe_trans(codec);
+       snd_printdd("hdmi_setup_stream: "
+                   "NID=0x%x, %sstream=0x%x, %sformat=0x%x\n",
+                   nid,
+                   tag == stream_tag ? "" : "new-",
+                   stream_tag,
+                   fmt == format ? "" : "new-",
+                   format);
 
-       return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+       if (tag != stream_tag)
+               snd_hda_codec_write(codec, nid, 0,
+                                   AC_VERB_SET_CHANNEL_STREAMID, stream_tag << 4);
+       if (fmt != format)
+               snd_hda_codec_write(codec, nid, 0,
+                                   AC_VERB_SET_STREAM_FORMAT, format);
 }
 
 static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
@@ -564,43 +797,53 @@ static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
                                           unsigned int format,
                                           struct snd_pcm_substream *substream)
 {
-       struct intel_hdmi_spec *spec = codec->spec;
-
-       snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
-                                            format, substream);
+       hdmi_set_channel_count(codec, hinfo->nid,
+                              substream->runtime->channels);
 
-       hdmi_set_channel_count(codec, substream->runtime->channels);
+       hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
 
-       hdmi_setup_audio_infoframe(codec, substream);
+       hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
+       return 0;
+}
 
+static int intel_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                                          struct hda_codec *codec,
+                                          struct snd_pcm_substream *substream)
+{
        return 0;
 }
 
 static struct hda_pcm_stream intel_hdmi_pcm_playback = {
        .substreams = 1,
        .channels_min = 2,
-       .channels_max = 8,
        .ops = {
-               .open    = intel_hdmi_playback_pcm_open,
-               .close   = intel_hdmi_playback_pcm_close,
-               .prepare = intel_hdmi_playback_pcm_prepare
+               .prepare = intel_hdmi_playback_pcm_prepare,
+               .cleanup = intel_hdmi_playback_pcm_cleanup,
        },
 };
 
 static int intel_hdmi_build_pcms(struct hda_codec *codec)
 {
        struct intel_hdmi_spec *spec = codec->spec;
-       struct hda_pcm *info = &spec->pcm_rec;
+       struct hda_pcm *info = spec->pcm_rec;
+       int i;
 
-       codec->num_pcms = 1;
+       codec->num_pcms = spec->num_cvts;
        codec->pcm_info = info;
 
-       /* NID to query formats and rates and setup streams */
-       intel_hdmi_pcm_playback.nid = cvt_nid;
+       for (i = 0; i < codec->num_pcms; i++, info++) {
+               unsigned int chans;
 
-       info->name = "INTEL HDMI";
-       info->pcm_type = HDA_PCM_TYPE_HDMI;
-       info->stream[SNDRV_PCM_STREAM_PLAYBACK] = intel_hdmi_pcm_playback;
+               chans = get_wcaps(codec, spec->cvt[i]);
+               chans = get_wcaps_channels(chans);
+
+               info->name = intel_hdmi_pcm_names[i];
+               info->pcm_type = HDA_PCM_TYPE_HDMI;
+               info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+                                                       intel_hdmi_pcm_playback;
+               info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->cvt[i];
+               info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
+       }
 
        return 0;
 }
@@ -609,29 +852,39 @@ static int intel_hdmi_build_controls(struct hda_codec *codec)
 {
        struct intel_hdmi_spec *spec = codec->spec;
        int err;
+       int i;
 
-       err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
-       if (err < 0)
-               return err;
+       for (i = 0; i < codec->num_pcms; i++) {
+               err = snd_hda_create_spdif_out_ctls(codec, spec->cvt[i]);
+               if (err < 0)
+                       return err;
+       }
 
        return 0;
 }
 
 static int intel_hdmi_init(struct hda_codec *codec)
 {
-       hdmi_enable_output(codec);
+       struct intel_hdmi_spec *spec = codec->spec;
+       int i;
 
-       snd_hda_codec_write(codec, pin_nid, 0,
-                           AC_VERB_SET_UNSOLICITED_ENABLE,
-                           AC_USRSP_EN | INTEL_HDMI_EVENT_TAG);
+       for (i = 0; spec->pin[i]; i++) {
+               hdmi_enable_output(codec, spec->pin[i]);
+               snd_hda_codec_write(codec, spec->pin[i], 0,
+                                   AC_VERB_SET_UNSOLICITED_ENABLE,
+                                   AC_USRSP_EN | spec->pin[i]);
+       }
        return 0;
 }
 
 static void intel_hdmi_free(struct hda_codec *codec)
 {
        struct intel_hdmi_spec *spec = codec->spec;
+       int i;
+
+       for (i = 0; i < spec->num_pins; i++)
+               snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
 
-       snd_hda_eld_proc_free(codec, &spec->sink_eld);
        kfree(spec);
 }
 
@@ -643,49 +896,38 @@ static struct hda_codec_ops intel_hdmi_patch_ops = {
        .unsol_event            = intel_hdmi_unsol_event,
 };
 
-static int do_patch_intel_hdmi(struct hda_codec *codec)
+static int patch_intel_hdmi(struct hda_codec *codec)
 {
        struct intel_hdmi_spec *spec;
+       int i;
 
        spec = kzalloc(sizeof(*spec), GFP_KERNEL);
        if (spec == NULL)
                return -ENOMEM;
 
-       spec->multiout.num_dacs = 0;      /* no analog */
-       spec->multiout.max_channels = 8;
-       spec->multiout.dig_out_nid = cvt_nid;
-
        codec->spec = spec;
+       if (intel_hdmi_parse_codec(codec) < 0) {
+               codec->spec = NULL;
+               kfree(spec);
+               return -EINVAL;
+       }
        codec->patch_ops = intel_hdmi_patch_ops;
 
-       snd_hda_eld_proc_new(codec, &spec->sink_eld);
+       for (i = 0; i < spec->num_pins; i++)
+               snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i);
 
        init_channel_allocations();
 
        return 0;
 }
 
-static int patch_intel_hdmi(struct hda_codec *codec)
-{
-       cvt_nid = 0x02;
-       pin_nid = 0x03;
-       return do_patch_intel_hdmi(codec);
-}
-
-static int patch_intel_hdmi_ibexpeak(struct hda_codec *codec)
-{
-       cvt_nid = 0x02;
-       pin_nid = 0x04;
-       return do_patch_intel_hdmi(codec);
-}
-
 static struct hda_codec_preset snd_hda_preset_intelhdmi[] = {
        { .id = 0x808629fb, .name = "G45 DEVCL",  .patch = patch_intel_hdmi },
        { .id = 0x80862801, .name = "G45 DEVBLC", .patch = patch_intel_hdmi },
        { .id = 0x80862802, .name = "G45 DEVCTG", .patch = patch_intel_hdmi },
        { .id = 0x80862803, .name = "G45 DEVELK", .patch = patch_intel_hdmi },
        { .id = 0x80862804, .name = "G45 DEVIBX", .patch = patch_intel_hdmi },
-       { .id = 0x80860054, .name = "Q57 DEVIBX", .patch = patch_intel_hdmi_ibexpeak },
+       { .id = 0x80860054, .name = "Q57 DEVIBX", .patch = patch_intel_hdmi },
        { .id = 0x10951392, .name = "SiI1392 HDMI",     .patch = patch_intel_hdmi },
        {} /* terminator */
 };
index 24b07c9..d967836 100644 (file)
@@ -961,18 +961,12 @@ static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid,
 static void alc_automute_pin(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       unsigned int present, pincap;
        unsigned int nid = spec->autocfg.hp_pins[0];
        int i;
 
        if (!nid)
                return;
-       pincap = snd_hda_query_pin_caps(codec, nid);
-       if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */
-               snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0);
-       present = snd_hda_codec_read(codec, nid, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0);
-       spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0;
+       spec->jack_present = snd_hda_jack_detect(codec, nid);
        for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
                nid = spec->autocfg.speaker_pins[i];
                if (!nid)
@@ -1012,9 +1006,7 @@ static void alc_mic_automute(struct hda_codec *codec)
 
        cap_nid = spec->capsrc_nids ? spec->capsrc_nids[0] : spec->adc_nids[0];
 
-       present = snd_hda_codec_read(codec, spec->ext_mic.pin, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0);
-       present &= AC_PINSENSE_PRESENCE;
+       present = snd_hda_jack_detect(codec, spec->ext_mic.pin);
        if (present) {
                alive = &spec->ext_mic;
                dead = &spec->int_mic;
@@ -1402,6 +1394,17 @@ static void alc_pick_fixup(struct hda_codec *codec,
                add_verb(codec->spec, fix->verbs);
 }
 
+static int alc_read_coef_idx(struct hda_codec *codec,
+                       unsigned int coef_idx)
+{
+       unsigned int val;
+       snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX,
+                               coef_idx);
+       val = snd_hda_codec_read(codec, 0x20, 0,
+                               AC_VERB_GET_PROC_COEF, 0);
+       return val;
+}
+
 /*
  * ALC888
  */
@@ -1513,7 +1516,7 @@ static struct hda_verb alc888_fujitsu_xa3530_verbs[] = {
 static void alc_automute_amp(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       unsigned int val, mute, pincap;
+       unsigned int mute;
        hda_nid_t nid;
        int i;
 
@@ -1522,13 +1525,7 @@ static void alc_automute_amp(struct hda_codec *codec)
                nid = spec->autocfg.hp_pins[i];
                if (!nid)
                        break;
-               pincap = snd_hda_query_pin_caps(codec, nid);
-               if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */
-                       snd_hda_codec_read(codec, nid, 0,
-                                          AC_VERB_SET_PIN_SENSE, 0);
-               val = snd_hda_codec_read(codec, nid, 0,
-                                        AC_VERB_GET_PIN_SENSE, 0);
-               if (val & AC_PINSENSE_PRESENCE) {
+               if (snd_hda_jack_detect(codec, nid)) {
                        spec->jack_present = 1;
                        break;
                }
@@ -1786,6 +1783,8 @@ static void alc888_acer_aspire_4930g_setup(struct hda_codec *codec)
 
        spec->autocfg.hp_pins[0] = 0x15;
        spec->autocfg.speaker_pins[0] = 0x14;
+       spec->autocfg.speaker_pins[1] = 0x16;
+       spec->autocfg.speaker_pins[2] = 0x17;
 }
 
 static void alc888_acer_aspire_6530g_setup(struct hda_codec *codec)
@@ -2410,12 +2409,14 @@ static const char *alc_slave_sws[] = {
 
 static void alc_free_kctls(struct hda_codec *codec);
 
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
 /* additional beep mixers; the actual parameters are overwritten at build */
 static struct snd_kcontrol_new alc_beep_mixer[] = {
        HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_INPUT),
-       HDA_CODEC_MUTE("Beep Playback Switch", 0, 0, HDA_INPUT),
+       HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_INPUT),
        { } /* end */
 };
+#endif
 
 static int alc_build_controls(struct hda_codec *codec)
 {
@@ -2452,6 +2453,7 @@ static int alc_build_controls(struct hda_codec *codec)
                        return err;
        }
 
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
        /* create beep controls if needed */
        if (spec->beep_amp) {
                struct snd_kcontrol_new *knew;
@@ -2461,11 +2463,13 @@ static int alc_build_controls(struct hda_codec *codec)
                        if (!kctl)
                                return -ENOMEM;
                        kctl->private_value = spec->beep_amp;
-                       err = snd_hda_ctl_add(codec, kctl);
+                       err = snd_hda_ctl_add(codec,
+                                       get_amp_nid_(spec->beep_amp), kctl);
                        if (err < 0)
                                return err;
                }
        }
+#endif
 
        /* if we have no master control, let's create it */
        if (!spec->no_analog &&
@@ -2779,8 +2783,7 @@ static void alc880_uniwill_mic_automute(struct hda_codec *codec)
        unsigned int present;
        unsigned char bits;
 
-       present = snd_hda_codec_read(codec, 0x18, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       present = snd_hda_jack_detect(codec, 0x18);
        bits = present ? HDA_AMP_MUTE : 0;
        snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits);
 }
@@ -3480,7 +3483,7 @@ static int alc_build_pcms(struct hda_codec *codec)
        snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
                 "%s Analog", codec->chip_name);
        info->name = spec->stream_name_analog;
-       
+
        if (spec->stream_analog_playback) {
                if (snd_BUG_ON(!spec->multiout.dac_nids))
                        return -EINVAL;
@@ -4322,10 +4325,26 @@ static int add_control(struct alc_spec *spec, int type, const char *name,
        knew->name = kstrdup(name, GFP_KERNEL);
        if (!knew->name)
                return -ENOMEM;
+       if (get_amp_nid_(val))
+               knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val);
        knew->private_value = val;
        return 0;
 }
 
+static int add_control_with_pfx(struct alc_spec *spec, int type,
+                               const char *pfx, const char *dir,
+                               const char *sfx, unsigned long val)
+{
+       char name[32];
+       snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx);
+       return add_control(spec, type, name, val);
+}
+
+#define add_pb_vol_ctrl(spec, type, pfx, val) \
+       add_control_with_pfx(spec, type, pfx, "Playback", "Volume", val)
+#define add_pb_sw_ctrl(spec, type, pfx, val) \
+       add_control_with_pfx(spec, type, pfx, "Playback", "Switch", val)
+
 #define alc880_is_fixed_pin(nid)       ((nid) >= 0x14 && (nid) <= 0x17)
 #define alc880_fixed_pin_idx(nid)      ((nid) - 0x14)
 #define alc880_is_multi_pin(nid)       ((nid) >= 0x18)
@@ -4379,7 +4398,6 @@ static int alc880_auto_fill_dac_nids(struct alc_spec *spec,
 static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec,
                                             const struct auto_pin_cfg *cfg)
 {
-       char name[32];
        static const char *chname[4] = {
                "Front", "Surround", NULL /*CLFE*/, "Side"
        };
@@ -4392,26 +4410,26 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec,
                nid = alc880_idx_to_mixer(alc880_dac_to_idx(spec->multiout.dac_nids[i]));
                if (i == 2) {
                        /* Center/LFE */
-                       err = add_control(spec, ALC_CTL_WIDGET_VOL,
-                                         "Center Playback Volume",
+                       err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
+                                             "Center",
                                          HDA_COMPOSE_AMP_VAL(nid, 1, 0,
                                                              HDA_OUTPUT));
                        if (err < 0)
                                return err;
-                       err = add_control(spec, ALC_CTL_WIDGET_VOL,
-                                         "LFE Playback Volume",
+                       err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
+                                             "LFE",
                                          HDA_COMPOSE_AMP_VAL(nid, 2, 0,
                                                              HDA_OUTPUT));
                        if (err < 0)
                                return err;
-                       err = add_control(spec, ALC_CTL_BIND_MUTE,
-                                         "Center Playback Switch",
+                       err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
+                                            "Center",
                                          HDA_COMPOSE_AMP_VAL(nid, 1, 2,
                                                              HDA_INPUT));
                        if (err < 0)
                                return err;
-                       err = add_control(spec, ALC_CTL_BIND_MUTE,
-                                         "LFE Playback Switch",
+                       err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
+                                            "LFE",
                                          HDA_COMPOSE_AMP_VAL(nid, 2, 2,
                                                              HDA_INPUT));
                        if (err < 0)
@@ -4423,14 +4441,12 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec,
                                pfx = "Speaker";
                        else
                                pfx = chname[i];
-                       sprintf(name, "%s Playback Volume", pfx);
-                       err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+                       err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
                                          HDA_COMPOSE_AMP_VAL(nid, 3, 0,
                                                              HDA_OUTPUT));
                        if (err < 0)
                                return err;
-                       sprintf(name, "%s Playback Switch", pfx);
-                       err = add_control(spec, ALC_CTL_BIND_MUTE, name,
+                       err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx,
                                          HDA_COMPOSE_AMP_VAL(nid, 3, 2,
                                                              HDA_INPUT));
                        if (err < 0)
@@ -4446,7 +4462,6 @@ static int alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin,
 {
        hda_nid_t nid;
        int err;
-       char name[32];
 
        if (!pin)
                return 0;
@@ -4460,21 +4475,18 @@ static int alc880_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin,
                        spec->multiout.extra_out_nid[0] = nid;
                /* control HP volume/switch on the output mixer amp */
                nid = alc880_idx_to_mixer(alc880_fixed_pin_idx(pin));
-               sprintf(name, "%s Playback Volume", pfx);
-               err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+               err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
                                  HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
                if (err < 0)
                        return err;
-               sprintf(name, "%s Playback Switch", pfx);
-               err = add_control(spec, ALC_CTL_BIND_MUTE, name,
+               err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx,
                                  HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT));
                if (err < 0)
                        return err;
        } else if (alc880_is_multi_pin(pin)) {
                /* set manual connection */
                /* we have only a switch on HP-out PIN */
-               sprintf(name, "%s Playback Switch", pfx);
-               err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+               err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx,
                                  HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
                if (err < 0)
                        return err;
@@ -4487,16 +4499,13 @@ static int new_analog_input(struct alc_spec *spec, hda_nid_t pin,
                            const char *ctlname,
                            int idx, hda_nid_t mix_nid)
 {
-       char name[32];
        int err;
 
-       sprintf(name, "%s Playback Volume", ctlname);
-       err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+       err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname,
                          HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
        if (err < 0)
                return err;
-       sprintf(name, "%s Playback Switch", ctlname);
-       err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+       err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
                          HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
        if (err < 0)
                return err;
@@ -4773,8 +4782,12 @@ static void set_capture_mixer(struct hda_codec *codec)
        }
 }
 
+#ifdef CONFIG_SND_HDA_INPUT_BEEP
 #define set_beep_amp(spec, nid, idx, dir) \
        ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir))
+#else
+#define set_beep_amp(spec, nid, idx, dir) /* NOP */
+#endif
 
 /*
  * OK, here we have finally the patch for ALC880
@@ -5087,11 +5100,8 @@ static struct hda_verb alc260_hp_unsol_verbs[] = {
 static void alc260_hp_automute(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       unsigned int present;
 
-       present = snd_hda_codec_read(codec, 0x10, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0);
-       spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0;
+       spec->jack_present = snd_hda_jack_detect(codec, 0x10);
        alc260_hp_master_update(codec, 0x0f, 0x10, 0x11);
 }
 
@@ -5156,11 +5166,8 @@ static struct hda_verb alc260_hp_3013_unsol_verbs[] = {
 static void alc260_hp_3013_automute(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       unsigned int present;
 
-       present = snd_hda_codec_read(codec, 0x15, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0);
-       spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0;
+       spec->jack_present = snd_hda_jack_detect(codec, 0x15);
        alc260_hp_master_update(codec, 0x15, 0x10, 0x11);
 }
 
@@ -5173,12 +5180,8 @@ static void alc260_hp_3013_unsol_event(struct hda_codec *codec,
 
 static void alc260_hp_3012_automute(struct hda_codec *codec)
 {
-       unsigned int present, bits;
-
-       present = snd_hda_codec_read(codec, 0x10, 0,
-                       AC_VERB_GET_PIN_SENSE, 0) & AC_PINSENSE_PRESENCE;
+       unsigned int bits = snd_hda_jack_detect(codec, 0x10) ? 0 : PIN_OUT;
 
-       bits = present ? 0 : PIN_OUT;
        snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
                            bits);
        snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
@@ -5748,8 +5751,7 @@ static void alc260_replacer_672v_automute(struct hda_codec *codec)
         unsigned int present;
 
        /* speaker --> GPIO Data 0, hp or spdif --> GPIO data 1 */
-        present = snd_hda_codec_read(codec, 0x0f, 0,
-                                     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       present = snd_hda_jack_detect(codec, 0x0f);
        if (present) {
                snd_hda_codec_write_cache(codec, 0x01, 0,
                                          AC_VERB_SET_GPIO_DATA, 1);
@@ -5989,7 +5991,6 @@ static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid,
 {
        hda_nid_t nid_vol;
        unsigned long vol_val, sw_val;
-       char name[32];
        int err;
 
        if (nid >= 0x0f && nid < 0x11) {
@@ -6009,14 +6010,12 @@ static int alc260_add_playback_controls(struct alc_spec *spec, hda_nid_t nid,
 
        if (!(*vol_bits & (1 << nid_vol))) {
                /* first control for the volume widget */
-               snprintf(name, sizeof(name), "%s Playback Volume", pfx);
-               err = add_control(spec, ALC_CTL_WIDGET_VOL, name, vol_val);
+               err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, vol_val);
                if (err < 0)
                        return err;
                *vol_bits |= (1 << nid_vol);
        }
-       snprintf(name, sizeof(name), "%s Playback Switch", pfx);
-       err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, sw_val);
+       err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, sw_val);
        if (err < 0)
                return err;
        return 1;
@@ -8184,12 +8183,8 @@ static void alc883_mitac_setup(struct hda_codec *codec)
 /*
 static void alc883_mitac_mic_automute(struct hda_codec *codec)
 {
-       unsigned int present;
-       unsigned char bits;
+       unsigned char bits = snd_hda_jack_detect(codec, 0x18) ? HDA_AMP_MUTE : 0;
 
-       present = snd_hda_codec_read(codec, 0x18, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       bits = present ? HDA_AMP_MUTE : 0;
        snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits);
 }
 */
@@ -8411,10 +8406,8 @@ static struct hda_channel_mode alc888_3st_hp_modes[3] = {
 /* toggle front-jack and RCA according to the hp-jack state */
 static void alc888_lenovo_ms7195_front_automute(struct hda_codec *codec)
 {
-       unsigned int present;
+       unsigned int present = snd_hda_jack_detect(codec, 0x1b);
 
-       present = snd_hda_codec_read(codec, 0x1b, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
        snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
                                 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
        snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
@@ -8424,10 +8417,8 @@ static void alc888_lenovo_ms7195_front_automute(struct hda_codec *codec)
 /* toggle RCA according to the front-jack state */
 static void alc888_lenovo_ms7195_rca_automute(struct hda_codec *codec)
 {
-       unsigned int present;
+       unsigned int present = snd_hda_jack_detect(codec, 0x14);
 
-       present = snd_hda_codec_read(codec, 0x14, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
        snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
                                 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
 }
@@ -8468,8 +8459,7 @@ static void alc883_clevo_m720_mic_automute(struct hda_codec *codec)
 {
        unsigned int present;
 
-       present = snd_hda_codec_read(codec, 0x18, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       present = snd_hda_jack_detect(codec, 0x18);
        snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1,
                                 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
 }
@@ -8520,24 +8510,16 @@ static void alc883_haier_w66_setup(struct hda_codec *codec)
 
 static void alc883_lenovo_101e_ispeaker_automute(struct hda_codec *codec)
 {
-       unsigned int present;
-       unsigned char bits;
+       int bits = snd_hda_jack_detect(codec, 0x14) ? HDA_AMP_MUTE : 0;
 
-       present = snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0)
-               & AC_PINSENSE_PRESENCE;
-       bits = present ? HDA_AMP_MUTE : 0;
        snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
                                 HDA_AMP_MUTE, bits);
 }
 
 static void alc883_lenovo_101e_all_automute(struct hda_codec *codec)
 {
-       unsigned int present;
-       unsigned char bits;
+       int bits = snd_hda_jack_detect(codec, 0x1b) ? HDA_AMP_MUTE : 0;
 
-       present = snd_hda_codec_read(codec, 0x1b, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-       bits = present ? HDA_AMP_MUTE : 0;
        snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
                                 HDA_AMP_MUTE, bits);
        snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
@@ -8688,8 +8670,7 @@ static void alc889A_mb31_automute(struct hda_codec *codec)
        /* Mute only in 2ch or 4ch mode */
        if (snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_CONNECT_SEL, 0)
            == 0x00) {
-               present = snd_hda_codec_read(codec, 0x15, 0,
-                       AC_VERB_GET_PIN_SENSE, 0) & AC_PINSENSE_PRESENCE;
+               present = snd_hda_jack_detect(codec, 0x15);
                snd_hda_codec_amp_stereo(codec, 0x14,  HDA_OUTPUT, 0,
                        HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
                snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
@@ -10032,10 +10013,8 @@ static void alc262_hp_master_update(struct hda_codec *codec)
 static void alc262_hp_bpc_automute(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       unsigned int presence;
-       presence = snd_hda_codec_read(codec, 0x1b, 0,
-                                     AC_VERB_GET_PIN_SENSE, 0);
-       spec->jack_present = !!(presence & AC_PINSENSE_PRESENCE);
+
+       spec->jack_present = snd_hda_jack_detect(codec, 0x1b);
        alc262_hp_master_update(codec);
 }
 
@@ -10049,10 +10028,8 @@ static void alc262_hp_bpc_unsol_event(struct hda_codec *codec, unsigned int res)
 static void alc262_hp_wildwest_automute(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       unsigned int presence;
-       presence = snd_hda_codec_read(codec, 0x15, 0,
-                                     AC_VERB_GET_PIN_SENSE, 0);
-       spec->jack_present = !!(presence & AC_PINSENSE_PRESENCE);
+
+       spec->jack_present = snd_hda_jack_detect(codec, 0x15);
        alc262_hp_master_update(codec);
 }
 
@@ -10286,13 +10263,8 @@ static void alc262_hippo_automute(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
        hda_nid_t hp_nid = spec->autocfg.hp_pins[0];
-       unsigned int present;
 
-       /* need to execute and sync at first */
-       snd_hda_codec_read(codec, hp_nid, 0, AC_VERB_SET_PIN_SENSE, 0);
-       present = snd_hda_codec_read(codec, hp_nid, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0);
-       spec->jack_present = (present & 0x80000000) != 0;
+       spec->jack_present = snd_hda_jack_detect(codec, hp_nid);
        alc262_hippo_master_update(codec);
 }
 
@@ -10618,21 +10590,8 @@ static void alc262_fujitsu_automute(struct hda_codec *codec, int force)
        unsigned int mute;
 
        if (force || !spec->sense_updated) {
-               unsigned int present;
-               /* need to execute and sync at first */
-               snd_hda_codec_read(codec, 0x14, 0, AC_VERB_SET_PIN_SENSE, 0);
-               /* check laptop HP jack */
-               present = snd_hda_codec_read(codec, 0x14, 0,
-                                            AC_VERB_GET_PIN_SENSE, 0);
-               /* need to execute and sync at first */
-               snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0);
-               /* check docking HP jack */
-               present |= snd_hda_codec_read(codec, 0x1b, 0,
-                                             AC_VERB_GET_PIN_SENSE, 0);
-               if (present & AC_PINSENSE_PRESENCE)
-                       spec->jack_present = 1;
-               else
-                       spec->jack_present = 0;
+               spec->jack_present = snd_hda_jack_detect(codec, 0x14) ||
+                                    snd_hda_jack_detect(codec, 0x1b);
                spec->sense_updated = 1;
        }
        /* unmute internal speaker only if both HPs are unplugged and
@@ -10677,12 +10636,7 @@ static void alc262_lenovo_3000_automute(struct hda_codec *codec, int force)
        unsigned int mute;
 
        if (force || !spec->sense_updated) {
-               unsigned int present_int_hp;
-               /* need to execute and sync at first */
-               snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0);
-               present_int_hp = snd_hda_codec_read(codec, 0x1b, 0,
-                                       AC_VERB_GET_PIN_SENSE, 0);
-               spec->jack_present = (present_int_hp & 0x80000000) != 0;
+               spec->jack_present = snd_hda_jack_detect(codec, 0x1b);
                spec->sense_updated = 1;
        }
        if (spec->jack_present) {
@@ -10874,12 +10828,7 @@ static void alc262_ultra_automute(struct hda_codec *codec)
        mute = 0;
        /* auto-mute only when HP is used as HP */
        if (!spec->cur_mux[0]) {
-               unsigned int present;
-               /* need to execute and sync at first */
-               snd_hda_codec_read(codec, 0x15, 0, AC_VERB_SET_PIN_SENSE, 0);
-               present = snd_hda_codec_read(codec, 0x15, 0,
-                                            AC_VERB_GET_PIN_SENSE, 0);
-               spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0;
+               spec->jack_present = snd_hda_jack_detect(codec, 0x15);
                if (spec->jack_present)
                        mute = HDA_AMP_MUTE;
        }
@@ -10956,7 +10905,6 @@ static int alc262_check_volbit(hda_nid_t nid)
 static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid,
                                  const char *pfx, int *vbits)
 {
-       char name[32];
        unsigned long val;
        int vbit;
 
@@ -10966,28 +10914,25 @@ static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid,
        if (*vbits & vbit) /* a volume control for this mixer already there */
                return 0;
        *vbits |= vbit;
-       snprintf(name, sizeof(name), "%s Playback Volume", pfx);
        if (vbit == 2)
                val = HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, HDA_OUTPUT);
        else
                val = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT);
-       return add_control(spec, ALC_CTL_WIDGET_VOL, name, val);
+       return add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx, val);
 }
 
 static int alc262_add_out_sw_ctl(struct alc_spec *spec, hda_nid_t nid,
                                 const char *pfx)
 {
-       char name[32];
        unsigned long val;
 
        if (!nid)
                return 0;
-       snprintf(name, sizeof(name), "%s Playback Switch", pfx);
        if (nid == 0x16)
                val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT);
        else
                val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
-       return add_control(spec, ALC_CTL_WIDGET_MUTE, name, val);
+       return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, val);
 }
 
 /* add playback controls from the parsed DAC table */
@@ -11463,8 +11408,10 @@ static struct snd_pci_quirk alc262_cfg_tbl[] = {
        SND_PCI_QUIRK(0x104d, 0x9025, "Sony VAIO Z21MN", ALC262_TOSHIBA_S06),
        SND_PCI_QUIRK(0x104d, 0x9035, "Sony VAIO VGN-FW170J", ALC262_AUTO),
        SND_PCI_QUIRK(0x104d, 0x9047, "Sony VAIO Type G", ALC262_AUTO),
+#if 0 /* disable the quirk since model=auto works better in recent versions */
        SND_PCI_QUIRK_MASK(0x104d, 0xff00, 0x9000, "Sony VAIO",
                           ALC262_SONY_ASSAMD),
+#endif
        SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba dynabook SS RX1",
                      ALC262_TOSHIBA_RX1),
        SND_PCI_QUIRK(0x1179, 0xff7b, "Toshiba S06", ALC262_TOSHIBA_S06),
@@ -11923,10 +11870,7 @@ static void alc268_acer_automute(struct hda_codec *codec, int force)
        unsigned int mute;
 
        if (force || !spec->sense_updated) {
-               unsigned int present;
-               present = snd_hda_codec_read(codec, 0x14, 0,
-                                        AC_VERB_GET_PIN_SENSE, 0);
-               spec->jack_present = (present & 0x80000000) != 0;
+               spec->jack_present = snd_hda_jack_detect(codec, 0x14);
                spec->sense_updated = 1;
        }
        if (spec->jack_present)
@@ -12045,8 +11989,7 @@ static void alc268_aspire_one_speaker_automute(struct hda_codec *codec)
        unsigned int present;
        unsigned char bits;
 
-       present = snd_hda_codec_read(codec, 0x15, 0,
-                               AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       present = snd_hda_jack_detect(codec, 0x15);
        bits = present ? AMP_IN_MUTE(0) : 0;
        snd_hda_codec_amp_stereo(codec, 0x0f, HDA_INPUT, 0,
                                AMP_IN_MUTE(0), bits);
@@ -12327,11 +12270,9 @@ static struct snd_kcontrol_new alc268_test_mixer[] = {
 static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
                                    const char *ctlname, int idx)
 {
-       char name[32];
        hda_nid_t dac;
        int err;
 
-       sprintf(name, "%s Playback Volume", ctlname);
        switch (nid) {
        case 0x14:
        case 0x16:
@@ -12345,7 +12286,7 @@ static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
        }
        if (spec->multiout.dac_nids[0] != dac &&
            spec->multiout.dac_nids[1] != dac) {
-               err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+               err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname,
                                  HDA_COMPOSE_AMP_VAL(dac, 3, idx,
                                                      HDA_OUTPUT));
                if (err < 0)
@@ -12353,12 +12294,11 @@ static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
                spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac;
        }
 
-       sprintf(name, "%s Playback Switch", ctlname);
        if (nid != 0x16)
-               err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+               err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
                          HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT));
        else /* mono */
-               err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+               err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname,
                          HDA_COMPOSE_AMP_VAL(nid, 2, idx, HDA_OUTPUT));
        if (err < 0)
                return err;
@@ -12388,8 +12328,7 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec,
 
        nid = cfg->speaker_pins[0];
        if (nid == 0x1d) {
-               err = add_control(spec, ALC_CTL_WIDGET_VOL,
-                                 "Speaker Playback Volume",
+               err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, "Speaker",
                                  HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
                if (err < 0)
                        return err;
@@ -12407,8 +12346,7 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec,
 
        nid = cfg->line_out_pins[1] | cfg->line_out_pins[2];
        if (nid == 0x16) {
-               err = add_control(spec, ALC_CTL_WIDGET_MUTE,
-                                 "Mono Playback Switch",
+               err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, "Mono",
                                  HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT));
                if (err < 0)
                        return err;
@@ -13034,8 +12972,7 @@ static void alc269_quanta_fl1_speaker_automute(struct hda_codec *codec)
        unsigned int present;
        unsigned char bits;
 
-       present = snd_hda_codec_read(codec, 0x15, 0,
-                       AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       present = snd_hda_jack_detect(codec, 0x15);
        bits = present ? AMP_IN_MUTE(0) : 0;
        snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
                        AMP_IN_MUTE(0), bits);
@@ -13060,12 +12997,10 @@ static void alc269_lifebook_speaker_automute(struct hda_codec *codec)
        unsigned char bits;
 
        /* Check laptop headphone socket */
-       present = snd_hda_codec_read(codec, 0x15, 0,
-                       AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       present = snd_hda_jack_detect(codec, 0x15);
 
        /* Check port replicator headphone socket */
-       present |= snd_hda_codec_read(codec, 0x1a, 0,
-                       AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       present |= snd_hda_jack_detect(codec, 0x1a);
 
        bits = present ? AMP_IN_MUTE(0) : 0;
        snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
@@ -13089,11 +13024,8 @@ static void alc269_lifebook_mic_autoswitch(struct hda_codec *codec)
        unsigned int present_laptop;
        unsigned int present_dock;
 
-       present_laptop = snd_hda_codec_read(codec, 0x18, 0,
-                               AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-
-       present_dock = snd_hda_codec_read(codec, 0x1b, 0,
-                               AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       present_laptop  = snd_hda_jack_detect(codec, 0x18);
+       present_dock    = snd_hda_jack_detect(codec, 0x1b);
 
        /* Laptop mic port overrides dock mic port, design decision */
        if (present_dock)
@@ -13178,8 +13110,7 @@ static void alc269_speaker_automute(struct hda_codec *codec)
        unsigned int present;
        unsigned char bits;
 
-       present = snd_hda_codec_read(codec, 0x15, 0,
-                               AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       present = snd_hda_jack_detect(codec, 0x15);
        bits = present ? AMP_IN_MUTE(0) : 0;
        snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
                                AMP_IN_MUTE(0), bits);
@@ -13525,6 +13456,15 @@ static int patch_alc269(struct hda_codec *codec)
 
        alc_fix_pll_init(codec, 0x20, 0x04, 15);
 
+       if ((alc_read_coef_idx(codec, 0) & 0x00f0) == 0x0010){
+               kfree(codec->chip_name);
+               codec->chip_name = kstrdup("ALC259", GFP_KERNEL);
+               if (!codec->chip_name) {
+                       alc_free(codec);
+                       return -ENOMEM;
+               }
+       }
+
        board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST,
                                                  alc269_models,
                                                  alc269_cfg_tbl);
@@ -14157,10 +14097,8 @@ static struct hda_verb alc861_toshiba_init_verbs[] = {
 /* toggle speaker-output according to the hp-jack state */
 static void alc861_toshiba_automute(struct hda_codec *codec)
 {
-       unsigned int present;
+       unsigned int present = snd_hda_jack_detect(codec, 0x0f);
 
-       present = snd_hda_codec_read(codec, 0x0f, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
        snd_hda_codec_amp_stereo(codec, 0x16, HDA_INPUT, 0,
                                 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
        snd_hda_codec_amp_stereo(codec, 0x1a, HDA_INPUT, 3,
@@ -14260,9 +14198,7 @@ static int alc861_auto_fill_dac_nids(struct hda_codec *codec,
 static int alc861_create_out_sw(struct hda_codec *codec, const char *pfx,
                                hda_nid_t nid, unsigned int chs)
 {
-       char name[32];
-       snprintf(name, sizeof(name), "%s Playback Switch", pfx);
-       return add_control(codec->spec, ALC_CTL_WIDGET_MUTE, name,
+       return add_pb_sw_ctrl(codec->spec, ALC_CTL_WIDGET_MUTE, pfx,
                           HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
 }
 
@@ -14627,6 +14563,27 @@ static struct alc_config_preset alc861_presets[] = {
        },
 };
 
+/* Pin config fixes */
+enum {
+       PINFIX_FSC_AMILO_PI1505,
+};
+
+static struct alc_pincfg alc861_fsc_amilo_pi1505_pinfix[] = {
+       { 0x0b, 0x0221101f }, /* HP */
+       { 0x0f, 0x90170310 }, /* speaker */
+       { }
+};
+
+static const struct alc_fixup alc861_fixups[] = {
+       [PINFIX_FSC_AMILO_PI1505] = {
+               .pins = alc861_fsc_amilo_pi1505_pinfix
+       },
+};
+
+static struct snd_pci_quirk alc861_fixup_tbl[] = {
+       SND_PCI_QUIRK(0x1734, 0x10c7, "FSC Amilo Pi1505", PINFIX_FSC_AMILO_PI1505),
+       {}
+};
 
 static int patch_alc861(struct hda_codec *codec)
 {
@@ -14650,6 +14607,8 @@ static int patch_alc861(struct hda_codec *codec)
                board_config = ALC861_AUTO;
        }
 
+       alc_pick_fixup(codec, alc861_fixup_tbl, alc861_fixups);
+
        if (board_config == ALC861_AUTO) {
                /* automatic parse from the BIOS config */
                err = alc861_parse_auto_config(codec);
@@ -15067,9 +15026,9 @@ static void alc861vd_lenovo_mic_automute(struct hda_codec *codec)
        unsigned int present;
        unsigned char bits;
 
-       present = snd_hda_codec_read(codec, 0x18, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       present = snd_hda_jack_detect(codec, 0x18);
        bits = present ? HDA_AMP_MUTE : 0;
+
        snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1,
                                 HDA_AMP_MUTE, bits);
 }
@@ -15386,7 +15345,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)
 {
-       char name[32];
        static const char *chname[4] = {"Front", "Surround", "CLFE", "Side"};
        hda_nid_t nid_v, nid_s;
        int i, err;
@@ -15403,26 +15361,26 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
 
                if (i == 2) {
                        /* Center/LFE */
-                       err = add_control(spec, ALC_CTL_WIDGET_VOL,
-                                         "Center Playback Volume",
+                       err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
+                                             "Center",
                                          HDA_COMPOSE_AMP_VAL(nid_v, 1, 0,
                                                              HDA_OUTPUT));
                        if (err < 0)
                                return err;
-                       err = add_control(spec, ALC_CTL_WIDGET_VOL,
-                                         "LFE Playback Volume",
+                       err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL,
+                                             "LFE",
                                          HDA_COMPOSE_AMP_VAL(nid_v, 2, 0,
                                                              HDA_OUTPUT));
                        if (err < 0)
                                return err;
-                       err = add_control(spec, ALC_CTL_BIND_MUTE,
-                                         "Center Playback Switch",
+                       err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
+                                            "Center",
                                          HDA_COMPOSE_AMP_VAL(nid_s, 1, 2,
                                                              HDA_INPUT));
                        if (err < 0)
                                return err;
-                       err = add_control(spec, ALC_CTL_BIND_MUTE,
-                                         "LFE Playback Switch",
+                       err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE,
+                                            "LFE",
                                          HDA_COMPOSE_AMP_VAL(nid_s, 2, 2,
                                                              HDA_INPUT));
                        if (err < 0)
@@ -15437,8 +15395,7 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
                                        pfx = "PCM";
                        } else
                                pfx = chname[i];
-                       sprintf(name, "%s Playback Volume", pfx);
-                       err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+                       err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
                                          HDA_COMPOSE_AMP_VAL(nid_v, 3, 0,
                                                              HDA_OUTPUT));
                        if (err < 0)
@@ -15446,8 +15403,7 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
                        if (cfg->line_outs == 1 &&
                            cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
                                pfx = "Speaker";
-                       sprintf(name, "%s Playback Switch", pfx);
-                       err = add_control(spec, ALC_CTL_BIND_MUTE, name,
+                       err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx,
                                          HDA_COMPOSE_AMP_VAL(nid_s, 3, 2,
                                                              HDA_INPUT));
                        if (err < 0)
@@ -15465,7 +15421,6 @@ static int alc861vd_auto_create_extra_out(struct alc_spec *spec,
 {
        hda_nid_t nid_v, nid_s;
        int err;
-       char name[32];
 
        if (!pin)
                return 0;
@@ -15483,21 +15438,18 @@ static int alc861vd_auto_create_extra_out(struct alc_spec *spec,
                nid_s = alc861vd_idx_to_mixer_switch(
                                alc880_fixed_pin_idx(pin));
 
-               sprintf(name, "%s Playback Volume", pfx);
-               err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+               err = add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
                                  HDA_COMPOSE_AMP_VAL(nid_v, 3, 0, HDA_OUTPUT));
                if (err < 0)
                        return err;
-               sprintf(name, "%s Playback Switch", pfx);
-               err = add_control(spec, ALC_CTL_BIND_MUTE, name,
+               err = add_pb_sw_ctrl(spec, ALC_CTL_BIND_MUTE, pfx,
                                  HDA_COMPOSE_AMP_VAL(nid_s, 3, 2, HDA_INPUT));
                if (err < 0)
                        return err;
        } else if (alc880_is_multi_pin(pin)) {
                /* set manual connection */
                /* we have only a switch on HP-out PIN */
-               sprintf(name, "%s Playback Switch", pfx);
-               err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+               err = add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx,
                                  HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
                if (err < 0)
                        return err;
@@ -16387,9 +16339,9 @@ static void alc662_lenovo_101e_ispeaker_automute(struct hda_codec *codec)
        unsigned int present;
        unsigned char bits;
 
-       present = snd_hda_codec_read(codec, 0x14, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       present = snd_hda_jack_detect(codec, 0x14);
        bits = present ? HDA_AMP_MUTE : 0;
+
        snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
                                 HDA_AMP_MUTE, bits);
 }
@@ -16399,9 +16351,9 @@ static void alc662_lenovo_101e_all_automute(struct hda_codec *codec)
        unsigned int present;
        unsigned char bits;
 
-       present = snd_hda_codec_read(codec, 0x1b, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       present = snd_hda_jack_detect(codec, 0x1b);
        bits = present ? HDA_AMP_MUTE : 0;
+
        snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
                                 HDA_AMP_MUTE, bits);
        snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
@@ -16460,9 +16412,7 @@ static void alc663_m51va_speaker_automute(struct hda_codec *codec)
        unsigned int present;
        unsigned char bits;
 
-       present = snd_hda_codec_read(codec, 0x21, 0,
-                       AC_VERB_GET_PIN_SENSE, 0)
-                       & AC_PINSENSE_PRESENCE;
+       present = snd_hda_jack_detect(codec, 0x21);
        bits = present ? HDA_AMP_MUTE : 0;
        snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
                                AMP_IN_MUTE(0), bits);
@@ -16475,9 +16425,7 @@ static void alc663_21jd_two_speaker_automute(struct hda_codec *codec)
        unsigned int present;
        unsigned char bits;
 
-       present = snd_hda_codec_read(codec, 0x21, 0,
-                       AC_VERB_GET_PIN_SENSE, 0)
-                       & AC_PINSENSE_PRESENCE;
+       present = snd_hda_jack_detect(codec, 0x21);
        bits = present ? HDA_AMP_MUTE : 0;
        snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
                                AMP_IN_MUTE(0), bits);
@@ -16494,9 +16442,7 @@ static void alc663_15jd_two_speaker_automute(struct hda_codec *codec)
        unsigned int present;
        unsigned char bits;
 
-       present = snd_hda_codec_read(codec, 0x15, 0,
-                       AC_VERB_GET_PIN_SENSE, 0)
-                       & AC_PINSENSE_PRESENCE;
+       present = snd_hda_jack_detect(codec, 0x15);
        bits = present ? HDA_AMP_MUTE : 0;
        snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
                                AMP_IN_MUTE(0), bits);
@@ -16513,9 +16459,7 @@ static void alc662_f5z_speaker_automute(struct hda_codec *codec)
        unsigned int present;
        unsigned char bits;
 
-       present = snd_hda_codec_read(codec, 0x1b, 0,
-                       AC_VERB_GET_PIN_SENSE, 0)
-                       & AC_PINSENSE_PRESENCE;
+       present = snd_hda_jack_detect(codec, 0x1b);
        bits = present ? 0 : PIN_OUT;
        snd_hda_codec_write(codec, 0x14, 0,
                         AC_VERB_SET_PIN_WIDGET_CONTROL, bits);
@@ -16525,12 +16469,8 @@ static void alc663_two_hp_m1_speaker_automute(struct hda_codec *codec)
 {
        unsigned int present1, present2;
 
-       present1 = snd_hda_codec_read(codec, 0x21, 0,
-                       AC_VERB_GET_PIN_SENSE, 0)
-                       & AC_PINSENSE_PRESENCE;
-       present2 = snd_hda_codec_read(codec, 0x15, 0,
-                       AC_VERB_GET_PIN_SENSE, 0)
-                       & AC_PINSENSE_PRESENCE;
+       present1 = snd_hda_jack_detect(codec, 0x21);
+       present2 = snd_hda_jack_detect(codec, 0x15);
 
        if (present1 || present2) {
                snd_hda_codec_write_cache(codec, 0x14, 0,
@@ -16545,12 +16485,8 @@ static void alc663_two_hp_m2_speaker_automute(struct hda_codec *codec)
 {
        unsigned int present1, present2;
 
-       present1 = snd_hda_codec_read(codec, 0x1b, 0,
-                               AC_VERB_GET_PIN_SENSE, 0)
-                               & AC_PINSENSE_PRESENCE;
-       present2 = snd_hda_codec_read(codec, 0x15, 0,
-                               AC_VERB_GET_PIN_SENSE, 0)
-                               & AC_PINSENSE_PRESENCE;
+       present1 = snd_hda_jack_detect(codec, 0x1b);
+       present2 = snd_hda_jack_detect(codec, 0x15);
 
        if (present1 || present2) {
                snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
@@ -16710,9 +16646,7 @@ static void alc663_g71v_hp_automute(struct hda_codec *codec)
        unsigned int present;
        unsigned char bits;
 
-       present = snd_hda_codec_read(codec, 0x21, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0)
-               & AC_PINSENSE_PRESENCE;
+       present = snd_hda_jack_detect(codec, 0x21);
        bits = present ? HDA_AMP_MUTE : 0;
        snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
                                 HDA_AMP_MUTE, bits);
@@ -16725,9 +16659,7 @@ static void alc663_g71v_front_automute(struct hda_codec *codec)
        unsigned int present;
        unsigned char bits;
 
-       present = snd_hda_codec_read(codec, 0x15, 0,
-                                    AC_VERB_GET_PIN_SENSE, 0)
-               & AC_PINSENSE_PRESENCE;
+       present = snd_hda_jack_detect(codec, 0x15);
        bits = present ? HDA_AMP_MUTE : 0;
        snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
                                 HDA_AMP_MUTE, bits);
@@ -17264,21 +17196,17 @@ static int alc662_auto_fill_dac_nids(struct hda_codec *codec,
        return 0;
 }
 
-static int alc662_add_vol_ctl(struct alc_spec *spec, const char *pfx,
+static inline int alc662_add_vol_ctl(struct alc_spec *spec, const char *pfx,
                              hda_nid_t nid, unsigned int chs)
 {
-       char name[32];
-       sprintf(name, "%s Playback Volume", pfx);
-       return add_control(spec, ALC_CTL_WIDGET_VOL, name,
+       return add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, pfx,
                           HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
 }
 
-static int alc662_add_sw_ctl(struct alc_spec *spec, const char *pfx,
+static inline int alc662_add_sw_ctl(struct alc_spec *spec, const char *pfx,
                             hda_nid_t nid, unsigned int chs)
 {
-       char name[32];
-       sprintf(name, "%s Playback Switch", pfx);
-       return add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+       return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx,
                           HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT));
 }
 
@@ -17356,13 +17284,11 @@ static int alc662_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin,
                return 0;
        nid = alc662_look_for_dac(codec, pin);
        if (!nid) {
-               char name[32];
                /* the corresponding DAC is already occupied */
                if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP))
                        return 0; /* no way */
                /* create a switch only */
-               sprintf(name, "%s Playback Switch", pfx);
-               return add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+               return add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx,
                                   HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
        }
 
@@ -17538,6 +17464,15 @@ static int patch_alc662(struct hda_codec *codec)
 
        alc_fix_pll_init(codec, 0x20, 0x04, 15);
 
+       if (alc_read_coef_idx(codec, 0)==0x8020){
+               kfree(codec->chip_name);
+               codec->chip_name = kstrdup("ALC661", GFP_KERNEL);
+               if (!codec->chip_name) {
+                       alc_free(codec);
+                       return -ENOMEM;
+               }
+       }
+
        board_config = snd_hda_check_board_config(codec, ALC662_MODEL_LAST,
                                                  alc662_models,
                                                  alc662_cfg_tbl);
@@ -17604,6 +17539,20 @@ static int patch_alc662(struct hda_codec *codec)
        return 0;
 }
 
+static int patch_alc888(struct hda_codec *codec)
+{
+       if ((alc_read_coef_idx(codec, 0) & 0x00f0)==0x0030){
+               kfree(codec->chip_name);
+               codec->chip_name = kstrdup("ALC888-VD", GFP_KERNEL);
+               if (!codec->chip_name) {
+                       alc_free(codec);
+                       return -ENOMEM;
+               }
+               return patch_alc662(codec);
+       }
+       return patch_alc882(codec);
+}
+
 /*
  * patch entries
  */
@@ -17635,8 +17584,9 @@ static struct hda_codec_preset snd_hda_preset_realtek[] = {
        { .id = 0x10ec0887, .name = "ALC887", .patch = patch_alc882 },
        { .id = 0x10ec0888, .rev = 0x100101, .name = "ALC1200",
          .patch = patch_alc882 },
-       { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc882 },
+       { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc888 },
        { .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc882 },
+       { .id = 0x10ec0892, .name = "ALC892", .patch = patch_alc662 },
        {} /* terminator */
 };
 
index ec25262..6b0bc04 100644 (file)
@@ -93,6 +93,7 @@ enum {
        STAC_92HD83XXX_REF,
        STAC_92HD83XXX_PWR_REF,
        STAC_DELL_S14,
+       STAC_92HD83XXX_HP,
        STAC_92HD83XXX_MODELS
 };
 
@@ -1085,7 +1086,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
        if (!spec->auto_mic && spec->num_dmuxes > 0 &&
            snd_hda_get_bool_hint(codec, "separate_dmux") == 1) {
                stac_dmux_mixer.count = spec->num_dmuxes;
-               err = snd_hda_ctl_add(codec,
+               err = snd_hda_ctl_add(codec, 0,
                                  snd_ctl_new1(&stac_dmux_mixer, codec));
                if (err < 0)
                        return err;
@@ -1101,7 +1102,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
                        spec->spdif_mute = 1;
                }
                stac_smux_mixer.count = spec->num_smuxes;
-               err = snd_hda_ctl_add(codec,
+               err = snd_hda_ctl_add(codec, 0,
                                  snd_ctl_new1(&stac_smux_mixer, codec));
                if (err < 0)
                        return err;
@@ -1624,6 +1625,7 @@ static const char *stac92hd83xxx_models[STAC_92HD83XXX_MODELS] = {
        [STAC_92HD83XXX_REF] = "ref",
        [STAC_92HD83XXX_PWR_REF] = "mic-ref",
        [STAC_DELL_S14] = "dell-s14",
+       [STAC_92HD83XXX_HP] = "hp",
 };
 
 static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
@@ -1634,6 +1636,8 @@ static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = {
                      "DFI LanParty", STAC_92HD83XXX_REF),
        SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02ba,
                      "unknown Dell", STAC_DELL_S14),
+       SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xff00, 0x3600,
+                     "HP", STAC_92HD83XXX_HP),
        {} /* terminator */
 };
 
@@ -2648,6 +2652,7 @@ static int stac92xx_clfe_switch_put(struct snd_kcontrol *kcontrol,
 enum {
        STAC_CTL_WIDGET_VOL,
        STAC_CTL_WIDGET_MUTE,
+       STAC_CTL_WIDGET_MUTE_BEEP,
        STAC_CTL_WIDGET_MONO_MUX,
        STAC_CTL_WIDGET_HP_SWITCH,
        STAC_CTL_WIDGET_IO_SWITCH,
@@ -2658,6 +2663,7 @@ enum {
 static struct snd_kcontrol_new stac92xx_control_templates[] = {
        HDA_CODEC_VOLUME(NULL, 0, 0, 0),
        HDA_CODEC_MUTE(NULL, 0, 0, 0),
+       HDA_CODEC_MUTE_BEEP(NULL, 0, 0, 0),
        STAC_MONO_MUX,
        STAC_CODEC_HP_SWITCH(NULL),
        STAC_CODEC_IO_SWITCH(NULL, 0),
@@ -2669,7 +2675,8 @@ static struct snd_kcontrol_new stac92xx_control_templates[] = {
 static struct snd_kcontrol_new *
 stac_control_new(struct sigmatel_spec *spec,
                 struct snd_kcontrol_new *ktemp,
-                const char *name)
+                const char *name,
+                hda_nid_t nid)
 {
        struct snd_kcontrol_new *knew;
 
@@ -2685,6 +2692,8 @@ stac_control_new(struct sigmatel_spec *spec,
                spec->kctls.alloced--;
                return NULL;
        }
+       if (nid)
+               knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
        return knew;
 }
 
@@ -2693,7 +2702,8 @@ static int stac92xx_add_control_temp(struct sigmatel_spec *spec,
                                     int idx, const char *name,
                                     unsigned long val)
 {
-       struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name);
+       struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name,
+                                                        get_amp_nid_(val));
        if (!knew)
                return -ENOMEM;
        knew->index = idx;
@@ -2764,7 +2774,7 @@ static int stac92xx_add_input_source(struct sigmatel_spec *spec)
        if (!spec->num_adcs || imux->num_items <= 1)
                return 0; /* no need for input source control */
        knew = stac_control_new(spec, &stac_input_src_temp,
-                               stac_input_src_temp.name);
+                               stac_input_src_temp.name, 0);
        if (!knew)
                return -ENOMEM;
        knew->count = spec->num_adcs;
@@ -3221,11 +3231,14 @@ static int stac92xx_auto_create_beep_ctls(struct hda_codec *codec,
 {
        struct sigmatel_spec *spec = codec->spec;
        u32 caps = query_amp_caps(codec, nid, HDA_OUTPUT);
-       int err;
+       int err, type = STAC_CTL_WIDGET_MUTE_BEEP;
+
+       if (spec->anabeep_nid == nid)
+               type = STAC_CTL_WIDGET_MUTE;
 
        /* check for mute support for the the amp */
        if ((caps & AC_AMPCAP_MUTE) >> AC_AMPCAP_MUTE_SHIFT) {
-               err = stac92xx_add_control(spec, STAC_CTL_WIDGET_MUTE,
+               err = stac92xx_add_control(spec, type,
                        "Beep Playback Switch",
                        HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT));
                        if (err < 0)
@@ -3258,12 +3271,7 @@ static int stac92xx_dig_beep_switch_put(struct snd_kcontrol *kcontrol,
                                        struct snd_ctl_elem_value *ucontrol)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       int enabled = !!ucontrol->value.integer.value[0];
-       if (codec->beep->enabled != enabled) {
-               codec->beep->enabled = enabled;
-               return 1;
-       }
-       return 0;
+       return snd_hda_enable_beep_device(codec, ucontrol->value.integer.value[0]);
 }
 
 static struct snd_kcontrol_new stac92xx_dig_beep_ctrl = {
@@ -3631,6 +3639,26 @@ static void stac92xx_auto_init_hp_out(struct hda_codec *codec)
        }
 }
 
+static int is_dual_headphones(struct hda_codec *codec)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       int i, valid_hps;
+
+       if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT ||
+           spec->autocfg.hp_outs <= 1)
+               return 0;
+       valid_hps = 0;
+       for (i = 0; i < spec->autocfg.hp_outs; i++) {
+               hda_nid_t nid = spec->autocfg.hp_pins[i];
+               unsigned int cfg = snd_hda_codec_get_pincfg(codec, nid);
+               if (get_defcfg_location(cfg) & AC_JACK_LOC_SEPARATE)
+                       continue;
+               valid_hps++;
+       }
+       return (valid_hps > 1);
+}
+
+
 static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out, hda_nid_t dig_in)
 {
        struct sigmatel_spec *spec = codec->spec;
@@ -3647,8 +3675,7 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
        /* If we have no real line-out pin and multiple hp-outs, HPs should
         * be set up as multi-channel outputs.
         */
-       if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT &&
-           spec->autocfg.hp_outs > 1) {
+       if (is_dual_headphones(codec)) {
                /* Copy hp_outs to line_outs, backup line_outs in
                 * speaker_outs so that the following routines can handle
                 * HP pins as primary outputs.
@@ -4329,6 +4356,28 @@ static void stac92xx_free_kctls(struct hda_codec *codec)
        snd_array_free(&spec->kctls);
 }
 
+static void stac92xx_shutup(struct hda_codec *codec)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       int i;
+       hda_nid_t nid;
+
+       /* reset each pin before powering down DAC/ADC to avoid click noise */
+       nid = codec->start_nid;
+       for (i = 0; i < codec->num_nodes; i++, nid++) {
+               unsigned int wcaps = get_wcaps(codec, nid);
+               unsigned int wid_type = get_wcaps_type(wcaps);
+               if (wid_type == AC_WID_PIN)
+                       snd_hda_codec_read(codec, nid, 0,
+                               AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+       }
+
+       if (spec->eapd_mask)
+               stac_gpio_set(codec, spec->gpio_mask,
+                               spec->gpio_dir, spec->gpio_data &
+                               ~spec->eapd_mask);
+}
+
 static void stac92xx_free(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec = codec->spec;
@@ -4336,6 +4385,7 @@ static void stac92xx_free(struct hda_codec *codec)
        if (! spec)
                return;
 
+       stac92xx_shutup(codec);
        stac92xx_free_jacks(codec);
        snd_array_free(&spec->events);
 
@@ -4386,12 +4436,16 @@ static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
                                          pin_ctl & ~flag);
 }
 
-static int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
+static inline int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
 {
        if (!nid)
                return 0;
-       if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0x00)
-           & (1 << 31))
+       /* NOTE: we can't use snd_hda_jack_detect() here because STAC/IDT
+        * codecs behave wrongly when SET_PIN_SENSE is triggered, although
+        * the pincap gives TRIG_REQ bit.
+        */
+       if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0) &
+           AC_PINSENSE_PRESENCE)
                return 1;
        return 0;
 }
@@ -4791,28 +4845,28 @@ static int stac92xx_hp_check_power_status(struct hda_codec *codec,
 
        return 0;
 }
-#endif
 
-static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
+static int idt92hd83xxx_hp_check_power_status(struct hda_codec *codec,
+                                             hda_nid_t nid)
 {
        struct sigmatel_spec *spec = codec->spec;
-       int i;
-       hda_nid_t nid;
 
-       /* reset each pin before powering down DAC/ADC to avoid click noise */
-       nid = codec->start_nid;
-       for (i = 0; i < codec->num_nodes; i++, nid++) {
-               unsigned int wcaps = get_wcaps(codec, nid);
-               unsigned int wid_type = get_wcaps_type(wcaps);
-               if (wid_type == AC_WID_PIN)
-                       snd_hda_codec_read(codec, nid, 0,
-                               AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
-       }
+       if (nid != 0x13)
+               return 0;
+       if (snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) & HDA_AMP_MUTE)
+               spec->gpio_data |= spec->gpio_led; /* mute LED on */
+       else
+               spec->gpio_data &= ~spec->gpio_led; /* mute LED off */
+       stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data);
 
-       if (spec->eapd_mask)
-               stac_gpio_set(codec, spec->gpio_mask,
-                               spec->gpio_dir, spec->gpio_data &
-                               ~spec->eapd_mask);
+       return 0;
+}
+
+#endif
+
+static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
+{
+       stac92xx_shutup(codec);
        return 0;
 }
 #endif
@@ -4827,6 +4881,7 @@ static struct hda_codec_ops stac92xx_patch_ops = {
        .suspend = stac92xx_suspend,
        .resume = stac92xx_resume,
 #endif
+       .reboot_notify = stac92xx_shutup,
 };
 
 static int patch_stac9200(struct hda_codec *codec)
@@ -5172,6 +5227,22 @@ again:
                break;
        }
 
+       codec->patch_ops = stac92xx_patch_ops;
+
+       if (spec->board_config == STAC_92HD83XXX_HP)
+               spec->gpio_led = 0x01;
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       if (spec->gpio_led) {
+               spec->gpio_mask |= spec->gpio_led;
+               spec->gpio_dir |= spec->gpio_led;
+               spec->gpio_data |= spec->gpio_led;
+               /* register check_power_status callback. */
+               codec->patch_ops.check_power_status =
+                       idt92hd83xxx_hp_check_power_status;
+       }
+#endif 
+
        err = stac92xx_parse_auto_config(codec, 0x1d, 0);
        if (!err) {
                if (spec->board_config < 0) {
@@ -5207,8 +5278,6 @@ again:
        snd_hda_codec_write_cache(codec, nid, 0,
                        AC_VERB_SET_CONNECT_SEL, num_dacs);
 
-       codec->patch_ops = stac92xx_patch_ops;
-
        codec->proc_widget_hook = stac92hd_proc_hook;
 
        return 0;
index ee89db9..b70e26a 100644 (file)
@@ -1,10 +1,10 @@
 /*
  * Universal Interface for Intel High Definition Audio Codec
  *
- * HD audio interface patch for VIA VT1702/VT1708/VT1709 codec
+ * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
  *
- * Copyright (c) 2006-2008 Lydia Wang <lydiawang@viatech.com>
- *                        Takashi Iwai <tiwai@suse.de>
+ *  (C) 2006-2009 VIA Technology, Inc.
+ *  (C) 2006-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
  */
 
 /* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
-/*                                                                           */
+/*                                                                          */
 /* 2006-03-03  Lydia Wang  Create the basic patch to support VT1708 codec    */
-/* 2006-03-14  Lydia Wang  Modify hard code for some pin widget nid          */
-/* 2006-08-02  Lydia Wang  Add support to VT1709 codec                       */
+/* 2006-03-14  Lydia Wang  Modify hard code for some pin widget nid         */
+/* 2006-08-02  Lydia Wang  Add support to VT1709 codec                      */
 /* 2006-09-08  Lydia Wang  Fix internal loopback recording source select bug */
-/* 2007-09-12  Lydia Wang  Add EAPD enable during driver initialization      */
-/* 2007-09-17  Lydia Wang  Add VT1708B codec support                        */
+/* 2007-09-12  Lydia Wang  Add EAPD enable during driver initialization             */
+/* 2007-09-17  Lydia Wang  Add VT1708B codec support                       */
 /* 2007-11-14  Lydia Wang  Add VT1708A codec HP and CD pin connect config    */
 /* 2008-02-03  Lydia Wang  Fix Rear channels and Back channels inverse issue */
-/* 2008-03-06  Lydia Wang  Add VT1702 codec and VT1708S codec support        */
-/* 2008-04-09  Lydia Wang  Add mute front speaker when HP plugin             */
-/* 2008-04-09  Lydia Wang  Add Independent HP feature                        */
+/* 2008-03-06  Lydia Wang  Add VT1702 codec and VT1708S codec support       */
+/* 2008-04-09  Lydia Wang  Add mute front speaker when HP plugin            */
+/* 2008-04-09  Lydia Wang  Add Independent HP feature                       */
 /* 2008-05-28  Lydia Wang  Add second S/PDIF Out support for VT1702         */
-/* 2008-09-15  Logan Li    Add VT1708S Mic Boost workaround/backdoor        */
-/*                                                                           */
+/* 2008-09-15  Logan Li           Add VT1708S Mic Boost workaround/backdoor         */
+/* 2009-02-16  Logan Li           Add support for VT1718S                           */
+/* 2009-03-13  Logan Li           Add support for VT1716S                           */
+/* 2009-04-14  Lydai Wang  Add support for VT1828S and VT2020               */
+/* 2009-07-08  Lydia Wang  Add support for VT2002P                          */
+/* 2009-07-21  Lydia Wang  Add support for VT1812                           */
+/* 2009-09-19  Lydia Wang  Add support for VT1818S                          */
+/*                                                                          */
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 
 #define VT1702_HP_NID          0x17
 #define VT1702_DIGOUT_NID      0x11
 
-#define IS_VT1708_VENDORID(x)          ((x) >= 0x11061708 && (x) <= 0x1106170b)
-#define IS_VT1709_10CH_VENDORID(x)     ((x) >= 0x1106e710 && (x) <= 0x1106e713)
-#define IS_VT1709_6CH_VENDORID(x)      ((x) >= 0x1106e714 && (x) <= 0x1106e717)
-#define IS_VT1708B_8CH_VENDORID(x)     ((x) >= 0x1106e720 && (x) <= 0x1106e723)
-#define IS_VT1708B_4CH_VENDORID(x)     ((x) >= 0x1106e724 && (x) <= 0x1106e727)
-#define IS_VT1708S_VENDORID(x)         ((x) >= 0x11060397 && (x) <= 0x11067397)
-#define IS_VT1702_VENDORID(x)          ((x) >= 0x11060398 && (x) <= 0x11067398)
-
 enum VIA_HDA_CODEC {
        UNKNOWN = -1,
        VT1708,
@@ -92,12 +90,76 @@ enum VIA_HDA_CODEC {
        VT1708B_8CH,
        VT1708B_4CH,
        VT1708S,
+       VT1708BCE,
        VT1702,
+       VT1718S,
+       VT1716S,
+       VT2002P,
+       VT1812,
        CODEC_TYPES,
 };
 
-static enum VIA_HDA_CODEC get_codec_type(u32 vendor_id)
+struct via_spec {
+       /* codec parameterization */
+       struct snd_kcontrol_new *mixers[6];
+       unsigned int num_mixers;
+
+       struct hda_verb *init_verbs[5];
+       unsigned int num_iverbs;
+
+       char *stream_name_analog;
+       struct hda_pcm_stream *stream_analog_playback;
+       struct hda_pcm_stream *stream_analog_capture;
+
+       char *stream_name_digital;
+       struct hda_pcm_stream *stream_digital_playback;
+       struct hda_pcm_stream *stream_digital_capture;
+
+       /* playback */
+       struct hda_multi_out multiout;
+       hda_nid_t slave_dig_outs[2];
+
+       /* capture */
+       unsigned int num_adc_nids;
+       hda_nid_t *adc_nids;
+       hda_nid_t mux_nids[3];
+       hda_nid_t dig_in_nid;
+       hda_nid_t dig_in_pin;
+
+       /* capture source */
+       const struct hda_input_mux *input_mux;
+       unsigned int cur_mux[3];
+
+       /* PCM information */
+       struct hda_pcm pcm_rec[3];
+
+       /* dynamic controls, init_verbs and input_mux */
+       struct auto_pin_cfg autocfg;
+       struct snd_array kctls;
+       struct hda_input_mux private_imux[2];
+       hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
+
+       /* HP mode source */
+       const struct hda_input_mux *hp_mux;
+       unsigned int hp_independent_mode;
+       unsigned int hp_independent_mode_index;
+       unsigned int smart51_enabled;
+       unsigned int dmic_enabled;
+       enum VIA_HDA_CODEC codec_type;
+
+       /* work to check hp jack state */
+       struct hda_codec *codec;
+       struct delayed_work vt1708_hp_work;
+       int vt1708_jack_detectect;
+       int vt1708_hp_present;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       struct hda_loopback_check loopback;
+#endif
+};
+
+static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
 {
+       u32 vendor_id = codec->vendor_id;
        u16 ven_id = vendor_id >> 16;
        u16 dev_id = vendor_id & 0xffff;
        enum VIA_HDA_CODEC codec_type;
@@ -111,9 +173,11 @@ static enum VIA_HDA_CODEC get_codec_type(u32 vendor_id)
                codec_type = VT1709_10CH;
        else if (dev_id >= 0xe714 && dev_id <= 0xe717)
                codec_type = VT1709_6CH;
-       else if (dev_id >= 0xe720 && dev_id <= 0xe723)
+       else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
                codec_type = VT1708B_8CH;
-       else if (dev_id >= 0xe724 && dev_id <= 0xe727)
+               if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
+                       codec_type = VT1708BCE;
+       } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
                codec_type = VT1708B_4CH;
        else if ((dev_id & 0xfff) == 0x397
                 && (dev_id >> 12) < 8)
@@ -121,6 +185,19 @@ static enum VIA_HDA_CODEC get_codec_type(u32 vendor_id)
        else if ((dev_id & 0xfff) == 0x398
                 && (dev_id >> 12) < 8)
                codec_type = VT1702;
+       else if ((dev_id & 0xfff) == 0x428
+                && (dev_id >> 12) < 8)
+               codec_type = VT1718S;
+       else if (dev_id == 0x0433 || dev_id == 0xa721)
+               codec_type = VT1716S;
+       else if (dev_id == 0x0441 || dev_id == 0x4441)
+               codec_type = VT1718S;
+       else if (dev_id == 0x0438 || dev_id == 0x4438)
+               codec_type = VT2002P;
+       else if (dev_id == 0x0448)
+               codec_type = VT1812;
+       else if (dev_id == 0x0440)
+               codec_type = VT1708S;
        else
                codec_type = UNKNOWN;
        return codec_type;
@@ -128,10 +205,16 @@ static enum VIA_HDA_CODEC get_codec_type(u32 vendor_id)
 
 #define VIA_HP_EVENT           0x01
 #define VIA_GPIO_EVENT         0x02
+#define VIA_JACK_EVENT         0x04
+#define VIA_MONO_EVENT         0x08
+#define VIA_SPEAKER_EVENT      0x10
+#define VIA_BIND_HP_EVENT      0x20
 
 enum {
        VIA_CTL_WIDGET_VOL,
        VIA_CTL_WIDGET_MUTE,
+       VIA_CTL_WIDGET_ANALOG_MUTE,
+       VIA_CTL_WIDGET_BIND_PIN_MUTE,
 };
 
 enum {
@@ -141,99 +224,162 @@ enum {
        AUTO_SEQ_SIDE
 };
 
-/* Some VT1708S based boards gets the micboost setting wrong, so we have
- * to apply some brute-force and re-write the TLV's by software. */
-static int mic_boost_tlv(struct snd_kcontrol *kcontrol, int op_flag,
-                        unsigned int size, unsigned int __user *_tlv)
+static void analog_low_current_mode(struct hda_codec *codec, int stream_idle);
+static void set_jack_power_state(struct hda_codec *codec);
+static int is_aa_path_mute(struct hda_codec *codec);
+
+static void vt1708_start_hp_work(struct via_spec *spec)
 {
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       hda_nid_t nid = get_amp_nid(kcontrol);
+       if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
+               return;
+       snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
+                           !spec->vt1708_jack_detectect);
+       if (!delayed_work_pending(&spec->vt1708_hp_work))
+               schedule_delayed_work(&spec->vt1708_hp_work,
+                                     msecs_to_jiffies(100));
+}
 
-       if (get_codec_type(codec->vendor_id) == VT1708S
-           && (nid == 0x1a || nid == 0x1e)) {
-               if (size < 4 * sizeof(unsigned int))
-                       return -ENOMEM;
-               if (put_user(1, _tlv))  /* SNDRV_CTL_TLVT_DB_SCALE */
-                       return -EFAULT;
-               if (put_user(2 * sizeof(unsigned int), _tlv + 1))
-                       return -EFAULT;
-               if (put_user(0, _tlv + 2)) /* offset = 0 */
-                       return -EFAULT;
-               if (put_user(1000, _tlv + 3)) /* step size = 10 dB */
-                       return -EFAULT;
-       }
-       return 0;
+static void vt1708_stop_hp_work(struct via_spec *spec)
+{
+       if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
+               return;
+       if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
+           && !is_aa_path_mute(spec->codec))
+               return;
+       snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
+                           !spec->vt1708_jack_detectect);
+       cancel_delayed_work(&spec->vt1708_hp_work);
+       flush_scheduled_work();
 }
 
-static int mic_boost_volume_info(struct snd_kcontrol *kcontrol,
-                                struct snd_ctl_elem_info *uinfo)
+
+static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
 {
+       int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       hda_nid_t nid = get_amp_nid(kcontrol);
 
-       if (get_codec_type(codec->vendor_id) == VT1708S
-           && (nid == 0x1a || nid == 0x1e)) {
-               uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-               uinfo->count = 2;
-               uinfo->value.integer.min = 0;
-               uinfo->value.integer.max = 3;
+       set_jack_power_state(codec);
+       analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1);
+       if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
+               if (is_aa_path_mute(codec))
+                       vt1708_start_hp_work(codec->spec);
+               else
+                       vt1708_stop_hp_work(codec->spec);
        }
-       return 0;
+       return change;
 }
 
-static struct snd_kcontrol_new vt1708_control_templates[] = {
-       HDA_CODEC_VOLUME(NULL, 0, 0, 0),
-       HDA_CODEC_MUTE(NULL, 0, 0, 0),
-};
-
-
-struct via_spec {
-       /* codec parameterization */
-       struct snd_kcontrol_new *mixers[3];
-       unsigned int num_mixers;
+/* modify .put = snd_hda_mixer_amp_switch_put */
+#define ANALOG_INPUT_MUTE                                              \
+       {               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,            \
+                       .name = NULL,                                   \
+                       .index = 0,                                     \
+                       .info = snd_hda_mixer_amp_switch_info,          \
+                       .get = snd_hda_mixer_amp_switch_get,            \
+                       .put = analog_input_switch_put,                 \
+                       .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
 
-       struct hda_verb *init_verbs[5];
-       unsigned int num_iverbs;
+static void via_hp_bind_automute(struct hda_codec *codec);
 
-       char *stream_name_analog;
-       struct hda_pcm_stream *stream_analog_playback;
-       struct hda_pcm_stream *stream_analog_capture;
-
-       char *stream_name_digital;
-       struct hda_pcm_stream *stream_digital_playback;
-       struct hda_pcm_stream *stream_digital_capture;
-
-       /* playback */
-       struct hda_multi_out multiout;
-       hda_nid_t slave_dig_outs[2];
-
-       /* capture */
-       unsigned int num_adc_nids;
-       hda_nid_t *adc_nids;
-       hda_nid_t mux_nids[3];
-       hda_nid_t dig_in_nid;
-       hda_nid_t dig_in_pin;
+static int bind_pin_switch_put(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct via_spec *spec = codec->spec;
+       int i;
+       int change = 0;
 
-       /* capture source */
-       const struct hda_input_mux *input_mux;
-       unsigned int cur_mux[3];
+       long *valp = ucontrol->value.integer.value;
+       int lmute, rmute;
+       if (strstr(kcontrol->id.name, "Switch") == NULL) {
+               snd_printd("Invalid control!\n");
+               return change;
+       }
+       change = snd_hda_mixer_amp_switch_put(kcontrol,
+                                             ucontrol);
+       /* Get mute value */
+       lmute = *valp ? 0 : HDA_AMP_MUTE;
+       valp++;
+       rmute = *valp ? 0 : HDA_AMP_MUTE;
+
+       /* Set hp pins */
+       if (!spec->hp_independent_mode) {
+               for (i = 0; i < spec->autocfg.hp_outs; i++) {
+                       snd_hda_codec_amp_update(
+                               codec, spec->autocfg.hp_pins[i],
+                               0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
+                               lmute);
+                       snd_hda_codec_amp_update(
+                               codec, spec->autocfg.hp_pins[i],
+                               1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
+                               rmute);
+               }
+       }
 
-       /* PCM information */
-       struct hda_pcm pcm_rec[3];
+       if (!lmute && !rmute) {
+               /* Line Outs */
+               for (i = 0; i < spec->autocfg.line_outs; i++)
+                       snd_hda_codec_amp_stereo(
+                               codec, spec->autocfg.line_out_pins[i],
+                               HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
+               /* Speakers */
+               for (i = 0; i < spec->autocfg.speaker_outs; i++)
+                       snd_hda_codec_amp_stereo(
+                               codec, spec->autocfg.speaker_pins[i],
+                               HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
+               /* unmute */
+               via_hp_bind_automute(codec);
 
-       /* dynamic controls, init_verbs and input_mux */
-       struct auto_pin_cfg autocfg;
-       struct snd_array kctls;
-       struct hda_input_mux private_imux[2];
-       hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
+       } else {
+               if (lmute) {
+                       /* Mute all left channels */
+                       for (i = 1; i < spec->autocfg.line_outs; i++)
+                               snd_hda_codec_amp_update(
+                                       codec,
+                                       spec->autocfg.line_out_pins[i],
+                                       0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
+                                       lmute);
+                       for (i = 0; i < spec->autocfg.speaker_outs; i++)
+                               snd_hda_codec_amp_update(
+                                       codec,
+                                       spec->autocfg.speaker_pins[i],
+                                       0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
+                                       lmute);
+               }
+               if (rmute) {
+                       /* mute all right channels */
+                       for (i = 1; i < spec->autocfg.line_outs; i++)
+                               snd_hda_codec_amp_update(
+                                       codec,
+                                       spec->autocfg.line_out_pins[i],
+                                       1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
+                                       rmute);
+                       for (i = 0; i < spec->autocfg.speaker_outs; i++)
+                               snd_hda_codec_amp_update(
+                                       codec,
+                                       spec->autocfg.speaker_pins[i],
+                                       1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
+                                       rmute);
+               }
+       }
+       return change;
+}
 
-       /* HP mode source */
-       const struct hda_input_mux *hp_mux;
-       unsigned int hp_independent_mode;
+#define BIND_PIN_MUTE                                                  \
+       {               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,            \
+                       .name = NULL,                                   \
+                       .index = 0,                                     \
+                       .info = snd_hda_mixer_amp_switch_info,          \
+                       .get = snd_hda_mixer_amp_switch_get,            \
+                       .put = bind_pin_switch_put,                     \
+                       .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
 
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-       struct hda_loopback_check loopback;
-#endif
+static struct snd_kcontrol_new via_control_templates[] = {
+       HDA_CODEC_VOLUME(NULL, 0, 0, 0),
+       HDA_CODEC_MUTE(NULL, 0, 0, 0),
+       ANALOG_INPUT_MUTE,
+       BIND_PIN_MUTE,
 };
 
 static hda_nid_t vt1708_adc_nids[2] = {
@@ -261,6 +407,27 @@ static hda_nid_t vt1702_adc_nids[3] = {
        0x12, 0x20, 0x1F
 };
 
+static hda_nid_t vt1718S_adc_nids[2] = {
+       /* ADC1-2 */
+       0x10, 0x11
+};
+
+static hda_nid_t vt1716S_adc_nids[2] = {
+       /* ADC1-2 */
+       0x13, 0x14
+};
+
+static hda_nid_t vt2002P_adc_nids[2] = {
+       /* ADC1-2 */
+       0x10, 0x11
+};
+
+static hda_nid_t vt1812_adc_nids[2] = {
+       /* ADC1-2 */
+       0x10, 0x11
+};
+
+
 /* add dynamic controls */
 static int via_add_control(struct via_spec *spec, int type, const char *name,
                           unsigned long val)
@@ -271,10 +438,12 @@ static int via_add_control(struct via_spec *spec, int type, const char *name,
        knew = snd_array_new(&spec->kctls);
        if (!knew)
                return -ENOMEM;
-       *knew = vt1708_control_templates[type];
+       *knew = via_control_templates[type];
        knew->name = kstrdup(name, GFP_KERNEL);
        if (!knew->name)
                return -ENOMEM;
+       if (get_amp_nid_(val))
+               knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val);
        knew->private_value = val;
        return 0;
 }
@@ -293,8 +462,8 @@ static void via_free_kctls(struct hda_codec *codec)
 }
 
 /* create input playback/capture controls for the given pin */
-static int via_new_analog_input(struct via_spec *spec, hda_nid_t pin,
-                               const char *ctlname, int idx, int mix_nid)
+static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
+                               int idx, int mix_nid)
 {
        char name[32];
        int err;
@@ -305,7 +474,7 @@ static int via_new_analog_input(struct via_spec *spec, hda_nid_t pin,
        if (err < 0)
                return err;
        sprintf(name, "%s Playback Switch", ctlname);
-       err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
+       err = via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name,
                              HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
        if (err < 0)
                return err;
@@ -322,7 +491,7 @@ static void via_auto_set_output_and_unmute(struct hda_codec *codec,
        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
                            AMP_OUT_UNMUTE);
        if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)
-               snd_hda_codec_write(codec, nid, 0, 
+               snd_hda_codec_write(codec, nid, 0,
                                    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
 }
 
@@ -343,10 +512,13 @@ static void via_auto_init_hp_out(struct hda_codec *codec)
 {
        struct via_spec *spec = codec->spec;
        hda_nid_t pin;
+       int i;
 
-       pin = spec->autocfg.hp_pins[0];
-       if (pin) /* connect to front */
-               via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
+       for (i = 0; i < spec->autocfg.hp_outs; i++) {
+               pin = spec->autocfg.hp_pins[i];
+               if (pin) /* connect to front */
+                       via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
+       }
 }
 
 static void via_auto_init_analog_input(struct hda_codec *codec)
@@ -364,6 +536,502 @@ static void via_auto_init_analog_input(struct hda_codec *codec)
 
        }
 }
+
+static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
+
+static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
+                               unsigned int *affected_parm)
+{
+       unsigned parm;
+       unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
+       unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
+               >> AC_DEFCFG_MISC_SHIFT
+               & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
+       unsigned present = snd_hda_jack_detect(codec, nid);
+       struct via_spec *spec = codec->spec;
+       if ((spec->smart51_enabled && is_smart51_pins(spec, nid))
+           || ((no_presence || present)
+               && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
+               *affected_parm = AC_PWRST_D0; /* if it's connected */
+               parm = AC_PWRST_D0;
+       } else
+               parm = AC_PWRST_D3;
+
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
+}
+
+static void set_jack_power_state(struct hda_codec *codec)
+{
+       struct via_spec *spec = codec->spec;
+       int imux_is_smixer;
+       unsigned int parm;
+
+       if (spec->codec_type == VT1702) {
+               imux_is_smixer = snd_hda_codec_read(
+                       codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
+               /* inputs */
+               /* PW 1/2/5 (14h/15h/18h) */
+               parm = AC_PWRST_D3;
+               set_pin_power_state(codec, 0x14, &parm);
+               set_pin_power_state(codec, 0x15, &parm);
+               set_pin_power_state(codec, 0x18, &parm);
+               if (imux_is_smixer)
+                       parm = AC_PWRST_D0; /* SW0 = stereo mixer (idx 3) */
+               /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
+               snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
+                                   parm);
+               snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE,
+                                   parm);
+               snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE,
+                                   parm);
+               snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE,
+                                   parm);
+
+               /* outputs */
+               /* PW 3/4 (16h/17h) */
+               parm = AC_PWRST_D3;
+               set_pin_power_state(codec, 0x16, &parm);
+               set_pin_power_state(codec, 0x17, &parm);
+               /* MW0 (1ah), AOW 0/1 (10h/1dh) */
+               snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
+                                   imux_is_smixer ? AC_PWRST_D0 : parm);
+               snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
+                                   parm);
+               snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE,
+                                   parm);
+       } else if (spec->codec_type == VT1708B_8CH
+                  || spec->codec_type == VT1708B_4CH
+                  || spec->codec_type == VT1708S) {
+               /* SW0 (17h) = stereo mixer */
+               int is_8ch = spec->codec_type != VT1708B_4CH;
+               imux_is_smixer = snd_hda_codec_read(
+                       codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
+                       == ((spec->codec_type == VT1708S)  ? 5 : 0);
+               /* inputs */
+               /* PW 1/2/5 (1ah/1bh/1eh) */
+               parm = AC_PWRST_D3;
+               set_pin_power_state(codec, 0x1a, &parm);
+               set_pin_power_state(codec, 0x1b, &parm);
+               set_pin_power_state(codec, 0x1e, &parm);
+               if (imux_is_smixer)
+                       parm = AC_PWRST_D0;
+               /* SW0 (17h), AIW 0/1 (13h/14h) */
+               snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE,
+                                   parm);
+               snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
+                                   parm);
+               snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE,
+                                   parm);
+
+               /* outputs */
+               /* PW0 (19h), SW1 (18h), AOW1 (11h) */
+               parm = AC_PWRST_D3;
+               set_pin_power_state(codec, 0x19, &parm);
+               snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE,
+                                   parm);
+               snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
+                                   parm);
+
+               /* PW6 (22h), SW2 (26h), AOW2 (24h) */
+               if (is_8ch) {
+                       parm = AC_PWRST_D3;
+                       set_pin_power_state(codec, 0x22, &parm);
+                       snd_hda_codec_write(codec, 0x26, 0,
+                                           AC_VERB_SET_POWER_STATE, parm);
+                       snd_hda_codec_write(codec, 0x24, 0,
+                                           AC_VERB_SET_POWER_STATE, parm);
+               }
+
+               /* PW 3/4/7 (1ch/1dh/23h) */
+               parm = AC_PWRST_D3;
+               /* force to D0 for internal Speaker */
+               set_pin_power_state(codec, 0x1c, &parm);
+               set_pin_power_state(codec, 0x1d, &parm);
+               if (is_8ch)
+                       set_pin_power_state(codec, 0x23, &parm);
+               /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
+               snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
+                                   imux_is_smixer ? AC_PWRST_D0 : parm);
+               snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
+                                   parm);
+               if (is_8ch) {
+                       snd_hda_codec_write(codec, 0x25, 0,
+                                           AC_VERB_SET_POWER_STATE, parm);
+                       snd_hda_codec_write(codec, 0x27, 0,
+                                           AC_VERB_SET_POWER_STATE, parm);
+               }
+       }  else if (spec->codec_type == VT1718S) {
+               /* MUX6 (1eh) = stereo mixer */
+               imux_is_smixer = snd_hda_codec_read(
+                       codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
+               /* inputs */
+               /* PW 5/6/7 (29h/2ah/2bh) */
+               parm = AC_PWRST_D3;
+               set_pin_power_state(codec, 0x29, &parm);
+               set_pin_power_state(codec, 0x2a, &parm);
+               set_pin_power_state(codec, 0x2b, &parm);
+               if (imux_is_smixer)
+                       parm = AC_PWRST_D0;
+               /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
+               snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE,
+                                   parm);
+               snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE,
+                                   parm);
+               snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
+                                   parm);
+               snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
+                                   parm);
+
+               /* outputs */
+               /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
+               parm = AC_PWRST_D3;
+               set_pin_power_state(codec, 0x27, &parm);
+               snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
+                                   parm);
+               snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE,
+                                   parm);
+
+               /* PW2 (26h), AOW2 (ah) */
+               parm = AC_PWRST_D3;
+               set_pin_power_state(codec, 0x26, &parm);
+               snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE,
+                                   parm);
+
+               /* PW0/1 (24h/25h) */
+               parm = AC_PWRST_D3;
+               set_pin_power_state(codec, 0x24, &parm);
+               set_pin_power_state(codec, 0x25, &parm);
+               if (!spec->hp_independent_mode) /* check for redirected HP */
+                       set_pin_power_state(codec, 0x28, &parm);
+               snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE,
+                                   parm);
+               snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE,
+                                   parm);
+               /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
+               snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
+                                   imux_is_smixer ? AC_PWRST_D0 : parm);
+               if (spec->hp_independent_mode) {
+                       /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
+                       parm = AC_PWRST_D3;
+                       set_pin_power_state(codec, 0x28, &parm);
+                       snd_hda_codec_write(codec, 0x1b, 0,
+                                           AC_VERB_SET_POWER_STATE, parm);
+                       snd_hda_codec_write(codec, 0x34, 0,
+                                           AC_VERB_SET_POWER_STATE, parm);
+                       snd_hda_codec_write(codec, 0xc, 0,
+