Ver código fonte

Merge branch 'topic/hda' into for-linus

Takashi Iwai 15 anos atrás
pai
commit
86e1d57e4f
48 arquivos alterados com 7036 adições e 1994 exclusões
  1. 3 0
      Documentation/sound/alsa/ALSA-Configuration.txt
  2. 1 0
      Documentation/sound/alsa/HD-Audio-Models.txt
  3. 321 0
      include/sound/ak4113.h
  4. 6 6
      include/sound/ak4114.h
  5. 4 1
      include/sound/ak4xxx-adda.h
  6. 2 1
      sound/i2c/other/Makefile
  7. 639 0
      sound/i2c/other/ak4113.c
  8. 104 32
      sound/i2c/other/ak4xxx-adda.c
  9. 12 1
      sound/pci/hda/Kconfig
  10. 96 18
      sound/pci/hda/hda_beep.c
  11. 10 0
      sound/pci/hda/hda_beep.h
  12. 474 79
      sound/pci/hda/hda_codec.c
  13. 11 0
      sound/pci/hda/hda_codec.h
  14. 11 9
      sound/pci/hda/hda_eld.c
  15. 11 6
      sound/pci/hda/hda_generic.c
  16. 38 0
      sound/pci/hda/hda_hwdep.c
  17. 34 16
      sound/pci/hda/hda_intel.c
  18. 64 5
      sound/pci/hda/hda_local.h
  19. 70 0
      sound/pci/hda/hda_proc.c
  20. 28 33
      sound/pci/hda/patch_analog.c
  21. 2 2
      sound/pci/hda/patch_ca0110.c
  22. 11 20
      sound/pci/hda/patch_cirrus.c
  23. 150 39
      sound/pci/hda/patch_conexant.c
  24. 365 123
      sound/pci/hda/patch_intelhdmi.c
  25. 161 225
      sound/pci/hda/patch_realtek.c
  26. 106 37
      sound/pci/hda/patch_sigmatel.c
  27. 776 108
      sound/pci/hda/patch_via.c
  28. 1 1
      sound/pci/ice1712/Makefile
  29. 12 0
      sound/pci/ice1712/ice1712.c
  30. 12 2
      sound/pci/ice1712/ice1712.h
  31. 83 20
      sound/pci/ice1712/ice1724.c
  32. 2 22
      sound/pci/ice1712/juli.c
  33. 1130 0
      sound/pci/ice1712/quartet.c
  34. 10 0
      sound/pci/ice1712/quartet.h
  35. 2 1
      sound/pci/oxygen/Makefile
  36. 83 0
      sound/pci/oxygen/cs2000.h
  37. 38 23
      sound/pci/oxygen/hifier.c
  38. 199 49
      sound/pci/oxygen/oxygen.c
  39. 5 0
      sound/pci/oxygen/oxygen.h
  40. 28 1
      sound/pci/oxygen/oxygen_lib.c
  41. 35 17
      sound/pci/oxygen/oxygen_mixer.c
  42. 13 6
      sound/pci/oxygen/oxygen_pcm.c
  43. 14 1091
      sound/pci/oxygen/virtuoso.c
  44. 50 0
      sound/pci/oxygen/xonar.h
  45. 434 0
      sound/pci/oxygen/xonar_cs43xx.c
  46. 128 0
      sound/pci/oxygen/xonar_hdmi.c
  47. 132 0
      sound/pci/oxygen/xonar_lib.c
  48. 1115 0
      sound/pci/oxygen/xonar_pcm179x.c

+ 3 - 0
Documentation/sound/alsa/ALSA-Configuration.txt

@@ -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

+ 1 - 0
Documentation/sound/alsa/HD-Audio-Models.txt

@@ -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

+ 321 - 0
include/sound/ak4113.h

@@ -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 */
+

+ 6 - 6
include/sound/ak4114.h

@@ -95,13 +95,13 @@
 
 /* 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 */

+ 4 - 1
include/sound/ak4xxx-adda.h

@@ -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,

+ 2 - 1
sound/i2c/other/Makefile

@@ -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

+ 639 - 0
sound/i2c/other/ak4113.c

@@ -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);
+}

+ 104 - 32
sound/i2c/other/ak4xxx-adda.c

@@ -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)

+ 12 - 1
sound/pci/hda/Kconfig

@@ -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

+ 96 - 18
sound/pci/hda/hda_beep.c

@@ -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);

+ 10 - 0
sound/pci/hda/hda_beep.h

@@ -24,19 +24,29 @@
 
 #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

Diferenças do arquivo suprimidas por serem muito extensas
+ 474 - 79
sound/pci/hda/hda_codec.c


+ 11 - 0
sound/pci/hda/hda_codec.h

@@ -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) {}

+ 11 - 9
sound/pci/hda/hda_eld.c

@@ -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;

+ 11 - 6
sound/pci/hda/hda_generic.c

@@ -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;
 	}

+ 38 - 0
sound/pci/hda/hda_hwdep.c

@@ -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
 
 /*

+ 34 - 16
sound/pci/hda/hda_intel.c

@@ -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)

+ 64 - 5
sound/pci/hda/hda_local.h

@@ -23,6 +23,15 @@
 #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, \
@@ -66,6 +77,28 @@
 /* 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;
 }

+ 70 - 0
sound/pci/hda/hda_proc.c

@@ -26,6 +26,21 @@
 #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
 		 */

+ 28 - 33
sound/pci/hda/patch_analog.c

@@ -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);
-	}
 }
 
 

+ 2 - 2
sound/pci/hda/patch_ca0110.c

@@ -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)

+ 11 - 20
sound/pci/hda/patch_cirrus.c

@@ -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) ?

+ 150 - 39
sound/pci/hda/patch_conexant.c

@@ -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),
 	{}
 };
 
@@ -2397,6 +2492,19 @@ static int patch_cxt5066(struct hda_codec *codec)
 		/* no S/PDIF out */
 		spec->multiout.dig_out_nid = 0;
 
+		/* 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;
@@ -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");

+ 365 - 123
sound/pci/hda/patch_intelhdmi.c

@@ -33,15 +33,41 @@
 #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 */
 };

Diferenças do arquivo suprimidas por serem muito extensas
+ 161 - 225
sound/pci/hda/patch_realtek.c


+ 106 - 37
sound/pci/hda/patch_sigmatel.c

@@ -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;

Diferenças do arquivo suprimidas por serem muito extensas
+ 776 - 108
sound/pci/hda/patch_via.c


+ 1 - 1
sound/pci/ice1712/Makefile

@@ -5,7 +5,7 @@
 
 snd-ice17xx-ak4xxx-objs := ak4xxx.o
 snd-ice1712-objs := ice1712.o delta.o hoontech.o ews.o
-snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o maya44.o
+snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o maya44.o quartet.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o snd-ice17xx-ak4xxx.o

+ 12 - 0
sound/pci/ice1712/ice1712.c

@@ -298,6 +298,16 @@ static void snd_ice1712_set_gpio_dir(struct snd_ice1712 *ice, unsigned int data)
 	inb(ICEREG(ice, DATA)); /* dummy read for pci-posting */
 }
 
+static unsigned int snd_ice1712_get_gpio_dir(struct snd_ice1712 *ice)
+{
+	return snd_ice1712_read(ice, ICE1712_IREG_GPIO_DIRECTION);
+}
+
+static unsigned int snd_ice1712_get_gpio_mask(struct snd_ice1712 *ice)
+{
+	return snd_ice1712_read(ice, ICE1712_IREG_GPIO_WRITE_MASK);
+}
+
 static void snd_ice1712_set_gpio_mask(struct snd_ice1712 *ice, unsigned int data)
 {
 	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, data);
@@ -2557,7 +2567,9 @@ static int __devinit snd_ice1712_create(struct snd_card *card,
 	mutex_init(&ice->i2c_mutex);
 	mutex_init(&ice->open_mutex);
 	ice->gpio.set_mask = snd_ice1712_set_gpio_mask;
+	ice->gpio.get_mask = snd_ice1712_get_gpio_mask;
 	ice->gpio.set_dir = snd_ice1712_set_gpio_dir;
+	ice->gpio.get_dir = snd_ice1712_get_gpio_dir;
 	ice->gpio.set_data = snd_ice1712_set_gpio_data;
 	ice->gpio.get_data = snd_ice1712_get_gpio_data;
 

+ 12 - 2
sound/pci/ice1712/ice1712.h

@@ -359,7 +359,9 @@ struct snd_ice1712 {
 		unsigned int saved[2];		/* for ewx_i2c */
 		/* operators */
 		void (*set_mask)(struct snd_ice1712 *ice, unsigned int data);
+		unsigned int (*get_mask)(struct snd_ice1712 *ice);
 		void (*set_dir)(struct snd_ice1712 *ice, unsigned int data);
+		unsigned int (*get_dir)(struct snd_ice1712 *ice);
 		void (*set_data)(struct snd_ice1712 *ice, unsigned int data);
 		unsigned int (*get_data)(struct snd_ice1712 *ice);
 		/* misc operators - move to another place? */
@@ -377,8 +379,11 @@ struct snd_ice1712 {
 	unsigned int (*get_rate)(struct snd_ice1712 *ice);
 	void (*set_rate)(struct snd_ice1712 *ice, unsigned int rate);
 	unsigned char (*set_mclk)(struct snd_ice1712 *ice, unsigned int rate);
-	void (*set_spdif_clock)(struct snd_ice1712 *ice);
-
+	int (*set_spdif_clock)(struct snd_ice1712 *ice, int type);
+	int (*get_spdif_master_type)(struct snd_ice1712 *ice);
+	char **ext_clock_names;
+	int ext_clock_count;
+	void (*pro_open)(struct snd_ice1712 *, struct snd_pcm_substream *);
 #ifdef CONFIG_PM
 	int (*pm_suspend)(struct snd_ice1712 *);
 	int (*pm_resume)(struct snd_ice1712 *);
@@ -399,6 +404,11 @@ static inline void snd_ice1712_gpio_set_dir(struct snd_ice1712 *ice, unsigned in
 	ice->gpio.set_dir(ice, bits);
 }
 
+static inline unsigned int snd_ice1712_gpio_get_dir(struct snd_ice1712 *ice)
+{
+	return ice->gpio.get_dir(ice);
+}
+
 static inline void snd_ice1712_gpio_set_mask(struct snd_ice1712 *ice, unsigned int bits)
 {
 	ice->gpio.set_mask(ice, bits);

+ 83 - 20
sound/pci/ice1712/ice1724.c

@@ -53,6 +53,7 @@
 #include "phase.h"
 #include "wtm.h"
 #include "se.h"
+#include "quartet.h"
 
 MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
 MODULE_DESCRIPTION("VIA ICEnsemble ICE1724/1720 (Envy24HT/PT)");
@@ -70,6 +71,7 @@ MODULE_SUPPORTED_DEVICE("{"
 	       PHASE_DEVICE_DESC
 	       WTM_DEVICE_DESC
 	       SE_DEVICE_DESC
+	       QTET_DEVICE_DESC
 		"{VIA,VT1720},"
 		"{VIA,VT1724},"
 		"{ICEnsemble,Generic ICE1724},"
@@ -104,6 +106,8 @@ static int PRO_RATE_LOCKED;
 static int PRO_RATE_RESET = 1;
 static unsigned int PRO_RATE_DEFAULT = 44100;
 
+static char *ext_clock_names[1] = { "IEC958 In" };
+
 /*
  *  Basic I/O
  */
@@ -118,9 +122,12 @@ static inline int stdclock_is_spdif_master(struct snd_ice1712 *ice)
 	return (inb(ICEMT1724(ice, RATE)) & VT1724_SPDIF_MASTER) ? 1 : 0;
 }
 
+/*
+ * locking rate makes sense only for internal clock mode
+ */
 static inline int is_pro_rate_locked(struct snd_ice1712 *ice)
 {
-	return ice->is_spdif_master(ice) || PRO_RATE_LOCKED;
+	return (!ice->is_spdif_master(ice)) && PRO_RATE_LOCKED;
 }
 
 /*
@@ -196,6 +203,12 @@ static void snd_vt1724_set_gpio_dir(struct snd_ice1712 *ice, unsigned int data)
 	inw(ICEREG1724(ice, GPIO_DIRECTION)); /* dummy read for pci-posting */
 }
 
+/* get gpio direction 0 = read, 1 = write */
+static unsigned int snd_vt1724_get_gpio_dir(struct snd_ice1712 *ice)
+{
+	return inl(ICEREG1724(ice, GPIO_DIRECTION));
+}
+
 /* set the gpio mask (0 = writable) */
 static void snd_vt1724_set_gpio_mask(struct snd_ice1712 *ice, unsigned int data)
 {
@@ -205,6 +218,17 @@ static void snd_vt1724_set_gpio_mask(struct snd_ice1712 *ice, unsigned int data)
 	inw(ICEREG1724(ice, GPIO_WRITE_MASK)); /* dummy read for pci-posting */
 }
 
+static unsigned int snd_vt1724_get_gpio_mask(struct snd_ice1712 *ice)
+{
+	unsigned int mask;
+	if (!ice->vt1720)
+		mask = (unsigned int)inb(ICEREG1724(ice, GPIO_WRITE_MASK_22));
+	else
+		mask = 0;
+	mask = (mask << 16) | inw(ICEREG1724(ice, GPIO_WRITE_MASK));
+	return mask;
+}
+
 static void snd_vt1724_set_gpio_data(struct snd_ice1712 *ice, unsigned int data)
 {
 	outw(data, ICEREG1724(ice, GPIO_DATA));
@@ -651,16 +675,22 @@ static int snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
 		return ((rate == ice->cur_rate) && !force) ? 0 : -EBUSY;
 	}
 	if (!force && is_pro_rate_locked(ice)) {
+		/* comparing required and current rate - makes sense for
+		 * internal clock only */
 		spin_unlock_irqrestore(&ice->reg_lock, flags);
 		return (rate == ice->cur_rate) ? 0 : -EBUSY;
 	}
 
-	old_rate = ice->get_rate(ice);
-	if (force || (old_rate != rate))
-		ice->set_rate(ice, rate);
-	else if (rate == ice->cur_rate) {
-		spin_unlock_irqrestore(&ice->reg_lock, flags);
-		return 0;
+	if (force || !ice->is_spdif_master(ice)) {
+		/* force means the rate was switched by ucontrol, otherwise
+		 * setting clock rate for internal clock mode */
+		old_rate = ice->get_rate(ice);
+		if (force || (old_rate != rate))
+			ice->set_rate(ice, rate);
+		else if (rate == ice->cur_rate) {
+			spin_unlock_irqrestore(&ice->reg_lock, flags);
+			return 0;
+		}
 	}
 
 	ice->cur_rate = rate;
@@ -1016,6 +1046,8 @@ static int snd_vt1724_playback_pro_open(struct snd_pcm_substream *substream)
 				   VT1724_BUFFER_ALIGN);
 	snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
 				   VT1724_BUFFER_ALIGN);
+	if (ice->pro_open)
+		ice->pro_open(ice, substream);
 	return 0;
 }
 
@@ -1034,6 +1066,8 @@ static int snd_vt1724_capture_pro_open(struct snd_pcm_substream *substream)
 				   VT1724_BUFFER_ALIGN);
 	snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
 				   VT1724_BUFFER_ALIGN);
+	if (ice->pro_open)
+		ice->pro_open(ice, substream);
 	return 0;
 }
 
@@ -1787,15 +1821,21 @@ static int snd_vt1724_pro_internal_clock_info(struct snd_kcontrol *kcontrol,
 					      struct snd_ctl_elem_info *uinfo)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
-
+	int hw_rates_count = ice->hw_rates->count;
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
 	uinfo->count = 1;
-	uinfo->value.enumerated.items = ice->hw_rates->count + 1;
+
+	uinfo->value.enumerated.items = hw_rates_count + ice->ext_clock_count;
+	/* upper limit - keep at top */
 	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
 		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
-	if (uinfo->value.enumerated.item == uinfo->value.enumerated.items - 1)
-		strcpy(uinfo->value.enumerated.name, "IEC958 Input");
+	if (uinfo->value.enumerated.item >= hw_rates_count)
+		/* ext_clock items */
+		strcpy(uinfo->value.enumerated.name,
+				ice->ext_clock_names[
+				uinfo->value.enumerated.item - hw_rates_count]);
 	else
+		/* int clock items */
 		sprintf(uinfo->value.enumerated.name, "%d",
 			ice->hw_rates->list[uinfo->value.enumerated.item]);
 	return 0;
@@ -1809,7 +1849,8 @@ static int snd_vt1724_pro_internal_clock_get(struct snd_kcontrol *kcontrol,
 
 	spin_lock_irq(&ice->reg_lock);
 	if (ice->is_spdif_master(ice)) {
-		ucontrol->value.enumerated.item[0] = ice->hw_rates->count;
+		ucontrol->value.enumerated.item[0] = ice->hw_rates->count +
+			ice->get_spdif_master_type(ice);
 	} else {
 		rate = ice->get_rate(ice);
 		ucontrol->value.enumerated.item[0] = 0;
@@ -1824,8 +1865,14 @@ static int snd_vt1724_pro_internal_clock_get(struct snd_kcontrol *kcontrol,
 	return 0;
 }
 
+static int stdclock_get_spdif_master_type(struct snd_ice1712 *ice)
+{
+	/* standard external clock - only single type - SPDIF IN */
+	return 0;
+}
+
 /* setting clock to external - SPDIF */
-static void stdclock_set_spdif_clock(struct snd_ice1712 *ice)
+static int stdclock_set_spdif_clock(struct snd_ice1712 *ice, int type)
 {
 	unsigned char oval;
 	unsigned char i2s_oval;
@@ -1834,27 +1881,30 @@ static void stdclock_set_spdif_clock(struct snd_ice1712 *ice)
 	/* setting 256fs */
 	i2s_oval = inb(ICEMT1724(ice, I2S_FORMAT));
 	outb(i2s_oval & ~VT1724_MT_I2S_MCLK_128X, ICEMT1724(ice, I2S_FORMAT));
+	return 0;
 }
 
+
 static int snd_vt1724_pro_internal_clock_put(struct snd_kcontrol *kcontrol,
 					     struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	unsigned int old_rate, new_rate;
 	unsigned int item = ucontrol->value.enumerated.item[0];
-	unsigned int spdif = ice->hw_rates->count;
+	unsigned int first_ext_clock = ice->hw_rates->count;
 
-	if (item > spdif)
+	if (item >  first_ext_clock + ice->ext_clock_count - 1)
 		return -EINVAL;
 
+	/* if rate = 0 => external clock */
 	spin_lock_irq(&ice->reg_lock);
 	if (ice->is_spdif_master(ice))
 		old_rate = 0;
 	else
 		old_rate = ice->get_rate(ice);
-	if (item == spdif) {
-		/* switching to external clock via SPDIF */
-		ice->set_spdif_clock(ice);
+	if (item >= first_ext_clock) {
+		/* switching to external clock */
+		ice->set_spdif_clock(ice, item - first_ext_clock);
 		new_rate = 0;
 	} else {
 		/* internal on-card clock */
@@ -1866,7 +1916,7 @@ static int snd_vt1724_pro_internal_clock_put(struct snd_kcontrol *kcontrol,
 	}
 	spin_unlock_irq(&ice->reg_lock);
 
-	/* the first reset to the SPDIF master mode? */
+	/* the first switch to the ext. clock mode? */
 	if (old_rate != new_rate && !new_rate) {
 		/* notify akm chips as well */
 		unsigned int i;
@@ -2136,6 +2186,7 @@ static struct snd_ice1712_card_info *card_tables[] __devinitdata = {
 	snd_vt1724_phase_cards,
 	snd_vt1724_wtm_cards,
 	snd_vt1724_se_cards,
+	snd_vt1724_qtet_cards,
 	NULL,
 };
 
@@ -2434,7 +2485,9 @@ static int __devinit snd_vt1724_create(struct snd_card *card,
 	mutex_init(&ice->open_mutex);
 	mutex_init(&ice->i2c_mutex);
 	ice->gpio.set_mask = snd_vt1724_set_gpio_mask;
+	ice->gpio.get_mask = snd_vt1724_get_gpio_mask;
 	ice->gpio.set_dir = snd_vt1724_set_gpio_dir;
+	ice->gpio.get_dir = snd_vt1724_get_gpio_dir;
 	ice->gpio.set_data = snd_vt1724_set_gpio_data;
 	ice->gpio.get_data = snd_vt1724_get_gpio_data;
 	ice->card = card;
@@ -2522,6 +2575,9 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,
 		return err;
 	}
 
+	/* field init before calling chip_init */
+	ice->ext_clock_count = 0;
+
 	for (tbl = card_tables; *tbl; tbl++) {
 		for (c = *tbl; c->subvendor; c++) {
 			if (c->subvendor == ice->eeprom.subvendor) {
@@ -2560,6 +2616,13 @@ __found:
 		ice->set_mclk = stdclock_set_mclk;
 	if (!ice->set_spdif_clock)
 		ice->set_spdif_clock = stdclock_set_spdif_clock;
+	if (!ice->get_spdif_master_type)
+		ice->get_spdif_master_type = stdclock_get_spdif_master_type;
+	if (!ice->ext_clock_names)
+		ice->ext_clock_names = ext_clock_names;
+	if (!ice->ext_clock_count)
+		ice->ext_clock_count = ARRAY_SIZE(ext_clock_names);
+
 	if (!ice->hw_rates)
 		set_std_hw_rates(ice);
 
@@ -2719,7 +2782,7 @@ static int snd_vt1724_resume(struct pci_dev *pci)
 
 	if (ice->pm_saved_is_spdif_master) {
 		/* switching to external clock via SPDIF */
-		ice->set_spdif_clock(ice);
+		ice->set_spdif_clock(ice, 0);
 	} else {
 		/* internal on-card clock */
 		snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 1);

+ 2 - 22
sound/pci/ice1712/juli.c

@@ -412,25 +412,6 @@ static struct snd_kcontrol_new juli_mute_controls[] __devinitdata = {
 	},
 };
 
-
-static void ak4358_proc_regs_read(struct snd_info_entry *entry,
-		struct snd_info_buffer *buffer)
-{
-	struct snd_ice1712 *ice = (struct snd_ice1712 *)entry->private_data;
-	int reg, val;
-	for (reg = 0; reg <= 0xf; reg++) {
-		val =  snd_akm4xxx_get(ice->akm, 0, reg);
-		snd_iprintf(buffer, "0x%02x = 0x%02x\n", reg, val);
-	}
-}
-
-static void ak4358_proc_init(struct snd_ice1712 *ice)
-{
-	struct snd_info_entry *entry;
-	if (!snd_card_proc_new(ice->card, "ak4358_codec", &entry))
-		snd_info_set_text_ops(entry, ice, ak4358_proc_regs_read);
-}
-
 static char *slave_vols[] __devinitdata = {
 	PCM_VOLUME,
 	MONITOR_AN_IN_VOLUME,
@@ -496,8 +477,6 @@ static int __devinit juli_add_controls(struct snd_ice1712 *ice)
 	/* only capture SPDIF over AK4114 */
 	err = snd_ak4114_build(spec->ak4114, NULL,
 			ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
-
-	ak4358_proc_init(ice);
 	if (err < 0)
 		return err;
 	return 0;
@@ -575,13 +554,14 @@ static inline unsigned char juli_set_mclk(struct snd_ice1712 *ice,
 }
 
 /* setting clock to external - SPDIF */
-static void juli_set_spdif_clock(struct snd_ice1712 *ice)
+static int juli_set_spdif_clock(struct snd_ice1712 *ice, int type)
 {
 	unsigned int old;
 	old = ice->gpio.get_data(ice);
 	/* external clock (= 0), multiply 1x, 48kHz */
 	ice->gpio.set_data(ice, (old & ~GPIO_RATE_MASK) | GPIO_MULTI_1X |
 			GPIO_FREQ_48KHZ);
+	return 0;
 }
 
 /* Called when ak4114 detects change in the input SPDIF stream */

+ 1130 - 0
sound/pci/ice1712/quartet.c

@@ -0,0 +1,1130 @@
+/*
+ *   ALSA driver for ICEnsemble VT1724 (Envy24HT)
+ *
+ *   Lowlevel functions for Infrasonic Quartet
+ *
+ *	Copyright (c) 2009 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 <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/tlv.h>
+#include <sound/info.h>
+
+#include "ice1712.h"
+#include "envy24ht.h"
+#include <sound/ak4113.h>
+#include "quartet.h"
+
+struct qtet_spec {
+	struct ak4113 *ak4113;
+	unsigned int scr;	/* system control register */
+	unsigned int mcr;	/* monitoring control register */
+	unsigned int cpld;	/* cpld register */
+};
+
+struct qtet_kcontrol_private {
+	unsigned int bit;
+	void (*set_register)(struct snd_ice1712 *ice, unsigned int val);
+	unsigned int (*get_register)(struct snd_ice1712 *ice);
+	unsigned char *texts[2];
+};
+
+enum {
+	IN12_SEL = 0,
+	IN34_SEL,
+	AIN34_SEL,
+	COAX_OUT,
+	IN12_MON12,
+	IN12_MON34,
+	IN34_MON12,
+	IN34_MON34,
+	OUT12_MON34,
+	OUT34_MON12,
+};
+
+static char *ext_clock_names[3] = {"IEC958 In", "Word Clock 1xFS",
+	"Word Clock 256xFS"};
+
+/* chip address on I2C bus */
+#define AK4113_ADDR		0x26	/* S/PDIF receiver */
+
+/* chip address on SPI bus */
+#define AK4620_ADDR		0x02	/* ADC/DAC */
+
+
+/*
+ * GPIO pins
+ */
+
+/* GPIO0 - O - DATA0, def. 0 */
+#define GPIO_D0			(1<<0)
+/* GPIO1 - I/O - DATA1, Jack Detect Input0 (0:present, 1:missing), def. 1 */
+#define GPIO_D1_JACKDTC0	(1<<1)
+/* GPIO2 - I/O - DATA2, Jack Detect Input1 (0:present, 1:missing), def. 1 */
+#define GPIO_D2_JACKDTC1	(1<<2)
+/* GPIO3 - I/O - DATA3, def. 1 */
+#define GPIO_D3			(1<<3)
+/* GPIO4 - I/O - DATA4, SPI CDTO, def. 1 */
+#define GPIO_D4_SPI_CDTO	(1<<4)
+/* GPIO5 - I/O - DATA5, SPI CCLK, def. 1 */
+#define GPIO_D5_SPI_CCLK	(1<<5)
+/* GPIO6 - I/O - DATA6, Cable Detect Input (0:detected, 1:not detected */
+#define GPIO_D6_CD		(1<<6)
+/* GPIO7 - I/O - DATA7, Device Detect Input (0:detected, 1:not detected */
+#define GPIO_D7_DD		(1<<7)
+/* GPIO8 - O - CPLD Chip Select, def. 1 */
+#define GPIO_CPLD_CSN		(1<<8)
+/* GPIO9 - O - CPLD register read/write (0:write, 1:read), def. 0 */
+#define GPIO_CPLD_RW		(1<<9)
+/* GPIO10 - O - SPI Chip Select for CODEC#0, def. 1 */
+#define GPIO_SPI_CSN0		(1<<10)
+/* GPIO11 - O - SPI Chip Select for CODEC#1, def. 1 */
+#define GPIO_SPI_CSN1		(1<<11)
+/* GPIO12 - O - Ex. Register Output Enable (0:enable, 1:disable), def. 1,
+ * init 0 */
+#define GPIO_EX_GPIOE		(1<<12)
+/* GPIO13 - O - Ex. Register0 Chip Select for System Control Register,
+ * def. 1 */
+#define GPIO_SCR		(1<<13)
+/* GPIO14 - O - Ex. Register1 Chip Select for Monitor Control Register,
+ * def. 1 */
+#define GPIO_MCR		(1<<14)
+
+#define GPIO_SPI_ALL		(GPIO_D4_SPI_CDTO | GPIO_D5_SPI_CCLK |\
+		GPIO_SPI_CSN0 | GPIO_SPI_CSN1)
+
+#define GPIO_DATA_MASK		(GPIO_D0 | GPIO_D1_JACKDTC0 | \
+		GPIO_D2_JACKDTC1 | GPIO_D3 | \
+		GPIO_D4_SPI_CDTO | GPIO_D5_SPI_CCLK | \
+		GPIO_D6_CD | GPIO_D7_DD)
+
+/* System Control Register GPIO_SCR data bits */
+/* Mic/Line select relay (0:line, 1:mic) */
+#define SCR_RELAY		GPIO_D0
+/* Phantom power drive control (0:5V, 1:48V) */
+#define SCR_PHP_V		GPIO_D1_JACKDTC0
+/* H/W mute control (0:Normal, 1:Mute) */
+#define SCR_MUTE		GPIO_D2_JACKDTC1
+/* Phantom power control (0:Phantom on, 1:off) */
+#define SCR_PHP			GPIO_D3
+/* Analog input 1/2 Source Select */
+#define SCR_AIN12_SEL0		GPIO_D4_SPI_CDTO
+#define SCR_AIN12_SEL1		GPIO_D5_SPI_CCLK
+/* Analog input 3/4 Source Select (0:line, 1:hi-z) */
+#define SCR_AIN34_SEL		GPIO_D6_CD
+/* Codec Power Down (0:power down, 1:normal) */
+#define SCR_CODEC_PDN		GPIO_D7_DD
+
+#define SCR_AIN12_LINE		(0)
+#define SCR_AIN12_MIC		(SCR_AIN12_SEL0)
+#define SCR_AIN12_LOWCUT	(SCR_AIN12_SEL1 | SCR_AIN12_SEL0)
+
+/* Monitor Control Register GPIO_MCR data bits */
+/* Input 1/2 to Monitor 1/2 (0:off, 1:on) */
+#define MCR_IN12_MON12		GPIO_D0
+/* Input 1/2 to Monitor 3/4 (0:off, 1:on) */
+#define MCR_IN12_MON34		GPIO_D1_JACKDTC0
+/* Input 3/4 to Monitor 1/2 (0:off, 1:on) */
+#define MCR_IN34_MON12		GPIO_D2_JACKDTC1
+/* Input 3/4 to Monitor 3/4 (0:off, 1:on) */
+#define MCR_IN34_MON34		GPIO_D3
+/* Output to Monitor 1/2 (0:off, 1:on) */
+#define MCR_OUT34_MON12		GPIO_D4_SPI_CDTO
+/* Output to Monitor 3/4 (0:off, 1:on) */
+#define MCR_OUT12_MON34		GPIO_D5_SPI_CCLK
+
+/* CPLD Register DATA bits */
+/* Clock Rate Select */
+#define CPLD_CKS0		GPIO_D0
+#define CPLD_CKS1		GPIO_D1_JACKDTC0
+#define CPLD_CKS2		GPIO_D2_JACKDTC1
+/* Sync Source Select (0:Internal, 1:External) */
+#define CPLD_SYNC_SEL		GPIO_D3
+/* Word Clock FS Select (0:FS, 1:256FS) */
+#define CPLD_WORD_SEL		GPIO_D4_SPI_CDTO
+/* Coaxial Output Source (IS-Link) (0:SPDIF, 1:I2S) */
+#define CPLD_COAX_OUT		GPIO_D5_SPI_CCLK
+/* Input 1/2 Source Select (0:Analog12, 1:An34) */
+#define CPLD_IN12_SEL		GPIO_D6_CD
+/* Input 3/4 Source Select (0:Analog34, 1:Digital In) */
+#define CPLD_IN34_SEL		GPIO_D7_DD
+
+/* internal clock (CPLD_SYNC_SEL = 0) options */
+#define CPLD_CKS_44100HZ	(0)
+#define CPLD_CKS_48000HZ	(CPLD_CKS0)
+#define CPLD_CKS_88200HZ	(CPLD_CKS1)
+#define CPLD_CKS_96000HZ	(CPLD_CKS1 | CPLD_CKS0)
+#define CPLD_CKS_176400HZ	(CPLD_CKS2)
+#define CPLD_CKS_192000HZ	(CPLD_CKS2 | CPLD_CKS0)
+
+#define CPLD_CKS_MASK		(CPLD_CKS0 | CPLD_CKS1 | CPLD_CKS2)
+
+/* external clock (CPLD_SYNC_SEL = 1) options */
+/* external clock - SPDIF */
+#define CPLD_EXT_SPDIF	(0 | CPLD_SYNC_SEL)
+/* external clock - WordClock 1xfs */
+#define CPLD_EXT_WORDCLOCK_1FS	(CPLD_CKS1 | CPLD_SYNC_SEL)
+/* external clock - WordClock 256xfs */
+#define CPLD_EXT_WORDCLOCK_256FS	(CPLD_CKS1 | CPLD_WORD_SEL |\
+		CPLD_SYNC_SEL)
+
+#define EXT_SPDIF_TYPE			0
+#define EXT_WORDCLOCK_1FS_TYPE		1
+#define EXT_WORDCLOCK_256FS_TYPE	2
+
+#define AK4620_DFS0		(1<<0)
+#define AK4620_DFS1		(1<<1)
+#define AK4620_CKS0		(1<<2)
+#define AK4620_CKS1		(1<<3)
+/* Clock and Format Control register */
+#define AK4620_DFS_REG		0x02
+
+/* Deem and Volume Control register */
+#define AK4620_DEEMVOL_REG	0x03
+#define AK4620_SMUTE		(1<<7)
+
+/*
+ * Conversion from int value to its binary form. Used for debugging.
+ * The output buffer must be allocated prior to calling the function.
+ */
+static char *get_binary(char *buffer, int value)
+{
+	int i, j, pos;
+	pos = 0;
+	for (i = 0; i < 4; ++i) {
+		for (j = 0; j < 8; ++j) {
+			if (value & (1 << (31-(i*8 + j))))
+				buffer[pos] = '1';
+			else
+				buffer[pos] = '0';
+			pos++;
+		}
+		if (i < 3) {
+			buffer[pos] = ' ';
+			pos++;
+		}
+	}
+	buffer[pos] = '\0';
+	return buffer;
+}
+
+/*
+ * Initial setup of the conversion array GPIO <-> rate
+ */
+static unsigned int qtet_rates[] = {
+	44100, 48000, 88200,
+	96000, 176400, 192000,
+};
+
+static unsigned int cks_vals[] = {
+	CPLD_CKS_44100HZ, CPLD_CKS_48000HZ, CPLD_CKS_88200HZ,
+	CPLD_CKS_96000HZ, CPLD_CKS_176400HZ, CPLD_CKS_192000HZ,
+};
+
+static struct snd_pcm_hw_constraint_list qtet_rates_info = {
+	.count = ARRAY_SIZE(qtet_rates),
+	.list = qtet_rates,
+	.mask = 0,
+};
+
+static void qtet_ak4113_write(void *private_data, unsigned char reg,
+		unsigned char val)
+{
+	snd_vt1724_write_i2c((struct snd_ice1712 *)private_data, AK4113_ADDR,
+			reg, val);
+}
+
+static unsigned char qtet_ak4113_read(void *private_data, unsigned char reg)
+{
+	return snd_vt1724_read_i2c((struct snd_ice1712 *)private_data,
+			AK4113_ADDR, reg);
+}
+
+
+/*
+ * AK4620 section
+ */
+
+/*
+ * Write data to addr register of ak4620
+ */
+static void qtet_akm_write(struct snd_akm4xxx *ak, int chip,
+		unsigned char addr, unsigned char data)
+{
+	unsigned int tmp, orig_dir;
+	int idx;
+	unsigned int addrdata;
+	struct snd_ice1712 *ice = ak->private_data[0];
+
+	if (snd_BUG_ON(chip < 0 || chip >= 4))
+		return;
+	/*printk(KERN_DEBUG "Writing to AK4620: chip=%d, addr=0x%x,
+	  data=0x%x\n", chip, addr, data);*/
+	orig_dir = ice->gpio.get_dir(ice);
+	ice->gpio.set_dir(ice, orig_dir | GPIO_SPI_ALL);
+	/* set mask - only SPI bits */
+	ice->gpio.set_mask(ice, ~GPIO_SPI_ALL);
+
+	tmp = ice->gpio.get_data(ice);
+	/* high all */
+	tmp |= GPIO_SPI_ALL;
+	ice->gpio.set_data(ice, tmp);
+	udelay(100);
+	/* drop chip select */
+	if (chip)
+		/* CODEC 1 */
+		tmp &= ~GPIO_SPI_CSN1;
+	else
+		tmp &= ~GPIO_SPI_CSN0;
+	ice->gpio.set_data(ice, tmp);
+	udelay(100);
+
+	/* build I2C address + data byte */
+	addrdata = (AK4620_ADDR << 6) | 0x20 | (addr & 0x1f);
+	addrdata = (addrdata << 8) | data;
+	for (idx = 15; idx >= 0; idx--) {
+		/* drop clock */
+		tmp &= ~GPIO_D5_SPI_CCLK;
+		ice->gpio.set_data(ice, tmp);
+		udelay(100);
+		/* set data */
+		if (addrdata & (1 << idx))
+			tmp |= GPIO_D4_SPI_CDTO;
+		else
+			tmp &= ~GPIO_D4_SPI_CDTO;
+		ice->gpio.set_data(ice, tmp);
+		udelay(100);
+		/* raise clock */
+		tmp |= GPIO_D5_SPI_CCLK;
+		ice->gpio.set_data(ice, tmp);
+		udelay(100);
+	}
+	/* all back to 1 */
+	tmp |= GPIO_SPI_ALL;
+	ice->gpio.set_data(ice, tmp);
+	udelay(100);
+
+	/* return all gpios to non-writable */
+	ice->gpio.set_mask(ice, 0xffffff);
+	/* restore GPIOs direction */
+	ice->gpio.set_dir(ice, orig_dir);
+}
+
+static void qtet_akm_set_regs(struct snd_akm4xxx *ak, unsigned char addr,
+		unsigned char mask, unsigned char value)
+{
+	unsigned char tmp;
+	int chip;
+	for (chip = 0; chip < ak->num_chips; chip++) {
+		tmp = snd_akm4xxx_get(ak, chip, addr);
+		/* clear the bits */
+		tmp &= ~mask;
+		/* set the new bits */
+		tmp |= value;
+		snd_akm4xxx_write(ak, chip, addr, tmp);
+	}
+}
+
+/*
+ * change the rate of AK4620
+ */
+static void qtet_akm_set_rate_val(struct snd_akm4xxx *ak, unsigned int rate)
+{
+	unsigned char ak4620_dfs;
+
+	if (rate == 0)  /* no hint - S/PDIF input is master or the new spdif
+			   input rate undetected, simply return */
+		return;
+
+	/* adjust DFS on codecs - see datasheet */
+	if (rate > 108000)
+		ak4620_dfs = AK4620_DFS1 | AK4620_CKS1;
+	else if (rate > 54000)
+		ak4620_dfs = AK4620_DFS0 | AK4620_CKS0;
+	else
+		ak4620_dfs = 0;
+
+	/* set new value */
+	qtet_akm_set_regs(ak, AK4620_DFS_REG, AK4620_DFS0 | AK4620_DFS1 |
+			AK4620_CKS0 | AK4620_CKS1, ak4620_dfs);
+}
+
+#define AK_CONTROL(xname, xch)	{ .name = xname, .num_channels = xch }
+
+#define PCM_12_PLAYBACK_VOLUME	"PCM 1/2 Playback Volume"
+#define PCM_34_PLAYBACK_VOLUME	"PCM 3/4 Playback Volume"
+#define PCM_12_CAPTURE_VOLUME	"PCM 1/2 Capture Volume"
+#define PCM_34_CAPTURE_VOLUME	"PCM 3/4 Capture Volume"
+
+static const struct snd_akm4xxx_dac_channel qtet_dac[] = {
+	AK_CONTROL(PCM_12_PLAYBACK_VOLUME, 2),
+	AK_CONTROL(PCM_34_PLAYBACK_VOLUME, 2),
+};
+
+static const struct snd_akm4xxx_adc_channel qtet_adc[] = {
+	AK_CONTROL(PCM_12_CAPTURE_VOLUME, 2),
+	AK_CONTROL(PCM_34_CAPTURE_VOLUME, 2),
+};
+
+static struct snd_akm4xxx akm_qtet_dac __devinitdata = {
+	.type = SND_AK4620,
+	.num_dacs = 4,	/* DAC1 - Output 12
+	*/
+	.num_adcs = 4,	/* ADC1 - Input 12
+	*/
+	.ops = {
+		.write = qtet_akm_write,
+		.set_rate_val = qtet_akm_set_rate_val,
+	},
+	.dac_info = qtet_dac,
+	.adc_info = qtet_adc,
+};
+
+/* Communication routines with the CPLD */
+
+
+/* Writes data to external register reg, both reg and data are
+ * GPIO representations */
+static void reg_write(struct snd_ice1712 *ice, unsigned int reg,
+		unsigned int data)
+{
+	unsigned int tmp;
+
+	mutex_lock(&ice->gpio_mutex);
+	/* set direction of used GPIOs*/
+	/* all outputs */
+	tmp = 0x00ffff;
+	ice->gpio.set_dir(ice, tmp);
+	/* mask - writable bits */
+	ice->gpio.set_mask(ice, ~(tmp));
+	/* write the data */
+	tmp = ice->gpio.get_data(ice);
+	tmp &= ~GPIO_DATA_MASK;
+	tmp |= data;
+	ice->gpio.set_data(ice, tmp);
+	udelay(100);
+	/* drop output enable */
+	tmp &=  ~GPIO_EX_GPIOE;
+	ice->gpio.set_data(ice, tmp);
+	udelay(100);
+	/* drop the register gpio */
+	tmp &= ~reg;
+	ice->gpio.set_data(ice, tmp);
+	udelay(100);
+	/* raise the register GPIO */
+	tmp |= reg;
+	ice->gpio.set_data(ice, tmp);
+	udelay(100);
+
+	/* raise all data gpios */
+	tmp |= GPIO_DATA_MASK;
+	ice->gpio.set_data(ice, tmp);
+	/* mask - immutable bits */
+	ice->gpio.set_mask(ice, 0xffffff);
+	/* outputs only 8-15 */
+	ice->gpio.set_dir(ice, 0x00ff00);
+	mutex_unlock(&ice->gpio_mutex);
+}
+
+static unsigned int get_scr(struct snd_ice1712 *ice)
+{
+	struct qtet_spec *spec = ice->spec;
+	return spec->scr;
+}
+
+static unsigned int get_mcr(struct snd_ice1712 *ice)
+{
+	struct qtet_spec *spec = ice->spec;
+	return spec->mcr;
+}
+
+static unsigned int get_cpld(struct snd_ice1712 *ice)
+{
+	struct qtet_spec *spec = ice->spec;
+	return spec->cpld;
+}
+
+static void set_scr(struct snd_ice1712 *ice, unsigned int val)
+{
+	struct qtet_spec *spec = ice->spec;
+	reg_write(ice, GPIO_SCR, val);
+	spec->scr = val;
+}
+
+static void set_mcr(struct snd_ice1712 *ice, unsigned int val)
+{
+	struct qtet_spec *spec = ice->spec;
+	reg_write(ice, GPIO_MCR, val);
+	spec->mcr = val;
+}
+
+static void set_cpld(struct snd_ice1712 *ice, unsigned int val)
+{
+	struct qtet_spec *spec = ice->spec;
+	reg_write(ice, GPIO_CPLD_CSN, val);
+	spec->cpld = val;
+}
+#ifdef CONFIG_PROC_FS
+static void proc_regs_read(struct snd_info_entry *entry,
+		struct snd_info_buffer *buffer)
+{
+	struct snd_ice1712 *ice = entry->private_data;
+	char bin_buffer[36];
+
+	snd_iprintf(buffer, "SCR:	%s\n", get_binary(bin_buffer,
+				get_scr(ice)));
+	snd_iprintf(buffer, "MCR:	%s\n", get_binary(bin_buffer,
+				get_mcr(ice)));
+	snd_iprintf(buffer, "CPLD:	%s\n", get_binary(bin_buffer,
+				get_cpld(ice)));
+}
+
+static void proc_init(struct snd_ice1712 *ice)
+{
+	struct snd_info_entry *entry;
+	if (!snd_card_proc_new(ice->card, "quartet", &entry))
+		snd_info_set_text_ops(entry, ice, proc_regs_read);
+}
+#else /* !CONFIG_PROC_FS */
+static void proc_init(struct snd_ice1712 *ice) {}
+#endif
+
+static int qtet_mute_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+	unsigned int val;
+	val = get_scr(ice) & SCR_MUTE;
+	ucontrol->value.integer.value[0] = (val) ? 0 : 1;
+	return 0;
+}
+
+static int qtet_mute_put(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+	unsigned int old, new, smute;
+	old = get_scr(ice) & SCR_MUTE;
+	if (ucontrol->value.integer.value[0]) {
+		/* unmute */
+		new = 0;
+		/* un-smuting DAC */
+		smute = 0;
+	} else {
+		/* mute */
+		new = SCR_MUTE;
+		/* smuting DAC */
+		smute = AK4620_SMUTE;
+	}
+	if (old != new) {
+		struct snd_akm4xxx *ak = ice->akm;
+		set_scr(ice, (get_scr(ice) & ~SCR_MUTE) | new);
+		/* set smute */
+		qtet_akm_set_regs(ak, AK4620_DEEMVOL_REG, AK4620_SMUTE, smute);
+		return 1;
+	}
+	/* no change */
+	return 0;
+}
+
+static int qtet_ain12_enum_info(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[3] = {"Line In 1/2", "Mic", "Mic + Low-cut"};
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = ARRAY_SIZE(texts);
+
+	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+		uinfo->value.enumerated.item =
+			uinfo->value.enumerated.items - 1;
+	strcpy(uinfo->value.enumerated.name,
+			texts[uinfo->value.enumerated.item]);
+
+	return 0;
+}
+
+static int qtet_ain12_sw_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+	unsigned int val, result;
+	val = get_scr(ice) & (SCR_AIN12_SEL1 | SCR_AIN12_SEL0);
+	switch (val) {
+	case SCR_AIN12_LINE:
+		result = 0;
+		break;
+	case SCR_AIN12_MIC:
+		result = 1;
+		break;
+	case SCR_AIN12_LOWCUT:
+		result = 2;
+		break;
+	default:
+		/* BUG - no other combinations allowed */
+		snd_BUG();
+		result = 0;
+	}
+	ucontrol->value.integer.value[0] = result;
+	return 0;
+}
+
+static int qtet_ain12_sw_put(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+	unsigned int old, new, tmp, masked_old;
+	old = new = get_scr(ice);
+	masked_old = old & (SCR_AIN12_SEL1 | SCR_AIN12_SEL0);
+	tmp = ucontrol->value.integer.value[0];
+	if (tmp == 2)
+		tmp = 3;	/* binary 10 is not supported */
+	tmp <<= 4;	/* shifting to SCR_AIN12_SEL0 */
+	if (tmp != masked_old) {
+		/* change requested */
+		switch (tmp) {
+		case SCR_AIN12_LINE:
+			new = old & ~(SCR_AIN12_SEL1 | SCR_AIN12_SEL0);
+			set_scr(ice, new);
+			/* turn off relay */
+			new &= ~SCR_RELAY;
+			set_scr(ice, new);
+			break;
+		case SCR_AIN12_MIC:
+			/* turn on relay */
+			new = old | SCR_RELAY;
+			set_scr(ice, new);
+			new = (new & ~SCR_AIN12_SEL1) | SCR_AIN12_SEL0;
+			set_scr(ice, new);
+			break;
+		case SCR_AIN12_LOWCUT:
+			/* turn on relay */
+			new = old | SCR_RELAY;
+			set_scr(ice, new);
+			new |= SCR_AIN12_SEL1 | SCR_AIN12_SEL0;
+			set_scr(ice, new);
+			break;
+		default:
+			snd_BUG();
+		}
+		return 1;
+	}
+	/* no change */
+	return 0;
+}
+
+static int qtet_php_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+	unsigned int val;
+	/* if phantom voltage =48V, phantom on */
+	val = get_scr(ice) & SCR_PHP_V;
+	ucontrol->value.integer.value[0] = val ? 1 : 0;
+	return 0;
+}
+
+static int qtet_php_put(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+	unsigned int old, new;
+	old = new = get_scr(ice);
+	if (ucontrol->value.integer.value[0] /* phantom on requested */
+			&& (~old & SCR_PHP_V)) /* 0 = voltage 5V */ {
+		/* is off, turn on */
+		/* turn voltage on first, = 1 */
+		new = old | SCR_PHP_V;
+		set_scr(ice, new);
+		/* turn phantom on, = 0 */
+		new &= ~SCR_PHP;
+		set_scr(ice, new);
+	} else if (!ucontrol->value.integer.value[0] && (old & SCR_PHP_V)) {
+		/* phantom off requested and 1 = voltage 48V */
+		/* is on, turn off */
+		/* turn voltage off first, = 0 */
+		new = old & ~SCR_PHP_V;
+		set_scr(ice, new);
+		/* turn phantom off, = 1 */
+		new |= SCR_PHP;
+		set_scr(ice, new);
+	}
+	if (old != new)
+		return 1;
+	/* no change */
+	return 0;
+}
+
+#define PRIV_SW(xid, xbit, xreg)	[xid] = {.bit = xbit,\
+	.set_register = set_##xreg,\
+	.get_register = get_##xreg, }
+
+
+#define PRIV_ENUM2(xid, xbit, xreg, xtext1, xtext2)	[xid] = {.bit = xbit,\
+	.set_register = set_##xreg,\
+	.get_register = get_##xreg,\
+	.texts = {xtext1, xtext2} }
+
+static struct qtet_kcontrol_private qtet_privates[] = {
+	PRIV_ENUM2(IN12_SEL, CPLD_IN12_SEL, cpld, "An In 1/2", "An In 3/4"),
+	PRIV_ENUM2(IN34_SEL, CPLD_IN34_SEL, cpld, "An In 3/4", "IEC958 In"),
+	PRIV_ENUM2(AIN34_SEL, SCR_AIN34_SEL, scr, "Line In 3/4", "Hi-Z"),
+	PRIV_ENUM2(COAX_OUT, CPLD_COAX_OUT, cpld, "IEC958", "I2S"),
+	PRIV_SW(IN12_MON12, MCR_IN12_MON12, mcr),
+	PRIV_SW(IN12_MON34, MCR_IN12_MON34, mcr),
+	PRIV_SW(IN34_MON12, MCR_IN34_MON12, mcr),
+	PRIV_SW(IN34_MON34, MCR_IN34_MON34, mcr),
+	PRIV_SW(OUT12_MON34, MCR_OUT12_MON34, mcr),
+	PRIV_SW(OUT34_MON12, MCR_OUT34_MON12, mcr),
+};
+
+static int qtet_enum_info(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_info *uinfo)
+{
+	struct qtet_kcontrol_private private =
+		qtet_privates[kcontrol->private_value];
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = ARRAY_SIZE(private.texts);
+
+	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+		uinfo->value.enumerated.item =
+			uinfo->value.enumerated.items - 1;
+	strcpy(uinfo->value.enumerated.name,
+			private.texts[uinfo->value.enumerated.item]);
+
+	return 0;
+}
+
+static int qtet_sw_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct qtet_kcontrol_private private =
+		qtet_privates[kcontrol->private_value];
+	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.integer.value[0] =
+		(private.get_register(ice) & private.bit) ? 1 : 0;
+	return 0;
+}
+
+static int qtet_sw_put(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct qtet_kcontrol_private private =
+		qtet_privates[kcontrol->private_value];
+	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
+	unsigned int old, new;
+	old = private.get_register(ice);
+	if (ucontrol->value.integer.value[0])
+		new = old | private.bit;
+	else
+		new = old & ~private.bit;
+	if (old != new) {
+		private.set_register(ice, new);
+		return 1;
+	}
+	/* no change */
+	return 0;
+}
+
+#define qtet_sw_info	snd_ctl_boolean_mono_info
+
+#define QTET_CONTROL(xname, xtype, xpriv)	\
+	{.iface = SNDRV_CTL_ELEM_IFACE_MIXER,\
+	.name = xname,\
+	.info = qtet_##xtype##_info,\
+	.get = qtet_sw_get,\
+	.put = qtet_sw_put,\
+	.private_value = xpriv }
+
+static struct snd_kcontrol_new qtet_controls[] __devinitdata = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Master Playback Switch",
+		.info = qtet_sw_info,
+		.get = qtet_mute_get,
+		.put = qtet_mute_put,
+		.private_value = 0
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Phantom Power",
+		.info = qtet_sw_info,
+		.get = qtet_php_get,
+		.put = qtet_php_put,
+		.private_value = 0
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Analog In 1/2 Capture Switch",
+		.info = qtet_ain12_enum_info,
+		.get = qtet_ain12_sw_get,
+		.put = qtet_ain12_sw_put,
+		.private_value = 0
+	},
+	QTET_CONTROL("Analog In 3/4 Capture Switch", enum, AIN34_SEL),
+	QTET_CONTROL("PCM In 1/2 Capture Switch", enum, IN12_SEL),
+	QTET_CONTROL("PCM In 3/4 Capture Switch", enum, IN34_SEL),
+	QTET_CONTROL("Coax Output Source", enum, COAX_OUT),
+	QTET_CONTROL("Analog In 1/2 to Monitor 1/2", sw, IN12_MON12),
+	QTET_CONTROL("Analog In 1/2 to Monitor 3/4", sw, IN12_MON34),
+	QTET_CONTROL("Analog In 3/4 to Monitor 1/2", sw, IN34_MON12),
+	QTET_CONTROL("Analog In 3/4 to Monitor 3/4", sw, IN34_MON34),
+	QTET_CONTROL("Output 1/2 to Monitor 3/4", sw, OUT12_MON34),
+	QTET_CONTROL("Output 3/4 to Monitor 1/2", sw, OUT34_MON12),
+};
+
+static char *slave_vols[] __devinitdata = {
+	PCM_12_PLAYBACK_VOLUME,
+	PCM_34_PLAYBACK_VOLUME,
+	NULL
+};
+
+static __devinitdata
+DECLARE_TLV_DB_SCALE(qtet_master_db_scale, -6350, 50, 1);
+
+static struct snd_kcontrol __devinit *ctl_find(struct snd_card *card,
+		const char *name)
+{
+	struct snd_ctl_elem_id sid;
+	memset(&sid, 0, sizeof(sid));
+	/* FIXME: strcpy is bad. */
+	strcpy(sid.name, name);
+	sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+	return snd_ctl_find_id(card, &sid);
+}
+
+static void __devinit add_slaves(struct snd_card *card,
+		struct snd_kcontrol *master, char **list)
+{
+	for (; *list; list++) {
+		struct snd_kcontrol *slave = ctl_find(card, *list);
+		if (slave)
+			snd_ctl_add_slave(master, slave);
+	}
+}
+
+static int __devinit qtet_add_controls(struct snd_ice1712 *ice)
+{
+	struct qtet_spec *spec = ice->spec;
+	int err, i;
+	struct snd_kcontrol *vmaster;
+	err = snd_ice1712_akm4xxx_build_controls(ice);
+	if (err < 0)
+		return err;
+	for (i = 0; i < ARRAY_SIZE(qtet_controls); i++) {
+		err = snd_ctl_add(ice->card,
+				snd_ctl_new1(&qtet_controls[i], ice));
+		if (err < 0)
+			return err;
+	}
+
+	/* Create virtual master control */
+	vmaster = snd_ctl_make_virtual_master("Master Playback Volume",
+			qtet_master_db_scale);
+	if (!vmaster)
+		return -ENOMEM;
+	add_slaves(ice->card, vmaster, slave_vols);
+	err = snd_ctl_add(ice->card, vmaster);
+	if (err < 0)
+		return err;
+	/* only capture SPDIF over AK4113 */
+	err = snd_ak4113_build(spec->ak4113,
+			ice->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream);
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+static inline int qtet_is_spdif_master(struct snd_ice1712 *ice)
+{
+	/* CPLD_SYNC_SEL: 0 = internal, 1 = external (i.e. spdif master) */
+	return (get_cpld(ice) & CPLD_SYNC_SEL) ? 1 : 0;
+}
+
+static unsigned int qtet_get_rate(struct snd_ice1712 *ice)
+{
+	int i;
+	unsigned char result;
+
+	result =  get_cpld(ice) & CPLD_CKS_MASK;
+	for (i = 0; i < ARRAY_SIZE(cks_vals); i++)
+		if (cks_vals[i] == result)
+			return qtet_rates[i];
+	return 0;
+}
+
+static int get_cks_val(int rate)
+{
+	int i;
+	for (i = 0; i < ARRAY_SIZE(qtet_rates); i++)
+		if (qtet_rates[i] == rate)
+			return cks_vals[i];
+	return 0;
+}
+
+/* setting new rate */
+static void qtet_set_rate(struct snd_ice1712 *ice, unsigned int rate)
+{
+	unsigned int new;
+	unsigned char val;
+	/* switching ice1724 to external clock - supplied by ext. circuits */
+	val = inb(ICEMT1724(ice, RATE));
+	outb(val | VT1724_SPDIF_MASTER, ICEMT1724(ice, RATE));
+
+	new =  (get_cpld(ice) & ~CPLD_CKS_MASK) | get_cks_val(rate);
+	/* switch to internal clock, drop CPLD_SYNC_SEL */
+	new &= ~CPLD_SYNC_SEL;
+	/* printk(KERN_DEBUG "QT - set_rate: old %x, new %x\n",
+	   get_cpld(ice), new); */
+	set_cpld(ice, new);
+}
+
+static inline unsigned char qtet_set_mclk(struct snd_ice1712 *ice,
+		unsigned int rate)
+{
+	/* no change in master clock */
+	return 0;
+}
+
+/* setting clock to external - SPDIF */
+static int qtet_set_spdif_clock(struct snd_ice1712 *ice, int type)
+{
+	unsigned int old, new;
+
+	old = new = get_cpld(ice);
+	new &= ~(CPLD_CKS_MASK | CPLD_WORD_SEL);
+	switch (type) {
+	case EXT_SPDIF_TYPE:
+		new |= CPLD_EXT_SPDIF;
+		break;
+	case EXT_WORDCLOCK_1FS_TYPE:
+		new |= CPLD_EXT_WORDCLOCK_1FS;
+		break;
+	case EXT_WORDCLOCK_256FS_TYPE:
+		new |= CPLD_EXT_WORDCLOCK_256FS;
+		break;
+	default:
+		snd_BUG();
+	}
+	if (old != new) {
+		set_cpld(ice, new);
+		/* changed */
+		return 1;
+	}
+	return 0;
+}
+
+static int qtet_get_spdif_master_type(struct snd_ice1712 *ice)
+{
+	unsigned int val;
+	int result;
+	val = get_cpld(ice);
+	/* checking only rate/clock-related bits */
+	val &= (CPLD_CKS_MASK | CPLD_WORD_SEL | CPLD_SYNC_SEL);
+	if (!(val & CPLD_SYNC_SEL)) {
+		/* switched to internal clock, is not any external type */
+		result = -1;
+	} else {
+		switch (val) {
+		case (CPLD_EXT_SPDIF):
+			result = EXT_SPDIF_TYPE;
+			break;
+		case (CPLD_EXT_WORDCLOCK_1FS):
+			result = EXT_WORDCLOCK_1FS_TYPE;
+			break;
+		case (CPLD_EXT_WORDCLOCK_256FS):
+			result = EXT_WORDCLOCK_256FS_TYPE;
+			break;
+		default:
+			/* undefined combination of external clock setup */
+			snd_BUG();
+			result = 0;
+		}
+	}
+	return result;
+}
+
+/* Called when ak4113 detects change in the input SPDIF stream */
+static void qtet_ak4113_change(struct ak4113 *ak4113, unsigned char c0,
+		unsigned char c1)
+{
+	struct snd_ice1712 *ice = ak4113->change_callback_private;
+	int rate;
+	if ((qtet_get_spdif_master_type(ice) == EXT_SPDIF_TYPE) &&
+			c1) {
+		/* only for SPDIF master mode, rate was changed */
+		rate = snd_ak4113_external_rate(ak4113);
+		/* printk(KERN_DEBUG "ak4113 - input rate changed to %d\n",
+		   rate); */
+		qtet_akm_set_rate_val(ice->akm, rate);
+	}
+}
+
+/*
+ * If clock slaved to SPDIF-IN, setting runtime rate
+ * to the detected external rate
+ */
+static void qtet_spdif_in_open(struct snd_ice1712 *ice,
+		struct snd_pcm_substream *substream)
+{
+	struct qtet_spec *spec = ice->spec;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int rate;
+
+	if (qtet_get_spdif_master_type(ice) != EXT_SPDIF_TYPE)
+		/* not external SPDIF, no rate limitation */
+		return;
+	/* only external SPDIF can detect incoming sample rate */
+	rate = snd_ak4113_external_rate(spec->ak4113);
+	if (rate >= runtime->hw.rate_min && rate <= runtime->hw.rate_max) {
+		runtime->hw.rate_min = rate;
+		runtime->hw.rate_max = rate;
+	}
+}
+
+/*
+ * initialize the chip
+ */
+static int __devinit qtet_init(struct snd_ice1712 *ice)
+{
+	static const unsigned char ak4113_init_vals[] = {
+		/* AK4113_REG_PWRDN */	AK4113_RST | AK4113_PWN |
+			AK4113_OCKS0 | AK4113_OCKS1,
+		/* AK4113_REQ_FORMAT */	AK4113_DIF_I24I2S | AK4113_VTX |
+			AK4113_DEM_OFF | AK4113_DEAU,
+		/* AK4113_REG_IO0 */	AK4113_OPS2 | AK4113_TXE |
+			AK4113_XTL_24_576M,
+		/* AK4113_REG_IO1 */	AK4113_EFH_1024LRCLK | AK4113_IPS(0),
+		/* AK4113_REG_INT0_MASK */	0,
+		/* AK4113_REG_INT1_MASK */	0,
+		/* AK4113_REG_DATDTS */		0,
+	};
+	int err;
+	struct qtet_spec *spec;
+	struct snd_akm4xxx *ak;
+	unsigned char val;
+
+	/* switching ice1724 to external clock - supplied by ext. circuits */
+	val = inb(ICEMT1724(ice, RATE));
+	outb(val | VT1724_SPDIF_MASTER, ICEMT1724(ice, RATE));
+
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (!spec)
+		return -ENOMEM;
+	/* qtet is clocked by Xilinx array */
+	ice->hw_rates = &qtet_rates_info;
+	ice->is_spdif_master = qtet_is_spdif_master;
+	ice->get_rate = qtet_get_rate;
+	ice->set_rate = qtet_set_rate;
+	ice->set_mclk = qtet_set_mclk;
+	ice->set_spdif_clock = qtet_set_spdif_clock;
+	ice->get_spdif_master_type = qtet_get_spdif_master_type;
+	ice->ext_clock_names = ext_clock_names;
+	ice->ext_clock_count = ARRAY_SIZE(ext_clock_names);
+	/* since Qtet can detect correct SPDIF-in rate, all streams can be
+	 * limited to this specific rate */
+	ice->spdif.ops.open = ice->pro_open = qtet_spdif_in_open;
+	ice->spec = spec;
+
+	/* Mute Off */
+	/* SCR Initialize*/
+	/* keep codec power down first */
+	set_scr(ice, SCR_PHP);
+	udelay(1);
+	/* codec power up */
+	set_scr(ice, SCR_PHP | SCR_CODEC_PDN);
+
+	/* MCR Initialize */
+	set_mcr(ice, 0);
+
+	/* CPLD Initialize */
+	set_cpld(ice, 0);
+
+
+	ice->num_total_dacs = 2;
+	ice->num_total_adcs = 2;
+
+	ice->akm = kcalloc(2, sizeof(struct snd_akm4xxx), GFP_KERNEL);
+	ak = ice->akm;
+	if (!ak)
+		return -ENOMEM;
+	/* only one codec with two chips */
+	ice->akm_codecs = 1;
+	err = snd_ice1712_akm4xxx_init(ak, &akm_qtet_dac, NULL, ice);
+	if (err < 0)
+		return err;
+	err = snd_ak4113_create(ice->card,
+			qtet_ak4113_read,
+			qtet_ak4113_write,
+			ak4113_init_vals,
+			ice, &spec->ak4113);
+	if (err < 0)
+		return err;
+	/* callback for codecs rate setting */
+	spec->ak4113->change_callback = qtet_ak4113_change;
+	spec->ak4113->change_callback_private = ice;
+	/* AK41143 in Quartet can detect external rate correctly
+	 * (i.e. check_flags = 0) */
+	spec->ak4113->check_flags = 0;
+
+	proc_init(ice);
+
+	qtet_set_rate(ice, 44100);
+	return 0;
+}
+
+static unsigned char qtet_eeprom[] __devinitdata = {
+	[ICE_EEP2_SYSCONF]     = 0x28,	/* clock 256(24MHz), mpu401, 1xADC,
+					   1xDACs, SPDIF in */
+	[ICE_EEP2_ACLINK]      = 0x80,	/* I2S */
+	[ICE_EEP2_I2S]         = 0x78,	/* 96k, 24bit, 192k */
+	[ICE_EEP2_SPDIF]       = 0xc3,	/* out-en, out-int, in, out-ext */
+	[ICE_EEP2_GPIO_DIR]    = 0x00,	/* 0-7 inputs, switched to output
+					   only during output operations */
+	[ICE_EEP2_GPIO_DIR1]   = 0xff,  /* 8-15 outputs */
+	[ICE_EEP2_GPIO_DIR2]   = 0x00,
+	[ICE_EEP2_GPIO_MASK]   = 0xff,	/* changed only for OUT operations */
+	[ICE_EEP2_GPIO_MASK1]  = 0x00,
+	[ICE_EEP2_GPIO_MASK2]  = 0xff,
+
+	[ICE_EEP2_GPIO_STATE]  = 0x00, /* inputs */
+	[ICE_EEP2_GPIO_STATE1] = 0x7d, /* all 1, but GPIO_CPLD_RW
+					  and GPIO15 always zero */
+	[ICE_EEP2_GPIO_STATE2] = 0x00, /* inputs */
+};
+
+/* entry point */
+struct snd_ice1712_card_info snd_vt1724_qtet_cards[] __devinitdata = {
+	{
+		.subvendor = VT1724_SUBDEVICE_QTET,
+		.name = "Infrasonic Quartet",
+		.model = "quartet",
+		.chip_init = qtet_init,
+		.build_controls = qtet_add_controls,
+		.eeprom_size = sizeof(qtet_eeprom),
+		.eeprom_data = qtet_eeprom,
+	},
+	{ } /* terminator */
+};

+ 10 - 0
sound/pci/ice1712/quartet.h

@@ -0,0 +1,10 @@
+#ifndef __SOUND_QTET_H
+#define __SOUND_QTET_H
+
+#define QTET_DEVICE_DESC		"{Infrasonic,Quartet},"
+
+#define VT1724_SUBDEVICE_QTET		0x30305349	/* Infrasonic Quartet */
+
+extern struct snd_ice1712_card_info  snd_vt1724_qtet_cards[];
+
+#endif	/* __SOUND_QTET_H */

+ 2 - 1
sound/pci/oxygen/Makefile

@@ -1,7 +1,8 @@
 snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o
 snd-hifier-objs := hifier.o
 snd-oxygen-objs := oxygen.o
-snd-virtuoso-objs := virtuoso.o
+snd-virtuoso-objs := virtuoso.o xonar_lib.o \
+	xonar_pcm179x.o xonar_cs43xx.o xonar_hdmi.o
 
 obj-$(CONFIG_SND_OXYGEN_LIB) += snd-oxygen-lib.o
 obj-$(CONFIG_SND_HIFIER) += snd-hifier.o

+ 83 - 0
sound/pci/oxygen/cs2000.h

@@ -0,0 +1,83 @@
+#ifndef CS2000_H_INCLUDED
+#define CS2000_H_INCLUDED
+
+#define CS2000_DEV_ID		0x01
+#define CS2000_DEV_CTRL		0x02
+#define CS2000_DEV_CFG_1	0x03
+#define CS2000_DEV_CFG_2	0x04
+#define CS2000_GLOBAL_CFG	0x05
+#define CS2000_RATIO_0		0x06 /* 32 bits, big endian */
+#define CS2000_RATIO_1		0x0a
+#define CS2000_RATIO_2		0x0e
+#define CS2000_RATIO_3		0x12
+#define CS2000_FUN_CFG_1	0x16
+#define CS2000_FUN_CFG_2	0x17
+#define CS2000_FUN_CFG_3	0x1e
+
+/* DEV_ID */
+#define CS2000_DEVICE_MASK		0xf8
+#define CS2000_REVISION_MASK		0x07
+
+/* DEV_CTRL */
+#define CS2000_UNLOCK			0x80
+#define CS2000_AUX_OUT_DIS		0x02
+#define CS2000_CLK_OUT_DIS		0x01
+
+/* DEV_CFG_1 */
+#define CS2000_R_MOD_SEL_MASK		0xe0
+#define CS2000_R_MOD_SEL_1		0x00
+#define CS2000_R_MOD_SEL_2		0x20
+#define CS2000_R_MOD_SEL_4		0x40
+#define CS2000_R_MOD_SEL_8		0x60
+#define CS2000_R_MOD_SEL_1_2		0x80
+#define CS2000_R_MOD_SEL_1_4		0xa0
+#define CS2000_R_MOD_SEL_1_8		0xc0
+#define CS2000_R_MOD_SEL_1_16		0xe0
+#define CS2000_R_SEL_MASK		0x18
+#define CS2000_R_SEL_SHIFT		3
+#define CS2000_AUX_OUT_SRC_MASK		0x06
+#define CS2000_AUX_OUT_SRC_REF_CLK	0x00
+#define CS2000_AUX_OUT_SRC_CLK_IN	0x02
+#define CS2000_AUX_OUT_SRC_CLK_OUT	0x04
+#define CS2000_AUX_OUT_SRC_PLL_LOCK	0x06
+#define CS2000_EN_DEV_CFG_1		0x01
+
+/* DEV_CFG_2 */
+#define CS2000_LOCK_CLK_MASK		0x06
+#define CS2000_LOCK_CLK_SHIFT		1
+#define CS2000_FRAC_N_SRC_MASK		0x01
+#define CS2000_FRAC_N_SRC_STATIC	0x00
+#define CS2000_FRAC_N_SRC_DYNAMIC	0x01
+
+/* GLOBAL_CFG */
+#define CS2000_FREEZE			0x08
+#define CS2000_EN_DEV_CFG_2		0x01
+
+/* FUN_CFG_1 */
+#define CS2000_CLK_SKIP_EN		0x80
+#define CS2000_AUX_LOCK_CFG_MASK	0x40
+#define CS2000_AUX_LOCK_CFG_PP_HIGH	0x00
+#define CS2000_AUX_LOCK_CFG_OD_LOW	0x40
+#define CS2000_REF_CLK_DIV_MASK		0x18
+#define CS2000_REF_CLK_DIV_4		0x00
+#define CS2000_REF_CLK_DIV_2		0x08
+#define CS2000_REF_CLK_DIV_1		0x10
+
+/* FUN_CFG_2 */
+#define CS2000_CLK_OUT_UNL		0x10
+#define CS2000_L_F_RATIO_CFG_MASK	0x08
+#define CS2000_L_F_RATIO_CFG_20_12	0x00
+#define CS2000_L_F_RATIO_CFG_12_20	0x08
+
+/* FUN_CFG_3 */
+#define CS2000_CLK_IN_BW_MASK		0x70
+#define CS2000_CLK_IN_BW_1		0x00
+#define CS2000_CLK_IN_BW_2		0x10
+#define CS2000_CLK_IN_BW_4		0x20
+#define CS2000_CLK_IN_BW_8		0x30
+#define CS2000_CLK_IN_BW_16		0x40
+#define CS2000_CLK_IN_BW_32		0x50
+#define CS2000_CLK_IN_BW_64		0x60
+#define CS2000_CLK_IN_BW_128		0x70
+
+#endif

+ 38 - 23
sound/pci/oxygen/hifier.c

@@ -17,6 +17,12 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
+/*
+ * CMI8788:
+ *
+ * SPI 0 -> AK4396
+ */
+
 #include <linux/delay.h>
 #include <linux/pci.h>
 #include <sound/control.h>
@@ -51,23 +57,28 @@ static struct pci_device_id hifier_ids[] __devinitdata = {
 MODULE_DEVICE_TABLE(pci, hifier_ids);
 
 struct hifier_data {
-	u8 ak4396_ctl2;
+	u8 ak4396_regs[5];
 };
 
 static void ak4396_write(struct oxygen *chip, u8 reg, u8 value)
 {
+	struct hifier_data *data = chip->model_data;
+
 	oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER  |
 			 OXYGEN_SPI_DATA_LENGTH_2 |
 			 OXYGEN_SPI_CLOCK_160 |
 			 (0 << OXYGEN_SPI_CODEC_SHIFT) |
 			 OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
 			 AK4396_WRITE | (reg << 8) | value);
+	data->ak4396_regs[reg] = value;
 }
 
-static void update_ak4396_volume(struct oxygen *chip)
+static void ak4396_write_cached(struct oxygen *chip, u8 reg, u8 value)
 {
-	ak4396_write(chip, AK4396_LCH_ATT, chip->dac_volume[0]);
-	ak4396_write(chip, AK4396_RCH_ATT, chip->dac_volume[1]);
+	struct hifier_data *data = chip->model_data;
+
+	if (value != data->ak4396_regs[reg])
+		ak4396_write(chip, reg, value);
 }
 
 static void hifier_registers_init(struct oxygen *chip)
@@ -75,16 +86,19 @@ static void hifier_registers_init(struct oxygen *chip)
 	struct hifier_data *data = chip->model_data;
 
 	ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
-	ak4396_write(chip, AK4396_CONTROL_2, data->ak4396_ctl2);
+	ak4396_write(chip, AK4396_CONTROL_2,
+		     data->ak4396_regs[AK4396_CONTROL_2]);
 	ak4396_write(chip, AK4396_CONTROL_3, AK4396_PCM);
-	update_ak4396_volume(chip);
+	ak4396_write(chip, AK4396_LCH_ATT, chip->dac_volume[0]);
+	ak4396_write(chip, AK4396_RCH_ATT, chip->dac_volume[1]);
 }
 
 static void hifier_init(struct oxygen *chip)
 {
 	struct hifier_data *data = chip->model_data;
 
-	data->ak4396_ctl2 = AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
+	data->ak4396_regs[AK4396_CONTROL_2] =
+		AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
 	hifier_registers_init(chip);
 
 	snd_component_add(chip->card, "AK4396");
@@ -106,20 +120,29 @@ static void set_ak4396_params(struct oxygen *chip,
 	struct hifier_data *data = chip->model_data;
 	u8 value;
 
-	value = data->ak4396_ctl2 & ~AK4396_DFS_MASK;
+	value = data->ak4396_regs[AK4396_CONTROL_2] & ~AK4396_DFS_MASK;
 	if (params_rate(params) <= 54000)
 		value |= AK4396_DFS_NORMAL;
 	else if (params_rate(params) <= 108000)
 		value |= AK4396_DFS_DOUBLE;
 	else
 		value |= AK4396_DFS_QUAD;
-	data->ak4396_ctl2 = value;
 
 	msleep(1); /* wait for the new MCLK to become stable */
 
-	ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB);
-	ak4396_write(chip, AK4396_CONTROL_2, value);
-	ak4396_write(chip, AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
+	if (value != data->ak4396_regs[AK4396_CONTROL_2]) {
+		ak4396_write(chip, AK4396_CONTROL_1,
+			     AK4396_DIF_24_MSB);
+		ak4396_write(chip, AK4396_CONTROL_2, value);
+		ak4396_write(chip, AK4396_CONTROL_1,
+			     AK4396_DIF_24_MSB | AK4396_RSTN);
+	}
+}
+
+static void update_ak4396_volume(struct oxygen *chip)
+{
+	ak4396_write_cached(chip, AK4396_LCH_ATT, chip->dac_volume[0]);
+	ak4396_write_cached(chip, AK4396_RCH_ATT, chip->dac_volume[1]);
 }
 
 static void update_ak4396_mute(struct oxygen *chip)
@@ -127,11 +150,10 @@ static void update_ak4396_mute(struct oxygen *chip)
 	struct hifier_data *data = chip->model_data;
 	u8 value;
 
-	value = data->ak4396_ctl2 & ~AK4396_SMUTE;
+	value = data->ak4396_regs[AK4396_CONTROL_2] & ~AK4396_SMUTE;
 	if (chip->dac_mute)
 		value |= AK4396_SMUTE;
-	data->ak4396_ctl2 = value;
-	ak4396_write(chip, AK4396_CONTROL_2, value);
+	ak4396_write_cached(chip, AK4396_CONTROL_2, value);
 }
 
 static void set_cs5340_params(struct oxygen *chip,
@@ -141,21 +163,14 @@ static void set_cs5340_params(struct oxygen *chip,
 
 static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
 
-static int hifier_control_filter(struct snd_kcontrol_new *template)
-{
-	if (!strcmp(template->name, "Stereo Upmixing"))
-		return 1; /* stereo only - we don't need upmixing */
-	return 0;
-}
-
 static const struct oxygen_model model_hifier = {
 	.shortname = "C-Media CMI8787",
 	.longname = "C-Media Oxygen HD Audio",
 	.chip = "CMI8788",
 	.init = hifier_init,
-	.control_filter = hifier_control_filter,
 	.cleanup = hifier_cleanup,
 	.resume = hifier_resume,
+	.get_i2s_mclk = oxygen_default_i2s_mclk,
 	.set_dac_params = set_ak4396_params,
 	.set_adc_params = set_cs5340_params,
 	.update_dac_volume = update_ak4396_volume,

+ 199 - 49
sound/pci/oxygen/oxygen.c

@@ -18,6 +18,8 @@
  */
 
 /*
+ * CMI8788:
+ *
  * SPI 0 -> 1st AK4396 (front)
  * SPI 1 -> 2nd AK4396 (surround)
  * SPI 2 -> 3rd AK4396 (center/LFE)
@@ -27,6 +29,10 @@
  * GPIO 0 -> DFS0 of AK5385
  * GPIO 1 -> DFS1 of AK5385
  * GPIO 8 -> enable headphone amplifier on HT-Omega models
+ *
+ * CM9780:
+ *
+ * GPO 0 -> route line-in (0) or AC97 output (1) to ADC input
  */
 
 #include <linux/delay.h>
@@ -91,8 +97,8 @@ MODULE_DEVICE_TABLE(pci, oxygen_ids);
 #define GPIO_CLARO_HP		0x0100
 
 struct generic_data {
-	u8 ak4396_ctl2;
-	u16 saved_wm8785_registers[2];
+	u8 ak4396_regs[4][5];
+	u16 wm8785_regs[3];
 };
 
 static void ak4396_write(struct oxygen *chip, unsigned int codec,
@@ -102,12 +108,24 @@ static void ak4396_write(struct oxygen *chip, unsigned int codec,
 	static const u8 codec_spi_map[4] = {
 		0, 1, 2, 4
 	};
+	struct generic_data *data = chip->model_data;
+
 	oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
 			 OXYGEN_SPI_DATA_LENGTH_2 |
 			 OXYGEN_SPI_CLOCK_160 |
 			 (codec_spi_map[codec] << OXYGEN_SPI_CODEC_SHIFT) |
 			 OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
 			 AK4396_WRITE | (reg << 8) | value);
+	data->ak4396_regs[codec][reg] = value;
+}
+
+static void ak4396_write_cached(struct oxygen *chip, unsigned int codec,
+				u8 reg, u8 value)
+{
+	struct generic_data *data = chip->model_data;
+
+	if (value != data->ak4396_regs[codec][reg])
+		ak4396_write(chip, codec, reg, value);
 }
 
 static void wm8785_write(struct oxygen *chip, u8 reg, unsigned int value)
@@ -120,20 +138,8 @@ static void wm8785_write(struct oxygen *chip, u8 reg, unsigned int value)
 			 (3 << OXYGEN_SPI_CODEC_SHIFT) |
 			 OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
 			 (reg << 9) | value);
-	if (reg < ARRAY_SIZE(data->saved_wm8785_registers))
-		data->saved_wm8785_registers[reg] = value;
-}
-
-static void update_ak4396_volume(struct oxygen *chip)
-{
-	unsigned int i;
-
-	for (i = 0; i < 4; ++i) {
-		ak4396_write(chip, i,
-			     AK4396_LCH_ATT, chip->dac_volume[i * 2]);
-		ak4396_write(chip, i,
-			     AK4396_RCH_ATT, chip->dac_volume[i * 2 + 1]);
-	}
+	if (reg < ARRAY_SIZE(data->wm8785_regs))
+		data->wm8785_regs[reg] = value;
 }
 
 static void ak4396_registers_init(struct oxygen *chip)
@@ -142,21 +148,25 @@ static void ak4396_registers_init(struct oxygen *chip)
 	unsigned int i;
 
 	for (i = 0; i < 4; ++i) {
-		ak4396_write(chip, i,
-			     AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
-		ak4396_write(chip, i,
-			     AK4396_CONTROL_2, data->ak4396_ctl2);
-		ak4396_write(chip, i,
-			     AK4396_CONTROL_3, AK4396_PCM);
+		ak4396_write(chip, i, AK4396_CONTROL_1,
+			     AK4396_DIF_24_MSB | AK4396_RSTN);
+		ak4396_write(chip, i, AK4396_CONTROL_2,
+			     data->ak4396_regs[0][AK4396_CONTROL_2]);
+		ak4396_write(chip, i, AK4396_CONTROL_3,
+			     AK4396_PCM);
+		ak4396_write(chip, i, AK4396_LCH_ATT,
+			     chip->dac_volume[i * 2]);
+		ak4396_write(chip, i, AK4396_RCH_ATT,
+			     chip->dac_volume[i * 2 + 1]);
 	}
-	update_ak4396_volume(chip);
 }
 
 static void ak4396_init(struct oxygen *chip)
 {
 	struct generic_data *data = chip->model_data;
 
-	data->ak4396_ctl2 = AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
+	data->ak4396_regs[0][AK4396_CONTROL_2] =
+		AK4396_SMUTE | AK4396_DEM_OFF | AK4396_DFS_NORMAL;
 	ak4396_registers_init(chip);
 	snd_component_add(chip->card, "AK4396");
 }
@@ -173,17 +183,17 @@ static void wm8785_registers_init(struct oxygen *chip)
 	struct generic_data *data = chip->model_data;
 
 	wm8785_write(chip, WM8785_R7, 0);
-	wm8785_write(chip, WM8785_R0, data->saved_wm8785_registers[0]);
-	wm8785_write(chip, WM8785_R1, data->saved_wm8785_registers[1]);
+	wm8785_write(chip, WM8785_R0, data->wm8785_regs[0]);
+	wm8785_write(chip, WM8785_R2, data->wm8785_regs[2]);
 }
 
 static void wm8785_init(struct oxygen *chip)
 {
 	struct generic_data *data = chip->model_data;
 
-	data->saved_wm8785_registers[0] = WM8785_MCR_SLAVE |
-		WM8785_OSR_SINGLE | WM8785_FORMAT_LJUST;
-	data->saved_wm8785_registers[1] = WM8785_WL_24;
+	data->wm8785_regs[0] =
+		WM8785_MCR_SLAVE | WM8785_OSR_SINGLE | WM8785_FORMAT_LJUST;
+	data->wm8785_regs[2] = WM8785_HPFR | WM8785_HPFL;
 	wm8785_registers_init(chip);
 	snd_component_add(chip->card, "WM8785");
 }
@@ -264,24 +274,36 @@ static void set_ak4396_params(struct oxygen *chip,
 	unsigned int i;
 	u8 value;
 
-	value = data->ak4396_ctl2 & ~AK4396_DFS_MASK;
+	value = data->ak4396_regs[0][AK4396_CONTROL_2] & ~AK4396_DFS_MASK;
 	if (params_rate(params) <= 54000)
 		value |= AK4396_DFS_NORMAL;
 	else if (params_rate(params) <= 108000)
 		value |= AK4396_DFS_DOUBLE;
 	else
 		value |= AK4396_DFS_QUAD;
-	data->ak4396_ctl2 = value;
 
 	msleep(1); /* wait for the new MCLK to become stable */
 
+	if (value != data->ak4396_regs[0][AK4396_CONTROL_2]) {
+		for (i = 0; i < 4; ++i) {
+			ak4396_write(chip, i, AK4396_CONTROL_1,
+				     AK4396_DIF_24_MSB);
+			ak4396_write(chip, i, AK4396_CONTROL_2, value);
+			ak4396_write(chip, i, AK4396_CONTROL_1,
+				     AK4396_DIF_24_MSB | AK4396_RSTN);
+		}
+	}
+}
+
+static void update_ak4396_volume(struct oxygen *chip)
+{
+	unsigned int i;
+
 	for (i = 0; i < 4; ++i) {
-		ak4396_write(chip, i,
-			     AK4396_CONTROL_1, AK4396_DIF_24_MSB);
-		ak4396_write(chip, i,
-			     AK4396_CONTROL_2, value);
-		ak4396_write(chip, i,
-			     AK4396_CONTROL_1, AK4396_DIF_24_MSB | AK4396_RSTN);
+		ak4396_write_cached(chip, i, AK4396_LCH_ATT,
+				    chip->dac_volume[i * 2]);
+		ak4396_write_cached(chip, i, AK4396_RCH_ATT,
+				    chip->dac_volume[i * 2 + 1]);
 	}
 }
 
@@ -291,21 +313,19 @@ static void update_ak4396_mute(struct oxygen *chip)
 	unsigned int i;
 	u8 value;
 
-	value = data->ak4396_ctl2 & ~AK4396_SMUTE;
+	value = data->ak4396_regs[0][AK4396_CONTROL_2] & ~AK4396_SMUTE;
 	if (chip->dac_mute)
 		value |= AK4396_SMUTE;
-	data->ak4396_ctl2 = value;
 	for (i = 0; i < 4; ++i)
-		ak4396_write(chip, i, AK4396_CONTROL_2, value);
+		ak4396_write_cached(chip, i, AK4396_CONTROL_2, value);
 }
 
 static void set_wm8785_params(struct oxygen *chip,
 			      struct snd_pcm_hw_params *params)
 {
+	struct generic_data *data = chip->model_data;
 	unsigned int value;
 
-	wm8785_write(chip, WM8785_R7, 0);
-
 	value = WM8785_MCR_SLAVE | WM8785_FORMAT_LJUST;
 	if (params_rate(params) <= 48000)
 		value |= WM8785_OSR_SINGLE;
@@ -313,13 +333,11 @@ static void set_wm8785_params(struct oxygen *chip,
 		value |= WM8785_OSR_DOUBLE;
 	else
 		value |= WM8785_OSR_QUAD;
-	wm8785_write(chip, WM8785_R0, value);
-
-	if (snd_pcm_format_width(params_format(params)) <= 16)
-		value = WM8785_WL_16;
-	else
-		value = WM8785_WL_24;
-	wm8785_write(chip, WM8785_R1, value);
+	if (value != data->wm8785_regs[0]) {
+		wm8785_write(chip, WM8785_R7, 0);
+		wm8785_write(chip, WM8785_R0, value);
+		wm8785_write(chip, WM8785_R2, data->wm8785_regs[2]);
+	}
 }
 
 static void set_ak5385_params(struct oxygen *chip,
@@ -337,6 +355,134 @@ static void set_ak5385_params(struct oxygen *chip,
 			      value, GPIO_AK5385_DFS_MASK);
 }
 
+static int rolloff_info(struct snd_kcontrol *ctl,
+			struct snd_ctl_elem_info *info)
+{
+	static const char *const names[2] = {
+		"Sharp Roll-off", "Slow Roll-off"
+	};
+
+	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	info->count = 1;
+	info->value.enumerated.items = 2;
+	if (info->value.enumerated.item >= 2)
+		info->value.enumerated.item = 1;
+	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
+	return 0;
+}
+
+static int rolloff_get(struct snd_kcontrol *ctl,
+		       struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+	struct generic_data *data = chip->model_data;
+
+	value->value.enumerated.item[0] =
+		(data->ak4396_regs[0][AK4396_CONTROL_2] & AK4396_SLOW) != 0;
+	return 0;
+}
+
+static int rolloff_put(struct snd_kcontrol *ctl,
+		       struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+	struct generic_data *data = chip->model_data;
+	unsigned int i;
+	int changed;
+	u8 reg;
+
+	mutex_lock(&chip->mutex);
+	reg = data->ak4396_regs[0][AK4396_CONTROL_2];
+	if (value->value.enumerated.item[0])
+		reg |= AK4396_SLOW;
+	else
+		reg &= ~AK4396_SLOW;
+	changed = reg != data->ak4396_regs[0][AK4396_CONTROL_2];
+	if (changed) {
+		for (i = 0; i < 4; ++i)
+			ak4396_write(chip, i, AK4396_CONTROL_2, reg);
+	}
+	mutex_unlock(&chip->mutex);
+	return changed;
+}
+
+static const struct snd_kcontrol_new rolloff_control = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "DAC Filter Playback Enum",
+	.info = rolloff_info,
+	.get = rolloff_get,
+	.put = rolloff_put,
+};
+
+static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
+{
+	static const char *const names[2] = {
+		"None", "High-pass Filter"
+	};
+
+	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	info->count = 1;
+	info->value.enumerated.items = 2;
+	if (info->value.enumerated.item >= 2)
+		info->value.enumerated.item = 1;
+	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
+	return 0;
+}
+
+static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+	struct generic_data *data = chip->model_data;
+
+	value->value.enumerated.item[0] =
+		(data->wm8785_regs[WM8785_R2] & WM8785_HPFR) != 0;
+	return 0;
+}
+
+static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+	struct generic_data *data = chip->model_data;
+	unsigned int reg;
+	int changed;
+
+	mutex_lock(&chip->mutex);
+	reg = data->wm8785_regs[WM8785_R2] & ~(WM8785_HPFR | WM8785_HPFL);
+	if (value->value.enumerated.item[0])
+		reg |= WM8785_HPFR | WM8785_HPFL;
+	changed = reg != data->wm8785_regs[WM8785_R2];
+	if (changed)
+		wm8785_write(chip, WM8785_R2, reg);
+	mutex_unlock(&chip->mutex);
+	return changed;
+}
+
+static const struct snd_kcontrol_new hpf_control = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "ADC Filter Capture Enum",
+	.info = hpf_info,
+	.get = hpf_get,
+	.put = hpf_put,
+};
+
+static int generic_mixer_init(struct oxygen *chip)
+{
+	return snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip));
+}
+
+static int generic_wm8785_mixer_init(struct oxygen *chip)
+{
+	int err;
+
+	err = generic_mixer_init(chip);
+	if (err < 0)
+		return err;
+	err = snd_ctl_add(chip->card, snd_ctl_new1(&hpf_control, chip));
+	if (err < 0)
+		return err;
+	return 0;
+}
+
 static const DECLARE_TLV_DB_LINEAR(ak4396_db_scale, TLV_DB_GAIN_MUTE, 0);
 
 static const struct oxygen_model model_generic = {
@@ -344,8 +490,10 @@ static const struct oxygen_model model_generic = {
 	.longname = "C-Media Oxygen HD Audio",
 	.chip = "CMI8788",
 	.init = generic_init,
+	.mixer_init = generic_wm8785_mixer_init,
 	.cleanup = generic_cleanup,
 	.resume = generic_resume,
+	.get_i2s_mclk = oxygen_default_i2s_mclk,
 	.set_dac_params = set_ak4396_params,
 	.set_adc_params = set_wm8785_params,
 	.update_dac_volume = update_ak4396_volume,
@@ -374,6 +522,7 @@ static int __devinit get_oxygen_model(struct oxygen *chip,
 	switch (id->driver_data) {
 	case MODEL_MERIDIAN:
 		chip->model.init = meridian_init;
+		chip->model.mixer_init = generic_mixer_init;
 		chip->model.resume = meridian_resume;
 		chip->model.set_adc_params = set_ak5385_params;
 		chip->model.device_config = PLAYBACK_0_TO_I2S |
@@ -389,6 +538,7 @@ static int __devinit get_oxygen_model(struct oxygen *chip,
 		break;
 	case MODEL_CLARO_HALO:
 		chip->model.init = claro_halo_init;
+		chip->model.mixer_init = generic_mixer_init;
 		chip->model.cleanup = claro_cleanup;
 		chip->model.suspend = claro_suspend;
 		chip->model.resume = claro_resume;

+ 5 - 0
sound/pci/oxygen/oxygen.h

@@ -78,12 +78,15 @@ struct oxygen_model {
 	void (*resume)(struct oxygen *chip);
 	void (*pcm_hardware_filter)(unsigned int channel,
 				    struct snd_pcm_hardware *hardware);
+	unsigned int (*get_i2s_mclk)(struct oxygen *chip, unsigned int channel,
+				     struct snd_pcm_hw_params *hw_params);
 	void (*set_dac_params)(struct oxygen *chip,
 			       struct snd_pcm_hw_params *params);
 	void (*set_adc_params)(struct oxygen *chip,
 			       struct snd_pcm_hw_params *params);
 	void (*update_dac_volume)(struct oxygen *chip);
 	void (*update_dac_mute)(struct oxygen *chip);
+	void (*update_center_lfe_mix)(struct oxygen *chip, bool mixed);
 	void (*gpio_changed)(struct oxygen *chip);
 	void (*uart_input)(struct oxygen *chip);
 	void (*ac97_switch)(struct oxygen *chip,
@@ -162,6 +165,8 @@ void oxygen_update_spdif_source(struct oxygen *chip);
 /* oxygen_pcm.c */
 
 int oxygen_pcm_init(struct oxygen *chip);
+unsigned int oxygen_default_i2s_mclk(struct oxygen *chip, unsigned int channel,
+				     struct snd_pcm_hw_params *hw_params);
 
 /* oxygen_io.c */
 

+ 28 - 1
sound/pci/oxygen/oxygen_lib.c

@@ -278,7 +278,11 @@ oxygen_search_pci_id(struct oxygen *chip, const struct pci_device_id ids[])
 static void oxygen_restore_eeprom(struct oxygen *chip,
 				  const struct pci_device_id *id)
 {
-	if (oxygen_read_eeprom(chip, 0) != OXYGEN_EEPROM_ID) {
+	u16 eeprom_id;
+
+	eeprom_id = oxygen_read_eeprom(chip, 0);
+	if (eeprom_id != OXYGEN_EEPROM_ID &&
+	    (eeprom_id != 0xffff || id->subdevice != 0x8788)) {
 		/*
 		 * This function gets called only when a known card model has
 		 * been detected, i.e., we know there is a valid subsystem
@@ -303,6 +307,28 @@ static void oxygen_restore_eeprom(struct oxygen *chip,
 	}
 }
 
+static void pci_bridge_magic(void)
+{
+	struct pci_dev *pci = NULL;
+	u32 tmp;
+
+	for (;;) {
+		/* If there is any Pericom PI7C9X110 PCI-E/PCI bridge ... */
+		pci = pci_get_device(0x12d8, 0xe110, pci);
+		if (!pci)
+			break;
+		/*
+		 * ... configure its secondary internal arbiter to park to
+		 * the secondary port, instead of to the last master.
+		 */
+		if (!pci_read_config_dword(pci, 0x40, &tmp)) {
+			tmp |= 1;
+			pci_write_config_dword(pci, 0x40, tmp);
+		}
+		/* Why?  Try asking C-Media. */
+	}
+}
+
 static void oxygen_init(struct oxygen *chip)
 {
 	unsigned int i;
@@ -581,6 +607,7 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
 	snd_card_set_dev(card, &pci->dev);
 	card->private_free = oxygen_card_free;
 
+	pci_bridge_magic();
 	oxygen_init(chip);
 	chip->model.init(chip);
 

+ 35 - 17
sound/pci/oxygen/oxygen_mixer.c

@@ -99,11 +99,15 @@ static int dac_mute_put(struct snd_kcontrol *ctl,
 
 static int upmix_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
 {
-	static const char *const names[3] = {
-		"Front", "Front+Surround", "Front+Surround+Back"
+	static const char *const names[5] = {
+		"Front",
+		"Front+Surround",
+		"Front+Surround+Back",
+		"Front+Surround+Center/LFE",
+		"Front+Surround+Center/LFE+Back",
 	};
 	struct oxygen *chip = ctl->private_data;
-	unsigned int count = 2 + (chip->model.dac_channels == 8);
+	unsigned int count = chip->model.update_center_lfe_mix ? 5 : 3;
 
 	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
 	info->count = 1;
@@ -127,7 +131,7 @@ static int upmix_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
 void oxygen_update_dac_routing(struct oxygen *chip)
 {
 	/* DAC 0: front, DAC 1: surround, DAC 2: center/LFE, DAC 3: back */
-	static const unsigned int reg_values[3] = {
+	static const unsigned int reg_values[5] = {
 		/* stereo -> front */
 		(0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
 		(1 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
@@ -143,6 +147,16 @@ void oxygen_update_dac_routing(struct oxygen *chip)
 		(0 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
 		(2 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
 		(0 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT),
+		/* stereo -> front+surround+center/LFE */
+		(0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
+		(0 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
+		(0 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
+		(3 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT),
+		/* stereo -> front+surround+center/LFE+back */
+		(0 << OXYGEN_PLAY_DAC0_SOURCE_SHIFT) |
+		(0 << OXYGEN_PLAY_DAC1_SOURCE_SHIFT) |
+		(0 << OXYGEN_PLAY_DAC2_SOURCE_SHIFT) |
+		(0 << OXYGEN_PLAY_DAC3_SOURCE_SHIFT),
 	};
 	u8 channels;
 	unsigned int reg_value;
@@ -167,22 +181,23 @@ void oxygen_update_dac_routing(struct oxygen *chip)
 			      OXYGEN_PLAY_DAC1_SOURCE_MASK |
 			      OXYGEN_PLAY_DAC2_SOURCE_MASK |
 			      OXYGEN_PLAY_DAC3_SOURCE_MASK);
+	if (chip->model.update_center_lfe_mix)
+		chip->model.update_center_lfe_mix(chip, chip->dac_routing > 2);
 }
 
 static int upmix_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
 {
 	struct oxygen *chip = ctl->private_data;
-	unsigned int count = 2 + (chip->model.dac_channels == 8);
+	unsigned int count = chip->model.update_center_lfe_mix ? 5 : 3;
 	int changed;
 
+	if (value->value.enumerated.item[0] >= count)
+		return -EINVAL;
 	mutex_lock(&chip->mutex);
 	changed = value->value.enumerated.item[0] != chip->dac_routing;
 	if (changed) {
-		chip->dac_routing = min(value->value.enumerated.item[0],
-					count - 1);
-		spin_lock_irq(&chip->reg_lock);
+		chip->dac_routing = value->value.enumerated.item[0];
 		oxygen_update_dac_routing(chip);
-		spin_unlock_irq(&chip->reg_lock);
 	}
 	mutex_unlock(&chip->mutex);
 	return changed;
@@ -790,7 +805,7 @@ static const struct {
 		.controls = {
 			{
 				.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-				.name = "Analog Input Monitor Switch",
+				.name = "Analog Input Monitor Playback Switch",
 				.info = snd_ctl_boolean_mono_info,
 				.get = monitor_get,
 				.put = monitor_put,
@@ -798,7 +813,7 @@ static const struct {
 			},
 			{
 				.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-				.name = "Analog Input Monitor Volume",
+				.name = "Analog Input Monitor Playback Volume",
 				.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
 					  SNDRV_CTL_ELEM_ACCESS_TLV_READ,
 				.info = monitor_volume_info,
@@ -815,7 +830,7 @@ static const struct {
 		.controls = {
 			{
 				.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-				.name = "Analog Input Monitor Switch",
+				.name = "Analog Input Monitor Playback Switch",
 				.info = snd_ctl_boolean_mono_info,
 				.get = monitor_get,
 				.put = monitor_put,
@@ -823,7 +838,7 @@ static const struct {
 			},
 			{
 				.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-				.name = "Analog Input Monitor Volume",
+				.name = "Analog Input Monitor Playback Volume",
 				.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
 					  SNDRV_CTL_ELEM_ACCESS_TLV_READ,
 				.info = monitor_volume_info,
@@ -840,7 +855,7 @@ static const struct {
 		.controls = {
 			{
 				.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-				.name = "Analog Input Monitor Switch",
+				.name = "Analog Input Monitor Playback Switch",
 				.index = 1,
 				.info = snd_ctl_boolean_mono_info,
 				.get = monitor_get,
@@ -849,7 +864,7 @@ static const struct {
 			},
 			{
 				.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-				.name = "Analog Input Monitor Volume",
+				.name = "Analog Input Monitor Playback Volume",
 				.index = 1,
 				.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
 					  SNDRV_CTL_ELEM_ACCESS_TLV_READ,
@@ -867,7 +882,7 @@ static const struct {
 		.controls = {
 			{
 				.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-				.name = "Digital Input Monitor Switch",
+				.name = "Digital Input Monitor Playback Switch",
 				.info = snd_ctl_boolean_mono_info,
 				.get = monitor_get,
 				.put = monitor_put,
@@ -875,7 +890,7 @@ static const struct {
 			},
 			{
 				.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-				.name = "Digital Input Monitor Volume",
+				.name = "Digital Input Monitor Playback Volume",
 				.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
 					  SNDRV_CTL_ELEM_ACCESS_TLV_READ,
 				.info = monitor_volume_info,
@@ -954,6 +969,9 @@ static int add_controls(struct oxygen *chip,
 			if (err == 1)
 				continue;
 		}
+		if (!strcmp(template.name, "Stereo Upmixing") &&
+		    chip->model.dac_channels == 2)
+			continue;
 		if (!strcmp(template.name, "Master Playback Volume") &&
 		    chip->model.dac_tlv) {
 			template.tlv.p = chip->model.dac_tlv;

+ 13 - 6
sound/pci/oxygen/oxygen_pcm.c

@@ -271,13 +271,16 @@ static unsigned int oxygen_rate(struct snd_pcm_hw_params *hw_params)
 	}
 }
 
-static unsigned int oxygen_i2s_mclk(struct snd_pcm_hw_params *hw_params)
+unsigned int oxygen_default_i2s_mclk(struct oxygen *chip,
+				     unsigned int channel,
+				     struct snd_pcm_hw_params *hw_params)
 {
 	if (params_rate(hw_params) <= 96000)
 		return OXYGEN_I2S_MCLK_256;
 	else
 		return OXYGEN_I2S_MCLK_128;
 }
+EXPORT_SYMBOL(oxygen_default_i2s_mclk);
 
 static unsigned int oxygen_i2s_bits(struct snd_pcm_hw_params *hw_params)
 {
@@ -354,7 +357,7 @@ static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream,
 			     OXYGEN_REC_FORMAT_A_MASK);
 	oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT,
 			      oxygen_rate(hw_params) |
-			      oxygen_i2s_mclk(hw_params) |
+			      chip->model.get_i2s_mclk(chip, PCM_A, hw_params) |
 			      chip->model.adc_i2s_format |
 			      oxygen_i2s_bits(hw_params),
 			      OXYGEN_I2S_RATE_MASK |
@@ -390,7 +393,8 @@ static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream,
 	if (!is_ac97)
 		oxygen_write16_masked(chip, OXYGEN_I2S_B_FORMAT,
 				      oxygen_rate(hw_params) |
-				      oxygen_i2s_mclk(hw_params) |
+				      chip->model.get_i2s_mclk(chip, PCM_B,
+							       hw_params) |
 				      chip->model.adc_i2s_format |
 				      oxygen_i2s_bits(hw_params),
 				      OXYGEN_I2S_RATE_MASK |
@@ -435,6 +439,7 @@ static int oxygen_spdif_hw_params(struct snd_pcm_substream *substream,
 	if (err < 0)
 		return err;
 
+	mutex_lock(&chip->mutex);
 	spin_lock_irq(&chip->reg_lock);
 	oxygen_clear_bits32(chip, OXYGEN_SPDIF_CONTROL,
 			    OXYGEN_SPDIF_OUT_ENABLE);
@@ -446,6 +451,7 @@ static int oxygen_spdif_hw_params(struct snd_pcm_substream *substream,
 			      OXYGEN_SPDIF_OUT_RATE_MASK);
 	oxygen_update_spdif_source(chip);
 	spin_unlock_irq(&chip->reg_lock);
+	mutex_unlock(&chip->mutex);
 	return 0;
 }
 
@@ -459,6 +465,7 @@ static int oxygen_multich_hw_params(struct snd_pcm_substream *substream,
 	if (err < 0)
 		return err;
 
+	mutex_lock(&chip->mutex);
 	spin_lock_irq(&chip->reg_lock);
 	oxygen_write8_masked(chip, OXYGEN_PLAY_CHANNELS,
 			     oxygen_play_channels(hw_params),
@@ -469,18 +476,18 @@ static int oxygen_multich_hw_params(struct snd_pcm_substream *substream,
 	oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT,
 			      oxygen_rate(hw_params) |
 			      chip->model.dac_i2s_format |
-			      oxygen_i2s_mclk(hw_params) |
+			      chip->model.get_i2s_mclk(chip, PCM_MULTICH,
+						       hw_params) |
 			      oxygen_i2s_bits(hw_params),
 			      OXYGEN_I2S_RATE_MASK |
 			      OXYGEN_I2S_FORMAT_MASK |
 			      OXYGEN_I2S_MCLK_MASK |
 			      OXYGEN_I2S_BITS_MASK);
-	oxygen_update_dac_routing(chip);
 	oxygen_update_spdif_source(chip);
 	spin_unlock_irq(&chip->reg_lock);
 
-	mutex_lock(&chip->mutex);
 	chip->model.set_dac_params(chip, hw_params);
+	oxygen_update_dac_routing(chip);
 	mutex_unlock(&chip->mutex);
 	return 0;
 }

+ 14 - 1091
sound/pci/oxygen/virtuoso.c

@@ -17,145 +17,12 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-/*
- * Xonar D2/D2X
- * ------------
- *
- * CMI8788:
- *
- * SPI 0 -> 1st PCM1796 (front)
- * SPI 1 -> 2nd PCM1796 (surround)
- * SPI 2 -> 3rd PCM1796 (center/LFE)
- * SPI 4 -> 4th PCM1796 (back)
- *
- * GPIO 2 -> M0 of CS5381
- * GPIO 3 -> M1 of CS5381
- * GPIO 5 <- external power present (D2X only)
- * GPIO 7 -> ALT
- * GPIO 8 -> enable output to speakers
- */
-
-/*
- * Xonar D1/DX
- * -----------
- *
- * CMI8788:
- *
- * I²C <-> CS4398 (front)
- *     <-> CS4362A (surround, center/LFE, back)
- *
- * GPI 0 <- external power present (DX only)
- *
- * GPIO 0 -> enable output to speakers
- * GPIO 1 -> enable front panel I/O
- * GPIO 2 -> M0 of CS5361
- * GPIO 3 -> M1 of CS5361
- * GPIO 8 -> route input jack to line-in (0) or mic-in (1)
- *
- * CS4398:
- *
- * AD0 <- 1
- * AD1 <- 1
- *
- * CS4362A:
- *
- * AD0 <- 0
- */
-
-/*
- * Xonar HDAV1.3 (Deluxe)
- * ----------------------
- *
- * CMI8788:
- *
- * I²C <-> PCM1796 (front)
- *
- * GPI 0 <- external power present
- *
- * GPIO 0 -> enable output to speakers
- * GPIO 2 -> M0 of CS5381
- * GPIO 3 -> M1 of CS5381
- * GPIO 8 -> route input jack to line-in (0) or mic-in (1)
- *
- * TXD -> HDMI controller
- * RXD <- HDMI controller
- *
- * PCM1796 front: AD1,0 <- 0,0
- *
- * no daughterboard
- * ----------------
- *
- * GPIO 4 <- 1
- *
- * H6 daughterboard
- * ----------------
- *
- * GPIO 4 <- 0
- * GPIO 5 <- 0
- *
- * I²C <-> PCM1796 (surround)
- *     <-> PCM1796 (center/LFE)
- *     <-> PCM1796 (back)
- *
- * PCM1796 surround:   AD1,0 <- 0,1
- * PCM1796 center/LFE: AD1,0 <- 1,0
- * PCM1796 back:       AD1,0 <- 1,1
- *
- * unknown daughterboard
- * ---------------------
- *
- * GPIO 4 <- 0
- * GPIO 5 <- 1
- *
- * I²C <-> CS4362A (surround, center/LFE, back)
- *
- * CS4362A: AD0 <- 0
- */
-
-/*
- * Xonar Essence ST (Deluxe)/STX
- * -----------------------------
- *
- * CMI8788:
- *
- * I²C <-> PCM1792A
- *
- * GPI 0 <- external power present
- *
- * GPIO 0 -> enable output to speakers
- * GPIO 1 -> route HP to front panel (0) or rear jack (1)
- * GPIO 2 -> M0 of CS5381
- * GPIO 3 -> M1 of CS5381
- * GPIO 7 -> route output to speaker jacks (0) or HP (1)
- * GPIO 8 -> route input jack to line-in (0) or mic-in (1)
- *
- * PCM1792A:
- *
- * AD0 <- 0
- *
- * H6 daughterboard
- * ----------------
- *
- * GPIO 4 <- 0
- * GPIO 5 <- 0
- */
-
 #include <linux/pci.h>
 #include <linux/delay.h>
-#include <linux/mutex.h>
-#include <sound/ac97_codec.h>
-#include <sound/asoundef.h>
-#include <sound/control.h>
 #include <sound/core.h>
 #include <sound/initval.h>
 #include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/tlv.h>
-#include "oxygen.h"
-#include "cm9780.h"
-#include "pcm1796.h"
-#include "cs4398.h"
-#include "cs4362a.h"
+#include "xonar.h"
 
 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_DESCRIPTION("Asus AVx00 driver");
@@ -173,972 +40,28 @@ MODULE_PARM_DESC(id, "ID string");
 module_param_array(enable, bool, NULL, 0444);
 MODULE_PARM_DESC(enable, "enable card");
 
-enum {
-	MODEL_D2,
-	MODEL_D2X,
-	MODEL_D1,
-	MODEL_DX,
-	MODEL_HDAV,	/* without daughterboard */
-	MODEL_HDAV_H6,	/* with H6 daughterboard */
-	MODEL_ST,
-	MODEL_ST_H6,
-	MODEL_STX,
-};
-
 static struct pci_device_id xonar_ids[] __devinitdata = {
-	{ OXYGEN_PCI_SUBID(0x1043, 0x8269), .driver_data = MODEL_D2 },
-	{ OXYGEN_PCI_SUBID(0x1043, 0x8275), .driver_data = MODEL_DX },
-	{ OXYGEN_PCI_SUBID(0x1043, 0x82b7), .driver_data = MODEL_D2X },
-	{ OXYGEN_PCI_SUBID(0x1043, 0x8314), .driver_data = MODEL_HDAV },
-	{ OXYGEN_PCI_SUBID(0x1043, 0x8327), .driver_data = MODEL_DX },
-	{ OXYGEN_PCI_SUBID(0x1043, 0x834f), .driver_data = MODEL_D1 },
-	{ OXYGEN_PCI_SUBID(0x1043, 0x835c), .driver_data = MODEL_STX },
-	{ OXYGEN_PCI_SUBID(0x1043, 0x835d), .driver_data = MODEL_ST },
+	{ OXYGEN_PCI_SUBID(0x1043, 0x8269) },
+	{ OXYGEN_PCI_SUBID(0x1043, 0x8275) },
+	{ OXYGEN_PCI_SUBID(0x1043, 0x82b7) },
+	{ OXYGEN_PCI_SUBID(0x1043, 0x8314) },
+	{ OXYGEN_PCI_SUBID(0x1043, 0x8327) },
+	{ OXYGEN_PCI_SUBID(0x1043, 0x834f) },
+	{ OXYGEN_PCI_SUBID(0x1043, 0x835c) },
+	{ OXYGEN_PCI_SUBID(0x1043, 0x835d) },
 	{ OXYGEN_PCI_SUBID_BROKEN_EEPROM },
 	{ }
 };
 MODULE_DEVICE_TABLE(pci, xonar_ids);
 
-
-#define GPIO_CS53x1_M_MASK	0x000c
-#define GPIO_CS53x1_M_SINGLE	0x0000
-#define GPIO_CS53x1_M_DOUBLE	0x0004
-#define GPIO_CS53x1_M_QUAD	0x0008
-
-#define GPIO_D2X_EXT_POWER	0x0020
-#define GPIO_D2_ALT		0x0080
-#define GPIO_D2_OUTPUT_ENABLE	0x0100
-
-#define GPI_DX_EXT_POWER	0x01
-#define GPIO_DX_OUTPUT_ENABLE	0x0001
-#define GPIO_DX_FRONT_PANEL	0x0002
-#define GPIO_DX_INPUT_ROUTE	0x0100
-
-#define GPIO_DB_MASK		0x0030
-#define GPIO_DB_H6		0x0000
-#define GPIO_DB_XX		0x0020
-
-#define GPIO_ST_HP_REAR		0x0002
-#define GPIO_ST_HP		0x0080
-
-#define I2C_DEVICE_PCM1796(i)	(0x98 + ((i) << 1))	/* 10011, ADx=i, /W=0 */
-#define I2C_DEVICE_CS4398	0x9e	/* 10011, AD1=1, AD0=1, /W=0 */
-#define I2C_DEVICE_CS4362A	0x30	/* 001100, AD0=0, /W=0 */
-
-struct xonar_data {
-	unsigned int anti_pop_delay;
-	unsigned int dacs;
-	u16 output_enable_bit;
-	u8 ext_power_reg;
-	u8 ext_power_int_reg;
-	u8 ext_power_bit;
-	u8 has_power;
-	u8 pcm1796_oversampling;
-	u8 cs4398_fm;
-	u8 cs4362a_fm;
-	u8 hdmi_params[5];
-};
-
-static void xonar_gpio_changed(struct oxygen *chip);
-
-static inline void pcm1796_write_spi(struct oxygen *chip, unsigned int codec,
-				     u8 reg, u8 value)
-{
-	/* maps ALSA channel pair number to SPI output */
-	static const u8 codec_map[4] = {
-		0, 1, 2, 4
-	};
-	oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER  |
-			 OXYGEN_SPI_DATA_LENGTH_2 |
-			 OXYGEN_SPI_CLOCK_160 |
-			 (codec_map[codec] << OXYGEN_SPI_CODEC_SHIFT) |
-			 OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
-			 (reg << 8) | value);
-}
-
-static inline void pcm1796_write_i2c(struct oxygen *chip, unsigned int codec,
-				     u8 reg, u8 value)
-{
-	oxygen_write_i2c(chip, I2C_DEVICE_PCM1796(codec), reg, value);
-}
-
-static void pcm1796_write(struct oxygen *chip, unsigned int codec,
-			  u8 reg, u8 value)
-{
-	if ((chip->model.function_flags & OXYGEN_FUNCTION_2WIRE_SPI_MASK) ==
-	    OXYGEN_FUNCTION_SPI)
-		pcm1796_write_spi(chip, codec, reg, value);
-	else
-		pcm1796_write_i2c(chip, codec, reg, value);
-}
-
-static void cs4398_write(struct oxygen *chip, u8 reg, u8 value)
-{
-	oxygen_write_i2c(chip, I2C_DEVICE_CS4398, reg, value);
-}
-
-static void cs4362a_write(struct oxygen *chip, u8 reg, u8 value)
-{
-	oxygen_write_i2c(chip, I2C_DEVICE_CS4362A, reg, value);
-}
-
-static void hdmi_write_command(struct oxygen *chip, u8 command,
-			       unsigned int count, const u8 *params)
-{
-	unsigned int i;
-	u8 checksum;
-
-	oxygen_write_uart(chip, 0xfb);
-	oxygen_write_uart(chip, 0xef);
-	oxygen_write_uart(chip, command);
-	oxygen_write_uart(chip, count);
-	for (i = 0; i < count; ++i)
-		oxygen_write_uart(chip, params[i]);
-	checksum = 0xfb + 0xef + command + count;
-	for (i = 0; i < count; ++i)
-		checksum += params[i];
-	oxygen_write_uart(chip, checksum);
-}
-
-static void xonar_enable_output(struct oxygen *chip)
-{
-	struct xonar_data *data = chip->model_data;
-
-	msleep(data->anti_pop_delay);
-	oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
-}
-
-static void xonar_common_init(struct oxygen *chip)
-{
-	struct xonar_data *data = chip->model_data;
-
-	if (data->ext_power_reg) {
-		oxygen_set_bits8(chip, data->ext_power_int_reg,
-				 data->ext_power_bit);
-		chip->interrupt_mask |= OXYGEN_INT_GPIO;
-		chip->model.gpio_changed = xonar_gpio_changed;
-		data->has_power = !!(oxygen_read8(chip, data->ext_power_reg)
-				     & data->ext_power_bit);
-	}
-	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
-			  GPIO_CS53x1_M_MASK | data->output_enable_bit);
-	oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
-			      GPIO_CS53x1_M_SINGLE, GPIO_CS53x1_M_MASK);
-	oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC);
-	xonar_enable_output(chip);
-}
-
-static void update_pcm1796_volume(struct oxygen *chip)
-{
-	struct xonar_data *data = chip->model_data;
-	unsigned int i;
-
-	for (i = 0; i < data->dacs; ++i) {
-		pcm1796_write(chip, i, 16, chip->dac_volume[i * 2]);
-		pcm1796_write(chip, i, 17, chip->dac_volume[i * 2 + 1]);
-	}
-}
-
-static void update_pcm1796_mute(struct oxygen *chip)
-{
-	struct xonar_data *data = chip->model_data;
-	unsigned int i;
-	u8 value;
-
-	value = PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD;
-	if (chip->dac_mute)
-		value |= PCM1796_MUTE;
-	for (i = 0; i < data->dacs; ++i)
-		pcm1796_write(chip, i, 18, value);
-}
-
-static void pcm1796_init(struct oxygen *chip)
-{
-	struct xonar_data *data = chip->model_data;
-	unsigned int i;
-
-	for (i = 0; i < data->dacs; ++i) {
-		pcm1796_write(chip, i, 19, PCM1796_FLT_SHARP | PCM1796_ATS_1);
-		pcm1796_write(chip, i, 20, data->pcm1796_oversampling);
-		pcm1796_write(chip, i, 21, 0);
-	}
-	update_pcm1796_mute(chip); /* set ATLD before ATL/ATR */
-	update_pcm1796_volume(chip);
-}
-
-static void xonar_d2_init(struct oxygen *chip)
-{
-	struct xonar_data *data = chip->model_data;
-
-	data->anti_pop_delay = 300;
-	data->dacs = 4;
-	data->output_enable_bit = GPIO_D2_OUTPUT_ENABLE;
-	data->pcm1796_oversampling = PCM1796_OS_64;
-
-	pcm1796_init(chip);
-
-	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2_ALT);
-	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_D2_ALT);
-
-	xonar_common_init(chip);
-
-	snd_component_add(chip->card, "PCM1796");
-	snd_component_add(chip->card, "CS5381");
-}
-
-static void xonar_d2x_init(struct oxygen *chip)
-{
-	struct xonar_data *data = chip->model_data;
-
-	data->ext_power_reg = OXYGEN_GPIO_DATA;
-	data->ext_power_int_reg = OXYGEN_GPIO_INTERRUPT_MASK;
-	data->ext_power_bit = GPIO_D2X_EXT_POWER;
-	oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2X_EXT_POWER);
-
-	xonar_d2_init(chip);
-}
-
-static void update_cs4362a_volumes(struct oxygen *chip)
-{
-	u8 mute;
-
-	mute = chip->dac_mute ? CS4362A_MUTE : 0;
-	cs4362a_write(chip, 7, (127 - chip->dac_volume[2]) | mute);
-	cs4362a_write(chip, 8, (127 - chip->dac_volume[3]) | mute);
-	cs4362a_write(chip, 10, (127 - chip->dac_volume[4]) | mute);
-	cs4362a_write(chip, 11, (127 - chip->dac_volume[5]) | mute);
-	cs4362a_write(chip, 13, (127 - chip->dac_volume[6]) | mute);
-	cs4362a_write(chip, 14, (127 - chip->dac_volume[7]) | mute);
-}
-
-static void update_cs43xx_volume(struct oxygen *chip)
-{
-	cs4398_write(chip, 5, (127 - chip->dac_volume[0]) * 2);
-	cs4398_write(chip, 6, (127 - chip->dac_volume[1]) * 2);
-	update_cs4362a_volumes(chip);
-}
-
-static void update_cs43xx_mute(struct oxygen *chip)
-{
-	u8 reg;
-
-	reg = CS4398_MUTEP_LOW | CS4398_PAMUTE;
-	if (chip->dac_mute)
-		reg |= CS4398_MUTE_B | CS4398_MUTE_A;
-	cs4398_write(chip, 4, reg);
-	update_cs4362a_volumes(chip);
-}
-
-static void cs43xx_init(struct oxygen *chip)
-{
-	struct xonar_data *data = chip->model_data;
-
-	/* set CPEN (control port mode) and power down */
-	cs4398_write(chip, 8, CS4398_CPEN | CS4398_PDN);
-	cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN);
-	/* configure */
-	cs4398_write(chip, 2, data->cs4398_fm);
-	cs4398_write(chip, 3, CS4398_ATAPI_B_R | CS4398_ATAPI_A_L);
-	cs4398_write(chip, 7, CS4398_RMP_DN | CS4398_RMP_UP |
-		     CS4398_ZERO_CROSS | CS4398_SOFT_RAMP);
-	cs4362a_write(chip, 0x02, CS4362A_DIF_LJUST);
-	cs4362a_write(chip, 0x03, CS4362A_MUTEC_6 | CS4362A_AMUTE |
-		      CS4362A_RMP_UP | CS4362A_ZERO_CROSS | CS4362A_SOFT_RAMP);
-	cs4362a_write(chip, 0x04, CS4362A_RMP_DN | CS4362A_DEM_NONE);
-	cs4362a_write(chip, 0x05, 0);
-	cs4362a_write(chip, 0x06, data->cs4362a_fm);
-	cs4362a_write(chip, 0x09, data->cs4362a_fm);
-	cs4362a_write(chip, 0x0c, data->cs4362a_fm);
-	update_cs43xx_volume(chip);
-	update_cs43xx_mute(chip);
-	/* clear power down */
-	cs4398_write(chip, 8, CS4398_CPEN);
-	cs4362a_write(chip, 0x01, CS4362A_CPEN);
-}
-
-static void xonar_d1_init(struct oxygen *chip)
-{
-	struct xonar_data *data = chip->model_data;
-
-	data->anti_pop_delay = 800;
-	data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE;
-	data->cs4398_fm = CS4398_FM_SINGLE | CS4398_DEM_NONE | CS4398_DIF_LJUST;
-	data->cs4362a_fm = CS4362A_FM_SINGLE |
-		CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L;
-
-	oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
-		       OXYGEN_2WIRE_LENGTH_8 |
-		       OXYGEN_2WIRE_INTERRUPT_MASK |
-		       OXYGEN_2WIRE_SPEED_FAST);
-
-	cs43xx_init(chip);
-
-	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
-			  GPIO_DX_FRONT_PANEL | GPIO_DX_INPUT_ROUTE);
-	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
-			    GPIO_DX_FRONT_PANEL | GPIO_DX_INPUT_ROUTE);
-
-	xonar_common_init(chip);
-
-	snd_component_add(chip->card, "CS4398");
-	snd_component_add(chip->card, "CS4362A");
-	snd_component_add(chip->card, "CS5361");
-}
-
-static void xonar_dx_init(struct oxygen *chip)
-{
-	struct xonar_data *data = chip->model_data;
-
-	data->ext_power_reg = OXYGEN_GPI_DATA;
-	data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
-	data->ext_power_bit = GPI_DX_EXT_POWER;
-
-	xonar_d1_init(chip);
-}
-
-static void xonar_hdav_init(struct oxygen *chip)
-{
-	struct xonar_data *data = chip->model_data;
-	u8 param;
-
-	oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
-		       OXYGEN_2WIRE_LENGTH_8 |
-		       OXYGEN_2WIRE_INTERRUPT_MASK |
-		       OXYGEN_2WIRE_SPEED_FAST);
-
-	data->anti_pop_delay = 100;
-	data->dacs = chip->model.private_data == MODEL_HDAV_H6 ? 4 : 1;
-	data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE;
-	data->ext_power_reg = OXYGEN_GPI_DATA;
-	data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
-	data->ext_power_bit = GPI_DX_EXT_POWER;
-	data->pcm1796_oversampling = PCM1796_OS_64;
-
-	pcm1796_init(chip);
-
-	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DX_INPUT_ROUTE);
-	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_DX_INPUT_ROUTE);
-
-	oxygen_reset_uart(chip);
-	param = 0;
-	hdmi_write_command(chip, 0x61, 1, &param);
-	param = 1;
-	hdmi_write_command(chip, 0x74, 1, &param);
-	data->hdmi_params[1] = IEC958_AES3_CON_FS_48000;
-	data->hdmi_params[4] = 1;
-	hdmi_write_command(chip, 0x54, 5, data->hdmi_params);
-
-	xonar_common_init(chip);
-
-	snd_component_add(chip->card, "PCM1796");
-	snd_component_add(chip->card, "CS5381");
-}
-
-static void xonar_st_init(struct oxygen *chip)
-{
-	struct xonar_data *data = chip->model_data;
-
-	oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
-		       OXYGEN_2WIRE_LENGTH_8 |
-		       OXYGEN_2WIRE_INTERRUPT_MASK |
-		       OXYGEN_2WIRE_SPEED_FAST);
-
-	if (chip->model.private_data == MODEL_ST_H6)
-		chip->model.dac_channels = 8;
-	data->anti_pop_delay = 100;
-	data->dacs = chip->model.private_data == MODEL_ST_H6 ? 4 : 1;
-	data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE;
-	data->pcm1796_oversampling = PCM1796_OS_64;
-
-	pcm1796_init(chip);
-
-	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
-			  GPIO_DX_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP);
-	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
-			    GPIO_DX_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP);
-
-	xonar_common_init(chip);
-
-	snd_component_add(chip->card, "PCM1792A");
-	snd_component_add(chip->card, "CS5381");
-}
-
-static void xonar_stx_init(struct oxygen *chip)
-{
-	struct xonar_data *data = chip->model_data;
-
-	data->ext_power_reg = OXYGEN_GPI_DATA;
-	data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
-	data->ext_power_bit = GPI_DX_EXT_POWER;
-
-	xonar_st_init(chip);
-}
-
-static void xonar_disable_output(struct oxygen *chip)
-{
-	struct xonar_data *data = chip->model_data;
-
-	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
-}
-
-static void xonar_d2_cleanup(struct oxygen *chip)
-{
-	xonar_disable_output(chip);
-}
-
-static void xonar_d1_cleanup(struct oxygen *chip)
-{
-	xonar_disable_output(chip);
-	cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN);
-	oxygen_clear_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC);
-}
-
-static void xonar_hdav_cleanup(struct oxygen *chip)
-{
-	u8 param = 0;
-
-	hdmi_write_command(chip, 0x74, 1, &param);
-	xonar_disable_output(chip);
-}
-
-static void xonar_st_cleanup(struct oxygen *chip)
-{
-	xonar_disable_output(chip);
-}
-
-static void xonar_d2_suspend(struct oxygen *chip)
-{
-	xonar_d2_cleanup(chip);
-}
-
-static void xonar_d1_suspend(struct oxygen *chip)
-{
-	xonar_d1_cleanup(chip);
-}
-
-static void xonar_hdav_suspend(struct oxygen *chip)
-{
-	xonar_hdav_cleanup(chip);
-	msleep(2);
-}
-
-static void xonar_st_suspend(struct oxygen *chip)
-{
-	xonar_st_cleanup(chip);
-}
-
-static void xonar_d2_resume(struct oxygen *chip)
-{
-	pcm1796_init(chip);
-	xonar_enable_output(chip);
-}
-
-static void xonar_d1_resume(struct oxygen *chip)
-{
-	oxygen_set_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC);
-	msleep(1);
-	cs43xx_init(chip);
-	xonar_enable_output(chip);
-}
-
-static void xonar_hdav_resume(struct oxygen *chip)
-{
-	struct xonar_data *data = chip->model_data;
-	u8 param;
-
-	oxygen_reset_uart(chip);
-	param = 0;
-	hdmi_write_command(chip, 0x61, 1, &param);
-	param = 1;
-	hdmi_write_command(chip, 0x74, 1, &param);
-	hdmi_write_command(chip, 0x54, 5, data->hdmi_params);
-	pcm1796_init(chip);
-	xonar_enable_output(chip);
-}
-
-static void xonar_st_resume(struct oxygen *chip)
-{
-	pcm1796_init(chip);
-	xonar_enable_output(chip);
-}
-
-static void xonar_hdav_pcm_hardware_filter(unsigned int channel,
-					   struct snd_pcm_hardware *hardware)
-{
-	if (channel == PCM_MULTICH) {
-		hardware->rates = SNDRV_PCM_RATE_44100 |
-				  SNDRV_PCM_RATE_48000 |
-				  SNDRV_PCM_RATE_96000 |
-				  SNDRV_PCM_RATE_192000;
-		hardware->rate_min = 44100;
-	}
-}
-
-static void set_pcm1796_params(struct oxygen *chip,
-			       struct snd_pcm_hw_params *params)
-{
-	struct xonar_data *data = chip->model_data;
-	unsigned int i;
-
-	data->pcm1796_oversampling =
-		params_rate(params) >= 96000 ? PCM1796_OS_32 : PCM1796_OS_64;
-	for (i = 0; i < data->dacs; ++i)
-		pcm1796_write(chip, i, 20, data->pcm1796_oversampling);
-}
-
-static void set_cs53x1_params(struct oxygen *chip,
-			      struct snd_pcm_hw_params *params)
-{
-	unsigned int value;
-
-	if (params_rate(params) <= 54000)
-		value = GPIO_CS53x1_M_SINGLE;
-	else if (params_rate(params) <= 108000)
-		value = GPIO_CS53x1_M_DOUBLE;
-	else
-		value = GPIO_CS53x1_M_QUAD;
-	oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
-			      value, GPIO_CS53x1_M_MASK);
-}
-
-static void set_cs43xx_params(struct oxygen *chip,
-			      struct snd_pcm_hw_params *params)
-{
-	struct xonar_data *data = chip->model_data;
-
-	data->cs4398_fm = CS4398_DEM_NONE | CS4398_DIF_LJUST;
-	data->cs4362a_fm = CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L;
-	if (params_rate(params) <= 50000) {
-		data->cs4398_fm |= CS4398_FM_SINGLE;
-		data->cs4362a_fm |= CS4362A_FM_SINGLE;
-	} else if (params_rate(params) <= 100000) {
-		data->cs4398_fm |= CS4398_FM_DOUBLE;
-		data->cs4362a_fm |= CS4362A_FM_DOUBLE;
-	} else {
-		data->cs4398_fm |= CS4398_FM_QUAD;
-		data->cs4362a_fm |= CS4362A_FM_QUAD;
-	}
-	cs4398_write(chip, 2, data->cs4398_fm);
-	cs4362a_write(chip, 0x06, data->cs4362a_fm);
-	cs4362a_write(chip, 0x09, data->cs4362a_fm);
-	cs4362a_write(chip, 0x0c, data->cs4362a_fm);
-}
-
-static void set_hdmi_params(struct oxygen *chip,
-			    struct snd_pcm_hw_params *params)
-{
-	struct xonar_data *data = chip->model_data;
-
-	data->hdmi_params[0] = 0; /* 1 = non-audio */
-	switch (params_rate(params)) {
-	case 44100:
-		data->hdmi_params[1] = IEC958_AES3_CON_FS_44100;
-		break;
-	case 48000:
-		data->hdmi_params[1] = IEC958_AES3_CON_FS_48000;
-		break;
-	default: /* 96000 */
-		data->hdmi_params[1] = IEC958_AES3_CON_FS_96000;
-		break;
-	case 192000:
-		data->hdmi_params[1] = IEC958_AES3_CON_FS_192000;
-		break;
-	}
-	data->hdmi_params[2] = params_channels(params) / 2 - 1;
-	if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE)
-		data->hdmi_params[3] = 0;
-	else
-		data->hdmi_params[3] = 0xc0;
-	data->hdmi_params[4] = 1; /* ? */
-	hdmi_write_command(chip, 0x54, 5, data->hdmi_params);
-}
-
-static void set_hdav_params(struct oxygen *chip,
-			    struct snd_pcm_hw_params *params)
-{
-	set_pcm1796_params(chip, params);
-	set_hdmi_params(chip, params);
-}
-
-static void xonar_gpio_changed(struct oxygen *chip)
-{
-	struct xonar_data *data = chip->model_data;
-	u8 has_power;
-
-	has_power = !!(oxygen_read8(chip, data->ext_power_reg)
-		       & data->ext_power_bit);
-	if (has_power != data->has_power) {
-		data->has_power = has_power;
-		if (has_power) {
-			snd_printk(KERN_NOTICE "power restored\n");
-		} else {
-			snd_printk(KERN_CRIT
-				   "Hey! Don't unplug the power cable!\n");
-			/* TODO: stop PCMs */
-		}
-	}
-}
-
-static void xonar_hdav_uart_input(struct oxygen *chip)
-{
-	if (chip->uart_input_count >= 2 &&
-	    chip->uart_input[chip->uart_input_count - 2] == 'O' &&
-	    chip->uart_input[chip->uart_input_count - 1] == 'K') {
-		printk(KERN_DEBUG "message from Xonar HDAV HDMI chip received:\n");
-		print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
-				     chip->uart_input, chip->uart_input_count);
-		chip->uart_input_count = 0;
-	}
-}
-
-static int gpio_bit_switch_get(struct snd_kcontrol *ctl,
-			       struct snd_ctl_elem_value *value)
-{
-	struct oxygen *chip = ctl->private_data;
-	u16 bit = ctl->private_value;
-
-	value->value.integer.value[0] =
-		!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & bit);
-	return 0;
-}
-
-static int gpio_bit_switch_put(struct snd_kcontrol *ctl,
-			       struct snd_ctl_elem_value *value)
-{
-	struct oxygen *chip = ctl->private_data;
-	u16 bit = ctl->private_value;
-	u16 old_bits, new_bits;
-	int changed;
-
-	spin_lock_irq(&chip->reg_lock);
-	old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA);
-	if (value->value.integer.value[0])
-		new_bits = old_bits | bit;
-	else
-		new_bits = old_bits & ~bit;
-	changed = new_bits != old_bits;
-	if (changed)
-		oxygen_write16(chip, OXYGEN_GPIO_DATA, new_bits);
-	spin_unlock_irq(&chip->reg_lock);
-	return changed;
-}
-
-static const struct snd_kcontrol_new alt_switch = {
-	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-	.name = "Analog Loopback Switch",
-	.info = snd_ctl_boolean_mono_info,
-	.get = gpio_bit_switch_get,
-	.put = gpio_bit_switch_put,
-	.private_value = GPIO_D2_ALT,
-};
-
-static const struct snd_kcontrol_new front_panel_switch = {
-	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-	.name = "Front Panel Switch",
-	.info = snd_ctl_boolean_mono_info,
-	.get = gpio_bit_switch_get,
-	.put = gpio_bit_switch_put,
-	.private_value = GPIO_DX_FRONT_PANEL,
-};
-
-static int st_output_switch_info(struct snd_kcontrol *ctl,
-				 struct snd_ctl_elem_info *info)
-{
-	static const char *const names[3] = {
-		"Speakers", "Headphones", "FP Headphones"
-	};
-
-	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-	info->count = 1;
-	info->value.enumerated.items = 3;
-	if (info->value.enumerated.item >= 3)
-		info->value.enumerated.item = 2;
-	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
-	return 0;
-}
-
-static int st_output_switch_get(struct snd_kcontrol *ctl,
-				struct snd_ctl_elem_value *value)
-{
-	struct oxygen *chip = ctl->private_data;
-	u16 gpio;
-
-	gpio = oxygen_read16(chip, OXYGEN_GPIO_DATA);
-	if (!(gpio & GPIO_ST_HP))
-		value->value.enumerated.item[0] = 0;
-	else if (gpio & GPIO_ST_HP_REAR)
-		value->value.enumerated.item[0] = 1;
-	else
-		value->value.enumerated.item[0] = 2;
-	return 0;
-}
-
-
-static int st_output_switch_put(struct snd_kcontrol *ctl,
-				struct snd_ctl_elem_value *value)
-{
-	struct oxygen *chip = ctl->private_data;
-	u16 gpio_old, gpio;
-
-	mutex_lock(&chip->mutex);
-	gpio_old = oxygen_read16(chip, OXYGEN_GPIO_DATA);
-	gpio = gpio_old;
-	switch (value->value.enumerated.item[0]) {
-	case 0:
-		gpio &= ~(GPIO_ST_HP | GPIO_ST_HP_REAR);
-		break;
-	case 1:
-		gpio |= GPIO_ST_HP | GPIO_ST_HP_REAR;
-		break;
-	case 2:
-		gpio = (gpio | GPIO_ST_HP) & ~GPIO_ST_HP_REAR;
-		break;
-	}
-	oxygen_write16(chip, OXYGEN_GPIO_DATA, gpio);
-	mutex_unlock(&chip->mutex);
-	return gpio != gpio_old;
-}
-
-static const struct snd_kcontrol_new st_output_switch = {
-	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-	.name = "Analog Output",
-	.info = st_output_switch_info,
-	.get = st_output_switch_get,
-	.put = st_output_switch_put,
-};
-
-static void xonar_line_mic_ac97_switch(struct oxygen *chip,
-				       unsigned int reg, unsigned int mute)
-{
-	if (reg == AC97_LINE) {
-		spin_lock_irq(&chip->reg_lock);
-		oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
-				      mute ? GPIO_DX_INPUT_ROUTE : 0,
-				      GPIO_DX_INPUT_ROUTE);
-		spin_unlock_irq(&chip->reg_lock);
-	}
-}
-
-static const DECLARE_TLV_DB_SCALE(pcm1796_db_scale, -6000, 50, 0);
-static const DECLARE_TLV_DB_SCALE(cs4362a_db_scale, -6000, 100, 0);
-
-static int xonar_d2_control_filter(struct snd_kcontrol_new *template)
-{
-	if (!strncmp(template->name, "CD Capture ", 11))
-		/* CD in is actually connected to the video in pin */
-		template->private_value ^= AC97_CD ^ AC97_VIDEO;
-	return 0;
-}
-
-static int xonar_d1_control_filter(struct snd_kcontrol_new *template)
-{
-	if (!strncmp(template->name, "CD Capture ", 11))
-		return 1; /* no CD input */
-	return 0;
-}
-
-static int xonar_st_control_filter(struct snd_kcontrol_new *template)
-{
-	if (!strncmp(template->name, "CD Capture ", 11))
-		return 1; /* no CD input */
-	if (!strcmp(template->name, "Stereo Upmixing"))
-		return 1; /* stereo only - we don't need upmixing */
-	return 0;
-}
-
-static int xonar_d2_mixer_init(struct oxygen *chip)
-{
-	return snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip));
-}
-
-static int xonar_d1_mixer_init(struct oxygen *chip)
-{
-	return snd_ctl_add(chip->card, snd_ctl_new1(&front_panel_switch, chip));
-}
-
-static int xonar_st_mixer_init(struct oxygen *chip)
-{
-	return snd_ctl_add(chip->card, snd_ctl_new1(&st_output_switch, chip));
-}
-
-static const struct oxygen_model model_xonar_d2 = {
-	.longname = "Asus Virtuoso 200",
-	.chip = "AV200",
-	.init = xonar_d2_init,
-	.control_filter = xonar_d2_control_filter,
-	.mixer_init = xonar_d2_mixer_init,
-	.cleanup = xonar_d2_cleanup,
-	.suspend = xonar_d2_suspend,
-	.resume = xonar_d2_resume,
-	.set_dac_params = set_pcm1796_params,
-	.set_adc_params = set_cs53x1_params,
-	.update_dac_volume = update_pcm1796_volume,
-	.update_dac_mute = update_pcm1796_mute,
-	.dac_tlv = pcm1796_db_scale,
-	.model_data_size = sizeof(struct xonar_data),
-	.device_config = PLAYBACK_0_TO_I2S |
-			 PLAYBACK_1_TO_SPDIF |
-			 CAPTURE_0_FROM_I2S_2 |
-			 CAPTURE_1_FROM_SPDIF |
-			 MIDI_OUTPUT |
-			 MIDI_INPUT,
-	.dac_channels = 8,
-	.dac_volume_min = 255 - 2*60,
-	.dac_volume_max = 255,
-	.misc_flags = OXYGEN_MISC_MIDI,
-	.function_flags = OXYGEN_FUNCTION_SPI |
-			  OXYGEN_FUNCTION_ENABLE_SPI_4_5,
-	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
-	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
-};
-
-static const struct oxygen_model model_xonar_d1 = {
-	.longname = "Asus Virtuoso 100",
-	.chip = "AV200",
-	.init = xonar_d1_init,
-	.control_filter = xonar_d1_control_filter,
-	.mixer_init = xonar_d1_mixer_init,
-	.cleanup = xonar_d1_cleanup,
-	.suspend = xonar_d1_suspend,
-	.resume = xonar_d1_resume,
-	.set_dac_params = set_cs43xx_params,
-	.set_adc_params = set_cs53x1_params,
-	.update_dac_volume = update_cs43xx_volume,
-	.update_dac_mute = update_cs43xx_mute,
-	.ac97_switch = xonar_line_mic_ac97_switch,
-	.dac_tlv = cs4362a_db_scale,
-	.model_data_size = sizeof(struct xonar_data),
-	.device_config = PLAYBACK_0_TO_I2S |
-			 PLAYBACK_1_TO_SPDIF |
-			 CAPTURE_0_FROM_I2S_2,
-	.dac_channels = 8,
-	.dac_volume_min = 127 - 60,
-	.dac_volume_max = 127,
-	.function_flags = OXYGEN_FUNCTION_2WIRE,
-	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
-	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
-};
-
-static const struct oxygen_model model_xonar_hdav = {
-	.longname = "Asus Virtuoso 200",
-	.chip = "AV200",
-	.init = xonar_hdav_init,
-	.cleanup = xonar_hdav_cleanup,
-	.suspend = xonar_hdav_suspend,
-	.resume = xonar_hdav_resume,
-	.pcm_hardware_filter = xonar_hdav_pcm_hardware_filter,
-	.set_dac_params = set_hdav_params,
-	.set_adc_params = set_cs53x1_params,
-	.update_dac_volume = update_pcm1796_volume,
-	.update_dac_mute = update_pcm1796_mute,
-	.uart_input = xonar_hdav_uart_input,
-	.ac97_switch = xonar_line_mic_ac97_switch,
-	.dac_tlv = pcm1796_db_scale,
-	.model_data_size = sizeof(struct xonar_data),
-	.device_config = PLAYBACK_0_TO_I2S |
-			 PLAYBACK_1_TO_SPDIF |
-			 CAPTURE_0_FROM_I2S_2 |
-			 CAPTURE_1_FROM_SPDIF,
-	.dac_channels = 8,
-	.dac_volume_min = 255 - 2*60,
-	.dac_volume_max = 255,
-	.misc_flags = OXYGEN_MISC_MIDI,
-	.function_flags = OXYGEN_FUNCTION_2WIRE,
-	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
-	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
-};
-
-static const struct oxygen_model model_xonar_st = {
-	.longname = "Asus Virtuoso 100",
-	.chip = "AV200",
-	.init = xonar_st_init,
-	.control_filter = xonar_st_control_filter,
-	.mixer_init = xonar_st_mixer_init,
-	.cleanup = xonar_st_cleanup,
-	.suspend = xonar_st_suspend,
-	.resume = xonar_st_resume,
-	.set_dac_params = set_pcm1796_params,
-	.set_adc_params = set_cs53x1_params,
-	.update_dac_volume = update_pcm1796_volume,
-	.update_dac_mute = update_pcm1796_mute,
-	.ac97_switch = xonar_line_mic_ac97_switch,
-	.dac_tlv = pcm1796_db_scale,
-	.model_data_size = sizeof(struct xonar_data),
-	.device_config = PLAYBACK_0_TO_I2S |
-			 PLAYBACK_1_TO_SPDIF |
-			 CAPTURE_0_FROM_I2S_2,
-	.dac_channels = 2,
-	.dac_volume_min = 255 - 2*60,
-	.dac_volume_max = 255,
-	.function_flags = OXYGEN_FUNCTION_2WIRE,
-	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
-	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
-};
-
 static int __devinit get_xonar_model(struct oxygen *chip,
 				     const struct pci_device_id *id)
 {
-	static const struct oxygen_model *const models[] = {
-		[MODEL_D1]	= &model_xonar_d1,
-		[MODEL_DX]	= &model_xonar_d1,
-		[MODEL_D2]	= &model_xonar_d2,
-		[MODEL_D2X]	= &model_xonar_d2,
-		[MODEL_HDAV]	= &model_xonar_hdav,
-		[MODEL_ST]	= &model_xonar_st,
-		[MODEL_STX]	= &model_xonar_st,
-	};
-	static const char *const names[] = {
-		[MODEL_D1]	= "Xonar D1",
-		[MODEL_DX]	= "Xonar DX",
-		[MODEL_D2]	= "Xonar D2",
-		[MODEL_D2X]	= "Xonar D2X",
-		[MODEL_HDAV]	= "Xonar HDAV1.3",
-		[MODEL_HDAV_H6]	= "Xonar HDAV1.3+H6",
-		[MODEL_ST]	= "Xonar Essence ST",
-		[MODEL_ST_H6]	= "Xonar Essence ST+H6",
-		[MODEL_STX]	= "Xonar Essence STX",
-	};
-	unsigned int model = id->driver_data;
-
-	if (model >= ARRAY_SIZE(models) || !models[model])
-		return -EINVAL;
-	chip->model = *models[model];
-
-	switch (model) {
-	case MODEL_D2X:
-		chip->model.init = xonar_d2x_init;
-		break;
-	case MODEL_DX:
-		chip->model.init = xonar_dx_init;
-		break;
-	case MODEL_HDAV:
-		oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK);
-		switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DB_MASK) {
-		case GPIO_DB_H6:
-			model = MODEL_HDAV_H6;
-			break;
-		case GPIO_DB_XX:
-			snd_printk(KERN_ERR "unknown daughterboard\n");
-			return -ENODEV;
-		}
-		break;
-	case MODEL_ST:
-		oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK);
-		switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DB_MASK) {
-		case GPIO_DB_H6:
-			model = MODEL_ST_H6;
-			break;
-		}
-		break;
-	case MODEL_STX:
-		chip->model.init = xonar_stx_init;
-		oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK);
-		break;
-	}
-
-	chip->model.shortname = names[model];
-	chip->model.private_data = model;
-	return 0;
+	if (get_xonar_pcm179x_model(chip, id) >= 0)
+		return 0;
+	if (get_xonar_cs43xx_model(chip, id) >= 0)
+		return 0;
+	return -EINVAL;
 }
 
 static int __devinit xonar_probe(struct pci_dev *pci,

+ 50 - 0
sound/pci/oxygen/xonar.h

@@ -0,0 +1,50 @@
+#ifndef XONAR_H_INCLUDED
+#define XONAR_H_INCLUDED
+
+#include "oxygen.h"
+
+struct xonar_generic {
+	unsigned int anti_pop_delay;
+	u16 output_enable_bit;
+	u8 ext_power_reg;
+	u8 ext_power_int_reg;
+	u8 ext_power_bit;
+	u8 has_power;
+};
+
+struct xonar_hdmi {
+	u8 params[5];
+};
+
+/* generic helper functions */
+
+void xonar_enable_output(struct oxygen *chip);
+void xonar_disable_output(struct oxygen *chip);
+void xonar_init_ext_power(struct oxygen *chip);
+void xonar_init_cs53x1(struct oxygen *chip);
+void xonar_set_cs53x1_params(struct oxygen *chip,
+			     struct snd_pcm_hw_params *params);
+int xonar_gpio_bit_switch_get(struct snd_kcontrol *ctl,
+			      struct snd_ctl_elem_value *value);
+int xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl,
+			      struct snd_ctl_elem_value *value);
+
+/* model-specific card drivers */
+
+int get_xonar_pcm179x_model(struct oxygen *chip,
+			    const struct pci_device_id *id);
+int get_xonar_cs43xx_model(struct oxygen *chip,
+			   const struct pci_device_id *id);
+
+/* HDMI helper functions */
+
+void xonar_hdmi_init(struct oxygen *chip, struct xonar_hdmi *data);
+void xonar_hdmi_cleanup(struct oxygen *chip);
+void xonar_hdmi_resume(struct oxygen *chip, struct xonar_hdmi *hdmi);
+void xonar_hdmi_pcm_hardware_filter(unsigned int channel,
+				    struct snd_pcm_hardware *hardware);
+void xonar_set_hdmi_params(struct oxygen *chip, struct xonar_hdmi *hdmi,
+			   struct snd_pcm_hw_params *params);
+void xonar_hdmi_uart_input(struct oxygen *chip);
+
+#endif

+ 434 - 0
sound/pci/oxygen/xonar_cs43xx.c

@@ -0,0 +1,434 @@
+/*
+ * card driver for models with CS4398/CS4362A DACs (Xonar D1/DX)
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ *
+ *
+ *  This driver is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License, version 2.
+ *
+ *  This driver is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this driver; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Xonar D1/DX
+ * -----------
+ *
+ * CMI8788:
+ *
+ * I²C <-> CS4398 (front)
+ *     <-> CS4362A (surround, center/LFE, back)
+ *
+ * GPI 0 <- external power present (DX only)
+ *
+ * GPIO 0 -> enable output to speakers
+ * GPIO 1 -> enable front panel I/O
+ * GPIO 2 -> M0 of CS5361
+ * GPIO 3 -> M1 of CS5361
+ * GPIO 8 -> route input jack to line-in (0) or mic-in (1)
+ *
+ * CS4398:
+ *
+ * AD0 <- 1
+ * AD1 <- 1
+ *
+ * CS4362A:
+ *
+ * AD0 <- 0
+ *
+ * CM9780:
+ *
+ * GPO 0 -> route line-in (0) or AC97 output (1) to CS5361 input
+ */
+
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <sound/ac97_codec.h>
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include "xonar.h"
+#include "cs4398.h"
+#include "cs4362a.h"
+
+#define GPI_EXT_POWER		0x01
+#define GPIO_D1_OUTPUT_ENABLE	0x0001
+#define GPIO_D1_FRONT_PANEL	0x0002
+#define GPIO_D1_INPUT_ROUTE	0x0100
+
+#define I2C_DEVICE_CS4398	0x9e	/* 10011, AD1=1, AD0=1, /W=0 */
+#define I2C_DEVICE_CS4362A	0x30	/* 001100, AD0=0, /W=0 */
+
+struct xonar_cs43xx {
+	struct xonar_generic generic;
+	u8 cs4398_regs[8];
+	u8 cs4362a_regs[15];
+};
+
+static void cs4398_write(struct oxygen *chip, u8 reg, u8 value)
+{
+	struct xonar_cs43xx *data = chip->model_data;
+
+	oxygen_write_i2c(chip, I2C_DEVICE_CS4398, reg, value);
+	if (reg < ARRAY_SIZE(data->cs4398_regs))
+		data->cs4398_regs[reg] = value;
+}
+
+static void cs4398_write_cached(struct oxygen *chip, u8 reg, u8 value)
+{
+	struct xonar_cs43xx *data = chip->model_data;
+
+	if (value != data->cs4398_regs[reg])
+		cs4398_write(chip, reg, value);
+}
+
+static void cs4362a_write(struct oxygen *chip, u8 reg, u8 value)
+{
+	struct xonar_cs43xx *data = chip->model_data;
+
+	oxygen_write_i2c(chip, I2C_DEVICE_CS4362A, reg, value);
+	if (reg < ARRAY_SIZE(data->cs4362a_regs))
+		data->cs4362a_regs[reg] = value;
+}
+
+static void cs4362a_write_cached(struct oxygen *chip, u8 reg, u8 value)
+{
+	struct xonar_cs43xx *data = chip->model_data;
+
+	if (value != data->cs4362a_regs[reg])
+		cs4362a_write(chip, reg, value);
+}
+
+static void cs43xx_registers_init(struct oxygen *chip)
+{
+	struct xonar_cs43xx *data = chip->model_data;
+	unsigned int i;
+
+	/* set CPEN (control port mode) and power down */
+	cs4398_write(chip, 8, CS4398_CPEN | CS4398_PDN);
+	cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN);
+	/* configure */
+	cs4398_write(chip, 2, data->cs4398_regs[2]);
+	cs4398_write(chip, 3, CS4398_ATAPI_B_R | CS4398_ATAPI_A_L);
+	cs4398_write(chip, 4, data->cs4398_regs[4]);
+	cs4398_write(chip, 5, data->cs4398_regs[5]);
+	cs4398_write(chip, 6, data->cs4398_regs[6]);
+	cs4398_write(chip, 7, data->cs4398_regs[7]);
+	cs4362a_write(chip, 0x02, CS4362A_DIF_LJUST);
+	cs4362a_write(chip, 0x03, CS4362A_MUTEC_6 | CS4362A_AMUTE |
+		      CS4362A_RMP_UP | CS4362A_ZERO_CROSS | CS4362A_SOFT_RAMP);
+	cs4362a_write(chip, 0x04, data->cs4362a_regs[0x04]);
+	cs4362a_write(chip, 0x05, 0);
+	for (i = 6; i <= 14; ++i)
+		cs4362a_write(chip, i, data->cs4362a_regs[i]);
+	/* clear power down */
+	cs4398_write(chip, 8, CS4398_CPEN);
+	cs4362a_write(chip, 0x01, CS4362A_CPEN);
+}
+
+static void xonar_d1_init(struct oxygen *chip)
+{
+	struct xonar_cs43xx *data = chip->model_data;
+
+	data->generic.anti_pop_delay = 800;
+	data->generic.output_enable_bit = GPIO_D1_OUTPUT_ENABLE;
+	data->cs4398_regs[2] =
+		CS4398_FM_SINGLE | CS4398_DEM_NONE | CS4398_DIF_LJUST;
+	data->cs4398_regs[4] = CS4398_MUTEP_LOW |
+		CS4398_MUTE_B | CS4398_MUTE_A | CS4398_PAMUTE;
+	data->cs4398_regs[5] = 60 * 2;
+	data->cs4398_regs[6] = 60 * 2;
+	data->cs4398_regs[7] = CS4398_RMP_DN | CS4398_RMP_UP |
+		CS4398_ZERO_CROSS | CS4398_SOFT_RAMP;
+	data->cs4362a_regs[4] = CS4362A_RMP_DN | CS4362A_DEM_NONE;
+	data->cs4362a_regs[6] = CS4362A_FM_SINGLE |
+		CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L;
+	data->cs4362a_regs[7] = 60 | CS4362A_MUTE;
+	data->cs4362a_regs[8] = 60 | CS4362A_MUTE;
+	data->cs4362a_regs[9] = data->cs4362a_regs[6];
+	data->cs4362a_regs[10] = 60 | CS4362A_MUTE;
+	data->cs4362a_regs[11] = 60 | CS4362A_MUTE;
+	data->cs4362a_regs[12] = data->cs4362a_regs[6];
+	data->cs4362a_regs[13] = 60 | CS4362A_MUTE;
+	data->cs4362a_regs[14] = 60 | CS4362A_MUTE;
+
+	oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
+		       OXYGEN_2WIRE_LENGTH_8 |
+		       OXYGEN_2WIRE_INTERRUPT_MASK |
+		       OXYGEN_2WIRE_SPEED_FAST);
+
+	cs43xx_registers_init(chip);
+
+	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
+			  GPIO_D1_FRONT_PANEL | GPIO_D1_INPUT_ROUTE);
+	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
+			    GPIO_D1_FRONT_PANEL | GPIO_D1_INPUT_ROUTE);
+
+	xonar_init_cs53x1(chip);
+	xonar_enable_output(chip);
+
+	snd_component_add(chip->card, "CS4398");
+	snd_component_add(chip->card, "CS4362A");
+	snd_component_add(chip->card, "CS5361");
+}
+
+static void xonar_dx_init(struct oxygen *chip)
+{
+	struct xonar_cs43xx *data = chip->model_data;
+
+	data->generic.ext_power_reg = OXYGEN_GPI_DATA;
+	data->generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
+	data->generic.ext_power_bit = GPI_EXT_POWER;
+	xonar_init_ext_power(chip);
+	xonar_d1_init(chip);
+}
+
+static void xonar_d1_cleanup(struct oxygen *chip)
+{
+	xonar_disable_output(chip);
+	cs4362a_write(chip, 0x01, CS4362A_PDN | CS4362A_CPEN);
+	oxygen_clear_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC);
+}
+
+static void xonar_d1_suspend(struct oxygen *chip)
+{
+	xonar_d1_cleanup(chip);
+}
+
+static void xonar_d1_resume(struct oxygen *chip)
+{
+	oxygen_set_bits8(chip, OXYGEN_FUNCTION, OXYGEN_FUNCTION_RESET_CODEC);
+	msleep(1);
+	cs43xx_registers_init(chip);
+	xonar_enable_output(chip);
+}
+
+static void set_cs43xx_params(struct oxygen *chip,
+			      struct snd_pcm_hw_params *params)
+{
+	struct xonar_cs43xx *data = chip->model_data;
+	u8 cs4398_fm, cs4362a_fm;
+
+	if (params_rate(params) <= 50000) {
+		cs4398_fm = CS4398_FM_SINGLE;
+		cs4362a_fm = CS4362A_FM_SINGLE;
+	} else if (params_rate(params) <= 100000) {
+		cs4398_fm = CS4398_FM_DOUBLE;
+		cs4362a_fm = CS4362A_FM_DOUBLE;
+	} else {
+		cs4398_fm = CS4398_FM_QUAD;
+		cs4362a_fm = CS4362A_FM_QUAD;
+	}
+	cs4398_fm |= CS4398_DEM_NONE | CS4398_DIF_LJUST;
+	cs4398_write_cached(chip, 2, cs4398_fm);
+	cs4362a_fm |= data->cs4362a_regs[6] & ~CS4362A_FM_MASK;
+	cs4362a_write_cached(chip, 6, cs4362a_fm);
+	cs4362a_write_cached(chip, 12, cs4362a_fm);
+	cs4362a_fm &= CS4362A_FM_MASK;
+	cs4362a_fm |= data->cs4362a_regs[9] & ~CS4362A_FM_MASK;
+	cs4362a_write_cached(chip, 9, cs4362a_fm);
+}
+
+static void update_cs4362a_volumes(struct oxygen *chip)
+{
+	unsigned int i;
+	u8 mute;
+
+	mute = chip->dac_mute ? CS4362A_MUTE : 0;
+	for (i = 0; i < 6; ++i)
+		cs4362a_write_cached(chip, 7 + i + i / 2,
+				     (127 - chip->dac_volume[2 + i]) | mute);
+}
+
+static void update_cs43xx_volume(struct oxygen *chip)
+{
+	cs4398_write_cached(chip, 5, (127 - chip->dac_volume[0]) * 2);
+	cs4398_write_cached(chip, 6, (127 - chip->dac_volume[1]) * 2);
+	update_cs4362a_volumes(chip);
+}
+
+static void update_cs43xx_mute(struct oxygen *chip)
+{
+	u8 reg;
+
+	reg = CS4398_MUTEP_LOW | CS4398_PAMUTE;
+	if (chip->dac_mute)
+		reg |= CS4398_MUTE_B | CS4398_MUTE_A;
+	cs4398_write_cached(chip, 4, reg);
+	update_cs4362a_volumes(chip);
+}
+
+static void update_cs43xx_center_lfe_mix(struct oxygen *chip, bool mixed)
+{
+	struct xonar_cs43xx *data = chip->model_data;
+	u8 reg;
+
+	reg = data->cs4362a_regs[9] & ~CS4362A_ATAPI_MASK;
+	if (mixed)
+		reg |= CS4362A_ATAPI_B_LR | CS4362A_ATAPI_A_LR;
+	else
+		reg |= CS4362A_ATAPI_B_R | CS4362A_ATAPI_A_L;
+	cs4362a_write_cached(chip, 9, reg);
+}
+
+static const struct snd_kcontrol_new front_panel_switch = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Front Panel Switch",
+	.info = snd_ctl_boolean_mono_info,
+	.get = xonar_gpio_bit_switch_get,
+	.put = xonar_gpio_bit_switch_put,
+	.private_value = GPIO_D1_FRONT_PANEL,
+};
+
+static int rolloff_info(struct snd_kcontrol *ctl,
+			struct snd_ctl_elem_info *info)
+{
+	static const char *const names[2] = {
+		"Fast Roll-off", "Slow Roll-off"
+	};
+
+	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	info->count = 1;
+	info->value.enumerated.items = 2;
+	if (info->value.enumerated.item >= 2)
+		info->value.enumerated.item = 1;
+	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
+	return 0;
+}
+
+static int rolloff_get(struct snd_kcontrol *ctl,
+		       struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+	struct xonar_cs43xx *data = chip->model_data;
+
+	value->value.enumerated.item[0] =
+		(data->cs4398_regs[7] & CS4398_FILT_SEL) != 0;
+	return 0;
+}
+
+static int rolloff_put(struct snd_kcontrol *ctl,
+		       struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+	struct xonar_cs43xx *data = chip->model_data;
+	int changed;
+	u8 reg;
+
+	mutex_lock(&chip->mutex);
+	reg = data->cs4398_regs[7];
+	if (value->value.enumerated.item[0])
+		reg |= CS4398_FILT_SEL;
+	else
+		reg &= ~CS4398_FILT_SEL;
+	changed = reg != data->cs4398_regs[7];
+	if (changed) {
+		cs4398_write(chip, 7, reg);
+		if (reg & CS4398_FILT_SEL)
+			reg = data->cs4362a_regs[0x04] | CS4362A_FILT_SEL;
+		else
+			reg = data->cs4362a_regs[0x04] & ~CS4362A_FILT_SEL;
+		cs4362a_write(chip, 0x04, reg);
+	}
+	mutex_unlock(&chip->mutex);
+	return changed;
+}
+
+static const struct snd_kcontrol_new rolloff_control = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "DAC Filter Playback Enum",
+	.info = rolloff_info,
+	.get = rolloff_get,
+	.put = rolloff_put,
+};
+
+static void xonar_d1_line_mic_ac97_switch(struct oxygen *chip,
+					  unsigned int reg, unsigned int mute)
+{
+	if (reg == AC97_LINE) {
+		spin_lock_irq(&chip->reg_lock);
+		oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
+				      mute ? GPIO_D1_INPUT_ROUTE : 0,
+				      GPIO_D1_INPUT_ROUTE);
+		spin_unlock_irq(&chip->reg_lock);
+	}
+}
+
+static const DECLARE_TLV_DB_SCALE(cs4362a_db_scale, -6000, 100, 0);
+
+static int xonar_d1_control_filter(struct snd_kcontrol_new *template)
+{
+	if (!strncmp(template->name, "CD Capture ", 11))
+		return 1; /* no CD input */
+	return 0;
+}
+
+static int xonar_d1_mixer_init(struct oxygen *chip)
+{
+	int err;
+
+	err = snd_ctl_add(chip->card, snd_ctl_new1(&front_panel_switch, chip));
+	if (err < 0)
+		return err;
+	err = snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip));
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+static const struct oxygen_model model_xonar_d1 = {
+	.longname = "Asus Virtuoso 100",
+	.chip = "AV200",
+	.init = xonar_d1_init,
+	.control_filter = xonar_d1_control_filter,
+	.mixer_init = xonar_d1_mixer_init,
+	.cleanup = xonar_d1_cleanup,
+	.suspend = xonar_d1_suspend,
+	.resume = xonar_d1_resume,
+	.get_i2s_mclk = oxygen_default_i2s_mclk,
+	.set_dac_params = set_cs43xx_params,
+	.set_adc_params = xonar_set_cs53x1_params,
+	.update_dac_volume = update_cs43xx_volume,
+	.update_dac_mute = update_cs43xx_mute,
+	.update_center_lfe_mix = update_cs43xx_center_lfe_mix,
+	.ac97_switch = xonar_d1_line_mic_ac97_switch,
+	.dac_tlv = cs4362a_db_scale,
+	.model_data_size = sizeof(struct xonar_cs43xx),
+	.device_config = PLAYBACK_0_TO_I2S |
+			 PLAYBACK_1_TO_SPDIF |
+			 CAPTURE_0_FROM_I2S_2,
+	.dac_channels = 8,
+	.dac_volume_min = 127 - 60,
+	.dac_volume_max = 127,
+	.function_flags = OXYGEN_FUNCTION_2WIRE,
+	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+};
+
+int __devinit get_xonar_cs43xx_model(struct oxygen *chip,
+				     const struct pci_device_id *id)
+{
+	switch (id->subdevice) {
+	case 0x834f:
+		chip->model = model_xonar_d1;
+		chip->model.shortname = "Xonar D1";
+		break;
+	case 0x8275:
+	case 0x8327:
+		chip->model = model_xonar_d1;
+		chip->model.shortname = "Xonar DX";
+		chip->model.init = xonar_dx_init;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}

+ 128 - 0
sound/pci/oxygen/xonar_hdmi.c

@@ -0,0 +1,128 @@
+/*
+ * helper functions for HDMI models (Xonar HDAV1.3)
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ *
+ *
+ *  This driver is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License, version 2.
+ *
+ *  This driver is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this driver; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <sound/asoundef.h>
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include "xonar.h"
+
+static void hdmi_write_command(struct oxygen *chip, u8 command,
+			       unsigned int count, const u8 *params)
+{
+	unsigned int i;
+	u8 checksum;
+
+	oxygen_write_uart(chip, 0xfb);
+	oxygen_write_uart(chip, 0xef);
+	oxygen_write_uart(chip, command);
+	oxygen_write_uart(chip, count);
+	for (i = 0; i < count; ++i)
+		oxygen_write_uart(chip, params[i]);
+	checksum = 0xfb + 0xef + command + count;
+	for (i = 0; i < count; ++i)
+		checksum += params[i];
+	oxygen_write_uart(chip, checksum);
+}
+
+static void xonar_hdmi_init_commands(struct oxygen *chip,
+				     struct xonar_hdmi *hdmi)
+{
+	u8 param;
+
+	oxygen_reset_uart(chip);
+	param = 0;
+	hdmi_write_command(chip, 0x61, 1, &param);
+	param = 1;
+	hdmi_write_command(chip, 0x74, 1, &param);
+	hdmi_write_command(chip, 0x54, 5, hdmi->params);
+}
+
+void xonar_hdmi_init(struct oxygen *chip, struct xonar_hdmi *hdmi)
+{
+	hdmi->params[1] = IEC958_AES3_CON_FS_48000;
+	hdmi->params[4] = 1;
+	xonar_hdmi_init_commands(chip, hdmi);
+}
+
+void xonar_hdmi_cleanup(struct oxygen *chip)
+{
+	u8 param = 0;
+
+	hdmi_write_command(chip, 0x74, 1, &param);
+}
+
+void xonar_hdmi_resume(struct oxygen *chip, struct xonar_hdmi *hdmi)
+{
+	xonar_hdmi_init_commands(chip, hdmi);
+}
+
+void xonar_hdmi_pcm_hardware_filter(unsigned int channel,
+				    struct snd_pcm_hardware *hardware)
+{
+	if (channel == PCM_MULTICH) {
+		hardware->rates = SNDRV_PCM_RATE_44100 |
+				  SNDRV_PCM_RATE_48000 |
+				  SNDRV_PCM_RATE_96000 |
+				  SNDRV_PCM_RATE_192000;
+		hardware->rate_min = 44100;
+	}
+}
+
+void xonar_set_hdmi_params(struct oxygen *chip, struct xonar_hdmi *hdmi,
+			   struct snd_pcm_hw_params *params)
+{
+	hdmi->params[0] = 0; /* 1 = non-audio */
+	switch (params_rate(params)) {
+	case 44100:
+		hdmi->params[1] = IEC958_AES3_CON_FS_44100;
+		break;
+	case 48000:
+		hdmi->params[1] = IEC958_AES3_CON_FS_48000;
+		break;
+	default: /* 96000 */
+		hdmi->params[1] = IEC958_AES3_CON_FS_96000;
+		break;
+	case 192000:
+		hdmi->params[1] = IEC958_AES3_CON_FS_192000;
+		break;
+	}
+	hdmi->params[2] = params_channels(params) / 2 - 1;
+	if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE)
+		hdmi->params[3] = 0;
+	else
+		hdmi->params[3] = 0xc0;
+	hdmi->params[4] = 1; /* ? */
+	hdmi_write_command(chip, 0x54, 5, hdmi->params);
+}
+
+void xonar_hdmi_uart_input(struct oxygen *chip)
+{
+	if (chip->uart_input_count >= 2 &&
+	    chip->uart_input[chip->uart_input_count - 2] == 'O' &&
+	    chip->uart_input[chip->uart_input_count - 1] == 'K') {
+		printk(KERN_DEBUG "message from HDMI chip received:\n");
+		print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+				     chip->uart_input, chip->uart_input_count);
+		chip->uart_input_count = 0;
+	}
+}

+ 132 - 0
sound/pci/oxygen/xonar_lib.c

@@ -0,0 +1,132 @@
+/*
+ * helper functions for Asus Xonar cards
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ *
+ *
+ *  This driver is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License, version 2.
+ *
+ *  This driver is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this driver; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "xonar.h"
+
+
+#define GPIO_CS53x1_M_MASK	0x000c
+#define GPIO_CS53x1_M_SINGLE	0x0000
+#define GPIO_CS53x1_M_DOUBLE	0x0004
+#define GPIO_CS53x1_M_QUAD	0x0008
+
+
+void xonar_enable_output(struct oxygen *chip)
+{
+	struct xonar_generic *data = chip->model_data;
+
+	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, data->output_enable_bit);
+	msleep(data->anti_pop_delay);
+	oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
+}
+
+void xonar_disable_output(struct oxygen *chip)
+{
+	struct xonar_generic *data = chip->model_data;
+
+	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, data->output_enable_bit);
+}
+
+static void xonar_ext_power_gpio_changed(struct oxygen *chip)
+{
+	struct xonar_generic *data = chip->model_data;
+	u8 has_power;
+
+	has_power = !!(oxygen_read8(chip, data->ext_power_reg)
+		       & data->ext_power_bit);
+	if (has_power != data->has_power) {
+		data->has_power = has_power;
+		if (has_power) {
+			snd_printk(KERN_NOTICE "power restored\n");
+		} else {
+			snd_printk(KERN_CRIT
+				   "Hey! Don't unplug the power cable!\n");
+			/* TODO: stop PCMs */
+		}
+	}
+}
+
+void xonar_init_ext_power(struct oxygen *chip)
+{
+	struct xonar_generic *data = chip->model_data;
+
+	oxygen_set_bits8(chip, data->ext_power_int_reg,
+			 data->ext_power_bit);
+	chip->interrupt_mask |= OXYGEN_INT_GPIO;
+	chip->model.gpio_changed = xonar_ext_power_gpio_changed;
+	data->has_power = !!(oxygen_read8(chip, data->ext_power_reg)
+			     & data->ext_power_bit);
+}
+
+void xonar_init_cs53x1(struct oxygen *chip)
+{
+	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_CS53x1_M_MASK);
+	oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
+			      GPIO_CS53x1_M_SINGLE, GPIO_CS53x1_M_MASK);
+}
+
+void xonar_set_cs53x1_params(struct oxygen *chip,
+			     struct snd_pcm_hw_params *params)
+{
+	unsigned int value;
+
+	if (params_rate(params) <= 54000)
+		value = GPIO_CS53x1_M_SINGLE;
+	else if (params_rate(params) <= 108000)
+		value = GPIO_CS53x1_M_DOUBLE;
+	else
+		value = GPIO_CS53x1_M_QUAD;
+	oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
+			      value, GPIO_CS53x1_M_MASK);
+}
+
+int xonar_gpio_bit_switch_get(struct snd_kcontrol *ctl,
+			      struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+	u16 bit = ctl->private_value;
+
+	value->value.integer.value[0] =
+		!!(oxygen_read16(chip, OXYGEN_GPIO_DATA) & bit);
+	return 0;
+}
+
+int xonar_gpio_bit_switch_put(struct snd_kcontrol *ctl,
+			      struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+	u16 bit = ctl->private_value;
+	u16 old_bits, new_bits;
+	int changed;
+
+	spin_lock_irq(&chip->reg_lock);
+	old_bits = oxygen_read16(chip, OXYGEN_GPIO_DATA);
+	if (value->value.integer.value[0])
+		new_bits = old_bits | bit;
+	else
+		new_bits = old_bits & ~bit;
+	changed = new_bits != old_bits;
+	if (changed)
+		oxygen_write16(chip, OXYGEN_GPIO_DATA, new_bits);
+	spin_unlock_irq(&chip->reg_lock);
+	return changed;
+}

+ 1115 - 0
sound/pci/oxygen/xonar_pcm179x.c

@@ -0,0 +1,1115 @@
+/*
+ * card driver for models with PCM1796 DACs (Xonar D2/D2X/HDAV1.3/ST/STX)
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ *
+ *
+ *  This driver is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License, version 2.
+ *
+ *  This driver is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this driver; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Xonar D2/D2X
+ * ------------
+ *
+ * CMI8788:
+ *
+ * SPI 0 -> 1st PCM1796 (front)
+ * SPI 1 -> 2nd PCM1796 (surround)
+ * SPI 2 -> 3rd PCM1796 (center/LFE)
+ * SPI 4 -> 4th PCM1796 (back)
+ *
+ * GPIO 2 -> M0 of CS5381
+ * GPIO 3 -> M1 of CS5381
+ * GPIO 5 <- external power present (D2X only)
+ * GPIO 7 -> ALT
+ * GPIO 8 -> enable output to speakers
+ *
+ * CM9780:
+ *
+ * GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input
+ */
+
+/*
+ * Xonar HDAV1.3 (Deluxe)
+ * ----------------------
+ *
+ * CMI8788:
+ *
+ * I²C <-> PCM1796 (front)
+ *
+ * GPI 0 <- external power present
+ *
+ * GPIO 0 -> enable output to speakers
+ * GPIO 2 -> M0 of CS5381
+ * GPIO 3 -> M1 of CS5381
+ * GPIO 8 -> route input jack to line-in (0) or mic-in (1)
+ *
+ * TXD -> HDMI controller
+ * RXD <- HDMI controller
+ *
+ * PCM1796 front: AD1,0 <- 0,0
+ *
+ * CM9780:
+ *
+ * GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input
+ *
+ * no daughterboard
+ * ----------------
+ *
+ * GPIO 4 <- 1
+ *
+ * H6 daughterboard
+ * ----------------
+ *
+ * GPIO 4 <- 0
+ * GPIO 5 <- 0
+ *
+ * I²C <-> PCM1796 (surround)
+ *     <-> PCM1796 (center/LFE)
+ *     <-> PCM1796 (back)
+ *
+ * PCM1796 surround:   AD1,0 <- 0,1
+ * PCM1796 center/LFE: AD1,0 <- 1,0
+ * PCM1796 back:       AD1,0 <- 1,1
+ *
+ * unknown daughterboard
+ * ---------------------
+ *
+ * GPIO 4 <- 0
+ * GPIO 5 <- 1
+ *
+ * I²C <-> CS4362A (surround, center/LFE, back)
+ *
+ * CS4362A: AD0 <- 0
+ */
+
+/*
+ * Xonar Essence ST (Deluxe)/STX
+ * -----------------------------
+ *
+ * CMI8788:
+ *
+ * I²C <-> PCM1792A
+ *     <-> CS2000 (ST only)
+ *
+ * ADC1 MCLK -> REF_CLK of CS2000 (ST only)
+ *
+ * GPI 0 <- external power present (STX only)
+ *
+ * GPIO 0 -> enable output to speakers
+ * GPIO 1 -> route HP to front panel (0) or rear jack (1)
+ * GPIO 2 -> M0 of CS5381
+ * GPIO 3 -> M1 of CS5381
+ * GPIO 7 -> route output to speaker jacks (0) or HP (1)
+ * GPIO 8 -> route input jack to line-in (0) or mic-in (1)
+ *
+ * PCM1792A:
+ *
+ * AD1,0 <- 0,0
+ * SCK <- CLK_OUT of CS2000 (ST only)
+ *
+ * CS2000:
+ *
+ * AD0 <- 0
+ *
+ * CM9780:
+ *
+ * GPO 0 -> route line-in (0) or AC97 output (1) to CS5381 input
+ *
+ * H6 daughterboard
+ * ----------------
+ *
+ * GPIO 4 <- 0
+ * GPIO 5 <- 0
+ */
+
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <sound/ac97_codec.h>
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include "xonar.h"
+#include "cm9780.h"
+#include "pcm1796.h"
+#include "cs2000.h"
+
+
+#define GPIO_D2X_EXT_POWER	0x0020
+#define GPIO_D2_ALT		0x0080
+#define GPIO_D2_OUTPUT_ENABLE	0x0100
+
+#define GPI_EXT_POWER		0x01
+#define GPIO_INPUT_ROUTE	0x0100
+
+#define GPIO_HDAV_OUTPUT_ENABLE	0x0001
+
+#define GPIO_DB_MASK		0x0030
+#define GPIO_DB_H6		0x0000
+
+#define GPIO_ST_OUTPUT_ENABLE	0x0001
+#define GPIO_ST_HP_REAR		0x0002
+#define GPIO_ST_HP		0x0080
+
+#define I2C_DEVICE_PCM1796(i)	(0x98 + ((i) << 1))	/* 10011, ii, /W=0 */
+#define I2C_DEVICE_CS2000	0x9c			/* 100111, 0, /W=0 */
+
+#define PCM1796_REG_BASE	16
+
+
+struct xonar_pcm179x {
+	struct xonar_generic generic;
+	unsigned int dacs;
+	u8 pcm1796_regs[4][5];
+	unsigned int current_rate;
+	bool os_128;
+	bool hp_active;
+	s8 hp_gain_offset;
+	bool has_cs2000;
+	u8 cs2000_fun_cfg_1;
+};
+
+struct xonar_hdav {
+	struct xonar_pcm179x pcm179x;
+	struct xonar_hdmi hdmi;
+};
+
+
+static inline void pcm1796_write_spi(struct oxygen *chip, unsigned int codec,
+				     u8 reg, u8 value)
+{
+	/* maps ALSA channel pair number to SPI output */
+	static const u8 codec_map[4] = {
+		0, 1, 2, 4
+	};
+	oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER  |
+			 OXYGEN_SPI_DATA_LENGTH_2 |
+			 OXYGEN_SPI_CLOCK_160 |
+			 (codec_map[codec] << OXYGEN_SPI_CODEC_SHIFT) |
+			 OXYGEN_SPI_CEN_LATCH_CLOCK_HI,
+			 (reg << 8) | value);
+}
+
+static inline void pcm1796_write_i2c(struct oxygen *chip, unsigned int codec,
+				     u8 reg, u8 value)
+{
+	oxygen_write_i2c(chip, I2C_DEVICE_PCM1796(codec), reg, value);
+}
+
+static void pcm1796_write(struct oxygen *chip, unsigned int codec,
+			  u8 reg, u8 value)
+{
+	struct xonar_pcm179x *data = chip->model_data;
+
+	if ((chip->model.function_flags & OXYGEN_FUNCTION_2WIRE_SPI_MASK) ==
+	    OXYGEN_FUNCTION_SPI)
+		pcm1796_write_spi(chip, codec, reg, value);
+	else
+		pcm1796_write_i2c(chip, codec, reg, value);
+	if ((unsigned int)(reg - PCM1796_REG_BASE)
+	    < ARRAY_SIZE(data->pcm1796_regs[codec]))
+		data->pcm1796_regs[codec][reg - PCM1796_REG_BASE] = value;
+}
+
+static void pcm1796_write_cached(struct oxygen *chip, unsigned int codec,
+				 u8 reg, u8 value)
+{
+	struct xonar_pcm179x *data = chip->model_data;
+
+	if (value != data->pcm1796_regs[codec][reg - PCM1796_REG_BASE])
+		pcm1796_write(chip, codec, reg, value);
+}
+
+static void cs2000_write(struct oxygen *chip, u8 reg, u8 value)
+{
+	struct xonar_pcm179x *data = chip->model_data;
+
+	oxygen_write_i2c(chip, I2C_DEVICE_CS2000, reg, value);
+	if (reg == CS2000_FUN_CFG_1)
+		data->cs2000_fun_cfg_1 = value;
+}
+
+static void cs2000_write_cached(struct oxygen *chip, u8 reg, u8 value)
+{
+	struct xonar_pcm179x *data = chip->model_data;
+
+	if (reg != CS2000_FUN_CFG_1 ||
+	    value != data->cs2000_fun_cfg_1)
+		cs2000_write(chip, reg, value);
+}
+
+static void pcm1796_registers_init(struct oxygen *chip)
+{
+	struct xonar_pcm179x *data = chip->model_data;
+	unsigned int i;
+	s8 gain_offset;
+
+	gain_offset = data->hp_active ? data->hp_gain_offset : 0;
+	for (i = 0; i < data->dacs; ++i) {
+		/* set ATLD before ATL/ATR */
+		pcm1796_write(chip, i, 18,
+			      data->pcm1796_regs[0][18 - PCM1796_REG_BASE]);
+		pcm1796_write(chip, i, 16, chip->dac_volume[i * 2]
+			      + gain_offset);
+		pcm1796_write(chip, i, 17, chip->dac_volume[i * 2 + 1]
+			      + gain_offset);
+		pcm1796_write(chip, i, 19,
+			      data->pcm1796_regs[0][19 - PCM1796_REG_BASE]);
+		pcm1796_write(chip, i, 20,
+			      data->pcm1796_regs[0][20 - PCM1796_REG_BASE]);
+		pcm1796_write(chip, i, 21, 0);
+	}
+}
+
+static void pcm1796_init(struct oxygen *chip)
+{
+	struct xonar_pcm179x *data = chip->model_data;
+
+	data->pcm1796_regs[0][18 - PCM1796_REG_BASE] = PCM1796_MUTE |
+		PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD;
+	data->pcm1796_regs[0][19 - PCM1796_REG_BASE] =
+		PCM1796_FLT_SHARP | PCM1796_ATS_1;
+	data->pcm1796_regs[0][20 - PCM1796_REG_BASE] = PCM1796_OS_64;
+	pcm1796_registers_init(chip);
+	data->current_rate = 48000;
+}
+
+static void xonar_d2_init(struct oxygen *chip)
+{
+	struct xonar_pcm179x *data = chip->model_data;
+
+	data->generic.anti_pop_delay = 300;
+	data->generic.output_enable_bit = GPIO_D2_OUTPUT_ENABLE;
+	data->dacs = 4;
+
+	pcm1796_init(chip);
+
+	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2_ALT);
+	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_D2_ALT);
+
+	oxygen_ac97_set_bits(chip, 0, CM9780_JACK, CM9780_FMIC2MIC);
+
+	xonar_init_cs53x1(chip);
+	xonar_enable_output(chip);
+
+	snd_component_add(chip->card, "PCM1796");
+	snd_component_add(chip->card, "CS5381");
+}
+
+static void xonar_d2x_init(struct oxygen *chip)
+{
+	struct xonar_pcm179x *data = chip->model_data;
+
+	data->generic.ext_power_reg = OXYGEN_GPIO_DATA;
+	data->generic.ext_power_int_reg = OXYGEN_GPIO_INTERRUPT_MASK;
+	data->generic.ext_power_bit = GPIO_D2X_EXT_POWER;
+	oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_D2X_EXT_POWER);
+	xonar_init_ext_power(chip);
+	xonar_d2_init(chip);
+}
+
+static void xonar_hdav_init(struct oxygen *chip)
+{
+	struct xonar_hdav *data = chip->model_data;
+
+	oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
+		       OXYGEN_2WIRE_LENGTH_8 |
+		       OXYGEN_2WIRE_INTERRUPT_MASK |
+		       OXYGEN_2WIRE_SPEED_FAST);
+
+	data->pcm179x.generic.anti_pop_delay = 100;
+	data->pcm179x.generic.output_enable_bit = GPIO_HDAV_OUTPUT_ENABLE;
+	data->pcm179x.generic.ext_power_reg = OXYGEN_GPI_DATA;
+	data->pcm179x.generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
+	data->pcm179x.generic.ext_power_bit = GPI_EXT_POWER;
+	data->pcm179x.dacs = chip->model.private_data ? 4 : 1;
+
+	pcm1796_init(chip);
+
+	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_INPUT_ROUTE);
+	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA, GPIO_INPUT_ROUTE);
+
+	xonar_init_cs53x1(chip);
+	xonar_init_ext_power(chip);
+	xonar_hdmi_init(chip, &data->hdmi);
+	xonar_enable_output(chip);
+
+	snd_component_add(chip->card, "PCM1796");
+	snd_component_add(chip->card, "CS5381");
+}
+
+static void xonar_st_init_i2c(struct oxygen *chip)
+{
+	oxygen_write16(chip, OXYGEN_2WIRE_BUS_STATUS,
+		       OXYGEN_2WIRE_LENGTH_8 |
+		       OXYGEN_2WIRE_INTERRUPT_MASK |
+		       OXYGEN_2WIRE_SPEED_FAST);
+}
+
+static void xonar_st_init_common(struct oxygen *chip)
+{
+	struct xonar_pcm179x *data = chip->model_data;
+
+	data->generic.anti_pop_delay = 100;
+	data->generic.output_enable_bit = GPIO_ST_OUTPUT_ENABLE;
+	data->dacs = chip->model.private_data ? 4 : 1;
+	data->hp_gain_offset = 2*-18;
+
+	pcm1796_init(chip);
+
+	oxygen_set_bits16(chip, OXYGEN_GPIO_CONTROL,
+			  GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP);
+	oxygen_clear_bits16(chip, OXYGEN_GPIO_DATA,
+			    GPIO_INPUT_ROUTE | GPIO_ST_HP_REAR | GPIO_ST_HP);
+
+	xonar_init_cs53x1(chip);
+	xonar_enable_output(chip);
+
+	snd_component_add(chip->card, "PCM1792A");
+	snd_component_add(chip->card, "CS5381");
+}
+
+static void cs2000_registers_init(struct oxygen *chip)
+{
+	struct xonar_pcm179x *data = chip->model_data;
+
+	cs2000_write(chip, CS2000_GLOBAL_CFG, CS2000_FREEZE);
+	cs2000_write(chip, CS2000_DEV_CTRL, 0);
+	cs2000_write(chip, CS2000_DEV_CFG_1,
+		     CS2000_R_MOD_SEL_1 |
+		     (0 << CS2000_R_SEL_SHIFT) |
+		     CS2000_AUX_OUT_SRC_REF_CLK |
+		     CS2000_EN_DEV_CFG_1);
+	cs2000_write(chip, CS2000_DEV_CFG_2,
+		     (0 << CS2000_LOCK_CLK_SHIFT) |
+		     CS2000_FRAC_N_SRC_STATIC);
+	cs2000_write(chip, CS2000_RATIO_0 + 0, 0x00); /* 1.0 */
+	cs2000_write(chip, CS2000_RATIO_0 + 1, 0x10);
+	cs2000_write(chip, CS2000_RATIO_0 + 2, 0x00);
+	cs2000_write(chip, CS2000_RATIO_0 + 3, 0x00);
+	cs2000_write(chip, CS2000_FUN_CFG_1, data->cs2000_fun_cfg_1);
+	cs2000_write(chip, CS2000_FUN_CFG_2, 0);
+	cs2000_write(chip, CS2000_GLOBAL_CFG, CS2000_EN_DEV_CFG_2);
+}
+
+static void xonar_st_init(struct oxygen *chip)
+{
+	struct xonar_pcm179x *data = chip->model_data;
+
+	data->has_cs2000 = 1;
+	data->cs2000_fun_cfg_1 = CS2000_REF_CLK_DIV_1;
+
+	oxygen_write16(chip, OXYGEN_I2S_A_FORMAT,
+		       OXYGEN_RATE_48000 | OXYGEN_I2S_FORMAT_I2S |
+		       OXYGEN_I2S_MCLK_128 | OXYGEN_I2S_BITS_16 |
+		       OXYGEN_I2S_MASTER | OXYGEN_I2S_BCLK_64);
+
+	xonar_st_init_i2c(chip);
+	cs2000_registers_init(chip);
+	xonar_st_init_common(chip);
+
+	snd_component_add(chip->card, "CS2000");
+}
+
+static void xonar_stx_init(struct oxygen *chip)
+{
+	struct xonar_pcm179x *data = chip->model_data;
+
+	xonar_st_init_i2c(chip);
+	data->generic.ext_power_reg = OXYGEN_GPI_DATA;
+	data->generic.ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
+	data->generic.ext_power_bit = GPI_EXT_POWER;
+	xonar_init_ext_power(chip);
+	xonar_st_init_common(chip);
+}
+
+static void xonar_d2_cleanup(struct oxygen *chip)
+{
+	xonar_disable_output(chip);
+}
+
+static void xonar_hdav_cleanup(struct oxygen *chip)
+{
+	xonar_hdmi_cleanup(chip);
+	xonar_disable_output(chip);
+	msleep(2);
+}
+
+static void xonar_st_cleanup(struct oxygen *chip)
+{
+	xonar_disable_output(chip);
+}
+
+static void xonar_d2_suspend(struct oxygen *chip)
+{
+	xonar_d2_cleanup(chip);
+}
+
+static void xonar_hdav_suspend(struct oxygen *chip)
+{
+	xonar_hdav_cleanup(chip);
+}
+
+static void xonar_st_suspend(struct oxygen *chip)
+{
+	xonar_st_cleanup(chip);
+}
+
+static void xonar_d2_resume(struct oxygen *chip)
+{
+	pcm1796_registers_init(chip);
+	xonar_enable_output(chip);
+}
+
+static void xonar_hdav_resume(struct oxygen *chip)
+{
+	struct xonar_hdav *data = chip->model_data;
+
+	pcm1796_registers_init(chip);
+	xonar_hdmi_resume(chip, &data->hdmi);
+	xonar_enable_output(chip);
+}
+
+static void xonar_stx_resume(struct oxygen *chip)
+{
+	pcm1796_registers_init(chip);
+	xonar_enable_output(chip);
+}
+
+static void xonar_st_resume(struct oxygen *chip)
+{
+	cs2000_registers_init(chip);
+	xonar_stx_resume(chip);
+}
+
+static unsigned int mclk_from_rate(struct oxygen *chip, unsigned int rate)
+{
+	struct xonar_pcm179x *data = chip->model_data;
+
+	if (rate <= 32000)
+		return OXYGEN_I2S_MCLK_512;
+	else if (rate <= 48000 && data->os_128)
+		return OXYGEN_I2S_MCLK_512;
+	else if (rate <= 96000)
+		return OXYGEN_I2S_MCLK_256;
+	else
+		return OXYGEN_I2S_MCLK_128;
+}
+
+static unsigned int get_pcm1796_i2s_mclk(struct oxygen *chip,
+					 unsigned int channel,
+					 struct snd_pcm_hw_params *params)
+{
+	if (channel == PCM_MULTICH)
+		return mclk_from_rate(chip, params_rate(params));
+	else
+		return oxygen_default_i2s_mclk(chip, channel, params);
+}
+
+static void update_pcm1796_oversampling(struct oxygen *chip)
+{
+	struct xonar_pcm179x *data = chip->model_data;
+	unsigned int i;
+	u8 reg;
+
+	if (data->current_rate <= 32000)
+		reg = PCM1796_OS_128;
+	else if (data->current_rate <= 48000 && data->os_128)
+		reg = PCM1796_OS_128;
+	else if (data->current_rate <= 96000 || data->os_128)
+		reg = PCM1796_OS_64;
+	else
+		reg = PCM1796_OS_32;
+	for (i = 0; i < data->dacs; ++i)
+		pcm1796_write_cached(chip, i, 20, reg);
+}
+
+static void set_pcm1796_params(struct oxygen *chip,
+			       struct snd_pcm_hw_params *params)
+{
+	struct xonar_pcm179x *data = chip->model_data;
+
+	data->current_rate = params_rate(params);
+	update_pcm1796_oversampling(chip);
+}
+
+static void update_pcm1796_volume(struct oxygen *chip)
+{
+	struct xonar_pcm179x *data = chip->model_data;
+	unsigned int i;
+	s8 gain_offset;
+
+	gain_offset = data->hp_active ? data->hp_gain_offset : 0;
+	for (i = 0; i < data->dacs; ++i) {
+		pcm1796_write_cached(chip, i, 16, chip->dac_volume[i * 2]
+				     + gain_offset);
+		pcm1796_write_cached(chip, i, 17, chip->dac_volume[i * 2 + 1]
+				     + gain_offset);
+	}
+}
+
+static void update_pcm1796_mute(struct oxygen *chip)
+{
+	struct xonar_pcm179x *data = chip->model_data;
+	unsigned int i;
+	u8 value;
+
+	value = PCM1796_DMF_DISABLED | PCM1796_FMT_24_LJUST | PCM1796_ATLD;
+	if (chip->dac_mute)
+		value |= PCM1796_MUTE;
+	for (i = 0; i < data->dacs; ++i)
+		pcm1796_write_cached(chip, i, 18, value);
+}
+
+static void update_cs2000_rate(struct oxygen *chip, unsigned int rate)
+{
+	struct xonar_pcm179x *data = chip->model_data;
+	u8 rate_mclk, reg;
+
+	switch (rate) {
+		/* XXX Why is the I2S A MCLK half the actual I2S MCLK? */
+	case 32000:
+		rate_mclk = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_256;
+		break;
+	case 44100:
+		if (data->os_128)
+			rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256;
+		else
+			rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_128;
+		break;
+	default: /* 48000 */
+		if (data->os_128)
+			rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256;
+		else
+			rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_128;
+		break;
+	case 64000:
+		rate_mclk = OXYGEN_RATE_32000 | OXYGEN_I2S_MCLK_256;
+		break;
+	case 88200:
+		rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256;
+		break;
+	case 96000:
+		rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256;
+		break;
+	case 176400:
+		rate_mclk = OXYGEN_RATE_44100 | OXYGEN_I2S_MCLK_256;
+		break;
+	case 192000:
+		rate_mclk = OXYGEN_RATE_48000 | OXYGEN_I2S_MCLK_256;
+		break;
+	}
+	oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT, rate_mclk,
+			      OXYGEN_I2S_RATE_MASK | OXYGEN_I2S_MCLK_MASK);
+	if ((rate_mclk & OXYGEN_I2S_MCLK_MASK) <= OXYGEN_I2S_MCLK_128)
+		reg = CS2000_REF_CLK_DIV_1;
+	else
+		reg = CS2000_REF_CLK_DIV_2;
+	cs2000_write_cached(chip, CS2000_FUN_CFG_1, reg);
+}
+
+static void set_st_params(struct oxygen *chip,
+			  struct snd_pcm_hw_params *params)
+{
+	update_cs2000_rate(chip, params_rate(params));
+	set_pcm1796_params(chip, params);
+}
+
+static void set_hdav_params(struct oxygen *chip,
+			    struct snd_pcm_hw_params *params)
+{
+	struct xonar_hdav *data = chip->model_data;
+
+	set_pcm1796_params(chip, params);
+	xonar_set_hdmi_params(chip, &data->hdmi, params);
+}
+
+static const struct snd_kcontrol_new alt_switch = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Analog Loopback Switch",
+	.info = snd_ctl_boolean_mono_info,
+	.get = xonar_gpio_bit_switch_get,
+	.put = xonar_gpio_bit_switch_put,
+	.private_value = GPIO_D2_ALT,
+};
+
+static int rolloff_info(struct snd_kcontrol *ctl,
+			struct snd_ctl_elem_info *info)
+{
+	static const char *const names[2] = {
+		"Sharp Roll-off", "Slow Roll-off"
+	};
+
+	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	info->count = 1;
+	info->value.enumerated.items = 2;
+	if (info->value.enumerated.item >= 2)
+		info->value.enumerated.item = 1;
+	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
+	return 0;
+}
+
+static int rolloff_get(struct snd_kcontrol *ctl,
+		       struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+	struct xonar_pcm179x *data = chip->model_data;
+
+	value->value.enumerated.item[0] =
+		(data->pcm1796_regs[0][19 - PCM1796_REG_BASE] &
+		 PCM1796_FLT_MASK) != PCM1796_FLT_SHARP;
+	return 0;
+}
+
+static int rolloff_put(struct snd_kcontrol *ctl,
+		       struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+	struct xonar_pcm179x *data = chip->model_data;
+	unsigned int i;
+	int changed;
+	u8 reg;
+
+	mutex_lock(&chip->mutex);
+	reg = data->pcm1796_regs[0][19 - PCM1796_REG_BASE];
+	reg &= ~PCM1796_FLT_MASK;
+	if (!value->value.enumerated.item[0])
+		reg |= PCM1796_FLT_SHARP;
+	else
+		reg |= PCM1796_FLT_SLOW;
+	changed = reg != data->pcm1796_regs[0][19 - PCM1796_REG_BASE];
+	if (changed) {
+		for (i = 0; i < data->dacs; ++i)
+			pcm1796_write(chip, i, 19, reg);
+	}
+	mutex_unlock(&chip->mutex);
+	return changed;
+}
+
+static const struct snd_kcontrol_new rolloff_control = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "DAC Filter Playback Enum",
+	.info = rolloff_info,
+	.get = rolloff_get,
+	.put = rolloff_put,
+};
+
+static int os_128_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
+{
+	static const char *const names[2] = { "64x", "128x" };
+
+	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	info->count = 1;
+	info->value.enumerated.items = 2;
+	if (info->value.enumerated.item >= 2)
+		info->value.enumerated.item = 1;
+	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
+	return 0;
+}
+
+static int os_128_get(struct snd_kcontrol *ctl,
+		      struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+	struct xonar_pcm179x *data = chip->model_data;
+
+	value->value.enumerated.item[0] = data->os_128;
+	return 0;
+}
+
+static int os_128_put(struct snd_kcontrol *ctl,
+		      struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+	struct xonar_pcm179x *data = chip->model_data;
+	int changed;
+
+	mutex_lock(&chip->mutex);
+	changed = value->value.enumerated.item[0] != data->os_128;
+	if (changed) {
+		data->os_128 = value->value.enumerated.item[0];
+		if (data->has_cs2000)
+			update_cs2000_rate(chip, data->current_rate);
+		oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT,
+				      mclk_from_rate(chip, data->current_rate),
+				      OXYGEN_I2S_MCLK_MASK);
+		update_pcm1796_oversampling(chip);
+	}
+	mutex_unlock(&chip->mutex);
+	return changed;
+}
+
+static const struct snd_kcontrol_new os_128_control = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "DAC Oversampling Playback Enum",
+	.info = os_128_info,
+	.get = os_128_get,
+	.put = os_128_put,
+};
+
+static int st_output_switch_info(struct snd_kcontrol *ctl,
+				 struct snd_ctl_elem_info *info)
+{
+	static const char *const names[3] = {
+		"Speakers", "Headphones", "FP Headphones"
+	};
+
+	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	info->count = 1;
+	info->value.enumerated.items = 3;
+	if (info->value.enumerated.item >= 3)
+		info->value.enumerated.item = 2;
+	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
+	return 0;
+}
+
+static int st_output_switch_get(struct snd_kcontrol *ctl,
+				struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+	u16 gpio;
+
+	gpio = oxygen_read16(chip, OXYGEN_GPIO_DATA);
+	if (!(gpio & GPIO_ST_HP))
+		value->value.enumerated.item[0] = 0;
+	else if (gpio & GPIO_ST_HP_REAR)
+		value->value.enumerated.item[0] = 1;
+	else
+		value->value.enumerated.item[0] = 2;
+	return 0;
+}
+
+
+static int st_output_switch_put(struct snd_kcontrol *ctl,
+				struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+	struct xonar_pcm179x *data = chip->model_data;
+	u16 gpio_old, gpio;
+
+	mutex_lock(&chip->mutex);
+	gpio_old = oxygen_read16(chip, OXYGEN_GPIO_DATA);
+	gpio = gpio_old;
+	switch (value->value.enumerated.item[0]) {
+	case 0:
+		gpio &= ~(GPIO_ST_HP | GPIO_ST_HP_REAR);
+		break;
+	case 1:
+		gpio |= GPIO_ST_HP | GPIO_ST_HP_REAR;
+		break;
+	case 2:
+		gpio = (gpio | GPIO_ST_HP) & ~GPIO_ST_HP_REAR;
+		break;
+	}
+	oxygen_write16(chip, OXYGEN_GPIO_DATA, gpio);
+	data->hp_active = gpio & GPIO_ST_HP;
+	update_pcm1796_volume(chip);
+	mutex_unlock(&chip->mutex);
+	return gpio != gpio_old;
+}
+
+static int st_hp_volume_offset_info(struct snd_kcontrol *ctl,
+				    struct snd_ctl_elem_info *info)
+{
+	static const char *const names[3] = {
+		"< 64 ohms", "64-300 ohms", "300-600 ohms"
+	};
+
+	info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	info->count = 1;
+	info->value.enumerated.items = 3;
+	if (info->value.enumerated.item > 2)
+		info->value.enumerated.item = 2;
+	strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
+	return 0;
+}
+
+static int st_hp_volume_offset_get(struct snd_kcontrol *ctl,
+				   struct snd_ctl_elem_value *value)
+{
+	struct oxygen *chip = ctl->private_data;
+	struct xonar_pcm179x *data = chip->model_data;
+
+	mutex_lock(&chip->mutex);
+	if (data->hp_gain_offset < 2*-6)
+		value->value.enumerated.item[0] = 0;
+	else if (data->hp_gain_offset < 0)
+		value->value.enumerated.item[0] = 1;
+	else
+		value->value.enumerated.item[0] = 2;
+	mutex_unlock(&chip->mutex);
+	return 0;
+}
+
+
+static int st_hp_volume_offset_put(struct snd_kcontrol *ctl,
+				   struct snd_ctl_elem_value *value)
+{
+	static const s8 offsets[] = { 2*-18, 2*-6, 0 };
+	struct oxygen *chip = ctl->private_data;
+	struct xonar_pcm179x *data = chip->model_data;
+	s8 offset;
+	int changed;
+
+	if (value->value.enumerated.item[0] > 2)
+		return -EINVAL;
+	offset = offsets[value->value.enumerated.item[0]];
+	mutex_lock(&chip->mutex);
+	changed = offset != data->hp_gain_offset;
+	if (changed) {
+		data->hp_gain_offset = offset;
+		update_pcm1796_volume(chip);
+	}
+	mutex_unlock(&chip->mutex);
+	return changed;
+}
+
+static const struct snd_kcontrol_new st_controls[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Analog Output",
+		.info = st_output_switch_info,
+		.get = st_output_switch_get,
+		.put = st_output_switch_put,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Headphones Impedance Playback Enum",
+		.info = st_hp_volume_offset_info,
+		.get = st_hp_volume_offset_get,
+		.put = st_hp_volume_offset_put,
+	},
+};
+
+static void xonar_line_mic_ac97_switch(struct oxygen *chip,
+				       unsigned int reg, unsigned int mute)
+{
+	if (reg == AC97_LINE) {
+		spin_lock_irq(&chip->reg_lock);
+		oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
+				      mute ? GPIO_INPUT_ROUTE : 0,
+				      GPIO_INPUT_ROUTE);
+		spin_unlock_irq(&chip->reg_lock);
+	}
+}
+
+static const DECLARE_TLV_DB_SCALE(pcm1796_db_scale, -6000, 50, 0);
+
+static int xonar_d2_control_filter(struct snd_kcontrol_new *template)
+{
+	if (!strncmp(template->name, "CD Capture ", 11))
+		/* CD in is actually connected to the video in pin */
+		template->private_value ^= AC97_CD ^ AC97_VIDEO;
+	return 0;
+}
+
+static int xonar_st_control_filter(struct snd_kcontrol_new *template)
+{
+	if (!strncmp(template->name, "CD Capture ", 11))
+		return 1; /* no CD input */
+	return 0;
+}
+
+static int add_pcm1796_controls(struct oxygen *chip)
+{
+	int err;
+
+	err = snd_ctl_add(chip->card, snd_ctl_new1(&rolloff_control, chip));
+	if (err < 0)
+		return err;
+	err = snd_ctl_add(chip->card, snd_ctl_new1(&os_128_control, chip));
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+static int xonar_d2_mixer_init(struct oxygen *chip)
+{
+	int err;
+
+	err = snd_ctl_add(chip->card, snd_ctl_new1(&alt_switch, chip));
+	if (err < 0)
+		return err;
+	err = add_pcm1796_controls(chip);
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+static int xonar_hdav_mixer_init(struct oxygen *chip)
+{
+	return add_pcm1796_controls(chip);
+}
+
+static int xonar_st_mixer_init(struct oxygen *chip)
+{
+	unsigned int i;
+	int err;
+
+	for (i = 0; i < ARRAY_SIZE(st_controls); ++i) {
+		err = snd_ctl_add(chip->card,
+				  snd_ctl_new1(&st_controls[i], chip));
+		if (err < 0)
+			return err;
+	}
+	err = add_pcm1796_controls(chip);
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+static const struct oxygen_model model_xonar_d2 = {
+	.longname = "Asus Virtuoso 200",
+	.chip = "AV200",
+	.init = xonar_d2_init,
+	.control_filter = xonar_d2_control_filter,
+	.mixer_init = xonar_d2_mixer_init,
+	.cleanup = xonar_d2_cleanup,
+	.suspend = xonar_d2_suspend,
+	.resume = xonar_d2_resume,
+	.get_i2s_mclk = get_pcm1796_i2s_mclk,
+	.set_dac_params = set_pcm1796_params,
+	.set_adc_params = xonar_set_cs53x1_params,
+	.update_dac_volume = update_pcm1796_volume,
+	.update_dac_mute = update_pcm1796_mute,
+	.dac_tlv = pcm1796_db_scale,
+	.model_data_size = sizeof(struct xonar_pcm179x),
+	.device_config = PLAYBACK_0_TO_I2S |
+			 PLAYBACK_1_TO_SPDIF |
+			 CAPTURE_0_FROM_I2S_2 |
+			 CAPTURE_1_FROM_SPDIF |
+			 MIDI_OUTPUT |
+			 MIDI_INPUT,
+	.dac_channels = 8,
+	.dac_volume_min = 255 - 2*60,
+	.dac_volume_max = 255,
+	.misc_flags = OXYGEN_MISC_MIDI,
+	.function_flags = OXYGEN_FUNCTION_SPI |
+			  OXYGEN_FUNCTION_ENABLE_SPI_4_5,
+	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+};
+
+static const struct oxygen_model model_xonar_hdav = {
+	.longname = "Asus Virtuoso 200",
+	.chip = "AV200",
+	.init = xonar_hdav_init,
+	.mixer_init = xonar_hdav_mixer_init,
+	.cleanup = xonar_hdav_cleanup,
+	.suspend = xonar_hdav_suspend,
+	.resume = xonar_hdav_resume,
+	.pcm_hardware_filter = xonar_hdmi_pcm_hardware_filter,
+	.get_i2s_mclk = get_pcm1796_i2s_mclk,
+	.set_dac_params = set_hdav_params,
+	.set_adc_params = xonar_set_cs53x1_params,
+	.update_dac_volume = update_pcm1796_volume,
+	.update_dac_mute = update_pcm1796_mute,
+	.uart_input = xonar_hdmi_uart_input,
+	.ac97_switch = xonar_line_mic_ac97_switch,
+	.dac_tlv = pcm1796_db_scale,
+	.model_data_size = sizeof(struct xonar_hdav),
+	.device_config = PLAYBACK_0_TO_I2S |
+			 PLAYBACK_1_TO_SPDIF |
+			 CAPTURE_0_FROM_I2S_2 |
+			 CAPTURE_1_FROM_SPDIF,
+	.dac_channels = 8,
+	.dac_volume_min = 255 - 2*60,
+	.dac_volume_max = 255,
+	.misc_flags = OXYGEN_MISC_MIDI,
+	.function_flags = OXYGEN_FUNCTION_2WIRE,
+	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+};
+
+static const struct oxygen_model model_xonar_st = {
+	.longname = "Asus Virtuoso 100",
+	.chip = "AV200",
+	.init = xonar_st_init,
+	.control_filter = xonar_st_control_filter,
+	.mixer_init = xonar_st_mixer_init,
+	.cleanup = xonar_st_cleanup,
+	.suspend = xonar_st_suspend,
+	.resume = xonar_st_resume,
+	.get_i2s_mclk = get_pcm1796_i2s_mclk,
+	.set_dac_params = set_st_params,
+	.set_adc_params = xonar_set_cs53x1_params,
+	.update_dac_volume = update_pcm1796_volume,
+	.update_dac_mute = update_pcm1796_mute,
+	.ac97_switch = xonar_line_mic_ac97_switch,
+	.dac_tlv = pcm1796_db_scale,
+	.model_data_size = sizeof(struct xonar_pcm179x),
+	.device_config = PLAYBACK_0_TO_I2S |
+			 PLAYBACK_1_TO_SPDIF |
+			 CAPTURE_0_FROM_I2S_2,
+	.dac_channels = 2,
+	.dac_volume_min = 255 - 2*60,
+	.dac_volume_max = 255,
+	.function_flags = OXYGEN_FUNCTION_2WIRE,
+	.dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+	.adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+};
+
+int __devinit get_xonar_pcm179x_model(struct oxygen *chip,
+				      const struct pci_device_id *id)
+{
+	switch (id->subdevice) {
+	case 0x8269:
+		chip->model = model_xonar_d2;
+		chip->model.shortname = "Xonar D2";
+		break;
+	case 0x82b7:
+		chip->model = model_xonar_d2;
+		chip->model.shortname = "Xonar D2X";
+		chip->model.init = xonar_d2x_init;
+		break;
+	case 0x8314:
+		chip->model = model_xonar_hdav;
+		oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK);
+		switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DB_MASK) {
+		default:
+			chip->model.shortname = "Xonar HDAV1.3";
+			break;
+		case GPIO_DB_H6:
+			chip->model.shortname = "Xonar HDAV1.3+H6";
+			chip->model.private_data = 1;
+			break;
+		}
+		break;
+	case 0x835d:
+		chip->model = model_xonar_st;
+		oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK);
+		switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DB_MASK) {
+		default:
+			chip->model.shortname = "Xonar ST";
+			break;
+		case GPIO_DB_H6:
+			chip->model.shortname = "Xonar ST+H6";
+			chip->model.dac_channels = 8;
+			chip->model.private_data = 1;
+			break;
+		}
+		break;
+	case 0x835c:
+		chip->model = model_xonar_st;
+		chip->model.shortname = "Xonar STX";
+		chip->model.init = xonar_stx_init;
+		chip->model.resume = xonar_stx_resume;
+		chip->model.set_dac_params = set_pcm1796_params;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff