浏览代码

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6: (377 commits)
  ASoC: au1x: PSC-AC97 bugfixes
  ALSA: dummy - Increase MAX_PCM_SUBSTREAMS to 128
  ALSA: dummy - Add debug proc file
  ALSA: Add const prefix to proc helper functions
  ALSA: Re-export snd_pcm_format_name() function
  ALSA: hda - Use auto model for HP laptops with ALC268 codec
  ALSA: cs46xx - Fix minimum period size
  ASoC: Fix WM835x Out4 capture enumeration
  ALSA: Remove unneeded ifdef from sound/core.h
  ALSA: Remove struct snd_monitor_file from public sound/core.h
  ASoC: Remove unuused hw_read_t
  sound: oxygen: work around MCE when changing volume
  ALSA: dummy - Fake buffer allocations
  ALSA: hda/realtek: Added support for CLEVO M540R subsystem, 6 channel + digital
  ASoC: fix pxa2xx-ac97.c breakage
  ALSA: dummy - Fix the timer calculation in systimer mode
  ALSA: dummy - Add more description
  ALSA: dummy - Better jiffies handling
  ALSA: dummy - Support high-res timer mode
  ALSA: Release v1.0.21
  ...
Linus Torvalds 16 年之前
父节点
当前提交
a9c86d4259
共有 100 个文件被更改,包括 7896 次插入4389 次删除
  1. 24 0
      Documentation/feature-removal-schedule.txt
  2. 30 0
      Documentation/sound/alsa/ALSA-Configuration.txt
  3. 24 9
      Documentation/sound/alsa/HD-Audio-Models.txt
  4. 64 0
      Documentation/sound/alsa/HD-Audio.txt
  5. 5 0
      arch/arm/mach-omap2/mcbsp.c
  6. 3 0
      arch/arm/mach-pxa/include/mach/audio.h
  7. 10 0
      arch/arm/plat-omap/dma.c
  8. 49 2
      arch/arm/plat-omap/include/mach/mcbsp.h
  9. 382 19
      arch/arm/plat-omap/mcbsp.c
  10. 37 0
      arch/arm/plat-s3c/include/plat/audio-simtec.h
  11. 5 0
      arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h
  12. 26 13
      fs/char_dev.c
  13. 16 3
      include/linux/fs.h
  14. 3 1
      include/linux/tty.h
  15. 9 0
      include/sound/ac97_codec.h
  16. 1 1
      include/sound/asound.h
  17. 14 37
      include/sound/core.h
  18. 2 2
      include/sound/info.h
  19. 6 0
      include/sound/memalloc.h
  20. 25 0
      include/sound/pcm.h
  21. 83 0
      include/sound/sh_fsi.h
  22. 24 16
      include/sound/soc-dai.h
  23. 10 0
      include/sound/soc-dapm.h
  24. 43 6
      include/sound/soc.h
  25. 14 0
      include/sound/tlv.h
  26. 22 0
      include/sound/uda1380.h
  27. 1 1
      include/sound/version.h
  28. 44 0
      include/sound/wm8993.h
  29. 1 0
      include/sound/ymfpci.h
  30. 28 0
      sound/Kconfig
  31. 10 0
      sound/arm/pxa2xx-ac97.c
  32. 3 0
      sound/arm/pxa2xx-pcm-lib.c
  33. 4 0
      sound/core/Kconfig
  34. 1 1
      sound/core/Makefile
  35. 17 17
      sound/core/control.c
  36. 3 5
      sound/core/info.c
  37. 8 0
      sound/core/init.c
  38. 4 0
      sound/core/memalloc.c
  39. 48 27
      sound/core/misc.c
  40. 2 1
      sound/core/oss/mixer_oss.c
  41. 9 3
      sound/core/oss/pcm_oss.c
  42. 14 12
      sound/core/pcm.c
  43. 8 4
      sound/core/pcm_lib.c
  44. 2 0
      sound/core/pcm_memory.c
  45. 41 23
      sound/core/pcm_native.c
  46. 1 1
      sound/core/rawmidi.c
  47. 8 6
      sound/core/seq/oss/seq_oss_midi.c
  48. 3 4
      sound/core/seq/seq_midi.c
  49. 6 2
      sound/core/vmaster.c
  50. 537 163
      sound/drivers/dummy.c
  51. 52 34
      sound/isa/cmi8330.c
  52. 4 3
      sound/oss/midibuf.c
  53. 3 3
      sound/oss/vwsnd.c
  54. 2 2
      sound/pci/Kconfig
  55. 0 65
      sound/pci/ali5451/ali5451.c
  56. 617 499
      sound/pci/azt3328.c
  57. 51 52
      sound/pci/azt3328.h
  58. 1 1
      sound/pci/cs46xx/cs46xx_lib.h
  59. 6 3
      sound/pci/ctxfi/ct20k2reg.h
  60. 10 10
      sound/pci/ctxfi/ctamixer.c
  61. 38 39
      sound/pci/ctxfi/ctatc.c
  62. 15 15
      sound/pci/ctxfi/ctdaio.c
  63. 11 11
      sound/pci/ctxfi/cthw20k1.c
  64. 58 15
      sound/pci/ctxfi/cthw20k2.c
  65. 4 4
      sound/pci/ctxfi/ctmixer.c
  66. 3 3
      sound/pci/ctxfi/ctpcm.c
  67. 2 2
      sound/pci/ctxfi/ctresource.c
  68. 5 5
      sound/pci/ctxfi/ctsrc.c
  69. 3 3
      sound/pci/ctxfi/ctvmem.c
  70. 27 0
      sound/pci/hda/Kconfig
  71. 4 0
      sound/pci/hda/Makefile
  72. 4 0
      sound/pci/hda/hda_beep.c
  73. 46 22
      sound/pci/hda/hda_codec.c
  74. 9 1
      sound/pci/hda/hda_codec.h
  75. 11 7
      sound/pci/hda/hda_generic.c
  76. 217 19
      sound/pci/hda/hda_hwdep.c
  77. 66 8
      sound/pci/hda/hda_intel.c
  78. 13 1
      sound/pci/hda/hda_local.h
  79. 2 5
      sound/pci/hda/hda_proc.c
  80. 90 41
      sound/pci/hda/patch_analog.c
  81. 1 2
      sound/pci/hda/patch_atihdmi.c
  82. 1 2
      sound/pci/hda/patch_ca0110.c
  83. 1194 0
      sound/pci/hda/patch_cirrus.c
  84. 2 1
      sound/pci/hda/patch_cmedia.c
  85. 479 0
      sound/pci/hda/patch_conexant.c
  86. 53 51
      sound/pci/hda/patch_intelhdmi.c
  87. 2 0
      sound/pci/hda/patch_nvhdmi.c
  88. 2172 2248
      sound/pci/hda/patch_realtek.c
  89. 574 632
      sound/pci/hda/patch_sigmatel.c
  90. 1 2
      sound/pci/hda/patch_via.c
  91. 9 0
      sound/pci/ice1712/ice1712.h
  92. 102 10
      sound/pci/ice1712/ice1724.c
  93. 36 10
      sound/pci/ice1712/prodigy_hifi.c
  94. 1 10
      sound/pci/oxygen/oxygen_io.c
  95. 22 17
      sound/pci/rme9652/hdsp.c
  96. 15 5
      sound/pci/ymfpci/ymfpci_main.c
  97. 1 0
      sound/soc/Kconfig
  98. 2 1
      sound/soc/Makefile
  99. 25 113
      sound/soc/atmel/sam9g20_wm8731.c
  100. 96 33
      sound/soc/au1x/psc-ac97.c

+ 24 - 0
Documentation/feature-removal-schedule.txt

@@ -468,3 +468,27 @@ Why:	cpu_policy_rwsem has a new cleaner definition making it local to
 	cpufreq core and contained inside cpufreq.c. Other dependent
 	cpufreq core and contained inside cpufreq.c. Other dependent
 	drivers should not use it in order to safely avoid lockdep issues.
 	drivers should not use it in order to safely avoid lockdep issues.
 Who:	Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
 Who:	Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
+
+----------------------------
+
+What:	sound-slot/service-* module aliases and related clutters in
+	sound/sound_core.c
+When:	August 2010
+Why:	OSS sound_core grabs all legacy minors (0-255) of SOUND_MAJOR
+	(14) and requests modules using custom sound-slot/service-*
+	module aliases.  The only benefit of doing this is allowing
+	use of custom module aliases which might as well be considered
+	a bug at this point.  This preemptive claiming prevents
+	alternative OSS implementations.
+
+	Till the feature is removed, the kernel will be requesting
+	both sound-slot/service-* and the standard char-major-* module
+	aliases and allow turning off the pre-claiming selectively via
+	CONFIG_SOUND_OSS_CORE_PRECLAIM and soundcore.preclaim_oss
+	kernel parameter.
+
+	After the transition phase is complete, both the custom module
+	aliases and switches to disable it will go away.  This removal
+	will also allow making ALSA OSS emulation independent of
+	sound_core.  The dependency will be broken then too.
+Who:	Tejun Heo <tj@kernel.org>

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

@@ -60,6 +60,12 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     slots	- Reserve the slot index for the given driver.
     slots	- Reserve the slot index for the given driver.
 		  This option takes multiple strings.		
 		  This option takes multiple strings.		
 		  See "Module Autoloading Support" section for details.
 		  See "Module Autoloading Support" section for details.
+    debug	- Specifies the debug message level
+		  (0 = disable debug prints, 1 = normal debug messages,
+		   2 = verbose debug messages)
+		  This option appears only when CONFIG_SND_DEBUG=y.
+		  This option can be dynamically changed via sysfs
+		  /sys/modules/snd/parameters/debug file.
   
   
   Module snd-pcm-oss
   Module snd-pcm-oss
   ------------------
   ------------------
@@ -513,6 +519,26 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     or input, but you may use this module for any application which
     or input, but you may use this module for any application which
     requires a sound card (like RealPlayer).
     requires a sound card (like RealPlayer).
 
 
+    pcm_devs       - Number of PCM devices assigned to each card
+                     (default = 1, up to 4)
+    pcm_substreams - Number of PCM substreams assigned to each PCM
+                     (default = 8, up to 16)
+    hrtimer        - Use hrtimer (=1, default) or system timer (=0)
+    fake_buffer    - Fake buffer allocations (default = 1)
+
+    When multiple PCM devices are created, snd-dummy gives different
+    behavior to each PCM device:
+      0 = interleaved with mmap support
+      1 = non-interleaved with mmap support
+      2 = interleaved without mmap 
+      3 = non-interleaved without mmap
+
+    As default, snd-dummy drivers doesn't allocate the real buffers
+    but either ignores read/write or mmap a single dummy page to all
+    buffer pages, in order to save the resouces.  If your apps need
+    the read/ written buffer data to be consistent, pass fake_buffer=0
+    option.
+
     The power-management is supported.
     The power-management is supported.
 
 
   Module snd-echo3g
   Module snd-echo3g
@@ -768,6 +794,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     bdl_pos_adj	- Specifies the DMA IRQ timing delay in samples.
     bdl_pos_adj	- Specifies the DMA IRQ timing delay in samples.
 		Passing -1 will make the driver to choose the appropriate
 		Passing -1 will make the driver to choose the appropriate
 		value based on the controller chip.
 		value based on the controller chip.
+    patch	- Specifies the early "patch" files to modify the HD-audio
+    		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.
     
     
     [Single (global) options]
     [Single (global) options]
     single_cmd  - Use single immediate commands to communicate with
     single_cmd  - Use single immediate commands to communicate with

+ 24 - 9
Documentation/sound/alsa/HD-Audio-Models.txt

@@ -114,8 +114,8 @@ ALC662/663/272
   samsung-nc10	Samsung NC10 mini notebook
   samsung-nc10	Samsung NC10 mini notebook
   auto		auto-config reading BIOS (default)
   auto		auto-config reading BIOS (default)
 
 
-ALC882/885
-==========
+ALC882/883/885/888/889
+======================
   3stack-dig	3-jack with SPDIF I/O
   3stack-dig	3-jack with SPDIF I/O
   6stack-dig	6-jack digital with SPDIF I/O
   6stack-dig	6-jack digital with SPDIF I/O
   arima		Arima W820Di1
   arima		Arima W820Di1
@@ -127,12 +127,8 @@ ALC882/885
   mbp3		Macbook Pro rev3
   mbp3		Macbook Pro rev3
   imac24	iMac 24'' with jack detection
   imac24	iMac 24'' with jack detection
   w2jc		ASUS W2JC
   w2jc		ASUS W2JC
-  auto		auto-config reading BIOS (default)
-
-ALC883/888
-==========
-  3stack-dig	3-jack with SPDIF I/O
-  6stack-dig	6-jack digital with SPDIF I/O
+  3stack-2ch-dig	3-jack with SPDIF I/O (ALC883)
+  alc883-6stack-dig	6-jack digital with SPDIF I/O (ALC883)
   3stack-6ch    3-jack 6-channel
   3stack-6ch    3-jack 6-channel
   3stack-6ch-dig 3-jack 6-channel with SPDIF I/O
   3stack-6ch-dig 3-jack 6-channel with SPDIF I/O
   6stack-dig-demo  6-jack digital for Intel demo board
   6stack-dig-demo  6-jack digital for Intel demo board
@@ -140,6 +136,7 @@ ALC883/888
   acer-aspire	Acer Aspire 9810
   acer-aspire	Acer Aspire 9810
   acer-aspire-4930g Acer Aspire 4930G
   acer-aspire-4930g Acer Aspire 4930G
   acer-aspire-6530g Acer Aspire 6530G
   acer-aspire-6530g Acer Aspire 6530G
+  acer-aspire-7730g Acer Aspire 7730G
   acer-aspire-8930g Acer Aspire 8930G
   acer-aspire-8930g Acer Aspire 8930G
   medion	Medion Laptops
   medion	Medion Laptops
   medion-md2	Medion MD2
   medion-md2	Medion MD2
@@ -155,10 +152,13 @@ ALC883/888
   3stack-hp	HP machines with 3stack (Lucknow, Samba boards)
   3stack-hp	HP machines with 3stack (Lucknow, Samba boards)
   6stack-dell	Dell machines with 6stack (Inspiron 530)
   6stack-dell	Dell machines with 6stack (Inspiron 530)
   mitac		Mitac 8252D
   mitac		Mitac 8252D
+  clevo-m540r	Clevo M540R (6ch + digital)
   clevo-m720	Clevo M720 laptop series
   clevo-m720	Clevo M720 laptop series
   fujitsu-pi2515 Fujitsu AMILO Pi2515
   fujitsu-pi2515 Fujitsu AMILO Pi2515
   fujitsu-xa3530 Fujitsu AMILO XA3530
   fujitsu-xa3530 Fujitsu AMILO XA3530
   3stack-6ch-intel Intel DG33* boards
   3stack-6ch-intel Intel DG33* boards
+  intel-alc889a	Intel IbexPeak with ALC889A
+  intel-x58	Intel DX58 with ALC889
   asus-p5q	ASUS P5Q-EM boards
   asus-p5q	ASUS P5Q-EM boards
   mb31		MacBook 3,1
   mb31		MacBook 3,1
   sony-vaio-tt  Sony VAIO TT
   sony-vaio-tt  Sony VAIO TT
@@ -229,7 +229,7 @@ AD1984
 ======
 ======
   basic		default configuration
   basic		default configuration
   thinkpad	Lenovo Thinkpad T61/X61
   thinkpad	Lenovo Thinkpad T61/X61
-  dell		Dell T3400
+  dell_desktop	Dell T3400
 
 
 AD1986A
 AD1986A
 =======
 =======
@@ -258,6 +258,7 @@ Conexant 5045
   laptop-micsense   Laptop with Mic sense (old model fujitsu)
   laptop-micsense   Laptop with Mic sense (old model fujitsu)
   laptop-hpmicsense Laptop with HP and Mic senses
   laptop-hpmicsense Laptop with HP and Mic senses
   benq		Benq R55E
   benq		Benq R55E
+  laptop-hp530	HP 530 laptop
   test		for testing/debugging purpose, almost all controls
   test		for testing/debugging purpose, almost all controls
 		can be adjusted.  Appearing only when compiled with
 		can be adjusted.  Appearing only when compiled with
 		$CONFIG_SND_DEBUG=y
 		$CONFIG_SND_DEBUG=y
@@ -278,9 +279,16 @@ Conexant 5051
   hp-dv6736	HP dv6736
   hp-dv6736	HP dv6736
   lenovo-x200	Lenovo X200 laptop
   lenovo-x200	Lenovo X200 laptop
 
 
+Conexant 5066
+=============
+  laptop	Basic Laptop config (default)
+  dell-laptop	Dell laptops
+  olpc-xo-1_5	OLPC XO 1.5
+
 STAC9200
 STAC9200
 ========
 ========
   ref		Reference board
   ref		Reference board
+  oqo		OQO Model 2
   dell-d21	Dell (unknown)
   dell-d21	Dell (unknown)
   dell-d22	Dell (unknown)
   dell-d22	Dell (unknown)
   dell-d23	Dell (unknown)
   dell-d23	Dell (unknown)
@@ -368,10 +376,12 @@ STAC92HD73*
 ===========
 ===========
   ref		Reference board
   ref		Reference board
   no-jd		BIOS setup but without jack-detection
   no-jd		BIOS setup but without jack-detection
+  intel		Intel DG45* mobos
   dell-m6-amic	Dell desktops/laptops with analog mics
   dell-m6-amic	Dell desktops/laptops with analog mics
   dell-m6-dmic	Dell desktops/laptops with digital mics
   dell-m6-dmic	Dell desktops/laptops with digital mics
   dell-m6	Dell desktops/laptops with both type of mics
   dell-m6	Dell desktops/laptops with both type of mics
   dell-eq	Dell desktops/laptops
   dell-eq	Dell desktops/laptops
+  alienware	Alienware M17x
   auto		BIOS setup (default)
   auto		BIOS setup (default)
 
 
 STAC92HD83*
 STAC92HD83*
@@ -385,3 +395,8 @@ STAC9872
 ========
 ========
   vaio		VAIO laptop without SPDIF
   vaio		VAIO laptop without SPDIF
   auto		BIOS setup (default)
   auto		BIOS setup (default)
+
+Cirrus Logic CS4206/4207
+========================
+  mbp55		MacBook Pro 5,5
+  auto		BIOS setup (default)

+ 64 - 0
Documentation/sound/alsa/HD-Audio.txt

@@ -138,6 +138,10 @@ override the BIOS setup or to provide more comprehensive features.
 The driver checks PCI SSID and looks through the static configuration
 The driver checks PCI SSID and looks through the static configuration
 table until any matching entry is found.  If you have a new machine,
 table until any matching entry is found.  If you have a new machine,
 you may see a message like below:
 you may see a message like below:
+------------------------------------------------------------------------
+    hda_codec: ALC880: BIOS auto-probing.
+------------------------------------------------------------------------
+Meanwhile, in the earlier versions, you would see a message like:
 ------------------------------------------------------------------------
 ------------------------------------------------------------------------
     hda_codec: Unknown model for ALC880, trying auto-probe from BIOS...
     hda_codec: Unknown model for ALC880, trying auto-probe from BIOS...
 ------------------------------------------------------------------------
 ------------------------------------------------------------------------
@@ -403,6 +407,66 @@ re-configure based on that state, run like below:
 ------------------------------------------------------------------------
 ------------------------------------------------------------------------
 
 
 
 
+Early Patching
+~~~~~~~~~~~~~~
+When CONFIG_SND_HDA_PATCH_LOADER=y is set, you can pass a "patch" as a
+firmware file for modifying the HD-audio setup before initializing the
+codec.  This can work basically like the reconfiguration via sysfs in
+the above, but it does it before the first codec configuration.
+
+A patch file is a plain text file which looks like below:
+
+------------------------------------------------------------------------
+  [codec]
+  0x12345678 0xabcd1234 2
+
+  [model]
+  auto
+
+  [pincfg]
+  0x12 0x411111f0
+
+  [verb]
+  0x20 0x500 0x03
+  0x20 0x400 0xff
+
+  [hint]
+  hp_detect = yes
+------------------------------------------------------------------------
+
+The file needs to have a line `[codec]`.  The next line should contain
+three numbers indicating the codec vendor-id (0x12345678 in the
+example), the codec subsystem-id (0xabcd1234) and the address (2) of
+the codec.  The rest patch entries are applied to this specified codec
+until another codec entry is given.
+
+The `[model]` line allows to change the model name of the each codec.
+In the example above, it will be changed to model=auto.
+Note that this overrides the module option.
+
+After the `[pincfg]` line, the contents are parsed as the initial
+default pin-configurations just like `user_pin_configs` sysfs above.
+The values can be shown in user_pin_configs sysfs file, too.
+
+Similarly, the lines after `[verb]` are parsed as `init_verbs`
+sysfs entries, and the lines after `[hint]` are parsed as `hints`
+sysfs entries, respectively.
+
+The hd-audio driver reads the file via request_firmware().  Thus,
+a patch file has to be located on the appropriate firmware path,
+typically, /lib/firmware.  For example, when you pass the option
+`patch=hda-init.fw`, the file /lib/firmware/hda-init-fw must be
+present.
+
+The patch module option is specific to each card instance, and you
+need to give one file name for each instance, separated by commas.
+For example, if you have two cards, one for an on-board analog and one 
+for an HDMI video board, you may pass patch option like below:
+------------------------------------------------------------------------
+    options snd-hda-intel patch=on-board-patch,hdmi-patch
+------------------------------------------------------------------------
+
+
 Power-Saving
 Power-Saving
 ~~~~~~~~~~~~
 ~~~~~~~~~~~~
 The power-saving is a kind of auto-suspend of the device.  When the
 The power-saving is a kind of auto-suspend of the device.  When the

+ 5 - 0
arch/arm/mach-omap2/mcbsp.c

@@ -128,6 +128,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
 		.rx_irq		= INT_24XX_MCBSP1_IRQ_RX,
 		.rx_irq		= INT_24XX_MCBSP1_IRQ_RX,
 		.tx_irq		= INT_24XX_MCBSP1_IRQ_TX,
 		.tx_irq		= INT_24XX_MCBSP1_IRQ_TX,
 		.ops		= &omap2_mcbsp_ops,
 		.ops		= &omap2_mcbsp_ops,
+		.buffer_size	= 0x6F,
 	},
 	},
 	{
 	{
 		.phys_base	= OMAP34XX_MCBSP2_BASE,
 		.phys_base	= OMAP34XX_MCBSP2_BASE,
@@ -136,6 +137,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
 		.rx_irq		= INT_24XX_MCBSP2_IRQ_RX,
 		.rx_irq		= INT_24XX_MCBSP2_IRQ_RX,
 		.tx_irq		= INT_24XX_MCBSP2_IRQ_TX,
 		.tx_irq		= INT_24XX_MCBSP2_IRQ_TX,
 		.ops		= &omap2_mcbsp_ops,
 		.ops		= &omap2_mcbsp_ops,
+		.buffer_size	= 0x3FF,
 	},
 	},
 	{
 	{
 		.phys_base	= OMAP34XX_MCBSP3_BASE,
 		.phys_base	= OMAP34XX_MCBSP3_BASE,
@@ -144,6 +146,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
 		.rx_irq		= INT_24XX_MCBSP3_IRQ_RX,
 		.rx_irq		= INT_24XX_MCBSP3_IRQ_RX,
 		.tx_irq		= INT_24XX_MCBSP3_IRQ_TX,
 		.tx_irq		= INT_24XX_MCBSP3_IRQ_TX,
 		.ops		= &omap2_mcbsp_ops,
 		.ops		= &omap2_mcbsp_ops,
+		.buffer_size	= 0x6F,
 	},
 	},
 	{
 	{
 		.phys_base	= OMAP34XX_MCBSP4_BASE,
 		.phys_base	= OMAP34XX_MCBSP4_BASE,
@@ -152,6 +155,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
 		.rx_irq		= INT_24XX_MCBSP4_IRQ_RX,
 		.rx_irq		= INT_24XX_MCBSP4_IRQ_RX,
 		.tx_irq		= INT_24XX_MCBSP4_IRQ_TX,
 		.tx_irq		= INT_24XX_MCBSP4_IRQ_TX,
 		.ops		= &omap2_mcbsp_ops,
 		.ops		= &omap2_mcbsp_ops,
+		.buffer_size	= 0x6F,
 	},
 	},
 	{
 	{
 		.phys_base	= OMAP34XX_MCBSP5_BASE,
 		.phys_base	= OMAP34XX_MCBSP5_BASE,
@@ -160,6 +164,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
 		.rx_irq		= INT_24XX_MCBSP5_IRQ_RX,
 		.rx_irq		= INT_24XX_MCBSP5_IRQ_RX,
 		.tx_irq		= INT_24XX_MCBSP5_IRQ_TX,
 		.tx_irq		= INT_24XX_MCBSP5_IRQ_TX,
 		.ops		= &omap2_mcbsp_ops,
 		.ops		= &omap2_mcbsp_ops,
+		.buffer_size	= 0x6F,
 	},
 	},
 };
 };
 #define OMAP34XX_MCBSP_PDATA_SZ		ARRAY_SIZE(omap34xx_mcbsp_pdata)
 #define OMAP34XX_MCBSP_PDATA_SZ		ARRAY_SIZE(omap34xx_mcbsp_pdata)

+ 3 - 0
arch/arm/mach-pxa/include/mach/audio.h

@@ -3,10 +3,12 @@
 
 
 #include <sound/core.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm.h>
+#include <sound/ac97_codec.h>
 
 
 /*
 /*
  * @reset_gpio: AC97 reset gpio (normally gpio113 or gpio95)
  * @reset_gpio: AC97 reset gpio (normally gpio113 or gpio95)
  *              a -1 value means no gpio will be used for reset
  *              a -1 value means no gpio will be used for reset
+ * @codec_pdata: AC97 codec platform_data
 
 
  * reset_gpio should only be specified for pxa27x CPUs where a silicon
  * reset_gpio should only be specified for pxa27x CPUs where a silicon
  * bug prevents correct operation of the reset line. If not specified,
  * bug prevents correct operation of the reset line. If not specified,
@@ -20,6 +22,7 @@ typedef struct {
 	void (*resume)(void *);
 	void (*resume)(void *);
 	void *priv;
 	void *priv;
 	int reset_gpio;
 	int reset_gpio;
+	void *codec_pdata[AC97_BUS_MAX_DEVICES];
 } pxa2xx_audio_ops_t;
 } pxa2xx_audio_ops_t;
 
 
 extern void pxa_set_ac97_info(pxa2xx_audio_ops_t *ops);
 extern void pxa_set_ac97_info(pxa2xx_audio_ops_t *ops);

+ 10 - 0
arch/arm/plat-omap/dma.c

@@ -1127,6 +1127,11 @@ int omap_dma_running(void)
 void omap_dma_link_lch(int lch_head, int lch_queue)
 void omap_dma_link_lch(int lch_head, int lch_queue)
 {
 {
 	if (omap_dma_in_1510_mode()) {
 	if (omap_dma_in_1510_mode()) {
+		if (lch_head == lch_queue) {
+			dma_write(dma_read(CCR(lch_head)) | (3 << 8),
+								CCR(lch_head));
+			return;
+		}
 		printk(KERN_ERR "DMA linking is not supported in 1510 mode\n");
 		printk(KERN_ERR "DMA linking is not supported in 1510 mode\n");
 		BUG();
 		BUG();
 		return;
 		return;
@@ -1149,6 +1154,11 @@ EXPORT_SYMBOL(omap_dma_link_lch);
 void omap_dma_unlink_lch(int lch_head, int lch_queue)
 void omap_dma_unlink_lch(int lch_head, int lch_queue)
 {
 {
 	if (omap_dma_in_1510_mode()) {
 	if (omap_dma_in_1510_mode()) {
+		if (lch_head == lch_queue) {
+			dma_write(dma_read(CCR(lch_head)) & ~(3 << 8),
+								CCR(lch_head));
+			return;
+		}
 		printk(KERN_ERR "DMA linking is not supported in 1510 mode\n");
 		printk(KERN_ERR "DMA linking is not supported in 1510 mode\n");
 		BUG();
 		BUG();
 		return;
 		return;

+ 49 - 2
arch/arm/plat-omap/include/mach/mcbsp.h

@@ -134,6 +134,11 @@
 #define OMAP_MCBSP_REG_XCERG	0x74
 #define OMAP_MCBSP_REG_XCERG	0x74
 #define OMAP_MCBSP_REG_XCERH	0x78
 #define OMAP_MCBSP_REG_XCERH	0x78
 #define OMAP_MCBSP_REG_SYSCON	0x8C
 #define OMAP_MCBSP_REG_SYSCON	0x8C
+#define OMAP_MCBSP_REG_THRSH2	0x90
+#define OMAP_MCBSP_REG_THRSH1	0x94
+#define OMAP_MCBSP_REG_IRQST	0xA0
+#define OMAP_MCBSP_REG_IRQEN	0xA4
+#define OMAP_MCBSP_REG_WAKEUPEN	0xA8
 #define OMAP_MCBSP_REG_XCCR	0xAC
 #define OMAP_MCBSP_REG_XCCR	0xAC
 #define OMAP_MCBSP_REG_RCCR	0xB0
 #define OMAP_MCBSP_REG_RCCR	0xB0
 
 
@@ -249,8 +254,27 @@
 #define RDISABLE		0x0001
 #define RDISABLE		0x0001
 
 
 /********************** McBSP SYSCONFIG bit definitions ********************/
 /********************** McBSP SYSCONFIG bit definitions ********************/
+#define CLOCKACTIVITY(value)	((value)<<8)
+#define SIDLEMODE(value)	((value)<<3)
+#define ENAWAKEUP		0x0004
 #define SOFTRST			0x0002
 #define SOFTRST			0x0002
 
 
+/********************** McBSP DMA operating modes **************************/
+#define MCBSP_DMA_MODE_ELEMENT		0
+#define MCBSP_DMA_MODE_THRESHOLD	1
+#define MCBSP_DMA_MODE_FRAME		2
+
+/********************** McBSP WAKEUPEN bit definitions *********************/
+#define XEMPTYEOFEN		0x4000
+#define XRDYEN			0x0400
+#define XEOFEN			0x0200
+#define XFSXEN			0x0100
+#define XSYNCERREN		0x0080
+#define RRDYEN			0x0008
+#define REOFEN			0x0004
+#define RFSREN			0x0002
+#define RSYNCERREN		0x0001
+
 /* we don't do multichannel for now */
 /* we don't do multichannel for now */
 struct omap_mcbsp_reg_cfg {
 struct omap_mcbsp_reg_cfg {
 	u16 spcr2;
 	u16 spcr2;
@@ -344,6 +368,9 @@ struct omap_mcbsp_platform_data {
 	u8 dma_rx_sync, dma_tx_sync;
 	u8 dma_rx_sync, dma_tx_sync;
 	u16 rx_irq, tx_irq;
 	u16 rx_irq, tx_irq;
 	struct omap_mcbsp_ops *ops;
 	struct omap_mcbsp_ops *ops;
+#ifdef CONFIG_ARCH_OMAP34XX
+	u16 buffer_size;
+#endif
 };
 };
 
 
 struct omap_mcbsp {
 struct omap_mcbsp {
@@ -377,6 +404,11 @@ struct omap_mcbsp {
 	struct omap_mcbsp_platform_data *pdata;
 	struct omap_mcbsp_platform_data *pdata;
 	struct clk *iclk;
 	struct clk *iclk;
 	struct clk *fclk;
 	struct clk *fclk;
+#ifdef CONFIG_ARCH_OMAP34XX
+	int dma_op_mode;
+	u16 max_tx_thres;
+	u16 max_rx_thres;
+#endif
 };
 };
 extern struct omap_mcbsp **mcbsp_ptr;
 extern struct omap_mcbsp **mcbsp_ptr;
 extern int omap_mcbsp_count;
 extern int omap_mcbsp_count;
@@ -385,10 +417,25 @@ int omap_mcbsp_init(void);
 void omap_mcbsp_register_board_cfg(struct omap_mcbsp_platform_data *config,
 void omap_mcbsp_register_board_cfg(struct omap_mcbsp_platform_data *config,
 					int size);
 					int size);
 void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg * config);
 void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg * config);
+#ifdef CONFIG_ARCH_OMAP34XX
+void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold);
+void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold);
+u16 omap_mcbsp_get_max_tx_threshold(unsigned int id);
+u16 omap_mcbsp_get_max_rx_threshold(unsigned int id);
+int omap_mcbsp_get_dma_op_mode(unsigned int id);
+#else
+static inline void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold)
+{ }
+static inline void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold)
+{ }
+static inline u16 omap_mcbsp_get_max_tx_threshold(unsigned int id) { return 0; }
+static inline u16 omap_mcbsp_get_max_rx_threshold(unsigned int id) { return 0; }
+static inline int omap_mcbsp_get_dma_op_mode(unsigned int id) { return 0; }
+#endif
 int omap_mcbsp_request(unsigned int id);
 int omap_mcbsp_request(unsigned int id);
 void omap_mcbsp_free(unsigned int id);
 void omap_mcbsp_free(unsigned int id);
-void omap_mcbsp_start(unsigned int id);
-void omap_mcbsp_stop(unsigned int id);
+void omap_mcbsp_start(unsigned int id, int tx, int rx);
+void omap_mcbsp_stop(unsigned int id, int tx, int rx);
 void omap_mcbsp_xmit_word(unsigned int id, u32 word);
 void omap_mcbsp_xmit_word(unsigned int id, u32 word);
 u32 omap_mcbsp_recv_word(unsigned int id);
 u32 omap_mcbsp_recv_word(unsigned int id);
 
 

+ 382 - 19
arch/arm/plat-omap/mcbsp.c

@@ -198,6 +198,170 @@ void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg *config)
 }
 }
 EXPORT_SYMBOL(omap_mcbsp_config);
 EXPORT_SYMBOL(omap_mcbsp_config);
 
 
+#ifdef CONFIG_ARCH_OMAP34XX
+/*
+ * omap_mcbsp_set_tx_threshold configures how to deal
+ * with transmit threshold. the threshold value and handler can be
+ * configure in here.
+ */
+void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold)
+{
+	struct omap_mcbsp *mcbsp;
+	void __iomem *io_base;
+
+	if (!cpu_is_omap34xx())
+		return;
+
+	if (!omap_mcbsp_check_valid_id(id)) {
+		printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+		return;
+	}
+	mcbsp = id_to_mcbsp_ptr(id);
+	io_base = mcbsp->io_base;
+
+	OMAP_MCBSP_WRITE(io_base, THRSH2, threshold);
+}
+EXPORT_SYMBOL(omap_mcbsp_set_tx_threshold);
+
+/*
+ * omap_mcbsp_set_rx_threshold configures how to deal
+ * with receive threshold. the threshold value and handler can be
+ * configure in here.
+ */
+void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold)
+{
+	struct omap_mcbsp *mcbsp;
+	void __iomem *io_base;
+
+	if (!cpu_is_omap34xx())
+		return;
+
+	if (!omap_mcbsp_check_valid_id(id)) {
+		printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+		return;
+	}
+	mcbsp = id_to_mcbsp_ptr(id);
+	io_base = mcbsp->io_base;
+
+	OMAP_MCBSP_WRITE(io_base, THRSH1, threshold);
+}
+EXPORT_SYMBOL(omap_mcbsp_set_rx_threshold);
+
+/*
+ * omap_mcbsp_get_max_tx_thres just return the current configured
+ * maximum threshold for transmission
+ */
+u16 omap_mcbsp_get_max_tx_threshold(unsigned int id)
+{
+	struct omap_mcbsp *mcbsp;
+
+	if (!omap_mcbsp_check_valid_id(id)) {
+		printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+		return -ENODEV;
+	}
+	mcbsp = id_to_mcbsp_ptr(id);
+
+	return mcbsp->max_tx_thres;
+}
+EXPORT_SYMBOL(omap_mcbsp_get_max_tx_threshold);
+
+/*
+ * omap_mcbsp_get_max_rx_thres just return the current configured
+ * maximum threshold for reception
+ */
+u16 omap_mcbsp_get_max_rx_threshold(unsigned int id)
+{
+	struct omap_mcbsp *mcbsp;
+
+	if (!omap_mcbsp_check_valid_id(id)) {
+		printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+		return -ENODEV;
+	}
+	mcbsp = id_to_mcbsp_ptr(id);
+
+	return mcbsp->max_rx_thres;
+}
+EXPORT_SYMBOL(omap_mcbsp_get_max_rx_threshold);
+
+/*
+ * omap_mcbsp_get_dma_op_mode just return the current configured
+ * operating mode for the mcbsp channel
+ */
+int omap_mcbsp_get_dma_op_mode(unsigned int id)
+{
+	struct omap_mcbsp *mcbsp;
+	int dma_op_mode;
+
+	if (!omap_mcbsp_check_valid_id(id)) {
+		printk(KERN_ERR "%s: Invalid id (%u)\n", __func__, id + 1);
+		return -ENODEV;
+	}
+	mcbsp = id_to_mcbsp_ptr(id);
+
+	spin_lock_irq(&mcbsp->lock);
+	dma_op_mode = mcbsp->dma_op_mode;
+	spin_unlock_irq(&mcbsp->lock);
+
+	return dma_op_mode;
+}
+EXPORT_SYMBOL(omap_mcbsp_get_dma_op_mode);
+
+static inline void omap34xx_mcbsp_request(struct omap_mcbsp *mcbsp)
+{
+	/*
+	 * Enable wakup behavior, smart idle and all wakeups
+	 * REVISIT: some wakeups may be unnecessary
+	 */
+	if (cpu_is_omap34xx()) {
+		u16 syscon;
+
+		syscon = OMAP_MCBSP_READ(mcbsp->io_base, SYSCON);
+		syscon &= ~(ENAWAKEUP | SIDLEMODE(0x03) | CLOCKACTIVITY(0x03));
+
+		spin_lock_irq(&mcbsp->lock);
+		if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) {
+			syscon |= (ENAWAKEUP | SIDLEMODE(0x02) |
+					CLOCKACTIVITY(0x02));
+			OMAP_MCBSP_WRITE(mcbsp->io_base, WAKEUPEN,
+					XRDYEN | RRDYEN);
+		} else {
+			syscon |= SIDLEMODE(0x01);
+		}
+		spin_unlock_irq(&mcbsp->lock);
+
+		OMAP_MCBSP_WRITE(mcbsp->io_base, SYSCON, syscon);
+	}
+}
+
+static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp)
+{
+	/*
+	 * Disable wakup behavior, smart idle and all wakeups
+	 */
+	if (cpu_is_omap34xx()) {
+		u16 syscon;
+
+		syscon = OMAP_MCBSP_READ(mcbsp->io_base, SYSCON);
+		syscon &= ~(ENAWAKEUP | SIDLEMODE(0x03) | CLOCKACTIVITY(0x03));
+		/*
+		 * HW bug workaround - If no_idle mode is taken, we need to
+		 * go to smart_idle before going to always_idle, or the
+		 * device will not hit retention anymore.
+		 */
+		syscon |= SIDLEMODE(0x02);
+		OMAP_MCBSP_WRITE(mcbsp->io_base, SYSCON, syscon);
+
+		syscon &= ~(SIDLEMODE(0x03));
+		OMAP_MCBSP_WRITE(mcbsp->io_base, SYSCON, syscon);
+
+		OMAP_MCBSP_WRITE(mcbsp->io_base, WAKEUPEN, 0);
+	}
+}
+#else
+static inline void omap34xx_mcbsp_request(struct omap_mcbsp *mcbsp) {}
+static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp) {}
+#endif
+
 /*
 /*
  * We can choose between IRQ based or polled IO.
  * We can choose between IRQ based or polled IO.
  * This needs to be called before omap_mcbsp_request().
  * This needs to be called before omap_mcbsp_request().
@@ -257,6 +421,9 @@ int omap_mcbsp_request(unsigned int id)
 	clk_enable(mcbsp->iclk);
 	clk_enable(mcbsp->iclk);
 	clk_enable(mcbsp->fclk);
 	clk_enable(mcbsp->fclk);
 
 
+	/* Do procedure specific to omap34xx arch, if applicable */
+	omap34xx_mcbsp_request(mcbsp);
+
 	/*
 	/*
 	 * Make sure that transmitter, receiver and sample-rate generator are
 	 * Make sure that transmitter, receiver and sample-rate generator are
 	 * not running before activating IRQs.
 	 * not running before activating IRQs.
@@ -305,6 +472,9 @@ void omap_mcbsp_free(unsigned int id)
 	if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free)
 	if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free)
 		mcbsp->pdata->ops->free(id);
 		mcbsp->pdata->ops->free(id);
 
 
+	/* Do procedure specific to omap34xx arch, if applicable */
+	omap34xx_mcbsp_free(mcbsp);
+
 	clk_disable(mcbsp->fclk);
 	clk_disable(mcbsp->fclk);
 	clk_disable(mcbsp->iclk);
 	clk_disable(mcbsp->iclk);
 
 
@@ -328,14 +498,15 @@ void omap_mcbsp_free(unsigned int id)
 EXPORT_SYMBOL(omap_mcbsp_free);
 EXPORT_SYMBOL(omap_mcbsp_free);
 
 
 /*
 /*
- * Here we start the McBSP, by enabling the sample
- * generator, both transmitter and receivers,
- * and the frame sync.
+ * Here we start the McBSP, by enabling transmitter, receiver or both.
+ * If no transmitter or receiver is active prior calling, then sample-rate
+ * generator and frame sync are started.
  */
  */
-void omap_mcbsp_start(unsigned int id)
+void omap_mcbsp_start(unsigned int id, int tx, int rx)
 {
 {
 	struct omap_mcbsp *mcbsp;
 	struct omap_mcbsp *mcbsp;
 	void __iomem *io_base;
 	void __iomem *io_base;
+	int idle;
 	u16 w;
 	u16 w;
 
 
 	if (!omap_mcbsp_check_valid_id(id)) {
 	if (!omap_mcbsp_check_valid_id(id)) {
@@ -348,32 +519,58 @@ void omap_mcbsp_start(unsigned int id)
 	mcbsp->rx_word_length = (OMAP_MCBSP_READ(io_base, RCR1) >> 5) & 0x7;
 	mcbsp->rx_word_length = (OMAP_MCBSP_READ(io_base, RCR1) >> 5) & 0x7;
 	mcbsp->tx_word_length = (OMAP_MCBSP_READ(io_base, XCR1) >> 5) & 0x7;
 	mcbsp->tx_word_length = (OMAP_MCBSP_READ(io_base, XCR1) >> 5) & 0x7;
 
 
-	/* Start the sample generator */
-	w = OMAP_MCBSP_READ(io_base, SPCR2);
-	OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 6));
+	idle = !((OMAP_MCBSP_READ(io_base, SPCR2) |
+		  OMAP_MCBSP_READ(io_base, SPCR1)) & 1);
+
+	if (idle) {
+		/* Start the sample generator */
+		w = OMAP_MCBSP_READ(io_base, SPCR2);
+		OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 6));
+	}
 
 
 	/* Enable transmitter and receiver */
 	/* Enable transmitter and receiver */
+	tx &= 1;
 	w = OMAP_MCBSP_READ(io_base, SPCR2);
 	w = OMAP_MCBSP_READ(io_base, SPCR2);
-	OMAP_MCBSP_WRITE(io_base, SPCR2, w | 1);
+	OMAP_MCBSP_WRITE(io_base, SPCR2, w | tx);
 
 
+	rx &= 1;
 	w = OMAP_MCBSP_READ(io_base, SPCR1);
 	w = OMAP_MCBSP_READ(io_base, SPCR1);
-	OMAP_MCBSP_WRITE(io_base, SPCR1, w | 1);
+	OMAP_MCBSP_WRITE(io_base, SPCR1, w | rx);
 
 
-	udelay(100);
+	/*
+	 * Worst case: CLKSRG*2 = 8000khz: (1/8000) * 2 * 2 usec
+	 * REVISIT: 100us may give enough time for two CLKSRG, however
+	 * due to some unknown PM related, clock gating etc. reason it
+	 * is now at 500us.
+	 */
+	udelay(500);
 
 
-	/* Start frame sync */
-	w = OMAP_MCBSP_READ(io_base, SPCR2);
-	OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 7));
+	if (idle) {
+		/* Start frame sync */
+		w = OMAP_MCBSP_READ(io_base, SPCR2);
+		OMAP_MCBSP_WRITE(io_base, SPCR2, w | (1 << 7));
+	}
+
+	if (cpu_is_omap2430() || cpu_is_omap34xx()) {
+		/* Release the transmitter and receiver */
+		w = OMAP_MCBSP_READ(io_base, XCCR);
+		w &= ~(tx ? XDISABLE : 0);
+		OMAP_MCBSP_WRITE(io_base, XCCR, w);
+		w = OMAP_MCBSP_READ(io_base, RCCR);
+		w &= ~(rx ? RDISABLE : 0);
+		OMAP_MCBSP_WRITE(io_base, RCCR, w);
+	}
 
 
 	/* Dump McBSP Regs */
 	/* Dump McBSP Regs */
 	omap_mcbsp_dump_reg(id);
 	omap_mcbsp_dump_reg(id);
 }
 }
 EXPORT_SYMBOL(omap_mcbsp_start);
 EXPORT_SYMBOL(omap_mcbsp_start);
 
 
-void omap_mcbsp_stop(unsigned int id)
+void omap_mcbsp_stop(unsigned int id, int tx, int rx)
 {
 {
 	struct omap_mcbsp *mcbsp;
 	struct omap_mcbsp *mcbsp;
 	void __iomem *io_base;
 	void __iomem *io_base;
+	int idle;
 	u16 w;
 	u16 w;
 
 
 	if (!omap_mcbsp_check_valid_id(id)) {
 	if (!omap_mcbsp_check_valid_id(id)) {
@@ -385,16 +582,33 @@ void omap_mcbsp_stop(unsigned int id)
 	io_base = mcbsp->io_base;
 	io_base = mcbsp->io_base;
 
 
 	/* Reset transmitter */
 	/* Reset transmitter */
+	tx &= 1;
+	if (cpu_is_omap2430() || cpu_is_omap34xx()) {
+		w = OMAP_MCBSP_READ(io_base, XCCR);
+		w |= (tx ? XDISABLE : 0);
+		OMAP_MCBSP_WRITE(io_base, XCCR, w);
+	}
 	w = OMAP_MCBSP_READ(io_base, SPCR2);
 	w = OMAP_MCBSP_READ(io_base, SPCR2);
-	OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1));
+	OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~tx);
 
 
 	/* Reset receiver */
 	/* Reset receiver */
+	rx &= 1;
+	if (cpu_is_omap2430() || cpu_is_omap34xx()) {
+		w = OMAP_MCBSP_READ(io_base, RCCR);
+		w |= (tx ? RDISABLE : 0);
+		OMAP_MCBSP_WRITE(io_base, RCCR, w);
+	}
 	w = OMAP_MCBSP_READ(io_base, SPCR1);
 	w = OMAP_MCBSP_READ(io_base, SPCR1);
-	OMAP_MCBSP_WRITE(io_base, SPCR1, w & ~(1));
+	OMAP_MCBSP_WRITE(io_base, SPCR1, w & ~rx);
 
 
-	/* Reset the sample rate generator */
-	w = OMAP_MCBSP_READ(io_base, SPCR2);
-	OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1 << 6));
+	idle = !((OMAP_MCBSP_READ(io_base, SPCR2) |
+		  OMAP_MCBSP_READ(io_base, SPCR1)) & 1);
+
+	if (idle) {
+		/* Reset the sample rate generator */
+		w = OMAP_MCBSP_READ(io_base, SPCR2);
+		OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1 << 6));
+	}
 }
 }
 EXPORT_SYMBOL(omap_mcbsp_stop);
 EXPORT_SYMBOL(omap_mcbsp_stop);
 
 
@@ -883,6 +1097,149 @@ void omap_mcbsp_set_spi_mode(unsigned int id,
 }
 }
 EXPORT_SYMBOL(omap_mcbsp_set_spi_mode);
 EXPORT_SYMBOL(omap_mcbsp_set_spi_mode);
 
 
+#ifdef CONFIG_ARCH_OMAP34XX
+#define max_thres(m)			(mcbsp->pdata->buffer_size)
+#define valid_threshold(m, val)		((val) <= max_thres(m))
+#define THRESHOLD_PROP_BUILDER(prop)					\
+static ssize_t prop##_show(struct device *dev,				\
+			struct device_attribute *attr, char *buf)	\
+{									\
+	struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);		\
+									\
+	return sprintf(buf, "%u\n", mcbsp->prop);			\
+}									\
+									\
+static ssize_t prop##_store(struct device *dev,				\
+				struct device_attribute *attr,		\
+				const char *buf, size_t size)		\
+{									\
+	struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);		\
+	unsigned long val;						\
+	int status;							\
+									\
+	status = strict_strtoul(buf, 0, &val);				\
+	if (status)							\
+		return status;						\
+									\
+	if (!valid_threshold(mcbsp, val))				\
+		return -EDOM;						\
+									\
+	mcbsp->prop = val;						\
+	return size;							\
+}									\
+									\
+static DEVICE_ATTR(prop, 0644, prop##_show, prop##_store);
+
+THRESHOLD_PROP_BUILDER(max_tx_thres);
+THRESHOLD_PROP_BUILDER(max_rx_thres);
+
+static const char *dma_op_modes[] = {
+	"element", "threshold", "frame",
+};
+
+static ssize_t dma_op_mode_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
+	int dma_op_mode, i = 0;
+	ssize_t len = 0;
+	const char * const *s;
+
+	spin_lock_irq(&mcbsp->lock);
+	dma_op_mode = mcbsp->dma_op_mode;
+	spin_unlock_irq(&mcbsp->lock);
+
+	for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) {
+		if (dma_op_mode == i)
+			len += sprintf(buf + len, "[%s] ", *s);
+		else
+			len += sprintf(buf + len, "%s ", *s);
+	}
+	len += sprintf(buf + len, "\n");
+
+	return len;
+}
+
+static ssize_t dma_op_mode_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t size)
+{
+	struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
+	const char * const *s;
+	int i = 0;
+
+	for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++)
+		if (sysfs_streq(buf, *s))
+			break;
+
+	if (i == ARRAY_SIZE(dma_op_modes))
+		return -EINVAL;
+
+	spin_lock_irq(&mcbsp->lock);
+	if (!mcbsp->free) {
+		size = -EBUSY;
+		goto unlock;
+	}
+	mcbsp->dma_op_mode = i;
+
+unlock:
+	spin_unlock_irq(&mcbsp->lock);
+
+	return size;
+}
+
+static DEVICE_ATTR(dma_op_mode, 0644, dma_op_mode_show, dma_op_mode_store);
+
+static const struct attribute *additional_attrs[] = {
+	&dev_attr_max_tx_thres.attr,
+	&dev_attr_max_rx_thres.attr,
+	&dev_attr_dma_op_mode.attr,
+	NULL,
+};
+
+static const struct attribute_group additional_attr_group = {
+	.attrs = (struct attribute **)additional_attrs,
+};
+
+static inline int __devinit omap_additional_add(struct device *dev)
+{
+	return sysfs_create_group(&dev->kobj, &additional_attr_group);
+}
+
+static inline void __devexit omap_additional_remove(struct device *dev)
+{
+	sysfs_remove_group(&dev->kobj, &additional_attr_group);
+}
+
+static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp)
+{
+	mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT;
+	if (cpu_is_omap34xx()) {
+		mcbsp->max_tx_thres = max_thres(mcbsp);
+		mcbsp->max_rx_thres = max_thres(mcbsp);
+		/*
+		 * REVISIT: Set dmap_op_mode to THRESHOLD as default
+		 * for mcbsp2 instances.
+		 */
+		if (omap_additional_add(mcbsp->dev))
+			dev_warn(mcbsp->dev,
+				"Unable to create additional controls\n");
+	} else {
+		mcbsp->max_tx_thres = -EINVAL;
+		mcbsp->max_rx_thres = -EINVAL;
+	}
+}
+
+static inline void __devexit omap34xx_device_exit(struct omap_mcbsp *mcbsp)
+{
+	if (cpu_is_omap34xx())
+		omap_additional_remove(mcbsp->dev);
+}
+#else
+static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp) {}
+static inline void __devexit omap34xx_device_exit(struct omap_mcbsp *mcbsp) {}
+#endif /* CONFIG_ARCH_OMAP34XX */
+
 /*
 /*
  * McBSP1 and McBSP3 are directly mapped on 1610 and 1510.
  * McBSP1 and McBSP3 are directly mapped on 1610 and 1510.
  * 730 has only 2 McBSP, and both of them are MPU peripherals.
  * 730 has only 2 McBSP, and both of them are MPU peripherals.
@@ -953,6 +1310,10 @@ static int __devinit omap_mcbsp_probe(struct platform_device *pdev)
 	mcbsp->dev = &pdev->dev;
 	mcbsp->dev = &pdev->dev;
 	mcbsp_ptr[id] = mcbsp;
 	mcbsp_ptr[id] = mcbsp;
 	platform_set_drvdata(pdev, mcbsp);
 	platform_set_drvdata(pdev, mcbsp);
+
+	/* Initialize mcbsp properties for OMAP34XX if needed / applicable */
+	omap34xx_device_init(mcbsp);
+
 	return 0;
 	return 0;
 
 
 err_fclk:
 err_fclk:
@@ -976,6 +1337,8 @@ static int __devexit omap_mcbsp_remove(struct platform_device *pdev)
 				mcbsp->pdata->ops->free)
 				mcbsp->pdata->ops->free)
 			mcbsp->pdata->ops->free(mcbsp->id);
 			mcbsp->pdata->ops->free(mcbsp->id);
 
 
+		omap34xx_device_exit(mcbsp);
+
 		clk_disable(mcbsp->fclk);
 		clk_disable(mcbsp->fclk);
 		clk_disable(mcbsp->iclk);
 		clk_disable(mcbsp->iclk);
 		clk_put(mcbsp->fclk);
 		clk_put(mcbsp->fclk);

+ 37 - 0
arch/arm/plat-s3c/include/plat/audio-simtec.h

@@ -0,0 +1,37 @@
+/* arch/arm/plat-s3c/include/plat/audio-simtec.h
+ *
+ * Copyright 2008 Simtec Electronics
+ *	http://armlinux.simtec.co.uk/
+ *	Ben Dooks <ben@simtec.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Simtec Audio support.
+*/
+
+/**
+ * struct s3c24xx_audio_simtec_pdata - platform data for simtec audio
+ * @use_mpllin: Select codec clock from MPLLin
+ * @output_cdclk: Need to output CDCLK to the codec
+ * @have_mic: Set if we have a MIC socket
+ * @have_lout: Set if we have a LineOut socket
+ * @amp_gpio: GPIO pin to enable the AMP
+ * @amp_gain: Option GPIO to control AMP gain
+ */
+struct s3c24xx_audio_simtec_pdata {
+	unsigned int	use_mpllin:1;
+	unsigned int	output_cdclk:1;
+
+	unsigned int	have_mic:1;
+	unsigned int	have_lout:1;
+
+	int		amp_gpio;
+	int		amp_gain[2];
+
+	void	(*startup)(void);
+};
+
+extern int simtec_audio_add(const char *codec_name,
+			    struct s3c24xx_audio_simtec_pdata *pdata);

+ 5 - 0
arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h

@@ -33,6 +33,11 @@
 #define S3C2412_IISCON_RXDMA_ACTIVE	(1 << 1)
 #define S3C2412_IISCON_RXDMA_ACTIVE	(1 << 1)
 #define S3C2412_IISCON_IIS_ACTIVE	(1 << 0)
 #define S3C2412_IISCON_IIS_ACTIVE	(1 << 0)
 
 
+#define S3C64XX_IISMOD_BLC_16BIT	(0 << 13)
+#define S3C64XX_IISMOD_BLC_8BIT		(1 << 13)
+#define S3C64XX_IISMOD_BLC_24BIT	(2 << 13)
+#define S3C64XX_IISMOD_BLC_MASK		(3 << 13)
+
 #define S3C64XX_IISMOD_IMS_PCLK		(0 << 10)
 #define S3C64XX_IISMOD_IMS_PCLK		(0 << 10)
 #define S3C64XX_IISMOD_IMS_SYSMUX	(1 << 10)
 #define S3C64XX_IISMOD_IMS_SYSMUX	(1 << 10)
 
 

+ 26 - 13
fs/char_dev.c

@@ -238,8 +238,10 @@ int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
 }
 }
 
 
 /**
 /**
- * register_chrdev() - Register a major number for character devices.
+ * __register_chrdev() - create and register a cdev occupying a range of minors
  * @major: major device number or 0 for dynamic allocation
  * @major: major device number or 0 for dynamic allocation
+ * @baseminor: first of the requested range of minor numbers
+ * @count: the number of minor numbers required
  * @name: name of this range of devices
  * @name: name of this range of devices
  * @fops: file operations associated with this devices
  * @fops: file operations associated with this devices
  *
  *
@@ -255,19 +257,17 @@ int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
  * /dev. It only helps to keep track of the different owners of devices. If
  * /dev. It only helps to keep track of the different owners of devices. If
  * your module name has only one type of devices it's ok to use e.g. the name
  * your module name has only one type of devices it's ok to use e.g. the name
  * of the module here.
  * of the module here.
- *
- * This function registers a range of 256 minor numbers. The first minor number
- * is 0.
  */
  */
-int register_chrdev(unsigned int major, const char *name,
-		    const struct file_operations *fops)
+int __register_chrdev(unsigned int major, unsigned int baseminor,
+		      unsigned int count, const char *name,
+		      const struct file_operations *fops)
 {
 {
 	struct char_device_struct *cd;
 	struct char_device_struct *cd;
 	struct cdev *cdev;
 	struct cdev *cdev;
 	char *s;
 	char *s;
 	int err = -ENOMEM;
 	int err = -ENOMEM;
 
 
-	cd = __register_chrdev_region(major, 0, 256, name);
+	cd = __register_chrdev_region(major, baseminor, count, name);
 	if (IS_ERR(cd))
 	if (IS_ERR(cd))
 		return PTR_ERR(cd);
 		return PTR_ERR(cd);
 	
 	
@@ -281,7 +281,7 @@ int register_chrdev(unsigned int major, const char *name,
 	for (s = strchr(kobject_name(&cdev->kobj),'/'); s; s = strchr(s, '/'))
 	for (s = strchr(kobject_name(&cdev->kobj),'/'); s; s = strchr(s, '/'))
 		*s = '!';
 		*s = '!';
 		
 		
-	err = cdev_add(cdev, MKDEV(cd->major, 0), 256);
+	err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);
 	if (err)
 	if (err)
 		goto out;
 		goto out;
 
 
@@ -291,7 +291,7 @@ int register_chrdev(unsigned int major, const char *name,
 out:
 out:
 	kobject_put(&cdev->kobj);
 	kobject_put(&cdev->kobj);
 out2:
 out2:
-	kfree(__unregister_chrdev_region(cd->major, 0, 256));
+	kfree(__unregister_chrdev_region(cd->major, baseminor, count));
 	return err;
 	return err;
 }
 }
 
 
@@ -317,10 +317,23 @@ void unregister_chrdev_region(dev_t from, unsigned count)
 	}
 	}
 }
 }
 
 
-void unregister_chrdev(unsigned int major, const char *name)
+/**
+ * __unregister_chrdev - unregister and destroy a cdev
+ * @major: major device number
+ * @baseminor: first of the range of minor numbers
+ * @count: the number of minor numbers this cdev is occupying
+ * @name: name of this range of devices
+ *
+ * Unregister and destroy the cdev occupying the region described by
+ * @major, @baseminor and @count.  This function undoes what
+ * __register_chrdev() did.
+ */
+void __unregister_chrdev(unsigned int major, unsigned int baseminor,
+			 unsigned int count, const char *name)
 {
 {
 	struct char_device_struct *cd;
 	struct char_device_struct *cd;
-	cd = __unregister_chrdev_region(major, 0, 256);
+
+	cd = __unregister_chrdev_region(major, baseminor, count);
 	if (cd && cd->cdev)
 	if (cd && cd->cdev)
 		cdev_del(cd->cdev);
 		cdev_del(cd->cdev);
 	kfree(cd);
 	kfree(cd);
@@ -569,6 +582,6 @@ EXPORT_SYMBOL(cdev_alloc);
 EXPORT_SYMBOL(cdev_del);
 EXPORT_SYMBOL(cdev_del);
 EXPORT_SYMBOL(cdev_add);
 EXPORT_SYMBOL(cdev_add);
 EXPORT_SYMBOL(cdev_index);
 EXPORT_SYMBOL(cdev_index);
-EXPORT_SYMBOL(register_chrdev);
-EXPORT_SYMBOL(unregister_chrdev);
+EXPORT_SYMBOL(__register_chrdev);
+EXPORT_SYMBOL(__unregister_chrdev);
 EXPORT_SYMBOL(directly_mappable_cdev_bdi);
 EXPORT_SYMBOL(directly_mappable_cdev_bdi);

+ 16 - 3
include/linux/fs.h

@@ -1997,12 +1997,25 @@ extern void bd_release_from_disk(struct block_device *, struct gendisk *);
 #define CHRDEV_MAJOR_HASH_SIZE	255
 #define CHRDEV_MAJOR_HASH_SIZE	255
 extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);
 extern int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);
 extern int register_chrdev_region(dev_t, unsigned, const char *);
 extern int register_chrdev_region(dev_t, unsigned, const char *);
-extern int register_chrdev(unsigned int, const char *,
-			   const struct file_operations *);
-extern void unregister_chrdev(unsigned int, const char *);
+extern int __register_chrdev(unsigned int major, unsigned int baseminor,
+			     unsigned int count, const char *name,
+			     const struct file_operations *fops);
+extern void __unregister_chrdev(unsigned int major, unsigned int baseminor,
+				unsigned int count, const char *name);
 extern void unregister_chrdev_region(dev_t, unsigned);
 extern void unregister_chrdev_region(dev_t, unsigned);
 extern void chrdev_show(struct seq_file *,off_t);
 extern void chrdev_show(struct seq_file *,off_t);
 
 
+static inline int register_chrdev(unsigned int major, const char *name,
+				  const struct file_operations *fops)
+{
+	return __register_chrdev(major, 0, 256, name, fops);
+}
+
+static inline void unregister_chrdev(unsigned int major, const char *name)
+{
+	__unregister_chrdev(major, 0, 256, name);
+}
+
 /* fs/block_dev.c */
 /* fs/block_dev.c */
 #define BDEVNAME_SIZE	32	/* Largest string for a blockdev identifier */
 #define BDEVNAME_SIZE	32	/* Largest string for a blockdev identifier */
 #define BDEVT_SIZE	10	/* Largest string for MAJ:MIN for blkdev */
 #define BDEVT_SIZE	10	/* Largest string for MAJ:MIN for blkdev */

+ 3 - 1
include/linux/tty.h

@@ -23,7 +23,7 @@
  */
  */
 #define NR_UNIX98_PTY_DEFAULT	4096      /* Default maximum for Unix98 ptys */
 #define NR_UNIX98_PTY_DEFAULT	4096      /* Default maximum for Unix98 ptys */
 #define NR_UNIX98_PTY_MAX	(1 << MINORBITS) /* Absolute limit */
 #define NR_UNIX98_PTY_MAX	(1 << MINORBITS) /* Absolute limit */
-#define NR_LDISCS		19
+#define NR_LDISCS		20
 
 
 /* line disciplines */
 /* line disciplines */
 #define N_TTY		0
 #define N_TTY		0
@@ -47,6 +47,8 @@
 #define N_SLCAN		17	/* Serial / USB serial CAN Adaptors */
 #define N_SLCAN		17	/* Serial / USB serial CAN Adaptors */
 #define N_PPS		18	/* Pulse per Second */
 #define N_PPS		18	/* Pulse per Second */
 
 
+#define N_V253		19	/* Codec control over voice modem */
+
 /*
 /*
  * This character is the same as _POSIX_VDISABLE: it cannot be used as
  * This character is the same as _POSIX_VDISABLE: it cannot be used as
  * a c_cc[] character, but indicates that a particular special character
  * a c_cc[] character, but indicates that a particular special character

+ 9 - 0
include/sound/ac97_codec.h

@@ -32,6 +32,9 @@
 #include "control.h"
 #include "control.h"
 #include "info.h"
 #include "info.h"
 
 
+/* maximum number of devices on the AC97 bus */
+#define	AC97_BUS_MAX_DEVICES	4
+
 /*
 /*
  *  AC'97 codec registers
  *  AC'97 codec registers
  */
  */
@@ -642,4 +645,10 @@ int snd_ac97_pcm_double_rate_rules(struct snd_pcm_runtime *runtime);
 /* ad hoc AC97 device driver access */
 /* ad hoc AC97 device driver access */
 extern struct bus_type ac97_bus_type;
 extern struct bus_type ac97_bus_type;
 
 
+/* AC97 platform_data adding function */
+static inline void snd_ac97_dev_add_pdata(struct snd_ac97 *ac97, void *data)
+{
+	ac97->dev.platform_data = data;
+}
+
 #endif /* __SOUND_AC97_CODEC_H */
 #endif /* __SOUND_AC97_CODEC_H */

+ 1 - 1
include/sound/asound.h

@@ -138,7 +138,7 @@ struct snd_hwdep_dsp_image {
  *                                                                           *
  *                                                                           *
  *****************************************************************************/
  *****************************************************************************/
 
 
-#define SNDRV_PCM_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 9)
+#define SNDRV_PCM_VERSION		SNDRV_PROTOCOL_VERSION(2, 0, 10)
 
 
 typedef unsigned long snd_pcm_uframes_t;
 typedef unsigned long snd_pcm_uframes_t;
 typedef signed long snd_pcm_sframes_t;
 typedef signed long snd_pcm_sframes_t;

+ 14 - 37
include/sound/core.h

@@ -93,15 +93,6 @@ struct snd_device {
 
 
 #define snd_device(n) list_entry(n, struct snd_device, list)
 #define snd_device(n) list_entry(n, struct snd_device, list)
 
 
-/* monitor files for graceful shutdown (hotplug) */
-
-struct snd_monitor_file {
-	struct file *file;
-	const struct file_operations *disconnected_f_op;
-	struct list_head shutdown_list;	/* still need to shutdown */
-	struct list_head list;	/* link of monitor files */
-};
-
 /* main structure for soundcard */
 /* main structure for soundcard */
 
 
 struct snd_card {
 struct snd_card {
@@ -311,9 +302,7 @@ int snd_component_add(struct snd_card *card, const char *component);
 int snd_card_file_add(struct snd_card *card, struct file *file);
 int snd_card_file_add(struct snd_card *card, struct file *file);
 int snd_card_file_remove(struct snd_card *card, struct file *file);
 int snd_card_file_remove(struct snd_card *card, struct file *file);
 
 
-#ifndef snd_card_set_dev
 #define snd_card_set_dev(card, devptr) ((card)->dev = (devptr))
 #define snd_card_set_dev(card, devptr) ((card)->dev = (devptr))
-#endif
 
 
 /* device.c */
 /* device.c */
 
 
@@ -340,18 +329,17 @@ unsigned int snd_dma_pointer(unsigned long dma, unsigned int size);
 struct resource;
 struct resource;
 void release_and_free_resource(struct resource *res);
 void release_and_free_resource(struct resource *res);
 
 
-#ifdef CONFIG_SND_VERBOSE_PRINTK
-void snd_verbose_printk(const char *file, int line, const char *format, ...)
-     __attribute__ ((format (printf, 3, 4)));
-#endif
-#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_VERBOSE_PRINTK)
-void snd_verbose_printd(const char *file, int line, const char *format, ...)
-     __attribute__ ((format (printf, 3, 4)));
-#endif
-
 /* --- */
 /* --- */
 
 
-#ifdef CONFIG_SND_VERBOSE_PRINTK
+#if defined(CONFIG_SND_DEBUG) || defined(CONFIG_SND_VERBOSE_PRINTK)
+void __snd_printk(unsigned int level, const char *file, int line,
+		  const char *format, ...)
+     __attribute__ ((format (printf, 4, 5)));
+#else
+#define __snd_printk(level, file, line, format, args...) \
+	printk(format, ##args)
+#endif
+
 /**
 /**
  * snd_printk - printk wrapper
  * snd_printk - printk wrapper
  * @fmt: format string
  * @fmt: format string
@@ -360,15 +348,9 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...)
  * when configured with CONFIG_SND_VERBOSE_PRINTK.
  * when configured with CONFIG_SND_VERBOSE_PRINTK.
  */
  */
 #define snd_printk(fmt, args...) \
 #define snd_printk(fmt, args...) \
-	snd_verbose_printk(__FILE__, __LINE__, fmt ,##args)
-#else
-#define snd_printk(fmt, args...) \
-	printk(fmt ,##args)
-#endif
+	__snd_printk(0, __FILE__, __LINE__, fmt, ##args)
 
 
 #ifdef CONFIG_SND_DEBUG
 #ifdef CONFIG_SND_DEBUG
-
-#ifdef CONFIG_SND_VERBOSE_PRINTK
 /**
 /**
  * snd_printd - debug printk
  * snd_printd - debug printk
  * @fmt: format string
  * @fmt: format string
@@ -377,11 +359,7 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...)
  * Ignored when CONFIG_SND_DEBUG is not set.
  * Ignored when CONFIG_SND_DEBUG is not set.
  */
  */
 #define snd_printd(fmt, args...) \
 #define snd_printd(fmt, args...) \
-	snd_verbose_printd(__FILE__, __LINE__, fmt ,##args)
-#else
-#define snd_printd(fmt, args...) \
-	printk(fmt ,##args)
-#endif
+	__snd_printk(1, __FILE__, __LINE__, fmt, ##args)
 
 
 /**
 /**
  * snd_BUG - give a BUG warning message and stack trace
  * snd_BUG - give a BUG warning message and stack trace
@@ -428,9 +406,10 @@ static inline int __snd_bug_on(int cond)
  * Works like snd_printk() for debugging purposes.
  * Works like snd_printk() for debugging purposes.
  * Ignored when CONFIG_SND_DEBUG_VERBOSE is not set.
  * Ignored when CONFIG_SND_DEBUG_VERBOSE is not set.
  */
  */
-#define snd_printdd(format, args...) snd_printk(format, ##args)
+#define snd_printdd(format, args...) \
+	__snd_printk(2, __FILE__, __LINE__, format, ##args)
 #else
 #else
-#define snd_printdd(format, args...) /* nothing */
+#define snd_printdd(format, args...)	do { } while (0)
 #endif
 #endif
 
 
 
 
@@ -438,12 +417,10 @@ static inline int __snd_bug_on(int cond)
 
 
 /* for easier backward-porting */
 /* for easier backward-porting */
 #if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE)
 #if defined(CONFIG_GAMEPORT) || defined(CONFIG_GAMEPORT_MODULE)
-#ifndef gameport_set_dev_parent
 #define gameport_set_dev_parent(gp,xdev) ((gp)->dev.parent = (xdev))
 #define gameport_set_dev_parent(gp,xdev) ((gp)->dev.parent = (xdev))
 #define gameport_set_port_data(gp,r) ((gp)->port_data = (r))
 #define gameport_set_port_data(gp,r) ((gp)->port_data = (r))
 #define gameport_get_port_data(gp) (gp)->port_data
 #define gameport_get_port_data(gp) (gp)->port_data
 #endif
 #endif
-#endif
 
 
 /* PCI quirk list helper */
 /* PCI quirk list helper */
 struct snd_pci_quirk {
 struct snd_pci_quirk {

+ 2 - 2
include/sound/info.h

@@ -110,13 +110,13 @@ void snd_card_info_read_oss(struct snd_info_buffer *buffer);
 static inline void snd_card_info_read_oss(struct snd_info_buffer *buffer) {}
 static inline void snd_card_info_read_oss(struct snd_info_buffer *buffer) {}
 #endif
 #endif
 
 
-int snd_iprintf(struct snd_info_buffer *buffer, char *fmt, ...) \
+int snd_iprintf(struct snd_info_buffer *buffer, const char *fmt, ...) \
 				__attribute__ ((format (printf, 2, 3)));
 				__attribute__ ((format (printf, 2, 3)));
 int snd_info_init(void);
 int snd_info_init(void);
 int snd_info_done(void);
 int snd_info_done(void);
 
 
 int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len);
 int snd_info_get_line(struct snd_info_buffer *buffer, char *line, int len);
-char *snd_info_get_str(char *dest, char *src, int len);
+const char *snd_info_get_str(char *dest, const char *src, int len);
 struct snd_info_entry *snd_info_create_module_entry(struct module *module,
 struct snd_info_entry *snd_info_create_module_entry(struct module *module,
 					       const char *name,
 					       const char *name,
 					       struct snd_info_entry *parent);
 					       struct snd_info_entry *parent);

+ 6 - 0
include/sound/memalloc.h

@@ -47,7 +47,11 @@ struct snd_dma_device {
 #define SNDRV_DMA_TYPE_UNKNOWN		0	/* not defined */
 #define SNDRV_DMA_TYPE_UNKNOWN		0	/* not defined */
 #define SNDRV_DMA_TYPE_CONTINUOUS	1	/* continuous no-DMA memory */
 #define SNDRV_DMA_TYPE_CONTINUOUS	1	/* continuous no-DMA memory */
 #define SNDRV_DMA_TYPE_DEV		2	/* generic device continuous */
 #define SNDRV_DMA_TYPE_DEV		2	/* generic device continuous */
+#ifdef CONFIG_SND_DMA_SGBUF
 #define SNDRV_DMA_TYPE_DEV_SG		3	/* generic device SG-buffer */
 #define SNDRV_DMA_TYPE_DEV_SG		3	/* generic device SG-buffer */
+#else
+#define SNDRV_DMA_TYPE_DEV_SG	SNDRV_DMA_TYPE_DEV /* no SG-buf support */
+#endif
 
 
 /*
 /*
  * info for buffer allocation
  * info for buffer allocation
@@ -60,6 +64,7 @@ struct snd_dma_buffer {
 	void *private_data;	/* private for allocator; don't touch */
 	void *private_data;	/* private for allocator; don't touch */
 };
 };
 
 
+#ifdef CONFIG_SND_DMA_SGBUF
 /*
 /*
  * Scatter-Gather generic device pages
  * Scatter-Gather generic device pages
  */
  */
@@ -107,6 +112,7 @@ static inline void *snd_sgbuf_get_ptr(struct snd_sg_buf *sgbuf, size_t offset)
 {
 {
 	return sgbuf->table[offset >> PAGE_SHIFT].buf + offset % PAGE_SIZE;
 	return sgbuf->table[offset >> PAGE_SHIFT].buf + offset % PAGE_SIZE;
 }
 }
+#endif /* CONFIG_SND_DMA_SGBUF */
 
 
 /* allocate/release a buffer */
 /* allocate/release a buffer */
 int snd_dma_alloc_pages(int type, struct device *dev, size_t size,
 int snd_dma_alloc_pages(int type, struct device *dev, size_t size,

+ 25 - 0
include/sound/pcm.h

@@ -902,6 +902,7 @@ int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,
 int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size);
 int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size);
 int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream);
 int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream);
 
 
+#ifdef CONFIG_SND_DMA_SGBUF
 /*
 /*
  * SG-buffer handling
  * SG-buffer handling
  */
  */
@@ -927,6 +928,28 @@ struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream,
 unsigned int snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream,
 unsigned int snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream,
 					  unsigned int ofs, unsigned int size);
 					  unsigned int ofs, unsigned int size);
 
 
+#else /* !SND_DMA_SGBUF */
+/*
+ * fake using a continuous buffer
+ */
+static inline dma_addr_t
+snd_pcm_sgbuf_get_addr(struct snd_pcm_substream *substream, unsigned int ofs)
+{
+	return substream->runtime->dma_addr + ofs;
+}
+
+static inline void *
+snd_pcm_sgbuf_get_ptr(struct snd_pcm_substream *substream, unsigned int ofs)
+{
+	return substream->runtime->dma_area + ofs;
+}
+
+#define snd_pcm_sgbuf_ops_page	NULL
+
+#define snd_pcm_sgbuf_get_chunk_size(subs, ofs, size)	(size)
+
+#endif /* SND_DMA_SGBUF */
+
 /* handle mmap counter - PCM mmap callback should handle this counter properly */
 /* handle mmap counter - PCM mmap callback should handle this counter properly */
 static inline void snd_pcm_mmap_data_open(struct vm_area_struct *area)
 static inline void snd_pcm_mmap_data_open(struct vm_area_struct *area)
 {
 {
@@ -965,4 +988,6 @@ static inline void snd_pcm_limit_isa_dma_size(int dma, size_t *max)
 
 
 #define PCM_RUNTIME_CHECK(sub) snd_BUG_ON(!(sub) || !(sub)->runtime)
 #define PCM_RUNTIME_CHECK(sub) snd_BUG_ON(!(sub) || !(sub)->runtime)
 
 
+const char *snd_pcm_format_name(snd_pcm_format_t format);
+
 #endif /* __SOUND_PCM_H */
 #endif /* __SOUND_PCM_H */

+ 83 - 0
include/sound/sh_fsi.h

@@ -0,0 +1,83 @@
+#ifndef __SOUND_FSI_H
+#define __SOUND_FSI_H
+
+/*
+ * Fifo-attached Serial Interface (FSI) support for SH7724
+ *
+ * Copyright (C) 2009 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* flags format
+
+ * 0xABCDEEFF
+ *
+ * A:  channel size for TDM (input)
+ * B:  channel size for TDM (ooutput)
+ * C:  inversion
+ * D:  mode
+ * E:  input format
+ * F:  output format
+ */
+
+#include <linux/clk.h>
+#include <sound/soc.h>
+
+/* TDM channel */
+#define SH_FSI_SET_CH_I(x)	((x & 0xF) << 28)
+#define SH_FSI_SET_CH_O(x)	((x & 0xF) << 24)
+
+#define SH_FSI_CH_IMASK		0xF0000000
+#define SH_FSI_CH_OMASK		0x0F000000
+#define SH_FSI_GET_CH_I(x)	((x & SH_FSI_CH_IMASK) >> 28)
+#define SH_FSI_GET_CH_O(x)	((x & SH_FSI_CH_OMASK) >> 24)
+
+/* clock inversion */
+#define SH_FSI_INVERSION_MASK	0x00F00000
+#define SH_FSI_LRM_INV		(1 << 20)
+#define SH_FSI_BRM_INV		(1 << 21)
+#define SH_FSI_LRS_INV		(1 << 22)
+#define SH_FSI_BRS_INV		(1 << 23)
+
+/* mode */
+#define SH_FSI_MODE_MASK	0x000F0000
+#define SH_FSI_IN_SLAVE_MODE	(1 << 16)  /* default master mode */
+#define SH_FSI_OUT_SLAVE_MODE	(1 << 17)  /* default master mode */
+
+/* DI format */
+#define SH_FSI_FMT_MASK		0x000000FF
+#define SH_FSI_IFMT(x)		(((SH_FSI_FMT_ ## x) & SH_FSI_FMT_MASK) << 8)
+#define SH_FSI_OFMT(x)		(((SH_FSI_FMT_ ## x) & SH_FSI_FMT_MASK) << 0)
+#define SH_FSI_GET_IFMT(x)	((x >> 8) & SH_FSI_FMT_MASK)
+#define SH_FSI_GET_OFMT(x)	((x >> 0) & SH_FSI_FMT_MASK)
+
+#define SH_FSI_FMT_MONO		(1 << 0)
+#define SH_FSI_FMT_MONO_DELAY	(1 << 1)
+#define SH_FSI_FMT_PCM		(1 << 2)
+#define SH_FSI_FMT_I2S		(1 << 3)
+#define SH_FSI_FMT_TDM		(1 << 4)
+#define SH_FSI_FMT_TDM_DELAY	(1 << 5)
+
+#define SH_FSI_IFMT_TDM_CH(x) \
+	(SH_FSI_IFMT(TDM)	| SH_FSI_SET_CH_I(x))
+#define SH_FSI_IFMT_TDM_DELAY_CH(x) \
+	(SH_FSI_IFMT(TDM_DELAY)	| SH_FSI_SET_CH_I(x))
+
+#define SH_FSI_OFMT_TDM_CH(x) \
+	(SH_FSI_OFMT(TDM)	| SH_FSI_SET_CH_O(x))
+#define SH_FSI_OFMT_TDM_DELAY_CH(x) \
+	(SH_FSI_OFMT(TDM_DELAY)	| SH_FSI_SET_CH_O(x))
+
+struct sh_fsi_platform_info {
+	unsigned long porta_flags;
+	unsigned long portb_flags;
+};
+
+extern struct snd_soc_dai fsi_soc_dai[2];
+extern struct snd_soc_platform fsi_soc_platform;
+
+#endif /* __SOUND_FSI_H */

+ 24 - 16
include/sound/soc-dai.h

@@ -27,8 +27,8 @@ struct snd_pcm_substream;
 #define SND_SOC_DAIFMT_I2S		0 /* I2S mode */
 #define SND_SOC_DAIFMT_I2S		0 /* I2S mode */
 #define SND_SOC_DAIFMT_RIGHT_J		1 /* Right Justified mode */
 #define SND_SOC_DAIFMT_RIGHT_J		1 /* Right Justified mode */
 #define SND_SOC_DAIFMT_LEFT_J		2 /* Left Justified mode */
 #define SND_SOC_DAIFMT_LEFT_J		2 /* Left Justified mode */
-#define SND_SOC_DAIFMT_DSP_A		3 /* L data msb after FRM LRC */
-#define SND_SOC_DAIFMT_DSP_B		4 /* L data msb during FRM LRC */
+#define SND_SOC_DAIFMT_DSP_A		3 /* L data MSB after FRM LRC */
+#define SND_SOC_DAIFMT_DSP_B		4 /* L data MSB during FRM LRC */
 #define SND_SOC_DAIFMT_AC97		5 /* AC97 */
 #define SND_SOC_DAIFMT_AC97		5 /* AC97 */
 
 
 /* left and right justified also known as MSB and LSB respectively */
 /* left and right justified also known as MSB and LSB respectively */
@@ -38,7 +38,7 @@ struct snd_pcm_substream;
 /*
 /*
  * DAI Clock gating.
  * DAI Clock gating.
  *
  *
- * DAI bit clocks can be be gated (disabled) when not the DAI is not
+ * DAI bit clocks can be be gated (disabled) when the DAI is not
  * sending or receiving PCM data in a frame. This can be used to save power.
  * sending or receiving PCM data in a frame. This can be used to save power.
  */
  */
 #define SND_SOC_DAIFMT_CONT		(0 << 4) /* continuous clock */
 #define SND_SOC_DAIFMT_CONT		(0 << 4) /* continuous clock */
@@ -51,21 +51,21 @@ struct snd_pcm_substream;
  * format.
  * format.
  */
  */
 #define SND_SOC_DAIFMT_NB_NF		(0 << 8) /* normal bit clock + frame */
 #define SND_SOC_DAIFMT_NB_NF		(0 << 8) /* normal bit clock + frame */
-#define SND_SOC_DAIFMT_NB_IF		(1 << 8) /* normal bclk + inv frm */
-#define SND_SOC_DAIFMT_IB_NF		(2 << 8) /* invert bclk + nor frm */
-#define SND_SOC_DAIFMT_IB_IF		(3 << 8) /* invert bclk + frm */
+#define SND_SOC_DAIFMT_NB_IF		(1 << 8) /* normal BCLK + inv FRM */
+#define SND_SOC_DAIFMT_IB_NF		(2 << 8) /* invert BCLK + nor FRM */
+#define SND_SOC_DAIFMT_IB_IF		(3 << 8) /* invert BCLK + FRM */
 
 
 /*
 /*
  * DAI hardware clock masters.
  * DAI hardware clock masters.
  *
  *
  * This is wrt the codec, the inverse is true for the interface
  * This is wrt the codec, the inverse is true for the interface
- * i.e. if the codec is clk and frm master then the interface is
+ * i.e. if the codec is clk and FRM master then the interface is
  * clk and frame slave.
  * clk and frame slave.
  */
  */
-#define SND_SOC_DAIFMT_CBM_CFM		(0 << 12) /* codec clk & frm master */
-#define SND_SOC_DAIFMT_CBS_CFM		(1 << 12) /* codec clk slave & frm master */
+#define SND_SOC_DAIFMT_CBM_CFM		(0 << 12) /* codec clk & FRM master */
+#define SND_SOC_DAIFMT_CBS_CFM		(1 << 12) /* codec clk slave & FRM master */
 #define SND_SOC_DAIFMT_CBM_CFS		(2 << 12) /* codec clk master & frame slave */
 #define SND_SOC_DAIFMT_CBM_CFS		(2 << 12) /* codec clk master & frame slave */
-#define SND_SOC_DAIFMT_CBS_CFS		(3 << 12) /* codec clk & frm slave */
+#define SND_SOC_DAIFMT_CBS_CFS		(3 << 12) /* codec clk & FRM slave */
 
 
 #define SND_SOC_DAIFMT_FORMAT_MASK	0x000f
 #define SND_SOC_DAIFMT_FORMAT_MASK	0x000f
 #define SND_SOC_DAIFMT_CLOCK_MASK	0x00f0
 #define SND_SOC_DAIFMT_CLOCK_MASK	0x00f0
@@ -78,7 +78,13 @@ struct snd_pcm_substream;
 #define SND_SOC_CLOCK_IN		0
 #define SND_SOC_CLOCK_IN		0
 #define SND_SOC_CLOCK_OUT		1
 #define SND_SOC_CLOCK_OUT		1
 
 
-#define SND_SOC_STD_AC97_FMTS (SNDRV_PCM_FMTBIT_S16_LE |\
+#define SND_SOC_STD_AC97_FMTS (SNDRV_PCM_FMTBIT_S8 |\
+			       SNDRV_PCM_FMTBIT_S16_LE |\
+			       SNDRV_PCM_FMTBIT_S16_BE |\
+			       SNDRV_PCM_FMTBIT_S20_3LE |\
+			       SNDRV_PCM_FMTBIT_S20_3BE |\
+			       SNDRV_PCM_FMTBIT_S24_3LE |\
+			       SNDRV_PCM_FMTBIT_S24_3BE |\
                                SNDRV_PCM_FMTBIT_S32_LE |\
                                SNDRV_PCM_FMTBIT_S32_LE |\
                                SNDRV_PCM_FMTBIT_S32_BE)
                                SNDRV_PCM_FMTBIT_S32_BE)
 
 
@@ -106,7 +112,7 @@ int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
 int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
 int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
 
 
 int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
 int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
-	unsigned int mask, int slots);
+	unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width);
 
 
 int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate);
 int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate);
 
 
@@ -116,12 +122,12 @@ int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute);
 /*
 /*
  * Digital Audio Interface.
  * Digital Audio Interface.
  *
  *
- * Describes the Digital Audio Interface in terms of it's ALSA, DAI and AC97
- * operations an capabilities. Codec and platfom drivers will register a this
+ * Describes the Digital Audio Interface in terms of its ALSA, DAI and AC97
+ * operations and capabilities. Codec and platform drivers will register this
  * structure for every DAI they have.
  * structure for every DAI they have.
  *
  *
  * This structure covers the clocking, formating and ALSA operations for each
  * This structure covers the clocking, formating and ALSA operations for each
- * interface a
+ * interface.
  */
  */
 struct snd_soc_dai_ops {
 struct snd_soc_dai_ops {
 	/*
 	/*
@@ -140,7 +146,8 @@ struct snd_soc_dai_ops {
 	 */
 	 */
 	int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt);
 	int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt);
 	int (*set_tdm_slot)(struct snd_soc_dai *dai,
 	int (*set_tdm_slot)(struct snd_soc_dai *dai,
-		unsigned int mask, int slots);
+		unsigned int tx_mask, unsigned int rx_mask,
+		int slots, int slot_width);
 	int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
 	int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
 
 
 	/*
 	/*
@@ -179,6 +186,7 @@ struct snd_soc_dai {
 	int ac97_control;
 	int ac97_control;
 
 
 	struct device *dev;
 	struct device *dev;
+	void *ac97_pdata;	/* platform_data for the ac97 codec */
 
 
 	/* DAI callbacks */
 	/* DAI callbacks */
 	int (*probe)(struct platform_device *pdev,
 	int (*probe)(struct platform_device *pdev,

+ 10 - 0
include/sound/soc-dapm.h

@@ -137,6 +137,12 @@
 	.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD}
 	.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD}
 
 
 /* stream domain */
 /* stream domain */
+#define SND_SOC_DAPM_AIF_IN(wname, stname, wslot, wreg, wshift, winvert) \
+{	.id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \
+	.reg = wreg, .shift = wshift, .invert = winvert }
+#define SND_SOC_DAPM_AIF_OUT(wname, stname, wslot, wreg, wshift, winvert) \
+{	.id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \
+	.reg = wreg, .shift = wshift, .invert = winvert }
 #define SND_SOC_DAPM_DAC(wname, stname, wreg, wshift, winvert) \
 #define SND_SOC_DAPM_DAC(wname, stname, wreg, wshift, winvert) \
 {	.id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \
 {	.id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \
 	.shift = wshift, .invert = winvert}
 	.shift = wshift, .invert = winvert}
@@ -279,9 +285,11 @@ int snd_soc_dapm_add_routes(struct snd_soc_codec *codec,
 /* dapm events */
 /* dapm events */
 int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, char *stream,
 int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, char *stream,
 	int event);
 	int event);
+void snd_soc_dapm_shutdown(struct snd_soc_device *socdev);
 
 
 /* dapm sys fs - used by the core */
 /* dapm sys fs - used by the core */
 int snd_soc_dapm_sys_add(struct device *dev);
 int snd_soc_dapm_sys_add(struct device *dev);
+void snd_soc_dapm_debugfs_init(struct snd_soc_codec *codec);
 
 
 /* dapm audio pin control and status */
 /* dapm audio pin control and status */
 int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, const char *pin);
 int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, const char *pin);
@@ -311,6 +319,8 @@ enum snd_soc_dapm_type {
 	snd_soc_dapm_pre,			/* machine specific pre widget - exec first */
 	snd_soc_dapm_pre,			/* machine specific pre widget - exec first */
 	snd_soc_dapm_post,			/* machine specific post widget - exec last */
 	snd_soc_dapm_post,			/* machine specific post widget - exec last */
 	snd_soc_dapm_supply,		/* power/clock supply */
 	snd_soc_dapm_supply,		/* power/clock supply */
+	snd_soc_dapm_aif_in,		/* audio interface input */
+	snd_soc_dapm_aif_out,		/* audio interface output */
 };
 };
 
 
 /*
 /*

+ 43 - 6
include/sound/soc.h

@@ -135,6 +135,28 @@
 	.info = snd_soc_info_volsw, \
 	.info = snd_soc_info_volsw, \
 	.get = xhandler_get, .put = xhandler_put, \
 	.get = xhandler_get, .put = xhandler_put, \
 	.private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) }
 	.private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) }
+#define SOC_DOUBLE_EXT_TLV(xname, xreg, shift_left, shift_right, xmax, xinvert,\
+	 xhandler_get, xhandler_put, tlv_array) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+		 SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+	.tlv.p = (tlv_array), \
+	.info = snd_soc_info_volsw, \
+	.get = xhandler_get, .put = xhandler_put, \
+	.private_value = (unsigned long)&(struct soc_mixer_control) \
+		{.reg = xreg, .shift = shift_left, .rshift = shift_right, \
+		.max = xmax, .invert = xinvert} }
+#define SOC_DOUBLE_R_EXT_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert,\
+	 xhandler_get, xhandler_put, tlv_array) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+		 SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+	.tlv.p = (tlv_array), \
+	.info = snd_soc_info_volsw_2r, \
+	.get = xhandler_get, .put = xhandler_put, \
+	.private_value = (unsigned long)&(struct soc_mixer_control) \
+		{.reg = reg_left, .rreg = reg_right, .shift = xshift, \
+		.max = xmax, .invert = xinvert} }
 #define SOC_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put) \
 #define SOC_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put) \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
 	.info = snd_soc_info_bool_ext, \
 	.info = snd_soc_info_bool_ext, \
@@ -183,14 +205,28 @@ struct snd_soc_jack_gpio;
 #endif
 #endif
 
 
 typedef int (*hw_write_t)(void *,const char* ,int);
 typedef int (*hw_write_t)(void *,const char* ,int);
-typedef int (*hw_read_t)(void *,char* ,int);
 
 
 extern struct snd_ac97_bus_ops soc_ac97_ops;
 extern struct snd_ac97_bus_ops soc_ac97_ops;
 
 
+enum snd_soc_control_type {
+	SND_SOC_CUSTOM,
+	SND_SOC_I2C,
+	SND_SOC_SPI,
+};
+
 int snd_soc_register_platform(struct snd_soc_platform *platform);
 int snd_soc_register_platform(struct snd_soc_platform *platform);
 void snd_soc_unregister_platform(struct snd_soc_platform *platform);
 void snd_soc_unregister_platform(struct snd_soc_platform *platform);
 int snd_soc_register_codec(struct snd_soc_codec *codec);
 int snd_soc_register_codec(struct snd_soc_codec *codec);
 void snd_soc_unregister_codec(struct snd_soc_codec *codec);
 void snd_soc_unregister_codec(struct snd_soc_codec *codec);
+int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg);
+int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
+			       int addr_bits, int data_bits,
+			       enum snd_soc_control_type control);
+
+#ifdef CONFIG_PM
+int snd_soc_suspend_device(struct device *dev);
+int snd_soc_resume_device(struct device *dev);
+#endif
 
 
 /* pcm <-> DAI connect */
 /* pcm <-> DAI connect */
 void snd_soc_free_pcms(struct snd_soc_device *socdev);
 void snd_soc_free_pcms(struct snd_soc_device *socdev);
@@ -216,9 +252,9 @@ void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count,
 
 
 /* codec register bit access */
 /* codec register bit access */
 int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
 int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
-				unsigned short mask, unsigned short value);
+				unsigned int mask, unsigned int value);
 int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg,
 int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg,
-				unsigned short mask, unsigned short value);
+				unsigned int mask, unsigned int value);
 
 
 int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
 int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
 	struct snd_ac97_bus_ops *ops, int num);
 	struct snd_ac97_bus_ops *ops, int num);
@@ -356,8 +392,10 @@ struct snd_soc_codec {
 	int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
 	int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
 	int (*display_register)(struct snd_soc_codec *, char *,
 	int (*display_register)(struct snd_soc_codec *, char *,
 				size_t, unsigned int);
 				size_t, unsigned int);
+	int (*volatile_register)(unsigned int);
+	int (*readable_register)(unsigned int);
 	hw_write_t hw_write;
 	hw_write_t hw_write;
-	hw_read_t hw_read;
+	unsigned int (*hw_read)(struct snd_soc_codec *, unsigned int);
 	void *reg_cache;
 	void *reg_cache;
 	short reg_cache_size;
 	short reg_cache_size;
 	short reg_cache_step;
 	short reg_cache_step;
@@ -369,8 +407,6 @@ struct snd_soc_codec {
 	enum snd_soc_bias_level bias_level;
 	enum snd_soc_bias_level bias_level;
 	enum snd_soc_bias_level suspend_bias_level;
 	enum snd_soc_bias_level suspend_bias_level;
 	struct delayed_work delayed_work;
 	struct delayed_work delayed_work;
-	struct list_head up_list;
-	struct list_head down_list;
 
 
 	/* codec DAI's */
 	/* codec DAI's */
 	struct snd_soc_dai *dai;
 	struct snd_soc_dai *dai;
@@ -379,6 +415,7 @@ struct snd_soc_codec {
 #ifdef CONFIG_DEBUG_FS
 #ifdef CONFIG_DEBUG_FS
 	struct dentry *debugfs_reg;
 	struct dentry *debugfs_reg;
 	struct dentry *debugfs_pop_time;
 	struct dentry *debugfs_pop_time;
+	struct dentry *debugfs_dapm;
 #endif
 #endif
 };
 };
 
 

+ 14 - 0
include/sound/tlv.h

@@ -35,6 +35,8 @@
 #define SNDRV_CTL_TLVT_DB_SCALE	1       /* dB scale */
 #define SNDRV_CTL_TLVT_DB_SCALE	1       /* dB scale */
 #define SNDRV_CTL_TLVT_DB_LINEAR 2	/* linear volume */
 #define SNDRV_CTL_TLVT_DB_LINEAR 2	/* linear volume */
 #define SNDRV_CTL_TLVT_DB_RANGE 3	/* dB range container */
 #define SNDRV_CTL_TLVT_DB_RANGE 3	/* dB range container */
+#define SNDRV_CTL_TLVT_DB_MINMAX 4	/* dB scale with min/max */
+#define SNDRV_CTL_TLVT_DB_MINMAX_MUTE 5	/* dB scale with min/max with mute */
 
 
 #define TLV_DB_SCALE_ITEM(min, step, mute)			\
 #define TLV_DB_SCALE_ITEM(min, step, mute)			\
 	SNDRV_CTL_TLVT_DB_SCALE, 2 * sizeof(unsigned int),	\
 	SNDRV_CTL_TLVT_DB_SCALE, 2 * sizeof(unsigned int),	\
@@ -42,6 +44,18 @@
 #define DECLARE_TLV_DB_SCALE(name, min, step, mute) \
 #define DECLARE_TLV_DB_SCALE(name, min, step, mute) \
 	unsigned int name[] = { TLV_DB_SCALE_ITEM(min, step, mute) }
 	unsigned int name[] = { TLV_DB_SCALE_ITEM(min, step, mute) }
 
 
+/* dB scale specified with min/max values instead of step */
+#define TLV_DB_MINMAX_ITEM(min_dB, max_dB)			\
+	SNDRV_CTL_TLVT_DB_MINMAX, 2 * sizeof(unsigned int),	\
+	(min_dB), (max_dB)
+#define TLV_DB_MINMAX_MUTE_ITEM(min_dB, max_dB)			\
+	SNDRV_CTL_TLVT_DB_MINMAX_MUTE, 2 * sizeof(unsigned int),	\
+	(min_dB), (max_dB)
+#define DECLARE_TLV_DB_MINMAX(name, min_dB, max_dB) \
+	unsigned int name[] = { TLV_DB_MINMAX_ITEM(min_dB, max_dB) }
+#define DECLARE_TLV_DB_MINMAX_MUTE(name, min_dB, max_dB) \
+	unsigned int name[] = { TLV_DB_MINMAX_MUTE_ITEM(min_dB, max_dB) }
+
 /* linear volume between min_dB and max_dB (.01dB unit) */
 /* linear volume between min_dB and max_dB (.01dB unit) */
 #define TLV_DB_LINEAR_ITEM(min_dB, max_dB)		    \
 #define TLV_DB_LINEAR_ITEM(min_dB, max_dB)		    \
 	SNDRV_CTL_TLVT_DB_LINEAR, 2 * sizeof(unsigned int), \
 	SNDRV_CTL_TLVT_DB_LINEAR, 2 * sizeof(unsigned int), \

+ 22 - 0
include/sound/uda1380.h

@@ -0,0 +1,22 @@
+/*
+ * UDA1380 ALSA SoC Codec driver
+ *
+ * Copyright 2009 Philipp Zabel
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __UDA1380_H
+#define __UDA1380_H
+
+struct uda1380_platform_data {
+	int gpio_power;
+	int gpio_reset;
+	int dac_clk;
+#define UDA1380_DAC_CLK_SYSCLK 0
+#define UDA1380_DAC_CLK_WSPLL  1
+};
+
+#endif /* __UDA1380_H */

+ 1 - 1
include/sound/version.h

@@ -1,3 +1,3 @@
 /* include/version.h */
 /* include/version.h */
-#define CONFIG_SND_VERSION "1.0.20"
+#define CONFIG_SND_VERSION "1.0.21"
 #define CONFIG_SND_DATE ""
 #define CONFIG_SND_DATE ""

+ 44 - 0
include/sound/wm8993.h

@@ -0,0 +1,44 @@
+/*
+ * linux/sound/wm8993.h -- Platform data for WM8993
+ *
+ * Copyright 2009 Wolfson Microelectronics. PLC.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __LINUX_SND_WM8993_H
+#define __LINUX_SND_WM8993_H
+
+/* Note that EQ1 only contains the enable/disable bit so will be
+   ignored but is included for simplicity.
+ */
+struct wm8993_retune_mobile_setting {
+	const char *name;
+	unsigned int rate;
+	u16 config[24];
+};
+
+struct wm8993_platform_data {
+	struct wm8993_retune_mobile_setting *retune_configs;
+	int num_retune_configs;
+
+	/* LINEOUT can be differential or single ended */
+	unsigned int lineout1_diff:1;
+	unsigned int lineout2_diff:1;
+
+	/* Common mode feedback */
+	unsigned int lineout1fb:1;
+	unsigned int lineout2fb:1;
+
+	/* Microphone biases: 0=0.9*AVDD1 1=0.65*AVVD1 */
+	unsigned int micbias1_lvl:1;
+	unsigned int micbias2_lvl:1;
+
+	/* Jack detect threashold levels, see datasheet for values */
+	unsigned int jd_scthr:2;
+	unsigned int jd_thr:2;
+};
+
+#endif

+ 1 - 0
include/sound/ymfpci.h

@@ -331,6 +331,7 @@ struct snd_ymfpci {
 	struct snd_ac97 *ac97;
 	struct snd_ac97 *ac97;
 	struct snd_rawmidi *rawmidi;
 	struct snd_rawmidi *rawmidi;
 	struct snd_timer *timer;
 	struct snd_timer *timer;
+	unsigned int timer_ticks;
 
 
 	struct pci_dev *pci;
 	struct pci_dev *pci;
 	struct snd_card *card;
 	struct snd_card *card;

+ 28 - 0
sound/Kconfig

@@ -32,6 +32,34 @@ config SOUND_OSS_CORE
 	bool
 	bool
 	default n
 	default n
 
 
+config SOUND_OSS_CORE_PRECLAIM
+	bool "Preclaim OSS device numbers"
+	depends on SOUND_OSS_CORE
+	default y
+	help
+	  With this option enabled, the kernel will claim all OSS device
+	  numbers if any OSS support (native or emulation) is enabled
+	  whether the respective module is loaded or not and try to load the
+	  appropriate module using sound-slot/service-* and char-major-*
+	  module aliases when one of the device numbers is opened.  With
+	  this option disabled, kernel will only claim actually in-use
+	  device numbers and opening a missing device will generate only the
+	  standard char-major-* aliases.
+
+	  The only visible difference is use of additional module aliases
+	  and whether OSS sound devices appear multiple times in
+	  /proc/devices.  sound-slot/service-* module aliases are scheduled
+	  to be removed (ie. PRECLAIM won't be available) and this option is
+	  to make the transition easier.  This option can be overridden
+	  during boot using the kernel parameter soundcore.preclaim_oss.
+
+	  Disabling this allows alternative OSS implementations.
+
+	  Please read Documentation/feature-removal-schedule.txt for
+	  details.
+
+	  If unusre, say Y.
+
 source "sound/oss/dmasound/Kconfig"
 source "sound/oss/dmasound/Kconfig"
 
 
 if !M68K
 if !M68K

+ 10 - 0
sound/arm/pxa2xx-ac97.c

@@ -170,6 +170,13 @@ static int __devinit pxa2xx_ac97_probe(struct platform_device *dev)
 	struct snd_ac97_bus *ac97_bus;
 	struct snd_ac97_bus *ac97_bus;
 	struct snd_ac97_template ac97_template;
 	struct snd_ac97_template ac97_template;
 	int ret;
 	int ret;
+	pxa2xx_audio_ops_t *pdata = dev->dev.platform_data;
+
+	if (dev->id >= 0) {
+		dev_err(&dev->dev, "PXA2xx has only one AC97 port.\n");
+		ret = -ENXIO;
+		goto err_dev;
+	}
 
 
 	ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
 	ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
 			      THIS_MODULE, 0, &card);
 			      THIS_MODULE, 0, &card);
@@ -200,6 +207,8 @@ static int __devinit pxa2xx_ac97_probe(struct platform_device *dev)
 	snprintf(card->longname, sizeof(card->longname),
 	snprintf(card->longname, sizeof(card->longname),
 		 "%s (%s)", dev->dev.driver->name, card->mixername);
 		 "%s (%s)", dev->dev.driver->name, card->mixername);
 
 
+	if (pdata && pdata->codec_pdata[0])
+		snd_ac97_dev_add_pdata(ac97_bus->codec[0], pdata->codec_pdata[0]);
 	snd_card_set_dev(card, &dev->dev);
 	snd_card_set_dev(card, &dev->dev);
 	ret = snd_card_register(card);
 	ret = snd_card_register(card);
 	if (ret == 0) {
 	if (ret == 0) {
@@ -212,6 +221,7 @@ err_remove:
 err:
 err:
 	if (card)
 	if (card)
 		snd_card_free(card);
 		snd_card_free(card);
+err_dev:
 	return ret;
 	return ret;
 }
 }
 
 

+ 3 - 0
sound/arm/pxa2xx-pcm-lib.c

@@ -136,6 +136,9 @@ int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
 {
 {
 	struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
 	struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
 
 
+	if (!prtd || !prtd->params)
+		return 0;
+
 	DCSR(prtd->dma_ch) &= ~DCSR_RUN;
 	DCSR(prtd->dma_ch) &= ~DCSR_RUN;
 	DCSR(prtd->dma_ch) = 0;
 	DCSR(prtd->dma_ch) = 0;
 	DCMD(prtd->dma_ch) = 0;
 	DCMD(prtd->dma_ch) = 0;

+ 4 - 0
sound/core/Kconfig

@@ -206,4 +206,8 @@ config SND_PCM_XRUN_DEBUG
 config SND_VMASTER
 config SND_VMASTER
 	bool
 	bool
 
 
+config SND_DMA_SGBUF
+	def_bool y
+	depends on X86
+
 source "sound/core/seq/Kconfig"
 source "sound/core/seq/Kconfig"

+ 1 - 1
sound/core/Makefile

@@ -13,7 +13,7 @@ snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
 		pcm_memory.o
 		pcm_memory.o
 
 
 snd-page-alloc-y := memalloc.o
 snd-page-alloc-y := memalloc.o
-snd-page-alloc-$(CONFIG_HAS_DMA) += sgbuf.o
+snd-page-alloc-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o
 
 
 snd-rawmidi-objs  := rawmidi.o
 snd-rawmidi-objs  := rawmidi.o
 snd-timer-objs    := timer.o
 snd-timer-objs    := timer.o

+ 17 - 17
sound/core/control.c

@@ -414,7 +414,7 @@ int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id)
 EXPORT_SYMBOL(snd_ctl_remove_id);
 EXPORT_SYMBOL(snd_ctl_remove_id);
 
 
 /**
 /**
- * snd_ctl_remove_unlocked_id - remove the unlocked control of the given id and release it
+ * snd_ctl_remove_user_ctl - remove and release the unlocked user control
  * @file: active control handle
  * @file: active control handle
  * @id: the control id to remove
  * @id: the control id to remove
  *
  *
@@ -423,8 +423,8 @@ EXPORT_SYMBOL(snd_ctl_remove_id);
  * 
  * 
  * Returns 0 if successful, or a negative error code on failure.
  * Returns 0 if successful, or a negative error code on failure.
  */
  */
-static int snd_ctl_remove_unlocked_id(struct snd_ctl_file * file,
-				      struct snd_ctl_elem_id *id)
+static int snd_ctl_remove_user_ctl(struct snd_ctl_file * file,
+				   struct snd_ctl_elem_id *id)
 {
 {
 	struct snd_card *card = file->card;
 	struct snd_card *card = file->card;
 	struct snd_kcontrol *kctl;
 	struct snd_kcontrol *kctl;
@@ -433,15 +433,23 @@ static int snd_ctl_remove_unlocked_id(struct snd_ctl_file * file,
 	down_write(&card->controls_rwsem);
 	down_write(&card->controls_rwsem);
 	kctl = snd_ctl_find_id(card, id);
 	kctl = snd_ctl_find_id(card, id);
 	if (kctl == NULL) {
 	if (kctl == NULL) {
-		up_write(&card->controls_rwsem);
-		return -ENOENT;
+		ret = -ENOENT;
+		goto error;
+	}
+	if (!(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_USER)) {
+		ret = -EINVAL;
+		goto error;
 	}
 	}
 	for (idx = 0; idx < kctl->count; idx++)
 	for (idx = 0; idx < kctl->count; idx++)
 		if (kctl->vd[idx].owner != NULL && kctl->vd[idx].owner != file) {
 		if (kctl->vd[idx].owner != NULL && kctl->vd[idx].owner != file) {
-			up_write(&card->controls_rwsem);
-			return -EBUSY;
+			ret = -EBUSY;
+			goto error;
 		}
 		}
 	ret = snd_ctl_remove(card, kctl);
 	ret = snd_ctl_remove(card, kctl);
+	if (ret < 0)
+		goto error;
+	card->user_ctl_count--;
+error:
 	up_write(&card->controls_rwsem);
 	up_write(&card->controls_rwsem);
 	return ret;
 	return ret;
 }
 }
@@ -951,7 +959,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
 	
 	
 	if (card->user_ctl_count >= MAX_USER_CONTROLS)
 	if (card->user_ctl_count >= MAX_USER_CONTROLS)
 		return -ENOMEM;
 		return -ENOMEM;
-	if (info->count > 1024)
+	if (info->count < 1)
 		return -EINVAL;
 		return -EINVAL;
 	access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
 	access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
 		(info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
 		(info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
@@ -1052,18 +1060,10 @@ static int snd_ctl_elem_remove(struct snd_ctl_file *file,
 			       struct snd_ctl_elem_id __user *_id)
 			       struct snd_ctl_elem_id __user *_id)
 {
 {
 	struct snd_ctl_elem_id id;
 	struct snd_ctl_elem_id id;
-	int err;
 
 
 	if (copy_from_user(&id, _id, sizeof(id)))
 	if (copy_from_user(&id, _id, sizeof(id)))
 		return -EFAULT;
 		return -EFAULT;
-	err = snd_ctl_remove_unlocked_id(file, &id);
-	if (! err) {
-		struct snd_card *card = file->card;
-		down_write(&card->controls_rwsem);
-		card->user_ctl_count--;
-		up_write(&card->controls_rwsem);
-	}
-	return err;
+	return snd_ctl_remove_user_ctl(file, &id);
 }
 }
 
 
 static int snd_ctl_subscribe_events(struct snd_ctl_file *file, int __user *ptr)
 static int snd_ctl_subscribe_events(struct snd_ctl_file *file, int __user *ptr)

+ 3 - 5
sound/core/info.c

@@ -88,12 +88,10 @@ static int resize_info_buffer(struct snd_info_buffer *buffer,
 	char *nbuf;
 	char *nbuf;
 
 
 	nsize = PAGE_ALIGN(nsize);
 	nsize = PAGE_ALIGN(nsize);
-	nbuf = kmalloc(nsize, GFP_KERNEL);
+	nbuf = krealloc(buffer->buffer, nsize, GFP_KERNEL);
 	if (! nbuf)
 	if (! nbuf)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
-	memcpy(nbuf, buffer->buffer, buffer->len);
-	kfree(buffer->buffer);
 	buffer->buffer = nbuf;
 	buffer->buffer = nbuf;
 	buffer->len = nsize;
 	buffer->len = nsize;
 	return 0;
 	return 0;
@@ -108,7 +106,7 @@ static int resize_info_buffer(struct snd_info_buffer *buffer,
  *
  *
  * Returns the size of output string.
  * Returns the size of output string.
  */
  */
-int snd_iprintf(struct snd_info_buffer *buffer, char *fmt,...)
+int snd_iprintf(struct snd_info_buffer *buffer, const char *fmt, ...)
 {
 {
 	va_list args;
 	va_list args;
 	int len, res;
 	int len, res;
@@ -727,7 +725,7 @@ EXPORT_SYMBOL(snd_info_get_line);
  * Returns the updated pointer of the original string so that
  * Returns the updated pointer of the original string so that
  * it can be used for the next call.
  * it can be used for the next call.
  */
  */
-char *snd_info_get_str(char *dest, char *src, int len)
+const char *snd_info_get_str(char *dest, const char *src, int len)
 {
 {
 	int c;
 	int c;
 
 

+ 8 - 0
sound/core/init.c

@@ -31,6 +31,14 @@
 #include <sound/control.h>
 #include <sound/control.h>
 #include <sound/info.h>
 #include <sound/info.h>
 
 
+/* monitor files for graceful shutdown (hotplug) */
+struct snd_monitor_file {
+	struct file *file;
+	const struct file_operations *disconnected_f_op;
+	struct list_head shutdown_list;	/* still need to shutdown */
+	struct list_head list;	/* link of monitor files */
+};
+
 static DEFINE_SPINLOCK(shutdown_lock);
 static DEFINE_SPINLOCK(shutdown_lock);
 static LIST_HEAD(shutdown_files);
 static LIST_HEAD(shutdown_files);
 
 

+ 4 - 0
sound/core/memalloc.c

@@ -199,6 +199,8 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size,
 	case SNDRV_DMA_TYPE_DEV:
 	case SNDRV_DMA_TYPE_DEV:
 		dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr);
 		dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr);
 		break;
 		break;
+#endif
+#ifdef CONFIG_SND_DMA_SGBUF
 	case SNDRV_DMA_TYPE_DEV_SG:
 	case SNDRV_DMA_TYPE_DEV_SG:
 		snd_malloc_sgbuf_pages(device, size, dmab, NULL);
 		snd_malloc_sgbuf_pages(device, size, dmab, NULL);
 		break;
 		break;
@@ -269,6 +271,8 @@ void snd_dma_free_pages(struct snd_dma_buffer *dmab)
 	case SNDRV_DMA_TYPE_DEV:
 	case SNDRV_DMA_TYPE_DEV:
 		snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
 		snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr);
 		break;
 		break;
+#endif
+#ifdef CONFIG_SND_DMA_SGBUF
 	case SNDRV_DMA_TYPE_DEV_SG:
 	case SNDRV_DMA_TYPE_DEV_SG:
 		snd_free_sgbuf_pages(dmab);
 		snd_free_sgbuf_pages(dmab);
 		break;
 		break;

+ 48 - 27
sound/core/misc.c

@@ -24,6 +24,20 @@
 #include <linux/ioport.h>
 #include <linux/ioport.h>
 #include <sound/core.h>
 #include <sound/core.h>
 
 
+#ifdef CONFIG_SND_DEBUG
+
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+#define DEFAULT_DEBUG_LEVEL	2
+#else
+#define DEFAULT_DEBUG_LEVEL	1
+#endif
+
+static int debug = DEFAULT_DEBUG_LEVEL;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0 = disable)");
+
+#endif /* CONFIG_SND_DEBUG */
+
 void release_and_free_resource(struct resource *res)
 void release_and_free_resource(struct resource *res)
 {
 {
 	if (res) {
 	if (res) {
@@ -35,46 +49,53 @@ void release_and_free_resource(struct resource *res)
 EXPORT_SYMBOL(release_and_free_resource);
 EXPORT_SYMBOL(release_and_free_resource);
 
 
 #ifdef CONFIG_SND_VERBOSE_PRINTK
 #ifdef CONFIG_SND_VERBOSE_PRINTK
-void snd_verbose_printk(const char *file, int line, const char *format, ...)
+/* strip the leading path if the given path is absolute */
+static const char *sanity_file_name(const char *path)
 {
 {
-	va_list args;
-	
-	if (format[0] == '<' && format[1] >= '0' && format[1] <= '7' && format[2] == '>') {
-		char tmp[] = "<0>";
+	if (*path == '/')
+		return strrchr(path, '/') + 1;
+	else
+		return path;
+}
+
+/* print file and line with a certain printk prefix */
+static int print_snd_pfx(unsigned int level, const char *path, int line,
+			 const char *format)
+{
+	const char *file = sanity_file_name(path);
+	char tmp[] = "<0>";
+	const char *pfx = level ? KERN_DEBUG : KERN_DEFAULT;
+	int ret = 0;
+
+	if (format[0] == '<' && format[2] == '>') {
 		tmp[1] = format[1];
 		tmp[1] = format[1];
-		printk("%sALSA %s:%d: ", tmp, file, line);
-		format += 3;
-	} else {
-		printk("ALSA %s:%d: ", file, line);
+		pfx = tmp;
+		ret = 1;
 	}
 	}
-	va_start(args, format);
-	vprintk(format, args);
-	va_end(args);
+	printk("%sALSA %s:%d: ", pfx, file, line);
+	return ret;
 }
 }
-
-EXPORT_SYMBOL(snd_verbose_printk);
+#else
+#define print_snd_pfx(level, path, line, format)	0
 #endif
 #endif
 
 
-#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_SND_VERBOSE_PRINTK)
-void snd_verbose_printd(const char *file, int line, const char *format, ...)
+#if defined(CONFIG_SND_DEBUG) || defined(CONFIG_SND_VERBOSE_PRINTK)
+void __snd_printk(unsigned int level, const char *path, int line,
+		  const char *format, ...)
 {
 {
 	va_list args;
 	va_list args;
 	
 	
-	if (format[0] == '<' && format[1] >= '0' && format[1] <= '7' && format[2] == '>') {
-		char tmp[] = "<0>";
-		tmp[1] = format[1];
-		printk("%sALSA %s:%d: ", tmp, file, line);
-		format += 3;
-	} else {
-		printk(KERN_DEBUG "ALSA %s:%d: ", file, line);
-	}
+#ifdef CONFIG_SND_DEBUG	
+	if (debug < level)
+		return;
+#endif
 	va_start(args, format);
 	va_start(args, format);
+	if (print_snd_pfx(level, path, line, format))
+		format += 3; /* skip the printk level-prefix */
 	vprintk(format, args);
 	vprintk(format, args);
 	va_end(args);
 	va_end(args);
-
 }
 }
-
-EXPORT_SYMBOL(snd_verbose_printd);
+EXPORT_SYMBOL_GPL(__snd_printk);
 #endif
 #endif
 
 
 #ifdef CONFIG_PCI
 #ifdef CONFIG_PCI

+ 2 - 1
sound/core/oss/mixer_oss.c

@@ -1154,7 +1154,8 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry,
 				     struct snd_info_buffer *buffer)
 				     struct snd_info_buffer *buffer)
 {
 {
 	struct snd_mixer_oss *mixer = entry->private_data;
 	struct snd_mixer_oss *mixer = entry->private_data;
-	char line[128], str[32], idxstr[16], *cptr;
+	char line[128], str[32], idxstr[16];
+	const char *cptr;
 	int ch, idx;
 	int ch, idx;
 	struct snd_mixer_oss_assign_table *tbl;
 	struct snd_mixer_oss_assign_table *tbl;
 	struct slot *slot;
 	struct slot *slot;

+ 9 - 3
sound/core/oss/pcm_oss.c

@@ -1043,10 +1043,15 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
 	runtime->oss.channels = params_channels(params);
 	runtime->oss.channels = params_channels(params);
 	runtime->oss.rate = params_rate(params);
 	runtime->oss.rate = params_rate(params);
 
 
-	runtime->oss.params = 0;
-	runtime->oss.prepare = 1;
 	vfree(runtime->oss.buffer);
 	vfree(runtime->oss.buffer);
 	runtime->oss.buffer = vmalloc(runtime->oss.period_bytes);
 	runtime->oss.buffer = vmalloc(runtime->oss.period_bytes);
+	if (!runtime->oss.buffer) {
+		err = -ENOMEM;
+		goto failure;
+	}
+
+	runtime->oss.params = 0;
+	runtime->oss.prepare = 1;
 	runtime->oss.buffer_used = 0;
 	runtime->oss.buffer_used = 0;
 	if (runtime->dma_area)
 	if (runtime->dma_area)
 		snd_pcm_format_set_silence(runtime->format, runtime->dma_area, bytes_to_samples(runtime, runtime->dma_bytes));
 		snd_pcm_format_set_silence(runtime->format, runtime->dma_area, bytes_to_samples(runtime, runtime->dma_bytes));
@@ -2836,7 +2841,8 @@ static void snd_pcm_oss_proc_write(struct snd_info_entry *entry,
 				   struct snd_info_buffer *buffer)
 				   struct snd_info_buffer *buffer)
 {
 {
 	struct snd_pcm_str *pstr = entry->private_data;
 	struct snd_pcm_str *pstr = entry->private_data;
-	char line[128], str[32], task_name[32], *ptr;
+	char line[128], str[32], task_name[32];
+	const char *ptr;
 	int idx1;
 	int idx1;
 	struct snd_pcm_oss_setup *setup, *setup1, template;
 	struct snd_pcm_oss_setup *setup, *setup1, template;
 
 

+ 14 - 12
sound/core/pcm.c

@@ -162,18 +162,7 @@ static int snd_pcm_control_ioctl(struct snd_card *card,
 	return -ENOIOCTLCMD;
 	return -ENOIOCTLCMD;
 }
 }
 
 
-#ifdef CONFIG_SND_VERBOSE_PROCFS
-
-#define STATE(v) [SNDRV_PCM_STATE_##v] = #v
-#define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v
-#define READY(v) [SNDRV_PCM_READY_##v] = #v
-#define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v
-#define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v
-#define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v
-#define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v
-#define START(v) [SNDRV_PCM_START_##v] = #v
 #define FORMAT(v) [SNDRV_PCM_FORMAT_##v] = #v
 #define FORMAT(v) [SNDRV_PCM_FORMAT_##v] = #v
-#define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v 
 
 
 static char *snd_pcm_format_names[] = {
 static char *snd_pcm_format_names[] = {
 	FORMAT(S8),
 	FORMAT(S8),
@@ -216,10 +205,23 @@ static char *snd_pcm_format_names[] = {
 	FORMAT(U18_3BE),
 	FORMAT(U18_3BE),
 };
 };
 
 
-static const char *snd_pcm_format_name(snd_pcm_format_t format)
+const char *snd_pcm_format_name(snd_pcm_format_t format)
 {
 {
 	return snd_pcm_format_names[format];
 	return snd_pcm_format_names[format];
 }
 }
+EXPORT_SYMBOL_GPL(snd_pcm_format_name);
+
+#ifdef CONFIG_SND_VERBOSE_PROCFS
+
+#define STATE(v) [SNDRV_PCM_STATE_##v] = #v
+#define STREAM(v) [SNDRV_PCM_STREAM_##v] = #v
+#define READY(v) [SNDRV_PCM_READY_##v] = #v
+#define XRUN(v) [SNDRV_PCM_XRUN_##v] = #v
+#define SILENCE(v) [SNDRV_PCM_SILENCE_##v] = #v
+#define TSTAMP(v) [SNDRV_PCM_TSTAMP_##v] = #v
+#define ACCESS(v) [SNDRV_PCM_ACCESS_##v] = #v
+#define START(v) [SNDRV_PCM_START_##v] = #v
+#define SUBFORMAT(v) [SNDRV_PCM_SUBFORMAT_##v] = #v 
 
 
 static char *snd_pcm_stream_names[] = {
 static char *snd_pcm_stream_names[] = {
 	STREAM(PLAYBACK),
 	STREAM(PLAYBACK),

+ 8 - 4
sound/core/pcm_lib.c

@@ -197,12 +197,16 @@ static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream,
 		avail = snd_pcm_capture_avail(runtime);
 		avail = snd_pcm_capture_avail(runtime);
 	if (avail > runtime->avail_max)
 	if (avail > runtime->avail_max)
 		runtime->avail_max = avail;
 		runtime->avail_max = avail;
-	if (avail >= runtime->stop_threshold) {
-		if (substream->runtime->status->state == SNDRV_PCM_STATE_DRAINING)
+	if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
+		if (avail >= runtime->buffer_size) {
 			snd_pcm_drain_done(substream);
 			snd_pcm_drain_done(substream);
-		else
+			return -EPIPE;
+		}
+	} else {
+		if (avail >= runtime->stop_threshold) {
 			xrun(substream);
 			xrun(substream);
-		return -EPIPE;
+			return -EPIPE;
+		}
 	}
 	}
 	if (avail >= runtime->control->avail_min)
 	if (avail >= runtime->control->avail_min)
 		wake_up(&runtime->sleep);
 		wake_up(&runtime->sleep);

+ 2 - 0
sound/core/pcm_memory.c

@@ -304,6 +304,7 @@ int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,
 
 
 EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all);
 EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all);
 
 
+#ifdef CONFIG_SND_DMA_SGBUF
 /**
 /**
  * snd_pcm_sgbuf_ops_page - get the page struct at the given offset
  * snd_pcm_sgbuf_ops_page - get the page struct at the given offset
  * @substream: the pcm substream instance
  * @substream: the pcm substream instance
@@ -349,6 +350,7 @@ unsigned int snd_pcm_sgbuf_get_chunk_size(struct snd_pcm_substream *substream,
 	return size;
 	return size;
 }
 }
 EXPORT_SYMBOL(snd_pcm_sgbuf_get_chunk_size);
 EXPORT_SYMBOL(snd_pcm_sgbuf_get_chunk_size);
+#endif /* CONFIG_SND_DMA_SGBUF */
 
 
 /**
 /**
  * snd_pcm_lib_malloc_pages - allocate the DMA buffer
  * snd_pcm_lib_malloc_pages - allocate the DMA buffer

+ 41 - 23
sound/core/pcm_native.c

@@ -1343,8 +1343,6 @@ static int snd_pcm_prepare(struct snd_pcm_substream *substream,
 
 
 static int snd_pcm_pre_drain_init(struct snd_pcm_substream *substream, int state)
 static int snd_pcm_pre_drain_init(struct snd_pcm_substream *substream, int state)
 {
 {
-	if (substream->f_flags & O_NONBLOCK)
-		return -EAGAIN;
 	substream->runtime->trigger_master = substream;
 	substream->runtime->trigger_master = substream;
 	return 0;
 	return 0;
 }
 }
@@ -1392,7 +1390,6 @@ static struct action_ops snd_pcm_action_drain_init = {
 struct drain_rec {
 struct drain_rec {
 	struct snd_pcm_substream *substream;
 	struct snd_pcm_substream *substream;
 	wait_queue_t wait;
 	wait_queue_t wait;
-	snd_pcm_uframes_t stop_threshold;
 };
 };
 
 
 static int snd_pcm_drop(struct snd_pcm_substream *substream);
 static int snd_pcm_drop(struct snd_pcm_substream *substream);
@@ -1404,13 +1401,15 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream);
  * After this call, all streams are supposed to be either SETUP or DRAINING
  * After this call, all streams are supposed to be either SETUP or DRAINING
  * (capture only) state.
  * (capture only) state.
  */
  */
-static int snd_pcm_drain(struct snd_pcm_substream *substream)
+static int snd_pcm_drain(struct snd_pcm_substream *substream,
+			 struct file *file)
 {
 {
 	struct snd_card *card;
 	struct snd_card *card;
 	struct snd_pcm_runtime *runtime;
 	struct snd_pcm_runtime *runtime;
 	struct snd_pcm_substream *s;
 	struct snd_pcm_substream *s;
 	int result = 0;
 	int result = 0;
 	int i, num_drecs;
 	int i, num_drecs;
+	int nonblock = 0;
 	struct drain_rec *drec, drec_tmp, *d;
 	struct drain_rec *drec, drec_tmp, *d;
 
 
 	card = substream->pcm->card;
 	card = substream->pcm->card;
@@ -1428,6 +1427,15 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream)
 		}
 		}
 	}
 	}
 
 
+	if (file) {
+		if (file->f_flags & O_NONBLOCK)
+			nonblock = 1;
+	} else if (substream->f_flags & O_NONBLOCK)
+		nonblock = 1;
+
+	if (nonblock)
+		goto lock; /* no need to allocate waitqueues */
+
 	/* allocate temporary record for drain sync */
 	/* allocate temporary record for drain sync */
 	down_read(&snd_pcm_link_rwsem);
 	down_read(&snd_pcm_link_rwsem);
 	if (snd_pcm_stream_linked(substream)) {
 	if (snd_pcm_stream_linked(substream)) {
@@ -1449,16 +1457,11 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream)
 			d->substream = s;
 			d->substream = s;
 			init_waitqueue_entry(&d->wait, current);
 			init_waitqueue_entry(&d->wait, current);
 			add_wait_queue(&runtime->sleep, &d->wait);
 			add_wait_queue(&runtime->sleep, &d->wait);
-			/* stop_threshold fixup to avoid endless loop when
-			 * stop_threshold > buffer_size
-			 */
-			d->stop_threshold = runtime->stop_threshold;
-			if (runtime->stop_threshold > runtime->buffer_size)
-				runtime->stop_threshold = runtime->buffer_size;
 		}
 		}
 	}
 	}
 	up_read(&snd_pcm_link_rwsem);
 	up_read(&snd_pcm_link_rwsem);
 
 
+ lock:
 	snd_pcm_stream_lock_irq(substream);
 	snd_pcm_stream_lock_irq(substream);
 	/* resume pause */
 	/* resume pause */
 	if (substream->runtime->status->state == SNDRV_PCM_STATE_PAUSED)
 	if (substream->runtime->status->state == SNDRV_PCM_STATE_PAUSED)
@@ -1466,9 +1469,12 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream)
 
 
 	/* pre-start/stop - all running streams are changed to DRAINING state */
 	/* pre-start/stop - all running streams are changed to DRAINING state */
 	result = snd_pcm_action(&snd_pcm_action_drain_init, substream, 0);
 	result = snd_pcm_action(&snd_pcm_action_drain_init, substream, 0);
-	if (result < 0) {
-		snd_pcm_stream_unlock_irq(substream);
-		goto _error;
+	if (result < 0)
+		goto unlock;
+	/* in non-blocking, we don't wait in ioctl but let caller poll */
+	if (nonblock) {
+		result = -EAGAIN;
+		goto unlock;
 	}
 	}
 
 
 	for (;;) {
 	for (;;) {
@@ -1504,18 +1510,18 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream)
 		}
 		}
 	}
 	}
 
 
+ unlock:
 	snd_pcm_stream_unlock_irq(substream);
 	snd_pcm_stream_unlock_irq(substream);
 
 
- _error:
-	for (i = 0; i < num_drecs; i++) {
-		d = &drec[i];
-		runtime = d->substream->runtime;
-		remove_wait_queue(&runtime->sleep, &d->wait);
-		runtime->stop_threshold = d->stop_threshold;
+	if (!nonblock) {
+		for (i = 0; i < num_drecs; i++) {
+			d = &drec[i];
+			runtime = d->substream->runtime;
+			remove_wait_queue(&runtime->sleep, &d->wait);
+		}
+		if (drec != &drec_tmp)
+			kfree(drec);
 	}
 	}
-
-	if (drec != &drec_tmp)
-		kfree(drec);
 	snd_power_unlock(card);
 	snd_power_unlock(card);
 
 
 	return result;
 	return result;
@@ -2208,6 +2214,9 @@ static snd_pcm_sframes_t snd_pcm_playback_rewind(struct snd_pcm_substream *subst
 	case SNDRV_PCM_STATE_XRUN:
 	case SNDRV_PCM_STATE_XRUN:
 		ret = -EPIPE;
 		ret = -EPIPE;
 		goto __end;
 		goto __end;
+	case SNDRV_PCM_STATE_SUSPENDED:
+		ret = -ESTRPIPE;
+		goto __end;
 	default:
 	default:
 		ret = -EBADFD;
 		ret = -EBADFD;
 		goto __end;
 		goto __end;
@@ -2253,6 +2262,9 @@ static snd_pcm_sframes_t snd_pcm_capture_rewind(struct snd_pcm_substream *substr
 	case SNDRV_PCM_STATE_XRUN:
 	case SNDRV_PCM_STATE_XRUN:
 		ret = -EPIPE;
 		ret = -EPIPE;
 		goto __end;
 		goto __end;
+	case SNDRV_PCM_STATE_SUSPENDED:
+		ret = -ESTRPIPE;
+		goto __end;
 	default:
 	default:
 		ret = -EBADFD;
 		ret = -EBADFD;
 		goto __end;
 		goto __end;
@@ -2299,6 +2311,9 @@ static snd_pcm_sframes_t snd_pcm_playback_forward(struct snd_pcm_substream *subs
 	case SNDRV_PCM_STATE_XRUN:
 	case SNDRV_PCM_STATE_XRUN:
 		ret = -EPIPE;
 		ret = -EPIPE;
 		goto __end;
 		goto __end;
+	case SNDRV_PCM_STATE_SUSPENDED:
+		ret = -ESTRPIPE;
+		goto __end;
 	default:
 	default:
 		ret = -EBADFD;
 		ret = -EBADFD;
 		goto __end;
 		goto __end;
@@ -2345,6 +2360,9 @@ static snd_pcm_sframes_t snd_pcm_capture_forward(struct snd_pcm_substream *subst
 	case SNDRV_PCM_STATE_XRUN:
 	case SNDRV_PCM_STATE_XRUN:
 		ret = -EPIPE;
 		ret = -EPIPE;
 		goto __end;
 		goto __end;
+	case SNDRV_PCM_STATE_SUSPENDED:
+		ret = -ESTRPIPE;
+		goto __end;
 	default:
 	default:
 		ret = -EBADFD;
 		ret = -EBADFD;
 		goto __end;
 		goto __end;
@@ -2544,7 +2562,7 @@ static int snd_pcm_common_ioctl1(struct file *file,
 		return snd_pcm_hw_params_old_user(substream, arg);
 		return snd_pcm_hw_params_old_user(substream, arg);
 #endif
 #endif
 	case SNDRV_PCM_IOCTL_DRAIN:
 	case SNDRV_PCM_IOCTL_DRAIN:
-		return snd_pcm_drain(substream);
+		return snd_pcm_drain(substream, file);
 	case SNDRV_PCM_IOCTL_DROP:
 	case SNDRV_PCM_IOCTL_DROP:
 		return snd_pcm_drop(substream);
 		return snd_pcm_drop(substream);
 	case SNDRV_PCM_IOCTL_PAUSE:
 	case SNDRV_PCM_IOCTL_PAUSE:

+ 1 - 1
sound/core/rawmidi.c

@@ -274,7 +274,7 @@ static int open_substream(struct snd_rawmidi *rmidi,
 		return err;
 		return err;
 	substream->opened = 1;
 	substream->opened = 1;
 	if (substream->use_count++ == 0)
 	if (substream->use_count++ == 0)
-		substream->active_sensing = 1;
+		substream->active_sensing = 0;
 	if (mode & SNDRV_RAWMIDI_LFLG_APPEND)
 	if (mode & SNDRV_RAWMIDI_LFLG_APPEND)
 		substream->append = 1;
 		substream->append = 1;
 	rmidi->streams[substream->stream].substream_opened++;
 	rmidi->streams[substream->stream].substream_opened++;

+ 8 - 6
sound/core/seq/oss/seq_oss_midi.c

@@ -20,6 +20,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
  */
 
 
+#include <sound/asoundef.h>
 #include "seq_oss_midi.h"
 #include "seq_oss_midi.h"
 #include "seq_oss_readq.h"
 #include "seq_oss_readq.h"
 #include "seq_oss_timer.h"
 #include "seq_oss_timer.h"
@@ -476,19 +477,20 @@ snd_seq_oss_midi_reset(struct seq_oss_devinfo *dp, int dev)
 		ev.source.port = dp->port;
 		ev.source.port = dp->port;
 		if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH) {
 		if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_SYNTH) {
 			ev.type = SNDRV_SEQ_EVENT_SENSING;
 			ev.type = SNDRV_SEQ_EVENT_SENSING;
-			snd_seq_oss_dispatch(dp, &ev, 0, 0); /* active sensing */
+			snd_seq_oss_dispatch(dp, &ev, 0, 0);
 		}
 		}
 		for (c = 0; c < 16; c++) {
 		for (c = 0; c < 16; c++) {
 			ev.type = SNDRV_SEQ_EVENT_CONTROLLER;
 			ev.type = SNDRV_SEQ_EVENT_CONTROLLER;
 			ev.data.control.channel = c;
 			ev.data.control.channel = c;
-			ev.data.control.param = 123;
-			snd_seq_oss_dispatch(dp, &ev, 0, 0); /* all notes off */
+			ev.data.control.param = MIDI_CTL_ALL_NOTES_OFF;
+			snd_seq_oss_dispatch(dp, &ev, 0, 0);
 			if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) {
 			if (dp->seq_mode == SNDRV_SEQ_OSS_MODE_MUSIC) {
-				ev.data.control.param = 121;
-				snd_seq_oss_dispatch(dp, &ev, 0, 0); /* reset all controllers */
+				ev.data.control.param =
+					MIDI_CTL_RESET_CONTROLLERS;
+				snd_seq_oss_dispatch(dp, &ev, 0, 0);
 				ev.type = SNDRV_SEQ_EVENT_PITCHBEND;
 				ev.type = SNDRV_SEQ_EVENT_PITCHBEND;
 				ev.data.control.value = 0;
 				ev.data.control.value = 0;
-				snd_seq_oss_dispatch(dp, &ev, 0, 0); /* bender off */
+				snd_seq_oss_dispatch(dp, &ev, 0, 0);
 			}
 			}
 		}
 		}
 	}
 	}

+ 3 - 4
sound/core/seq/seq_midi.c

@@ -120,7 +120,8 @@ static int dump_midi(struct snd_rawmidi_substream *substream, const char *buf, i
 		return -EINVAL;
 		return -EINVAL;
 	runtime = substream->runtime;
 	runtime = substream->runtime;
 	if ((tmp = runtime->avail) < count) {
 	if ((tmp = runtime->avail) < count) {
-		snd_printd("warning, output event was lost (count = %i, available = %i)\n", count, tmp);
+		if (printk_ratelimit())
+			snd_printk(KERN_ERR "MIDI output buffer overrun\n");
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
 	if (snd_rawmidi_kernel_write(substream, buf, count) < count)
 	if (snd_rawmidi_kernel_write(substream, buf, count) < count)
@@ -236,6 +237,7 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info
 	memset(&params, 0, sizeof(params));
 	memset(&params, 0, sizeof(params));
 	params.avail_min = 1;
 	params.avail_min = 1;
 	params.buffer_size = output_buffer_size;
 	params.buffer_size = output_buffer_size;
+	params.no_active_sensing = 1;
 	if ((err = snd_rawmidi_output_params(msynth->output_rfile.output, &params)) < 0) {
 	if ((err = snd_rawmidi_output_params(msynth->output_rfile.output, &params)) < 0) {
 		snd_rawmidi_kernel_release(&msynth->output_rfile);
 		snd_rawmidi_kernel_release(&msynth->output_rfile);
 		return err;
 		return err;
@@ -248,12 +250,9 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info
 static int midisynth_unuse(void *private_data, struct snd_seq_port_subscribe *info)
 static int midisynth_unuse(void *private_data, struct snd_seq_port_subscribe *info)
 {
 {
 	struct seq_midisynth *msynth = private_data;
 	struct seq_midisynth *msynth = private_data;
-	unsigned char buf = 0xff; /* MIDI reset */
 
 
 	if (snd_BUG_ON(!msynth->output_rfile.output))
 	if (snd_BUG_ON(!msynth->output_rfile.output))
 		return -EINVAL;
 		return -EINVAL;
-	/* sending single MIDI reset message to shut the device up */
-	snd_rawmidi_kernel_write(msynth->output_rfile.output, &buf, 1);
 	snd_rawmidi_drain_output(msynth->output_rfile.output);
 	snd_rawmidi_drain_output(msynth->output_rfile.output);
 	return snd_rawmidi_kernel_release(&msynth->output_rfile);
 	return snd_rawmidi_kernel_release(&msynth->output_rfile);
 }
 }

+ 6 - 2
sound/core/vmaster.c

@@ -353,7 +353,8 @@ static void master_free(struct snd_kcontrol *kcontrol)
  *
  *
  * The optional argument @tlv can be used to specify the TLV information
  * The optional argument @tlv can be used to specify the TLV information
  * for dB scale of the master control.  It should be a single element
  * for dB scale of the master control.  It should be a single element
- * with #SNDRV_CTL_TLVT_DB_SCALE type, and should be the max 0dB.
+ * with #SNDRV_CTL_TLVT_DB_SCALE, #SNDRV_CTL_TLV_DB_MINMAX or
+ * #SNDRV_CTL_TLVT_DB_MINMAX_MUTE type, and should be the max 0dB.
  */
  */
 struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
 struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
 						 const unsigned int *tlv)
 						 const unsigned int *tlv)
@@ -384,7 +385,10 @@ struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,
 	kctl->private_free = master_free;
 	kctl->private_free = master_free;
 
 
 	/* additional (constant) TLV read */
 	/* additional (constant) TLV read */
-	if (tlv && tlv[0] == SNDRV_CTL_TLVT_DB_SCALE) {
+	if (tlv &&
+	    (tlv[0] == SNDRV_CTL_TLVT_DB_SCALE ||
+	     tlv[0] == SNDRV_CTL_TLVT_DB_MINMAX ||
+	     tlv[0] == SNDRV_CTL_TLVT_DB_MINMAX_MUTE)) {
 		kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
 		kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
 		memcpy(master->tlv, tlv, sizeof(master->tlv));
 		memcpy(master->tlv, tlv, sizeof(master->tlv));
 		kctl->tlv.p = master->tlv;
 		kctl->tlv.p = master->tlv;

+ 537 - 163
sound/drivers/dummy.c

@@ -25,12 +25,15 @@
 #include <linux/slab.h>
 #include <linux/slab.h>
 #include <linux/time.h>
 #include <linux/time.h>
 #include <linux/wait.h>
 #include <linux/wait.h>
+#include <linux/hrtimer.h>
+#include <linux/math64.h>
 #include <linux/moduleparam.h>
 #include <linux/moduleparam.h>
 #include <sound/core.h>
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/control.h>
 #include <sound/tlv.h>
 #include <sound/tlv.h>
 #include <sound/pcm.h>
 #include <sound/pcm.h>
 #include <sound/rawmidi.h>
 #include <sound/rawmidi.h>
+#include <sound/info.h>
 #include <sound/initval.h>
 #include <sound/initval.h>
 
 
 MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
 MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
@@ -39,7 +42,7 @@ MODULE_LICENSE("GPL");
 MODULE_SUPPORTED_DEVICE("{{ALSA,Dummy soundcard}}");
 MODULE_SUPPORTED_DEVICE("{{ALSA,Dummy soundcard}}");
 
 
 #define MAX_PCM_DEVICES		4
 #define MAX_PCM_DEVICES		4
-#define MAX_PCM_SUBSTREAMS	16
+#define MAX_PCM_SUBSTREAMS	128
 #define MAX_MIDI_DEVICES	2
 #define MAX_MIDI_DEVICES	2
 
 
 #if 0 /* emu10k1 emulation */
 #if 0 /* emu10k1 emulation */
@@ -148,6 +151,10 @@ static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
 static int pcm_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
 static int pcm_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
 static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8};
 static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8};
 //static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
 //static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
+#ifdef CONFIG_HIGH_RES_TIMERS
+static int hrtimer = 1;
+#endif
+static int fake_buffer = 1;
 
 
 module_param_array(index, int, NULL, 0444);
 module_param_array(index, int, NULL, 0444);
 MODULE_PARM_DESC(index, "Index value for dummy soundcard.");
 MODULE_PARM_DESC(index, "Index value for dummy soundcard.");
@@ -161,6 +168,12 @@ module_param_array(pcm_substreams, int, NULL, 0444);
 MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-16) for dummy driver.");
 MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-16) for dummy driver.");
 //module_param_array(midi_devs, int, NULL, 0444);
 //module_param_array(midi_devs, int, NULL, 0444);
 //MODULE_PARM_DESC(midi_devs, "MIDI devices # (0-2) for dummy driver.");
 //MODULE_PARM_DESC(midi_devs, "MIDI devices # (0-2) for dummy driver.");
+module_param(fake_buffer, bool, 0444);
+MODULE_PARM_DESC(fake_buffer, "Fake buffer allocations.");
+#ifdef CONFIG_HIGH_RES_TIMERS
+module_param(hrtimer, bool, 0644);
+MODULE_PARM_DESC(hrtimer, "Use hrtimer as the timer source.");
+#endif
 
 
 static struct platform_device *devices[SNDRV_CARDS];
 static struct platform_device *devices[SNDRV_CARDS];
 
 
@@ -171,137 +184,324 @@ static struct platform_device *devices[SNDRV_CARDS];
 #define MIXER_ADDR_CD		4
 #define MIXER_ADDR_CD		4
 #define MIXER_ADDR_LAST		4
 #define MIXER_ADDR_LAST		4
 
 
+struct dummy_timer_ops {
+	int (*create)(struct snd_pcm_substream *);
+	void (*free)(struct snd_pcm_substream *);
+	int (*prepare)(struct snd_pcm_substream *);
+	int (*start)(struct snd_pcm_substream *);
+	int (*stop)(struct snd_pcm_substream *);
+	snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *);
+};
+
 struct snd_dummy {
 struct snd_dummy {
 	struct snd_card *card;
 	struct snd_card *card;
 	struct snd_pcm *pcm;
 	struct snd_pcm *pcm;
 	spinlock_t mixer_lock;
 	spinlock_t mixer_lock;
 	int mixer_volume[MIXER_ADDR_LAST+1][2];
 	int mixer_volume[MIXER_ADDR_LAST+1][2];
 	int capture_source[MIXER_ADDR_LAST+1][2];
 	int capture_source[MIXER_ADDR_LAST+1][2];
+	const struct dummy_timer_ops *timer_ops;
 };
 };
 
 
-struct snd_dummy_pcm {
-	struct snd_dummy *dummy;
+/*
+ * system timer interface
+ */
+
+struct dummy_systimer_pcm {
 	spinlock_t lock;
 	spinlock_t lock;
 	struct timer_list timer;
 	struct timer_list timer;
-	unsigned int pcm_buffer_size;
-	unsigned int pcm_period_size;
-	unsigned int pcm_bps;		/* bytes per second */
-	unsigned int pcm_hz;		/* HZ */
-	unsigned int pcm_irq_pos;	/* IRQ position */
-	unsigned int pcm_buf_pos;	/* position in buffer */
+	unsigned long base_time;
+	unsigned int frac_pos;	/* fractional sample position (based HZ) */
+	unsigned int frac_period_rest;
+	unsigned int frac_buffer_size;	/* buffer_size * HZ */
+	unsigned int frac_period_size;	/* period_size * HZ */
+	unsigned int rate;
+	int elapsed;
 	struct snd_pcm_substream *substream;
 	struct snd_pcm_substream *substream;
 };
 };
 
 
-
-static inline void snd_card_dummy_pcm_timer_start(struct snd_dummy_pcm *dpcm)
+static void dummy_systimer_rearm(struct dummy_systimer_pcm *dpcm)
 {
 {
-	dpcm->timer.expires = 1 + jiffies;
+	dpcm->timer.expires = jiffies +
+		(dpcm->frac_period_rest + dpcm->rate - 1) / dpcm->rate;
 	add_timer(&dpcm->timer);
 	add_timer(&dpcm->timer);
 }
 }
 
 
-static inline void snd_card_dummy_pcm_timer_stop(struct snd_dummy_pcm *dpcm)
+static void dummy_systimer_update(struct dummy_systimer_pcm *dpcm)
 {
 {
-	del_timer(&dpcm->timer);
+	unsigned long delta;
+
+	delta = jiffies - dpcm->base_time;
+	if (!delta)
+		return;
+	dpcm->base_time += delta;
+	delta *= dpcm->rate;
+	dpcm->frac_pos += delta;
+	while (dpcm->frac_pos >= dpcm->frac_buffer_size)
+		dpcm->frac_pos -= dpcm->frac_buffer_size;
+	while (dpcm->frac_period_rest <= delta) {
+		dpcm->elapsed++;
+		dpcm->frac_period_rest += dpcm->frac_period_size;
+	}
+	dpcm->frac_period_rest -= delta;
 }
 }
 
 
-static int snd_card_dummy_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+static int dummy_systimer_start(struct snd_pcm_substream *substream)
 {
 {
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct snd_dummy_pcm *dpcm = runtime->private_data;
-	int err = 0;
+	struct dummy_systimer_pcm *dpcm = substream->runtime->private_data;
+	spin_lock(&dpcm->lock);
+	dpcm->base_time = jiffies;
+	dummy_systimer_rearm(dpcm);
+	spin_unlock(&dpcm->lock);
+	return 0;
+}
 
 
+static int dummy_systimer_stop(struct snd_pcm_substream *substream)
+{
+	struct dummy_systimer_pcm *dpcm = substream->runtime->private_data;
 	spin_lock(&dpcm->lock);
 	spin_lock(&dpcm->lock);
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-	case SNDRV_PCM_TRIGGER_RESUME:
-		snd_card_dummy_pcm_timer_start(dpcm);
-		break;
-	case SNDRV_PCM_TRIGGER_STOP:
-	case SNDRV_PCM_TRIGGER_SUSPEND:
-		snd_card_dummy_pcm_timer_stop(dpcm);
-		break;
-	default:
-		err = -EINVAL;
-		break;
-	}
+	del_timer(&dpcm->timer);
 	spin_unlock(&dpcm->lock);
 	spin_unlock(&dpcm->lock);
 	return 0;
 	return 0;
 }
 }
 
 
-static int snd_card_dummy_pcm_prepare(struct snd_pcm_substream *substream)
+static int dummy_systimer_prepare(struct snd_pcm_substream *substream)
 {
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct snd_dummy_pcm *dpcm = runtime->private_data;
-	int bps;
-
-	bps = snd_pcm_format_width(runtime->format) * runtime->rate *
-		runtime->channels / 8;
-
-	if (bps <= 0)
-		return -EINVAL;
-
-	dpcm->pcm_bps = bps;
-	dpcm->pcm_hz = HZ;
-	dpcm->pcm_buffer_size = snd_pcm_lib_buffer_bytes(substream);
-	dpcm->pcm_period_size = snd_pcm_lib_period_bytes(substream);
-	dpcm->pcm_irq_pos = 0;
-	dpcm->pcm_buf_pos = 0;
+	struct dummy_systimer_pcm *dpcm = runtime->private_data;
 
 
-	snd_pcm_format_set_silence(runtime->format, runtime->dma_area,
-			bytes_to_samples(runtime, runtime->dma_bytes));
+	dpcm->frac_pos = 0;
+	dpcm->rate = runtime->rate;
+	dpcm->frac_buffer_size = runtime->buffer_size * HZ;
+	dpcm->frac_period_size = runtime->period_size * HZ;
+	dpcm->frac_period_rest = dpcm->frac_period_size;
+	dpcm->elapsed = 0;
 
 
 	return 0;
 	return 0;
 }
 }
 
 
-static void snd_card_dummy_pcm_timer_function(unsigned long data)
+static void dummy_systimer_callback(unsigned long data)
 {
 {
-	struct snd_dummy_pcm *dpcm = (struct snd_dummy_pcm *)data;
+	struct dummy_systimer_pcm *dpcm = (struct dummy_systimer_pcm *)data;
 	unsigned long flags;
 	unsigned long flags;
+	int elapsed = 0;
 	
 	
 	spin_lock_irqsave(&dpcm->lock, flags);
 	spin_lock_irqsave(&dpcm->lock, flags);
-	dpcm->timer.expires = 1 + jiffies;
-	add_timer(&dpcm->timer);
-	dpcm->pcm_irq_pos += dpcm->pcm_bps;
-	dpcm->pcm_buf_pos += dpcm->pcm_bps;
-	dpcm->pcm_buf_pos %= dpcm->pcm_buffer_size * dpcm->pcm_hz;
-	if (dpcm->pcm_irq_pos >= dpcm->pcm_period_size * dpcm->pcm_hz) {
-		dpcm->pcm_irq_pos %= dpcm->pcm_period_size * dpcm->pcm_hz;
-		spin_unlock_irqrestore(&dpcm->lock, flags);
+	dummy_systimer_update(dpcm);
+	dummy_systimer_rearm(dpcm);
+	elapsed = dpcm->elapsed;
+	dpcm->elapsed = 0;
+	spin_unlock_irqrestore(&dpcm->lock, flags);
+	if (elapsed)
+		snd_pcm_period_elapsed(dpcm->substream);
+}
+
+static snd_pcm_uframes_t
+dummy_systimer_pointer(struct snd_pcm_substream *substream)
+{
+	struct dummy_systimer_pcm *dpcm = substream->runtime->private_data;
+	snd_pcm_uframes_t pos;
+
+	spin_lock(&dpcm->lock);
+	dummy_systimer_update(dpcm);
+	pos = dpcm->frac_pos / HZ;
+	spin_unlock(&dpcm->lock);
+	return pos;
+}
+
+static int dummy_systimer_create(struct snd_pcm_substream *substream)
+{
+	struct dummy_systimer_pcm *dpcm;
+
+	dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
+	if (!dpcm)
+		return -ENOMEM;
+	substream->runtime->private_data = dpcm;
+	init_timer(&dpcm->timer);
+	dpcm->timer.data = (unsigned long) dpcm;
+	dpcm->timer.function = dummy_systimer_callback;
+	spin_lock_init(&dpcm->lock);
+	dpcm->substream = substream;
+	return 0;
+}
+
+static void dummy_systimer_free(struct snd_pcm_substream *substream)
+{
+	kfree(substream->runtime->private_data);
+}
+
+static struct dummy_timer_ops dummy_systimer_ops = {
+	.create =	dummy_systimer_create,
+	.free =		dummy_systimer_free,
+	.prepare =	dummy_systimer_prepare,
+	.start =	dummy_systimer_start,
+	.stop =		dummy_systimer_stop,
+	.pointer =	dummy_systimer_pointer,
+};
+
+#ifdef CONFIG_HIGH_RES_TIMERS
+/*
+ * hrtimer interface
+ */
+
+struct dummy_hrtimer_pcm {
+	ktime_t base_time;
+	ktime_t period_time;
+	atomic_t running;
+	struct hrtimer timer;
+	struct tasklet_struct tasklet;
+	struct snd_pcm_substream *substream;
+};
+
+static void dummy_hrtimer_pcm_elapsed(unsigned long priv)
+{
+	struct dummy_hrtimer_pcm *dpcm = (struct dummy_hrtimer_pcm *)priv;
+	if (atomic_read(&dpcm->running))
 		snd_pcm_period_elapsed(dpcm->substream);
 		snd_pcm_period_elapsed(dpcm->substream);
-	} else
-		spin_unlock_irqrestore(&dpcm->lock, flags);
 }
 }
 
 
-static snd_pcm_uframes_t snd_card_dummy_pcm_pointer(struct snd_pcm_substream *substream)
+static enum hrtimer_restart dummy_hrtimer_callback(struct hrtimer *timer)
+{
+	struct dummy_hrtimer_pcm *dpcm;
+
+	dpcm = container_of(timer, struct dummy_hrtimer_pcm, timer);
+	if (!atomic_read(&dpcm->running))
+		return HRTIMER_NORESTART;
+	tasklet_schedule(&dpcm->tasklet);
+	hrtimer_forward_now(timer, dpcm->period_time);
+	return HRTIMER_RESTART;
+}
+
+static int dummy_hrtimer_start(struct snd_pcm_substream *substream)
+{
+	struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data;
+
+	dpcm->base_time = hrtimer_cb_get_time(&dpcm->timer);
+	hrtimer_start(&dpcm->timer, dpcm->period_time, HRTIMER_MODE_REL);
+	atomic_set(&dpcm->running, 1);
+	return 0;
+}
+
+static int dummy_hrtimer_stop(struct snd_pcm_substream *substream)
+{
+	struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data;
+
+	atomic_set(&dpcm->running, 0);
+	hrtimer_cancel(&dpcm->timer);
+	return 0;
+}
+
+static inline void dummy_hrtimer_sync(struct dummy_hrtimer_pcm *dpcm)
+{
+	tasklet_kill(&dpcm->tasklet);
+}
+
+static snd_pcm_uframes_t
+dummy_hrtimer_pointer(struct snd_pcm_substream *substream)
 {
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct snd_dummy_pcm *dpcm = runtime->private_data;
+	struct dummy_hrtimer_pcm *dpcm = runtime->private_data;
+	u64 delta;
+	u32 pos;
+
+	delta = ktime_us_delta(hrtimer_cb_get_time(&dpcm->timer),
+			       dpcm->base_time);
+	delta = div_u64(delta * runtime->rate + 999999, 1000000);
+	div_u64_rem(delta, runtime->buffer_size, &pos);
+	return pos;
+}
 
 
-	return bytes_to_frames(runtime, dpcm->pcm_buf_pos / dpcm->pcm_hz);
+static int dummy_hrtimer_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct dummy_hrtimer_pcm *dpcm = runtime->private_data;
+	unsigned int period, rate;
+	long sec;
+	unsigned long nsecs;
+
+	dummy_hrtimer_sync(dpcm);
+	period = runtime->period_size;
+	rate = runtime->rate;
+	sec = period / rate;
+	period %= rate;
+	nsecs = div_u64((u64)period * 1000000000UL + rate - 1, rate);
+	dpcm->period_time = ktime_set(sec, nsecs);
+
+	return 0;
 }
 }
 
 
-static struct snd_pcm_hardware snd_card_dummy_playback =
+static int dummy_hrtimer_create(struct snd_pcm_substream *substream)
 {
 {
-	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID),
-	.formats =		USE_FORMATS,
-	.rates =		USE_RATE,
-	.rate_min =		USE_RATE_MIN,
-	.rate_max =		USE_RATE_MAX,
-	.channels_min =		USE_CHANNELS_MIN,
-	.channels_max =		USE_CHANNELS_MAX,
-	.buffer_bytes_max =	MAX_BUFFER_SIZE,
-	.period_bytes_min =	64,
-	.period_bytes_max =	MAX_PERIOD_SIZE,
-	.periods_min =		USE_PERIODS_MIN,
-	.periods_max =		USE_PERIODS_MAX,
-	.fifo_size =		0,
+	struct dummy_hrtimer_pcm *dpcm;
+
+	dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
+	if (!dpcm)
+		return -ENOMEM;
+	substream->runtime->private_data = dpcm;
+	hrtimer_init(&dpcm->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	dpcm->timer.function = dummy_hrtimer_callback;
+	dpcm->substream = substream;
+	atomic_set(&dpcm->running, 0);
+	tasklet_init(&dpcm->tasklet, dummy_hrtimer_pcm_elapsed,
+		     (unsigned long)dpcm);
+	return 0;
+}
+
+static void dummy_hrtimer_free(struct snd_pcm_substream *substream)
+{
+	struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data;
+	dummy_hrtimer_sync(dpcm);
+	kfree(dpcm);
+}
+
+static struct dummy_timer_ops dummy_hrtimer_ops = {
+	.create =	dummy_hrtimer_create,
+	.free =		dummy_hrtimer_free,
+	.prepare =	dummy_hrtimer_prepare,
+	.start =	dummy_hrtimer_start,
+	.stop =		dummy_hrtimer_stop,
+	.pointer =	dummy_hrtimer_pointer,
 };
 };
 
 
-static struct snd_pcm_hardware snd_card_dummy_capture =
+#endif /* CONFIG_HIGH_RES_TIMERS */
+
+/*
+ * PCM interface
+ */
+
+static int dummy_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 {
 {
-	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID),
+	struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		return dummy->timer_ops->start(substream);
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		return dummy->timer_ops->stop(substream);
+	}
+	return -EINVAL;
+}
+
+static int dummy_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
+
+	return dummy->timer_ops->prepare(substream);
+}
+
+static snd_pcm_uframes_t dummy_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
+
+	return dummy->timer_ops->pointer(substream);
+}
+
+static struct snd_pcm_hardware dummy_pcm_hardware = {
+	.info =			(SNDRV_PCM_INFO_MMAP |
+				 SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_RESUME |
+				 SNDRV_PCM_INFO_MMAP_VALID),
 	.formats =		USE_FORMATS,
 	.formats =		USE_FORMATS,
 	.rates =		USE_RATE,
 	.rates =		USE_RATE,
 	.rate_min =		USE_RATE_MIN,
 	.rate_min =		USE_RATE_MIN,
@@ -316,123 +516,152 @@ static struct snd_pcm_hardware snd_card_dummy_capture =
 	.fifo_size =		0,
 	.fifo_size =		0,
 };
 };
 
 
-static void snd_card_dummy_runtime_free(struct snd_pcm_runtime *runtime)
-{
-	kfree(runtime->private_data);
-}
-
-static int snd_card_dummy_hw_params(struct snd_pcm_substream *substream,
-				    struct snd_pcm_hw_params *hw_params)
+static int dummy_pcm_hw_params(struct snd_pcm_substream *substream,
+			       struct snd_pcm_hw_params *hw_params)
 {
 {
-	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+	if (fake_buffer) {
+		/* runtime->dma_bytes has to be set manually to allow mmap */
+		substream->runtime->dma_bytes = params_buffer_bytes(hw_params);
+		return 0;
+	}
+	return snd_pcm_lib_malloc_pages(substream,
+					params_buffer_bytes(hw_params));
 }
 }
 
 
-static int snd_card_dummy_hw_free(struct snd_pcm_substream *substream)
+static int dummy_pcm_hw_free(struct snd_pcm_substream *substream)
 {
 {
+	if (fake_buffer)
+		return 0;
 	return snd_pcm_lib_free_pages(substream);
 	return snd_pcm_lib_free_pages(substream);
 }
 }
 
 
-static struct snd_dummy_pcm *new_pcm_stream(struct snd_pcm_substream *substream)
-{
-	struct snd_dummy_pcm *dpcm;
-
-	dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
-	if (! dpcm)
-		return dpcm;
-	init_timer(&dpcm->timer);
-	dpcm->timer.data = (unsigned long) dpcm;
-	dpcm->timer.function = snd_card_dummy_pcm_timer_function;
-	spin_lock_init(&dpcm->lock);
-	dpcm->substream = substream;
-	return dpcm;
-}
-
-static int snd_card_dummy_playback_open(struct snd_pcm_substream *substream)
+static int dummy_pcm_open(struct snd_pcm_substream *substream)
 {
 {
+	struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct snd_dummy_pcm *dpcm;
 	int err;
 	int err;
 
 
-	if ((dpcm = new_pcm_stream(substream)) == NULL)
-		return -ENOMEM;
-	runtime->private_data = dpcm;
-	/* makes the infrastructure responsible for freeing dpcm */
-	runtime->private_free = snd_card_dummy_runtime_free;
-	runtime->hw = snd_card_dummy_playback;
+	dummy->timer_ops = &dummy_systimer_ops;
+#ifdef CONFIG_HIGH_RES_TIMERS
+	if (hrtimer)
+		dummy->timer_ops = &dummy_hrtimer_ops;
+#endif
+
+	err = dummy->timer_ops->create(substream);
+	if (err < 0)
+		return err;
+
+	runtime->hw = dummy_pcm_hardware;
 	if (substream->pcm->device & 1) {
 	if (substream->pcm->device & 1) {
 		runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED;
 		runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED;
 		runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED;
 		runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED;
 	}
 	}
 	if (substream->pcm->device & 2)
 	if (substream->pcm->device & 2)
-		runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID);
-	err = add_playback_constraints(runtime);
-	if (err < 0)
+		runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP |
+				      SNDRV_PCM_INFO_MMAP_VALID);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		err = add_playback_constraints(substream->runtime);
+	else
+		err = add_capture_constraints(substream->runtime);
+	if (err < 0) {
+		dummy->timer_ops->free(substream);
 		return err;
 		return err;
-
+	}
 	return 0;
 	return 0;
 }
 }
 
 
-static int snd_card_dummy_capture_open(struct snd_pcm_substream *substream)
+static int dummy_pcm_close(struct snd_pcm_substream *substream)
 {
 {
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct snd_dummy_pcm *dpcm;
-	int err;
+	struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
+	dummy->timer_ops->free(substream);
+	return 0;
+}
 
 
-	if ((dpcm = new_pcm_stream(substream)) == NULL)
-		return -ENOMEM;
-	runtime->private_data = dpcm;
-	/* makes the infrastructure responsible for freeing dpcm */
-	runtime->private_free = snd_card_dummy_runtime_free;
-	runtime->hw = snd_card_dummy_capture;
-	if (substream->pcm->device == 1) {
-		runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED;
-		runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED;
+/*
+ * dummy buffer handling
+ */
+
+static void *dummy_page[2];
+
+static void free_fake_buffer(void)
+{
+	if (fake_buffer) {
+		int i;
+		for (i = 0; i < 2; i++)
+			if (dummy_page[i]) {
+				free_page((unsigned long)dummy_page[i]);
+				dummy_page[i] = NULL;
+			}
 	}
 	}
-	if (substream->pcm->device & 2)
-		runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID);
-	err = add_capture_constraints(runtime);
-	if (err < 0)
-		return err;
+}
 
 
+static int alloc_fake_buffer(void)
+{
+	int i;
+
+	if (!fake_buffer)
+		return 0;
+	for (i = 0; i < 2; i++) {
+		dummy_page[i] = (void *)get_zeroed_page(GFP_KERNEL);
+		if (!dummy_page[i]) {
+			free_fake_buffer();
+			return -ENOMEM;
+		}
+	}
 	return 0;
 	return 0;
 }
 }
 
 
-static int snd_card_dummy_playback_close(struct snd_pcm_substream *substream)
+static int dummy_pcm_copy(struct snd_pcm_substream *substream,
+			  int channel, snd_pcm_uframes_t pos,
+			  void __user *dst, snd_pcm_uframes_t count)
 {
 {
-	return 0;
+	return 0; /* do nothing */
 }
 }
 
 
-static int snd_card_dummy_capture_close(struct snd_pcm_substream *substream)
+static int dummy_pcm_silence(struct snd_pcm_substream *substream,
+			     int channel, snd_pcm_uframes_t pos,
+			     snd_pcm_uframes_t count)
 {
 {
-	return 0;
+	return 0; /* do nothing */
+}
+
+static struct page *dummy_pcm_page(struct snd_pcm_substream *substream,
+				   unsigned long offset)
+{
+	return virt_to_page(dummy_page[substream->stream]); /* the same page */
 }
 }
 
 
-static struct snd_pcm_ops snd_card_dummy_playback_ops = {
-	.open =			snd_card_dummy_playback_open,
-	.close =		snd_card_dummy_playback_close,
-	.ioctl =		snd_pcm_lib_ioctl,
-	.hw_params =		snd_card_dummy_hw_params,
-	.hw_free =		snd_card_dummy_hw_free,
-	.prepare =		snd_card_dummy_pcm_prepare,
-	.trigger =		snd_card_dummy_pcm_trigger,
-	.pointer =		snd_card_dummy_pcm_pointer,
+static struct snd_pcm_ops dummy_pcm_ops = {
+	.open =		dummy_pcm_open,
+	.close =	dummy_pcm_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	dummy_pcm_hw_params,
+	.hw_free =	dummy_pcm_hw_free,
+	.prepare =	dummy_pcm_prepare,
+	.trigger =	dummy_pcm_trigger,
+	.pointer =	dummy_pcm_pointer,
 };
 };
 
 
-static struct snd_pcm_ops snd_card_dummy_capture_ops = {
-	.open =			snd_card_dummy_capture_open,
-	.close =		snd_card_dummy_capture_close,
-	.ioctl =		snd_pcm_lib_ioctl,
-	.hw_params =		snd_card_dummy_hw_params,
-	.hw_free =		snd_card_dummy_hw_free,
-	.prepare =		snd_card_dummy_pcm_prepare,
-	.trigger =		snd_card_dummy_pcm_trigger,
-	.pointer =		snd_card_dummy_pcm_pointer,
+static struct snd_pcm_ops dummy_pcm_ops_no_buf = {
+	.open =		dummy_pcm_open,
+	.close =	dummy_pcm_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	dummy_pcm_hw_params,
+	.hw_free =	dummy_pcm_hw_free,
+	.prepare =	dummy_pcm_prepare,
+	.trigger =	dummy_pcm_trigger,
+	.pointer =	dummy_pcm_pointer,
+	.copy =		dummy_pcm_copy,
+	.silence =	dummy_pcm_silence,
+	.page =		dummy_pcm_page,
 };
 };
 
 
 static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device,
 static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device,
 					int substreams)
 					int substreams)
 {
 {
 	struct snd_pcm *pcm;
 	struct snd_pcm *pcm;
+	struct snd_pcm_ops *ops;
 	int err;
 	int err;
 
 
 	err = snd_pcm_new(dummy->card, "Dummy PCM", device,
 	err = snd_pcm_new(dummy->card, "Dummy PCM", device,
@@ -440,17 +669,28 @@ static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device,
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
 	dummy->pcm = pcm;
 	dummy->pcm = pcm;
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_dummy_playback_ops);
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_dummy_capture_ops);
+	if (fake_buffer)
+		ops = &dummy_pcm_ops_no_buf;
+	else
+		ops = &dummy_pcm_ops;
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, ops);
 	pcm->private_data = dummy;
 	pcm->private_data = dummy;
 	pcm->info_flags = 0;
 	pcm->info_flags = 0;
 	strcpy(pcm->name, "Dummy PCM");
 	strcpy(pcm->name, "Dummy PCM");
-	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
-					      snd_dma_continuous_data(GFP_KERNEL),
-					      0, 64*1024);
+	if (!fake_buffer) {
+		snd_pcm_lib_preallocate_pages_for_all(pcm,
+			SNDRV_DMA_TYPE_CONTINUOUS,
+			snd_dma_continuous_data(GFP_KERNEL),
+			0, 64*1024);
+	}
 	return 0;
 	return 0;
 }
 }
 
 
+/*
+ * mixer interface
+ */
+
 #define DUMMY_VOLUME(xname, xindex, addr) \
 #define DUMMY_VOLUME(xname, xindex, addr) \
 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
   .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
   .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
@@ -581,6 +821,131 @@ static int __devinit snd_card_dummy_new_mixer(struct snd_dummy *dummy)
 	return 0;
 	return 0;
 }
 }
 
 
+#if defined(CONFIG_SND_DEBUG) && defined(CONFIG_PROC_FS)
+/*
+ * proc interface
+ */
+static void print_formats(struct snd_info_buffer *buffer)
+{
+	int i;
+
+	for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) {
+		if (dummy_pcm_hardware.formats & (1ULL << i))
+			snd_iprintf(buffer, " %s", snd_pcm_format_name(i));
+	}
+}
+
+static void print_rates(struct snd_info_buffer *buffer)
+{
+	static int rates[] = {
+		5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000,
+		64000, 88200, 96000, 176400, 192000,
+	};
+	int i;
+
+	if (dummy_pcm_hardware.rates & SNDRV_PCM_RATE_CONTINUOUS)
+		snd_iprintf(buffer, " continuous");
+	if (dummy_pcm_hardware.rates & SNDRV_PCM_RATE_KNOT)
+		snd_iprintf(buffer, " knot");
+	for (i = 0; i < ARRAY_SIZE(rates); i++)
+		if (dummy_pcm_hardware.rates & (1 << i))
+			snd_iprintf(buffer, " %d", rates[i]);
+}
+
+#define get_dummy_int_ptr(ofs) \
+	(unsigned int *)((char *)&dummy_pcm_hardware + (ofs))
+#define get_dummy_ll_ptr(ofs) \
+	(unsigned long long *)((char *)&dummy_pcm_hardware + (ofs))
+
+struct dummy_hw_field {
+	const char *name;
+	const char *format;
+	unsigned int offset;
+	unsigned int size;
+};
+#define FIELD_ENTRY(item, fmt) {		   \
+	.name = #item,				   \
+	.format = fmt,				   \
+	.offset = offsetof(struct snd_pcm_hardware, item), \
+	.size = sizeof(dummy_pcm_hardware.item) }
+
+static struct dummy_hw_field fields[] = {
+	FIELD_ENTRY(formats, "%#llx"),
+	FIELD_ENTRY(rates, "%#x"),
+	FIELD_ENTRY(rate_min, "%d"),
+	FIELD_ENTRY(rate_max, "%d"),
+	FIELD_ENTRY(channels_min, "%d"),
+	FIELD_ENTRY(channels_max, "%d"),
+	FIELD_ENTRY(buffer_bytes_max, "%ld"),
+	FIELD_ENTRY(period_bytes_min, "%ld"),
+	FIELD_ENTRY(period_bytes_max, "%ld"),
+	FIELD_ENTRY(periods_min, "%d"),
+	FIELD_ENTRY(periods_max, "%d"),
+};
+
+static void dummy_proc_read(struct snd_info_entry *entry,
+			    struct snd_info_buffer *buffer)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(fields); i++) {
+		snd_iprintf(buffer, "%s ", fields[i].name);
+		if (fields[i].size == sizeof(int))
+			snd_iprintf(buffer, fields[i].format,
+				    *get_dummy_int_ptr(fields[i].offset));
+		else
+			snd_iprintf(buffer, fields[i].format,
+				    *get_dummy_ll_ptr(fields[i].offset));
+		if (!strcmp(fields[i].name, "formats"))
+			print_formats(buffer);
+		else if (!strcmp(fields[i].name, "rates"))
+			print_rates(buffer);
+		snd_iprintf(buffer, "\n");
+	}
+}
+
+static void dummy_proc_write(struct snd_info_entry *entry,
+			     struct snd_info_buffer *buffer)
+{
+	char line[64];
+
+	while (!snd_info_get_line(buffer, line, sizeof(line))) {
+		char item[20];
+		const char *ptr;
+		unsigned long long val;
+		int i;
+
+		ptr = snd_info_get_str(item, line, sizeof(item));
+		for (i = 0; i < ARRAY_SIZE(fields); i++) {
+			if (!strcmp(item, fields[i].name))
+				break;
+		}
+		if (i >= ARRAY_SIZE(fields))
+			continue;
+		snd_info_get_str(item, ptr, sizeof(item));
+		if (strict_strtoull(item, 0, &val))
+			continue;
+		if (fields[i].size == sizeof(int))
+			*get_dummy_int_ptr(fields[i].offset) = val;
+		else
+			*get_dummy_ll_ptr(fields[i].offset) = val;
+	}
+}
+
+static void __devinit dummy_proc_init(struct snd_dummy *chip)
+{
+	struct snd_info_entry *entry;
+
+	if (!snd_card_proc_new(chip->card, "dummy_pcm", &entry)) {
+		snd_info_set_text_ops(entry, chip, dummy_proc_read);
+		entry->c.text.write = dummy_proc_write;
+		entry->mode |= S_IWUSR;
+	}
+}
+#else
+#define dummy_proc_init(x)
+#endif /* CONFIG_SND_DEBUG && CONFIG_PROC_FS */
+
 static int __devinit snd_dummy_probe(struct platform_device *devptr)
 static int __devinit snd_dummy_probe(struct platform_device *devptr)
 {
 {
 	struct snd_card *card;
 	struct snd_card *card;
@@ -610,6 +975,8 @@ static int __devinit snd_dummy_probe(struct platform_device *devptr)
 	strcpy(card->shortname, "Dummy");
 	strcpy(card->shortname, "Dummy");
 	sprintf(card->longname, "Dummy %i", dev + 1);
 	sprintf(card->longname, "Dummy %i", dev + 1);
 
 
+	dummy_proc_init(dummy);
+
 	snd_card_set_dev(card, &devptr->dev);
 	snd_card_set_dev(card, &devptr->dev);
 
 
 	err = snd_card_register(card);
 	err = snd_card_register(card);
@@ -670,6 +1037,7 @@ static void snd_dummy_unregister_all(void)
 	for (i = 0; i < ARRAY_SIZE(devices); ++i)
 	for (i = 0; i < ARRAY_SIZE(devices); ++i)
 		platform_device_unregister(devices[i]);
 		platform_device_unregister(devices[i]);
 	platform_driver_unregister(&snd_dummy_driver);
 	platform_driver_unregister(&snd_dummy_driver);
+	free_fake_buffer();
 }
 }
 
 
 static int __init alsa_card_dummy_init(void)
 static int __init alsa_card_dummy_init(void)
@@ -680,6 +1048,12 @@ static int __init alsa_card_dummy_init(void)
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
 
 
+	err = alloc_fake_buffer();
+	if (err < 0) {
+		platform_driver_unregister(&snd_dummy_driver);
+		return err;
+	}
+
 	cards = 0;
 	cards = 0;
 	for (i = 0; i < SNDRV_CARDS; i++) {
 	for (i = 0; i < SNDRV_CARDS; i++) {
 		struct platform_device *device;
 		struct platform_device *device;

+ 52 - 34
sound/isa/cmi8330.c

@@ -1,5 +1,5 @@
 /*
 /*
- *  Driver for C-Media's CMI8330 soundcards.
+ *  Driver for C-Media's CMI8330 and CMI8329 soundcards.
  *  Copyright (c) by George Talusan <gstalusan@uwaterloo.ca>
  *  Copyright (c) by George Talusan <gstalusan@uwaterloo.ca>
  *    http://www.undergrad.math.uwaterloo.ca/~gstalusa
  *    http://www.undergrad.math.uwaterloo.ca/~gstalusa
  *
  *
@@ -35,7 +35,7 @@
  *
  *
  *  This card has two mixers and two PCM devices.  I've cheesed it such
  *  This card has two mixers and two PCM devices.  I've cheesed it such
  *  that recording and playback can be done through the same device.
  *  that recording and playback can be done through the same device.
- *  The driver "magically" routes the capturing to the CMI8330 codec,
+ *  The driver "magically" routes the capturing to the AD1848 codec,
  *  and playback to the SB16 codec.  This allows for full-duplex mode
  *  and playback to the SB16 codec.  This allows for full-duplex mode
  *  to some extent.
  *  to some extent.
  *  The utilities in alsa-utils are aware of both devices, so passing
  *  The utilities in alsa-utils are aware of both devices, so passing
@@ -64,7 +64,7 @@
 /*
 /*
  */
  */
 MODULE_AUTHOR("George Talusan <gstalusan@uwaterloo.ca>");
 MODULE_AUTHOR("George Talusan <gstalusan@uwaterloo.ca>");
-MODULE_DESCRIPTION("C-Media CMI8330");
+MODULE_DESCRIPTION("C-Media CMI8330/CMI8329");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");
 MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8330,isapnp:{CMI0001,@@@0001,@X@0001}}}");
 MODULE_SUPPORTED_DEVICE("{{C-Media,CMI8330,isapnp:{CMI0001,@@@0001,@X@0001}}}");
 
 
@@ -86,38 +86,38 @@ static long mpuport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
 static int mpuirq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
 static int mpuirq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
 
 
 module_param_array(index, int, NULL, 0444);
 module_param_array(index, int, NULL, 0444);
-MODULE_PARM_DESC(index, "Index value for CMI8330 soundcard.");
+MODULE_PARM_DESC(index, "Index value for CMI8330/CMI8329 soundcard.");
 module_param_array(id, charp, NULL, 0444);
 module_param_array(id, charp, NULL, 0444);
-MODULE_PARM_DESC(id, "ID string  for CMI8330 soundcard.");
+MODULE_PARM_DESC(id, "ID string  for CMI8330/CMI8329 soundcard.");
 module_param_array(enable, bool, NULL, 0444);
 module_param_array(enable, bool, NULL, 0444);
-MODULE_PARM_DESC(enable, "Enable CMI8330 soundcard.");
+MODULE_PARM_DESC(enable, "Enable CMI8330/CMI8329 soundcard.");
 #ifdef CONFIG_PNP
 #ifdef CONFIG_PNP
 module_param_array(isapnp, bool, NULL, 0444);
 module_param_array(isapnp, bool, NULL, 0444);
 MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard.");
 MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard.");
 #endif
 #endif
 
 
 module_param_array(sbport, long, NULL, 0444);
 module_param_array(sbport, long, NULL, 0444);
-MODULE_PARM_DESC(sbport, "Port # for CMI8330 SB driver.");
+MODULE_PARM_DESC(sbport, "Port # for CMI8330/CMI8329 SB driver.");
 module_param_array(sbirq, int, NULL, 0444);
 module_param_array(sbirq, int, NULL, 0444);
-MODULE_PARM_DESC(sbirq, "IRQ # for CMI8330 SB driver.");
+MODULE_PARM_DESC(sbirq, "IRQ # for CMI8330/CMI8329 SB driver.");
 module_param_array(sbdma8, int, NULL, 0444);
 module_param_array(sbdma8, int, NULL, 0444);
-MODULE_PARM_DESC(sbdma8, "DMA8 for CMI8330 SB driver.");
+MODULE_PARM_DESC(sbdma8, "DMA8 for CMI8330/CMI8329 SB driver.");
 module_param_array(sbdma16, int, NULL, 0444);
 module_param_array(sbdma16, int, NULL, 0444);
-MODULE_PARM_DESC(sbdma16, "DMA16 for CMI8330 SB driver.");
+MODULE_PARM_DESC(sbdma16, "DMA16 for CMI8330/CMI8329 SB driver.");
 
 
 module_param_array(wssport, long, NULL, 0444);
 module_param_array(wssport, long, NULL, 0444);
-MODULE_PARM_DESC(wssport, "Port # for CMI8330 WSS driver.");
+MODULE_PARM_DESC(wssport, "Port # for CMI8330/CMI8329 WSS driver.");
 module_param_array(wssirq, int, NULL, 0444);
 module_param_array(wssirq, int, NULL, 0444);
-MODULE_PARM_DESC(wssirq, "IRQ # for CMI8330 WSS driver.");
+MODULE_PARM_DESC(wssirq, "IRQ # for CMI8330/CMI8329 WSS driver.");
 module_param_array(wssdma, int, NULL, 0444);
 module_param_array(wssdma, int, NULL, 0444);
-MODULE_PARM_DESC(wssdma, "DMA for CMI8330 WSS driver.");
+MODULE_PARM_DESC(wssdma, "DMA for CMI8330/CMI8329 WSS driver.");
 
 
 module_param_array(fmport, long, NULL, 0444);
 module_param_array(fmport, long, NULL, 0444);
-MODULE_PARM_DESC(fmport, "FM port # for CMI8330 driver.");
+MODULE_PARM_DESC(fmport, "FM port # for CMI8330/CMI8329 driver.");
 module_param_array(mpuport, long, NULL, 0444);
 module_param_array(mpuport, long, NULL, 0444);
-MODULE_PARM_DESC(mpuport, "MPU-401 port # for CMI8330 driver.");
+MODULE_PARM_DESC(mpuport, "MPU-401 port # for CMI8330/CMI8329 driver.");
 module_param_array(mpuirq, int, NULL, 0444);
 module_param_array(mpuirq, int, NULL, 0444);
-MODULE_PARM_DESC(mpuirq, "IRQ # for CMI8330 MPU-401 port.");
+MODULE_PARM_DESC(mpuirq, "IRQ # for CMI8330/CMI8329 MPU-401 port.");
 #ifdef CONFIG_PNP
 #ifdef CONFIG_PNP
 static int isa_registered;
 static int isa_registered;
 static int pnp_registered;
 static int pnp_registered;
@@ -156,6 +156,11 @@ static unsigned char snd_cmi8330_image[((CMI8330_CDINGAIN)-16) + 1] =
 
 
 typedef int (*snd_pcm_open_callback_t)(struct snd_pcm_substream *);
 typedef int (*snd_pcm_open_callback_t)(struct snd_pcm_substream *);
 
 
+enum card_type {
+	CMI8330,
+	CMI8329
+};
+
 struct snd_cmi8330 {
 struct snd_cmi8330 {
 #ifdef CONFIG_PNP
 #ifdef CONFIG_PNP
 	struct pnp_dev *cap;
 	struct pnp_dev *cap;
@@ -172,11 +177,14 @@ struct snd_cmi8330 {
 		snd_pcm_open_callback_t open;
 		snd_pcm_open_callback_t open;
 		void *private_data; /* sb or wss */
 		void *private_data; /* sb or wss */
 	} streams[2];
 	} streams[2];
+
+	enum card_type type;
 };
 };
 
 
 #ifdef CONFIG_PNP
 #ifdef CONFIG_PNP
 
 
 static struct pnp_card_device_id snd_cmi8330_pnpids[] = {
 static struct pnp_card_device_id snd_cmi8330_pnpids[] = {
+	{ .id = "CMI0001", .devs = { { "@X@0001" }, { "@@@0001" }, { "@H@0001" }, { "A@@0001" } } },
 	{ .id = "CMI0001", .devs = { { "@@@0001" }, { "@X@0001" }, { "@H@0001" } } },
 	{ .id = "CMI0001", .devs = { { "@@@0001" }, { "@X@0001" }, { "@H@0001" } } },
 	{ .id = "" }
 	{ .id = "" }
 };
 };
@@ -304,7 +312,7 @@ static int __devinit snd_cmi8330_mixer(struct snd_card *card, struct snd_cmi8330
 	unsigned int idx;
 	unsigned int idx;
 	int err;
 	int err;
 
 
-	strcpy(card->mixername, "CMI8330/C3D");
+	strcpy(card->mixername, (acard->type == CMI8329) ? "CMI8329" : "CMI8330/C3D");
 
 
 	for (idx = 0; idx < ARRAY_SIZE(snd_cmi8330_controls); idx++) {
 	for (idx = 0; idx < ARRAY_SIZE(snd_cmi8330_controls); idx++) {
 		err = snd_ctl_add(card,
 		err = snd_ctl_add(card,
@@ -329,6 +337,9 @@ static int __devinit snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard,
 	struct pnp_dev *pdev;
 	struct pnp_dev *pdev;
 	int err;
 	int err;
 
 
+	/* CMI8329 has a device with ID A@@0001, CMI8330 does not */
+	acard->type = (id->devs[3].id[0]) ? CMI8329 : CMI8330;
+
 	acard->cap = pnp_request_card_device(card, id->devs[0].id, NULL);
 	acard->cap = pnp_request_card_device(card, id->devs[0].id, NULL);
 	if (acard->cap == NULL)
 	if (acard->cap == NULL)
 		return -EBUSY;
 		return -EBUSY;
@@ -345,38 +356,45 @@ static int __devinit snd_cmi8330_pnp(int dev, struct snd_cmi8330 *acard,
 
 
 	err = pnp_activate_dev(pdev);
 	err = pnp_activate_dev(pdev);
 	if (err < 0) {
 	if (err < 0) {
-		snd_printk(KERN_ERR "CMI8330/C3D PnP configure failure\n");
+		snd_printk(KERN_ERR "AD1848 PnP configure failure\n");
 		return -EBUSY;
 		return -EBUSY;
 	}
 	}
 	wssport[dev] = pnp_port_start(pdev, 0);
 	wssport[dev] = pnp_port_start(pdev, 0);
 	wssdma[dev] = pnp_dma(pdev, 0);
 	wssdma[dev] = pnp_dma(pdev, 0);
 	wssirq[dev] = pnp_irq(pdev, 0);
 	wssirq[dev] = pnp_irq(pdev, 0);
-	fmport[dev] = pnp_port_start(pdev, 1);
+	if (pnp_port_start(pdev, 1))
+		fmport[dev] = pnp_port_start(pdev, 1);
 
 
 	/* allocate SB16 resources */
 	/* allocate SB16 resources */
 	pdev = acard->play;
 	pdev = acard->play;
 
 
 	err = pnp_activate_dev(pdev);
 	err = pnp_activate_dev(pdev);
 	if (err < 0) {
 	if (err < 0) {
-		snd_printk(KERN_ERR "CMI8330/C3D (SB16) PnP configure failure\n");
+		snd_printk(KERN_ERR "SB16 PnP configure failure\n");
 		return -EBUSY;
 		return -EBUSY;
 	}
 	}
 	sbport[dev] = pnp_port_start(pdev, 0);
 	sbport[dev] = pnp_port_start(pdev, 0);
 	sbdma8[dev] = pnp_dma(pdev, 0);
 	sbdma8[dev] = pnp_dma(pdev, 0);
 	sbdma16[dev] = pnp_dma(pdev, 1);
 	sbdma16[dev] = pnp_dma(pdev, 1);
 	sbirq[dev] = pnp_irq(pdev, 0);
 	sbirq[dev] = pnp_irq(pdev, 0);
+	/* On CMI8239, the OPL3 port might be present in SB16 PnP resources */
+	if (fmport[dev] == SNDRV_AUTO_PORT) {
+		if (pnp_port_start(pdev, 1))
+			fmport[dev] = pnp_port_start(pdev, 1);
+		else
+			fmport[dev] = 0x388;	/* Or hardwired */
+	}
 
 
 	/* allocate MPU-401 resources */
 	/* allocate MPU-401 resources */
 	pdev = acard->mpu;
 	pdev = acard->mpu;
 
 
 	err = pnp_activate_dev(pdev);
 	err = pnp_activate_dev(pdev);
-	if (err < 0) {
-		snd_printk(KERN_ERR
-			   "CMI8330/C3D (MPU-401) PnP configure failure\n");
-		return -EBUSY;
+	if (err < 0)
+		snd_printk(KERN_ERR "MPU-401 PnP configure failure: will be disabled\n");
+	else {
+		mpuport[dev] = pnp_port_start(pdev, 0);
+		mpuirq[dev] = pnp_irq(pdev, 0);
 	}
 	}
-	mpuport[dev] = pnp_port_start(pdev, 0);
-	mpuirq[dev] = pnp_irq(pdev, 0);
 	return 0;
 	return 0;
 }
 }
 #endif
 #endif
@@ -430,9 +448,9 @@ static int __devinit snd_cmi8330_pcm(struct snd_card *card, struct snd_cmi8330 *
 		snd_cmi8330_capture_open
 		snd_cmi8330_capture_open
 	};
 	};
 
 
-	if ((err = snd_pcm_new(card, "CMI8330", 0, 1, 1, &pcm)) < 0)
+	if ((err = snd_pcm_new(card, (chip->type == CMI8329) ? "CMI8329" : "CMI8330", 0, 1, 1, &pcm)) < 0)
 		return err;
 		return err;
-	strcpy(pcm->name, "CMI8330");
+	strcpy(pcm->name, (chip->type == CMI8329) ? "CMI8329" : "CMI8330");
 	pcm->private_data = chip;
 	pcm->private_data = chip;
 	
 	
 	/* SB16 */
 	/* SB16 */
@@ -527,11 +545,11 @@ static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev)
 			     wssdma[dev], -1,
 			     wssdma[dev], -1,
 			     WSS_HW_DETECT, 0, &acard->wss);
 			     WSS_HW_DETECT, 0, &acard->wss);
 	if (err < 0) {
 	if (err < 0) {
-		snd_printk(KERN_ERR PFX "(CMI8330) device busy??\n");
+		snd_printk(KERN_ERR PFX "AD1848 device busy??\n");
 		return err;
 		return err;
 	}
 	}
 	if (acard->wss->hardware != WSS_HW_CMI8330) {
 	if (acard->wss->hardware != WSS_HW_CMI8330) {
-		snd_printk(KERN_ERR PFX "(CMI8330) not found during probe\n");
+		snd_printk(KERN_ERR PFX "AD1848 not found during probe\n");
 		return -ENODEV;
 		return -ENODEV;
 	}
 	}
 
 
@@ -541,11 +559,11 @@ static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev)
 				    sbdma8[dev],
 				    sbdma8[dev],
 				    sbdma16[dev],
 				    sbdma16[dev],
 				    SB_HW_AUTO, &acard->sb)) < 0) {
 				    SB_HW_AUTO, &acard->sb)) < 0) {
-		snd_printk(KERN_ERR PFX "(SB16) device busy??\n");
+		snd_printk(KERN_ERR PFX "SB16 device busy??\n");
 		return err;
 		return err;
 	}
 	}
 	if (acard->sb->hardware != SB_HW_16) {
 	if (acard->sb->hardware != SB_HW_16) {
-		snd_printk(KERN_ERR PFX "(SB16) not found during probe\n");
+		snd_printk(KERN_ERR PFX "SB16 not found during probe\n");
 		return err;
 		return err;
 	}
 	}
 
 
@@ -585,8 +603,8 @@ static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev)
 				mpuport[dev]);
 				mpuport[dev]);
 	}
 	}
 
 
-	strcpy(card->driver, "CMI8330/C3D");
-	strcpy(card->shortname, "C-Media CMI8330/C3D");
+	strcpy(card->driver, (acard->type == CMI8329) ? "CMI8329" : "CMI8330/C3D");
+	strcpy(card->shortname, (acard->type == CMI8329) ? "C-Media CMI8329" : "C-Media CMI8330/C3D");
 	sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
 	sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
 		card->shortname,
 		card->shortname,
 		acard->wss->port,
 		acard->wss->port,

+ 4 - 3
sound/oss/midibuf.c

@@ -127,15 +127,16 @@ static void midi_poll(unsigned long dummy)
 		for (dev = 0; dev < num_midis; dev++)
 		for (dev = 0; dev < num_midis; dev++)
 			if (midi_devs[dev] != NULL && midi_out_buf[dev] != NULL)
 			if (midi_devs[dev] != NULL && midi_out_buf[dev] != NULL)
 			{
 			{
-				int ok = 1;
-
-				while (DATA_AVAIL(midi_out_buf[dev]) && ok)
+				while (DATA_AVAIL(midi_out_buf[dev]))
 				{
 				{
+					int ok;
 					int c = midi_out_buf[dev]->queue[midi_out_buf[dev]->head];
 					int c = midi_out_buf[dev]->queue[midi_out_buf[dev]->head];
 
 
 					spin_unlock_irqrestore(&lock,flags);/* Give some time to others */
 					spin_unlock_irqrestore(&lock,flags);/* Give some time to others */
 					ok = midi_devs[dev]->outputc(dev, c);
 					ok = midi_devs[dev]->outputc(dev, c);
 					spin_lock_irqsave(&lock, flags);
 					spin_lock_irqsave(&lock, flags);
+					if (!ok)
+						break;
 					midi_out_buf[dev]->head = (midi_out_buf[dev]->head + 1) % MAX_QUEUE_SIZE;
 					midi_out_buf[dev]->head = (midi_out_buf[dev]->head + 1) % MAX_QUEUE_SIZE;
 					midi_out_buf[dev]->len--;
 					midi_out_buf[dev]->len--;
 				}
 				}

+ 3 - 3
sound/oss/vwsnd.c

@@ -628,7 +628,7 @@ static void li_setup_dma(dma_chan_t *chan,
 	ASSERT(!(buffer_paddr & 0xFF));
 	ASSERT(!(buffer_paddr & 0xFF));
 	chan->baseval = (buffer_paddr >> 8) | 1 << (37 - 8);
 	chan->baseval = (buffer_paddr >> 8) | 1 << (37 - 8);
 
 
-	chan->cfgval = (!LI_CCFG_LOCK |
+	chan->cfgval = ((chan->cfgval & ~LI_CCFG_LOCK) |
 			SHIFT_FIELD(desc->ad1843_slot, LI_CCFG_SLOT) |
 			SHIFT_FIELD(desc->ad1843_slot, LI_CCFG_SLOT) |
 			desc->direction |
 			desc->direction |
 			mode |
 			mode |
@@ -638,9 +638,9 @@ static void li_setup_dma(dma_chan_t *chan,
 	tmask = 13 - fragshift;		/* See Lithium DMA Notes above. */
 	tmask = 13 - fragshift;		/* See Lithium DMA Notes above. */
 	ASSERT(size >= 2 && size <= 7);
 	ASSERT(size >= 2 && size <= 7);
 	ASSERT(tmask >= 1 && tmask <= 7);
 	ASSERT(tmask >= 1 && tmask <= 7);
-	chan->ctlval = (!LI_CCTL_RESET |
+	chan->ctlval = ((chan->ctlval & ~LI_CCTL_RESET) |
 			SHIFT_FIELD(size, LI_CCTL_SIZE) |
 			SHIFT_FIELD(size, LI_CCTL_SIZE) |
-			!LI_CCTL_DMA_ENABLE |
+			(chan->ctlval & ~LI_CCTL_DMA_ENABLE) |
 			SHIFT_FIELD(tmask, LI_CCTL_TMASK) |
 			SHIFT_FIELD(tmask, LI_CCTL_TMASK) |
 			SHIFT_FIELD(0, LI_CCTL_TPTR));
 			SHIFT_FIELD(0, LI_CCTL_TPTR));
 
 

+ 2 - 2
sound/pci/Kconfig

@@ -135,11 +135,11 @@ config SND_AW2
 
 
 
 
 config SND_AZT3328
 config SND_AZT3328
-	tristate "Aztech AZF3328 / PCI168 (EXPERIMENTAL)"
-	depends on EXPERIMENTAL
+	tristate "Aztech AZF3328 / PCI168"
 	select SND_OPL3_LIB
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_MPU401_UART
 	select SND_PCM
 	select SND_PCM
+	select SND_RAWMIDI
 	help
 	help
 	  Say Y here to include support for Aztech AZF3328 (PCI168)
 	  Say Y here to include support for Aztech AZF3328 (PCI168)
 	  soundcards.
 	  soundcards.

+ 0 - 65
sound/pci/ali5451/ali5451.c

@@ -478,45 +478,6 @@ static int snd_ali_reset_5451(struct snd_ali *codec)
 	return 0;
 	return 0;
 }
 }
 
 
-#ifdef CODEC_RESET
-
-static int snd_ali_reset_codec(struct snd_ali *codec)
-{
-	struct pci_dev *pci_dev;
-	unsigned char bVal;
-	unsigned int   dwVal;
-	unsigned short wCount, wReg;
-
-	pci_dev = codec->pci_m1533;
-	
-	pci_read_config_dword(pci_dev, 0x7c, &dwVal);
-	pci_write_config_dword(pci_dev, 0x7c, dwVal | 0x08000000);
-	udelay(5000);
-	pci_read_config_dword(pci_dev, 0x7c, &dwVal);
-	pci_write_config_dword(pci_dev, 0x7c, dwVal & 0xf7ffffff);
-	udelay(5000);
-
-	bVal = inb(ALI_REG(codec,ALI_SCTRL));
-	bVal |= 0x02;
-	outb(ALI_REG(codec,ALI_SCTRL),bVal);
-	udelay(5000);
-	bVal = inb(ALI_REG(codec,ALI_SCTRL));
-	bVal &= 0xfd;
-	outb(ALI_REG(codec,ALI_SCTRL),bVal);
-	udelay(15000);
-
-	wCount = 200;
-	while (wCount--) {
-		wReg = snd_ali_codec_read(codec->ac97, AC97_POWERDOWN);
-		if ((wReg & 0x000f) == 0x000f)
-			return 0;
-		udelay(5000);
-	}
-	return -1;
-}
-
-#endif
-
 /*
 /*
  *  ALI 5451 Controller
  *  ALI 5451 Controller
  */
  */
@@ -561,22 +522,6 @@ static void snd_ali_disable_address_interrupt(struct snd_ali *codec)
 	outl(gc, ALI_REG(codec, ALI_GC_CIR));
 	outl(gc, ALI_REG(codec, ALI_GC_CIR));
 }
 }
 
 
-#if 0 /* not used */
-static void snd_ali_enable_voice_irq(struct snd_ali *codec,
-				     unsigned int channel)
-{
-	unsigned int mask;
-	struct snd_ali_channel_control *pchregs = &(codec->chregs);
-
-	snd_ali_printk("enable_voice_irq channel=%d\n",channel);
-	
-	mask = 1 << (channel & 0x1f);
-	pchregs->data.ainten  = inl(ALI_REG(codec, pchregs->regs.ainten));
-	pchregs->data.ainten |= mask;
-	outl(pchregs->data.ainten, ALI_REG(codec, pchregs->regs.ainten));
-}
-#endif
-
 static void snd_ali_disable_voice_irq(struct snd_ali *codec,
 static void snd_ali_disable_voice_irq(struct snd_ali *codec,
 				      unsigned int channel)
 				      unsigned int channel)
 {
 {
@@ -677,16 +622,6 @@ static void snd_ali_free_channel_pcm(struct snd_ali *codec, int channel)
 	}
 	}
 }
 }
 
 
-#if 0 /* not used */
-static void snd_ali_start_voice(struct snd_ali *codec, unsigned int channel)
-{
-	unsigned int mask = 1 << (channel & 0x1f);
-	
-	snd_ali_printk("start_voice: channel=%d\n",channel);
-	outl(mask, ALI_REG(codec,codec->chregs.regs.start));
-}
-#endif
-
 static void snd_ali_stop_voice(struct snd_ali *codec, unsigned int channel)
 static void snd_ali_stop_voice(struct snd_ali *codec, unsigned int channel)
 {
 {
 	unsigned int mask = 1 << (channel & 0x1f);
 	unsigned int mask = 1 << (channel & 0x1f);

+ 617 - 499
sound/pci/azt3328.c

@@ -1,6 +1,6 @@
 /*
 /*
  *  azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168).
  *  azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168).
- *  Copyright (C) 2002, 2005 - 2008 by Andreas Mohr <andi AT lisas.de>
+ *  Copyright (C) 2002, 2005 - 2009 by Andreas Mohr <andi AT lisas.de>
  *
  *
  *  Framework borrowed from Bart Hartgers's als4000.c.
  *  Framework borrowed from Bart Hartgers's als4000.c.
  *  Driver developed on PCI168 AP(W) version (PCI rev. 10, subsystem ID 1801),
  *  Driver developed on PCI168 AP(W) version (PCI rev. 10, subsystem ID 1801),
@@ -10,6 +10,13 @@
  *  PCI168 A/AP, sub ID 8000
  *  PCI168 A/AP, sub ID 8000
  *  Please give me feedback in case you try my driver with one of these!!
  *  Please give me feedback in case you try my driver with one of these!!
  *
  *
+ *  Keywords: Windows XP Vista 168nt4-125.zip 168win95-125.zip PCI 168 download
+ *  (XP/Vista do not support this card at all but every Linux distribution
+ *   has very good support out of the box;
+ *   just to make sure that the right people hit this and get to know that,
+ *   despite the high level of Internet ignorance - as usual :-P -
+ *   about very good support for this card - on Linux!)
+ *
  * GPL LICENSE
  * GPL LICENSE
  *  This program is free software; you can redistribute it and/or modify
  *  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
  *  it under the terms of the GNU General Public License as published by
@@ -71,10 +78,11 @@
  *  - built-in General DirectX timer having a 20 bits counter
  *  - built-in General DirectX timer having a 20 bits counter
  *    with 1us resolution (see below!)
  *    with 1us resolution (see below!)
  *  - I2S serial output port for external DAC
  *  - I2S serial output port for external DAC
+ *    [FIXME: 3.3V or 5V level? maximum rate is 66.2kHz right?]
  *  - supports 33MHz PCI spec 2.1, PCI power management 1.0, compliant with ACPI
  *  - supports 33MHz PCI spec 2.1, PCI power management 1.0, compliant with ACPI
  *  - supports hardware volume control
  *  - supports hardware volume control
  *  - single chip low cost solution (128 pin QFP)
  *  - single chip low cost solution (128 pin QFP)
- *  - supports programmable Sub-vendor and Sub-system ID
+ *  - supports programmable Sub-vendor and Sub-system ID [24C02 SEEPROM chip]
  *    required for Microsoft's logo compliance (FIXME: where?)
  *    required for Microsoft's logo compliance (FIXME: where?)
  *    At least the Trident 4D Wave DX has one bit somewhere
  *    At least the Trident 4D Wave DX has one bit somewhere
  *    to enable writes to PCI subsystem VID registers, that should be it.
  *    to enable writes to PCI subsystem VID registers, that should be it.
@@ -82,6 +90,7 @@
  *    some custom data starting at 0x80. What kind of config settings
  *    some custom data starting at 0x80. What kind of config settings
  *    are located in our extended PCI space anyway??
  *    are located in our extended PCI space anyway??
  *  - PCI168 AP(W) card: power amplifier with 4 Watts/channel at 4 Ohms
  *  - PCI168 AP(W) card: power amplifier with 4 Watts/channel at 4 Ohms
+ *    [TDA1517P chip]
  *
  *
  *  Note that this driver now is actually *better* than the Windows driver,
  *  Note that this driver now is actually *better* than the Windows driver,
  *  since it additionally supports the card's 1MHz DirectX timer - just try
  *  since it additionally supports the card's 1MHz DirectX timer - just try
@@ -146,10 +155,15 @@
  *    to read the Digital Enhanced Game Port. Not sure whether it is fixable.
  *    to read the Digital Enhanced Game Port. Not sure whether it is fixable.
  *
  *
  * TODO
  * TODO
+ *  - use PCI_VDEVICE
+ *  - verify driver status on x86_64
+ *  - test multi-card driver operation
+ *  - (ab)use 1MHz DirectX timer as kernel clocksource
  *  - test MPU401 MIDI playback etc.
  *  - test MPU401 MIDI playback etc.
  *  - add more power micro-management (disable various units of the card
  *  - add more power micro-management (disable various units of the card
- *    as long as they're unused). However this requires more I/O ports which I
- *    haven't figured out yet and which thus might not even exist...
+ *    as long as they're unused, to improve audio quality and save power).
+ *    However this requires more I/O ports which I haven't figured out yet
+ *    and which thus might not even exist...
  *    The standard suspend/resume functionality could probably make use of
  *    The standard suspend/resume functionality could probably make use of
  *    some improvement, too...
  *    some improvement, too...
  *  - figure out what all unknown port bits are responsible for
  *  - figure out what all unknown port bits are responsible for
@@ -185,25 +199,46 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
 #define SUPPORT_GAMEPORT 1
 #define SUPPORT_GAMEPORT 1
 #endif
 #endif
 
 
+/* === Debug settings ===
+  Further diagnostic functionality than the settings below
+  does not need to be provided, since one can easily write a bash script
+  to dump the card's I/O ports (those listed in lspci -v -v):
+  function dump()
+  {
+    local descr=$1; local addr=$2; local count=$3
+
+    echo "${descr}: ${count} @ ${addr}:"
+    dd if=/dev/port skip=$[${addr}] count=${count} bs=1 2>/dev/null| hexdump -C
+  }
+  and then use something like
+  "dump joy200 0x200 8", "dump mpu388 0x388 4", "dump joy 0xb400 8",
+  "dump codec00 0xa800 32", "dump mixer 0xb800 64", "dump synth 0xbc00 8",
+  possibly within a "while true; do ... sleep 1; done" loop.
+  Tweaking ports could be done using
+  VALSTRING="`printf "%02x" $value`"
+  printf "\x""$VALSTRING"|dd of=/dev/port seek=$[${addr}] bs=1 2>/dev/null
+*/
+
 #define DEBUG_MISC	0
 #define DEBUG_MISC	0
 #define DEBUG_CALLS	0
 #define DEBUG_CALLS	0
 #define DEBUG_MIXER	0
 #define DEBUG_MIXER	0
-#define DEBUG_PLAY_REC	0
+#define DEBUG_CODEC	0
 #define DEBUG_IO	0
 #define DEBUG_IO	0
 #define DEBUG_TIMER	0
 #define DEBUG_TIMER	0
 #define DEBUG_GAME	0
 #define DEBUG_GAME	0
+#define DEBUG_PM	0
 #define MIXER_TESTING	0
 #define MIXER_TESTING	0
 
 
 #if DEBUG_MISC
 #if DEBUG_MISC
-#define snd_azf3328_dbgmisc(format, args...) printk(KERN_ERR format, ##args)
+#define snd_azf3328_dbgmisc(format, args...) printk(KERN_DEBUG format, ##args)
 #else
 #else
 #define snd_azf3328_dbgmisc(format, args...)
 #define snd_azf3328_dbgmisc(format, args...)
 #endif
 #endif
 
 
 #if DEBUG_CALLS
 #if DEBUG_CALLS
 #define snd_azf3328_dbgcalls(format, args...) printk(format, ##args)
 #define snd_azf3328_dbgcalls(format, args...) printk(format, ##args)
-#define snd_azf3328_dbgcallenter() printk(KERN_ERR "--> %s\n", __func__)
-#define snd_azf3328_dbgcallleave() printk(KERN_ERR "<-- %s\n", __func__)
+#define snd_azf3328_dbgcallenter() printk(KERN_DEBUG "--> %s\n", __func__)
+#define snd_azf3328_dbgcallleave() printk(KERN_DEBUG "<-- %s\n", __func__)
 #else
 #else
 #define snd_azf3328_dbgcalls(format, args...)
 #define snd_azf3328_dbgcalls(format, args...)
 #define snd_azf3328_dbgcallenter()
 #define snd_azf3328_dbgcallenter()
@@ -216,10 +251,10 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
 #define snd_azf3328_dbgmixer(format, args...)
 #define snd_azf3328_dbgmixer(format, args...)
 #endif
 #endif
 
 
-#if DEBUG_PLAY_REC
-#define snd_azf3328_dbgplay(format, args...) printk(KERN_DEBUG format, ##args)
+#if DEBUG_CODEC
+#define snd_azf3328_dbgcodec(format, args...) printk(KERN_DEBUG format, ##args)
 #else
 #else
-#define snd_azf3328_dbgplay(format, args...)
+#define snd_azf3328_dbgcodec(format, args...)
 #endif
 #endif
 
 
 #if DEBUG_MISC
 #if DEBUG_MISC
@@ -234,6 +269,12 @@ MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");
 #define snd_azf3328_dbggame(format, args...)
 #define snd_azf3328_dbggame(format, args...)
 #endif
 #endif
 
 
+#if DEBUG_PM
+#define snd_azf3328_dbgpm(format, args...) printk(KERN_DEBUG format, ##args)
+#else
+#define snd_azf3328_dbgpm(format, args...)
+#endif
+
 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
 module_param_array(index, int, NULL, 0444);
 module_param_array(index, int, NULL, 0444);
 MODULE_PARM_DESC(index, "Index value for AZF3328 soundcard.");
 MODULE_PARM_DESC(index, "Index value for AZF3328 soundcard.");
@@ -250,22 +291,23 @@ static int seqtimer_scaling = 128;
 module_param(seqtimer_scaling, int, 0444);
 module_param(seqtimer_scaling, int, 0444);
 MODULE_PARM_DESC(seqtimer_scaling, "Set 1024000Hz sequencer timer scale factor (lockup danger!). Default 128.");
 MODULE_PARM_DESC(seqtimer_scaling, "Set 1024000Hz sequencer timer scale factor (lockup danger!). Default 128.");
 
 
-struct snd_azf3328_audio_stream {
+struct snd_azf3328_codec_data {
+	unsigned long io_base;
 	struct snd_pcm_substream *substream;
 	struct snd_pcm_substream *substream;
-	int enabled;
-	int running;
-	unsigned long portbase;
+	bool running;
+	const char *name;
 };
 };
 
 
-enum snd_azf3328_stream_index {
-  AZF_PLAYBACK = 0,
-  AZF_CAPTURE = 1,
+enum snd_azf3328_codec_type {
+  AZF_CODEC_PLAYBACK = 0,
+  AZF_CODEC_CAPTURE = 1,
+  AZF_CODEC_I2S_OUT = 2,
 };
 };
 
 
 struct snd_azf3328 {
 struct snd_azf3328 {
 	/* often-used fields towards beginning, then grouped */
 	/* often-used fields towards beginning, then grouped */
 
 
-	unsigned long codec_io; /* usually 0xb000, size 128 */
+	unsigned long ctrl_io; /* usually 0xb000, size 128 */
 	unsigned long game_io;  /* usually 0xb400, size 8 */
 	unsigned long game_io;  /* usually 0xb400, size 8 */
 	unsigned long mpu_io;   /* usually 0xb800, size 4 */
 	unsigned long mpu_io;   /* usually 0xb800, size 4 */
 	unsigned long opl3_io; /* usually 0xbc00, size 8 */
 	unsigned long opl3_io; /* usually 0xbc00, size 8 */
@@ -275,15 +317,17 @@ struct snd_azf3328 {
 
 
 	struct snd_timer *timer;
 	struct snd_timer *timer;
 
 
-	struct snd_pcm *pcm;
-	struct snd_azf3328_audio_stream audio_stream[2];
+	struct snd_pcm *pcm[3];
+
+	/* playback, recording and I2S out codecs */
+	struct snd_azf3328_codec_data codecs[3];
 
 
 	struct snd_card *card;
 	struct snd_card *card;
 	struct snd_rawmidi *rmidi;
 	struct snd_rawmidi *rmidi;
 
 
 #ifdef SUPPORT_GAMEPORT
 #ifdef SUPPORT_GAMEPORT
 	struct gameport *gameport;
 	struct gameport *gameport;
-	int axes[4];
+	u16 axes[4];
 #endif
 #endif
 
 
 	struct pci_dev *pci;
 	struct pci_dev *pci;
@@ -293,16 +337,16 @@ struct snd_azf3328 {
 	 * If we need to add more registers here, then we might try to fold this
 	 * If we need to add more registers here, then we might try to fold this
 	 * into some transparent combined shadow register handling with
 	 * into some transparent combined shadow register handling with
 	 * CONFIG_PM register storage below, but that's slightly difficult. */
 	 * CONFIG_PM register storage below, but that's slightly difficult. */
-	u16 shadow_reg_codec_6AH;
+	u16 shadow_reg_ctrl_6AH;
 
 
 #ifdef CONFIG_PM
 #ifdef CONFIG_PM
 	/* register value containers for power management
 	/* register value containers for power management
-	 * Note: not always full I/O range preserved (just like Win driver!) */
-	u16 saved_regs_codec[AZF_IO_SIZE_CODEC_PM / 2];
-	u16 saved_regs_game [AZF_IO_SIZE_GAME_PM / 2];
-	u16 saved_regs_mpu  [AZF_IO_SIZE_MPU_PM / 2];
-	u16 saved_regs_opl3 [AZF_IO_SIZE_OPL3_PM / 2];
-	u16 saved_regs_mixer[AZF_IO_SIZE_MIXER_PM / 2];
+	 * Note: not always full I/O range preserved (similar to Win driver!) */
+	u32 saved_regs_ctrl[AZF_ALIGN(AZF_IO_SIZE_CTRL_PM) / 4];
+	u32 saved_regs_game[AZF_ALIGN(AZF_IO_SIZE_GAME_PM) / 4];
+	u32 saved_regs_mpu[AZF_ALIGN(AZF_IO_SIZE_MPU_PM) / 4];
+	u32 saved_regs_opl3[AZF_ALIGN(AZF_IO_SIZE_OPL3_PM) / 4];
+	u32 saved_regs_mixer[AZF_ALIGN(AZF_IO_SIZE_MIXER_PM) / 4];
 #endif
 #endif
 };
 };
 
 
@@ -316,7 +360,7 @@ MODULE_DEVICE_TABLE(pci, snd_azf3328_ids);
 
 
 
 
 static int
 static int
-snd_azf3328_io_reg_setb(unsigned reg, u8 mask, int do_set)
+snd_azf3328_io_reg_setb(unsigned reg, u8 mask, bool do_set)
 {
 {
 	u8 prev = inb(reg), new;
 	u8 prev = inb(reg), new;
 
 
@@ -331,39 +375,72 @@ snd_azf3328_io_reg_setb(unsigned reg, u8 mask, int do_set)
 }
 }
 
 
 static inline void
 static inline void
-snd_azf3328_codec_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value)
+snd_azf3328_codec_outb(const struct snd_azf3328_codec_data *codec,
+		       unsigned reg,
+		       u8 value
+)
 {
 {
-	outb(value, chip->codec_io + reg);
+	outb(value, codec->io_base + reg);
 }
 }
 
 
 static inline u8
 static inline u8
-snd_azf3328_codec_inb(const struct snd_azf3328 *chip, unsigned reg)
+snd_azf3328_codec_inb(const struct snd_azf3328_codec_data *codec, unsigned reg)
 {
 {
-	return inb(chip->codec_io + reg);
+	return inb(codec->io_base + reg);
 }
 }
 
 
 static inline void
 static inline void
-snd_azf3328_codec_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
+snd_azf3328_codec_outw(const struct snd_azf3328_codec_data *codec,
+		       unsigned reg,
+		       u16 value
+)
 {
 {
-	outw(value, chip->codec_io + reg);
+	outw(value, codec->io_base + reg);
 }
 }
 
 
 static inline u16
 static inline u16
-snd_azf3328_codec_inw(const struct snd_azf3328 *chip, unsigned reg)
+snd_azf3328_codec_inw(const struct snd_azf3328_codec_data *codec, unsigned reg)
 {
 {
-	return inw(chip->codec_io + reg);
+	return inw(codec->io_base + reg);
 }
 }
 
 
 static inline void
 static inline void
-snd_azf3328_codec_outl(const struct snd_azf3328 *chip, unsigned reg, u32 value)
+snd_azf3328_codec_outl(const struct snd_azf3328_codec_data *codec,
+		       unsigned reg,
+		       u32 value
+)
 {
 {
-	outl(value, chip->codec_io + reg);
+	outl(value, codec->io_base + reg);
 }
 }
 
 
 static inline u32
 static inline u32
-snd_azf3328_codec_inl(const struct snd_azf3328 *chip, unsigned reg)
+snd_azf3328_codec_inl(const struct snd_azf3328_codec_data *codec, unsigned reg)
+{
+	return inl(codec->io_base + reg);
+}
+
+static inline void
+snd_azf3328_ctrl_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value)
+{
+	outb(value, chip->ctrl_io + reg);
+}
+
+static inline u8
+snd_azf3328_ctrl_inb(const struct snd_azf3328 *chip, unsigned reg)
+{
+	return inb(chip->ctrl_io + reg);
+}
+
+static inline void
+snd_azf3328_ctrl_outw(const struct snd_azf3328 *chip, unsigned reg, u16 value)
+{
+	outw(value, chip->ctrl_io + reg);
+}
+
+static inline void
+snd_azf3328_ctrl_outl(const struct snd_azf3328 *chip, unsigned reg, u32 value)
 {
 {
-	return inl(chip->codec_io + reg);
+	outl(value, chip->ctrl_io + reg);
 }
 }
 
 
 static inline void
 static inline void
@@ -404,13 +481,13 @@ snd_azf3328_mixer_inw(const struct snd_azf3328 *chip, unsigned reg)
 
 
 #define AZF_MUTE_BIT 0x80
 #define AZF_MUTE_BIT 0x80
 
 
-static int
+static bool
 snd_azf3328_mixer_set_mute(const struct snd_azf3328 *chip,
 snd_azf3328_mixer_set_mute(const struct snd_azf3328 *chip,
-			   unsigned reg, int do_mute
+			   unsigned reg, bool do_mute
 )
 )
 {
 {
 	unsigned long portbase = chip->mixer_io + reg + 1;
 	unsigned long portbase = chip->mixer_io + reg + 1;
-	int updated;
+	bool updated;
 
 
 	/* the mute bit is on the *second* (i.e. right) register of a
 	/* the mute bit is on the *second* (i.e. right) register of a
 	 * left/right channel setting */
 	 * left/right channel setting */
@@ -569,7 +646,7 @@ snd_azf3328_get_mixer(struct snd_kcontrol *kcontrol,
 {
 {
 	struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
 	struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
 	struct azf3328_mixer_reg reg;
 	struct azf3328_mixer_reg reg;
-	unsigned int oreg, val;
+	u16 oreg, val;
 
 
 	snd_azf3328_dbgcallenter();
 	snd_azf3328_dbgcallenter();
 	snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
 	snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
@@ -600,7 +677,7 @@ snd_azf3328_put_mixer(struct snd_kcontrol *kcontrol,
 {
 {
 	struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
 	struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
 	struct azf3328_mixer_reg reg;
 	struct azf3328_mixer_reg reg;
-	unsigned int oreg, nreg, val;
+	u16 oreg, nreg, val;
 
 
 	snd_azf3328_dbgcallenter();
 	snd_azf3328_dbgcallenter();
 	snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
 	snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
@@ -709,7 +786,7 @@ snd_azf3328_put_mixer_enum(struct snd_kcontrol *kcontrol,
 {
 {
         struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
         struct snd_azf3328 *chip = snd_kcontrol_chip(kcontrol);
 	struct azf3328_mixer_reg reg;
 	struct azf3328_mixer_reg reg;
-	unsigned int oreg, nreg, val;
+	u16 oreg, nreg, val;
 
 
 	snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
 	snd_azf3328_mixer_reg_decode(&reg, kcontrol->private_value);
 	oreg = snd_azf3328_mixer_inw(chip, reg.reg);
 	oreg = snd_azf3328_mixer_inw(chip, reg.reg);
@@ -867,14 +944,15 @@ snd_azf3328_hw_free(struct snd_pcm_substream *substream)
 
 
 static void
 static void
 snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
 snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
-			       unsigned reg,
+			       enum snd_azf3328_codec_type codec_type,
 			       enum azf_freq_t bitrate,
 			       enum azf_freq_t bitrate,
 			       unsigned int format_width,
 			       unsigned int format_width,
 			       unsigned int channels
 			       unsigned int channels
 )
 )
 {
 {
-	u16 val = 0xff00;
 	unsigned long flags;
 	unsigned long flags;
+	const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
+	u16 val = 0xff00;
 
 
 	snd_azf3328_dbgcallenter();
 	snd_azf3328_dbgcallenter();
 	switch (bitrate) {
 	switch (bitrate) {
@@ -917,7 +995,7 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	spin_lock_irqsave(&chip->reg_lock, flags);
 
 
 	/* set bitrate/format */
 	/* set bitrate/format */
-	snd_azf3328_codec_outw(chip, reg, val);
+	snd_azf3328_codec_outw(codec, IDX_IO_CODEC_SOUNDFORMAT, val);
 
 
 	/* changing the bitrate/format settings switches off the
 	/* changing the bitrate/format settings switches off the
 	 * audio output with an annoying click in case of 8/16bit format change
 	 * audio output with an annoying click in case of 8/16bit format change
@@ -926,11 +1004,11 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
 	 * (FIXME: yes, it works, but what exactly am I doing here?? :)
 	 * (FIXME: yes, it works, but what exactly am I doing here?? :)
 	 * FIXME: does this have some side effects for full-duplex
 	 * FIXME: does this have some side effects for full-duplex
 	 * or other dramatic side effects? */
 	 * or other dramatic side effects? */
-	if (reg == IDX_IO_PLAY_SOUNDFORMAT) /* only do it for playback */
-		snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS,
-			snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS) |
-			DMA_PLAY_SOMETHING1 |
-			DMA_PLAY_SOMETHING2 |
+	if (codec_type == AZF_CODEC_PLAYBACK) /* only do it for playback */
+		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
+			snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS) |
+			DMA_RUN_SOMETHING1 |
+			DMA_RUN_SOMETHING2 |
 			SOMETHING_ALMOST_ALWAYS_SET |
 			SOMETHING_ALMOST_ALWAYS_SET |
 			DMA_EPILOGUE_SOMETHING |
 			DMA_EPILOGUE_SOMETHING |
 			DMA_SOMETHING_ELSE
 			DMA_SOMETHING_ELSE
@@ -942,112 +1020,134 @@ snd_azf3328_codec_setfmt(struct snd_azf3328 *chip,
 
 
 static inline void
 static inline void
 snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328 *chip,
 snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328 *chip,
-			    unsigned reg
+			    enum snd_azf3328_codec_type codec_type
 )
 )
 {
 {
 	/* choose lowest frequency for low power consumption.
 	/* choose lowest frequency for low power consumption.
 	 * While this will cause louder noise due to rather coarse frequency,
 	 * While this will cause louder noise due to rather coarse frequency,
 	 * it should never matter since output should always
 	 * it should never matter since output should always
 	 * get disabled properly when idle anyway. */
 	 * get disabled properly when idle anyway. */
-	snd_azf3328_codec_setfmt(chip, reg, AZF_FREQ_4000, 8, 1);
+	snd_azf3328_codec_setfmt(chip, codec_type, AZF_FREQ_4000, 8, 1);
 }
 }
 
 
 static void
 static void
-snd_azf3328_codec_reg_6AH_update(struct snd_azf3328 *chip,
+snd_azf3328_ctrl_reg_6AH_update(struct snd_azf3328 *chip,
 					unsigned bitmask,
 					unsigned bitmask,
-					int enable
+					bool enable
 )
 )
 {
 {
-	if (enable)
-		chip->shadow_reg_codec_6AH &= ~bitmask;
+	bool do_mask = !enable;
+	if (do_mask)
+		chip->shadow_reg_ctrl_6AH |= bitmask;
 	else
 	else
-		chip->shadow_reg_codec_6AH |= bitmask;
-	snd_azf3328_dbgplay("6AH_update mask 0x%04x enable %d: val 0x%04x\n",
-			bitmask, enable, chip->shadow_reg_codec_6AH);
-	snd_azf3328_codec_outw(chip, IDX_IO_6AH, chip->shadow_reg_codec_6AH);
+		chip->shadow_reg_ctrl_6AH &= ~bitmask;
+	snd_azf3328_dbgcodec("6AH_update mask 0x%04x do_mask %d: val 0x%04x\n",
+			bitmask, do_mask, chip->shadow_reg_ctrl_6AH);
+	snd_azf3328_ctrl_outw(chip, IDX_IO_6AH, chip->shadow_reg_ctrl_6AH);
 }
 }
 
 
 static inline void
 static inline void
-snd_azf3328_codec_enable(struct snd_azf3328 *chip, int enable)
+snd_azf3328_ctrl_enable_codecs(struct snd_azf3328 *chip, bool enable)
 {
 {
-	snd_azf3328_dbgplay("codec_enable %d\n", enable);
+	snd_azf3328_dbgcodec("codec_enable %d\n", enable);
 	/* no idea what exactly is being done here, but I strongly assume it's
 	/* no idea what exactly is being done here, but I strongly assume it's
 	 * PM related */
 	 * PM related */
-	snd_azf3328_codec_reg_6AH_update(
+	snd_azf3328_ctrl_reg_6AH_update(
 		chip, IO_6A_PAUSE_PLAYBACK_BIT8, enable
 		chip, IO_6A_PAUSE_PLAYBACK_BIT8, enable
 	);
 	);
 }
 }
 
 
 static void
 static void
-snd_azf3328_codec_activity(struct snd_azf3328 *chip,
-				enum snd_azf3328_stream_index stream_type,
-				int enable
+snd_azf3328_ctrl_codec_activity(struct snd_azf3328 *chip,
+				enum snd_azf3328_codec_type codec_type,
+				bool enable
 )
 )
 {
 {
-	int need_change = (chip->audio_stream[stream_type].running != enable);
+	struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
+	bool need_change = (codec->running != enable);
 
 
-	snd_azf3328_dbgplay(
-		"codec_activity: type %d, enable %d, need_change %d\n",
-				stream_type, enable, need_change
+	snd_azf3328_dbgcodec(
+		"codec_activity: %s codec, enable %d, need_change %d\n",
+				codec->name, enable, need_change
 	);
 	);
 	if (need_change) {
 	if (need_change) {
-		enum snd_azf3328_stream_index other =
-			(stream_type == AZF_PLAYBACK) ?
-				AZF_CAPTURE : AZF_PLAYBACK;
-		/* small check to prevent shutting down the other party
-		 * in case it's active */
-		if ((enable) || !(chip->audio_stream[other].running))
-			snd_azf3328_codec_enable(chip, enable);
+		static const struct {
+			enum snd_azf3328_codec_type other1;
+			enum snd_azf3328_codec_type other2;
+		} peer_codecs[3] =
+			{ { AZF_CODEC_CAPTURE, AZF_CODEC_I2S_OUT },
+			  { AZF_CODEC_PLAYBACK, AZF_CODEC_I2S_OUT },
+			  { AZF_CODEC_PLAYBACK, AZF_CODEC_CAPTURE } };
+		bool call_function;
+
+		if (enable)
+			/* if enable codec, call enable_codecs func
+			   to enable codec supply... */
+			call_function = 1;
+		else {
+			/* ...otherwise call enable_codecs func
+			   (which globally shuts down operation of codecs)
+			   only in case the other codecs are currently
+			   not active either! */
+			call_function =
+				((!chip->codecs[peer_codecs[codec_type].other1]
+					.running)
+			     &&  (!chip->codecs[peer_codecs[codec_type].other2]
+					.running));
+		 }
+		 if (call_function)
+			snd_azf3328_ctrl_enable_codecs(chip, enable);
 
 
 		/* ...and adjust clock, too
 		/* ...and adjust clock, too
 		 * (reduce noise and power consumption) */
 		 * (reduce noise and power consumption) */
 		if (!enable)
 		if (!enable)
 			snd_azf3328_codec_setfmt_lowpower(
 			snd_azf3328_codec_setfmt_lowpower(
 				chip,
 				chip,
-				chip->audio_stream[stream_type].portbase
-					+ IDX_IO_PLAY_SOUNDFORMAT
+				codec_type
 			);
 			);
+		codec->running = enable;
 	}
 	}
-	chip->audio_stream[stream_type].running = enable;
 }
 }
 
 
 static void
 static void
-snd_azf3328_setdmaa(struct snd_azf3328 *chip,
-				long unsigned int addr,
-                                unsigned int count,
-                                unsigned int size,
-				enum snd_azf3328_stream_index stream_type
+snd_azf3328_codec_setdmaa(struct snd_azf3328 *chip,
+				enum snd_azf3328_codec_type codec_type,
+				unsigned long addr,
+				unsigned int count,
+				unsigned int size
 )
 )
 {
 {
+	const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
 	snd_azf3328_dbgcallenter();
 	snd_azf3328_dbgcallenter();
-	if (!chip->audio_stream[stream_type].running) {
-		/* AZF3328 uses a two buffer pointer DMA playback approach */
+	if (!codec->running) {
+		/* AZF3328 uses a two buffer pointer DMA transfer approach */
 
 
-		unsigned long flags, portbase, addr_area2;
+		unsigned long flags, addr_area2;
 
 
 		/* width 32bit (prevent overflow): */
 		/* width 32bit (prevent overflow): */
-		unsigned long count_areas, count_tmp;
+		u32 count_areas, lengths;
 
 
-		portbase = chip->audio_stream[stream_type].portbase;
 		count_areas = size/2;
 		count_areas = size/2;
 		addr_area2 = addr+count_areas;
 		addr_area2 = addr+count_areas;
 		count_areas--; /* max. index */
 		count_areas--; /* max. index */
-		snd_azf3328_dbgplay("set DMA: buf1 %08lx[%lu], buf2 %08lx[%lu]\n", addr, count_areas, addr_area2, count_areas);
+		snd_azf3328_dbgcodec("setdma: buffers %08lx[%u] / %08lx[%u]\n",
+				addr, count_areas, addr_area2, count_areas);
 
 
 		/* build combined I/O buffer length word */
 		/* build combined I/O buffer length word */
-		count_tmp = count_areas;
-		count_areas |= (count_tmp << 16);
+		lengths = (count_areas << 16) | (count_areas);
 		spin_lock_irqsave(&chip->reg_lock, flags);
 		spin_lock_irqsave(&chip->reg_lock, flags);
-		outl(addr, portbase + IDX_IO_PLAY_DMA_START_1);
-		outl(addr_area2, portbase + IDX_IO_PLAY_DMA_START_2);
-		outl(count_areas, portbase + IDX_IO_PLAY_DMA_LEN_1);
+		snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_START_1, addr);
+		snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_START_2,
+								addr_area2);
+		snd_azf3328_codec_outl(codec, IDX_IO_CODEC_DMA_LENGTHS,
+								lengths);
 		spin_unlock_irqrestore(&chip->reg_lock, flags);
 		spin_unlock_irqrestore(&chip->reg_lock, flags);
 	}
 	}
 	snd_azf3328_dbgcallleave();
 	snd_azf3328_dbgcallleave();
 }
 }
 
 
 static int
 static int
-snd_azf3328_playback_prepare(struct snd_pcm_substream *substream)
+snd_azf3328_codec_prepare(struct snd_pcm_substream *substream)
 {
 {
 #if 0
 #if 0
 	struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
 	struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
@@ -1058,157 +1158,161 @@ snd_azf3328_playback_prepare(struct snd_pcm_substream *substream)
 
 
 	snd_azf3328_dbgcallenter();
 	snd_azf3328_dbgcallenter();
 #if 0
 #if 0
-	snd_azf3328_codec_setfmt(chip, IDX_IO_PLAY_SOUNDFORMAT,
+	snd_azf3328_codec_setfmt(chip, AZF_CODEC_...,
 		runtime->rate,
 		runtime->rate,
 		snd_pcm_format_width(runtime->format),
 		snd_pcm_format_width(runtime->format),
 		runtime->channels);
 		runtime->channels);
-	snd_azf3328_setdmaa(chip, runtime->dma_addr, count, size, AZF_PLAYBACK);
+	snd_azf3328_codec_setdmaa(chip, AZF_CODEC_...,
+					runtime->dma_addr, count, size);
 #endif
 #endif
 	snd_azf3328_dbgcallleave();
 	snd_azf3328_dbgcallleave();
 	return 0;
 	return 0;
 }
 }
 
 
 static int
 static int
-snd_azf3328_capture_prepare(struct snd_pcm_substream *substream)
-{
-#if 0
-	struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
-	struct snd_pcm_runtime *runtime = substream->runtime;
-        unsigned int size = snd_pcm_lib_buffer_bytes(substream);
-	unsigned int count = snd_pcm_lib_period_bytes(substream);
-#endif
-
-	snd_azf3328_dbgcallenter();
-#if 0
-	snd_azf3328_codec_setfmt(chip, IDX_IO_REC_SOUNDFORMAT,
-		runtime->rate,
-		snd_pcm_format_width(runtime->format),
-		runtime->channels);
-	snd_azf3328_setdmaa(chip, runtime->dma_addr, count, size, AZF_CAPTURE);
-#endif
-	snd_azf3328_dbgcallleave();
-	return 0;
-}
-
-static int
-snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+snd_azf3328_codec_trigger(enum snd_azf3328_codec_type codec_type,
+			struct snd_pcm_substream *substream, int cmd)
 {
 {
 	struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
 	struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
+	const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	int result = 0;
 	int result = 0;
-	unsigned int status1;
-	int previously_muted;
+	u16 flags1;
+	bool previously_muted = 0;
+	bool is_playback_codec = (AZF_CODEC_PLAYBACK == codec_type);
 
 
-	snd_azf3328_dbgcalls("snd_azf3328_playback_trigger cmd %d\n", cmd);
+	snd_azf3328_dbgcalls("snd_azf3328_codec_trigger cmd %d\n", cmd);
 
 
 	switch (cmd) {
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
 	case SNDRV_PCM_TRIGGER_START:
-		snd_azf3328_dbgplay("START PLAYBACK\n");
-
-		/* mute WaveOut (avoid clicking during setup) */
-		previously_muted =
-			snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
+		snd_azf3328_dbgcodec("START %s\n", codec->name);
+
+		if (is_playback_codec) {
+			/* mute WaveOut (avoid clicking during setup) */
+			previously_muted =
+				snd_azf3328_mixer_set_mute(
+						chip, IDX_MIXER_WAVEOUT, 1
+				);
+		}
 
 
-		snd_azf3328_codec_setfmt(chip, IDX_IO_PLAY_SOUNDFORMAT,
+		snd_azf3328_codec_setfmt(chip, codec_type,
 			runtime->rate,
 			runtime->rate,
 			snd_pcm_format_width(runtime->format),
 			snd_pcm_format_width(runtime->format),
 			runtime->channels);
 			runtime->channels);
 
 
 		spin_lock(&chip->reg_lock);
 		spin_lock(&chip->reg_lock);
 		/* first, remember current value: */
 		/* first, remember current value: */
-		status1 = snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS);
+		flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
 
 
-		/* stop playback */
-		status1 &= ~DMA_RESUME;
-		snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1);
+		/* stop transfer */
+		flags1 &= ~DMA_RESUME;
+		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
 
 
 		/* FIXME: clear interrupts or what??? */
 		/* FIXME: clear interrupts or what??? */
-		snd_azf3328_codec_outw(chip, IDX_IO_PLAY_IRQTYPE, 0xffff);
+		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_IRQTYPE, 0xffff);
 		spin_unlock(&chip->reg_lock);
 		spin_unlock(&chip->reg_lock);
 
 
-		snd_azf3328_setdmaa(chip, runtime->dma_addr,
+		snd_azf3328_codec_setdmaa(chip, codec_type, runtime->dma_addr,
 			snd_pcm_lib_period_bytes(substream),
 			snd_pcm_lib_period_bytes(substream),
-			snd_pcm_lib_buffer_bytes(substream),
-			AZF_PLAYBACK);
+			snd_pcm_lib_buffer_bytes(substream)
+		);
 
 
 		spin_lock(&chip->reg_lock);
 		spin_lock(&chip->reg_lock);
 #ifdef WIN9X
 #ifdef WIN9X
 		/* FIXME: enable playback/recording??? */
 		/* FIXME: enable playback/recording??? */
-		status1 |= DMA_PLAY_SOMETHING1 | DMA_PLAY_SOMETHING2;
-		snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1);
+		flags1 |= DMA_RUN_SOMETHING1 | DMA_RUN_SOMETHING2;
+		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
 
 
-		/* start playback again */
+		/* start transfer again */
 		/* FIXME: what is this value (0x0010)??? */
 		/* FIXME: what is this value (0x0010)??? */
-		status1 |= DMA_RESUME | DMA_EPILOGUE_SOMETHING;
-		snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1);
+		flags1 |= DMA_RESUME | DMA_EPILOGUE_SOMETHING;
+		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
 #else /* NT4 */
 #else /* NT4 */
-		snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS,
+		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
 			0x0000);
 			0x0000);
-		snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS,
-			DMA_PLAY_SOMETHING1);
-		snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS,
-			DMA_PLAY_SOMETHING1 |
-			DMA_PLAY_SOMETHING2);
-		snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS,
+		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
+			DMA_RUN_SOMETHING1);
+		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
+			DMA_RUN_SOMETHING1 |
+			DMA_RUN_SOMETHING2);
+		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
 			DMA_RESUME |
 			DMA_RESUME |
 			SOMETHING_ALMOST_ALWAYS_SET |
 			SOMETHING_ALMOST_ALWAYS_SET |
 			DMA_EPILOGUE_SOMETHING |
 			DMA_EPILOGUE_SOMETHING |
 			DMA_SOMETHING_ELSE);
 			DMA_SOMETHING_ELSE);
 #endif
 #endif
 		spin_unlock(&chip->reg_lock);
 		spin_unlock(&chip->reg_lock);
-		snd_azf3328_codec_activity(chip, AZF_PLAYBACK, 1);
-
-		/* now unmute WaveOut */
-		if (!previously_muted)
-			snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 0);
+		snd_azf3328_ctrl_codec_activity(chip, codec_type, 1);
+
+		if (is_playback_codec) {
+			/* now unmute WaveOut */
+			if (!previously_muted)
+				snd_azf3328_mixer_set_mute(
+						chip, IDX_MIXER_WAVEOUT, 0
+				);
+		}
 
 
-		snd_azf3328_dbgplay("STARTED PLAYBACK\n");
+		snd_azf3328_dbgcodec("STARTED %s\n", codec->name);
 		break;
 		break;
 	case SNDRV_PCM_TRIGGER_RESUME:
 	case SNDRV_PCM_TRIGGER_RESUME:
-		snd_azf3328_dbgplay("RESUME PLAYBACK\n");
-		/* resume playback if we were active */
+		snd_azf3328_dbgcodec("RESUME %s\n", codec->name);
+		/* resume codec if we were active */
 		spin_lock(&chip->reg_lock);
 		spin_lock(&chip->reg_lock);
-		if (chip->audio_stream[AZF_PLAYBACK].running)
-			snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS,
-				snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS) | DMA_RESUME);
+		if (codec->running)
+			snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
+				snd_azf3328_codec_inw(
+					codec, IDX_IO_CODEC_DMA_FLAGS
+				) | DMA_RESUME
+			);
 		spin_unlock(&chip->reg_lock);
 		spin_unlock(&chip->reg_lock);
 		break;
 		break;
 	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_STOP:
-		snd_azf3328_dbgplay("STOP PLAYBACK\n");
-
-		/* mute WaveOut (avoid clicking during setup) */
-		previously_muted =
-			snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
+		snd_azf3328_dbgcodec("STOP %s\n", codec->name);
+
+		if (is_playback_codec) {
+			/* mute WaveOut (avoid clicking during setup) */
+			previously_muted =
+				snd_azf3328_mixer_set_mute(
+						chip, IDX_MIXER_WAVEOUT, 1
+				);
+		}
 
 
 		spin_lock(&chip->reg_lock);
 		spin_lock(&chip->reg_lock);
 		/* first, remember current value: */
 		/* first, remember current value: */
-		status1 = snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS);
+		flags1 = snd_azf3328_codec_inw(codec, IDX_IO_CODEC_DMA_FLAGS);
 
 
-		/* stop playback */
-		status1 &= ~DMA_RESUME;
-		snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1);
+		/* stop transfer */
+		flags1 &= ~DMA_RESUME;
+		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
 
 
 		/* hmm, is this really required? we're resetting the same bit
 		/* hmm, is this really required? we're resetting the same bit
 		 * immediately thereafter... */
 		 * immediately thereafter... */
-		status1 |= DMA_PLAY_SOMETHING1;
-		snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1);
+		flags1 |= DMA_RUN_SOMETHING1;
+		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
 
 
-		status1 &= ~DMA_PLAY_SOMETHING1;
-		snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS, status1);
+		flags1 &= ~DMA_RUN_SOMETHING1;
+		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS, flags1);
 		spin_unlock(&chip->reg_lock);
 		spin_unlock(&chip->reg_lock);
-		snd_azf3328_codec_activity(chip, AZF_PLAYBACK, 0);
-
-		/* now unmute WaveOut */
-		if (!previously_muted)
-			snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 0);
+		snd_azf3328_ctrl_codec_activity(chip, codec_type, 0);
+
+		if (is_playback_codec) {
+			/* now unmute WaveOut */
+			if (!previously_muted)
+				snd_azf3328_mixer_set_mute(
+						chip, IDX_MIXER_WAVEOUT, 0
+				);
+		}
 
 
-		snd_azf3328_dbgplay("STOPPED PLAYBACK\n");
+		snd_azf3328_dbgcodec("STOPPED %s\n", codec->name);
 		break;
 		break;
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 	case SNDRV_PCM_TRIGGER_SUSPEND:
-		snd_azf3328_dbgplay("SUSPEND PLAYBACK\n");
-		/* make sure playback is stopped */
-		snd_azf3328_codec_outw(chip, IDX_IO_PLAY_FLAGS,
-			snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS) & ~DMA_RESUME);
+		snd_azf3328_dbgcodec("SUSPEND %s\n", codec->name);
+		/* make sure codec is stopped */
+		snd_azf3328_codec_outw(codec, IDX_IO_CODEC_DMA_FLAGS,
+			snd_azf3328_codec_inw(
+				codec, IDX_IO_CODEC_DMA_FLAGS
+			) & ~DMA_RESUME
+		);
 		break;
 		break;
         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 		snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n");
 		snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n");
@@ -1217,7 +1321,7 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd)
 		snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n");
 		snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n");
                 break;
                 break;
         default:
         default:
-		printk(KERN_ERR "FIXME: unknown trigger mode!\n");
+		snd_printk(KERN_ERR "FIXME: unknown trigger mode!\n");
                 return -EINVAL;
                 return -EINVAL;
 	}
 	}
 
 
@@ -1225,172 +1329,74 @@ snd_azf3328_playback_trigger(struct snd_pcm_substream *substream, int cmd)
 	return result;
 	return result;
 }
 }
 
 
-/* this is just analogous to playback; I'm not quite sure whether recording
- * should actually be triggered like that */
 static int
 static int
-snd_azf3328_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+snd_azf3328_codec_playback_trigger(struct snd_pcm_substream *substream, int cmd)
 {
 {
-	struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	int result = 0;
-	unsigned int status1;
-
-	snd_azf3328_dbgcalls("snd_azf3328_capture_trigger cmd %d\n", cmd);
-
-        switch (cmd) {
-        case SNDRV_PCM_TRIGGER_START:
-
-		snd_azf3328_dbgplay("START CAPTURE\n");
-
-		snd_azf3328_codec_setfmt(chip, IDX_IO_REC_SOUNDFORMAT,
-			runtime->rate,
-			snd_pcm_format_width(runtime->format),
-			runtime->channels);
-
-		spin_lock(&chip->reg_lock);
-		/* first, remember current value: */
-		status1 = snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS);
-
-		/* stop recording */
-		status1 &= ~DMA_RESUME;
-		snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1);
-
-		/* FIXME: clear interrupts or what??? */
-		snd_azf3328_codec_outw(chip, IDX_IO_REC_IRQTYPE, 0xffff);
-		spin_unlock(&chip->reg_lock);
-
-		snd_azf3328_setdmaa(chip, runtime->dma_addr,
-			snd_pcm_lib_period_bytes(substream),
-			snd_pcm_lib_buffer_bytes(substream),
-			AZF_CAPTURE);
-
-		spin_lock(&chip->reg_lock);
-#ifdef WIN9X
-		/* FIXME: enable playback/recording??? */
-		status1 |= DMA_PLAY_SOMETHING1 | DMA_PLAY_SOMETHING2;
-		snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1);
-
-		/* start capture again */
-		/* FIXME: what is this value (0x0010)??? */
-		status1 |= DMA_RESUME | DMA_EPILOGUE_SOMETHING;
-		snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1);
-#else
-		snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS,
-			0x0000);
-		snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS,
-			DMA_PLAY_SOMETHING1);
-		snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS,
-			DMA_PLAY_SOMETHING1 |
-			DMA_PLAY_SOMETHING2);
-		snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS,
-			DMA_RESUME |
-			SOMETHING_ALMOST_ALWAYS_SET |
-			DMA_EPILOGUE_SOMETHING |
-			DMA_SOMETHING_ELSE);
-#endif
-		spin_unlock(&chip->reg_lock);
-		snd_azf3328_codec_activity(chip, AZF_CAPTURE, 1);
-
-		snd_azf3328_dbgplay("STARTED CAPTURE\n");
-		break;
-	case SNDRV_PCM_TRIGGER_RESUME:
-		snd_azf3328_dbgplay("RESUME CAPTURE\n");
-		/* resume recording if we were active */
-		spin_lock(&chip->reg_lock);
-		if (chip->audio_stream[AZF_CAPTURE].running)
-			snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS,
-				snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS) | DMA_RESUME);
-		spin_unlock(&chip->reg_lock);
-		break;
-        case SNDRV_PCM_TRIGGER_STOP:
-		snd_azf3328_dbgplay("STOP CAPTURE\n");
-
-		spin_lock(&chip->reg_lock);
-		/* first, remember current value: */
-		status1 = snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS);
-
-		/* stop recording */
-		status1 &= ~DMA_RESUME;
-		snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1);
-
-		status1 |= DMA_PLAY_SOMETHING1;
-		snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1);
-
-		status1 &= ~DMA_PLAY_SOMETHING1;
-		snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS, status1);
-		spin_unlock(&chip->reg_lock);
-		snd_azf3328_codec_activity(chip, AZF_CAPTURE, 0);
+	return snd_azf3328_codec_trigger(AZF_CODEC_PLAYBACK, substream, cmd);
+}
 
 
-		snd_azf3328_dbgplay("STOPPED CAPTURE\n");
-		break;
-	case SNDRV_PCM_TRIGGER_SUSPEND:
-		snd_azf3328_dbgplay("SUSPEND CAPTURE\n");
-		/* make sure recording is stopped */
-		snd_azf3328_codec_outw(chip, IDX_IO_REC_FLAGS,
-			snd_azf3328_codec_inw(chip, IDX_IO_REC_FLAGS) & ~DMA_RESUME);
-		break;
-        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_PUSH NIY!\n");
-                break;
-        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		snd_printk(KERN_ERR "FIXME: SNDRV_PCM_TRIGGER_PAUSE_RELEASE NIY!\n");
-                break;
-        default:
-		printk(KERN_ERR "FIXME: unknown trigger mode!\n");
-                return -EINVAL;
-	}
+static int
+snd_azf3328_codec_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	return snd_azf3328_codec_trigger(AZF_CODEC_CAPTURE, substream, cmd);
+}
 
 
-	snd_azf3328_dbgcallleave();
-	return result;
+static int
+snd_azf3328_codec_i2s_out_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	return snd_azf3328_codec_trigger(AZF_CODEC_I2S_OUT, substream, cmd);
 }
 }
 
 
 static snd_pcm_uframes_t
 static snd_pcm_uframes_t
-snd_azf3328_playback_pointer(struct snd_pcm_substream *substream)
+snd_azf3328_codec_pointer(struct snd_pcm_substream *substream,
+			  enum snd_azf3328_codec_type codec_type
+)
 {
 {
-	struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
+	const struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
+	const struct snd_azf3328_codec_data *codec = &chip->codecs[codec_type];
 	unsigned long bufptr, result;
 	unsigned long bufptr, result;
 	snd_pcm_uframes_t frmres;
 	snd_pcm_uframes_t frmres;
 
 
 #ifdef QUERY_HARDWARE
 #ifdef QUERY_HARDWARE
-	bufptr = snd_azf3328_codec_inl(chip, IDX_IO_PLAY_DMA_START_1);
+	bufptr = snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_START_1);
 #else
 #else
 	bufptr = substream->runtime->dma_addr;
 	bufptr = substream->runtime->dma_addr;
 #endif
 #endif
-	result = snd_azf3328_codec_inl(chip, IDX_IO_PLAY_DMA_CURRPOS);
+	result = snd_azf3328_codec_inl(codec, IDX_IO_CODEC_DMA_CURRPOS);
 
 
 	/* calculate offset */
 	/* calculate offset */
 	result -= bufptr;
 	result -= bufptr;
 	frmres = bytes_to_frames( substream->runtime, result);
 	frmres = bytes_to_frames( substream->runtime, result);
-	snd_azf3328_dbgplay("PLAY @ 0x%8lx, frames %8ld\n", result, frmres);
+	snd_azf3328_dbgcodec("%s @ 0x%8lx, frames %8ld\n",
+				codec->name, result, frmres);
 	return frmres;
 	return frmres;
 }
 }
 
 
 static snd_pcm_uframes_t
 static snd_pcm_uframes_t
-snd_azf3328_capture_pointer(struct snd_pcm_substream *substream)
+snd_azf3328_codec_playback_pointer(struct snd_pcm_substream *substream)
 {
 {
-	struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
-	unsigned long bufptr, result;
-	snd_pcm_uframes_t frmres;
+	return snd_azf3328_codec_pointer(substream, AZF_CODEC_PLAYBACK);
+}
 
 
-#ifdef QUERY_HARDWARE
-	bufptr = snd_azf3328_codec_inl(chip, IDX_IO_REC_DMA_START_1);
-#else
-	bufptr = substream->runtime->dma_addr;
-#endif
-	result = snd_azf3328_codec_inl(chip, IDX_IO_REC_DMA_CURRPOS);
+static snd_pcm_uframes_t
+snd_azf3328_codec_capture_pointer(struct snd_pcm_substream *substream)
+{
+	return snd_azf3328_codec_pointer(substream, AZF_CODEC_CAPTURE);
+}
 
 
-	/* calculate offset */
-	result -= bufptr;
-	frmres = bytes_to_frames( substream->runtime, result);
-	snd_azf3328_dbgplay("REC  @ 0x%8lx, frames %8ld\n", result, frmres);
-	return frmres;
+static snd_pcm_uframes_t
+snd_azf3328_codec_i2s_out_pointer(struct snd_pcm_substream *substream)
+{
+	return snd_azf3328_codec_pointer(substream, AZF_CODEC_I2S_OUT);
 }
 }
 
 
 /******************************************************************/
 /******************************************************************/
 
 
 #ifdef SUPPORT_GAMEPORT
 #ifdef SUPPORT_GAMEPORT
 static inline void
 static inline void
-snd_azf3328_gameport_irq_enable(struct snd_azf3328 *chip, int enable)
+snd_azf3328_gameport_irq_enable(struct snd_azf3328 *chip,
+				bool enable
+)
 {
 {
 	snd_azf3328_io_reg_setb(
 	snd_azf3328_io_reg_setb(
 		chip->game_io+IDX_GAME_HWCONFIG,
 		chip->game_io+IDX_GAME_HWCONFIG,
@@ -1400,7 +1406,9 @@ snd_azf3328_gameport_irq_enable(struct snd_azf3328 *chip, int enable)
 }
 }
 
 
 static inline void
 static inline void
-snd_azf3328_gameport_legacy_address_enable(struct snd_azf3328 *chip, int enable)
+snd_azf3328_gameport_legacy_address_enable(struct snd_azf3328 *chip,
+					   bool enable
+)
 {
 {
 	snd_azf3328_io_reg_setb(
 	snd_azf3328_io_reg_setb(
 		chip->game_io+IDX_GAME_HWCONFIG,
 		chip->game_io+IDX_GAME_HWCONFIG,
@@ -1409,10 +1417,27 @@ snd_azf3328_gameport_legacy_address_enable(struct snd_azf3328 *chip, int enable)
 	);
 	);
 }
 }
 
 
+static void
+snd_azf3328_gameport_set_counter_frequency(struct snd_azf3328 *chip,
+					   unsigned int freq_cfg
+)
+{
+	snd_azf3328_io_reg_setb(
+		chip->game_io+IDX_GAME_HWCONFIG,
+		0x02,
+		(freq_cfg & 1) != 0
+	);
+	snd_azf3328_io_reg_setb(
+		chip->game_io+IDX_GAME_HWCONFIG,
+		0x04,
+		(freq_cfg & 2) != 0
+	);
+}
+
 static inline void
 static inline void
-snd_azf3328_gameport_axis_circuit_enable(struct snd_azf3328 *chip, int enable)
+snd_azf3328_gameport_axis_circuit_enable(struct snd_azf3328 *chip, bool enable)
 {
 {
-	snd_azf3328_codec_reg_6AH_update(
+	snd_azf3328_ctrl_reg_6AH_update(
 		chip, IO_6A_SOMETHING2_GAMEPORT, enable
 		chip, IO_6A_SOMETHING2_GAMEPORT, enable
 	);
 	);
 }
 }
@@ -1447,6 +1472,8 @@ snd_azf3328_gameport_open(struct gameport *gameport, int mode)
 		break;
 		break;
 	}
 	}
 
 
+	snd_azf3328_gameport_set_counter_frequency(chip,
+				GAME_HWCFG_ADC_COUNTER_FREQ_STD);
 	snd_azf3328_gameport_axis_circuit_enable(chip, (res == 0));
 	snd_azf3328_gameport_axis_circuit_enable(chip, (res == 0));
 
 
 	return res;
 	return res;
@@ -1458,6 +1485,8 @@ snd_azf3328_gameport_close(struct gameport *gameport)
 	struct snd_azf3328 *chip = gameport_get_port_data(gameport);
 	struct snd_azf3328 *chip = gameport_get_port_data(gameport);
 
 
 	snd_azf3328_dbggame("gameport_close\n");
 	snd_azf3328_dbggame("gameport_close\n");
+	snd_azf3328_gameport_set_counter_frequency(chip,
+				GAME_HWCFG_ADC_COUNTER_FREQ_1_200);
 	snd_azf3328_gameport_axis_circuit_enable(chip, 0);
 	snd_azf3328_gameport_axis_circuit_enable(chip, 0);
 }
 }
 
 
@@ -1491,7 +1520,7 @@ snd_azf3328_gameport_cooked_read(struct gameport *gameport,
 
 
 	val = snd_azf3328_game_inb(chip, IDX_GAME_AXES_CONFIG);
 	val = snd_azf3328_game_inb(chip, IDX_GAME_AXES_CONFIG);
 	if (val & GAME_AXES_SAMPLING_READY) {
 	if (val & GAME_AXES_SAMPLING_READY) {
-		for (i = 0; i < 4; ++i) {
+		for (i = 0; i < ARRAY_SIZE(chip->axes); ++i) {
 			/* configure the axis to read */
 			/* configure the axis to read */
 			val = (i << 4) | 0x0f;
 			val = (i << 4) | 0x0f;
 			snd_azf3328_game_outb(chip, IDX_GAME_AXES_CONFIG, val);
 			snd_azf3328_game_outb(chip, IDX_GAME_AXES_CONFIG, val);
@@ -1514,7 +1543,7 @@ snd_azf3328_gameport_cooked_read(struct gameport *gameport,
 	snd_azf3328_game_outw(chip, IDX_GAME_AXIS_VALUE, 0xffff);
 	snd_azf3328_game_outw(chip, IDX_GAME_AXIS_VALUE, 0xffff);
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
 
 
-	for (i = 0; i < 4; i++) {
+	for (i = 0; i < ARRAY_SIZE(chip->axes); i++) {
 		axes[i] = chip->axes[i];
 		axes[i] = chip->axes[i];
 		if (axes[i] == 0xffff)
 		if (axes[i] == 0xffff)
 			axes[i] = -1;
 			axes[i] = -1;
@@ -1552,6 +1581,8 @@ snd_azf3328_gameport(struct snd_azf3328 *chip, int dev)
 	/* DISABLE legacy address: we don't need it! */
 	/* DISABLE legacy address: we don't need it! */
 	snd_azf3328_gameport_legacy_address_enable(chip, 0);
 	snd_azf3328_gameport_legacy_address_enable(chip, 0);
 
 
+	snd_azf3328_gameport_set_counter_frequency(chip,
+				GAME_HWCFG_ADC_COUNTER_FREQ_1_200);
 	snd_azf3328_gameport_axis_circuit_enable(chip, 0);
 	snd_azf3328_gameport_axis_circuit_enable(chip, 0);
 
 
 	gameport_register_port(chip->gameport);
 	gameport_register_port(chip->gameport);
@@ -1585,40 +1616,77 @@ snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip)
 static inline void
 static inline void
 snd_azf3328_irq_log_unknown_type(u8 which)
 snd_azf3328_irq_log_unknown_type(u8 which)
 {
 {
-	snd_azf3328_dbgplay(
+	snd_azf3328_dbgcodec(
 	"azt3328: unknown IRQ type (%x) occurred, please report!\n",
 	"azt3328: unknown IRQ type (%x) occurred, please report!\n",
 		which
 		which
 	);
 	);
 }
 }
 
 
+static inline void
+snd_azf3328_codec_interrupt(struct snd_azf3328 *chip, u8 status)
+{
+	u8 which;
+	enum snd_azf3328_codec_type codec_type;
+	const struct snd_azf3328_codec_data *codec;
+
+	for (codec_type = AZF_CODEC_PLAYBACK;
+		 codec_type <= AZF_CODEC_I2S_OUT;
+			 ++codec_type) {
+
+		/* skip codec if there's no interrupt for it */
+		if (!(status & (1 << codec_type)))
+			continue;
+
+		codec = &chip->codecs[codec_type];
+
+		spin_lock(&chip->reg_lock);
+		which = snd_azf3328_codec_inb(codec, IDX_IO_CODEC_IRQTYPE);
+		/* ack all IRQ types immediately */
+		snd_azf3328_codec_outb(codec, IDX_IO_CODEC_IRQTYPE, which);
+		spin_unlock(&chip->reg_lock);
+
+		if ((chip->pcm[codec_type]) && (codec->substream)) {
+			snd_pcm_period_elapsed(codec->substream);
+			snd_azf3328_dbgcodec("%s period done (#%x), @ %x\n",
+				codec->name,
+				which,
+				snd_azf3328_codec_inl(
+					codec, IDX_IO_CODEC_DMA_CURRPOS
+				)
+			);
+		} else
+			printk(KERN_WARNING "azt3328: irq handler problem!\n");
+		if (which & IRQ_SOMETHING)
+			snd_azf3328_irq_log_unknown_type(which);
+	}
+}
+
 static irqreturn_t
 static irqreturn_t
 snd_azf3328_interrupt(int irq, void *dev_id)
 snd_azf3328_interrupt(int irq, void *dev_id)
 {
 {
 	struct snd_azf3328 *chip = dev_id;
 	struct snd_azf3328 *chip = dev_id;
-	u8 status, which;
-#if DEBUG_PLAY_REC
+	u8 status;
+#if DEBUG_CODEC
 	static unsigned long irq_count;
 	static unsigned long irq_count;
 #endif
 #endif
 
 
-	status = snd_azf3328_codec_inb(chip, IDX_IO_IRQSTATUS);
+	status = snd_azf3328_ctrl_inb(chip, IDX_IO_IRQSTATUS);
 
 
         /* fast path out, to ease interrupt sharing */
         /* fast path out, to ease interrupt sharing */
 	if (!(status &
 	if (!(status &
-		(IRQ_PLAYBACK|IRQ_RECORDING|IRQ_GAMEPORT|IRQ_MPU401|IRQ_TIMER)
+		(IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT
+		|IRQ_GAMEPORT|IRQ_MPU401|IRQ_TIMER)
 	))
 	))
 		return IRQ_NONE; /* must be interrupt for another device */
 		return IRQ_NONE; /* must be interrupt for another device */
 
 
-	snd_azf3328_dbgplay(
-		"irq_count %ld! IDX_IO_PLAY_FLAGS %04x, "
-		"IDX_IO_PLAY_IRQTYPE %04x, IDX_IO_IRQSTATUS %04x\n",
+	snd_azf3328_dbgcodec(
+		"irq_count %ld! IDX_IO_IRQSTATUS %04x\n",
 			irq_count++ /* debug-only */,
 			irq_count++ /* debug-only */,
-			snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS),
-			snd_azf3328_codec_inw(chip, IDX_IO_PLAY_IRQTYPE),
 			status
 			status
 	);
 	);
 
 
 	if (status & IRQ_TIMER) {
 	if (status & IRQ_TIMER) {
-		/* snd_azf3328_dbgplay("timer %ld\n",
+		/* snd_azf3328_dbgcodec("timer %ld\n",
 			snd_azf3328_codec_inl(chip, IDX_IO_TIMER_VALUE)
 			snd_azf3328_codec_inl(chip, IDX_IO_TIMER_VALUE)
 				& TIMER_VALUE_MASK
 				& TIMER_VALUE_MASK
 		); */
 		); */
@@ -1626,71 +1694,36 @@ snd_azf3328_interrupt(int irq, void *dev_id)
 			snd_timer_interrupt(chip->timer, chip->timer->sticks);
 			snd_timer_interrupt(chip->timer, chip->timer->sticks);
 		/* ACK timer */
 		/* ACK timer */
                 spin_lock(&chip->reg_lock);
                 spin_lock(&chip->reg_lock);
-		snd_azf3328_codec_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x07);
+		snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x07);
 		spin_unlock(&chip->reg_lock);
 		spin_unlock(&chip->reg_lock);
-		snd_azf3328_dbgplay("azt3328: timer IRQ\n");
+		snd_azf3328_dbgcodec("azt3328: timer IRQ\n");
 	}
 	}
-	if (status & IRQ_PLAYBACK) {
-		spin_lock(&chip->reg_lock);
-		which = snd_azf3328_codec_inb(chip, IDX_IO_PLAY_IRQTYPE);
-		/* ack all IRQ types immediately */
-		snd_azf3328_codec_outb(chip, IDX_IO_PLAY_IRQTYPE, which);
-               	spin_unlock(&chip->reg_lock);
 
 
-		if (chip->pcm && chip->audio_stream[AZF_PLAYBACK].substream) {
-			snd_pcm_period_elapsed(
-				chip->audio_stream[AZF_PLAYBACK].substream
-			);
-			snd_azf3328_dbgplay("PLAY period done (#%x), @ %x\n",
-				which,
-				snd_azf3328_codec_inl(
-					chip, IDX_IO_PLAY_DMA_CURRPOS
-				)
-			);
-		} else
-			printk(KERN_WARNING "azt3328: irq handler problem!\n");
-		if (which & IRQ_PLAY_SOMETHING)
-			snd_azf3328_irq_log_unknown_type(which);
-	}
-	if (status & IRQ_RECORDING) {
-                spin_lock(&chip->reg_lock);
-		which = snd_azf3328_codec_inb(chip, IDX_IO_REC_IRQTYPE);
-		/* ack all IRQ types immediately */
-		snd_azf3328_codec_outb(chip, IDX_IO_REC_IRQTYPE, which);
-		spin_unlock(&chip->reg_lock);
+	if (status & (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_I2S_OUT))
+		snd_azf3328_codec_interrupt(chip, status);
 
 
-		if (chip->pcm && chip->audio_stream[AZF_CAPTURE].substream) {
-			snd_pcm_period_elapsed(
-				chip->audio_stream[AZF_CAPTURE].substream
-			);
-			snd_azf3328_dbgplay("REC  period done (#%x), @ %x\n",
-				which,
-				snd_azf3328_codec_inl(
-					chip, IDX_IO_REC_DMA_CURRPOS
-				)
-			);
-		} else
-			printk(KERN_WARNING "azt3328: irq handler problem!\n");
-		if (which & IRQ_REC_SOMETHING)
-			snd_azf3328_irq_log_unknown_type(which);
-	}
 	if (status & IRQ_GAMEPORT)
 	if (status & IRQ_GAMEPORT)
 		snd_azf3328_gameport_interrupt(chip);
 		snd_azf3328_gameport_interrupt(chip);
+
 	/* MPU401 has less critical IRQ requirements
 	/* MPU401 has less critical IRQ requirements
 	 * than timer and playback/recording, right? */
 	 * than timer and playback/recording, right? */
 	if (status & IRQ_MPU401) {
 	if (status & IRQ_MPU401) {
 		snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data);
 		snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data);
 
 
 		/* hmm, do we have to ack the IRQ here somehow?
 		/* hmm, do we have to ack the IRQ here somehow?
-		 * If so, then I don't know how... */
-		snd_azf3328_dbgplay("azt3328: MPU401 IRQ\n");
+		 * If so, then I don't know how yet... */
+		snd_azf3328_dbgcodec("azt3328: MPU401 IRQ\n");
 	}
 	}
 	return IRQ_HANDLED;
 	return IRQ_HANDLED;
 }
 }
 
 
 /*****************************************************************/
 /*****************************************************************/
 
 
-static const struct snd_pcm_hardware snd_azf3328_playback =
+/* as long as we think we have identical snd_pcm_hardware parameters
+   for playback, capture and i2s out, we can use the same physical struct
+   since the struct is simply being copied into a member.
+*/
+static const struct snd_pcm_hardware snd_azf3328_hardware =
 {
 {
 	/* FIXME!! Correct? */
 	/* FIXME!! Correct? */
 	.info =			SNDRV_PCM_INFO_MMAP |
 	.info =			SNDRV_PCM_INFO_MMAP |
@@ -1718,31 +1751,6 @@ static const struct snd_pcm_hardware snd_azf3328_playback =
 	.fifo_size =		0,
 	.fifo_size =		0,
 };
 };
 
 
-static const struct snd_pcm_hardware snd_azf3328_capture =
-{
-	/* FIXME */
-	.info =			SNDRV_PCM_INFO_MMAP |
-				SNDRV_PCM_INFO_INTERLEAVED |
-				SNDRV_PCM_INFO_MMAP_VALID,
-	.formats =		SNDRV_PCM_FMTBIT_S8 |
-				SNDRV_PCM_FMTBIT_U8 |
-				SNDRV_PCM_FMTBIT_S16_LE |
-				SNDRV_PCM_FMTBIT_U16_LE,
-	.rates =		SNDRV_PCM_RATE_5512 |
-				SNDRV_PCM_RATE_8000_48000 |
-				SNDRV_PCM_RATE_KNOT,
-	.rate_min =		AZF_FREQ_4000,
-	.rate_max =		AZF_FREQ_66200,
-	.channels_min =		1,
-	.channels_max =		2,
-	.buffer_bytes_max =	65536,
-	.period_bytes_min =	64,
-	.period_bytes_max =	65536,
-	.periods_min =		1,
-	.periods_max =		1024,
-	.fifo_size =		0,
-};
-
 
 
 static unsigned int snd_azf3328_fixed_rates[] = {
 static unsigned int snd_azf3328_fixed_rates[] = {
 	AZF_FREQ_4000,
 	AZF_FREQ_4000,
@@ -1770,55 +1778,72 @@ static struct snd_pcm_hw_constraint_list snd_azf3328_hw_constraints_rates = {
 /*****************************************************************/
 /*****************************************************************/
 
 
 static int
 static int
-snd_azf3328_playback_open(struct snd_pcm_substream *substream)
+snd_azf3328_pcm_open(struct snd_pcm_substream *substream,
+		     enum snd_azf3328_codec_type codec_type
+)
 {
 {
 	struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
 	struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct snd_pcm_runtime *runtime = substream->runtime;
 
 
 	snd_azf3328_dbgcallenter();
 	snd_azf3328_dbgcallenter();
-	chip->audio_stream[AZF_PLAYBACK].substream = substream;
-	runtime->hw = snd_azf3328_playback;
+	chip->codecs[codec_type].substream = substream;
+
+	/* same parameters for all our codecs - at least we think so... */
+	runtime->hw = snd_azf3328_hardware;
+
 	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
 	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
 				   &snd_azf3328_hw_constraints_rates);
 				   &snd_azf3328_hw_constraints_rates);
 	snd_azf3328_dbgcallleave();
 	snd_azf3328_dbgcallleave();
 	return 0;
 	return 0;
 }
 }
 
 
+static int
+snd_azf3328_playback_open(struct snd_pcm_substream *substream)
+{
+	return snd_azf3328_pcm_open(substream, AZF_CODEC_PLAYBACK);
+}
+
 static int
 static int
 snd_azf3328_capture_open(struct snd_pcm_substream *substream)
 snd_azf3328_capture_open(struct snd_pcm_substream *substream)
 {
 {
-	struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
-	struct snd_pcm_runtime *runtime = substream->runtime;
+	return snd_azf3328_pcm_open(substream, AZF_CODEC_CAPTURE);
+}
 
 
-	snd_azf3328_dbgcallenter();
-	chip->audio_stream[AZF_CAPTURE].substream = substream;
-	runtime->hw = snd_azf3328_capture;
-	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
-				   &snd_azf3328_hw_constraints_rates);
-	snd_azf3328_dbgcallleave();
-	return 0;
+static int
+snd_azf3328_i2s_out_open(struct snd_pcm_substream *substream)
+{
+	return snd_azf3328_pcm_open(substream, AZF_CODEC_I2S_OUT);
 }
 }
 
 
 static int
 static int
-snd_azf3328_playback_close(struct snd_pcm_substream *substream)
+snd_azf3328_pcm_close(struct snd_pcm_substream *substream,
+		      enum snd_azf3328_codec_type codec_type
+)
 {
 {
 	struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
 	struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
 
 
 	snd_azf3328_dbgcallenter();
 	snd_azf3328_dbgcallenter();
-	chip->audio_stream[AZF_PLAYBACK].substream = NULL;
+	chip->codecs[codec_type].substream = NULL;
 	snd_azf3328_dbgcallleave();
 	snd_azf3328_dbgcallleave();
 	return 0;
 	return 0;
 }
 }
 
 
+static int
+snd_azf3328_playback_close(struct snd_pcm_substream *substream)
+{
+	return snd_azf3328_pcm_close(substream, AZF_CODEC_PLAYBACK);
+}
+
 static int
 static int
 snd_azf3328_capture_close(struct snd_pcm_substream *substream)
 snd_azf3328_capture_close(struct snd_pcm_substream *substream)
 {
 {
-	struct snd_azf3328 *chip = snd_pcm_substream_chip(substream);
+	return snd_azf3328_pcm_close(substream, AZF_CODEC_CAPTURE);
+}
 
 
-	snd_azf3328_dbgcallenter();
-	chip->audio_stream[AZF_CAPTURE].substream = NULL;
-	snd_azf3328_dbgcallleave();
-	return 0;
+static int
+snd_azf3328_i2s_out_close(struct snd_pcm_substream *substream)
+{
+	return snd_azf3328_pcm_close(substream, AZF_CODEC_I2S_OUT);
 }
 }
 
 
 /******************************************************************/
 /******************************************************************/
@@ -1829,9 +1854,9 @@ static struct snd_pcm_ops snd_azf3328_playback_ops = {
 	.ioctl =	snd_pcm_lib_ioctl,
 	.ioctl =	snd_pcm_lib_ioctl,
 	.hw_params =	snd_azf3328_hw_params,
 	.hw_params =	snd_azf3328_hw_params,
 	.hw_free =	snd_azf3328_hw_free,
 	.hw_free =	snd_azf3328_hw_free,
-	.prepare =	snd_azf3328_playback_prepare,
-	.trigger =	snd_azf3328_playback_trigger,
-	.pointer =	snd_azf3328_playback_pointer
+	.prepare =	snd_azf3328_codec_prepare,
+	.trigger =	snd_azf3328_codec_playback_trigger,
+	.pointer =	snd_azf3328_codec_playback_pointer
 };
 };
 
 
 static struct snd_pcm_ops snd_azf3328_capture_ops = {
 static struct snd_pcm_ops snd_azf3328_capture_ops = {
@@ -1840,30 +1865,67 @@ static struct snd_pcm_ops snd_azf3328_capture_ops = {
 	.ioctl =	snd_pcm_lib_ioctl,
 	.ioctl =	snd_pcm_lib_ioctl,
 	.hw_params =	snd_azf3328_hw_params,
 	.hw_params =	snd_azf3328_hw_params,
 	.hw_free =	snd_azf3328_hw_free,
 	.hw_free =	snd_azf3328_hw_free,
-	.prepare =	snd_azf3328_capture_prepare,
-	.trigger =	snd_azf3328_capture_trigger,
-	.pointer =	snd_azf3328_capture_pointer
+	.prepare =	snd_azf3328_codec_prepare,
+	.trigger =	snd_azf3328_codec_capture_trigger,
+	.pointer =	snd_azf3328_codec_capture_pointer
+};
+
+static struct snd_pcm_ops snd_azf3328_i2s_out_ops = {
+	.open =		snd_azf3328_i2s_out_open,
+	.close =	snd_azf3328_i2s_out_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_azf3328_hw_params,
+	.hw_free =	snd_azf3328_hw_free,
+	.prepare =	snd_azf3328_codec_prepare,
+	.trigger =	snd_azf3328_codec_i2s_out_trigger,
+	.pointer =	snd_azf3328_codec_i2s_out_pointer
 };
 };
 
 
 static int __devinit
 static int __devinit
-snd_azf3328_pcm(struct snd_azf3328 *chip, int device)
+snd_azf3328_pcm(struct snd_azf3328 *chip)
 {
 {
+enum { AZF_PCMDEV_STD, AZF_PCMDEV_I2S_OUT, NUM_AZF_PCMDEVS }; /* pcm devices */
+
 	struct snd_pcm *pcm;
 	struct snd_pcm *pcm;
 	int err;
 	int err;
 
 
 	snd_azf3328_dbgcallenter();
 	snd_azf3328_dbgcallenter();
-	if ((err = snd_pcm_new(chip->card, "AZF3328 DSP", device, 1, 1, &pcm)) < 0)
+
+	err = snd_pcm_new(chip->card, "AZF3328 DSP", AZF_PCMDEV_STD,
+								1, 1, &pcm);
+	if (err < 0)
 		return err;
 		return err;
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_azf3328_playback_ops);
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_azf3328_capture_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+						&snd_azf3328_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+						&snd_azf3328_capture_ops);
 
 
 	pcm->private_data = chip;
 	pcm->private_data = chip;
 	pcm->info_flags = 0;
 	pcm->info_flags = 0;
 	strcpy(pcm->name, chip->card->shortname);
 	strcpy(pcm->name, chip->card->shortname);
-	chip->pcm = pcm;
+	/* same pcm object for playback/capture (see snd_pcm_new() above) */
+	chip->pcm[AZF_CODEC_PLAYBACK] = pcm;
+	chip->pcm[AZF_CODEC_CAPTURE] = pcm;
 
 
 	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
 	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
-					      snd_dma_pci_data(chip->pci), 64*1024, 64*1024);
+						snd_dma_pci_data(chip->pci),
+							64*1024, 64*1024);
+
+	err = snd_pcm_new(chip->card, "AZF3328 I2S OUT", AZF_PCMDEV_I2S_OUT,
+								1, 0, &pcm);
+	if (err < 0)
+		return err;
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+						&snd_azf3328_i2s_out_ops);
+
+	pcm->private_data = chip;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, chip->card->shortname);
+	chip->pcm[AZF_CODEC_I2S_OUT] = pcm;
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+						snd_dma_pci_data(chip->pci),
+							64*1024, 64*1024);
 
 
 	snd_azf3328_dbgcallleave();
 	snd_azf3328_dbgcallleave();
 	return 0;
 	return 0;
@@ -1902,7 +1964,7 @@ snd_azf3328_timer_start(struct snd_timer *timer)
 	snd_azf3328_dbgtimer("setting timer countdown value %d, add COUNTDOWN|IRQ\n", delay);
 	snd_azf3328_dbgtimer("setting timer countdown value %d, add COUNTDOWN|IRQ\n", delay);
 	delay |= TIMER_COUNTDOWN_ENABLE | TIMER_IRQ_ENABLE;
 	delay |= TIMER_COUNTDOWN_ENABLE | TIMER_IRQ_ENABLE;
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	spin_lock_irqsave(&chip->reg_lock, flags);
-	snd_azf3328_codec_outl(chip, IDX_IO_TIMER_VALUE, delay);
+	snd_azf3328_ctrl_outl(chip, IDX_IO_TIMER_VALUE, delay);
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
 	snd_azf3328_dbgcallleave();
 	snd_azf3328_dbgcallleave();
 	return 0;
 	return 0;
@@ -1919,7 +1981,7 @@ snd_azf3328_timer_stop(struct snd_timer *timer)
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	/* disable timer countdown and interrupt */
 	/* disable timer countdown and interrupt */
 	/* FIXME: should we write TIMER_IRQ_ACK here? */
 	/* FIXME: should we write TIMER_IRQ_ACK here? */
-	snd_azf3328_codec_outb(chip, IDX_IO_TIMER_VALUE + 3, 0);
+	snd_azf3328_ctrl_outb(chip, IDX_IO_TIMER_VALUE + 3, 0);
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
 	snd_azf3328_dbgcallleave();
 	snd_azf3328_dbgcallleave();
 	return 0;
 	return 0;
@@ -2035,7 +2097,7 @@ snd_azf3328_test_bit(unsigned unsigned reg, int bit)
 
 
 	outb(val, reg);
 	outb(val, reg);
 
 
-	printk(KERN_ERR "reg %04x bit %d: %02x %02x %02x\n",
+	printk(KERN_DEBUG "reg %04x bit %d: %02x %02x %02x\n",
 				reg, bit, val, valoff, valon
 				reg, bit, val, valoff, valon
 	);
 	);
 }
 }
@@ -2048,9 +2110,9 @@ snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip)
 	u16 tmp;
 	u16 tmp;
 
 
 	snd_azf3328_dbgmisc(
 	snd_azf3328_dbgmisc(
-		"codec_io 0x%lx, game_io 0x%lx, mpu_io 0x%lx, "
+		"ctrl_io 0x%lx, game_io 0x%lx, mpu_io 0x%lx, "
 		"opl3_io 0x%lx, mixer_io 0x%lx, irq %d\n",
 		"opl3_io 0x%lx, mixer_io 0x%lx, irq %d\n",
-		chip->codec_io, chip->game_io, chip->mpu_io,
+		chip->ctrl_io, chip->game_io, chip->mpu_io,
 		chip->opl3_io, chip->mixer_io, chip->irq
 		chip->opl3_io, chip->mixer_io, chip->irq
 	);
 	);
 
 
@@ -2083,9 +2145,9 @@ snd_azf3328_debug_show_ports(const struct snd_azf3328 *chip)
 				inb(0x38c + tmp)
 				inb(0x38c + tmp)
 		);
 		);
 
 
-	for (tmp = 0; tmp < AZF_IO_SIZE_CODEC; tmp += 2)
-		snd_azf3328_dbgmisc("codec 0x%02x: 0x%04x\n",
-			tmp, snd_azf3328_codec_inw(chip, tmp)
+	for (tmp = 0; tmp < AZF_IO_SIZE_CTRL; tmp += 2)
+		snd_azf3328_dbgmisc("ctrl 0x%02x: 0x%04x\n",
+			tmp, snd_azf3328_ctrl_inw(chip, tmp)
 		);
 		);
 
 
 	for (tmp = 0; tmp < AZF_IO_SIZE_MIXER; tmp += 2)
 	for (tmp = 0; tmp < AZF_IO_SIZE_MIXER; tmp += 2)
@@ -2106,7 +2168,8 @@ snd_azf3328_create(struct snd_card *card,
 	static struct snd_device_ops ops = {
 	static struct snd_device_ops ops = {
 		.dev_free =     snd_azf3328_dev_free,
 		.dev_free =     snd_azf3328_dev_free,
 	};
 	};
-	u16 tmp;
+	u8 dma_init;
+	enum snd_azf3328_codec_type codec_type;
 
 
 	*rchip = NULL;
 	*rchip = NULL;
 
 
@@ -2138,14 +2201,21 @@ snd_azf3328_create(struct snd_card *card,
 	if (err < 0)
 	if (err < 0)
 		goto out_err;
 		goto out_err;
 
 
-	chip->codec_io = pci_resource_start(pci, 0);
+	chip->ctrl_io  = pci_resource_start(pci, 0);
 	chip->game_io  = pci_resource_start(pci, 1);
 	chip->game_io  = pci_resource_start(pci, 1);
 	chip->mpu_io   = pci_resource_start(pci, 2);
 	chip->mpu_io   = pci_resource_start(pci, 2);
-	chip->opl3_io = pci_resource_start(pci, 3);
+	chip->opl3_io  = pci_resource_start(pci, 3);
 	chip->mixer_io = pci_resource_start(pci, 4);
 	chip->mixer_io = pci_resource_start(pci, 4);
 
 
-	chip->audio_stream[AZF_PLAYBACK].portbase = chip->codec_io + 0x00;
-	chip->audio_stream[AZF_CAPTURE].portbase   = chip->codec_io + 0x20;
+	chip->codecs[AZF_CODEC_PLAYBACK].io_base =
+				chip->ctrl_io + AZF_IO_OFFS_CODEC_PLAYBACK;
+	chip->codecs[AZF_CODEC_PLAYBACK].name = "PLAYBACK";
+	chip->codecs[AZF_CODEC_CAPTURE].io_base =
+				chip->ctrl_io + AZF_IO_OFFS_CODEC_CAPTURE;
+	chip->codecs[AZF_CODEC_CAPTURE].name = "CAPTURE";
+	chip->codecs[AZF_CODEC_I2S_OUT].io_base =
+				chip->ctrl_io + AZF_IO_OFFS_CODEC_I2S_OUT;
+	chip->codecs[AZF_CODEC_I2S_OUT].name = "I2S_OUT";
 
 
 	if (request_irq(pci->irq, snd_azf3328_interrupt,
 	if (request_irq(pci->irq, snd_azf3328_interrupt,
 			IRQF_SHARED, card->shortname, chip)) {
 			IRQF_SHARED, card->shortname, chip)) {
@@ -2168,20 +2238,25 @@ snd_azf3328_create(struct snd_card *card,
 	if (err < 0)
 	if (err < 0)
 		goto out_err;
 		goto out_err;
 
 
-	/* shutdown codecs to save power */
-		/* have snd_azf3328_codec_activity() act properly */
-	chip->audio_stream[AZF_PLAYBACK].running = 1;
-	snd_azf3328_codec_activity(chip, AZF_PLAYBACK, 0);
+	/* standard codec init stuff */
+		/* default DMA init value */
+	dma_init = DMA_RUN_SOMETHING2|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE;
 
 
-	/* standard chip init stuff */
-		/* default IRQ init value */
-	tmp = DMA_PLAY_SOMETHING2|DMA_EPILOGUE_SOMETHING|DMA_SOMETHING_ELSE;
+	for (codec_type = AZF_CODEC_PLAYBACK;
+		codec_type <= AZF_CODEC_I2S_OUT; ++codec_type) {
+		struct snd_azf3328_codec_data *codec =
+			 &chip->codecs[codec_type];
 
 
-	spin_lock_irq(&chip->reg_lock);
-	snd_azf3328_codec_outb(chip, IDX_IO_PLAY_FLAGS, tmp);
-	snd_azf3328_codec_outb(chip, IDX_IO_REC_FLAGS, tmp);
-	snd_azf3328_codec_outb(chip, IDX_IO_SOMETHING_FLAGS, tmp);
-	spin_unlock_irq(&chip->reg_lock);
+		/* shutdown codecs to save power */
+			/* have ...ctrl_codec_activity() act properly */
+		codec->running = 1;
+		snd_azf3328_ctrl_codec_activity(chip, codec_type, 0);
+
+		spin_lock_irq(&chip->reg_lock);
+		snd_azf3328_codec_outb(codec, IDX_IO_CODEC_DMA_FLAGS,
+						 dma_init);
+		spin_unlock_irq(&chip->reg_lock);
+	}
 
 
 	snd_card_set_dev(card, &pci->dev);
 	snd_card_set_dev(card, &pci->dev);
 
 
@@ -2229,8 +2304,11 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
 
 
 	card->private_data = chip;
 	card->private_data = chip;
 
 
+	/* chose to use MPU401_HW_AZT2320 ID instead of MPU401_HW_MPU401,
+	   since our hardware ought to be similar, thus use same ID. */
 	err = snd_mpu401_uart_new(
 	err = snd_mpu401_uart_new(
-		card, 0, MPU401_HW_MPU401, chip->mpu_io, MPU401_INFO_INTEGRATED,
+		card, 0,
+		MPU401_HW_AZT2320, chip->mpu_io, MPU401_INFO_INTEGRATED,
 		pci->irq, 0, &chip->rmidi
 		pci->irq, 0, &chip->rmidi
 	);
 	);
 	if (err < 0) {
 	if (err < 0) {
@@ -2244,7 +2322,7 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
 	if (err < 0)
 	if (err < 0)
 		goto out_err;
 		goto out_err;
 
 
-	err = snd_azf3328_pcm(chip, 0);
+	err = snd_azf3328_pcm(chip);
 	if (err < 0)
 	if (err < 0)
 		goto out_err;
 		goto out_err;
 
 
@@ -2266,14 +2344,14 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
 	opl3->private_data = chip;
 	opl3->private_data = chip;
 
 
 	sprintf(card->longname, "%s at 0x%lx, irq %i",
 	sprintf(card->longname, "%s at 0x%lx, irq %i",
-		card->shortname, chip->codec_io, chip->irq);
+		card->shortname, chip->ctrl_io, chip->irq);
 
 
 	err = snd_card_register(card);
 	err = snd_card_register(card);
 	if (err < 0)
 	if (err < 0)
 		goto out_err;
 		goto out_err;
 
 
 #ifdef MODULE
 #ifdef MODULE
-	printk(
+	printk(KERN_INFO
 "azt3328: Sound driver for Aztech AZF3328-based soundcards such as PCI168.\n"
 "azt3328: Sound driver for Aztech AZF3328-based soundcards such as PCI168.\n"
 "azt3328: Hardware was completely undocumented, unfortunately.\n"
 "azt3328: Hardware was completely undocumented, unfortunately.\n"
 "azt3328: Feel free to contact andi AT lisas.de for bug reports etc.!\n"
 "azt3328: Feel free to contact andi AT lisas.de for bug reports etc.!\n"
@@ -2308,36 +2386,52 @@ snd_azf3328_remove(struct pci_dev *pci)
 }
 }
 
 
 #ifdef CONFIG_PM
 #ifdef CONFIG_PM
+static inline void
+snd_azf3328_suspend_regs(unsigned long io_addr, unsigned count, u32 *saved_regs)
+{
+	unsigned reg;
+
+	for (reg = 0; reg < count; ++reg) {
+		*saved_regs = inl(io_addr);
+		snd_azf3328_dbgpm("suspend: io 0x%04lx: 0x%08x\n",
+			io_addr, *saved_regs);
+		++saved_regs;
+		io_addr += sizeof(*saved_regs);
+	}
+}
+
 static int
 static int
 snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state)
 snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state)
 {
 {
 	struct snd_card *card = pci_get_drvdata(pci);
 	struct snd_card *card = pci_get_drvdata(pci);
 	struct snd_azf3328 *chip = card->private_data;
 	struct snd_azf3328 *chip = card->private_data;
-	unsigned reg;
+	u16 *saved_regs_ctrl_u16;
 
 
 	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
 	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
 
 
-	snd_pcm_suspend_all(chip->pcm);
+	snd_pcm_suspend_all(chip->pcm[AZF_CODEC_PLAYBACK]);
+	snd_pcm_suspend_all(chip->pcm[AZF_CODEC_I2S_OUT]);
 
 
-	for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; ++reg)
-		chip->saved_regs_mixer[reg] = inw(chip->mixer_io + reg * 2);
+	snd_azf3328_suspend_regs(chip->mixer_io,
+		ARRAY_SIZE(chip->saved_regs_mixer), chip->saved_regs_mixer);
 
 
 	/* make sure to disable master volume etc. to prevent looping sound */
 	/* make sure to disable master volume etc. to prevent looping sound */
 	snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1);
 	snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1);
 	snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
 	snd_azf3328_mixer_set_mute(chip, IDX_MIXER_WAVEOUT, 1);
 
 
-	for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; ++reg)
-		chip->saved_regs_codec[reg] = inw(chip->codec_io + reg * 2);
+	snd_azf3328_suspend_regs(chip->ctrl_io,
+		ARRAY_SIZE(chip->saved_regs_ctrl), chip->saved_regs_ctrl);
 
 
 	/* manually store the one currently relevant write-only reg, too */
 	/* manually store the one currently relevant write-only reg, too */
-	chip->saved_regs_codec[IDX_IO_6AH / 2] = chip->shadow_reg_codec_6AH;
+	saved_regs_ctrl_u16 = (u16 *)chip->saved_regs_ctrl;
+	saved_regs_ctrl_u16[IDX_IO_6AH / 2] = chip->shadow_reg_ctrl_6AH;
 
 
-	for (reg = 0; reg < AZF_IO_SIZE_GAME_PM / 2; ++reg)
-		chip->saved_regs_game[reg] = inw(chip->game_io + reg * 2);
-	for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; ++reg)
-		chip->saved_regs_mpu[reg] = inw(chip->mpu_io + reg * 2);
-	for (reg = 0; reg < AZF_IO_SIZE_OPL3_PM / 2; ++reg)
-		chip->saved_regs_opl3[reg] = inw(chip->opl3_io + reg * 2);
+	snd_azf3328_suspend_regs(chip->game_io,
+		ARRAY_SIZE(chip->saved_regs_game), chip->saved_regs_game);
+	snd_azf3328_suspend_regs(chip->mpu_io,
+		ARRAY_SIZE(chip->saved_regs_mpu), chip->saved_regs_mpu);
+	snd_azf3328_suspend_regs(chip->opl3_io,
+		ARRAY_SIZE(chip->saved_regs_opl3), chip->saved_regs_opl3);
 
 
 	pci_disable_device(pci);
 	pci_disable_device(pci);
 	pci_save_state(pci);
 	pci_save_state(pci);
@@ -2345,12 +2439,28 @@ snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state)
 	return 0;
 	return 0;
 }
 }
 
 
+static inline void
+snd_azf3328_resume_regs(const u32 *saved_regs,
+			unsigned long io_addr,
+			unsigned count
+)
+{
+	unsigned reg;
+
+	for (reg = 0; reg < count; ++reg) {
+		outl(*saved_regs, io_addr);
+		snd_azf3328_dbgpm("resume: io 0x%04lx: 0x%08x --> 0x%08x\n",
+			io_addr, *saved_regs, inl(io_addr));
+		++saved_regs;
+		io_addr += sizeof(*saved_regs);
+	}
+}
+
 static int
 static int
 snd_azf3328_resume(struct pci_dev *pci)
 snd_azf3328_resume(struct pci_dev *pci)
 {
 {
 	struct snd_card *card = pci_get_drvdata(pci);
 	struct snd_card *card = pci_get_drvdata(pci);
-	struct snd_azf3328 *chip = card->private_data;
-	unsigned reg;
+	const struct snd_azf3328 *chip = card->private_data;
 
 
 	pci_set_power_state(pci, PCI_D0);
 	pci_set_power_state(pci, PCI_D0);
 	pci_restore_state(pci);
 	pci_restore_state(pci);
@@ -2362,16 +2472,24 @@ snd_azf3328_resume(struct pci_dev *pci)
 	}
 	}
 	pci_set_master(pci);
 	pci_set_master(pci);
 
 
-	for (reg = 0; reg < AZF_IO_SIZE_GAME_PM / 2; ++reg)
-		outw(chip->saved_regs_game[reg], chip->game_io + reg * 2);
-	for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; ++reg)
-		outw(chip->saved_regs_mpu[reg], chip->mpu_io + reg * 2);
-	for (reg = 0; reg < AZF_IO_SIZE_OPL3_PM / 2; ++reg)
-		outw(chip->saved_regs_opl3[reg], chip->opl3_io + reg * 2);
-	for (reg = 0; reg < AZF_IO_SIZE_MIXER_PM / 2; ++reg)
-		outw(chip->saved_regs_mixer[reg], chip->mixer_io + reg * 2);
-	for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; ++reg)
-		outw(chip->saved_regs_codec[reg], chip->codec_io + reg * 2);
+	snd_azf3328_resume_regs(chip->saved_regs_game, chip->game_io,
+					ARRAY_SIZE(chip->saved_regs_game));
+	snd_azf3328_resume_regs(chip->saved_regs_mpu, chip->mpu_io,
+					ARRAY_SIZE(chip->saved_regs_mpu));
+	snd_azf3328_resume_regs(chip->saved_regs_opl3, chip->opl3_io,
+					ARRAY_SIZE(chip->saved_regs_opl3));
+
+	snd_azf3328_resume_regs(chip->saved_regs_mixer, chip->mixer_io,
+					ARRAY_SIZE(chip->saved_regs_mixer));
+
+	/* unfortunately with 32bit transfers, IDX_MIXER_PLAY_MASTER (0x02)
+	   and IDX_MIXER_RESET (offset 0x00) get touched at the same time,
+	   resulting in a mixer reset condition persisting until _after_
+	   master vol was restored. Thus master vol needs an extra restore. */
+	outw(((u16 *)chip->saved_regs_mixer)[1], chip->mixer_io + 2);
+
+	snd_azf3328_resume_regs(chip->saved_regs_ctrl, chip->ctrl_io,
+					ARRAY_SIZE(chip->saved_regs_ctrl));
 
 
 	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
 	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
 	return 0;
 	return 0;

+ 51 - 52
sound/pci/azt3328.h

@@ -6,50 +6,59 @@
 
 
 /*** main I/O area port indices ***/
 /*** main I/O area port indices ***/
 /* (only 0x70 of 0x80 bytes saved/restored by Windows driver) */
 /* (only 0x70 of 0x80 bytes saved/restored by Windows driver) */
-#define AZF_IO_SIZE_CODEC	0x80
-#define AZF_IO_SIZE_CODEC_PM	0x70
+#define AZF_IO_SIZE_CTRL	0x80
+#define AZF_IO_SIZE_CTRL_PM	0x70
 
 
-/* the driver initialisation suggests a layout of 4 main areas:
- * from 0x00 (playback), from 0x20 (recording) and from 0x40 (maybe MPU401??).
+/* the driver initialisation suggests a layout of 4 areas
+ * within the main card control I/O:
+ * from 0x00 (playback codec), from 0x20 (recording codec)
+ * and from 0x40 (most certainly I2S out codec).
  * And another area from 0x60 to 0x6f (DirectX timer, IRQ management,
  * And another area from 0x60 to 0x6f (DirectX timer, IRQ management,
  * power management etc.???). */
  * power management etc.???). */
 
 
-/** playback area **/
-#define IDX_IO_PLAY_FLAGS       0x00 /* PU:0x0000 */
+#define AZF_IO_OFFS_CODEC_PLAYBACK	0x00
+#define AZF_IO_OFFS_CODEC_CAPTURE	0x20
+#define AZF_IO_OFFS_CODEC_I2S_OUT	0x40
+
+#define IDX_IO_CODEC_DMA_FLAGS       0x00 /* PU:0x0000 */
      /* able to reactivate output after output muting due to 8/16bit
      /* able to reactivate output after output muting due to 8/16bit
       * output change, just like 0x0002.
       * output change, just like 0x0002.
       * 0x0001 is the only bit that's able to start the DMA counter */
       * 0x0001 is the only bit that's able to start the DMA counter */
-  #define DMA_RESUME			0x0001 /* paused if cleared ? */
+  #define DMA_RESUME			0x0001 /* paused if cleared? */
      /* 0x0002 *temporarily* set during DMA stopping. hmm
      /* 0x0002 *temporarily* set during DMA stopping. hmm
       * both 0x0002 and 0x0004 set in playback setup. */
       * both 0x0002 and 0x0004 set in playback setup. */
      /* able to reactivate output after output muting due to 8/16bit
      /* able to reactivate output after output muting due to 8/16bit
       * output change, just like 0x0001. */
       * output change, just like 0x0001. */
-  #define DMA_PLAY_SOMETHING1		0x0002 /* \ alternated (toggled) */
+  #define DMA_RUN_SOMETHING1		0x0002 /* \ alternated (toggled) */
      /* 0x0004: NOT able to reactivate output */
      /* 0x0004: NOT able to reactivate output */
-  #define DMA_PLAY_SOMETHING2		0x0004 /* / bits */
+  #define DMA_RUN_SOMETHING2		0x0004 /* / bits */
   #define SOMETHING_ALMOST_ALWAYS_SET	0x0008 /* ???; can be modified */
   #define SOMETHING_ALMOST_ALWAYS_SET	0x0008 /* ???; can be modified */
   #define DMA_EPILOGUE_SOMETHING	0x0010
   #define DMA_EPILOGUE_SOMETHING	0x0010
   #define DMA_SOMETHING_ELSE		0x0020 /* ??? */
   #define DMA_SOMETHING_ELSE		0x0020 /* ??? */
-  #define SOMETHING_UNMODIFIABLE	0xffc0 /* unused ? not modifiable */
-#define IDX_IO_PLAY_IRQTYPE     0x02 /* PU:0x0001 */
+  #define SOMETHING_UNMODIFIABLE	0xffc0 /* unused? not modifiable */
+#define IDX_IO_CODEC_IRQTYPE     0x02 /* PU:0x0001 */
   /* write back to flags in case flags are set, in order to ACK IRQ in handler
   /* write back to flags in case flags are set, in order to ACK IRQ in handler
    * (bit 1 of port 0x64 indicates interrupt for one of these three types)
    * (bit 1 of port 0x64 indicates interrupt for one of these three types)
    * sometimes in this case it just writes 0xffff to globally ACK all IRQs
    * sometimes in this case it just writes 0xffff to globally ACK all IRQs
    * settings written are not reflected when reading back, though.
    * settings written are not reflected when reading back, though.
-   * seems to be IRQ, too (frequently used: port |= 0x07 !), but who knows ? */
-  #define IRQ_PLAY_SOMETHING		0x0001 /* something & ACK */
-  #define IRQ_FINISHED_PLAYBUF_1	0x0002 /* 1st dmabuf finished & ACK */
-  #define IRQ_FINISHED_PLAYBUF_2	0x0004 /* 2nd dmabuf finished & ACK */
+   * seems to be IRQ, too (frequently used: port |= 0x07 !), but who knows? */
+  #define IRQ_SOMETHING			0x0001 /* something & ACK */
+  #define IRQ_FINISHED_DMABUF_1		0x0002 /* 1st dmabuf finished & ACK */
+  #define IRQ_FINISHED_DMABUF_2		0x0004 /* 2nd dmabuf finished & ACK */
   #define IRQMASK_SOME_STATUS_1		0x0008 /* \ related bits */
   #define IRQMASK_SOME_STATUS_1		0x0008 /* \ related bits */
   #define IRQMASK_SOME_STATUS_2		0x0010 /* / (checked together in loop) */
   #define IRQMASK_SOME_STATUS_2		0x0010 /* / (checked together in loop) */
-  #define IRQMASK_UNMODIFIABLE		0xffe0 /* unused ? not modifiable */
-#define IDX_IO_PLAY_DMA_START_1 0x04 /* start address of 1st DMA play area, PU:0x00000000 */
-#define IDX_IO_PLAY_DMA_START_2 0x08 /* start address of 2nd DMA play area, PU:0x00000000 */
-#define IDX_IO_PLAY_DMA_LEN_1   0x0c /* length of 1st DMA play area, PU:0x0000 */
-#define IDX_IO_PLAY_DMA_LEN_2   0x0e /* length of 2nd DMA play area, PU:0x0000 */
-#define IDX_IO_PLAY_DMA_CURRPOS 0x10 /* current DMA position, PU:0x00000000 */
-#define IDX_IO_PLAY_DMA_CURROFS	0x14 /* offset within current DMA play area, PU:0x0000 */
-#define IDX_IO_PLAY_SOUNDFORMAT 0x16 /* PU:0x0010 */
+  #define IRQMASK_UNMODIFIABLE		0xffe0 /* unused? not modifiable */
+  /* start address of 1st DMA transfer area, PU:0x00000000 */
+#define IDX_IO_CODEC_DMA_START_1 0x04
+  /* start address of 2nd DMA transfer area, PU:0x00000000 */
+#define IDX_IO_CODEC_DMA_START_2 0x08
+  /* both lengths of DMA transfer areas, PU:0x00000000
+     length1: offset 0x0c, length2: offset 0x0e */
+#define IDX_IO_CODEC_DMA_LENGTHS 0x0c
+#define IDX_IO_CODEC_DMA_CURRPOS 0x10 /* current DMA position, PU:0x00000000 */
+  /* offset within current DMA transfer area, PU:0x0000 */
+#define IDX_IO_CODEC_DMA_CURROFS 0x14
+#define IDX_IO_CODEC_SOUNDFORMAT 0x16 /* PU:0x0010 */
   /* all unspecified bits can't be modified */
   /* all unspecified bits can't be modified */
   #define SOUNDFORMAT_FREQUENCY_MASK	0x000f
   #define SOUNDFORMAT_FREQUENCY_MASK	0x000f
   #define SOUNDFORMAT_XTAL1		0x00
   #define SOUNDFORMAT_XTAL1		0x00
@@ -76,6 +85,7 @@
   #define SOUNDFORMAT_FLAG_16BIT	0x0010
   #define SOUNDFORMAT_FLAG_16BIT	0x0010
   #define SOUNDFORMAT_FLAG_2CHANNELS	0x0020
   #define SOUNDFORMAT_FLAG_2CHANNELS	0x0020
 
 
+
 /* define frequency helpers, for maximum value safety */
 /* define frequency helpers, for maximum value safety */
 enum azf_freq_t {
 enum azf_freq_t {
 #define AZF_FREQ(rate) AZF_FREQ_##rate = rate
 #define AZF_FREQ(rate) AZF_FREQ_##rate = rate
@@ -96,29 +106,6 @@ enum azf_freq_t {
 #undef AZF_FREQ
 #undef AZF_FREQ
 };
 };
 
 
-/** recording area (see also: playback bit flag definitions) **/
-#define IDX_IO_REC_FLAGS	0x20 /* ??, PU:0x0000 */
-#define IDX_IO_REC_IRQTYPE	0x22 /* ??, PU:0x0000 */
-  #define IRQ_REC_SOMETHING		0x0001 /* something & ACK */
-  #define IRQ_FINISHED_RECBUF_1		0x0002 /* 1st dmabuf finished & ACK */
-  #define IRQ_FINISHED_RECBUF_2		0x0004 /* 2nd dmabuf finished & ACK */
-  /* hmm, maybe these are just the corresponding *recording* flags ?
-   * but OTOH they are most likely at port 0x22 instead */
-  #define IRQMASK_SOME_STATUS_1		0x0008 /* \ related bits */
-  #define IRQMASK_SOME_STATUS_2		0x0010 /* / (checked together in loop) */
-#define IDX_IO_REC_DMA_START_1  0x24 /* PU:0x00000000 */
-#define IDX_IO_REC_DMA_START_2  0x28 /* PU:0x00000000 */
-#define IDX_IO_REC_DMA_LEN_1    0x2c /* PU:0x0000 */
-#define IDX_IO_REC_DMA_LEN_2    0x2e /* PU:0x0000 */
-#define IDX_IO_REC_DMA_CURRPOS  0x30 /* PU:0x00000000 */
-#define IDX_IO_REC_DMA_CURROFS  0x34 /* PU:0x00000000 */
-#define IDX_IO_REC_SOUNDFORMAT  0x36 /* PU:0x0000 */
-
-/** hmm, what is this I/O area for? MPU401?? or external DAC via I2S?? (after playback, recording, ???, timer) **/
-#define IDX_IO_SOMETHING_FLAGS	0x40 /* gets set to 0x34 just like port 0x0 and 0x20 on card init, PU:0x0000 */
-/* general */
-#define IDX_IO_42H		0x42 /* PU:0x0001 */
-
 /** DirectX timer, main interrupt area (FIXME: and something else?) **/ 
 /** DirectX timer, main interrupt area (FIXME: and something else?) **/ 
 #define IDX_IO_TIMER_VALUE	0x60 /* found this timer area by pure luck :-) */
 #define IDX_IO_TIMER_VALUE	0x60 /* found this timer area by pure luck :-) */
   /* timer countdown value; triggers IRQ when timer is finished */
   /* timer countdown value; triggers IRQ when timer is finished */
@@ -133,17 +120,19 @@ enum azf_freq_t {
 #define IDX_IO_IRQSTATUS        0x64
 #define IDX_IO_IRQSTATUS        0x64
   /* some IRQ bit in here might also be used to signal a power-management timer
   /* some IRQ bit in here might also be used to signal a power-management timer
    * timeout, to request shutdown of the chip (e.g. AD1815JS has such a thing).
    * timeout, to request shutdown of the chip (e.g. AD1815JS has such a thing).
-   * Some OPL3 hardware (e.g. in LM4560) has some special timer hardware which
-   * can trigger an OPL3 timer IRQ, so maybe there's such a thing as well... */
+   * OPL3 hardware contains several timers which confusingly in most cases
+   * are NOT routed to an IRQ, but some designs (e.g. LM4560) DO support that,
+   * so I wouldn't be surprised at all to discover that AZF3328
+   * supports that thing as well... */
 
 
   #define IRQ_PLAYBACK	0x0001
   #define IRQ_PLAYBACK	0x0001
   #define IRQ_RECORDING	0x0002
   #define IRQ_RECORDING	0x0002
-  #define IRQ_UNKNOWN1	0x0004 /* most probably I2S port */
+  #define IRQ_I2S_OUT	0x0004 /* this IS I2S, right!? (untested) */
   #define IRQ_GAMEPORT	0x0008 /* Interrupt of Digital(ly) Enhanced Game Port */
   #define IRQ_GAMEPORT	0x0008 /* Interrupt of Digital(ly) Enhanced Game Port */
   #define IRQ_MPU401	0x0010
   #define IRQ_MPU401	0x0010
   #define IRQ_TIMER	0x0020 /* DirectX timer */
   #define IRQ_TIMER	0x0020 /* DirectX timer */
-  #define IRQ_UNKNOWN2	0x0040 /* probably unused, or possibly I2S port? */
-  #define IRQ_UNKNOWN3	0x0080 /* probably unused, or possibly I2S port? */
+  #define IRQ_UNKNOWN2	0x0040 /* probably unused, or possibly OPL3 timer? */
+  #define IRQ_UNKNOWN3	0x0080 /* probably unused, or possibly OPL3 timer? */
 #define IDX_IO_66H		0x66    /* writing 0xffff returns 0x0000 */
 #define IDX_IO_66H		0x66    /* writing 0xffff returns 0x0000 */
   /* this is set to e.g. 0x3ff or 0x300, and writable;
   /* this is set to e.g. 0x3ff or 0x300, and writable;
    * maybe some buffer limit, but I couldn't find out more, PU:0x00ff: */
    * maybe some buffer limit, but I couldn't find out more, PU:0x00ff: */
@@ -206,7 +195,7 @@ enum azf_freq_t {
 /*** Gameport area port indices ***/
 /*** Gameport area port indices ***/
 /* (only 0x06 of 0x08 bytes saved/restored by Windows driver) */ 
 /* (only 0x06 of 0x08 bytes saved/restored by Windows driver) */ 
 #define AZF_IO_SIZE_GAME		0x08
 #define AZF_IO_SIZE_GAME		0x08
-#define AZF_IO_SIZE_GAME_PM	0x06
+#define AZF_IO_SIZE_GAME_PM		0x06
 
 
 enum {
 enum {
 	AZF_GAME_LEGACY_IO_PORT = 0x200
 	AZF_GAME_LEGACY_IO_PORT = 0x200
@@ -272,6 +261,12 @@ enum {
    * 11 --> 1/200: */
    * 11 --> 1/200: */
   #define GAME_HWCFG_ADC_COUNTER_FREQ_MASK	0x06
   #define GAME_HWCFG_ADC_COUNTER_FREQ_MASK	0x06
 
 
+  /* FIXME: these values might be reversed... */
+  #define GAME_HWCFG_ADC_COUNTER_FREQ_STD	0
+  #define GAME_HWCFG_ADC_COUNTER_FREQ_1_2	1
+  #define GAME_HWCFG_ADC_COUNTER_FREQ_1_20	2
+  #define GAME_HWCFG_ADC_COUNTER_FREQ_1_200	3
+
   /* enable gameport legacy I/O address (0x200)
   /* enable gameport legacy I/O address (0x200)
    * I was unable to locate any configurability for a different address: */
    * I was unable to locate any configurability for a different address: */
   #define GAME_HWCFG_LEGACY_ADDRESS_ENABLE	0x08
   #define GAME_HWCFG_LEGACY_ADDRESS_ENABLE	0x08
@@ -281,6 +276,7 @@ enum {
 #define AZF_IO_SIZE_MPU_PM	0x04
 #define AZF_IO_SIZE_MPU_PM	0x04
 
 
 /*** OPL3 synth ***/
 /*** OPL3 synth ***/
+/* (only 0x06 of 0x08 bytes saved/restored by Windows driver) */
 #define AZF_IO_SIZE_OPL3	0x08
 #define AZF_IO_SIZE_OPL3	0x08
 #define AZF_IO_SIZE_OPL3_PM	0x06
 #define AZF_IO_SIZE_OPL3_PM	0x06
 /* hmm, given that a standard OPL3 has 4 registers only,
 /* hmm, given that a standard OPL3 has 4 registers only,
@@ -340,4 +336,7 @@ enum {
 #define SET_CHAN_LEFT	1
 #define SET_CHAN_LEFT	1
 #define SET_CHAN_RIGHT	2
 #define SET_CHAN_RIGHT	2
 
 
+/* helper macro to align I/O port ranges to 32bit I/O width */
+#define AZF_ALIGN(x) (((x) + 3) & (~3))
+
 #endif /* __SOUND_AZT3328_H  */
 #endif /* __SOUND_AZT3328_H  */

+ 1 - 1
sound/pci/cs46xx/cs46xx_lib.h

@@ -35,7 +35,7 @@
 
 
 
 
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
-#define CS46XX_MIN_PERIOD_SIZE 1
+#define CS46XX_MIN_PERIOD_SIZE 64
 #define CS46XX_MAX_PERIOD_SIZE 1024*1024
 #define CS46XX_MAX_PERIOD_SIZE 1024*1024
 #else
 #else
 #define CS46XX_MIN_PERIOD_SIZE 2048
 #define CS46XX_MIN_PERIOD_SIZE 2048

+ 6 - 3
sound/pci/ctxfi/ct20k2reg.h

@@ -11,9 +11,12 @@
 
 
 
 
 /* Timer Registers */
 /* Timer Registers */
-#define TIMER_TIMR          0x1B7004
-#define INTERRUPT_GIP       0x1B7010
-#define INTERRUPT_GIE       0x1B7014
+#define WC		0x1b7000
+#define TIMR		0x1b7004
+# define	TIMR_IE		(1<<15)
+# define	TIMR_IP		(1<<14)
+#define GIP		0x1b7010
+#define GIE		0x1b7014
 
 
 /* I2C Registers */
 /* I2C Registers */
 #define I2C_IF_ADDRESS   0x1B9000
 #define I2C_IF_ADDRESS   0x1B9000

+ 10 - 10
sound/pci/ctxfi/ctamixer.c

@@ -63,7 +63,7 @@ static int amixer_set_input(struct amixer *amixer, struct rsc *rsc)
 	hw = amixer->rsc.hw;
 	hw = amixer->rsc.hw;
 	hw->amixer_set_mode(amixer->rsc.ctrl_blk, AMIXER_Y_IMMEDIATE);
 	hw->amixer_set_mode(amixer->rsc.ctrl_blk, AMIXER_Y_IMMEDIATE);
 	amixer->input = rsc;
 	amixer->input = rsc;
-	if (NULL == rsc)
+	if (!rsc)
 		hw->amixer_set_x(amixer->rsc.ctrl_blk, BLANK_SLOT);
 		hw->amixer_set_x(amixer->rsc.ctrl_blk, BLANK_SLOT);
 	else
 	else
 		hw->amixer_set_x(amixer->rsc.ctrl_blk,
 		hw->amixer_set_x(amixer->rsc.ctrl_blk,
@@ -99,7 +99,7 @@ static int amixer_set_sum(struct amixer *amixer, struct sum *sum)
 
 
 	hw = amixer->rsc.hw;
 	hw = amixer->rsc.hw;
 	amixer->sum = sum;
 	amixer->sum = sum;
-	if (NULL == sum) {
+	if (!sum) {
 		hw->amixer_set_se(amixer->rsc.ctrl_blk, 0);
 		hw->amixer_set_se(amixer->rsc.ctrl_blk, 0);
 	} else {
 	} else {
 		hw->amixer_set_se(amixer->rsc.ctrl_blk, 1);
 		hw->amixer_set_se(amixer->rsc.ctrl_blk, 1);
@@ -124,20 +124,20 @@ static int amixer_commit_write(struct amixer *amixer)
 
 
 	/* Program master and conjugate resources */
 	/* Program master and conjugate resources */
 	amixer->rsc.ops->master(&amixer->rsc);
 	amixer->rsc.ops->master(&amixer->rsc);
-	if (NULL != input)
+	if (input)
 		input->ops->master(input);
 		input->ops->master(input);
 
 
-	if (NULL != sum)
+	if (sum)
 		sum->rsc.ops->master(&sum->rsc);
 		sum->rsc.ops->master(&sum->rsc);
 
 
 	for (i = 0; i < amixer->rsc.msr; i++) {
 	for (i = 0; i < amixer->rsc.msr; i++) {
 		hw->amixer_set_dirty_all(amixer->rsc.ctrl_blk);
 		hw->amixer_set_dirty_all(amixer->rsc.ctrl_blk);
-		if (NULL != input) {
+		if (input) {
 			hw->amixer_set_x(amixer->rsc.ctrl_blk,
 			hw->amixer_set_x(amixer->rsc.ctrl_blk,
 						input->ops->output_slot(input));
 						input->ops->output_slot(input));
 			input->ops->next_conj(input);
 			input->ops->next_conj(input);
 		}
 		}
-		if (NULL != sum) {
+		if (sum) {
 			hw->amixer_set_sadr(amixer->rsc.ctrl_blk,
 			hw->amixer_set_sadr(amixer->rsc.ctrl_blk,
 						sum->rsc.ops->index(&sum->rsc));
 						sum->rsc.ops->index(&sum->rsc));
 			sum->rsc.ops->next_conj(&sum->rsc);
 			sum->rsc.ops->next_conj(&sum->rsc);
@@ -147,10 +147,10 @@ static int amixer_commit_write(struct amixer *amixer)
 		amixer->rsc.ops->next_conj(&amixer->rsc);
 		amixer->rsc.ops->next_conj(&amixer->rsc);
 	}
 	}
 	amixer->rsc.ops->master(&amixer->rsc);
 	amixer->rsc.ops->master(&amixer->rsc);
-	if (NULL != input)
+	if (input)
 		input->ops->master(input);
 		input->ops->master(input);
 
 
-	if (NULL != sum)
+	if (sum)
 		sum->rsc.ops->master(&sum->rsc);
 		sum->rsc.ops->master(&sum->rsc);
 
 
 	return 0;
 	return 0;
@@ -303,7 +303,7 @@ int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr)
 
 
 	*ramixer_mgr = NULL;
 	*ramixer_mgr = NULL;
 	amixer_mgr = kzalloc(sizeof(*amixer_mgr), GFP_KERNEL);
 	amixer_mgr = kzalloc(sizeof(*amixer_mgr), GFP_KERNEL);
-	if (NULL == amixer_mgr)
+	if (!amixer_mgr)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	err = rsc_mgr_init(&amixer_mgr->mgr, AMIXER, AMIXER_RESOURCE_NUM, hw);
 	err = rsc_mgr_init(&amixer_mgr->mgr, AMIXER, AMIXER_RESOURCE_NUM, hw);
@@ -456,7 +456,7 @@ int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr)
 
 
 	*rsum_mgr = NULL;
 	*rsum_mgr = NULL;
 	sum_mgr = kzalloc(sizeof(*sum_mgr), GFP_KERNEL);
 	sum_mgr = kzalloc(sizeof(*sum_mgr), GFP_KERNEL);
-	if (NULL == sum_mgr)
+	if (!sum_mgr)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	err = rsc_mgr_init(&sum_mgr->mgr, SUM, SUM_RESOURCE_NUM, hw);
 	err = rsc_mgr_init(&sum_mgr->mgr, SUM, SUM_RESOURCE_NUM, hw);

+ 38 - 39
sound/pci/ctxfi/ctatc.c

@@ -136,7 +136,7 @@ static int ct_map_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm)
 	struct snd_pcm_runtime *runtime;
 	struct snd_pcm_runtime *runtime;
 	struct ct_vm *vm;
 	struct ct_vm *vm;
 
 
-	if (NULL == apcm->substream)
+	if (!apcm->substream)
 		return 0;
 		return 0;
 
 
 	runtime = apcm->substream->runtime;
 	runtime = apcm->substream->runtime;
@@ -144,7 +144,7 @@ static int ct_map_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm)
 
 
 	apcm->vm_block = vm->map(vm, apcm->substream, runtime->dma_bytes);
 	apcm->vm_block = vm->map(vm, apcm->substream, runtime->dma_bytes);
 
 
-	if (NULL == apcm->vm_block)
+	if (!apcm->vm_block)
 		return -ENOENT;
 		return -ENOENT;
 
 
 	return 0;
 	return 0;
@@ -154,7 +154,7 @@ static void ct_unmap_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm)
 {
 {
 	struct ct_vm *vm;
 	struct ct_vm *vm;
 
 
-	if (NULL == apcm->vm_block)
+	if (!apcm->vm_block)
 		return;
 		return;
 
 
 	vm = atc->vm;
 	vm = atc->vm;
@@ -231,16 +231,16 @@ atc_get_pitch(unsigned int input_rate, unsigned int output_rate)
 
 
 static int select_rom(unsigned int pitch)
 static int select_rom(unsigned int pitch)
 {
 {
-	if ((pitch > 0x00428f5c) && (pitch < 0x01b851ec)) {
+	if (pitch > 0x00428f5c && pitch < 0x01b851ec) {
 		/* 0.26 <= pitch <= 1.72 */
 		/* 0.26 <= pitch <= 1.72 */
 		return 1;
 		return 1;
-	} else if ((0x01d66666 == pitch) || (0x01d66667 == pitch)) {
+	} else if (pitch == 0x01d66666 || pitch == 0x01d66667) {
 		/* pitch == 1.8375 */
 		/* pitch == 1.8375 */
 		return 2;
 		return 2;
-	} else if (0x02000000 == pitch) {
+	} else if (pitch == 0x02000000) {
 		/* pitch == 2 */
 		/* pitch == 2 */
 		return 3;
 		return 3;
-	} else if ((pitch >= 0x0) && (pitch <= 0x08000000)) {
+	} else if (pitch >= 0x0 && pitch <= 0x08000000) {
 		/* 0 <= pitch <= 8 */
 		/* 0 <= pitch <= 8 */
 		return 0;
 		return 0;
 	} else {
 	} else {
@@ -283,7 +283,7 @@ static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
 	/* Get AMIXER resource */
 	/* Get AMIXER resource */
 	n_amixer = (n_amixer < 2) ? 2 : n_amixer;
 	n_amixer = (n_amixer < 2) ? 2 : n_amixer;
 	apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL);
 	apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL);
-	if (NULL == apcm->amixers) {
+	if (!apcm->amixers) {
 		err = -ENOMEM;
 		err = -ENOMEM;
 		goto error1;
 		goto error1;
 	}
 	}
@@ -311,7 +311,7 @@ static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
 					INIT_VOL, atc->pcm[i+device*2]);
 					INIT_VOL, atc->pcm[i+device*2]);
 		mutex_unlock(&atc->atc_mutex);
 		mutex_unlock(&atc->atc_mutex);
 		src = src->ops->next_interleave(src);
 		src = src->ops->next_interleave(src);
-		if (NULL == src)
+		if (!src)
 			src = apcm->src;
 			src = apcm->src;
 	}
 	}
 
 
@@ -334,7 +334,7 @@ atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm)
 	struct srcimp *srcimp;
 	struct srcimp *srcimp;
 	int i;
 	int i;
 
 
-	if (NULL != apcm->srcimps) {
+	if (apcm->srcimps) {
 		for (i = 0; i < apcm->n_srcimp; i++) {
 		for (i = 0; i < apcm->n_srcimp; i++) {
 			srcimp = apcm->srcimps[i];
 			srcimp = apcm->srcimps[i];
 			srcimp->ops->unmap(srcimp);
 			srcimp->ops->unmap(srcimp);
@@ -345,7 +345,7 @@ atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm)
 		apcm->srcimps = NULL;
 		apcm->srcimps = NULL;
 	}
 	}
 
 
-	if (NULL != apcm->srccs) {
+	if (apcm->srccs) {
 		for (i = 0; i < apcm->n_srcc; i++) {
 		for (i = 0; i < apcm->n_srcc; i++) {
 			src_mgr->put_src(src_mgr, apcm->srccs[i]);
 			src_mgr->put_src(src_mgr, apcm->srccs[i]);
 			apcm->srccs[i] = NULL;
 			apcm->srccs[i] = NULL;
@@ -354,7 +354,7 @@ atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm)
 		apcm->srccs = NULL;
 		apcm->srccs = NULL;
 	}
 	}
 
 
-	if (NULL != apcm->amixers) {
+	if (apcm->amixers) {
 		for (i = 0; i < apcm->n_amixer; i++) {
 		for (i = 0; i < apcm->n_amixer; i++) {
 			amixer_mgr->put_amixer(amixer_mgr, apcm->amixers[i]);
 			amixer_mgr->put_amixer(amixer_mgr, apcm->amixers[i]);
 			apcm->amixers[i] = NULL;
 			apcm->amixers[i] = NULL;
@@ -363,17 +363,17 @@ atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm)
 		apcm->amixers = NULL;
 		apcm->amixers = NULL;
 	}
 	}
 
 
-	if (NULL != apcm->mono) {
+	if (apcm->mono) {
 		sum_mgr->put_sum(sum_mgr, apcm->mono);
 		sum_mgr->put_sum(sum_mgr, apcm->mono);
 		apcm->mono = NULL;
 		apcm->mono = NULL;
 	}
 	}
 
 
-	if (NULL != apcm->src) {
+	if (apcm->src) {
 		src_mgr->put_src(src_mgr, apcm->src);
 		src_mgr->put_src(src_mgr, apcm->src);
 		apcm->src = NULL;
 		apcm->src = NULL;
 	}
 	}
 
 
-	if (NULL != apcm->vm_block) {
+	if (apcm->vm_block) {
 		/* Undo device virtual mem map */
 		/* Undo device virtual mem map */
 		ct_unmap_audio_buffer(atc, apcm);
 		ct_unmap_audio_buffer(atc, apcm);
 		apcm->vm_block = NULL;
 		apcm->vm_block = NULL;
@@ -419,7 +419,7 @@ static int atc_pcm_stop(struct ct_atc *atc, struct ct_atc_pcm *apcm)
 	src->ops->set_state(src, SRC_STATE_OFF);
 	src->ops->set_state(src, SRC_STATE_OFF);
 	src->ops->commit_write(src);
 	src->ops->commit_write(src);
 
 
-	if (NULL != apcm->srccs) {
+	if (apcm->srccs) {
 		for (i = 0; i < apcm->n_srcc; i++) {
 		for (i = 0; i < apcm->n_srcc; i++) {
 			src = apcm->srccs[i];
 			src = apcm->srccs[i];
 			src->ops->set_bm(src, 0);
 			src->ops->set_bm(src, 0);
@@ -544,18 +544,18 @@ atc_pcm_capture_get_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm)
 
 
 	if (n_srcc) {
 	if (n_srcc) {
 		apcm->srccs = kzalloc(sizeof(void *)*n_srcc, GFP_KERNEL);
 		apcm->srccs = kzalloc(sizeof(void *)*n_srcc, GFP_KERNEL);
-		if (NULL == apcm->srccs)
+		if (!apcm->srccs)
 			return -ENOMEM;
 			return -ENOMEM;
 	}
 	}
 	if (n_amixer) {
 	if (n_amixer) {
 		apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL);
 		apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL);
-		if (NULL == apcm->amixers) {
+		if (!apcm->amixers) {
 			err = -ENOMEM;
 			err = -ENOMEM;
 			goto error1;
 			goto error1;
 		}
 		}
 	}
 	}
 	apcm->srcimps = kzalloc(sizeof(void *)*n_srcimp, GFP_KERNEL);
 	apcm->srcimps = kzalloc(sizeof(void *)*n_srcimp, GFP_KERNEL);
-	if (NULL == apcm->srcimps) {
+	if (!apcm->srcimps) {
 		err = -ENOMEM;
 		err = -ENOMEM;
 		goto error1;
 		goto error1;
 	}
 	}
@@ -818,7 +818,7 @@ static int spdif_passthru_playback_get_resources(struct ct_atc *atc,
 	/* Get AMIXER resource */
 	/* Get AMIXER resource */
 	n_amixer = (n_amixer < 2) ? 2 : n_amixer;
 	n_amixer = (n_amixer < 2) ? 2 : n_amixer;
 	apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL);
 	apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL);
-	if (NULL == apcm->amixers) {
+	if (!apcm->amixers) {
 		err = -ENOMEM;
 		err = -ENOMEM;
 		goto error1;
 		goto error1;
 	}
 	}
@@ -919,7 +919,7 @@ spdif_passthru_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
 		amixer = apcm->amixers[i];
 		amixer = apcm->amixers[i];
 		amixer->ops->setup(amixer, &src->rsc, INIT_VOL, NULL);
 		amixer->ops->setup(amixer, &src->rsc, INIT_VOL, NULL);
 		src = src->ops->next_interleave(src);
 		src = src->ops->next_interleave(src);
-		if (NULL == src)
+		if (!src)
 			src = apcm->src;
 			src = apcm->src;
 	}
 	}
 	/* Connect to SPDIFOO */
 	/* Connect to SPDIFOO */
@@ -1121,7 +1121,7 @@ static int atc_release_resources(struct ct_atc *atc)
 	struct ct_mixer *mixer = NULL;
 	struct ct_mixer *mixer = NULL;
 
 
 	/* disconnect internal mixer objects */
 	/* disconnect internal mixer objects */
-	if (NULL != atc->mixer) {
+	if (atc->mixer) {
 		mixer = atc->mixer;
 		mixer = atc->mixer;
 		mixer->set_input_left(mixer, MIX_LINE_IN, NULL);
 		mixer->set_input_left(mixer, MIX_LINE_IN, NULL);
 		mixer->set_input_right(mixer, MIX_LINE_IN, NULL);
 		mixer->set_input_right(mixer, MIX_LINE_IN, NULL);
@@ -1131,7 +1131,7 @@ static int atc_release_resources(struct ct_atc *atc)
 		mixer->set_input_right(mixer, MIX_SPDIF_IN, NULL);
 		mixer->set_input_right(mixer, MIX_SPDIF_IN, NULL);
 	}
 	}
 
 
-	if (NULL != atc->daios) {
+	if (atc->daios) {
 		daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO];
 		daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO];
 		for (i = 0; i < atc->n_daio; i++) {
 		for (i = 0; i < atc->n_daio; i++) {
 			daio = atc->daios[i];
 			daio = atc->daios[i];
@@ -1149,7 +1149,7 @@ static int atc_release_resources(struct ct_atc *atc)
 		atc->daios = NULL;
 		atc->daios = NULL;
 	}
 	}
 
 
-	if (NULL != atc->pcm) {
+	if (atc->pcm) {
 		sum_mgr = atc->rsc_mgrs[SUM];
 		sum_mgr = atc->rsc_mgrs[SUM];
 		for (i = 0; i < atc->n_pcm; i++)
 		for (i = 0; i < atc->n_pcm; i++)
 			sum_mgr->put_sum(sum_mgr, atc->pcm[i]);
 			sum_mgr->put_sum(sum_mgr, atc->pcm[i]);
@@ -1158,7 +1158,7 @@ static int atc_release_resources(struct ct_atc *atc)
 		atc->pcm = NULL;
 		atc->pcm = NULL;
 	}
 	}
 
 
-	if (NULL != atc->srcs) {
+	if (atc->srcs) {
 		src_mgr = atc->rsc_mgrs[SRC];
 		src_mgr = atc->rsc_mgrs[SRC];
 		for (i = 0; i < atc->n_src; i++)
 		for (i = 0; i < atc->n_src; i++)
 			src_mgr->put_src(src_mgr, atc->srcs[i]);
 			src_mgr->put_src(src_mgr, atc->srcs[i]);
@@ -1167,7 +1167,7 @@ static int atc_release_resources(struct ct_atc *atc)
 		atc->srcs = NULL;
 		atc->srcs = NULL;
 	}
 	}
 
 
-	if (NULL != atc->srcimps) {
+	if (atc->srcimps) {
 		srcimp_mgr = atc->rsc_mgrs[SRCIMP];
 		srcimp_mgr = atc->rsc_mgrs[SRCIMP];
 		for (i = 0; i < atc->n_srcimp; i++) {
 		for (i = 0; i < atc->n_srcimp; i++) {
 			srcimp = atc->srcimps[i];
 			srcimp = atc->srcimps[i];
@@ -1185,7 +1185,7 @@ static int ct_atc_destroy(struct ct_atc *atc)
 {
 {
 	int i = 0;
 	int i = 0;
 
 
-	if (NULL == atc)
+	if (!atc)
 		return 0;
 		return 0;
 
 
 	if (atc->timer) {
 	if (atc->timer) {
@@ -1196,21 +1196,20 @@ static int ct_atc_destroy(struct ct_atc *atc)
 	atc_release_resources(atc);
 	atc_release_resources(atc);
 
 
 	/* Destroy internal mixer objects */
 	/* Destroy internal mixer objects */
-	if (NULL != atc->mixer)
+	if (atc->mixer)
 		ct_mixer_destroy(atc->mixer);
 		ct_mixer_destroy(atc->mixer);
 
 
 	for (i = 0; i < NUM_RSCTYP; i++) {
 	for (i = 0; i < NUM_RSCTYP; i++) {
-		if ((NULL != rsc_mgr_funcs[i].destroy) &&
-		    (NULL != atc->rsc_mgrs[i]))
+		if (rsc_mgr_funcs[i].destroy && atc->rsc_mgrs[i])
 			rsc_mgr_funcs[i].destroy(atc->rsc_mgrs[i]);
 			rsc_mgr_funcs[i].destroy(atc->rsc_mgrs[i]);
 
 
 	}
 	}
 
 
-	if (NULL != atc->hw)
+	if (atc->hw)
 		destroy_hw_obj((struct hw *)atc->hw);
 		destroy_hw_obj((struct hw *)atc->hw);
 
 
 	/* Destroy device virtual memory manager object */
 	/* Destroy device virtual memory manager object */
-	if (NULL != atc->vm) {
+	if (atc->vm) {
 		ct_vm_destroy(atc->vm);
 		ct_vm_destroy(atc->vm);
 		atc->vm = NULL;
 		atc->vm = NULL;
 	}
 	}
@@ -1275,7 +1274,7 @@ int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc)
 	alsa_dev_funcs[MIXER].public_name = atc->chip_name;
 	alsa_dev_funcs[MIXER].public_name = atc->chip_name;
 
 
 	for (i = 0; i < NUM_CTALSADEVS; i++) {
 	for (i = 0; i < NUM_CTALSADEVS; i++) {
-		if (NULL == alsa_dev_funcs[i].create)
+		if (!alsa_dev_funcs[i].create)
 			continue;
 			continue;
 
 
 		err = alsa_dev_funcs[i].create(atc, i,
 		err = alsa_dev_funcs[i].create(atc, i,
@@ -1312,7 +1311,7 @@ static int __devinit atc_create_hw_devs(struct ct_atc *atc)
 		return err;
 		return err;
 
 
 	for (i = 0; i < NUM_RSCTYP; i++) {
 	for (i = 0; i < NUM_RSCTYP; i++) {
-		if (NULL == rsc_mgr_funcs[i].create)
+		if (!rsc_mgr_funcs[i].create)
 			continue;
 			continue;
 
 
 		err = rsc_mgr_funcs[i].create(atc->hw, &atc->rsc_mgrs[i]);
 		err = rsc_mgr_funcs[i].create(atc->hw, &atc->rsc_mgrs[i]);
@@ -1339,19 +1338,19 @@ static int atc_get_resources(struct ct_atc *atc)
 	int err, i;
 	int err, i;
 
 
 	atc->daios = kzalloc(sizeof(void *)*(DAIONUM), GFP_KERNEL);
 	atc->daios = kzalloc(sizeof(void *)*(DAIONUM), GFP_KERNEL);
-	if (NULL == atc->daios)
+	if (!atc->daios)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	atc->srcs = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL);
 	atc->srcs = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL);
-	if (NULL == atc->srcs)
+	if (!atc->srcs)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	atc->srcimps = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL);
 	atc->srcimps = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL);
-	if (NULL == atc->srcimps)
+	if (!atc->srcimps)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	atc->pcm = kzalloc(sizeof(void *)*(2*4), GFP_KERNEL);
 	atc->pcm = kzalloc(sizeof(void *)*(2*4), GFP_KERNEL);
-	if (NULL == atc->pcm)
+	if (!atc->pcm)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO];
 	daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO];
@@ -1648,7 +1647,7 @@ int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci,
 	*ratc = NULL;
 	*ratc = NULL;
 
 
 	atc = kzalloc(sizeof(*atc), GFP_KERNEL);
 	atc = kzalloc(sizeof(*atc), GFP_KERNEL);
-	if (NULL == atc)
+	if (!atc)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	/* Set operations */
 	/* Set operations */

+ 15 - 15
sound/pci/ctxfi/ctdaio.c

@@ -173,7 +173,7 @@ static int dao_set_left_input(struct dao *dao, struct rsc *input)
 	int i;
 	int i;
 
 
 	entry = kzalloc((sizeof(*entry) * daio->rscl.msr), GFP_KERNEL);
 	entry = kzalloc((sizeof(*entry) * daio->rscl.msr), GFP_KERNEL);
-	if (NULL == entry)
+	if (!entry)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	/* Program master and conjugate resources */
 	/* Program master and conjugate resources */
@@ -201,7 +201,7 @@ static int dao_set_right_input(struct dao *dao, struct rsc *input)
 	int i;
 	int i;
 
 
 	entry = kzalloc((sizeof(*entry) * daio->rscr.msr), GFP_KERNEL);
 	entry = kzalloc((sizeof(*entry) * daio->rscr.msr), GFP_KERNEL);
-	if (NULL == entry)
+	if (!entry)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	/* Program master and conjugate resources */
 	/* Program master and conjugate resources */
@@ -228,7 +228,7 @@ static int dao_clear_left_input(struct dao *dao)
 	struct daio *daio = &dao->daio;
 	struct daio *daio = &dao->daio;
 	int i;
 	int i;
 
 
-	if (NULL == dao->imappers[0])
+	if (!dao->imappers[0])
 		return 0;
 		return 0;
 
 
 	entry = dao->imappers[0];
 	entry = dao->imappers[0];
@@ -252,7 +252,7 @@ static int dao_clear_right_input(struct dao *dao)
 	struct daio *daio = &dao->daio;
 	struct daio *daio = &dao->daio;
 	int i;
 	int i;
 
 
-	if (NULL == dao->imappers[daio->rscl.msr])
+	if (!dao->imappers[daio->rscl.msr])
 		return 0;
 		return 0;
 
 
 	entry = dao->imappers[daio->rscl.msr];
 	entry = dao->imappers[daio->rscl.msr];
@@ -408,7 +408,7 @@ static int dao_rsc_init(struct dao *dao,
 		return err;
 		return err;
 
 
 	dao->imappers = kzalloc(sizeof(void *)*desc->msr*2, GFP_KERNEL);
 	dao->imappers = kzalloc(sizeof(void *)*desc->msr*2, GFP_KERNEL);
-	if (NULL == dao->imappers) {
+	if (!dao->imappers) {
 		err = -ENOMEM;
 		err = -ENOMEM;
 		goto error1;
 		goto error1;
 	}
 	}
@@ -442,11 +442,11 @@ error1:
 
 
 static int dao_rsc_uninit(struct dao *dao)
 static int dao_rsc_uninit(struct dao *dao)
 {
 {
-	if (NULL != dao->imappers) {
-		if (NULL != dao->imappers[0])
+	if (dao->imappers) {
+		if (dao->imappers[0])
 			dao_clear_left_input(dao);
 			dao_clear_left_input(dao);
 
 
-		if (NULL != dao->imappers[dao->daio.rscl.msr])
+		if (dao->imappers[dao->daio.rscl.msr])
 			dao_clear_right_input(dao);
 			dao_clear_right_input(dao);
 
 
 		kfree(dao->imappers);
 		kfree(dao->imappers);
@@ -555,7 +555,7 @@ static int get_daio_rsc(struct daio_mgr *mgr,
 	/* Allocate mem for daio resource */
 	/* Allocate mem for daio resource */
 	if (desc->type <= DAIO_OUT_MAX) {
 	if (desc->type <= DAIO_OUT_MAX) {
 		dao = kzalloc(sizeof(*dao), GFP_KERNEL);
 		dao = kzalloc(sizeof(*dao), GFP_KERNEL);
-		if (NULL == dao) {
+		if (!dao) {
 			err = -ENOMEM;
 			err = -ENOMEM;
 			goto error;
 			goto error;
 		}
 		}
@@ -566,7 +566,7 @@ static int get_daio_rsc(struct daio_mgr *mgr,
 		*rdaio = &dao->daio;
 		*rdaio = &dao->daio;
 	} else {
 	} else {
 		dai = kzalloc(sizeof(*dai), GFP_KERNEL);
 		dai = kzalloc(sizeof(*dai), GFP_KERNEL);
-		if (NULL == dai) {
+		if (!dai) {
 			err = -ENOMEM;
 			err = -ENOMEM;
 			goto error;
 			goto error;
 		}
 		}
@@ -583,9 +583,9 @@ static int get_daio_rsc(struct daio_mgr *mgr,
 	return 0;
 	return 0;
 
 
 error:
 error:
-	if (NULL != dao)
+	if (dao)
 		kfree(dao);
 		kfree(dao);
-	else if (NULL != dai)
+	else if (dai)
 		kfree(dai);
 		kfree(dai);
 
 
 	spin_lock_irqsave(&mgr->mgr_lock, flags);
 	spin_lock_irqsave(&mgr->mgr_lock, flags);
@@ -663,7 +663,7 @@ static int daio_imap_add(struct daio_mgr *mgr, struct imapper *entry)
 	int err;
 	int err;
 
 
 	spin_lock_irqsave(&mgr->imap_lock, flags);
 	spin_lock_irqsave(&mgr->imap_lock, flags);
-	if ((0 == entry->addr) && (mgr->init_imap_added)) {
+	if (!entry->addr && mgr->init_imap_added) {
 		input_mapper_delete(&mgr->imappers, mgr->init_imap,
 		input_mapper_delete(&mgr->imappers, mgr->init_imap,
 							daio_map_op, mgr);
 							daio_map_op, mgr);
 		mgr->init_imap_added = 0;
 		mgr->init_imap_added = 0;
@@ -707,7 +707,7 @@ int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr)
 
 
 	*rdaio_mgr = NULL;
 	*rdaio_mgr = NULL;
 	daio_mgr = kzalloc(sizeof(*daio_mgr), GFP_KERNEL);
 	daio_mgr = kzalloc(sizeof(*daio_mgr), GFP_KERNEL);
-	if (NULL == daio_mgr)
+	if (!daio_mgr)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	err = rsc_mgr_init(&daio_mgr->mgr, DAIO, DAIO_RESOURCE_NUM, hw);
 	err = rsc_mgr_init(&daio_mgr->mgr, DAIO, DAIO_RESOURCE_NUM, hw);
@@ -718,7 +718,7 @@ int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr)
 	spin_lock_init(&daio_mgr->imap_lock);
 	spin_lock_init(&daio_mgr->imap_lock);
 	INIT_LIST_HEAD(&daio_mgr->imappers);
 	INIT_LIST_HEAD(&daio_mgr->imappers);
 	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
 	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
-	if (NULL == entry) {
+	if (!entry) {
 		err = -ENOMEM;
 		err = -ENOMEM;
 		goto error2;
 		goto error2;
 	}
 	}

+ 11 - 11
sound/pci/ctxfi/cthw20k1.c

@@ -168,7 +168,7 @@ static int src_get_rsc_ctrl_blk(void **rblk)
 
 
 	*rblk = NULL;
 	*rblk = NULL;
 	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
 	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
-	if (NULL == blk)
+	if (!blk)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	*rblk = blk;
 	*rblk = blk;
@@ -494,7 +494,7 @@ static int src_mgr_get_ctrl_blk(void **rblk)
 
 
 	*rblk = NULL;
 	*rblk = NULL;
 	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
 	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
-	if (NULL == blk)
+	if (!blk)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	*rblk = blk;
 	*rblk = blk;
@@ -515,7 +515,7 @@ static int srcimp_mgr_get_ctrl_blk(void **rblk)
 
 
 	*rblk = NULL;
 	*rblk = NULL;
 	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
 	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
-	if (NULL == blk)
+	if (!blk)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	*rblk = blk;
 	*rblk = blk;
@@ -702,7 +702,7 @@ static int amixer_rsc_get_ctrl_blk(void **rblk)
 
 
 	*rblk = NULL;
 	*rblk = NULL;
 	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
 	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
-	if (NULL == blk)
+	if (!blk)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	*rblk = blk;
 	*rblk = blk;
@@ -723,7 +723,7 @@ static int amixer_mgr_get_ctrl_blk(void **rblk)
 
 
 	*rblk = NULL;
 	*rblk = NULL;
 	/*blk = kzalloc(sizeof(*blk), GFP_KERNEL);
 	/*blk = kzalloc(sizeof(*blk), GFP_KERNEL);
-	if (NULL == blk)
+	if (!blk)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	*rblk = blk;*/
 	*rblk = blk;*/
@@ -909,7 +909,7 @@ static int dai_get_ctrl_blk(void **rblk)
 
 
 	*rblk = NULL;
 	*rblk = NULL;
 	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
 	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
-	if (NULL == blk)
+	if (!blk)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	*rblk = blk;
 	*rblk = blk;
@@ -958,7 +958,7 @@ static int dao_get_ctrl_blk(void **rblk)
 
 
 	*rblk = NULL;
 	*rblk = NULL;
 	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
 	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
-	if (NULL == blk)
+	if (!blk)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	*rblk = blk;
 	*rblk = blk;
@@ -1152,7 +1152,7 @@ static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk)
 
 
 	*rblk = NULL;
 	*rblk = NULL;
 	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
 	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
-	if (NULL == blk)
+	if (!blk)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	blk->i2sctl = hw_read_20kx(hw, I2SCTL);
 	blk->i2sctl = hw_read_20kx(hw, I2SCTL);
@@ -1808,7 +1808,7 @@ static int uaa_to_xfi(struct pci_dev *pci)
 	/* By default, Hendrix card UAA Bar0 should be using memory... */
 	/* By default, Hendrix card UAA Bar0 should be using memory... */
 	io_base = pci_resource_start(pci, 0);
 	io_base = pci_resource_start(pci, 0);
 	mem_base = ioremap(io_base, pci_resource_len(pci, 0));
 	mem_base = ioremap(io_base, pci_resource_len(pci, 0));
-	if (NULL == mem_base)
+	if (!mem_base)
 		return -ENOENT;
 		return -ENOENT;
 
 
 	/* Read current mode from Mode Change Register */
 	/* Read current mode from Mode Change Register */
@@ -1977,7 +1977,7 @@ static int hw_card_shutdown(struct hw *hw)
 
 
 	hw->irq	= -1;
 	hw->irq	= -1;
 
 
-	if (NULL != ((void *)hw->mem_base))
+	if (hw->mem_base)
 		iounmap((void *)hw->mem_base);
 		iounmap((void *)hw->mem_base);
 
 
 	hw->mem_base = (unsigned long)NULL;
 	hw->mem_base = (unsigned long)NULL;
@@ -2274,7 +2274,7 @@ int __devinit create_20k1_hw_obj(struct hw **rhw)
 
 
 	*rhw = NULL;
 	*rhw = NULL;
 	hw20k1 = kzalloc(sizeof(*hw20k1), GFP_KERNEL);
 	hw20k1 = kzalloc(sizeof(*hw20k1), GFP_KERNEL);
-	if (NULL == hw20k1)
+	if (!hw20k1)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	spin_lock_init(&hw20k1->reg_20k1_lock);
 	spin_lock_init(&hw20k1->reg_20k1_lock);

+ 58 - 15
sound/pci/ctxfi/cthw20k2.c

@@ -166,7 +166,7 @@ static int src_get_rsc_ctrl_blk(void **rblk)
 
 
 	*rblk = NULL;
 	*rblk = NULL;
 	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
 	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
-	if (NULL == blk)
+	if (!blk)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	*rblk = blk;
 	*rblk = blk;
@@ -492,7 +492,7 @@ static int src_mgr_get_ctrl_blk(void **rblk)
 
 
 	*rblk = NULL;
 	*rblk = NULL;
 	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
 	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
-	if (NULL == blk)
+	if (!blk)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	*rblk = blk;
 	*rblk = blk;
@@ -513,7 +513,7 @@ static int srcimp_mgr_get_ctrl_blk(void **rblk)
 
 
 	*rblk = NULL;
 	*rblk = NULL;
 	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
 	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
-	if (NULL == blk)
+	if (!blk)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	*rblk = blk;
 	*rblk = blk;
@@ -702,7 +702,7 @@ static int amixer_rsc_get_ctrl_blk(void **rblk)
 
 
 	*rblk = NULL;
 	*rblk = NULL;
 	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
 	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
-	if (NULL == blk)
+	if (!blk)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	*rblk = blk;
 	*rblk = blk;
@@ -891,7 +891,7 @@ static int dai_get_ctrl_blk(void **rblk)
 
 
 	*rblk = NULL;
 	*rblk = NULL;
 	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
 	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
-	if (NULL == blk)
+	if (!blk)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	*rblk = blk;
 	*rblk = blk;
@@ -941,7 +941,7 @@ static int dao_get_ctrl_blk(void **rblk)
 
 
 	*rblk = NULL;
 	*rblk = NULL;
 	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
 	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
-	if (NULL == blk)
+	if (!blk)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	*rblk = blk;
 	*rblk = blk;
@@ -1092,7 +1092,7 @@ static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk)
 
 
 	*rblk = NULL;
 	*rblk = NULL;
 	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
 	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
-	if (NULL == blk)
+	if (!blk)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	for (i = 0; i < 8; i++) {
 	for (i = 0; i < 8; i++) {
@@ -1112,6 +1112,26 @@ static int daio_mgr_put_ctrl_blk(void *blk)
 	return 0;
 	return 0;
 }
 }
 
 
+/* Timer interrupt */
+static int set_timer_irq(struct hw *hw, int enable)
+{
+	hw_write_20kx(hw, GIE, enable ? IT_INT : 0);
+	return 0;
+}
+
+static int set_timer_tick(struct hw *hw, unsigned int ticks)
+{
+	if (ticks)
+		ticks |= TIMR_IE | TIMR_IP;
+	hw_write_20kx(hw, TIMR, ticks);
+	return 0;
+}
+
+static unsigned int get_wc(struct hw *hw)
+{
+	return hw_read_20kx(hw, WC);
+}
+
 /* Card hardware initialization block */
 /* Card hardware initialization block */
 struct dac_conf {
 struct dac_conf {
 	unsigned int msr; /* master sample rate in rsrs */
 	unsigned int msr; /* master sample rate in rsrs */
@@ -1841,6 +1861,22 @@ static int hw_have_digit_io_switch(struct hw *hw)
 	return 0;
 	return 0;
 }
 }
 
 
+static irqreturn_t ct_20k2_interrupt(int irq, void *dev_id)
+{
+	struct hw *hw = dev_id;
+	unsigned int status;
+
+	status = hw_read_20kx(hw, GIP);
+	if (!status)
+		return IRQ_NONE;
+
+	if (hw->irq_callback)
+		hw->irq_callback(hw->irq_callback_data, status);
+
+	hw_write_20kx(hw, GIP, status);
+	return IRQ_HANDLED;
+}
+
 static int hw_card_start(struct hw *hw)
 static int hw_card_start(struct hw *hw)
 {
 {
 	int err = 0;
 	int err = 0;
@@ -1868,7 +1904,7 @@ static int hw_card_start(struct hw *hw)
 		hw->io_base = pci_resource_start(hw->pci, 2);
 		hw->io_base = pci_resource_start(hw->pci, 2);
 		hw->mem_base = (unsigned long)ioremap(hw->io_base,
 		hw->mem_base = (unsigned long)ioremap(hw->io_base,
 					pci_resource_len(hw->pci, 2));
 					pci_resource_len(hw->pci, 2));
-		if (NULL == (void *)hw->mem_base) {
+		if (!hw->mem_base) {
 			err = -ENOENT;
 			err = -ENOENT;
 			goto error2;
 			goto error2;
 		}
 		}
@@ -1879,12 +1915,15 @@ static int hw_card_start(struct hw *hw)
 	set_field(&gctl, GCTL_UAA, 0);
 	set_field(&gctl, GCTL_UAA, 0);
 	hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl);
 	hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl);
 
 
-	/*if ((err = request_irq(pci->irq, ct_atc_interrupt, IRQF_SHARED,
-				atc->chip_details->nm_card, hw))) {
-		goto error3;
+	if (hw->irq < 0) {
+		err = request_irq(pci->irq, ct_20k2_interrupt, IRQF_SHARED,
+				  "ctxfi", hw);
+		if (err < 0) {
+			printk(KERN_ERR "XFi: Cannot get irq %d\n", pci->irq);
+			goto error2;
+		}
+		hw->irq = pci->irq;
 	}
 	}
-	hw->irq = pci->irq;
-	*/
 
 
 	pci_set_master(pci);
 	pci_set_master(pci);
 
 
@@ -1923,7 +1962,7 @@ static int hw_card_shutdown(struct hw *hw)
 
 
 	hw->irq	= -1;
 	hw->irq	= -1;
 
 
-	if (NULL != ((void *)hw->mem_base))
+	if (hw->mem_base)
 		iounmap((void *)hw->mem_base);
 		iounmap((void *)hw->mem_base);
 
 
 	hw->mem_base = (unsigned long)NULL;
 	hw->mem_base = (unsigned long)NULL;
@@ -1972,7 +2011,7 @@ static int hw_card_init(struct hw *hw, struct card_conf *info)
 	hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl);
 	hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl);
 
 
 	/* Reset all global pending interrupts */
 	/* Reset all global pending interrupts */
-	hw_write_20kx(hw, INTERRUPT_GIE, 0);
+	hw_write_20kx(hw, GIE, 0);
 	/* Reset all SRC pending interrupts */
 	/* Reset all SRC pending interrupts */
 	hw_write_20kx(hw, SRC_IP, 0);
 	hw_write_20kx(hw, SRC_IP, 0);
 
 
@@ -2149,6 +2188,10 @@ static struct hw ct20k2_preset __devinitdata = {
 	.daio_mgr_set_imapnxt = daio_mgr_set_imapnxt,
 	.daio_mgr_set_imapnxt = daio_mgr_set_imapnxt,
 	.daio_mgr_set_imapaddr = daio_mgr_set_imapaddr,
 	.daio_mgr_set_imapaddr = daio_mgr_set_imapaddr,
 	.daio_mgr_commit_write = daio_mgr_commit_write,
 	.daio_mgr_commit_write = daio_mgr_commit_write,
+
+	.set_timer_irq = set_timer_irq,
+	.set_timer_tick = set_timer_tick,
+	.get_wc = get_wc,
 };
 };
 
 
 int __devinit create_20k2_hw_obj(struct hw **rhw)
 int __devinit create_20k2_hw_obj(struct hw **rhw)

+ 4 - 4
sound/pci/ctxfi/ctmixer.c

@@ -654,7 +654,7 @@ ct_mixer_kcontrol_new(struct ct_mixer *mixer, struct snd_kcontrol_new *new)
 	int err;
 	int err;
 
 
 	kctl = snd_ctl_new1(new, mixer->atc);
 	kctl = snd_ctl_new1(new, mixer->atc);
-	if (NULL == kctl)
+	if (!kctl)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	if (SNDRV_CTL_ELEM_IFACE_PCM == kctl->id.iface)
 	if (SNDRV_CTL_ELEM_IFACE_PCM == kctl->id.iface)
@@ -837,17 +837,17 @@ static int ct_mixer_get_mem(struct ct_mixer **rmixer)
 	*rmixer = NULL;
 	*rmixer = NULL;
 	/* Allocate mem for mixer obj */
 	/* Allocate mem for mixer obj */
 	mixer = kzalloc(sizeof(*mixer), GFP_KERNEL);
 	mixer = kzalloc(sizeof(*mixer), GFP_KERNEL);
-	if (NULL == mixer)
+	if (!mixer)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	mixer->amixers = kzalloc(sizeof(void *)*(NUM_CT_AMIXERS*CHN_NUM),
 	mixer->amixers = kzalloc(sizeof(void *)*(NUM_CT_AMIXERS*CHN_NUM),
 				 GFP_KERNEL);
 				 GFP_KERNEL);
-	if (NULL == mixer->amixers) {
+	if (!mixer->amixers) {
 		err = -ENOMEM;
 		err = -ENOMEM;
 		goto error1;
 		goto error1;
 	}
 	}
 	mixer->sums = kzalloc(sizeof(void *)*(NUM_CT_SUMS*CHN_NUM), GFP_KERNEL);
 	mixer->sums = kzalloc(sizeof(void *)*(NUM_CT_SUMS*CHN_NUM), GFP_KERNEL);
-	if (NULL == mixer->sums) {
+	if (!mixer->sums) {
 		err = -ENOMEM;
 		err = -ENOMEM;
 		goto error2;
 		goto error2;
 	}
 	}

+ 3 - 3
sound/pci/ctxfi/ctpcm.c

@@ -97,7 +97,7 @@ static void ct_atc_pcm_interrupt(struct ct_atc_pcm *atc_pcm)
 {
 {
 	struct ct_atc_pcm *apcm = atc_pcm;
 	struct ct_atc_pcm *apcm = atc_pcm;
 
 
-	if (NULL == apcm->substream)
+	if (!apcm->substream)
 		return;
 		return;
 
 
 	snd_pcm_period_elapsed(apcm->substream);
 	snd_pcm_period_elapsed(apcm->substream);
@@ -123,7 +123,7 @@ static int ct_pcm_playback_open(struct snd_pcm_substream *substream)
 	int err;
 	int err;
 
 
 	apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
 	apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
-	if (NULL == apcm)
+	if (!apcm)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	apcm->substream = substream;
 	apcm->substream = substream;
@@ -271,7 +271,7 @@ static int ct_pcm_capture_open(struct snd_pcm_substream *substream)
 	int err;
 	int err;
 
 
 	apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
 	apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
-	if (NULL == apcm)
+	if (!apcm)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	apcm->started = 0;
 	apcm->started = 0;

+ 2 - 2
sound/pci/ctxfi/ctresource.c

@@ -144,7 +144,7 @@ int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw)
 	rsc->msr = msr;
 	rsc->msr = msr;
 	rsc->hw = hw;
 	rsc->hw = hw;
 	rsc->ops = &rsc_generic_ops;
 	rsc->ops = &rsc_generic_ops;
-	if (NULL == hw) {
+	if (!hw) {
 		rsc->ctrl_blk = NULL;
 		rsc->ctrl_blk = NULL;
 		return 0;
 		return 0;
 	}
 	}
@@ -216,7 +216,7 @@ int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type,
 	mgr->type = NUM_RSCTYP;
 	mgr->type = NUM_RSCTYP;
 
 
 	mgr->rscs = kzalloc(((amount + 8 - 1) / 8), GFP_KERNEL);
 	mgr->rscs = kzalloc(((amount + 8 - 1) / 8), GFP_KERNEL);
-	if (NULL == mgr->rscs)
+	if (!mgr->rscs)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	switch (type) {
 	switch (type) {

+ 5 - 5
sound/pci/ctxfi/ctsrc.c

@@ -441,7 +441,7 @@ get_src_rsc(struct src_mgr *mgr, const struct src_desc *desc, struct src **rsrc)
 	else
 	else
 		src = kzalloc(sizeof(*src), GFP_KERNEL);
 		src = kzalloc(sizeof(*src), GFP_KERNEL);
 
 
-	if (NULL == src) {
+	if (!src) {
 		err = -ENOMEM;
 		err = -ENOMEM;
 		goto error1;
 		goto error1;
 	}
 	}
@@ -550,7 +550,7 @@ int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr)
 
 
 	*rsrc_mgr = NULL;
 	*rsrc_mgr = NULL;
 	src_mgr = kzalloc(sizeof(*src_mgr), GFP_KERNEL);
 	src_mgr = kzalloc(sizeof(*src_mgr), GFP_KERNEL);
-	if (NULL == src_mgr)
+	if (!src_mgr)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	err = rsc_mgr_init(&src_mgr->mgr, SRC, SRC_RESOURCE_NUM, hw);
 	err = rsc_mgr_init(&src_mgr->mgr, SRC, SRC_RESOURCE_NUM, hw);
@@ -679,7 +679,7 @@ static int srcimp_rsc_init(struct srcimp *srcimp,
 	/* Reserve memory for imapper nodes */
 	/* Reserve memory for imapper nodes */
 	srcimp->imappers = kzalloc(sizeof(struct imapper)*desc->msr,
 	srcimp->imappers = kzalloc(sizeof(struct imapper)*desc->msr,
 				   GFP_KERNEL);
 				   GFP_KERNEL);
-	if (NULL == srcimp->imappers) {
+	if (!srcimp->imappers) {
 		err = -ENOMEM;
 		err = -ENOMEM;
 		goto error1;
 		goto error1;
 	}
 	}
@@ -833,7 +833,7 @@ int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrcimp_mgr)
 
 
 	*rsrcimp_mgr = NULL;
 	*rsrcimp_mgr = NULL;
 	srcimp_mgr = kzalloc(sizeof(*srcimp_mgr), GFP_KERNEL);
 	srcimp_mgr = kzalloc(sizeof(*srcimp_mgr), GFP_KERNEL);
-	if (NULL == srcimp_mgr)
+	if (!srcimp_mgr)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	err = rsc_mgr_init(&srcimp_mgr->mgr, SRCIMP, SRCIMP_RESOURCE_NUM, hw);
 	err = rsc_mgr_init(&srcimp_mgr->mgr, SRCIMP, SRCIMP_RESOURCE_NUM, hw);
@@ -844,7 +844,7 @@ int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrcimp_mgr)
 	spin_lock_init(&srcimp_mgr->imap_lock);
 	spin_lock_init(&srcimp_mgr->imap_lock);
 	INIT_LIST_HEAD(&srcimp_mgr->imappers);
 	INIT_LIST_HEAD(&srcimp_mgr->imappers);
 	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
 	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
-	if (NULL == entry) {
+	if (!entry) {
 		err = -ENOMEM;
 		err = -ENOMEM;
 		goto error2;
 		goto error2;
 	}
 	}

+ 3 - 3
sound/pci/ctxfi/ctvmem.c

@@ -60,7 +60,7 @@ get_vm_block(struct ct_vm *vm, unsigned int size)
 	}
 	}
 
 
 	block = kzalloc(sizeof(*block), GFP_KERNEL);
 	block = kzalloc(sizeof(*block), GFP_KERNEL);
-	if (NULL == block)
+	if (!block)
 		goto out;
 		goto out;
 
 
 	block->addr = entry->addr;
 	block->addr = entry->addr;
@@ -181,7 +181,7 @@ int ct_vm_create(struct ct_vm **rvm)
 	*rvm = NULL;
 	*rvm = NULL;
 
 
 	vm = kzalloc(sizeof(*vm), GFP_KERNEL);
 	vm = kzalloc(sizeof(*vm), GFP_KERNEL);
-	if (NULL == vm)
+	if (!vm)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	mutex_init(&vm->lock);
 	mutex_init(&vm->lock);
@@ -189,7 +189,7 @@ int ct_vm_create(struct ct_vm **rvm)
 	/* Allocate page table pages */
 	/* Allocate page table pages */
 	for (i = 0; i < CT_PTP_NUM; i++) {
 	for (i = 0; i < CT_PTP_NUM; i++) {
 		vm->ptp[i] = kmalloc(PAGE_SIZE, GFP_KERNEL);
 		vm->ptp[i] = kmalloc(PAGE_SIZE, GFP_KERNEL);
-		if (NULL == vm->ptp[i])
+		if (!vm->ptp[i])
 			break;
 			break;
 	}
 	}
 	if (!i) {
 	if (!i) {

+ 27 - 0
sound/pci/hda/Kconfig

@@ -46,6 +46,20 @@ config SND_HDA_INPUT_JACK
 	  Say Y here to enable the jack plugging notification via
 	  Say Y here to enable the jack plugging notification via
 	  input layer.
 	  input layer.
 
 
+config SND_HDA_PATCH_LOADER
+	bool "Support initialization patch loading for HD-audio"
+	depends on EXPERIMENTAL
+	select FW_LOADER
+	select SND_HDA_HWDEP
+	select SND_HDA_RECONFIG
+	help
+	  Say Y here to allow the HD-audio driver to load a pseudo
+	  firmware file ("patch") for overriding the BIOS setup at
+	  start up.  The "patch" file can be specified via patch module
+	  option, such as patch=hda-init.
+
+	  This option turns on hwdep and reconfig features automatically.
+
 config SND_HDA_CODEC_REALTEK
 config SND_HDA_CODEC_REALTEK
 	bool "Build Realtek HD-audio codec support"
 	bool "Build Realtek HD-audio codec support"
 	default y
 	default y
@@ -134,6 +148,19 @@ config SND_HDA_ELD
 	def_bool y
 	def_bool y
 	depends on SND_HDA_CODEC_INTELHDMI
 	depends on SND_HDA_CODEC_INTELHDMI
 
 
+config SND_HDA_CODEC_CIRRUS
+	bool "Build Cirrus Logic codec support"
+	depends on SND_HDA_INTEL
+	default y
+	help
+	  Say Y here to include Cirrus Logic codec support in
+	  snd-hda-intel driver, such as CS4206.
+
+	  When the HD-audio driver is built as a module, the codec
+	  support code is also built as another module,
+	  snd-hda-codec-cirrus.
+	  This module is automatically loaded at probing.
+
 config SND_HDA_CODEC_CONEXANT
 config SND_HDA_CODEC_CONEXANT
 	bool "Build Conexant HD-audio codec support"
 	bool "Build Conexant HD-audio codec support"
 	default y
 	default y

+ 4 - 0
sound/pci/hda/Makefile

@@ -13,6 +13,7 @@ snd-hda-codec-analog-objs :=	patch_analog.o
 snd-hda-codec-idt-objs :=	patch_sigmatel.o
 snd-hda-codec-idt-objs :=	patch_sigmatel.o
 snd-hda-codec-si3054-objs :=	patch_si3054.o
 snd-hda-codec-si3054-objs :=	patch_si3054.o
 snd-hda-codec-atihdmi-objs :=	patch_atihdmi.o
 snd-hda-codec-atihdmi-objs :=	patch_atihdmi.o
+snd-hda-codec-cirrus-objs :=	patch_cirrus.o
 snd-hda-codec-ca0110-objs :=	patch_ca0110.o
 snd-hda-codec-ca0110-objs :=	patch_ca0110.o
 snd-hda-codec-conexant-objs :=	patch_conexant.o
 snd-hda-codec-conexant-objs :=	patch_conexant.o
 snd-hda-codec-via-objs :=	patch_via.o
 snd-hda-codec-via-objs :=	patch_via.o
@@ -41,6 +42,9 @@ endif
 ifdef CONFIG_SND_HDA_CODEC_ATIHDMI
 ifdef CONFIG_SND_HDA_CODEC_ATIHDMI
 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-atihdmi.o
 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-atihdmi.o
 endif
 endif
+ifdef CONFIG_SND_HDA_CODEC_CIRRUS
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-cirrus.o
+endif
 ifdef CONFIG_SND_HDA_CODEC_CA0110
 ifdef CONFIG_SND_HDA_CODEC_CA0110
 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-ca0110.o
 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-ca0110.o
 endif
 endif

+ 4 - 0
sound/pci/hda/hda_beep.c

@@ -24,6 +24,7 @@
 #include <linux/workqueue.h>
 #include <linux/workqueue.h>
 #include <sound/core.h>
 #include <sound/core.h>
 #include "hda_beep.h"
 #include "hda_beep.h"
+#include "hda_local.h"
 
 
 enum {
 enum {
 	DIGBEEP_HZ_STEP = 46875,	/* 46.875 Hz */
 	DIGBEEP_HZ_STEP = 46875,	/* 46.875 Hz */
@@ -118,6 +119,9 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
 	struct hda_beep *beep;
 	struct hda_beep *beep;
 	int err;
 	int err;
 
 
+	if (!snd_hda_get_bool_hint(codec, "beep"))
+		return 0; /* disabled explicitly */
+
 	beep = kzalloc(sizeof(*beep), GFP_KERNEL);
 	beep = kzalloc(sizeof(*beep), GFP_KERNEL);
 	if (beep == NULL)
 	if (beep == NULL)
 		return -ENOMEM;
 		return -ENOMEM;

+ 46 - 22
sound/pci/hda/hda_codec.c

@@ -44,6 +44,7 @@ struct hda_vendor_id {
 /* codec vendor labels */
 /* codec vendor labels */
 static struct hda_vendor_id hda_vendor_ids[] = {
 static struct hda_vendor_id hda_vendor_ids[] = {
 	{ 0x1002, "ATI" },
 	{ 0x1002, "ATI" },
+	{ 0x1013, "Cirrus Logic" },
 	{ 0x1057, "Motorola" },
 	{ 0x1057, "Motorola" },
 	{ 0x1095, "Silicon Image" },
 	{ 0x1095, "Silicon Image" },
 	{ 0x10de, "Nvidia" },
 	{ 0x10de, "Nvidia" },
@@ -150,7 +151,14 @@ make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
 {
 {
 	u32 val;
 	u32 val;
 
 
-	val = (u32)(codec->addr & 0x0f) << 28;
+	if ((codec->addr & ~0xf) || (direct & ~1) || (nid & ~0x7f) ||
+	    (verb & ~0xfff) || (parm & ~0xffff)) {
+		printk(KERN_ERR "hda-codec: out of range cmd %x:%x:%x:%x:%x\n",
+		       codec->addr, direct, nid, verb, parm);
+		return ~0;
+	}
+
+	val = (u32)codec->addr << 28;
 	val |= (u32)direct << 27;
 	val |= (u32)direct << 27;
 	val |= (u32)nid << 20;
 	val |= (u32)nid << 20;
 	val |= verb << 8;
 	val |= verb << 8;
@@ -167,6 +175,9 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
 	struct hda_bus *bus = codec->bus;
 	struct hda_bus *bus = codec->bus;
 	int err;
 	int err;
 
 
+	if (cmd == ~0)
+		return -1;
+
 	if (res)
 	if (res)
 		*res = -1;
 		*res = -1;
  again:
  again:
@@ -291,11 +302,20 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
 	unsigned int parm;
 	unsigned int parm;
 	int i, conn_len, conns;
 	int i, conn_len, conns;
 	unsigned int shift, num_elems, mask;
 	unsigned int shift, num_elems, mask;
+	unsigned int wcaps;
 	hda_nid_t prev_nid;
 	hda_nid_t prev_nid;
 
 
 	if (snd_BUG_ON(!conn_list || max_conns <= 0))
 	if (snd_BUG_ON(!conn_list || max_conns <= 0))
 		return -EINVAL;
 		return -EINVAL;
 
 
+	wcaps = get_wcaps(codec, nid);
+	if (!(wcaps & AC_WCAP_CONN_LIST) &&
+	    get_wcaps_type(wcaps) != AC_WID_VOL_KNB) {
+		snd_printk(KERN_WARNING "hda_codec: "
+			   "connection list not available for 0x%x\n", nid);
+		return -EINVAL;
+	}
+
 	parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN);
 	parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN);
 	if (parm & AC_CLIST_LONG) {
 	if (parm & AC_CLIST_LONG) {
 		/* long form */
 		/* long form */
@@ -316,6 +336,8 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
 		/* single connection */
 		/* single connection */
 		parm = snd_hda_codec_read(codec, nid, 0,
 		parm = snd_hda_codec_read(codec, nid, 0,
 					  AC_VERB_GET_CONNECT_LIST, 0);
 					  AC_VERB_GET_CONNECT_LIST, 0);
+		if (parm == -1 && codec->bus->rirb_error)
+			return -EIO;
 		conn_list[0] = parm & mask;
 		conn_list[0] = parm & mask;
 		return 1;
 		return 1;
 	}
 	}
@@ -327,9 +349,12 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
 		int range_val;
 		int range_val;
 		hda_nid_t val, n;
 		hda_nid_t val, n;
 
 
-		if (i % num_elems == 0)
+		if (i % num_elems == 0) {
 			parm = snd_hda_codec_read(codec, nid, 0,
 			parm = snd_hda_codec_read(codec, nid, 0,
 						  AC_VERB_GET_CONNECT_LIST, i);
 						  AC_VERB_GET_CONNECT_LIST, i);
+			if (parm == -1 && codec->bus->rirb_error)
+				return -EIO;
+		}
 		range_val = !!(parm & (1 << (shift-1))); /* ranges */
 		range_val = !!(parm & (1 << (shift-1))); /* ranges */
 		val = parm & mask;
 		val = parm & mask;
 		if (val == 0) {
 		if (val == 0) {
@@ -727,8 +752,7 @@ static int read_pin_defaults(struct hda_codec *codec)
 	for (i = 0; i < codec->num_nodes; i++, nid++) {
 	for (i = 0; i < codec->num_nodes; i++, nid++) {
 		struct hda_pincfg *pin;
 		struct hda_pincfg *pin;
 		unsigned int wcaps = get_wcaps(codec, nid);
 		unsigned int wcaps = get_wcaps(codec, nid);
-		unsigned int wid_type = (wcaps & AC_WCAP_TYPE) >>
-				AC_WCAP_TYPE_SHIFT;
+		unsigned int wid_type = get_wcaps_type(wcaps);
 		if (wid_type != AC_WID_PIN)
 		if (wid_type != AC_WID_PIN)
 			continue;
 			continue;
 		pin = snd_array_new(&codec->init_pins);
 		pin = snd_array_new(&codec->init_pins);
@@ -891,7 +915,7 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
  * Returns 0 if successful, or a negative error code.
  * Returns 0 if successful, or a negative error code.
  */
  */
 int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
 int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
-				    int do_init, struct hda_codec **codecp)
+				    struct hda_codec **codecp)
 {
 {
 	struct hda_codec *codec;
 	struct hda_codec *codec;
 	char component[31];
 	char component[31];
@@ -984,11 +1008,6 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
 			    codec->afg ? codec->afg : codec->mfg,
 			    codec->afg ? codec->afg : codec->mfg,
 			    AC_PWRST_D0);
 			    AC_PWRST_D0);
 
 
-	if (do_init) {
-		err = snd_hda_codec_configure(codec);
-		if (err < 0)
-			goto error;
-	}
 	snd_hda_codec_proc_new(codec);
 	snd_hda_codec_proc_new(codec);
 
 
 	snd_hda_create_hwdep(codec);
 	snd_hda_create_hwdep(codec);
@@ -1042,6 +1061,7 @@ int snd_hda_codec_configure(struct hda_codec *codec)
 		err = init_unsol_queue(codec->bus);
 		err = init_unsol_queue(codec->bus);
 	return err;
 	return err;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_codec_configure);
 
 
 /**
 /**
  * snd_hda_codec_setup_stream - set up the codec for streaming
  * snd_hda_codec_setup_stream - set up the codec for streaming
@@ -2356,16 +2376,20 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
 	hda_nid_t nid;
 	hda_nid_t nid;
 	int i;
 	int i;
 
 
-	snd_hda_codec_write(codec, fg, 0, AC_VERB_SET_POWER_STATE,
+	/* this delay seems necessary to avoid click noise at power-down */
+	if (power_state == AC_PWRST_D3)
+		msleep(100);
+	snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE,
 			    power_state);
 			    power_state);
-	msleep(10); /* partial workaround for "azx_get_response timeout" */
+	/* partial workaround for "azx_get_response timeout" */
+	if (power_state == AC_PWRST_D0)
+		msleep(10);
 
 
 	nid = codec->start_nid;
 	nid = codec->start_nid;
 	for (i = 0; i < codec->num_nodes; i++, nid++) {
 	for (i = 0; i < codec->num_nodes; i++, nid++) {
 		unsigned int wcaps = get_wcaps(codec, nid);
 		unsigned int wcaps = get_wcaps(codec, nid);
 		if (wcaps & AC_WCAP_POWER) {
 		if (wcaps & AC_WCAP_POWER) {
-			unsigned int wid_type = (wcaps & AC_WCAP_TYPE) >>
-				AC_WCAP_TYPE_SHIFT;
+			unsigned int wid_type = get_wcaps_type(wcaps);
 			if (power_state == AC_PWRST_D3 &&
 			if (power_state == AC_PWRST_D3 &&
 			    wid_type == AC_WID_PIN) {
 			    wid_type == AC_WID_PIN) {
 				unsigned int pincap;
 				unsigned int pincap;
@@ -2573,7 +2597,7 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
 	case 20:
 	case 20:
 	case 24:
 	case 24:
 	case 32:
 	case 32:
-		if (maxbps >= 32)
+		if (maxbps >= 32 || format == SNDRV_PCM_FORMAT_FLOAT_LE)
 			val |= 0x40;
 			val |= 0x40;
 		else if (maxbps >= 24)
 		else if (maxbps >= 24)
 			val |= 0x30;
 			val |= 0x30;
@@ -2700,11 +2724,12 @@ static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
 					bps = 20;
 					bps = 20;
 			}
 			}
 		}
 		}
-		else if (streams == AC_SUPFMT_FLOAT32) {
-			/* should be exclusive */
+		if (streams & AC_SUPFMT_FLOAT32) {
 			formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
 			formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
-			bps = 32;
-		} else if (streams == AC_SUPFMT_AC3) {
+			if (!bps)
+				bps = 32;
+		}
+		if (streams == AC_SUPFMT_AC3) {
 			/* should be exclusive */
 			/* should be exclusive */
 			/* temporary hack: we have still no proper support
 			/* temporary hack: we have still no proper support
 			 * for the direct AC3 stream...
 			 * for the direct AC3 stream...
@@ -3102,7 +3127,7 @@ int snd_hda_check_board_codec_sid_config(struct hda_codec *codec,
 	tbl = q;
 	tbl = q;
 
 
 	if (tbl->value >= 0 && tbl->value < num_configs) {
 	if (tbl->value >= 0 && tbl->value < num_configs) {
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
 		char tmp[10];
 		char tmp[10];
 		const char *model = NULL;
 		const char *model = NULL;
 		if (models)
 		if (models)
@@ -3655,8 +3680,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
 	end_nid = codec->start_nid + codec->num_nodes;
 	end_nid = codec->start_nid + codec->num_nodes;
 	for (nid = codec->start_nid; nid < end_nid; nid++) {
 	for (nid = codec->start_nid; nid < end_nid; nid++) {
 		unsigned int wid_caps = get_wcaps(codec, nid);
 		unsigned int wid_caps = get_wcaps(codec, nid);
-		unsigned int wid_type =
-			(wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+		unsigned int wid_type = get_wcaps_type(wid_caps);
 		unsigned int def_conf;
 		unsigned int def_conf;
 		short assoc, loc;
 		short assoc, loc;
 
 

+ 9 - 1
sound/pci/hda/hda_codec.h

@@ -830,7 +830,8 @@ enum {
 int snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp,
 int snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp,
 		    struct hda_bus **busp);
 		    struct hda_bus **busp);
 int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
 int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
-		      int do_init, struct hda_codec **codecp);
+		      struct hda_codec **codecp);
+int snd_hda_codec_configure(struct hda_codec *codec);
 
 
 /*
 /*
  * low level functions
  * low level functions
@@ -938,6 +939,13 @@ static inline void snd_hda_power_down(struct hda_codec *codec) {}
 #define snd_hda_codec_needs_resume(codec) 1
 #define snd_hda_codec_needs_resume(codec) 1
 #endif
 #endif
 
 
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+/*
+ * patch firmware
+ */
+int snd_hda_load_patch(struct hda_bus *bus, const char *patch);
+#endif
+
 /*
 /*
  * Codec modularization
  * Codec modularization
  */
  */

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

@@ -121,11 +121,17 @@ static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid
 	if (node == NULL)
 	if (node == NULL)
 		return -ENOMEM;
 		return -ENOMEM;
 	node->nid = nid;
 	node->nid = nid;
-	nconns = snd_hda_get_connections(codec, nid, conn_list,
-					 HDA_MAX_CONNECTIONS);
-	if (nconns < 0) {
-		kfree(node);
-		return nconns;
+	node->wid_caps = get_wcaps(codec, nid);
+	node->type = get_wcaps_type(node->wid_caps);
+	if (node->wid_caps & AC_WCAP_CONN_LIST) {
+		nconns = snd_hda_get_connections(codec, nid, conn_list,
+						 HDA_MAX_CONNECTIONS);
+		if (nconns < 0) {
+			kfree(node);
+			return nconns;
+		}
+	} else {
+		nconns = 0;
 	}
 	}
 	if (nconns <= ARRAY_SIZE(node->slist))
 	if (nconns <= ARRAY_SIZE(node->slist))
 		node->conn_list = node->slist;
 		node->conn_list = node->slist;
@@ -140,8 +146,6 @@ static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid
 	}
 	}
 	memcpy(node->conn_list, conn_list, nconns * sizeof(hda_nid_t));
 	memcpy(node->conn_list, conn_list, nconns * sizeof(hda_nid_t));
 	node->nconns = nconns;
 	node->nconns = nconns;
-	node->wid_caps = get_wcaps(codec, nid);
-	node->type = (node->wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
 
 
 	if (node->type == AC_WID_PIN) {
 	if (node->type == AC_WID_PIN) {
 		node->pin_caps = snd_hda_query_pin_caps(codec, node->nid);
 		node->pin_caps = snd_hda_query_pin_caps(codec, node->nid);

+ 217 - 19
sound/pci/hda/hda_hwdep.c

@@ -24,6 +24,7 @@
 #include <linux/compat.h>
 #include <linux/compat.h>
 #include <linux/mutex.h>
 #include <linux/mutex.h>
 #include <linux/ctype.h>
 #include <linux/ctype.h>
+#include <linux/firmware.h>
 #include <sound/core.h>
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_codec.h"
 #include "hda_local.h"
 #include "hda_local.h"
@@ -312,12 +313,8 @@ static ssize_t init_verbs_show(struct device *dev,
 	return len;
 	return len;
 }
 }
 
 
-static ssize_t init_verbs_store(struct device *dev,
-				struct device_attribute *attr,
-				const char *buf, size_t count)
+static int parse_init_verbs(struct hda_codec *codec, const char *buf)
 {
 {
-	struct snd_hwdep *hwdep = dev_get_drvdata(dev);
-	struct hda_codec *codec = hwdep->private_data;
 	struct hda_verb *v;
 	struct hda_verb *v;
 	int nid, verb, param;
 	int nid, verb, param;
 
 
@@ -331,6 +328,18 @@ static ssize_t init_verbs_store(struct device *dev,
 	v->nid = nid;
 	v->nid = nid;
 	v->verb = verb;
 	v->verb = verb;
 	v->param = param;
 	v->param = param;
+	return 0;
+}
+
+static ssize_t init_verbs_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+	struct hda_codec *codec = hwdep->private_data;
+	int err = parse_init_verbs(codec, buf);
+	if (err < 0)
+		return err;
 	return count;
 	return count;
 }
 }
 
 
@@ -376,19 +385,15 @@ static void remove_trail_spaces(char *str)
 
 
 #define MAX_HINTS	1024
 #define MAX_HINTS	1024
 
 
-static ssize_t hints_store(struct device *dev,
-			   struct device_attribute *attr,
-			   const char *buf, size_t count)
+static int parse_hints(struct hda_codec *codec, const char *buf)
 {
 {
-	struct snd_hwdep *hwdep = dev_get_drvdata(dev);
-	struct hda_codec *codec = hwdep->private_data;
 	char *key, *val;
 	char *key, *val;
 	struct hda_hint *hint;
 	struct hda_hint *hint;
 
 
 	while (isspace(*buf))
 	while (isspace(*buf))
 		buf++;
 		buf++;
 	if (!*buf || *buf == '#' || *buf == '\n')
 	if (!*buf || *buf == '#' || *buf == '\n')
-		return count;
+		return 0;
 	if (*buf == '=')
 	if (*buf == '=')
 		return -EINVAL;
 		return -EINVAL;
 	key = kstrndup_noeol(buf, 1024);
 	key = kstrndup_noeol(buf, 1024);
@@ -411,7 +416,7 @@ static ssize_t hints_store(struct device *dev,
 		kfree(hint->key);
 		kfree(hint->key);
 		hint->key = key;
 		hint->key = key;
 		hint->val = val;
 		hint->val = val;
-		return count;
+		return 0;
 	}
 	}
 	/* allocate a new hint entry */
 	/* allocate a new hint entry */
 	if (codec->hints.used >= MAX_HINTS)
 	if (codec->hints.used >= MAX_HINTS)
@@ -424,6 +429,18 @@ static ssize_t hints_store(struct device *dev,
 	}
 	}
 	hint->key = key;
 	hint->key = key;
 	hint->val = val;
 	hint->val = val;
+	return 0;
+}
+
+static ssize_t hints_store(struct device *dev,
+			   struct device_attribute *attr,
+			   const char *buf, size_t count)
+{
+	struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+	struct hda_codec *codec = hwdep->private_data;
+	int err = parse_hints(codec, buf);
+	if (err < 0)
+		return err;
 	return count;
 	return count;
 }
 }
 
 
@@ -469,20 +486,24 @@ static ssize_t driver_pin_configs_show(struct device *dev,
 
 
 #define MAX_PIN_CONFIGS		32
 #define MAX_PIN_CONFIGS		32
 
 
-static ssize_t user_pin_configs_store(struct device *dev,
-				      struct device_attribute *attr,
-				      const char *buf, size_t count)
+static int parse_user_pin_configs(struct hda_codec *codec, const char *buf)
 {
 {
-	struct snd_hwdep *hwdep = dev_get_drvdata(dev);
-	struct hda_codec *codec = hwdep->private_data;
 	int nid, cfg;
 	int nid, cfg;
-	int err;
 
 
 	if (sscanf(buf, "%i %i", &nid, &cfg) != 2)
 	if (sscanf(buf, "%i %i", &nid, &cfg) != 2)
 		return -EINVAL;
 		return -EINVAL;
 	if (!nid)
 	if (!nid)
 		return -EINVAL;
 		return -EINVAL;
-	err = snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);
+	return snd_hda_add_pincfg(codec, &codec->user_pins, nid, cfg);
+}
+
+static ssize_t user_pin_configs_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t count)
+{
+	struct snd_hwdep *hwdep = dev_get_drvdata(dev);
+	struct hda_codec *codec = hwdep->private_data;
+	int err = parse_user_pin_configs(codec, buf);
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
 	return count;
 	return count;
@@ -553,3 +574,180 @@ int snd_hda_get_bool_hint(struct hda_codec *codec, const char *key)
 EXPORT_SYMBOL_HDA(snd_hda_get_bool_hint);
 EXPORT_SYMBOL_HDA(snd_hda_get_bool_hint);
 
 
 #endif /* CONFIG_SND_HDA_RECONFIG */
 #endif /* CONFIG_SND_HDA_RECONFIG */
+
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+
+/* parser mode */
+enum {
+	LINE_MODE_NONE,
+	LINE_MODE_CODEC,
+	LINE_MODE_MODEL,
+	LINE_MODE_PINCFG,
+	LINE_MODE_VERB,
+	LINE_MODE_HINT,
+	NUM_LINE_MODES,
+};
+
+static inline int strmatch(const char *a, const char *b)
+{
+	return strnicmp(a, b, strlen(b)) == 0;
+}
+
+/* parse the contents after the line "[codec]"
+ * accept only the line with three numbers, and assign the current codec
+ */
+static void parse_codec_mode(char *buf, struct hda_bus *bus,
+			     struct hda_codec **codecp)
+{
+	unsigned int vendorid, subid, caddr;
+	struct hda_codec *codec;
+
+	*codecp = NULL;
+	if (sscanf(buf, "%i %i %i", &vendorid, &subid, &caddr) == 3) {
+		list_for_each_entry(codec, &bus->codec_list, list) {
+			if (codec->addr == caddr) {
+				*codecp = codec;
+				break;
+			}
+		}
+	}
+}
+
+/* parse the contents after the other command tags, [pincfg], [verb],
+ * [hint] and [model]
+ * just pass to the sysfs helper (only when any codec was specified)
+ */
+static void parse_pincfg_mode(char *buf, struct hda_bus *bus,
+			      struct hda_codec **codecp)
+{
+	if (!*codecp)
+		return;
+	parse_user_pin_configs(*codecp, buf);
+}
+
+static void parse_verb_mode(char *buf, struct hda_bus *bus,
+			    struct hda_codec **codecp)
+{
+	if (!*codecp)
+		return;
+	parse_init_verbs(*codecp, buf);
+}
+
+static void parse_hint_mode(char *buf, struct hda_bus *bus,
+			    struct hda_codec **codecp)
+{
+	if (!*codecp)
+		return;
+	parse_hints(*codecp, buf);
+}
+
+static void parse_model_mode(char *buf, struct hda_bus *bus,
+			     struct hda_codec **codecp)
+{
+	if (!*codecp)
+		return;
+	kfree((*codecp)->modelname);
+	(*codecp)->modelname = kstrdup(buf, GFP_KERNEL);
+}
+
+struct hda_patch_item {
+	const char *tag;
+	void (*parser)(char *buf, struct hda_bus *bus, struct hda_codec **retc);
+};
+
+static struct hda_patch_item patch_items[NUM_LINE_MODES] = {
+	[LINE_MODE_CODEC] = { "[codec]", parse_codec_mode },
+	[LINE_MODE_MODEL] = { "[model]", parse_model_mode },
+	[LINE_MODE_VERB] = { "[verb]", parse_verb_mode },
+	[LINE_MODE_PINCFG] = { "[pincfg]", parse_pincfg_mode },
+	[LINE_MODE_HINT] = { "[hint]", parse_hint_mode },
+};
+
+/* check the line starting with '[' -- change the parser mode accodingly */
+static int parse_line_mode(char *buf, struct hda_bus *bus)
+{
+	int i;
+	for (i = 0; i < ARRAY_SIZE(patch_items); i++) {
+		if (!patch_items[i].tag)
+			continue;
+		if (strmatch(buf, patch_items[i].tag))
+			return i;
+	}
+	return LINE_MODE_NONE;
+}
+
+/* copy one line from the buffer in fw, and update the fields in fw
+ * return zero if it reaches to the end of the buffer, or non-zero
+ * if successfully copied a line
+ *
+ * the spaces at the beginning and the end of the line are stripped
+ */
+static int get_line_from_fw(char *buf, int size, struct firmware *fw)
+{
+	int len;
+	const char *p = fw->data;
+	while (isspace(*p) && fw->size) {
+		p++;
+		fw->size--;
+	}
+	if (!fw->size)
+		return 0;
+	if (size < fw->size)
+		size = fw->size;
+
+	for (len = 0; len < fw->size; len++) {
+		if (!*p)
+			break;
+		if (*p == '\n') {
+			p++;
+			len++;
+			break;
+		}
+		if (len < size)
+			*buf++ = *p++;
+	}
+	*buf = 0;
+	fw->size -= len;
+	fw->data = p;
+	remove_trail_spaces(buf);
+	return 1;
+}
+
+/*
+ * load a "patch" firmware file and parse it
+ */
+int snd_hda_load_patch(struct hda_bus *bus, const char *patch)
+{
+	int err;
+	const struct firmware *fw;
+	struct firmware tmp;
+	char buf[128];
+	struct hda_codec *codec;
+	int line_mode;
+	struct device *dev = bus->card->dev;
+
+	if (snd_BUG_ON(!dev))
+		return -ENODEV;
+	err = request_firmware(&fw, patch, dev);
+	if (err < 0) {
+		printk(KERN_ERR "hda-codec: Cannot load the patch '%s'\n",
+		       patch);
+		return err;
+	}
+
+	tmp = *fw;
+	line_mode = LINE_MODE_NONE;
+	codec = NULL;
+	while (get_line_from_fw(buf, sizeof(buf) - 1, &tmp)) {
+		if (!*buf || *buf == '#' || *buf == '\n')
+			continue;
+		if (*buf == '[')
+			line_mode = parse_line_mode(buf, bus);
+		else if (patch_items[line_mode].parser)
+			patch_items[line_mode].parser(buf, bus, &codec);
+	}
+	release_firmware(fw);
+	return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_load_patch);
+#endif /* CONFIG_SND_HDA_PATCH_LOADER */

+ 66 - 8
sound/pci/hda/hda_intel.c

@@ -61,6 +61,9 @@ static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
 static int probe_only[SNDRV_CARDS];
 static int probe_only[SNDRV_CARDS];
 static int single_cmd;
 static int single_cmd;
 static int enable_msi;
 static int enable_msi;
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+static char *patch[SNDRV_CARDS];
+#endif
 
 
 module_param_array(index, int, NULL, 0444);
 module_param_array(index, int, NULL, 0444);
 MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
 MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
@@ -84,6 +87,10 @@ MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs "
 		 "(for debugging only).");
 		 "(for debugging only).");
 module_param(enable_msi, int, 0444);
 module_param(enable_msi, int, 0444);
 MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)");
 MODULE_PARM_DESC(enable_msi, "Enable Message Signaled Interrupt (MSI)");
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+module_param_array(patch, charp, NULL, 0444);
+MODULE_PARM_DESC(patch, "Patch file for Intel HD audio interface.");
+#endif
 
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
 static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
@@ -1331,8 +1338,7 @@ static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] __devinitdata = {
 	[AZX_DRIVER_TERA] = 1,
 	[AZX_DRIVER_TERA] = 1,
 };
 };
 
 
-static int __devinit azx_codec_create(struct azx *chip, const char *model,
-				      int no_init)
+static int __devinit azx_codec_create(struct azx *chip, const char *model)
 {
 {
 	struct hda_bus_template bus_temp;
 	struct hda_bus_template bus_temp;
 	int c, codecs, err;
 	int c, codecs, err;
@@ -1391,7 +1397,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
 	for (c = 0; c < max_slots; c++) {
 	for (c = 0; c < max_slots; c++) {
 		if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) {
 		if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) {
 			struct hda_codec *codec;
 			struct hda_codec *codec;
-			err = snd_hda_codec_new(chip->bus, c, !no_init, &codec);
+			err = snd_hda_codec_new(chip->bus, c, &codec);
 			if (err < 0)
 			if (err < 0)
 				continue;
 				continue;
 			codecs++;
 			codecs++;
@@ -1401,7 +1407,16 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
 		snd_printk(KERN_ERR SFX "no codecs initialized\n");
 		snd_printk(KERN_ERR SFX "no codecs initialized\n");
 		return -ENXIO;
 		return -ENXIO;
 	}
 	}
+	return 0;
+}
 
 
+/* configure each codec instance */
+static int __devinit azx_codec_configure(struct azx *chip)
+{
+	struct hda_codec *codec;
+	list_for_each_entry(codec, &chip->bus->codec_list, list) {
+		snd_hda_codec_configure(codec);
+	}
 	return 0;
 	return 0;
 }
 }
 
 
@@ -2284,6 +2299,30 @@ static void __devinit check_probe_mask(struct azx *chip, int dev)
 	}
 	}
 }
 }
 
 
+/*
+ * white-list for enable_msi
+ */
+static struct snd_pci_quirk msi_white_list[] __devinitdata = {
+	SND_PCI_QUIRK(0x103c, 0x3607, "HP Compa CQ40", 1),
+	{}
+};
+
+static void __devinit check_msi(struct azx *chip)
+{
+	const struct snd_pci_quirk *q;
+
+	chip->msi = enable_msi;
+	if (chip->msi)
+		return;
+	q = snd_pci_quirk_lookup(chip->pci, msi_white_list);
+	if (q) {
+		printk(KERN_INFO
+		       "hda_intel: msi for device %04x:%04x set to %d\n",
+		       q->subvendor, q->subdevice, q->value);
+		chip->msi = q->value;
+	}
+}
+
 
 
 /*
 /*
  * constructor
  * constructor
@@ -2318,7 +2357,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
 	chip->pci = pci;
 	chip->pci = pci;
 	chip->irq = -1;
 	chip->irq = -1;
 	chip->driver_type = driver_type;
 	chip->driver_type = driver_type;
-	chip->msi = enable_msi;
+	check_msi(chip);
 	chip->dev_index = dev;
 	chip->dev_index = dev;
 	INIT_WORK(&chip->irq_pending_work, azx_irq_pending_work);
 	INIT_WORK(&chip->irq_pending_work, azx_irq_pending_work);
 
 
@@ -2526,15 +2565,32 @@ static int __devinit azx_probe(struct pci_dev *pci,
 		return err;
 		return err;
 	}
 	}
 
 
+	/* set this here since it's referred in snd_hda_load_patch() */
+	snd_card_set_dev(card, &pci->dev);
+
 	err = azx_create(card, pci, dev, pci_id->driver_data, &chip);
 	err = azx_create(card, pci, dev, pci_id->driver_data, &chip);
 	if (err < 0)
 	if (err < 0)
 		goto out_free;
 		goto out_free;
 	card->private_data = chip;
 	card->private_data = chip;
 
 
 	/* create codec instances */
 	/* create codec instances */
-	err = azx_codec_create(chip, model[dev], probe_only[dev]);
+	err = azx_codec_create(chip, model[dev]);
 	if (err < 0)
 	if (err < 0)
 		goto out_free;
 		goto out_free;
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+	if (patch[dev]) {
+		snd_printk(KERN_ERR SFX "Applying patch firmware '%s'\n",
+			   patch[dev]);
+		err = snd_hda_load_patch(chip->bus, patch[dev]);
+		if (err < 0)
+			goto out_free;
+	}
+#endif
+	if (!probe_only[dev]) {
+		err = azx_codec_configure(chip);
+		if (err < 0)
+			goto out_free;
+	}
 
 
 	/* create PCM streams */
 	/* create PCM streams */
 	err = snd_hda_build_pcms(chip->bus);
 	err = snd_hda_build_pcms(chip->bus);
@@ -2546,8 +2602,6 @@ static int __devinit azx_probe(struct pci_dev *pci,
 	if (err < 0)
 	if (err < 0)
 		goto out_free;
 		goto out_free;
 
 
-	snd_card_set_dev(card, &pci->dev);
-
 	err = snd_card_register(card);
 	err = snd_card_register(card);
 	if (err < 0)
 	if (err < 0)
 		goto out_free;
 		goto out_free;
@@ -2649,11 +2703,15 @@ static struct pci_device_id azx_ids[] = {
 	/* this entry seems still valid -- i.e. without emu20kx chip */
 	/* this entry seems still valid -- i.e. without emu20kx chip */
 	{ PCI_DEVICE(0x1102, 0x0009), .driver_data = AZX_DRIVER_GENERIC },
 	{ PCI_DEVICE(0x1102, 0x0009), .driver_data = AZX_DRIVER_GENERIC },
 #endif
 #endif
-	/* AMD Generic, PCI class code and Vendor ID for HD Audio */
+	/* AMD/ATI Generic, PCI class code and Vendor ID for HD Audio */
 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID),
 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID),
 	  .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
 	  .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
 	  .class_mask = 0xffffff,
 	  .class_mask = 0xffffff,
 	  .driver_data = AZX_DRIVER_GENERIC },
 	  .driver_data = AZX_DRIVER_GENERIC },
+	{ PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_ANY_ID),
+	  .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
+	  .class_mask = 0xffffff,
+	  .driver_data = AZX_DRIVER_GENERIC },
 	{ 0, }
 	{ 0, }
 };
 };
 MODULE_DEVICE_TABLE(pci, azx_ids);
 MODULE_DEVICE_TABLE(pci, azx_ids);

+ 13 - 1
sound/pci/hda/hda_local.h

@@ -99,7 +99,6 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
 int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
 int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
 			unsigned int *tlv, const char **slaves);
 			unsigned int *tlv, const char **slaves);
 int snd_hda_codec_reset(struct hda_codec *codec);
 int snd_hda_codec_reset(struct hda_codec *codec);
-int snd_hda_codec_configure(struct hda_codec *codec);
 
 
 /* amp value bits */
 /* amp value bits */
 #define HDA_AMP_MUTE	0x80
 #define HDA_AMP_MUTE	0x80
@@ -408,6 +407,19 @@ static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid)
 	return codec->wcaps[nid - codec->start_nid];
 	return codec->wcaps[nid - codec->start_nid];
 }
 }
 
 
+/* get the widget type from widget capability bits */
+#define get_wcaps_type(wcaps) (((wcaps) & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT)
+
+static inline unsigned int get_wcaps_channels(u32 wcaps)
+{
+	unsigned int chans;
+
+	chans = (wcaps & AC_WCAP_CHAN_CNT_EXT) >> 13;
+	chans = ((chans << 1) | 1) + 1;
+
+	return chans;
+}
+
 u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction);
 u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction);
 int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
 int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
 			      unsigned int caps);
 			      unsigned int caps);

+ 2 - 5
sound/pci/hda/hda_proc.c

@@ -508,17 +508,14 @@ static void print_codec_info(struct snd_info_entry *entry,
 		unsigned int wid_caps =
 		unsigned int wid_caps =
 			snd_hda_param_read(codec, nid,
 			snd_hda_param_read(codec, nid,
 					   AC_PAR_AUDIO_WIDGET_CAP);
 					   AC_PAR_AUDIO_WIDGET_CAP);
-		unsigned int wid_type =
-			(wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+		unsigned int wid_type = get_wcaps_type(wid_caps);
 		hda_nid_t conn[HDA_MAX_CONNECTIONS];
 		hda_nid_t conn[HDA_MAX_CONNECTIONS];
 		int conn_len = 0;
 		int conn_len = 0;
 
 
 		snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid,
 		snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid,
 			    get_wid_type_name(wid_type), wid_caps);
 			    get_wid_type_name(wid_type), wid_caps);
 		if (wid_caps & AC_WCAP_STEREO) {
 		if (wid_caps & AC_WCAP_STEREO) {
-			unsigned int chans;
-			chans = (wid_caps & AC_WCAP_CHAN_CNT_EXT) >> 13;
-			chans = ((chans << 1) | 1) + 1;
+			unsigned int chans = get_wcaps_channels(wid_caps);
 			if (chans == 2)
 			if (chans == 2)
 				snd_iprintf(buffer, " Stereo");
 				snd_iprintf(buffer, " Stereo");
 			else
 			else

+ 90 - 41
sound/pci/hda/patch_analog.c

@@ -2982,7 +2982,8 @@ static int patch_ad1988(struct hda_codec *codec)
 	board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
 	board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
 						  ad1988_models, ad1988_cfg_tbl);
 						  ad1988_models, ad1988_cfg_tbl);
 	if (board_config < 0) {
 	if (board_config < 0) {
-		printk(KERN_INFO "hda_codec: Unknown model for AD1988, trying auto-probe from BIOS...\n");
+		printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+		       codec->chip_name);
 		board_config = AD1988_AUTO;
 		board_config = AD1988_AUTO;
 	}
 	}
 
 
@@ -3702,19 +3703,29 @@ static struct hda_amp_list ad1884a_loopbacks[] = {
  * Port F: Internal speakers
  * Port F: Internal speakers
  */
  */
 
 
-static struct hda_input_mux ad1884a_laptop_capture_source = {
-	.num_items = 4,
-	.items = {
-		{ "Mic", 0x0 },		/* port-B */
-		{ "Internal Mic", 0x1 }, /* port-C */
-		{ "Dock Mic", 0x4 },	/* port-E */
-		{ "Mix", 0x3 },
-	},
-};
+static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+	int mute = (!ucontrol->value.integer.value[0] &&
+		    !ucontrol->value.integer.value[1]);
+	/* toggle GPIO1 according to the mute state */
+	snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+			    mute ? 0x02 : 0x0);
+	return ret;
+}
 
 
 static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
 static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
 	HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Master Playback Switch",
+		.info = snd_hda_mixer_amp_switch_info,
+		.get = snd_hda_mixer_amp_switch_get,
+		.put = ad1884a_mobile_master_sw_put,
+		.private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
+	},
 	HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
 	HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
 	HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
 	HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
@@ -3729,36 +3740,9 @@ static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
 	HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		/* The multiple "Capture Source" controls confuse alsamixer
-		 * So call somewhat different..
-		 */
-		/* .name = "Capture Source", */
-		.name = "Input Source",
-		.count = 2,
-		.info = ad198x_mux_enum_info,
-		.get = ad198x_mux_enum_get,
-		.put = ad198x_mux_enum_put,
-	},
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
-static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol,
-				        struct snd_ctl_elem_value *ucontrol)
-{
-	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
-	int mute = (!ucontrol->value.integer.value[0] &&
-		    !ucontrol->value.integer.value[1]);
-	/* toggle GPIO1 according to the mute state */
-	snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
-			    mute ? 0x02 : 0x0);
-	return ret;
-}
-
 static struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
 static struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
 	HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
 	/*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
 	/*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
@@ -3828,6 +3812,63 @@ static int ad1884a_hp_init(struct hda_codec *codec)
 	return 0;
 	return 0;
 }
 }
 
 
+/* mute internal speaker if HP or docking HP is plugged */
+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;
+	}
+	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,
+			    present ? 0x00 : 0x02);
+}
+
+/* switch to external mic if plugged */
+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)
+		idx = 0;
+	else if (snd_hda_codec_read(codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0) &
+		 AC_PINSENSE_PRESENCE)
+		idx = 4;
+	else
+		idx = 1;
+	snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx);
+}
+
+/* unsolicited event for HP jack sensing */
+static void ad1884a_laptop_unsol_event(struct hda_codec *codec,
+				       unsigned int res)
+{
+	switch (res >> 26) {
+	case AD1884A_HP_EVENT:
+		ad1884a_laptop_automute(codec);
+		break;
+	case AD1884A_MIC_EVENT:
+		ad1884a_laptop_automic(codec);
+		break;
+	}
+}
+
+/* initialize jack-sensing, too */
+static int ad1884a_laptop_init(struct hda_codec *codec)
+{
+	ad198x_init(codec);
+	ad1884a_laptop_automute(codec);
+	ad1884a_laptop_automic(codec);
+	return 0;
+}
+
 /* additional verbs for laptop model */
 /* additional verbs for laptop model */
 static struct hda_verb ad1884a_laptop_verbs[] = {
 static struct hda_verb ad1884a_laptop_verbs[] = {
 	/* Port-A (HP) pin - always unmuted */
 	/* Port-A (HP) pin - always unmuted */
@@ -3844,11 +3885,19 @@ static struct hda_verb ad1884a_laptop_verbs[] = {
 	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
 	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
+	/* Port-D (docking line-out) pin - default unmuted */
+	{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 	/* analog mix */
 	/* analog mix */
 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
 	/* unsolicited event for pin-sense */
 	/* unsolicited event for pin-sense */
 	{0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
 	{0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
+	{0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
 	{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
 	{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
+	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
+	/* allow to touch GPIO1 (for mute control) */
+	{0x01, AC_VERB_SET_GPIO_MASK, 0x02},
+	{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
+	{0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
@@ -4008,6 +4057,7 @@ static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
 	SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP),
 	SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP),
 	SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
 	SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
 	SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
 	SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
+	SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE),
 	SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
 	SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
 	{}
 	{}
 };
 };
@@ -4057,9 +4107,8 @@ static int patch_ad1884a(struct hda_codec *codec)
 		spec->mixers[0] = ad1884a_laptop_mixers;
 		spec->mixers[0] = ad1884a_laptop_mixers;
 		spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
 		spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
 		spec->multiout.dig_out_nid = 0;
 		spec->multiout.dig_out_nid = 0;
-		spec->input_mux = &ad1884a_laptop_capture_source;
-		codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
-		codec->patch_ops.init = ad1884a_hp_init;
+		codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event;
+		codec->patch_ops.init = ad1884a_laptop_init;
 		/* set the upper-limit for mixer amp to 0dB for avoiding the
 		/* set the upper-limit for mixer amp to 0dB for avoiding the
 		 * possible damage by overloading
 		 * possible damage by overloading
 		 */
 		 */

+ 1 - 2
sound/pci/hda/patch_atihdmi.c

@@ -141,8 +141,7 @@ static int atihdmi_build_pcms(struct hda_codec *codec)
 	/* FIXME: we must check ELD and change the PCM parameters dynamically
 	/* FIXME: we must check ELD and change the PCM parameters dynamically
 	 */
 	 */
 	chans = get_wcaps(codec, CVT_NID);
 	chans = get_wcaps(codec, CVT_NID);
-	chans = (chans & AC_WCAP_CHAN_CNT_EXT) >> 13;
-	chans = ((chans << 1) | 1) + 1;
+	chans = get_wcaps_channels(chans);
 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
 
 
 	return 0;
 	return 0;

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

@@ -459,8 +459,7 @@ static void parse_input(struct hda_codec *codec)
 	nid = codec->start_nid;
 	nid = codec->start_nid;
 	for (i = 0; i < codec->num_nodes; i++, nid++) {
 	for (i = 0; i < codec->num_nodes; i++, nid++) {
 		unsigned int wcaps = get_wcaps(codec, nid);
 		unsigned int wcaps = get_wcaps(codec, nid);
-		unsigned int type = (wcaps & AC_WCAP_TYPE) >>
-			AC_WCAP_TYPE_SHIFT;
+		unsigned int type = get_wcaps_type(wcaps);
 		if (type != AC_WID_AUD_IN)
 		if (type != AC_WID_AUD_IN)
 			continue;
 			continue;
 		if (snd_hda_get_connections(codec, nid, &pin, 1) != 1)
 		if (snd_hda_get_connections(codec, nid, &pin, 1) != 1)

+ 1194 - 0
sound/pci/hda/patch_cirrus.c

@@ -0,0 +1,1194 @@
+/*
+ * HD audio interface patch for Cirrus Logic CS420x chip
+ *
+ * Copyright (c) 2009 Takashi Iwai <tiwai@suse.de>
+ *
+ *  This driver is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This driver is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+/*
+ */
+
+struct cs_spec {
+	int board_config;
+	struct auto_pin_cfg autocfg;
+	struct hda_multi_out multiout;
+	struct snd_kcontrol *vmaster_sw;
+	struct snd_kcontrol *vmaster_vol;
+
+	hda_nid_t dac_nid[AUTO_CFG_MAX_OUTS];
+	hda_nid_t slave_dig_outs[2];
+
+	unsigned int input_idx[AUTO_PIN_LAST];
+	unsigned int capsrc_idx[AUTO_PIN_LAST];
+	hda_nid_t adc_nid[AUTO_PIN_LAST];
+	unsigned int adc_idx[AUTO_PIN_LAST];
+	unsigned int num_inputs;
+	unsigned int cur_input;
+	unsigned int automic_idx;
+	hda_nid_t cur_adc;
+	unsigned int cur_adc_stream_tag;
+	unsigned int cur_adc_format;
+	hda_nid_t dig_in;
+
+	struct hda_bind_ctls *capture_bind[2];
+
+	unsigned int gpio_mask;
+	unsigned int gpio_dir;
+	unsigned int gpio_data;
+
+	struct hda_pcm pcm_rec[2];	/* PCM information */
+
+	unsigned int hp_detect:1;
+	unsigned int mic_detect:1;
+};
+
+/* available models */
+enum {
+	CS420X_MBP55,
+	CS420X_AUTO,
+	CS420X_MODELS
+};
+
+/* Vendor-specific processing widget */
+#define CS420X_VENDOR_NID	0x11
+#define CS_DIG_OUT1_PIN_NID	0x10
+#define CS_DIG_OUT2_PIN_NID	0x15
+#define CS_DMIC1_PIN_NID	0x12
+#define CS_DMIC2_PIN_NID	0x0e
+
+/* coef indices */
+#define IDX_SPDIF_STAT		0x0000
+#define IDX_SPDIF_CTL		0x0001
+#define IDX_ADC_CFG		0x0002
+/* SZC bitmask, 4 modes below:
+ * 0 = immediate,
+ * 1 = digital immediate, analog zero-cross
+ * 2 = digtail & analog soft-ramp
+ * 3 = digital soft-ramp, analog zero-cross
+ */
+#define   CS_COEF_ADC_SZC_MASK		(3 << 0)
+#define   CS_COEF_ADC_MIC_SZC_MODE	(3 << 0) /* SZC setup for mic */
+#define   CS_COEF_ADC_LI_SZC_MODE	(3 << 0) /* SZC setup for line-in */
+/* PGA mode: 0 = differential, 1 = signle-ended */
+#define   CS_COEF_ADC_MIC_PGA_MODE	(1 << 5) /* PGA setup for mic */
+#define   CS_COEF_ADC_LI_PGA_MODE	(1 << 6) /* PGA setup for line-in */
+#define IDX_DAC_CFG		0x0003
+/* SZC bitmask, 4 modes below:
+ * 0 = Immediate
+ * 1 = zero-cross
+ * 2 = soft-ramp
+ * 3 = soft-ramp on zero-cross
+ */
+#define   CS_COEF_DAC_HP_SZC_MODE	(3 << 0) /* nid 0x02 */
+#define   CS_COEF_DAC_LO_SZC_MODE	(3 << 2) /* nid 0x03 */
+#define   CS_COEF_DAC_SPK_SZC_MODE	(3 << 4) /* nid 0x04 */
+
+#define IDX_BEEP_CFG		0x0004
+/* 0x0008 - test reg key */
+/* 0x0009 - 0x0014 -> 12 test regs */
+/* 0x0015 - visibility reg */
+
+
+static inline int cs_vendor_coef_get(struct hda_codec *codec, unsigned int idx)
+{
+	snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0,
+			    AC_VERB_SET_COEF_INDEX, idx);
+	return snd_hda_codec_read(codec, CS420X_VENDOR_NID, 0,
+				  AC_VERB_GET_PROC_COEF, 0);
+}
+
+static inline void cs_vendor_coef_set(struct hda_codec *codec, unsigned int idx,
+				      unsigned int coef)
+{
+	snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0,
+			    AC_VERB_SET_COEF_INDEX, idx);
+	snd_hda_codec_write(codec, CS420X_VENDOR_NID, 0,
+			    AC_VERB_SET_PROC_COEF, coef);
+}
+
+
+#define HP_EVENT	1
+#define MIC_EVENT	2
+
+/*
+ * PCM callbacks
+ */
+static int cs_playback_pcm_open(struct hda_pcm_stream *hinfo,
+				struct hda_codec *codec,
+				struct snd_pcm_substream *substream)
+{
+	struct cs_spec *spec = codec->spec;
+	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+					     hinfo);
+}
+
+static int cs_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+				   struct hda_codec *codec,
+				   unsigned int stream_tag,
+				   unsigned int format,
+				   struct snd_pcm_substream *substream)
+{
+	struct cs_spec *spec = codec->spec;
+	return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
+						stream_tag, format, substream);
+}
+
+static int cs_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+				   struct hda_codec *codec,
+				   struct snd_pcm_substream *substream)
+{
+	struct cs_spec *spec = codec->spec;
+	return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+}
+
+/*
+ * Digital out
+ */
+static int cs_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+				    struct hda_codec *codec,
+				    struct snd_pcm_substream *substream)
+{
+	struct cs_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int cs_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+				     struct hda_codec *codec,
+				     struct snd_pcm_substream *substream)
+{
+	struct cs_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static int cs_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+				       struct hda_codec *codec,
+				       unsigned int stream_tag,
+				       unsigned int format,
+				       struct snd_pcm_substream *substream)
+{
+	struct cs_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
+					     format, substream);
+}
+
+static int cs_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+				       struct hda_codec *codec,
+				       struct snd_pcm_substream *substream)
+{
+	struct cs_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
+}
+
+/*
+ * Analog capture
+ */
+static int cs_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+				  struct hda_codec *codec,
+				  unsigned int stream_tag,
+				  unsigned int format,
+				  struct snd_pcm_substream *substream)
+{
+	struct cs_spec *spec = codec->spec;
+	spec->cur_adc = spec->adc_nid[spec->cur_input];
+	spec->cur_adc_stream_tag = stream_tag;
+	spec->cur_adc_format = format;
+	snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
+	return 0;
+}
+
+static int cs_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+				  struct hda_codec *codec,
+				  struct snd_pcm_substream *substream)
+{
+	struct cs_spec *spec = codec->spec;
+	snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
+	spec->cur_adc = 0;
+	return 0;
+}
+
+/*
+ */
+static struct hda_pcm_stream cs_pcm_analog_playback = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+	.ops = {
+		.open = cs_playback_pcm_open,
+		.prepare = cs_playback_pcm_prepare,
+		.cleanup = cs_playback_pcm_cleanup
+	},
+};
+
+static struct hda_pcm_stream cs_pcm_analog_capture = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+	.ops = {
+		.prepare = cs_capture_pcm_prepare,
+		.cleanup = cs_capture_pcm_cleanup
+	},
+};
+
+static struct hda_pcm_stream cs_pcm_digital_playback = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+	.ops = {
+		.open = cs_dig_playback_pcm_open,
+		.close = cs_dig_playback_pcm_close,
+		.prepare = cs_dig_playback_pcm_prepare,
+		.cleanup = cs_dig_playback_pcm_cleanup
+	},
+};
+
+static struct hda_pcm_stream cs_pcm_digital_capture = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+};
+
+static int cs_build_pcms(struct hda_codec *codec)
+{
+	struct cs_spec *spec = codec->spec;
+	struct hda_pcm *info = spec->pcm_rec;
+
+	codec->pcm_info = info;
+	codec->num_pcms = 0;
+
+	info->name = "Cirrus Analog";
+	info->stream[SNDRV_PCM_STREAM_PLAYBACK] = cs_pcm_analog_playback;
+	info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dac_nid[0];
+	info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
+		spec->multiout.max_channels;
+	info->stream[SNDRV_PCM_STREAM_CAPTURE] = cs_pcm_analog_capture;
+	info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
+		spec->adc_nid[spec->cur_input];
+	codec->num_pcms++;
+
+	if (!spec->multiout.dig_out_nid && !spec->dig_in)
+		return 0;
+
+	info++;
+	info->name = "Cirrus Digital";
+	info->pcm_type = spec->autocfg.dig_out_type[0];
+	if (!info->pcm_type)
+		info->pcm_type = HDA_PCM_TYPE_SPDIF;
+	if (spec->multiout.dig_out_nid) {
+		info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+			cs_pcm_digital_playback;
+		info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
+			spec->multiout.dig_out_nid;
+	}
+	if (spec->dig_in) {
+		info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+			cs_pcm_digital_capture;
+		info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in;
+	}
+	codec->num_pcms++;
+
+	return 0;
+}
+
+/*
+ * parse codec topology
+ */
+
+static hda_nid_t get_dac(struct hda_codec *codec, hda_nid_t pin)
+{
+	hda_nid_t dac;
+	if (!pin)
+		return 0;
+	if (snd_hda_get_connections(codec, pin, &dac, 1) != 1)
+		return 0;
+	return dac;
+}
+
+static int is_ext_mic(struct hda_codec *codec, unsigned int idx)
+{
+	struct cs_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	hda_nid_t pin = cfg->input_pins[idx];
+	unsigned int val = snd_hda_query_pin_caps(codec, pin);
+	if (!(val & AC_PINCAP_PRES_DETECT))
+		return 0;
+	val = snd_hda_codec_get_pincfg(codec, pin);
+	return (get_defcfg_connect(val) == AC_JACK_PORT_COMPLEX);
+}
+
+static hda_nid_t get_adc(struct hda_codec *codec, hda_nid_t pin,
+			 unsigned int *idxp)
+{
+	int i;
+	hda_nid_t nid;
+
+	nid = codec->start_nid;
+	for (i = 0; i < codec->num_nodes; i++, nid++) {
+		hda_nid_t pins[2];
+		unsigned int type;
+		int j, nums;
+		type = (get_wcaps(codec, nid) & AC_WCAP_TYPE)
+			>> AC_WCAP_TYPE_SHIFT;
+		if (type != AC_WID_AUD_IN)
+			continue;
+		nums = snd_hda_get_connections(codec, nid, pins,
+					       ARRAY_SIZE(pins));
+		if (nums <= 0)
+			continue;
+		for (j = 0; j < nums; j++) {
+			if (pins[j] == pin) {
+				*idxp = j;
+				return nid;
+			}
+		}
+	}
+	return 0;
+}
+
+static int is_active_pin(struct hda_codec *codec, hda_nid_t nid)
+{
+	unsigned int val;
+	val = snd_hda_codec_get_pincfg(codec, nid);
+	return (get_defcfg_connect(val) != AC_JACK_PORT_NONE);
+}
+
+static int parse_output(struct hda_codec *codec)
+{
+	struct cs_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	int i, extra_nids;
+	hda_nid_t dac;
+
+	for (i = 0; i < cfg->line_outs; i++) {
+		dac = get_dac(codec, cfg->line_out_pins[i]);
+		if (!dac)
+			break;
+		spec->dac_nid[i] = dac;
+	}
+	spec->multiout.num_dacs = i;
+	spec->multiout.dac_nids = spec->dac_nid;
+	spec->multiout.max_channels = i * 2;
+
+	/* add HP and speakers */
+	extra_nids = 0;
+	for (i = 0; i < cfg->hp_outs; i++) {
+		dac = get_dac(codec, cfg->hp_pins[i]);
+		if (!dac)
+			break;
+		if (!i)
+			spec->multiout.hp_nid = dac;
+		else
+			spec->multiout.extra_out_nid[extra_nids++] = dac;
+	}
+	for (i = 0; i < cfg->speaker_outs; i++) {
+		dac = get_dac(codec, cfg->speaker_pins[i]);
+		if (!dac)
+			break;
+		spec->multiout.extra_out_nid[extra_nids++] = dac;
+	}
+
+	if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
+		cfg->speaker_outs = cfg->line_outs;
+		memcpy(cfg->speaker_pins, cfg->line_out_pins,
+		       sizeof(cfg->speaker_pins));
+		cfg->line_outs = 0;
+	}
+
+	return 0;
+}
+
+static int parse_input(struct hda_codec *codec)
+{
+	struct cs_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	int i;
+
+	for (i = 0; i < AUTO_PIN_LAST; i++) {
+		hda_nid_t pin = cfg->input_pins[i];
+		if (!pin)
+			continue;
+		spec->input_idx[spec->num_inputs] = i;
+		spec->capsrc_idx[i] = spec->num_inputs++;
+		spec->cur_input = i;
+		spec->adc_nid[i] = get_adc(codec, pin, &spec->adc_idx[i]);
+	}
+	if (!spec->num_inputs)
+		return 0;
+
+	/* check whether the automatic mic switch is available */
+	if (spec->num_inputs == 2 &&
+	    spec->adc_nid[AUTO_PIN_MIC] && spec->adc_nid[AUTO_PIN_FRONT_MIC]) {
+		if (is_ext_mic(codec, cfg->input_pins[AUTO_PIN_FRONT_MIC])) {
+			if (!is_ext_mic(codec, cfg->input_pins[AUTO_PIN_MIC])) {
+				spec->mic_detect = 1;
+				spec->automic_idx = AUTO_PIN_FRONT_MIC;
+			}
+		} else {
+			if (is_ext_mic(codec, cfg->input_pins[AUTO_PIN_MIC])) {
+				spec->mic_detect = 1;
+				spec->automic_idx = AUTO_PIN_MIC;
+			}
+		}
+	}
+	return 0;
+}
+
+
+static int parse_digital_output(struct hda_codec *codec)
+{
+	struct cs_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	hda_nid_t nid;
+
+	if (!cfg->dig_outs)
+		return 0;
+	if (snd_hda_get_connections(codec, cfg->dig_out_pins[0], &nid, 1) < 1)
+		return 0;
+	spec->multiout.dig_out_nid = nid;
+	spec->multiout.share_spdif = 1;
+	if (cfg->dig_outs > 1 &&
+	    snd_hda_get_connections(codec, cfg->dig_out_pins[1], &nid, 1) > 0) {
+		spec->slave_dig_outs[0] = nid;
+		codec->slave_dig_outs = spec->slave_dig_outs;
+	}
+	return 0;
+}
+
+static int parse_digital_input(struct hda_codec *codec)
+{
+	struct cs_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	int idx;
+
+	if (cfg->dig_in_pin)
+		spec->dig_in = get_adc(codec, cfg->dig_in_pin, &idx);
+	return 0;
+}
+
+/*
+ * create mixer controls
+ */
+
+static const char *dir_sfx[2] = { "Playback", "Capture" };
+
+static int add_mute(struct hda_codec *codec, const char *name, int index,
+		    unsigned int pval, int dir, struct snd_kcontrol **kctlp)
+{
+	char tmp[44];
+	struct snd_kcontrol_new knew =
+		HDA_CODEC_MUTE_IDX(tmp, index, 0, 0, HDA_OUTPUT);
+	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);
+}
+
+static int add_volume(struct hda_codec *codec, const char *name,
+		      int index, unsigned int pval, int dir,
+		      struct snd_kcontrol **kctlp)
+{
+	char tmp[32];
+	struct snd_kcontrol_new knew =
+		HDA_CODEC_VOLUME_IDX(tmp, index, 0, 0, HDA_OUTPUT);
+	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);
+}
+
+static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac)
+{
+	unsigned int caps;
+
+	/* set the upper-limit for mixer amp to 0dB */
+	caps = query_amp_caps(codec, dac, HDA_OUTPUT);
+	caps &= ~(0x7f << AC_AMPCAP_NUM_STEPS_SHIFT);
+	caps |= ((caps >> AC_AMPCAP_OFFSET_SHIFT) & 0x7f)
+		<< AC_AMPCAP_NUM_STEPS_SHIFT;
+	snd_hda_override_amp_caps(codec, dac, HDA_OUTPUT, caps);
+}
+
+static int add_vmaster(struct hda_codec *codec, hda_nid_t dac)
+{
+	struct cs_spec *spec = codec->spec;
+	unsigned int tlv[4];
+	int err;
+
+	spec->vmaster_sw =
+		snd_ctl_make_virtual_master("Master Playback Switch", NULL);
+	err = snd_hda_ctl_add(codec, 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);
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+static int add_output(struct hda_codec *codec, hda_nid_t dac, int idx,
+		      int num_ctls, int type)
+{
+	struct cs_spec *spec = codec->spec;
+	const char *name;
+	int err, index;
+	struct snd_kcontrol *kctl;
+	static char *speakers[] = {
+		"Front Speaker", "Surround Speaker", "Bass Speaker"
+	};
+	static char *line_outs[] = {
+		"Front Line-Out", "Surround Line-Out", "Bass Line-Out"
+	};
+
+	fix_volume_caps(codec, dac);
+	if (!spec->vmaster_sw) {
+		err = add_vmaster(codec, dac);
+		if (err < 0)
+			return err;
+	}
+
+	index = 0;
+	switch (type) {
+	case AUTO_PIN_HP_OUT:
+		name = "Headphone";
+		index = idx;
+		break;
+	case AUTO_PIN_SPEAKER_OUT:
+		if (num_ctls > 1)
+			name = speakers[idx];
+		else
+			name = "Speaker";
+		break;
+	default:
+		if (num_ctls > 1)
+			name = line_outs[idx];
+		else
+			name = "Line-Out";
+		break;
+	}
+
+	err = add_mute(codec, name, index,
+		       HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl);
+	if (err < 0)
+		return err;
+	err = snd_ctl_add_slave(spec->vmaster_sw, kctl);
+	if (err < 0)
+		return err;
+
+	err = add_volume(codec, name, index,
+			 HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl);
+	if (err < 0)
+		return err;
+	err = snd_ctl_add_slave(spec->vmaster_vol, kctl);
+	if (err < 0)
+		return err;
+
+	return 0;
+}		
+
+static int build_output(struct hda_codec *codec)
+{
+	struct cs_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	int i, err;
+
+	for (i = 0; i < cfg->line_outs; i++) {
+		err = add_output(codec, get_dac(codec, cfg->line_out_pins[i]),
+				 i, cfg->line_outs, cfg->line_out_type);
+		if (err < 0)
+			return err;
+	}
+	for (i = 0; i < cfg->hp_outs; i++) {
+		err = add_output(codec, get_dac(codec, cfg->hp_pins[i]),
+				 i, cfg->hp_outs, AUTO_PIN_HP_OUT);
+		if (err < 0)
+			return err;
+	}
+	for (i = 0; i < cfg->speaker_outs; i++) {
+		err = add_output(codec, get_dac(codec, cfg->speaker_pins[i]),
+				 i, cfg->speaker_outs, AUTO_PIN_SPEAKER_OUT);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+/*
+ */
+
+static struct snd_kcontrol_new cs_capture_ctls[] = {
+	HDA_BIND_SW("Capture Switch", 0),
+	HDA_BIND_VOL("Capture Volume", 0),
+};
+
+static int change_cur_input(struct hda_codec *codec, unsigned int idx,
+			    int force)
+{
+	struct cs_spec *spec = codec->spec;
+	
+	if (spec->cur_input == idx && !force)
+		return 0;
+	if (spec->cur_adc && spec->cur_adc != spec->adc_nid[idx]) {
+		/* stream is running, let's swap the current ADC */
+		snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
+		spec->cur_adc = spec->adc_nid[idx];
+		snd_hda_codec_setup_stream(codec, spec->cur_adc,
+					   spec->cur_adc_stream_tag, 0,
+					   spec->cur_adc_format);
+	}
+	snd_hda_codec_write(codec, spec->cur_adc, 0,
+			    AC_VERB_SET_CONNECT_SEL,
+			    spec->adc_idx[idx]);
+	spec->cur_input = idx;
+	return 1;
+}
+
+static int cs_capture_source_info(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_info *uinfo)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct cs_spec *spec = codec->spec;
+	unsigned int idx;
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = spec->num_inputs;
+	if (uinfo->value.enumerated.item >= spec->num_inputs)
+		uinfo->value.enumerated.item = spec->num_inputs - 1;
+	idx = spec->input_idx[uinfo->value.enumerated.item];
+	strcpy(uinfo->value.enumerated.name, auto_pin_cfg_labels[idx]);
+	return 0;
+}
+
+static int cs_capture_source_get(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct cs_spec *spec = codec->spec;
+	ucontrol->value.enumerated.item[0] = spec->capsrc_idx[spec->cur_input];
+	return 0;
+}
+
+static int cs_capture_source_put(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct cs_spec *spec = codec->spec;
+	unsigned int idx = ucontrol->value.enumerated.item[0];
+
+	if (idx >= spec->num_inputs)
+		return -EINVAL;
+	idx = spec->input_idx[idx];
+	return change_cur_input(codec, idx, 0);
+}
+
+static struct snd_kcontrol_new cs_capture_source = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Capture Source",
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.info = cs_capture_source_info,
+	.get = cs_capture_source_get,
+	.put = cs_capture_source_put,
+};
+
+static struct hda_bind_ctls *make_bind_capture(struct hda_codec *codec,
+					       struct hda_ctl_ops *ops)
+{
+	struct cs_spec *spec = codec->spec;
+	struct hda_bind_ctls *bind;
+	int i, n;
+
+	bind = kzalloc(sizeof(*bind) + sizeof(long) * (spec->num_inputs + 1),
+		       GFP_KERNEL);
+	if (!bind)
+		return NULL;
+	bind->ops = ops;
+	n = 0;
+	for (i = 0; i < AUTO_PIN_LAST; i++) {
+		if (!spec->adc_nid[i])
+			continue;
+		bind->values[n++] =
+			HDA_COMPOSE_AMP_VAL(spec->adc_nid[i], 3,
+					    spec->adc_idx[i], HDA_INPUT);
+	}
+	return bind;
+}
+
+static int build_input(struct hda_codec *codec)
+{
+	struct cs_spec *spec = codec->spec;
+	int i, err;
+
+	if (!spec->num_inputs)
+		return 0;
+
+	/* make bind-capture */
+	spec->capture_bind[0] = make_bind_capture(codec, &snd_hda_bind_sw);
+	spec->capture_bind[1] = make_bind_capture(codec, &snd_hda_bind_vol);
+	for (i = 0; i < 2; i++) {
+		struct snd_kcontrol *kctl;
+		if (!spec->capture_bind[i])
+			return -ENOMEM;
+		kctl = snd_ctl_new1(&cs_capture_ctls[i], codec);
+		if (!kctl)
+			return -ENOMEM;
+		kctl->private_value = (long)spec->capture_bind[i];
+		err = snd_hda_ctl_add(codec, kctl);
+		if (err < 0)
+			return err;
+	}
+	
+	if (spec->num_inputs > 1 && !spec->mic_detect) {
+		err = snd_hda_ctl_add(codec,
+				      snd_ctl_new1(&cs_capture_source, codec));
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+/*
+ */
+
+static int build_digital_output(struct hda_codec *codec)
+{
+	struct cs_spec *spec = codec->spec;
+	int err;
+
+	if (!spec->multiout.dig_out_nid)
+		return 0;
+
+	err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
+	if (err < 0)
+		return err;
+	err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+static int build_digital_input(struct hda_codec *codec)
+{
+	struct cs_spec *spec = codec->spec;
+	if (spec->dig_in)
+		return snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
+	return 0;
+}
+
+/*
+ * auto-mute and auto-mic switching
+ */
+
+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;
+	hda_nid_t nid;
+	int i;
+
+	hp_present = 0;
+	for (i = 0; i < cfg->hp_outs; i++) {
+		nid = cfg->hp_pins[i];
+		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;
+		if (hp_present)
+			break;
+	}
+	for (i = 0; i < cfg->speaker_outs; i++) {
+		nid = cfg->speaker_pins[i];
+		snd_hda_codec_write(codec, nid, 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL,
+				    hp_present ? 0 : PIN_OUT);
+	}
+	if (spec->board_config == CS420X_MBP55) {
+		unsigned int gpio = hp_present ? 0x02 : 0x08;
+		snd_hda_codec_write(codec, 0x01, 0,
+				    AC_VERB_SET_GPIO_DATA, gpio);
+	}
+}
+
+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;
+	
+	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)
+		change_cur_input(codec, spec->automic_idx, 0);
+	else {
+		unsigned int imic = (spec->automic_idx == AUTO_PIN_MIC) ?
+			AUTO_PIN_FRONT_MIC : AUTO_PIN_MIC;
+		change_cur_input(codec, imic, 0);
+	}
+}
+
+/*
+ */
+
+static void init_output(struct hda_codec *codec)
+{
+	struct cs_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	int i;
+
+	/* mute first */
+	for (i = 0; i < spec->multiout.num_dacs; i++)
+		snd_hda_codec_write(codec, spec->multiout.dac_nids[i], 0,
+				    AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+	if (spec->multiout.hp_nid)
+		snd_hda_codec_write(codec, spec->multiout.hp_nid, 0,
+				    AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+	for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) {
+		if (!spec->multiout.extra_out_nid[i])
+			break;
+		snd_hda_codec_write(codec, spec->multiout.extra_out_nid[i], 0,
+				    AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+	}
+
+	/* set appropriate pin controls */
+	for (i = 0; i < cfg->line_outs; i++)
+		snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+	for (i = 0; i < cfg->hp_outs; i++) {
+		hda_nid_t nid = cfg->hp_pins[i];
+		snd_hda_codec_write(codec, nid, 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
+		if (!cfg->speaker_outs)
+			continue;
+		if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) {
+			snd_hda_codec_write(codec, nid, 0,
+					    AC_VERB_SET_UNSOLICITED_ENABLE,
+					    AC_USRSP_EN | HP_EVENT);
+			spec->hp_detect = 1;
+		}
+	}
+	for (i = 0; i < cfg->speaker_outs; i++)
+		snd_hda_codec_write(codec, cfg->speaker_pins[i], 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+	if (spec->hp_detect)
+		cs_automute(codec);
+}
+
+static void init_input(struct hda_codec *codec)
+{
+	struct cs_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	unsigned int coef;
+	int i;
+
+	for (i = 0; i < AUTO_PIN_LAST; i++) {
+		unsigned int ctl;
+		hda_nid_t pin = cfg->input_pins[i];
+		if (!pin || !spec->adc_nid[i])
+			continue;
+		/* set appropriate pin control and mute first */
+		ctl = PIN_IN;
+		if (i <= AUTO_PIN_FRONT_MIC) {
+			unsigned int caps = snd_hda_query_pin_caps(codec, pin);
+			caps >>= AC_PINCAP_VREF_SHIFT;
+			if (caps & AC_PINCAP_VREF_80)
+				ctl = PIN_VREF80;
+		}
+		snd_hda_codec_write(codec, pin, 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
+		snd_hda_codec_write(codec, spec->adc_nid[i], 0,
+				    AC_VERB_SET_AMP_GAIN_MUTE,
+				    AMP_IN_MUTE(spec->adc_idx[i]));
+		if (spec->mic_detect && spec->automic_idx == i)
+			snd_hda_codec_write(codec, pin, 0,
+					    AC_VERB_SET_UNSOLICITED_ENABLE,
+					    AC_USRSP_EN | MIC_EVENT);
+	}
+	change_cur_input(codec, spec->cur_input, 1);
+	if (spec->mic_detect)
+		cs_automic(codec);
+
+	coef = 0x000a; /* ADC1/2 - Digital and Analog Soft Ramp */
+	if (is_active_pin(codec, CS_DMIC2_PIN_NID))
+		coef |= 0x0500; /* DMIC2 enable 2 channels, disable GPIO1 */
+	if (is_active_pin(codec, CS_DMIC1_PIN_NID))
+		coef |= 0x1800; /* DMIC1 enable 2 channels, disable GPIO0 
+				 * No effect if SPDIF_OUT2 is slected in 
+				 * IDX_SPDIF_CTL.
+				  */
+	cs_vendor_coef_set(codec, IDX_ADC_CFG, coef);
+}
+
+static struct hda_verb cs_coef_init_verbs[] = {
+	{0x11, AC_VERB_SET_PROC_STATE, 1},
+	{0x11, AC_VERB_SET_COEF_INDEX, IDX_DAC_CFG},
+	{0x11, AC_VERB_SET_PROC_COEF,
+	 (0x002a /* DAC1/2/3 SZCMode Soft Ramp */
+	  | 0x0040 /* Mute DACs on FIFO error */
+	  | 0x1000 /* Enable DACs High Pass Filter */
+	  | 0x0400 /* Disable Coefficient Auto increment */
+	  )},
+	/* Beep */
+	{0x11, AC_VERB_SET_COEF_INDEX, IDX_DAC_CFG},
+	{0x11, AC_VERB_SET_PROC_COEF, 0x0007}, /* Enable Beep thru DAC1/2/3 */
+
+	{} /* terminator */
+};
+
+/* SPDIF setup */
+static void init_digital(struct hda_codec *codec)
+{
+	unsigned int coef;
+
+	coef = 0x0002; /* SRC_MUTE soft-mute on SPDIF (if no lock) */
+	coef |= 0x0008; /* Replace with mute on error */
+	if (is_active_pin(codec, CS_DIG_OUT2_PIN_NID))
+		coef |= 0x4000; /* RX to TX1 or TX2 Loopthru / SPDIF2
+				 * SPDIF_OUT2 is shared with GPIO1 and
+				 * DMIC_SDA2.
+				 */
+	cs_vendor_coef_set(codec, IDX_SPDIF_CTL, coef);
+}
+
+static int cs_init(struct hda_codec *codec)
+{
+	struct cs_spec *spec = codec->spec;
+
+	snd_hda_sequence_write(codec, cs_coef_init_verbs);
+
+	if (spec->gpio_mask) {
+		snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK,
+				    spec->gpio_mask);
+		snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DIRECTION,
+				    spec->gpio_dir);
+		snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+				    spec->gpio_data);
+	}
+
+	init_output(codec);
+	init_input(codec);
+	init_digital(codec);
+	return 0;
+}
+
+static int cs_build_controls(struct hda_codec *codec)
+{
+	int err;
+
+	err = build_output(codec);
+	if (err < 0)
+		return err;
+	err = build_input(codec);
+	if (err < 0)
+		return err;
+	err = build_digital_output(codec);
+	if (err < 0)
+		return err;
+	err = build_digital_input(codec);
+	if (err < 0)
+		return err;
+	return cs_init(codec);
+}
+
+static void cs_free(struct hda_codec *codec)
+{
+	struct cs_spec *spec = codec->spec;
+	kfree(spec->capture_bind[0]);
+	kfree(spec->capture_bind[1]);
+	kfree(codec->spec);
+}
+
+static void cs_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+	switch ((res >> 26) & 0x7f) {
+	case HP_EVENT:
+		cs_automute(codec);
+		break;
+	case MIC_EVENT:
+		cs_automic(codec);
+		break;
+	}
+}
+
+static struct hda_codec_ops cs_patch_ops = {
+	.build_controls = cs_build_controls,
+	.build_pcms = cs_build_pcms,
+	.init = cs_init,
+	.free = cs_free,
+	.unsol_event = cs_unsol_event,
+};
+
+static int cs_parse_auto_config(struct hda_codec *codec)
+{
+	struct cs_spec *spec = codec->spec;
+	int err;
+
+	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
+	if (err < 0)
+		return err;
+
+	err = parse_output(codec);
+	if (err < 0)
+		return err;
+	err = parse_input(codec);
+	if (err < 0)
+		return err;
+	err = parse_digital_output(codec);
+	if (err < 0)
+		return err;
+	err = parse_digital_input(codec);
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+static const char *cs420x_models[CS420X_MODELS] = {
+	[CS420X_MBP55] = "mbp55",
+	[CS420X_AUTO] = "auto",
+};
+
+
+static struct snd_pci_quirk cs420x_cfg_tbl[] = {
+	SND_PCI_QUIRK(0x10de, 0xcb79, "MacBookPro 5,5", CS420X_MBP55),
+	{} /* terminator */
+};
+
+struct cs_pincfg {
+	hda_nid_t nid;
+	u32 val;
+};
+
+static struct cs_pincfg mbp55_pincfgs[] = {
+	{ 0x09, 0x012b4030 },
+	{ 0x0a, 0x90100121 },
+	{ 0x0b, 0x90100120 },
+	{ 0x0c, 0x400000f0 },
+	{ 0x0d, 0x90a00110 },
+	{ 0x0e, 0x400000f0 },
+	{ 0x0f, 0x400000f0 },
+	{ 0x10, 0x014be040 },
+	{ 0x12, 0x400000f0 },
+	{ 0x15, 0x400000f0 },
+	{} /* terminator */
+};
+
+static struct cs_pincfg *cs_pincfgs[CS420X_MODELS] = {
+	[CS420X_MBP55] = mbp55_pincfgs,
+};
+
+static void fix_pincfg(struct hda_codec *codec, int model)
+{
+	const struct cs_pincfg *cfg = cs_pincfgs[model];
+	if (!cfg)
+		return;
+	for (; cfg->nid; cfg++)
+		snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
+}
+
+
+static int patch_cs420x(struct hda_codec *codec)
+{
+	struct cs_spec *spec;
+	int err;
+
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (!spec)
+		return -ENOMEM;
+	codec->spec = spec;
+
+	spec->board_config =
+		snd_hda_check_board_config(codec, CS420X_MODELS,
+					   cs420x_models, cs420x_cfg_tbl);
+	if (spec->board_config >= 0)
+		fix_pincfg(codec, spec->board_config);
+
+	switch (spec->board_config) {
+	case CS420X_MBP55:
+		/* GPIO1 = headphones */
+		/* GPIO3 = speakers */
+		spec->gpio_mask = 0x0a;
+		spec->gpio_dir = 0x0a;
+		break;
+	}
+
+	err = cs_parse_auto_config(codec);
+	if (err < 0)
+		goto error;
+
+	codec->patch_ops = cs_patch_ops;
+
+	return 0;
+
+ error:
+	kfree(codec->spec);
+	codec->spec = NULL;
+	return err;
+}
+
+
+/*
+ * patch entries
+ */
+static struct hda_codec_preset snd_hda_preset_cirrus[] = {
+	{ .id = 0x10134206, .name = "CS4206", .patch = patch_cs420x },
+	{ .id = 0x10134207, .name = "CS4207", .patch = patch_cs420x },
+	{} /* terminator */
+};
+
+MODULE_ALIAS("snd-hda-codec-id:10134206");
+MODULE_ALIAS("snd-hda-codec-id:10134207");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cirrus Logic HD-audio codec");
+
+static struct hda_codec_preset_list cirrus_list = {
+	.preset = snd_hda_preset_cirrus,
+	.owner = THIS_MODULE,
+};
+
+static int __init patch_cirrus_init(void)
+{
+	return snd_hda_add_codec_preset(&cirrus_list);
+}
+
+static void __exit patch_cirrus_exit(void)
+{
+	snd_hda_delete_codec_preset(&cirrus_list);
+}
+
+module_init(patch_cirrus_init)
+module_exit(patch_cirrus_exit)

+ 2 - 1
sound/pci/hda/patch_cmedia.c

@@ -635,7 +635,8 @@ static int patch_cmi9880(struct hda_codec *codec)
 							cmi9880_models,
 							cmi9880_models,
 							cmi9880_cfg_tbl);
 							cmi9880_cfg_tbl);
 	if (spec->board_config < 0) {
 	if (spec->board_config < 0) {
-		snd_printdd(KERN_INFO "hda_codec: Unknown model for CMI9880\n");
+		snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+			    codec->chip_name);
 		spec->board_config = CMI_AUTO; /* try everything */
 		spec->board_config = CMI_AUTO; /* try everything */
 	}
 	}
 
 

+ 479 - 0
sound/pci/hda/patch_conexant.c

@@ -108,6 +108,8 @@ struct conexant_spec {
 	struct hda_input_mux private_imux;
 	struct hda_input_mux private_imux;
 	hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
 	hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
 
 
+	unsigned int dell_automute;
+	unsigned int port_d_mode;
 };
 };
 
 
 static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo,
 static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo,
@@ -1908,6 +1910,480 @@ static int patch_cxt5051(struct hda_codec *codec)
 	return 0;
 	return 0;
 }
 }
 
 
+/* Conexant 5066 specific */
+
+static hda_nid_t cxt5066_dac_nids[1] = { 0x10 };
+static hda_nid_t cxt5066_adc_nids[3] = { 0x14, 0x15, 0x16 };
+static hda_nid_t cxt5066_capsrc_nids[1] = { 0x17 };
+#define CXT5066_SPDIF_OUT	0x21
+
+static struct hda_channel_mode cxt5066_modes[1] = {
+	{ 2, NULL },
+};
+
+static void cxt5066_update_speaker(struct hda_codec *codec)
+{
+	struct conexant_spec *spec = codec->spec;
+	unsigned int pinctl;
+
+	snd_printdd("CXT5066: update speaker, hp_present=%d\n",
+		spec->hp_present);
+
+	/* Port A (HP) */
+	pinctl = ((spec->hp_present & 1) && spec->cur_eapd) ? PIN_HP : 0;
+	snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+			pinctl);
+
+	/* Port D (HP/LO) */
+	pinctl = ((spec->hp_present & 2) && spec->cur_eapd)
+		? spec->port_d_mode : 0;
+	snd_hda_codec_write(codec, 0x1c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+			pinctl);
+
+	/* CLASS_D AMP */
+	pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0;
+	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+			pinctl);
+
+	if (spec->dell_automute) {
+		/* DELL AIO Port Rule: PortA > PortD > IntSpk */
+		pinctl = (!(spec->hp_present & 1) && spec->cur_eapd)
+			? PIN_OUT : 0;
+		snd_hda_codec_write(codec, 0x1c, 0,
+			AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl);
+	}
+}
+
+/* turn on/off EAPD (+ mute HP) as a master switch */
+static int cxt5066_hp_master_sw_put(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+
+	if (!cxt_eapd_put(kcontrol, ucontrol))
+		return 0;
+
+	cxt5066_update_speaker(codec);
+	return 1;
+}
+
+/* toggle input of built-in and mic jack appropriately */
+static void cxt5066_automic(struct hda_codec *codec)
+{
+	static struct hda_verb ext_mic_present[] = {
+		/* enable external mic, port B */
+		{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+
+		/* switch to external mic input */
+		{0x17, AC_VERB_SET_CONNECT_SEL, 0},
+
+		/* disable internal mic, port C */
+		{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+		{}
+	};
+	static struct hda_verb ext_mic_absent[] = {
+		/* enable internal mic, port C */
+		{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+
+		/* switch to internal mic input */
+		{0x17, AC_VERB_SET_CONNECT_SEL, 1},
+
+		/* disable external mic, port B */
+		{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+		{}
+	};
+	unsigned int present;
+
+	present = snd_hda_codec_read(codec, 0x1a, 0,
+				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+	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);
+	}
+}
+
+/* mute internal speaker if HP is plugged */
+static void cxt5066_hp_automute(struct hda_codec *codec)
+{
+	struct conexant_spec *spec = codec->spec;
+	unsigned int portA, portD;
+
+	/* Port A */
+	portA = snd_hda_codec_read(codec, 0x19, 0, AC_VERB_GET_PIN_SENSE, 0)
+		& AC_PINSENSE_PRESENCE;
+
+	/* Port D */
+	portD = (snd_hda_codec_read(codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0)
+		& AC_PINSENSE_PRESENCE) << 1;
+
+	spec->hp_present = !!(portA | portD);
+	snd_printdd("CXT5066: hp automute portA=%x portD=%x present=%d\n",
+		portA, portD, spec->hp_present);
+	cxt5066_update_speaker(codec);
+}
+
+/* unsolicited event for jack sensing */
+static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+	snd_printdd("CXT5066: 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_automic(codec);
+		break;
+	}
+}
+
+static const struct hda_input_mux cxt5066_analog_mic_boost = {
+	.num_items = 5,
+	.items = {
+		{ "0dB",  0 },
+		{ "10dB", 1 },
+		{ "20dB", 2 },
+		{ "30dB", 3 },
+		{ "40dB", 4 },
+	},
+};
+
+static int cxt5066_mic_boost_mux_enum_info(struct snd_kcontrol *kcontrol,
+					   struct snd_ctl_elem_info *uinfo)
+{
+	return snd_hda_input_mux_info(&cxt5066_analog_mic_boost, uinfo);
+}
+
+static int cxt5066_mic_boost_mux_enum_get(struct snd_kcontrol *kcontrol,
+					  struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	int val;
+
+	val = snd_hda_codec_read(codec, 0x17, 0,
+		AC_VERB_GET_AMP_GAIN_MUTE, AC_AMP_GET_OUTPUT);
+
+	ucontrol->value.enumerated.item[0] = val & AC_AMP_GAIN;
+	return 0;
+}
+
+static int cxt5066_mic_boost_mux_enum_put(struct snd_kcontrol *kcontrol,
+					  struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	const struct hda_input_mux *imux = &cxt5066_analog_mic_boost;
+	unsigned int idx;
+
+	if (!imux->num_items)
+		return 0;
+	idx = ucontrol->value.enumerated.item[0];
+	if (idx >= imux->num_items)
+		idx = imux->num_items - 1;
+
+	snd_hda_codec_write_cache(codec, 0x17, 0,
+		AC_VERB_SET_AMP_GAIN_MUTE,
+		AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT |
+			imux->items[idx].index);
+
+	return 1;
+}
+
+static struct hda_input_mux cxt5066_capture_source = {
+	.num_items = 4,
+	.items = {
+		{ "Mic B", 0 },
+		{ "Mic C", 1 },
+		{ "Mic E", 2 },
+		{ "Mic F", 3 },
+	},
+};
+
+static struct hda_bind_ctls cxt5066_bind_capture_vol_others = {
+	.ops = &snd_hda_bind_vol,
+	.values = {
+		HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_INPUT),
+		HDA_COMPOSE_AMP_VAL(0x14, 3, 2, HDA_INPUT),
+		0
+	},
+};
+
+static struct hda_bind_ctls cxt5066_bind_capture_sw_others = {
+	.ops = &snd_hda_bind_sw,
+	.values = {
+		HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_INPUT),
+		HDA_COMPOSE_AMP_VAL(0x14, 3, 2, HDA_INPUT),
+		0
+	},
+};
+
+static struct snd_kcontrol_new cxt5066_mixer_master[] = {
+	HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT),
+	{}
+};
+
+static struct snd_kcontrol_new cxt5066_mixer_master_olpc[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Master Playback Volume",
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+				  SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+				  SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
+		.info = snd_hda_mixer_amp_volume_info,
+		.get = snd_hda_mixer_amp_volume_get,
+		.put = snd_hda_mixer_amp_volume_put,
+		.tlv = { .c = snd_hda_mixer_amp_tlv },
+		/* offset by 28 volume steps to limit minimum gain to -46dB */
+		.private_value =
+			HDA_COMPOSE_AMP_VAL_OFS(0x10, 3, 0, HDA_OUTPUT, 28),
+	},
+	{}
+};
+
+static struct snd_kcontrol_new cxt5066_mixers[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Master Playback Switch",
+		.info = cxt_eapd_info,
+		.get = cxt_eapd_get,
+		.put = cxt5066_hp_master_sw_put,
+		.private_value = 0x1d,
+	},
+
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Analog Mic Boost Capture Enum",
+		.info = cxt5066_mic_boost_mux_enum_info,
+		.get = cxt5066_mic_boost_mux_enum_get,
+		.put = cxt5066_mic_boost_mux_enum_put,
+	},
+
+	HDA_BIND_VOL("Capture Volume", &cxt5066_bind_capture_vol_others),
+	HDA_BIND_SW("Capture Switch", &cxt5066_bind_capture_sw_others),
+	{}
+};
+
+static struct hda_verb cxt5066_init_verbs[] = {
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port B */
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port C */
+	{0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port F */
+	{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port E */
+
+	/* Speakers  */
+	{0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+	/* HP, Amp  */
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x1c, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+	/* DAC1 */
+	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+	/* Node 14 connections: 0x17 0x18 0x23 0x24 0x27 */
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2) | 0x50},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+
+	/* no digital microphone support yet */
+	{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+	/* Audio input selector */
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x3},
+
+	/* SPDIF route: PCM */
+	{0x20, AC_VERB_SET_CONNECT_SEL, 0x0},
+	{0x22, AC_VERB_SET_CONNECT_SEL, 0x0},
+
+	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+
+	/* EAPD */
+	{0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
+
+	/* not handling these yet */
+	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
+	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
+	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
+	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
+	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
+	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
+	{0x20, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
+	{0x22, AC_VERB_SET_UNSOLICITED_ENABLE, 0},
+	{ } /* end */
+};
+
+static struct hda_verb cxt5066_init_verbs_olpc[] = {
+	/* Port A: headphones */
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+	/* Port B: external microphone */
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+
+	/* Port C: internal microphone */
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+
+	/* 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_UNMUTE(0) | 0x50},
+	{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)},
+
+	/* Disable digital microphone port */
+	{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+	/* 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 */
+};
+
+/* initialize jack-sensing, too */
+static int cxt5066_init(struct hda_codec *codec)
+{
+	snd_printdd("CXT5066: init\n");
+	conexant_init(codec);
+	if (codec->patch_ops.unsol_event) {
+		cxt5066_hp_automute(codec);
+		cxt5066_automic(codec);
+	}
+	return 0;
+}
+
+enum {
+	CXT5066_LAPTOP,			/* Laptops w/ EAPD support */
+	CXT5066_DELL_LAPTOP,	/* Dell Laptop */
+	CXT5066_OLPC_XO_1_5,	/* OLPC XO 1.5 */
+	CXT5066_MODELS
+};
+
+static const char *cxt5066_models[CXT5066_MODELS] = {
+	[CXT5066_LAPTOP]		= "laptop",
+	[CXT5066_DELL_LAPTOP]	= "dell-laptop",
+	[CXT5066_OLPC_XO_1_5]	= "olpc-xo-1_5",
+};
+
+static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
+	SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
+		      CXT5066_LAPTOP),
+	SND_PCI_QUIRK(0x1028, 0x02f5, "Dell",
+		      CXT5066_DELL_LAPTOP),
+	{}
+};
+
+static int patch_cxt5066(struct hda_codec *codec)
+{
+	struct conexant_spec *spec;
+	int board_config;
+
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (!spec)
+		return -ENOMEM;
+	codec->spec = spec;
+
+	codec->patch_ops = conexant_patch_ops;
+	codec->patch_ops.init = cxt5066_init;
+
+	spec->dell_automute = 0;
+	spec->multiout.max_channels = 2;
+	spec->multiout.num_dacs = ARRAY_SIZE(cxt5066_dac_nids);
+	spec->multiout.dac_nids = cxt5066_dac_nids;
+	spec->multiout.dig_out_nid = CXT5066_SPDIF_OUT;
+	spec->num_adc_nids = 1;
+	spec->adc_nids = cxt5066_adc_nids;
+	spec->capsrc_nids = cxt5066_capsrc_nids;
+	spec->input_mux = &cxt5066_capture_source;
+
+	spec->port_d_mode = PIN_HP;
+
+	spec->num_init_verbs = 1;
+	spec->init_verbs[0] = cxt5066_init_verbs;
+	spec->num_channel_mode = ARRAY_SIZE(cxt5066_modes);
+	spec->channel_mode = cxt5066_modes;
+	spec->cur_adc = 0;
+	spec->cur_adc_idx = 0;
+
+	board_config = snd_hda_check_board_config(codec, CXT5066_MODELS,
+						  cxt5066_models, cxt5066_cfg_tbl);
+	switch (board_config) {
+	default:
+	case CXT5066_LAPTOP:
+		spec->mixers[spec->num_mixers++] = cxt5066_mixer_master;
+		spec->mixers[spec->num_mixers++] = cxt5066_mixers;
+		break;
+	case CXT5066_DELL_LAPTOP:
+		spec->mixers[spec->num_mixers++] = cxt5066_mixer_master;
+		spec->mixers[spec->num_mixers++] = cxt5066_mixers;
+
+		spec->port_d_mode = PIN_OUT;
+		spec->init_verbs[spec->num_init_verbs] = cxt5066_init_verbs_portd_lo;
+		spec->num_init_verbs++;
+		spec->dell_automute = 1;
+		break;
+	case CXT5066_OLPC_XO_1_5:
+		codec->patch_ops.unsol_event = cxt5066_unsol_event;
+		spec->init_verbs[0] = cxt5066_init_verbs_olpc;
+		spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc;
+		spec->mixers[spec->num_mixers++] = cxt5066_mixers;
+		spec->port_d_mode = 0;
+
+		/* no S/PDIF out */
+		spec->multiout.dig_out_nid = 0;
+
+		/* input source automatically selected */
+		spec->input_mux = NULL;
+		break;
+	}
+
+	return 0;
+}
 
 
 /*
 /*
  */
  */
@@ -1919,12 +2395,15 @@ static struct hda_codec_preset snd_hda_preset_conexant[] = {
 	  .patch = patch_cxt5047 },
 	  .patch = patch_cxt5047 },
 	{ .id = 0x14f15051, .name = "CX20561 (Hermosa)",
 	{ .id = 0x14f15051, .name = "CX20561 (Hermosa)",
 	  .patch = patch_cxt5051 },
 	  .patch = patch_cxt5051 },
+	{ .id = 0x14f15066, .name = "CX20582 (Pebble)",
+	  .patch = patch_cxt5066 },
 	{} /* terminator */
 	{} /* terminator */
 };
 };
 
 
 MODULE_ALIAS("snd-hda-codec-id:14f15045");
 MODULE_ALIAS("snd-hda-codec-id:14f15045");
 MODULE_ALIAS("snd-hda-codec-id:14f15047");
 MODULE_ALIAS("snd-hda-codec-id:14f15047");
 MODULE_ALIAS("snd-hda-codec-id:14f15051");
 MODULE_ALIAS("snd-hda-codec-id:14f15051");
+MODULE_ALIAS("snd-hda-codec-id:14f15066");
 
 
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Conexant HD-audio codec");
 MODULE_DESCRIPTION("Conexant HD-audio codec");

+ 53 - 51
sound/pci/hda/patch_intelhdmi.c

@@ -33,8 +33,8 @@
 #include "hda_codec.h"
 #include "hda_codec.h"
 #include "hda_local.h"
 #include "hda_local.h"
 
 
-#define CVT_NID		0x02	/* audio converter */
-#define PIN_NID		0x03	/* HDMI output pin */
+static hda_nid_t cvt_nid;	/* audio converter */
+static hda_nid_t pin_nid;	/* HDMI output pin */
 
 
 #define INTEL_HDMI_EVENT_TAG		0x08
 #define INTEL_HDMI_EVENT_TAG		0x08
 
 
@@ -44,30 +44,6 @@ struct intel_hdmi_spec {
 	struct hdmi_eld sink_eld;
 	struct hdmi_eld sink_eld;
 };
 };
 
 
-static struct hda_verb pinout_enable_verb[] = {
-	{PIN_NID, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-	{} /* terminator */
-};
-
-static struct hda_verb unsolicited_response_verb[] = {
-	{PIN_NID, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN |
-						  INTEL_HDMI_EVENT_TAG},
-	{}
-};
-
-static struct hda_verb def_chan_map[] = {
-	{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x00},
-	{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x11},
-	{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x22},
-	{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x33},
-	{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x44},
-	{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x55},
-	{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x66},
-	{CVT_NID, AC_VERB_SET_HDMI_CHAN_SLOT, 0x77},
-	{}
-};
-
-
 struct hdmi_audio_infoframe {
 struct hdmi_audio_infoframe {
 	u8 type; /* 0x84 */
 	u8 type; /* 0x84 */
 	u8 ver;  /* 0x01 */
 	u8 ver;  /* 0x01 */
@@ -244,11 +220,12 @@ static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t nid,
 static void hdmi_enable_output(struct hda_codec *codec)
 static void hdmi_enable_output(struct hda_codec *codec)
 {
 {
 	/* Unmute */
 	/* Unmute */
-	if (get_wcaps(codec, PIN_NID) & AC_WCAP_OUT_AMP)
-		snd_hda_codec_write(codec, PIN_NID, 0,
+	if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP)
+		snd_hda_codec_write(codec, pin_nid, 0,
 				AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
 				AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
 	/* Enable pin out */
 	/* Enable pin out */
-	snd_hda_sequence_write(codec, pinout_enable_verb);
+	snd_hda_codec_write(codec, pin_nid, 0,
+			    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
 }
 }
 
 
 /*
 /*
@@ -256,8 +233,8 @@ static void hdmi_enable_output(struct hda_codec *codec)
  */
  */
 static void hdmi_start_infoframe_trans(struct hda_codec *codec)
 static void hdmi_start_infoframe_trans(struct hda_codec *codec)
 {
 {
-	hdmi_set_dip_index(codec, PIN_NID, 0x0, 0x0);
-	snd_hda_codec_write(codec, PIN_NID, 0, AC_VERB_SET_HDMI_DIP_XMIT,
+	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_BEST);
 						AC_DIPXMIT_BEST);
 }
 }
 
 
@@ -266,20 +243,20 @@ static void hdmi_start_infoframe_trans(struct hda_codec *codec)
  */
  */
 static void hdmi_stop_infoframe_trans(struct hda_codec *codec)
 static void hdmi_stop_infoframe_trans(struct hda_codec *codec)
 {
 {
-	hdmi_set_dip_index(codec, PIN_NID, 0x0, 0x0);
-	snd_hda_codec_write(codec, PIN_NID, 0, AC_VERB_SET_HDMI_DIP_XMIT,
+	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);
 						AC_DIPXMIT_DISABLE);
 }
 }
 
 
 static int hdmi_get_channel_count(struct hda_codec *codec)
 static int hdmi_get_channel_count(struct hda_codec *codec)
 {
 {
-	return 1 + snd_hda_codec_read(codec, CVT_NID, 0,
+	return 1 + snd_hda_codec_read(codec, cvt_nid, 0,
 					AC_VERB_GET_CVT_CHAN_COUNT, 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, int chs)
 {
 {
-	snd_hda_codec_write(codec, CVT_NID, 0,
+	snd_hda_codec_write(codec, cvt_nid, 0,
 					AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
 					AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
 
 
 	if (chs != hdmi_get_channel_count(codec))
 	if (chs != hdmi_get_channel_count(codec))
@@ -294,7 +271,7 @@ static void hdmi_debug_channel_mapping(struct hda_codec *codec)
 	int slot;
 	int slot;
 
 
 	for (i = 0; i < 8; i++) {
 	for (i = 0; i < 8; i++) {
-		slot = snd_hda_codec_read(codec, CVT_NID, 0,
+		slot = snd_hda_codec_read(codec, cvt_nid, 0,
 						AC_VERB_GET_HDMI_CHAN_SLOT, i);
 						AC_VERB_GET_HDMI_CHAN_SLOT, i);
 		printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
 		printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
 						slot >> 4, slot & 0x7);
 						slot >> 4, slot & 0x7);
@@ -307,7 +284,7 @@ static void hdmi_parse_eld(struct hda_codec *codec)
 	struct intel_hdmi_spec *spec = codec->spec;
 	struct intel_hdmi_spec *spec = codec->spec;
 	struct hdmi_eld *eld = &spec->sink_eld;
 	struct hdmi_eld *eld = &spec->sink_eld;
 
 
-	if (!snd_hdmi_get_eld(eld, codec, PIN_NID))
+	if (!snd_hdmi_get_eld(eld, codec, pin_nid))
 		snd_hdmi_show_eld(eld);
 		snd_hdmi_show_eld(eld);
 }
 }
 
 
@@ -322,11 +299,11 @@ static void hdmi_debug_dip_size(struct hda_codec *codec)
 	int i;
 	int i;
 	int size;
 	int size;
 
 
-	size = snd_hdmi_get_eld_size(codec, PIN_NID);
+	size = snd_hdmi_get_eld_size(codec, pin_nid);
 	printk(KERN_DEBUG "HDMI: ELD buf size is %d\n", size);
 	printk(KERN_DEBUG "HDMI: ELD buf size is %d\n", size);
 
 
 	for (i = 0; i < 8; i++) {
 	for (i = 0; i < 8; i++) {
-		size = snd_hda_codec_read(codec, PIN_NID, 0,
+		size = snd_hda_codec_read(codec, pin_nid, 0,
 						AC_VERB_GET_HDMI_DIP_SIZE, i);
 						AC_VERB_GET_HDMI_DIP_SIZE, i);
 		printk(KERN_DEBUG "HDMI: DIP GP[%d] buf size is %d\n", i, size);
 		printk(KERN_DEBUG "HDMI: DIP GP[%d] buf size is %d\n", i, size);
 	}
 	}
@@ -340,15 +317,15 @@ static void hdmi_clear_dip_buffers(struct hda_codec *codec)
 	int size;
 	int size;
 	int pi, bi;
 	int pi, bi;
 	for (i = 0; i < 8; i++) {
 	for (i = 0; i < 8; i++) {
-		size = snd_hda_codec_read(codec, PIN_NID, 0,
+		size = snd_hda_codec_read(codec, pin_nid, 0,
 						AC_VERB_GET_HDMI_DIP_SIZE, i);
 						AC_VERB_GET_HDMI_DIP_SIZE, i);
 		if (size == 0)
 		if (size == 0)
 			continue;
 			continue;
 
 
-		hdmi_set_dip_index(codec, PIN_NID, i, 0x0);
+		hdmi_set_dip_index(codec, pin_nid, i, 0x0);
 		for (j = 1; j < 1000; j++) {
 		for (j = 1; j < 1000; j++) {
-			hdmi_write_dip_byte(codec, PIN_NID, 0x0);
-			hdmi_get_dip_index(codec, PIN_NID, &pi, &bi);
+			hdmi_write_dip_byte(codec, pin_nid, 0x0);
+			hdmi_get_dip_index(codec, pin_nid, &pi, &bi);
 			if (pi != i)
 			if (pi != i)
 				snd_printd(KERN_INFO "dip index %d: %d != %d\n",
 				snd_printd(KERN_INFO "dip index %d: %d != %d\n",
 						bi, pi, i);
 						bi, pi, i);
@@ -376,9 +353,9 @@ static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
 		sum += params[i];
 		sum += params[i];
 	ai->checksum = - sum;
 	ai->checksum = - sum;
 
 
-	hdmi_set_dip_index(codec, PIN_NID, 0x0, 0x0);
+	hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
 	for (i = 0; i < sizeof(ai); i++)
 	for (i = 0; i < sizeof(ai); i++)
-		hdmi_write_dip_byte(codec, PIN_NID, params[i]);
+		hdmi_write_dip_byte(codec, pin_nid, params[i]);
 }
 }
 
 
 /*
 /*
@@ -465,6 +442,8 @@ static int hdmi_setup_channel_allocation(struct hda_codec *codec,
 static void hdmi_setup_channel_mapping(struct hda_codec *codec,
 static void hdmi_setup_channel_mapping(struct hda_codec *codec,
 					struct hdmi_audio_infoframe *ai)
 					struct hdmi_audio_infoframe *ai)
 {
 {
+	int i;
+
 	if (!ai->CA)
 	if (!ai->CA)
 		return;
 		return;
 
 
@@ -473,7 +452,11 @@ static void hdmi_setup_channel_mapping(struct hda_codec *codec,
 	 * ALSA sequence is front/surr/clfe/side?
 	 * ALSA sequence is front/surr/clfe/side?
 	 */
 	 */
 
 
-	snd_hda_sequence_write(codec, def_chan_map);
+	for (i = 0; i < 8; i++)
+		snd_hda_codec_write(codec, cvt_nid, 0,
+				    AC_VERB_SET_HDMI_CHAN_SLOT,
+				    (i << 4) | i);
+
 	hdmi_debug_channel_mapping(codec);
 	hdmi_debug_channel_mapping(codec);
 }
 }
 
 
@@ -597,7 +580,6 @@ static struct hda_pcm_stream intel_hdmi_pcm_playback = {
 	.substreams = 1,
 	.substreams = 1,
 	.channels_min = 2,
 	.channels_min = 2,
 	.channels_max = 8,
 	.channels_max = 8,
-	.nid = CVT_NID, /* NID to query formats and rates and setup streams */
 	.ops = {
 	.ops = {
 		.open    = intel_hdmi_playback_pcm_open,
 		.open    = intel_hdmi_playback_pcm_open,
 		.close   = intel_hdmi_playback_pcm_close,
 		.close   = intel_hdmi_playback_pcm_close,
@@ -613,6 +595,9 @@ static int intel_hdmi_build_pcms(struct hda_codec *codec)
 	codec->num_pcms = 1;
 	codec->num_pcms = 1;
 	codec->pcm_info = info;
 	codec->pcm_info = info;
 
 
+	/* NID to query formats and rates and setup streams */
+	intel_hdmi_pcm_playback.nid = cvt_nid;
+
 	info->name = "INTEL HDMI";
 	info->name = "INTEL HDMI";
 	info->pcm_type = HDA_PCM_TYPE_HDMI;
 	info->pcm_type = HDA_PCM_TYPE_HDMI;
 	info->stream[SNDRV_PCM_STREAM_PLAYBACK] = intel_hdmi_pcm_playback;
 	info->stream[SNDRV_PCM_STREAM_PLAYBACK] = intel_hdmi_pcm_playback;
@@ -636,8 +621,9 @@ static int intel_hdmi_init(struct hda_codec *codec)
 {
 {
 	hdmi_enable_output(codec);
 	hdmi_enable_output(codec);
 
 
-	snd_hda_sequence_write(codec, unsolicited_response_verb);
-
+	snd_hda_codec_write(codec, pin_nid, 0,
+			    AC_VERB_SET_UNSOLICITED_ENABLE,
+			    AC_USRSP_EN | INTEL_HDMI_EVENT_TAG);
 	return 0;
 	return 0;
 }
 }
 
 
@@ -657,7 +643,7 @@ static struct hda_codec_ops intel_hdmi_patch_ops = {
 	.unsol_event		= intel_hdmi_unsol_event,
 	.unsol_event		= intel_hdmi_unsol_event,
 };
 };
 
 
-static int patch_intel_hdmi(struct hda_codec *codec)
+static int do_patch_intel_hdmi(struct hda_codec *codec)
 {
 {
 	struct intel_hdmi_spec *spec;
 	struct intel_hdmi_spec *spec;
 
 
@@ -667,7 +653,7 @@ static int patch_intel_hdmi(struct hda_codec *codec)
 
 
 	spec->multiout.num_dacs = 0;	  /* no analog */
 	spec->multiout.num_dacs = 0;	  /* no analog */
 	spec->multiout.max_channels = 8;
 	spec->multiout.max_channels = 8;
-	spec->multiout.dig_out_nid = CVT_NID;
+	spec->multiout.dig_out_nid = cvt_nid;
 
 
 	codec->spec = spec;
 	codec->spec = spec;
 	codec->patch_ops = intel_hdmi_patch_ops;
 	codec->patch_ops = intel_hdmi_patch_ops;
@@ -679,12 +665,27 @@ static int patch_intel_hdmi(struct hda_codec *codec)
 	return 0;
 	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[] = {
 static struct hda_codec_preset snd_hda_preset_intelhdmi[] = {
 	{ .id = 0x808629fb, .name = "G45 DEVCL",  .patch = patch_intel_hdmi },
 	{ .id = 0x808629fb, .name = "G45 DEVCL",  .patch = patch_intel_hdmi },
 	{ .id = 0x80862801, .name = "G45 DEVBLC", .patch = patch_intel_hdmi },
 	{ .id = 0x80862801, .name = "G45 DEVBLC", .patch = patch_intel_hdmi },
 	{ .id = 0x80862802, .name = "G45 DEVCTG", .patch = patch_intel_hdmi },
 	{ .id = 0x80862802, .name = "G45 DEVCTG", .patch = patch_intel_hdmi },
 	{ .id = 0x80862803, .name = "G45 DEVELK", .patch = patch_intel_hdmi },
 	{ .id = 0x80862803, .name = "G45 DEVELK", .patch = patch_intel_hdmi },
 	{ .id = 0x80862804, .name = "G45 DEVIBX", .patch = patch_intel_hdmi },
 	{ .id = 0x80862804, .name = "G45 DEVIBX", .patch = patch_intel_hdmi },
+	{ .id = 0x80860054, .name = "Q57 DEVIBX", .patch = patch_intel_hdmi_ibexpeak },
 	{ .id = 0x10951392, .name = "SiI1392 HDMI",     .patch = patch_intel_hdmi },
 	{ .id = 0x10951392, .name = "SiI1392 HDMI",     .patch = patch_intel_hdmi },
 	{} /* terminator */
 	{} /* terminator */
 };
 };
@@ -694,6 +695,7 @@ MODULE_ALIAS("snd-hda-codec-id:80862801");
 MODULE_ALIAS("snd-hda-codec-id:80862802");
 MODULE_ALIAS("snd-hda-codec-id:80862802");
 MODULE_ALIAS("snd-hda-codec-id:80862803");
 MODULE_ALIAS("snd-hda-codec-id:80862803");
 MODULE_ALIAS("snd-hda-codec-id:80862804");
 MODULE_ALIAS("snd-hda-codec-id:80862804");
+MODULE_ALIAS("snd-hda-codec-id:80860054");
 MODULE_ALIAS("snd-hda-codec-id:10951392");
 MODULE_ALIAS("snd-hda-codec-id:10951392");
 
 
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");

+ 2 - 0
sound/pci/hda/patch_nvhdmi.c

@@ -377,6 +377,7 @@ static int patch_nvhdmi_2ch(struct hda_codec *codec)
  */
  */
 static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
 static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
 	{ .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
 	{ .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
+	{ .id = 0x10de0003, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
 	{ .id = 0x10de0006, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
 	{ .id = 0x10de0006, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
 	{ .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi_8ch },
 	{ .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi_8ch },
 	{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
 	{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
@@ -385,6 +386,7 @@ static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
 };
 };
 
 
 MODULE_ALIAS("snd-hda-codec-id:10de0002");
 MODULE_ALIAS("snd-hda-codec-id:10de0002");
+MODULE_ALIAS("snd-hda-codec-id:10de0003");
 MODULE_ALIAS("snd-hda-codec-id:10de0006");
 MODULE_ALIAS("snd-hda-codec-id:10de0006");
 MODULE_ALIAS("snd-hda-codec-id:10de0007");
 MODULE_ALIAS("snd-hda-codec-id:10de0007");
 MODULE_ALIAS("snd-hda-codec-id:10de0067");
 MODULE_ALIAS("snd-hda-codec-id:10de0067");

+ 2172 - 2248
sound/pci/hda/patch_realtek.c

@@ -208,12 +208,6 @@ enum {
 	ALC885_MBP3,
 	ALC885_MBP3,
 	ALC885_MB5,
 	ALC885_MB5,
 	ALC885_IMAC24,
 	ALC885_IMAC24,
-	ALC882_AUTO,
-	ALC882_MODEL_LAST,
-};
-
-/* ALC883 models */
-enum {
 	ALC883_3ST_2ch_DIG,
 	ALC883_3ST_2ch_DIG,
 	ALC883_3ST_6ch_DIG,
 	ALC883_3ST_6ch_DIG,
 	ALC883_3ST_6ch,
 	ALC883_3ST_6ch,
@@ -226,6 +220,7 @@ enum {
 	ALC888_ACER_ASPIRE_4930G,
 	ALC888_ACER_ASPIRE_4930G,
 	ALC888_ACER_ASPIRE_6530G,
 	ALC888_ACER_ASPIRE_6530G,
 	ALC888_ACER_ASPIRE_8930G,
 	ALC888_ACER_ASPIRE_8930G,
+	ALC888_ACER_ASPIRE_7730G,
 	ALC883_MEDION,
 	ALC883_MEDION,
 	ALC883_MEDION_MD2,
 	ALC883_MEDION_MD2,
 	ALC883_LAPTOP_EAPD,
 	ALC883_LAPTOP_EAPD,
@@ -237,17 +232,20 @@ enum {
 	ALC888_3ST_HP,
 	ALC888_3ST_HP,
 	ALC888_6ST_DELL,
 	ALC888_6ST_DELL,
 	ALC883_MITAC,
 	ALC883_MITAC,
+	ALC883_CLEVO_M540R,
 	ALC883_CLEVO_M720,
 	ALC883_CLEVO_M720,
 	ALC883_FUJITSU_PI2515,
 	ALC883_FUJITSU_PI2515,
 	ALC888_FUJITSU_XA3530,
 	ALC888_FUJITSU_XA3530,
 	ALC883_3ST_6ch_INTEL,
 	ALC883_3ST_6ch_INTEL,
+	ALC889A_INTEL,
+	ALC889_INTEL,
 	ALC888_ASUS_M90V,
 	ALC888_ASUS_M90V,
 	ALC888_ASUS_EEE1601,
 	ALC888_ASUS_EEE1601,
 	ALC889A_MB31,
 	ALC889A_MB31,
 	ALC1200_ASUS_P5Q,
 	ALC1200_ASUS_P5Q,
 	ALC883_SONY_VAIO_TT,
 	ALC883_SONY_VAIO_TT,
-	ALC883_AUTO,
-	ALC883_MODEL_LAST,
+	ALC882_AUTO,
+	ALC882_MODEL_LAST,
 };
 };
 
 
 /* for GPIO Poll */
 /* for GPIO Poll */
@@ -262,6 +260,14 @@ enum {
 	ALC_INIT_GPIO3,
 	ALC_INIT_GPIO3,
 };
 };
 
 
+struct alc_mic_route {
+	hda_nid_t pin;
+	unsigned char mux_idx;
+	unsigned char amix_idx;
+};
+
+#define MUX_IDX_UNDEF	((unsigned char)-1)
+
 struct alc_spec {
 struct alc_spec {
 	/* codec parameterization */
 	/* codec parameterization */
 	struct snd_kcontrol_new *mixers[5];	/* mixer arrays */
 	struct snd_kcontrol_new *mixers[5];	/* mixer arrays */
@@ -304,6 +310,8 @@ struct alc_spec {
 	unsigned int num_mux_defs;
 	unsigned int num_mux_defs;
 	const struct hda_input_mux *input_mux;
 	const struct hda_input_mux *input_mux;
 	unsigned int cur_mux[3];
 	unsigned int cur_mux[3];
+	struct alc_mic_route ext_mic;
+	struct alc_mic_route int_mic;
 
 
 	/* channel model */
 	/* channel model */
 	const struct hda_channel_mode *channel_mode;
 	const struct hda_channel_mode *channel_mode;
@@ -320,6 +328,8 @@ struct alc_spec {
 	struct snd_array kctls;
 	struct snd_array kctls;
 	struct hda_input_mux private_imux[3];
 	struct hda_input_mux private_imux[3];
 	hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
 	hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
+	hda_nid_t private_adc_nids[AUTO_CFG_MAX_OUTS];
+	hda_nid_t private_capsrc_nids[AUTO_CFG_MAX_OUTS];
 
 
 	/* hooks */
 	/* hooks */
 	void (*init_hook)(struct hda_codec *codec);
 	void (*init_hook)(struct hda_codec *codec);
@@ -329,6 +339,7 @@ struct alc_spec {
 	unsigned int sense_updated: 1;
 	unsigned int sense_updated: 1;
 	unsigned int jack_present: 1;
 	unsigned int jack_present: 1;
 	unsigned int master_sw: 1;
 	unsigned int master_sw: 1;
+	unsigned int auto_mic:1;
 
 
 	/* other flags */
 	/* other flags */
 	unsigned int no_analog :1; /* digital I/O only */
 	unsigned int no_analog :1; /* digital I/O only */
@@ -370,6 +381,7 @@ struct alc_config_preset {
 	unsigned int num_mux_defs;
 	unsigned int num_mux_defs;
 	const struct hda_input_mux *input_mux;
 	const struct hda_input_mux *input_mux;
 	void (*unsol_event)(struct hda_codec *, unsigned int);
 	void (*unsol_event)(struct hda_codec *, unsigned int);
+	void (*setup)(struct hda_codec *);
 	void (*init_hook)(struct hda_codec *);
 	void (*init_hook)(struct hda_codec *);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 	struct hda_amp_list *loopbacks;
 	struct hda_amp_list *loopbacks;
@@ -417,7 +429,7 @@ static int alc_mux_enum_put(struct snd_kcontrol *kcontrol,
 	mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx;
 	mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx;
 	imux = &spec->input_mux[mux_idx];
 	imux = &spec->input_mux[mux_idx];
 
 
-	type = (get_wcaps(codec, nid) & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+	type = get_wcaps_type(get_wcaps(codec, nid));
 	if (type == AC_WID_AUD_MIX) {
 	if (type == AC_WID_AUD_MIX) {
 		/* Matrix-mixer style (e.g. ALC882) */
 		/* Matrix-mixer style (e.g. ALC882) */
 		unsigned int *cur_val = &spec->cur_mux[adc_idx];
 		unsigned int *cur_val = &spec->cur_mux[adc_idx];
@@ -842,9 +854,10 @@ static void print_realtek_coef(struct snd_info_buffer *buffer,
 /*
 /*
  * set up from the preset table
  * set up from the preset table
  */
  */
-static void setup_preset(struct alc_spec *spec,
+static void setup_preset(struct hda_codec *codec,
 			 const struct alc_config_preset *preset)
 			 const struct alc_config_preset *preset)
 {
 {
+	struct alc_spec *spec = codec->spec;
 	int i;
 	int i;
 
 
 	for (i = 0; i < ARRAY_SIZE(preset->mixers) && preset->mixers[i]; i++)
 	for (i = 0; i < ARRAY_SIZE(preset->mixers) && preset->mixers[i]; i++)
@@ -886,6 +899,9 @@ static void setup_preset(struct alc_spec *spec,
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 	spec->loopback.amplist = preset->loopbacks;
 	spec->loopback.amplist = preset->loopbacks;
 #endif
 #endif
+
+	if (preset->setup)
+		preset->setup(codec);
 }
 }
 
 
 /* Enable GPIO mask and set output */
 /* Enable GPIO mask and set output */
@@ -965,30 +981,64 @@ static void alc_automute_pin(struct hda_codec *codec)
 	}
 	}
 }
 }
 
 
-#if 0 /* it's broken in some cases -- temporarily disabled */
+static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
+				hda_nid_t nid)
+{
+	hda_nid_t conn[HDA_MAX_NUM_INPUTS];
+	int i, nums;
+
+	nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
+	for (i = 0; i < nums; i++)
+		if (conn[i] == nid)
+			return i;
+	return -1;
+}
+
 static void alc_mic_automute(struct hda_codec *codec)
 static void alc_mic_automute(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
-	unsigned int present;
-	unsigned int mic_nid = spec->autocfg.input_pins[AUTO_PIN_MIC];
-	unsigned int fmic_nid = spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC];
-	unsigned int mix_nid = spec->capsrc_nids[0];
-	unsigned int capsrc_idx_mic, capsrc_idx_fmic;
-
-	capsrc_idx_mic = mic_nid - 0x18;
-	capsrc_idx_fmic = fmic_nid - 0x18;
-	present = snd_hda_codec_read(codec, mic_nid, 0,
-				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	snd_hda_codec_write(codec, mix_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-		    0x7000 | (capsrc_idx_mic << 8) | (present ? 0 : 0x80));
-	snd_hda_codec_write(codec, mix_nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-		    0x7000 | (capsrc_idx_fmic << 8) | (present ? 0x80 : 0));
-	snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, capsrc_idx_fmic,
-			 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+	struct alc_mic_route *dead, *alive;
+	unsigned int present, type;
+	hda_nid_t cap_nid;
+
+	if (!spec->auto_mic)
+		return;
+	if (!spec->int_mic.pin || !spec->ext_mic.pin)
+		return;
+	if (snd_BUG_ON(!spec->adc_nids))
+		return;
+
+	cap_nid = spec->capsrc_nids ? spec->capsrc_nids[0] : spec->adc_nids[0];
+
+	present = snd_hda_codec_read(codec, spec->ext_mic.pin, 0,
+				     AC_VERB_GET_PIN_SENSE, 0);
+	present &= AC_PINSENSE_PRESENCE;
+	if (present) {
+		alive = &spec->ext_mic;
+		dead = &spec->int_mic;
+	} else {
+		alive = &spec->int_mic;
+		dead = &spec->ext_mic;
+	}
+
+	type = get_wcaps_type(get_wcaps(codec, cap_nid));
+	if (type == AC_WID_AUD_MIX) {
+		/* Matrix-mixer style (e.g. ALC882) */
+		snd_hda_codec_amp_stereo(codec, cap_nid, HDA_INPUT,
+					 alive->mux_idx,
+					 HDA_AMP_MUTE, 0);
+		snd_hda_codec_amp_stereo(codec, cap_nid, HDA_INPUT,
+					 dead->mux_idx,
+					 HDA_AMP_MUTE, HDA_AMP_MUTE);
+	} else {
+		/* MUX style (e.g. ALC880) */
+		snd_hda_codec_write_cache(codec, cap_nid, 0,
+					  AC_VERB_SET_CONNECT_SEL,
+					  alive->mux_idx);
+	}
+
+	/* FIXME: analog mixer */
 }
 }
-#else
-#define alc_mic_automute(codec) do {} while(0) /* NOP */
-#endif /* disabled */
 
 
 /* unsolicited event for HP jack sensing */
 /* unsolicited event for HP jack sensing */
 static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res)
 static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res)
@@ -1031,6 +1081,16 @@ static void alc888_coef_init(struct hda_codec *codec)
 				    AC_VERB_SET_PROC_COEF, 0x3030);
 				    AC_VERB_SET_PROC_COEF, 0x3030);
 }
 }
 
 
+static void alc889_coef_init(struct hda_codec *codec)
+{
+	unsigned int tmp;
+
+	snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 7);
+	tmp = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_PROC_COEF, 0);
+	snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 7);
+	snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_PROC_COEF, tmp|0x2010);
+}
+
 static void alc_auto_init_amp(struct hda_codec *codec, int type)
 static void alc_auto_init_amp(struct hda_codec *codec, int type)
 {
 {
 	unsigned int tmp;
 	unsigned int tmp;
@@ -1088,15 +1148,7 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type)
 		case 0x10ec0885:
 		case 0x10ec0885:
 		case 0x10ec0887:
 		case 0x10ec0887:
 		case 0x10ec0889:
 		case 0x10ec0889:
-			snd_hda_codec_write(codec, 0x20, 0,
-					    AC_VERB_SET_COEF_INDEX, 7);
-			tmp = snd_hda_codec_read(codec, 0x20, 0,
-						 AC_VERB_GET_PROC_COEF, 0);
-			snd_hda_codec_write(codec, 0x20, 0,
-					    AC_VERB_SET_COEF_INDEX, 7);
-			snd_hda_codec_write(codec, 0x20, 0,
-					    AC_VERB_SET_PROC_COEF,
-					    tmp | 0x2010);
+			alc889_coef_init(codec);
 			break;
 			break;
 		case 0x10ec0888:
 		case 0x10ec0888:
 			alc888_coef_init(codec);
 			alc888_coef_init(codec);
@@ -1142,6 +1194,55 @@ static void alc_init_auto_hp(struct hda_codec *codec)
 	spec->unsol_event = alc_sku_unsol_event;
 	spec->unsol_event = alc_sku_unsol_event;
 }
 }
 
 
+static void alc_init_auto_mic(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	hda_nid_t fixed, ext;
+	int i;
+
+	/* there must be only two mic inputs exclusively */
+	for (i = AUTO_PIN_LINE; i < AUTO_PIN_LAST; i++)
+		if (cfg->input_pins[i])
+			return;
+
+	fixed = ext = 0;
+	for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++) {
+		hda_nid_t nid = cfg->input_pins[i];
+		unsigned int defcfg;
+		if (!nid)
+			return;
+		defcfg = snd_hda_codec_get_pincfg(codec, nid);
+		switch (get_defcfg_connect(defcfg)) {
+		case AC_JACK_PORT_FIXED:
+			if (fixed)
+				return; /* already occupied */
+			fixed = nid;
+			break;
+		case AC_JACK_PORT_COMPLEX:
+			if (ext)
+				return; /* already occupied */
+			ext = nid;
+			break;
+		default:
+			return; /* invalid entry */
+		}
+	}
+	if (!(get_wcaps(codec, ext) & AC_WCAP_UNSOL_CAP))
+		return; /* no unsol support */
+	snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x\n",
+		    ext, fixed);
+	spec->ext_mic.pin = ext;
+	spec->int_mic.pin = fixed;
+	spec->ext_mic.mux_idx = MUX_IDX_UNDEF; /* set later */
+	spec->int_mic.mux_idx = MUX_IDX_UNDEF; /* set later */
+	spec->auto_mic = 1;
+	snd_hda_codec_write_cache(codec, spec->ext_mic.pin, 0,
+				  AC_VERB_SET_UNSOLICITED_ENABLE,
+				  AC_USRSP_EN | ALC880_MIC_EVENT);
+	spec->unsol_event = alc_sku_unsol_event;
+}
+
 /* check subsystem ID and set up device-specific initialization;
 /* check subsystem ID and set up device-specific initialization;
  * return 1 if initialized, 0 if invalid SSID
  * return 1 if initialized, 0 if invalid SSID
  */
  */
@@ -1243,6 +1344,7 @@ do_sku:
 	}
 	}
 
 
 	alc_init_auto_hp(codec);
 	alc_init_auto_hp(codec);
+	alc_init_auto_mic(codec);
 	return 1;
 	return 1;
 }
 }
 
 
@@ -1255,6 +1357,7 @@ static void alc_ssid_check(struct hda_codec *codec,
 			   "Enable default setup for auto mode as fallback\n");
 			   "Enable default setup for auto mode as fallback\n");
 		spec->init_amp = ALC_INIT_DEFAULT;
 		spec->init_amp = ALC_INIT_DEFAULT;
 		alc_init_auto_hp(codec);
 		alc_init_auto_hp(codec);
+		alc_init_auto_mic(codec);
 	}
 	}
 }
 }
 
 
@@ -1436,7 +1539,25 @@ static void alc_automute_amp_unsol_event(struct hda_codec *codec,
 		alc_automute_amp(codec);
 		alc_automute_amp(codec);
 }
 }
 
 
-static void alc888_fujitsu_xa3530_init_hook(struct hda_codec *codec)
+static void alc889_automute_setup(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+
+	spec->autocfg.hp_pins[0] = 0x15;
+	spec->autocfg.speaker_pins[0] = 0x14;
+	spec->autocfg.speaker_pins[1] = 0x16;
+	spec->autocfg.speaker_pins[2] = 0x17;
+	spec->autocfg.speaker_pins[3] = 0x19;
+	spec->autocfg.speaker_pins[4] = 0x1a;
+}
+
+static void alc889_intel_init_hook(struct hda_codec *codec)
+{
+	alc889_coef_init(codec);
+	alc_automute_amp(codec);
+}
+
+static void alc888_fujitsu_xa3530_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
@@ -1444,7 +1565,6 @@ static void alc888_fujitsu_xa3530_init_hook(struct hda_codec *codec)
 	spec->autocfg.hp_pins[1] = 0x1b; /* hp */
 	spec->autocfg.hp_pins[1] = 0x1b; /* hp */
 	spec->autocfg.speaker_pins[0] = 0x14; /* speaker */
 	spec->autocfg.speaker_pins[0] = 0x14; /* speaker */
 	spec->autocfg.speaker_pins[1] = 0x15; /* bass */
 	spec->autocfg.speaker_pins[1] = 0x15; /* bass */
-	alc_automute_amp(codec);
 }
 }
 
 
 /*
 /*
@@ -1643,16 +1763,15 @@ static struct snd_kcontrol_new alc888_base_mixer[] = {
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
-static void alc888_acer_aspire_4930g_init_hook(struct hda_codec *codec)
+static void alc888_acer_aspire_4930g_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
 	spec->autocfg.hp_pins[0] = 0x15;
 	spec->autocfg.hp_pins[0] = 0x15;
 	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[0] = 0x14;
-	alc_automute_amp(codec);
 }
 }
 
 
-static void alc888_acer_aspire_6530g_init_hook(struct hda_codec *codec)
+static void alc888_acer_aspire_6530g_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
@@ -1660,10 +1779,9 @@ static void alc888_acer_aspire_6530g_init_hook(struct hda_codec *codec)
 	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[1] = 0x16;
 	spec->autocfg.speaker_pins[1] = 0x16;
 	spec->autocfg.speaker_pins[2] = 0x17;
 	spec->autocfg.speaker_pins[2] = 0x17;
-	alc_automute_amp(codec);
 }
 }
 
 
-static void alc889_acer_aspire_8930g_init_hook(struct hda_codec *codec)
+static void alc889_acer_aspire_8930g_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
@@ -1671,7 +1789,6 @@ static void alc889_acer_aspire_8930g_init_hook(struct hda_codec *codec)
 	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[1] = 0x16;
 	spec->autocfg.speaker_pins[1] = 0x16;
 	spec->autocfg.speaker_pins[2] = 0x1b;
 	spec->autocfg.speaker_pins[2] = 0x1b;
-	alc_automute_amp(codec);
 }
 }
 
 
 /*
 /*
@@ -2651,13 +2768,17 @@ static void alc880_uniwill_mic_automute(struct hda_codec *codec)
 	snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits);
 	snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits);
 }
 }
 
 
-static void alc880_uniwill_init_hook(struct hda_codec *codec)
+static void alc880_uniwill_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
 	spec->autocfg.hp_pins[0] = 0x14;
 	spec->autocfg.hp_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[0] = 0x15;
 	spec->autocfg.speaker_pins[0] = 0x15;
 	spec->autocfg.speaker_pins[0] = 0x16;
 	spec->autocfg.speaker_pins[0] = 0x16;
+}
+
+static void alc880_uniwill_init_hook(struct hda_codec *codec)
+{
 	alc_automute_amp(codec);
 	alc_automute_amp(codec);
 	alc880_uniwill_mic_automute(codec);
 	alc880_uniwill_mic_automute(codec);
 }
 }
@@ -2678,13 +2799,12 @@ static void alc880_uniwill_unsol_event(struct hda_codec *codec,
 	}
 	}
 }
 }
 
 
-static void alc880_uniwill_p53_init_hook(struct hda_codec *codec)
+static void alc880_uniwill_p53_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
 	spec->autocfg.hp_pins[0] = 0x14;
 	spec->autocfg.hp_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[0] = 0x15;
 	spec->autocfg.speaker_pins[0] = 0x15;
-	alc_automute_amp(codec);
 }
 }
 
 
 static void alc880_uniwill_p53_dcvol_automute(struct hda_codec *codec)
 static void alc880_uniwill_p53_dcvol_automute(struct hda_codec *codec)
@@ -2947,13 +3067,12 @@ static struct hda_verb alc880_lg_init_verbs[] = {
 };
 };
 
 
 /* toggle speaker-output according to the hp-jack state */
 /* toggle speaker-output according to the hp-jack state */
-static void alc880_lg_init_hook(struct hda_codec *codec)
+static void alc880_lg_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
 	spec->autocfg.hp_pins[0] = 0x1b;
 	spec->autocfg.hp_pins[0] = 0x1b;
 	spec->autocfg.speaker_pins[0] = 0x17;
 	spec->autocfg.speaker_pins[0] = 0x17;
-	alc_automute_amp(codec);
 }
 }
 
 
 /*
 /*
@@ -3032,13 +3151,12 @@ static struct hda_verb alc880_lg_lw_init_verbs[] = {
 };
 };
 
 
 /* toggle speaker-output according to the hp-jack state */
 /* toggle speaker-output according to the hp-jack state */
-static void alc880_lg_lw_init_hook(struct hda_codec *codec)
+static void alc880_lg_lw_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
 	spec->autocfg.hp_pins[0] = 0x1b;
 	spec->autocfg.hp_pins[0] = 0x1b;
 	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[0] = 0x14;
-	alc_automute_amp(codec);
 }
 }
 
 
 static struct snd_kcontrol_new alc880_medion_rim_mixer[] = {
 static struct snd_kcontrol_new alc880_medion_rim_mixer[] = {
@@ -3104,13 +3222,12 @@ static void alc880_medion_rim_unsol_event(struct hda_codec *codec,
 		alc880_medion_rim_automute(codec);
 		alc880_medion_rim_automute(codec);
 }
 }
 
 
-static void alc880_medion_rim_init_hook(struct hda_codec *codec)
+static void alc880_medion_rim_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
 	spec->autocfg.hp_pins[0] = 0x14;
 	spec->autocfg.hp_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[0] = 0x1b;
 	spec->autocfg.speaker_pins[0] = 0x1b;
-	alc880_medion_rim_automute(codec);
 }
 }
 
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -3977,7 +4094,8 @@ static struct alc_config_preset alc880_presets[] = {
 		.channel_mode = alc880_2_jack_modes,
 		.channel_mode = alc880_2_jack_modes,
 		.input_mux = &alc880_f1734_capture_source,
 		.input_mux = &alc880_f1734_capture_source,
 		.unsol_event = alc880_uniwill_p53_unsol_event,
 		.unsol_event = alc880_uniwill_p53_unsol_event,
-		.init_hook = alc880_uniwill_p53_init_hook,
+		.setup = alc880_uniwill_p53_setup,
+		.init_hook = alc_automute_amp,
 	},
 	},
 	[ALC880_ASUS] = {
 	[ALC880_ASUS] = {
 		.mixers = { alc880_asus_mixer },
 		.mixers = { alc880_asus_mixer },
@@ -4054,6 +4172,7 @@ static struct alc_config_preset alc880_presets[] = {
 		.need_dac_fix = 1,
 		.need_dac_fix = 1,
 		.input_mux = &alc880_capture_source,
 		.input_mux = &alc880_capture_source,
 		.unsol_event = alc880_uniwill_unsol_event,
 		.unsol_event = alc880_uniwill_unsol_event,
+		.setup = alc880_uniwill_setup,
 		.init_hook = alc880_uniwill_init_hook,
 		.init_hook = alc880_uniwill_init_hook,
 	},
 	},
 	[ALC880_UNIWILL_P53] = {
 	[ALC880_UNIWILL_P53] = {
@@ -4066,7 +4185,8 @@ static struct alc_config_preset alc880_presets[] = {
 		.channel_mode = alc880_threestack_modes,
 		.channel_mode = alc880_threestack_modes,
 		.input_mux = &alc880_capture_source,
 		.input_mux = &alc880_capture_source,
 		.unsol_event = alc880_uniwill_p53_unsol_event,
 		.unsol_event = alc880_uniwill_p53_unsol_event,
-		.init_hook = alc880_uniwill_p53_init_hook,
+		.setup = alc880_uniwill_p53_setup,
+		.init_hook = alc_automute_amp,
 	},
 	},
 	[ALC880_FUJITSU] = {
 	[ALC880_FUJITSU] = {
 		.mixers = { alc880_fujitsu_mixer },
 		.mixers = { alc880_fujitsu_mixer },
@@ -4080,7 +4200,8 @@ static struct alc_config_preset alc880_presets[] = {
 		.channel_mode = alc880_2_jack_modes,
 		.channel_mode = alc880_2_jack_modes,
 		.input_mux = &alc880_capture_source,
 		.input_mux = &alc880_capture_source,
 		.unsol_event = alc880_uniwill_p53_unsol_event,
 		.unsol_event = alc880_uniwill_p53_unsol_event,
-		.init_hook = alc880_uniwill_p53_init_hook,
+		.setup = alc880_uniwill_p53_setup,
+		.init_hook = alc_automute_amp,
 	},
 	},
 	[ALC880_CLEVO] = {
 	[ALC880_CLEVO] = {
 		.mixers = { alc880_three_stack_mixer },
 		.mixers = { alc880_three_stack_mixer },
@@ -4106,7 +4227,8 @@ static struct alc_config_preset alc880_presets[] = {
 		.need_dac_fix = 1,
 		.need_dac_fix = 1,
 		.input_mux = &alc880_lg_capture_source,
 		.input_mux = &alc880_lg_capture_source,
 		.unsol_event = alc_automute_amp_unsol_event,
 		.unsol_event = alc_automute_amp_unsol_event,
-		.init_hook = alc880_lg_init_hook,
+		.setup = alc880_lg_setup,
+		.init_hook = alc_automute_amp,
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 		.loopbacks = alc880_lg_loopbacks,
 		.loopbacks = alc880_lg_loopbacks,
 #endif
 #endif
@@ -4122,7 +4244,8 @@ static struct alc_config_preset alc880_presets[] = {
 		.channel_mode = alc880_lg_lw_modes,
 		.channel_mode = alc880_lg_lw_modes,
 		.input_mux = &alc880_lg_lw_capture_source,
 		.input_mux = &alc880_lg_lw_capture_source,
 		.unsol_event = alc_automute_amp_unsol_event,
 		.unsol_event = alc_automute_amp_unsol_event,
-		.init_hook = alc880_lg_lw_init_hook,
+		.setup = alc880_lg_lw_setup,
+		.init_hook = alc_automute_amp,
 	},
 	},
 	[ALC880_MEDION_RIM] = {
 	[ALC880_MEDION_RIM] = {
 		.mixers = { alc880_medion_rim_mixer },
 		.mixers = { alc880_medion_rim_mixer },
@@ -4136,7 +4259,8 @@ static struct alc_config_preset alc880_presets[] = {
 		.channel_mode = alc880_2_jack_modes,
 		.channel_mode = alc880_2_jack_modes,
 		.input_mux = &alc880_medion_rim_capture_source,
 		.input_mux = &alc880_medion_rim_capture_source,
 		.unsol_event = alc880_medion_rim_unsol_event,
 		.unsol_event = alc880_medion_rim_unsol_event,
-		.init_hook = alc880_medion_rim_init_hook,
+		.setup = alc880_medion_rim_setup,
+		.init_hook = alc880_medion_rim_automute,
 	},
 	},
 #ifdef CONFIG_SND_DEBUG
 #ifdef CONFIG_SND_DEBUG
 	[ALC880_TEST] = {
 	[ALC880_TEST] = {
@@ -4189,8 +4313,6 @@ static int add_control(struct alc_spec *spec, int type, const char *name,
 #define alc880_fixed_pin_idx(nid)	((nid) - 0x14)
 #define alc880_fixed_pin_idx(nid)	((nid) - 0x14)
 #define alc880_is_multi_pin(nid)	((nid) >= 0x18)
 #define alc880_is_multi_pin(nid)	((nid) >= 0x18)
 #define alc880_multi_pin_idx(nid)	((nid) - 0x18)
 #define alc880_multi_pin_idx(nid)	((nid) - 0x18)
-#define alc880_is_input_pin(nid)	((nid) >= 0x18)
-#define alc880_input_pin_idx(nid)	((nid) - 0x18)
 #define alc880_idx_to_dac(nid)		((nid) + 0x02)
 #define alc880_idx_to_dac(nid)		((nid) + 0x02)
 #define alc880_dac_to_idx(nid)		((nid) - 0x02)
 #define alc880_dac_to_idx(nid)		((nid) - 0x02)
 #define alc880_idx_to_mixer(nid)	((nid) + 0x0c)
 #define alc880_idx_to_mixer(nid)	((nid) + 0x0c)
@@ -4278,13 +4400,19 @@ static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec,
 			if (err < 0)
 			if (err < 0)
 				return err;
 				return err;
 		} else {
 		} else {
-			sprintf(name, "%s Playback Volume", chname[i]);
+			const char *pfx;
+			if (cfg->line_outs == 1 &&
+			    cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+				pfx = "Speaker";
+			else
+				pfx = chname[i];
+			sprintf(name, "%s Playback Volume", pfx);
 			err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
 			err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
 					  HDA_COMPOSE_AMP_VAL(nid, 3, 0,
 					  HDA_COMPOSE_AMP_VAL(nid, 3, 0,
 							      HDA_OUTPUT));
 							      HDA_OUTPUT));
 			if (err < 0)
 			if (err < 0)
 				return err;
 				return err;
-			sprintf(name, "%s Playback Switch", chname[i]);
+			sprintf(name, "%s Playback Switch", pfx);
 			err = add_control(spec, ALC_CTL_BIND_MUTE, name,
 			err = add_control(spec, ALC_CTL_BIND_MUTE, name,
 					  HDA_COMPOSE_AMP_VAL(nid, 3, 2,
 					  HDA_COMPOSE_AMP_VAL(nid, 3, 2,
 							      HDA_INPUT));
 							      HDA_INPUT));
@@ -4358,31 +4486,61 @@ static int new_analog_input(struct alc_spec *spec, hda_nid_t pin,
 	return 0;
 	return 0;
 }
 }
 
 
+static int alc_is_input_pin(struct hda_codec *codec, hda_nid_t nid)
+{
+	unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
+	return (pincap & AC_PINCAP_IN) != 0;
+}
+
 /* create playback/capture controls for input pins */
 /* create playback/capture controls for input pins */
-static int alc880_auto_create_analog_input_ctls(struct alc_spec *spec,
-						const struct auto_pin_cfg *cfg)
+static int alc_auto_create_input_ctls(struct hda_codec *codec,
+				      const struct auto_pin_cfg *cfg,
+				      hda_nid_t mixer,
+				      hda_nid_t cap1, hda_nid_t cap2)
 {
 {
+	struct alc_spec *spec = codec->spec;
 	struct hda_input_mux *imux = &spec->private_imux[0];
 	struct hda_input_mux *imux = &spec->private_imux[0];
 	int i, err, idx;
 	int i, err, idx;
 
 
 	for (i = 0; i < AUTO_PIN_LAST; i++) {
 	for (i = 0; i < AUTO_PIN_LAST; i++) {
-		if (alc880_is_input_pin(cfg->input_pins[i])) {
-			idx = alc880_input_pin_idx(cfg->input_pins[i]);
-			err = new_analog_input(spec, cfg->input_pins[i],
-					       auto_pin_cfg_labels[i],
-					       idx, 0x0b);
-			if (err < 0)
-				return err;
+		hda_nid_t pin;
+
+		pin = cfg->input_pins[i];
+		if (!alc_is_input_pin(codec, pin))
+			continue;
+
+		if (mixer) {
+			idx = get_connection_index(codec, mixer, pin);
+			if (idx >= 0) {
+				err = new_analog_input(spec, pin,
+						       auto_pin_cfg_labels[i],
+						       idx, mixer);
+				if (err < 0)
+					return err;
+			}
+		}
+
+		if (!cap1)
+			continue;
+		idx = get_connection_index(codec, cap1, pin);
+		if (idx < 0 && cap2)
+			idx = get_connection_index(codec, cap2, pin);
+		if (idx >= 0) {
 			imux->items[imux->num_items].label =
 			imux->items[imux->num_items].label =
 				auto_pin_cfg_labels[i];
 				auto_pin_cfg_labels[i];
-			imux->items[imux->num_items].index =
-				alc880_input_pin_idx(cfg->input_pins[i]);
+			imux->items[imux->num_items].index = idx;
 			imux->num_items++;
 			imux->num_items++;
 		}
 		}
 	}
 	}
 	return 0;
 	return 0;
 }
 }
 
 
+static int alc880_auto_create_input_ctls(struct hda_codec *codec,
+						const struct auto_pin_cfg *cfg)
+{
+	return alc_auto_create_input_ctls(codec, cfg, 0x0b, 0x08, 0x09);
+}
+
 static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid,
 static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid,
 			       unsigned int pin_type)
 			       unsigned int pin_type)
 {
 {
@@ -4448,7 +4606,7 @@ static void alc880_auto_init_analog_input(struct hda_codec *codec)
 
 
 	for (i = 0; i < AUTO_PIN_LAST; i++) {
 	for (i = 0; i < AUTO_PIN_LAST; i++) {
 		hda_nid_t nid = spec->autocfg.input_pins[i];
 		hda_nid_t nid = spec->autocfg.input_pins[i];
-		if (alc880_is_input_pin(nid)) {
+		if (alc_is_input_pin(codec, nid)) {
 			alc_set_input_pin(codec, nid, i);
 			alc_set_input_pin(codec, nid, i);
 			if (nid != ALC880_PIN_CD_NID &&
 			if (nid != ALC880_PIN_CD_NID &&
 			    (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
 			    (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
@@ -4491,7 +4649,7 @@ static int alc880_parse_auto_config(struct hda_codec *codec)
 					   "Headphone");
 					   "Headphone");
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
-	err = alc880_auto_create_analog_input_ctls(spec, &spec->autocfg);
+	err = alc880_auto_create_input_ctls(codec, &spec->autocfg);
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
 
 
@@ -4505,12 +4663,6 @@ static int alc880_parse_auto_config(struct hda_codec *codec)
 					      &dig_nid, 1);
 					      &dig_nid, 1);
 		if (err < 0)
 		if (err < 0)
 			continue;
 			continue;
-		if (dig_nid > 0x7f) {
-			printk(KERN_ERR "alc880_auto: invalid dig_nid "
-				"connection 0x%x for NID 0x%x\n", dig_nid,
-				spec->autocfg.dig_out_pins[i]);
-			continue;
-		}
 		if (!i)
 		if (!i)
 			spec->multiout.dig_out_nid = dig_nid;
 			spec->multiout.dig_out_nid = dig_nid;
 		else {
 		else {
@@ -4547,8 +4699,42 @@ static void alc880_auto_init(struct hda_codec *codec)
 		alc_inithook(codec);
 		alc_inithook(codec);
 }
 }
 
 
-static void set_capture_mixer(struct alc_spec *spec)
+/* check the ADC/MUX contains all input pins; some ADC/MUX contains only
+ * one of two digital mic pins, e.g. on ALC272
+ */
+static void fixup_automic_adc(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	int i;
+
+	for (i = 0; i < spec->num_adc_nids; i++) {
+		hda_nid_t cap = spec->capsrc_nids ?
+			spec->capsrc_nids[i] : spec->adc_nids[i];
+		int iidx, eidx;
+
+		iidx = get_connection_index(codec, cap, spec->int_mic.pin);
+		if (iidx < 0)
+			continue;
+		eidx = get_connection_index(codec, cap, spec->ext_mic.pin);
+		if (eidx < 0)
+			continue;
+		spec->int_mic.mux_idx = iidx;
+		spec->ext_mic.mux_idx = eidx;
+		if (spec->capsrc_nids)
+			spec->capsrc_nids += i;
+		spec->adc_nids += i;
+		spec->num_adc_nids = 1;
+		return;
+	}
+	snd_printd(KERN_INFO "hda_codec: %s: "
+		   "No ADC/MUX containing both 0x%x and 0x%x pins\n",
+		   codec->chip_name, spec->int_mic.pin, spec->ext_mic.pin);
+	spec->auto_mic = 0; /* disable auto-mic to be sure */
+}
+
+static void set_capture_mixer(struct hda_codec *codec)
 {
 {
+	struct alc_spec *spec = codec->spec;
 	static struct snd_kcontrol_new *caps[2][3] = {
 	static struct snd_kcontrol_new *caps[2][3] = {
 		{ alc_capture_mixer_nosrc1,
 		{ alc_capture_mixer_nosrc1,
 		  alc_capture_mixer_nosrc2,
 		  alc_capture_mixer_nosrc2,
@@ -4559,7 +4745,10 @@ static void set_capture_mixer(struct alc_spec *spec)
 	};
 	};
 	if (spec->num_adc_nids > 0 && spec->num_adc_nids <= 3) {
 	if (spec->num_adc_nids > 0 && spec->num_adc_nids <= 3) {
 		int mux;
 		int mux;
-		if (spec->input_mux && spec->input_mux->num_items > 1)
+		if (spec->auto_mic) {
+			mux = 0;
+			fixup_automic_adc(codec);
+		} else if (spec->input_mux && spec->input_mux->num_items > 1)
 			mux = 1;
 			mux = 1;
 		else
 		else
 			mux = 0;
 			mux = 0;
@@ -4590,8 +4779,8 @@ static int patch_alc880(struct hda_codec *codec)
 						  alc880_models,
 						  alc880_models,
 						  alc880_cfg_tbl);
 						  alc880_cfg_tbl);
 	if (board_config < 0) {
 	if (board_config < 0) {
-		printk(KERN_INFO "hda_codec: Unknown model for %s, "
-		       "trying auto-probe from BIOS...\n", codec->chip_name);
+		printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+		       codec->chip_name);
 		board_config = ALC880_AUTO;
 		board_config = ALC880_AUTO;
 	}
 	}
 
 
@@ -4616,7 +4805,7 @@ static int patch_alc880(struct hda_codec *codec)
 	}
 	}
 
 
 	if (board_config != ALC880_AUTO)
 	if (board_config != ALC880_AUTO)
-		setup_preset(spec, &alc880_presets[board_config]);
+		setup_preset(codec, &alc880_presets[board_config]);
 
 
 	spec->stream_analog_playback = &alc880_pcm_analog_playback;
 	spec->stream_analog_playback = &alc880_pcm_analog_playback;
 	spec->stream_analog_capture = &alc880_pcm_analog_capture;
 	spec->stream_analog_capture = &alc880_pcm_analog_capture;
@@ -4629,7 +4818,7 @@ static int patch_alc880(struct hda_codec *codec)
 		/* check whether NID 0x07 is valid */
 		/* check whether NID 0x07 is valid */
 		unsigned int wcap = get_wcaps(codec, alc880_adc_nids[0]);
 		unsigned int wcap = get_wcaps(codec, alc880_adc_nids[0]);
 		/* get type */
 		/* get type */
-		wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+		wcap = get_wcaps_type(wcap);
 		if (wcap != AC_WID_AUD_IN) {
 		if (wcap != AC_WID_AUD_IN) {
 			spec->adc_nids = alc880_adc_nids_alt;
 			spec->adc_nids = alc880_adc_nids_alt;
 			spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids_alt);
 			spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids_alt);
@@ -4638,7 +4827,7 @@ static int patch_alc880(struct hda_codec *codec)
 			spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids);
 			spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids);
 		}
 		}
 	}
 	}
-	set_capture_mixer(spec);
+	set_capture_mixer(codec);
 	set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
 	set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
 
 
 	spec->vmaster_nid = 0x0c;
 	spec->vmaster_nid = 0x0c;
@@ -5830,7 +6019,14 @@ static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec,
 
 
 	nid = cfg->line_out_pins[0];
 	nid = cfg->line_out_pins[0];
 	if (nid) {
 	if (nid) {
-		err = alc260_add_playback_controls(spec, nid, "Front", &vols);
+		const char *pfx;
+		if (!cfg->speaker_pins[0] && !cfg->hp_pins[0])
+			pfx = "Master";
+		else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+			pfx = "Speaker";
+		else
+			pfx = "Front";
+		err = alc260_add_playback_controls(spec, nid, pfx, &vols);
 		if (err < 0)
 		if (err < 0)
 			return err;
 			return err;
 	}
 	}
@@ -5853,39 +6049,10 @@ static int alc260_auto_create_multi_out_ctls(struct alc_spec *spec,
 }
 }
 
 
 /* create playback/capture controls for input pins */
 /* create playback/capture controls for input pins */
-static int alc260_auto_create_analog_input_ctls(struct alc_spec *spec,
+static int alc260_auto_create_input_ctls(struct hda_codec *codec,
 						const struct auto_pin_cfg *cfg)
 						const struct auto_pin_cfg *cfg)
 {
 {
-	struct hda_input_mux *imux = &spec->private_imux[0];
-	int i, err, idx;
-
-	for (i = 0; i < AUTO_PIN_LAST; i++) {
-		if (cfg->input_pins[i] >= 0x12) {
-			idx = cfg->input_pins[i] - 0x12;
-			err = new_analog_input(spec, cfg->input_pins[i],
-					       auto_pin_cfg_labels[i], idx,
-					       0x07);
-			if (err < 0)
-				return err;
-			imux->items[imux->num_items].label =
-				auto_pin_cfg_labels[i];
-			imux->items[imux->num_items].index = idx;
-			imux->num_items++;
-		}
-		if (cfg->input_pins[i] >= 0x0f && cfg->input_pins[i] <= 0x10){
-			idx = cfg->input_pins[i] - 0x09;
-			err = new_analog_input(spec, cfg->input_pins[i],
-					       auto_pin_cfg_labels[i], idx,
-					       0x07);
-			if (err < 0)
-				return err;
-			imux->items[imux->num_items].label =
-				auto_pin_cfg_labels[i];
-			imux->items[imux->num_items].index = idx;
-			imux->num_items++;
-		}
-	}
-	return 0;
+	return alc_auto_create_input_ctls(codec, cfg, 0x07, 0x04, 0x05);
 }
 }
 
 
 static void alc260_auto_set_output_and_unmute(struct hda_codec *codec,
 static void alc260_auto_set_output_and_unmute(struct hda_codec *codec,
@@ -5999,7 +6166,7 @@ static int alc260_parse_auto_config(struct hda_codec *codec)
 		return err;
 		return err;
 	if (!spec->kctls.list)
 	if (!spec->kctls.list)
 		return 0; /* can't find valid BIOS pin config */
 		return 0; /* can't find valid BIOS pin config */
-	err = alc260_auto_create_analog_input_ctls(spec, &spec->autocfg);
+	err = alc260_auto_create_input_ctls(codec, &spec->autocfg);
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
 
 
@@ -6234,8 +6401,7 @@ static int patch_alc260(struct hda_codec *codec)
 						  alc260_models,
 						  alc260_models,
 						  alc260_cfg_tbl);
 						  alc260_cfg_tbl);
 	if (board_config < 0) {
 	if (board_config < 0) {
-		snd_printd(KERN_INFO "hda_codec: Unknown model for %s, "
-			   "trying auto-probe from BIOS...\n",
+		snd_printd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
 			   codec->chip_name);
 			   codec->chip_name);
 		board_config = ALC260_AUTO;
 		board_config = ALC260_AUTO;
 	}
 	}
@@ -6261,7 +6427,7 @@ static int patch_alc260(struct hda_codec *codec)
 	}
 	}
 
 
 	if (board_config != ALC260_AUTO)
 	if (board_config != ALC260_AUTO)
-		setup_preset(spec, &alc260_presets[board_config]);
+		setup_preset(codec, &alc260_presets[board_config]);
 
 
 	spec->stream_analog_playback = &alc260_pcm_analog_playback;
 	spec->stream_analog_playback = &alc260_pcm_analog_playback;
 	spec->stream_analog_capture = &alc260_pcm_analog_capture;
 	spec->stream_analog_capture = &alc260_pcm_analog_capture;
@@ -6272,7 +6438,7 @@ static int patch_alc260(struct hda_codec *codec)
 	if (!spec->adc_nids && spec->input_mux) {
 	if (!spec->adc_nids && spec->input_mux) {
 		/* check whether NID 0x04 is valid */
 		/* check whether NID 0x04 is valid */
 		unsigned int wcap = get_wcaps(codec, 0x04);
 		unsigned int wcap = get_wcaps(codec, 0x04);
-		wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+		wcap = get_wcaps_type(wcap);
 		/* get type */
 		/* get type */
 		if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) {
 		if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) {
 			spec->adc_nids = alc260_adc_nids_alt;
 			spec->adc_nids = alc260_adc_nids_alt;
@@ -6282,7 +6448,7 @@ static int patch_alc260(struct hda_codec *codec)
 			spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids);
 			spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids);
 		}
 		}
 	}
 	}
-	set_capture_mixer(spec);
+	set_capture_mixer(codec);
 	set_beep_amp(spec, 0x07, 0x05, HDA_INPUT);
 	set_beep_amp(spec, 0x07, 0x05, HDA_INPUT);
 
 
 	spec->vmaster_nid = 0x08;
 	spec->vmaster_nid = 0x08;
@@ -6301,7 +6467,7 @@ static int patch_alc260(struct hda_codec *codec)
 
 
 
 
 /*
 /*
- * ALC882 support
+ * ALC882/883/885/888/889 support
  *
  *
  * ALC882 is almost identical with ALC880 but has cleaner and more flexible
  * ALC882 is almost identical with ALC880 but has cleaner and more flexible
  * configuration.  Each pin widget can choose any input DACs and a mixer.
  * configuration.  Each pin widget can choose any input DACs and a mixer.
@@ -6313,22 +6479,35 @@ static int patch_alc260(struct hda_codec *codec)
  */
  */
 #define ALC882_DIGOUT_NID	0x06
 #define ALC882_DIGOUT_NID	0x06
 #define ALC882_DIGIN_NID	0x0a
 #define ALC882_DIGIN_NID	0x0a
+#define ALC883_DIGOUT_NID	ALC882_DIGOUT_NID
+#define ALC883_DIGIN_NID	ALC882_DIGIN_NID
+#define ALC1200_DIGOUT_NID	0x10
+
 
 
 static struct hda_channel_mode alc882_ch_modes[1] = {
 static struct hda_channel_mode alc882_ch_modes[1] = {
 	{ 8, NULL }
 	{ 8, NULL }
 };
 };
 
 
+/* DACs */
 static hda_nid_t alc882_dac_nids[4] = {
 static hda_nid_t alc882_dac_nids[4] = {
 	/* front, rear, clfe, rear_surr */
 	/* front, rear, clfe, rear_surr */
 	0x02, 0x03, 0x04, 0x05
 	0x02, 0x03, 0x04, 0x05
 };
 };
+#define alc883_dac_nids		alc882_dac_nids
 
 
-/* identical with ALC880 */
+/* ADCs */
 #define alc882_adc_nids		alc880_adc_nids
 #define alc882_adc_nids		alc880_adc_nids
 #define alc882_adc_nids_alt	alc880_adc_nids_alt
 #define alc882_adc_nids_alt	alc880_adc_nids_alt
+#define alc883_adc_nids		alc882_adc_nids_alt
+static hda_nid_t alc883_adc_nids_alt[1] = { 0x08 };
+static hda_nid_t alc883_adc_nids_rev[2] = { 0x09, 0x08 };
+#define alc889_adc_nids		alc880_adc_nids
 
 
 static hda_nid_t alc882_capsrc_nids[3] = { 0x24, 0x23, 0x22 };
 static hda_nid_t alc882_capsrc_nids[3] = { 0x24, 0x23, 0x22 };
 static hda_nid_t alc882_capsrc_nids_alt[2] = { 0x23, 0x22 };
 static hda_nid_t alc882_capsrc_nids_alt[2] = { 0x23, 0x22 };
+#define alc883_capsrc_nids	alc882_capsrc_nids_alt
+static hda_nid_t alc883_capsrc_nids_rev[2] = { 0x22, 0x23 };
+#define alc889_capsrc_nids	alc882_capsrc_nids
 
 
 /* input MUX */
 /* input MUX */
 /* FIXME: should be a matrix-type input source selection */
 /* FIXME: should be a matrix-type input source selection */
@@ -6343,6 +6522,17 @@ static struct hda_input_mux alc882_capture_source = {
 	},
 	},
 };
 };
 
 
+#define alc883_capture_source	alc882_capture_source
+
+static struct hda_input_mux alc889_capture_source = {
+	.num_items = 3,
+	.items = {
+		{ "Front Mic", 0x0 },
+		{ "Mic", 0x3 },
+		{ "Line", 0x2 },
+	},
+};
+
 static struct hda_input_mux mb5_capture_source = {
 static struct hda_input_mux mb5_capture_source = {
 	.num_items = 3,
 	.num_items = 3,
 	.items = {
 	.items = {
@@ -6352,6 +6542,77 @@ static struct hda_input_mux mb5_capture_source = {
 	},
 	},
 };
 };
 
 
+static struct hda_input_mux alc883_3stack_6ch_intel = {
+	.num_items = 4,
+	.items = {
+		{ "Mic", 0x1 },
+		{ "Front Mic", 0x0 },
+		{ "Line", 0x2 },
+		{ "CD", 0x4 },
+	},
+};
+
+static struct hda_input_mux alc883_lenovo_101e_capture_source = {
+	.num_items = 2,
+	.items = {
+		{ "Mic", 0x1 },
+		{ "Line", 0x2 },
+	},
+};
+
+static struct hda_input_mux alc883_lenovo_nb0763_capture_source = {
+	.num_items = 4,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "iMic", 0x1 },
+		{ "Line", 0x2 },
+		{ "CD", 0x4 },
+	},
+};
+
+static struct hda_input_mux alc883_fujitsu_pi2515_capture_source = {
+	.num_items = 2,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "Int Mic", 0x1 },
+	},
+};
+
+static struct hda_input_mux alc883_lenovo_sky_capture_source = {
+	.num_items = 3,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "Front Mic", 0x1 },
+		{ "Line", 0x4 },
+	},
+};
+
+static struct hda_input_mux alc883_asus_eee1601_capture_source = {
+	.num_items = 2,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "Line", 0x2 },
+	},
+};
+
+static struct hda_input_mux alc889A_mb31_capture_source = {
+	.num_items = 2,
+	.items = {
+		{ "Mic", 0x0 },
+		/* Front Mic (0x01) unused */
+		{ "Line", 0x2 },
+		/* Line 2 (0x03) unused */
+		/* CD (0x04) unsused? */
+	},
+};
+
+/*
+ * 2ch mode
+ */
+static struct hda_channel_mode alc883_3ST_2ch_modes[1] = {
+	{ 2, NULL }
+};
+
 /*
 /*
  * 2ch mode
  * 2ch mode
  */
  */
@@ -6364,23 +6625,84 @@ static struct hda_verb alc882_3ST_ch2_init[] = {
 };
 };
 
 
 /*
 /*
- * 6ch mode
+ * 4ch mode
  */
  */
-static struct hda_verb alc882_3ST_ch6_init[] = {
-	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-	{ 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
+static struct hda_verb alc882_3ST_ch4_init[] = {
+	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
 	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
 	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
 	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
 	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
 	{ 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
 	{ 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
-static struct hda_channel_mode alc882_3ST_6ch_modes[2] = {
-	{ 2, alc882_3ST_ch2_init },
+/*
+ * 6ch mode
+ */
+static struct hda_verb alc882_3ST_ch6_init[] = {
+	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+	{ } /* end */
+};
+
+static struct hda_channel_mode alc882_3ST_6ch_modes[3] = {
+	{ 2, alc882_3ST_ch2_init },
+	{ 4, alc882_3ST_ch4_init },
 	{ 6, alc882_3ST_ch6_init },
 	{ 6, alc882_3ST_ch6_init },
 };
 };
 
 
+#define alc883_3ST_6ch_modes	alc882_3ST_6ch_modes
+
+/*
+ * 2ch mode
+ */
+static struct hda_verb alc883_3ST_ch2_clevo_init[] = {
+	{ 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
+	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{ } /* end */
+};
+
+/*
+ * 4ch mode
+ */
+static struct hda_verb alc883_3ST_ch4_clevo_init[] = {
+	{ 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+	{ } /* end */
+};
+
+/*
+ * 6ch mode
+ */
+static struct hda_verb alc883_3ST_ch6_clevo_init[] = {
+	{ 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+	{ } /* end */
+};
+
+static struct hda_channel_mode alc883_3ST_6ch_clevo_modes[3] = {
+	{ 2, alc883_3ST_ch2_clevo_init },
+	{ 4, alc883_3ST_ch4_clevo_init },
+	{ 6, alc883_3ST_ch6_clevo_init },
+};
+
+
 /*
 /*
  * 6ch mode
  * 6ch mode
  */
  */
@@ -6468,6 +6790,189 @@ static struct hda_channel_mode alc885_mb5_6ch_modes[2] = {
 	{ 6, alc885_mb5_ch6_init },
 	{ 6, alc885_mb5_ch6_init },
 };
 };
 
 
+
+/*
+ * 2ch mode
+ */
+static struct hda_verb alc883_4ST_ch2_init[] = {
+	{ 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{ } /* end */
+};
+
+/*
+ * 4ch mode
+ */
+static struct hda_verb alc883_4ST_ch4_init[] = {
+	{ 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+	{ } /* end */
+};
+
+/*
+ * 6ch mode
+ */
+static struct hda_verb alc883_4ST_ch6_init[] = {
+	{ 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+	{ } /* end */
+};
+
+/*
+ * 8ch mode
+ */
+static struct hda_verb alc883_4ST_ch8_init[] = {
+	{ 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 },
+	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+	{ } /* end */
+};
+
+static struct hda_channel_mode alc883_4ST_8ch_modes[4] = {
+	{ 2, alc883_4ST_ch2_init },
+	{ 4, alc883_4ST_ch4_init },
+	{ 6, alc883_4ST_ch6_init },
+	{ 8, alc883_4ST_ch8_init },
+};
+
+
+/*
+ * 2ch mode
+ */
+static struct hda_verb alc883_3ST_ch2_intel_init[] = {
+	{ 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+	{ 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{ } /* end */
+};
+
+/*
+ * 4ch mode
+ */
+static struct hda_verb alc883_3ST_ch4_intel_init[] = {
+	{ 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+	{ 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+	{ } /* end */
+};
+
+/*
+ * 6ch mode
+ */
+static struct hda_verb alc883_3ST_ch6_intel_init[] = {
+	{ 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x19, AC_VERB_SET_CONNECT_SEL, 0x02 },
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+	{ } /* end */
+};
+
+static struct hda_channel_mode alc883_3ST_6ch_intel_modes[3] = {
+	{ 2, alc883_3ST_ch2_intel_init },
+	{ 4, alc883_3ST_ch4_intel_init },
+	{ 6, alc883_3ST_ch6_intel_init },
+};
+
+/*
+ * 2ch mode
+ */
+static struct hda_verb alc889_ch2_intel_init[] = {
+	{ 0x14, AC_VERB_SET_CONNECT_SEL, 0x00 },
+	{ 0x19, AC_VERB_SET_CONNECT_SEL, 0x00 },
+	{ 0x16, AC_VERB_SET_CONNECT_SEL, 0x00 },
+	{ 0x17, AC_VERB_SET_CONNECT_SEL, 0x00 },
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{ } /* end */
+};
+
+/*
+ * 6ch mode
+ */
+static struct hda_verb alc889_ch6_intel_init[] = {
+	{ 0x14, AC_VERB_SET_CONNECT_SEL, 0x00 },
+	{ 0x19, AC_VERB_SET_CONNECT_SEL, 0x01 },
+	{ 0x16, AC_VERB_SET_CONNECT_SEL, 0x02 },
+	{ 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 },
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{ } /* end */
+};
+
+/*
+ * 8ch mode
+ */
+static struct hda_verb alc889_ch8_intel_init[] = {
+	{ 0x14, AC_VERB_SET_CONNECT_SEL, 0x00 },
+	{ 0x19, AC_VERB_SET_CONNECT_SEL, 0x01 },
+	{ 0x16, AC_VERB_SET_CONNECT_SEL, 0x02 },
+	{ 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 },
+	{ 0x1a, AC_VERB_SET_CONNECT_SEL, 0x03 },
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ } /* end */
+};
+
+static struct hda_channel_mode alc889_8ch_intel_modes[3] = {
+	{ 2, alc889_ch2_intel_init },
+	{ 6, alc889_ch6_intel_init },
+	{ 8, alc889_ch8_intel_init },
+};
+
+/*
+ * 6ch mode
+ */
+static struct hda_verb alc883_sixstack_ch6_init[] = {
+	{ 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
+	{ 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ } /* end */
+};
+
+/*
+ * 8ch mode
+ */
+static struct hda_verb alc883_sixstack_ch8_init[] = {
+	{ 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ } /* end */
+};
+
+static struct hda_channel_mode alc883_sixstack_modes[2] = {
+	{ 6, alc883_sixstack_ch6_init },
+	{ 8, alc883_sixstack_ch8_init },
+};
+
+
 /* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17
 /* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17
  *                 Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b
  *                 Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b
  */
  */
@@ -6604,7 +7109,7 @@ static struct snd_kcontrol_new alc882_chmode_mixer[] = {
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
-static struct hda_verb alc882_init_verbs[] = {
+static struct hda_verb alc882_base_init_verbs[] = {
 	/* Front mixer: unmute input/output amp left and right (volume = 0) */
 	/* Front mixer: unmute input/output amp left and right (volume = 0) */
 	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
 	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
 	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
 	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
@@ -6622,6 +7127,13 @@ static struct hda_verb alc882_init_verbs[] = {
 	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
 	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
 	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
 	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
 
 
+	/* mute analog input loopbacks */
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+
 	/* Front Pin: output 0 (0x0c) */
 	/* Front Pin: output 0 (0x0c) */
 	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
 	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
@@ -6656,11 +7168,6 @@ static struct hda_verb alc882_init_verbs[] = {
 
 
 	/* FIXME: use matrix-type input source selection */
 	/* FIXME: use matrix-type input source selection */
 	/* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
 	/* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
-	/* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
-	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
 	/* Input mixer2 */
 	/* Input mixer2 */
 	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
 	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
@@ -6671,9 +7178,6 @@ static struct hda_verb alc882_init_verbs[] = {
 	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
 	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
 	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
 	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
 	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
 	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-	/* ADC1: mute amp left and right */
-	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-	{0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
 	/* ADC2: mute amp left and right */
 	/* ADC2: mute amp left and right */
 	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
 	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
 	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
 	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
@@ -6684,6 +7188,18 @@ static struct hda_verb alc882_init_verbs[] = {
 	{ }
 	{ }
 };
 };
 
 
+static struct hda_verb alc882_adc1_init_verbs[] = {
+	/* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+	/* ADC1: mute amp left and right */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{ }
+};
+
 static struct hda_verb alc882_eapd_verbs[] = {
 static struct hda_verb alc882_eapd_verbs[] = {
 	/* change to EAPD mode */
 	/* change to EAPD mode */
 	{0x20, AC_VERB_SET_COEF_INDEX, 0x07},
 	{0x20, AC_VERB_SET_COEF_INDEX, 0x07},
@@ -6691,18 +7207,122 @@ static struct hda_verb alc882_eapd_verbs[] = {
 	{ }
 	{ }
 };
 };
 
 
-/* Mac Pro test */
-static struct snd_kcontrol_new alc882_macpro_mixer[] = {
-	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
-	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
-	HDA_CODEC_MUTE("Headphone Playback Switch", 0x18, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT),
-	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT),
-	/* FIXME: this looks suspicious...
-	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x02, HDA_INPUT),
-	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x02, HDA_INPUT),
-	*/
-	{ } /* end */
+static struct hda_verb alc889_eapd_verbs[] = {
+	{0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
+	{0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
+	{ }
+};
+
+static struct hda_verb alc_hp15_unsol_verbs[] = {
+	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{}
+};
+
+static struct hda_verb alc885_init_verbs[] = {
+	/* Front mixer: unmute input/output amp left and right (volume = 0) */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	/* Rear mixer */
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	/* CLFE mixer */
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	/* Side mixer */
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+
+	/* mute analog input loopbacks */
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+
+	/* Front HP Pin: output 0 (0x0c) */
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* Front Pin: output 0 (0x0c) */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* Rear Pin: output 1 (0x0d) */
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x19, AC_VERB_SET_CONNECT_SEL, 0x01},
+	/* CLFE Pin: output 2 (0x0e) */
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x16, AC_VERB_SET_CONNECT_SEL, 0x02},
+	/* Side Pin: output 3 (0x0f) */
+	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
+	/* Mic (rear) pin: input vref at 80% */
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Front Mic pin: input vref at 80% */
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Line In pin: input */
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+
+	/* Mixer elements: 0x18, , 0x1a, 0x1b */
+	/* Input mixer1 */
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+	/* Input mixer2 */
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+	/* Input mixer3 */
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+	/* ADC2: mute amp left and right */
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	/* ADC3: mute amp left and right */
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+
+	{ }
+};
+
+static struct hda_verb alc885_init_input_verbs[] = {
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+	{ }
+};
+
+
+/* Unmute Selector 24h and set the default input to front mic */
+static struct hda_verb alc889_init_input_verbs[] = {
+	{0x24, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{ }
+};
+
+
+#define alc883_init_verbs	alc882_base_init_verbs
+
+/* Mac Pro test */
+static struct snd_kcontrol_new alc882_macpro_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x18, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x01, HDA_INPUT),
+	/* FIXME: this looks suspicious...
+	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	*/
+	{ } /* end */
 };
 };
 
 
 static struct hda_verb alc882_macpro_init_verbs[] = {
 static struct hda_verb alc882_macpro_init_verbs[] = {
@@ -6898,23 +7518,21 @@ static struct hda_verb alc885_imac24_init_verbs[] = {
 };
 };
 
 
 /* Toggle speaker-output according to the hp-jack state */
 /* Toggle speaker-output according to the hp-jack state */
-static void alc885_imac24_automute_init_hook(struct hda_codec *codec)
+static void alc885_imac24_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
 	spec->autocfg.hp_pins[0] = 0x14;
 	spec->autocfg.hp_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[0] = 0x18;
 	spec->autocfg.speaker_pins[0] = 0x18;
 	spec->autocfg.speaker_pins[1] = 0x1a;
 	spec->autocfg.speaker_pins[1] = 0x1a;
-	alc_automute_amp(codec);
 }
 }
 
 
-static void alc885_mbp3_init_hook(struct hda_codec *codec)
+static void alc885_mbp3_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
 	spec->autocfg.hp_pins[0] = 0x15;
 	spec->autocfg.hp_pins[0] = 0x15;
 	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[0] = 0x14;
-	alc_automute_amp(codec);
 }
 }
 
 
 
 
@@ -6942,13 +7560,12 @@ static void alc882_targa_automute(struct hda_codec *codec)
 				  spec->jack_present ? 1 : 3);
 				  spec->jack_present ? 1 : 3);
 }
 }
 
 
-static void alc882_targa_init_hook(struct hda_codec *codec)
+static void alc882_targa_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
 	spec->autocfg.hp_pins[0] = 0x14;
 	spec->autocfg.hp_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[0] = 0x1b;
 	spec->autocfg.speaker_pins[0] = 0x1b;
-	alc882_targa_automute(codec);
 }
 }
 
 
 static void alc882_targa_unsol_event(struct hda_codec *codec, unsigned int res)
 static void alc882_targa_unsol_event(struct hda_codec *codec, unsigned int res)
@@ -7019,907 +7636,85 @@ static void alc882_gpio_mute(struct hda_codec *codec, int pin, int muted)
 	snd_hda_codec_write(codec, codec->afg, 0,
 	snd_hda_codec_write(codec, codec->afg, 0,
 			    AC_VERB_SET_GPIO_DIRECTION, gpiodir);
 			    AC_VERB_SET_GPIO_DIRECTION, gpiodir);
 
 
-	msleep(1);
-
-	snd_hda_codec_write(codec, codec->afg, 0,
-			    AC_VERB_SET_GPIO_DATA, gpiostate);
-}
-
-/* set up GPIO at initialization */
-static void alc885_macpro_init_hook(struct hda_codec *codec)
-{
-	alc882_gpio_mute(codec, 0, 0);
-	alc882_gpio_mute(codec, 1, 0);
-}
-
-/* set up GPIO and update auto-muting at initialization */
-static void alc885_imac24_init_hook(struct hda_codec *codec)
-{
-	alc885_macpro_init_hook(codec);
-	alc885_imac24_automute_init_hook(codec);
-}
-
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc882_auto_init_verbs[] = {
-	/*
-	 * Unmute ADC0-2 and set the default input to mic-in
-	 */
-	{0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
-	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
-	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
-	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
-	 * mixer widget
-	 * Note: PASD motherboards uses the Line In 2 as the input for
-	 * front panel mic (mic 2)
-	 */
-	/* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
-	/*
-	 * Set up output mixers (0x0c - 0x0f)
-	 */
-	/* set vol=0 to output mixers */
-	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-	/* set up input amps for analog loopback */
-	/* Amp Indices: DAC = 0, mixer = 1 */
-	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
-	/* FIXME: use matrix-type input source selection */
-	/* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
-	/* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
-	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
-	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
-	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
-	/* Input mixer2 */
-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
-	/* Input mixer3 */
-	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
-	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
-	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
-
-	{ }
-};
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-#define alc882_loopbacks	alc880_loopbacks
-#endif
-
-/* pcm configuration: identical with ALC880 */
-#define alc882_pcm_analog_playback	alc880_pcm_analog_playback
-#define alc882_pcm_analog_capture	alc880_pcm_analog_capture
-#define alc882_pcm_digital_playback	alc880_pcm_digital_playback
-#define alc882_pcm_digital_capture	alc880_pcm_digital_capture
-
-/*
- * configuration and preset
- */
-static const char *alc882_models[ALC882_MODEL_LAST] = {
-	[ALC882_3ST_DIG]	= "3stack-dig",
-	[ALC882_6ST_DIG]	= "6stack-dig",
-	[ALC882_ARIMA]		= "arima",
-	[ALC882_W2JC]		= "w2jc",
-	[ALC882_TARGA]		= "targa",
-	[ALC882_ASUS_A7J]	= "asus-a7j",
-	[ALC882_ASUS_A7M]	= "asus-a7m",
-	[ALC885_MACPRO]		= "macpro",
-	[ALC885_MB5]		= "mb5",
-	[ALC885_MBP3]		= "mbp3",
-	[ALC885_IMAC24]		= "imac24",
-	[ALC882_AUTO]		= "auto",
-};
-
-static struct snd_pci_quirk alc882_cfg_tbl[] = {
-	SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC882_6ST_DIG),
-	SND_PCI_QUIRK(0x1043, 0x060d, "Asus A7J", ALC882_ASUS_A7J),
-	SND_PCI_QUIRK(0x1043, 0x1243, "Asus A7J", ALC882_ASUS_A7J),
-	SND_PCI_QUIRK(0x1043, 0x13c2, "Asus A7M", ALC882_ASUS_A7M),
-	SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_W2JC),
-	SND_PCI_QUIRK(0x1043, 0x817f, "Asus P5LD2", ALC882_6ST_DIG),
-	SND_PCI_QUIRK(0x1043, 0x81d8, "Asus P5WD", ALC882_6ST_DIG),
-	SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG),
-	SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P35 DS3R", ALC882_6ST_DIG),
-	SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8  */
-	SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG),
-	SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA),
-	{}
-};
-
-static struct alc_config_preset alc882_presets[] = {
-	[ALC882_3ST_DIG] = {
-		.mixers = { alc882_base_mixer },
-		.init_verbs = { alc882_init_verbs },
-		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
-		.dac_nids = alc882_dac_nids,
-		.dig_out_nid = ALC882_DIGOUT_NID,
-		.dig_in_nid = ALC882_DIGIN_NID,
-		.num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
-		.channel_mode = alc882_ch_modes,
-		.need_dac_fix = 1,
-		.input_mux = &alc882_capture_source,
-	},
-	[ALC882_6ST_DIG] = {
-		.mixers = { alc882_base_mixer, alc882_chmode_mixer },
-		.init_verbs = { alc882_init_verbs },
-		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
-		.dac_nids = alc882_dac_nids,
-		.dig_out_nid = ALC882_DIGOUT_NID,
-		.dig_in_nid = ALC882_DIGIN_NID,
-		.num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes),
-		.channel_mode = alc882_sixstack_modes,
-		.input_mux = &alc882_capture_source,
-	},
-	[ALC882_ARIMA] = {
-		.mixers = { alc882_base_mixer, alc882_chmode_mixer },
-		.init_verbs = { alc882_init_verbs, alc882_eapd_verbs },
-		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
-		.dac_nids = alc882_dac_nids,
-		.num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes),
-		.channel_mode = alc882_sixstack_modes,
-		.input_mux = &alc882_capture_source,
-	},
-	[ALC882_W2JC] = {
-		.mixers = { alc882_w2jc_mixer, alc882_chmode_mixer },
-		.init_verbs = { alc882_init_verbs, alc882_eapd_verbs,
-				alc880_gpio1_init_verbs },
-		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
-		.dac_nids = alc882_dac_nids,
-		.num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
-		.channel_mode = alc880_threestack_modes,
-		.need_dac_fix = 1,
-		.input_mux = &alc882_capture_source,
-		.dig_out_nid = ALC882_DIGOUT_NID,
-	},
-	[ALC885_MBP3] = {
-		.mixers = { alc885_mbp3_mixer, alc882_chmode_mixer },
-		.init_verbs = { alc885_mbp3_init_verbs,
-				alc880_gpio1_init_verbs },
-		.num_dacs = 2,
-		.dac_nids = alc882_dac_nids,
-		.hp_nid = 0x04,
-		.channel_mode = alc885_mbp_4ch_modes,
-		.num_channel_mode = ARRAY_SIZE(alc885_mbp_4ch_modes),
-		.input_mux = &alc882_capture_source,
-		.dig_out_nid = ALC882_DIGOUT_NID,
-		.dig_in_nid = ALC882_DIGIN_NID,
-		.unsol_event = alc_automute_amp_unsol_event,
-		.init_hook = alc885_mbp3_init_hook,
-	},
-	[ALC885_MB5] = {
-		.mixers = { alc885_mb5_mixer, alc882_chmode_mixer },
-		.init_verbs = { alc885_mb5_init_verbs,
-				alc880_gpio1_init_verbs },
-		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
-		.dac_nids = alc882_dac_nids,
-		.channel_mode = alc885_mb5_6ch_modes,
-		.num_channel_mode = ARRAY_SIZE(alc885_mb5_6ch_modes),
-		.input_mux = &mb5_capture_source,
-		.dig_out_nid = ALC882_DIGOUT_NID,
-		.dig_in_nid = ALC882_DIGIN_NID,
-	},
-	[ALC885_MACPRO] = {
-		.mixers = { alc882_macpro_mixer },
-		.init_verbs = { alc882_macpro_init_verbs },
-		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
-		.dac_nids = alc882_dac_nids,
-		.dig_out_nid = ALC882_DIGOUT_NID,
-		.dig_in_nid = ALC882_DIGIN_NID,
-		.num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
-		.channel_mode = alc882_ch_modes,
-		.input_mux = &alc882_capture_source,
-		.init_hook = alc885_macpro_init_hook,
-	},
-	[ALC885_IMAC24] = {
-		.mixers = { alc885_imac24_mixer },
-		.init_verbs = { alc885_imac24_init_verbs },
-		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
-		.dac_nids = alc882_dac_nids,
-		.dig_out_nid = ALC882_DIGOUT_NID,
-		.dig_in_nid = ALC882_DIGIN_NID,
-		.num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
-		.channel_mode = alc882_ch_modes,
-		.input_mux = &alc882_capture_source,
-		.unsol_event = alc_automute_amp_unsol_event,
-		.init_hook = alc885_imac24_init_hook,
-	},
-	[ALC882_TARGA] = {
-		.mixers = { alc882_targa_mixer, alc882_chmode_mixer },
-		.init_verbs = { alc882_init_verbs, alc880_gpio3_init_verbs,
-				alc882_targa_verbs},
-		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
-		.dac_nids = alc882_dac_nids,
-		.dig_out_nid = ALC882_DIGOUT_NID,
-		.num_adc_nids = ARRAY_SIZE(alc882_adc_nids),
-		.adc_nids = alc882_adc_nids,
-		.capsrc_nids = alc882_capsrc_nids,
-		.num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes),
-		.channel_mode = alc882_3ST_6ch_modes,
-		.need_dac_fix = 1,
-		.input_mux = &alc882_capture_source,
-		.unsol_event = alc882_targa_unsol_event,
-		.init_hook = alc882_targa_init_hook,
-	},
-	[ALC882_ASUS_A7J] = {
-		.mixers = { alc882_asus_a7j_mixer, alc882_chmode_mixer },
-		.init_verbs = { alc882_init_verbs, alc882_asus_a7j_verbs},
-		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
-		.dac_nids = alc882_dac_nids,
-		.dig_out_nid = ALC882_DIGOUT_NID,
-		.num_adc_nids = ARRAY_SIZE(alc882_adc_nids),
-		.adc_nids = alc882_adc_nids,
-		.capsrc_nids = alc882_capsrc_nids,
-		.num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes),
-		.channel_mode = alc882_3ST_6ch_modes,
-		.need_dac_fix = 1,
-		.input_mux = &alc882_capture_source,
-	},
-	[ALC882_ASUS_A7M] = {
-		.mixers = { alc882_asus_a7m_mixer, alc882_chmode_mixer },
-		.init_verbs = { alc882_init_verbs, alc882_eapd_verbs,
-				alc880_gpio1_init_verbs,
-				alc882_asus_a7m_verbs },
-		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
-		.dac_nids = alc882_dac_nids,
-		.dig_out_nid = ALC882_DIGOUT_NID,
-		.num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
-		.channel_mode = alc880_threestack_modes,
-		.need_dac_fix = 1,
-		.input_mux = &alc882_capture_source,
-	},
-};
-
-
-/*
- * Pin config fixes
- */
-enum {
-	PINFIX_ABIT_AW9D_MAX
-};
-
-static struct alc_pincfg alc882_abit_aw9d_pinfix[] = {
-	{ 0x15, 0x01080104 }, /* side */
-	{ 0x16, 0x01011012 }, /* rear */
-	{ 0x17, 0x01016011 }, /* clfe */
-	{ }
-};
-
-static const struct alc_pincfg *alc882_pin_fixes[] = {
-	[PINFIX_ABIT_AW9D_MAX] = alc882_abit_aw9d_pinfix,
-};
-
-static struct snd_pci_quirk alc882_pinfix_tbl[] = {
-	SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX),
-	{}
-};
-
-/*
- * BIOS auto configuration
- */
-static void alc882_auto_set_output_and_unmute(struct hda_codec *codec,
-					      hda_nid_t nid, int pin_type,
-					      int dac_idx)
-{
-	/* set as output */
-	struct alc_spec *spec = codec->spec;
-	int idx;
-
-	alc_set_pin_output(codec, nid, pin_type);
-	if (spec->multiout.dac_nids[dac_idx] == 0x25)
-		idx = 4;
-	else
-		idx = spec->multiout.dac_nids[dac_idx] - 2;
-	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx);
-
-}
-
-static void alc882_auto_init_multi_out(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	int i;
-
-	for (i = 0; i <= HDA_SIDE; i++) {
-		hda_nid_t nid = spec->autocfg.line_out_pins[i];
-		int pin_type = get_pin_type(spec->autocfg.line_out_type);
-		if (nid)
-			alc882_auto_set_output_and_unmute(codec, nid, pin_type,
-							  i);
-	}
-}
-
-static void alc882_auto_init_hp_out(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	hda_nid_t pin;
-
-	pin = spec->autocfg.hp_pins[0];
-	if (pin) /* connect to front */
-		/* use dac 0 */
-		alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
-	pin = spec->autocfg.speaker_pins[0];
-	if (pin)
-		alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
-}
-
-#define alc882_is_input_pin(nid)	alc880_is_input_pin(nid)
-#define ALC882_PIN_CD_NID		ALC880_PIN_CD_NID
-
-static void alc882_auto_init_analog_input(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	int i;
-
-	for (i = 0; i < AUTO_PIN_LAST; i++) {
-		hda_nid_t nid = spec->autocfg.input_pins[i];
-		if (!nid)
-			continue;
-		alc_set_input_pin(codec, nid, AUTO_PIN_FRONT_MIC /*i*/);
-		if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
-			snd_hda_codec_write(codec, nid, 0,
-					    AC_VERB_SET_AMP_GAIN_MUTE,
-					    AMP_OUT_MUTE);
-	}
-}
-
-static void alc882_auto_init_input_src(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	int c;
-
-	for (c = 0; c < spec->num_adc_nids; c++) {
-		hda_nid_t conn_list[HDA_MAX_NUM_INPUTS];
-		hda_nid_t nid = spec->capsrc_nids[c];
-		unsigned int mux_idx;
-		const struct hda_input_mux *imux;
-		int conns, mute, idx, item;
-
-		conns = snd_hda_get_connections(codec, nid, conn_list,
-						ARRAY_SIZE(conn_list));
-		if (conns < 0)
-			continue;
-		mux_idx = c >= spec->num_mux_defs ? 0 : c;
-		imux = &spec->input_mux[mux_idx];
-		for (idx = 0; idx < conns; idx++) {
-			/* if the current connection is the selected one,
-			 * unmute it as default - otherwise mute it
-			 */
-			mute = AMP_IN_MUTE(idx);
-			for (item = 0; item < imux->num_items; item++) {
-				if (imux->items[item].index == idx) {
-					if (spec->cur_mux[c] == item)
-						mute = AMP_IN_UNMUTE(idx);
-					break;
-				}
-			}
-			/* check if we have a selector or mixer
-			 * we could check for the widget type instead, but
-			 * just check for Amp-In presence (in case of mixer
-			 * without amp-in there is something wrong, this
-			 * function shouldn't be used or capsrc nid is wrong)
-			 */
-			if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)
-				snd_hda_codec_write(codec, nid, 0,
-						    AC_VERB_SET_AMP_GAIN_MUTE,
-						    mute);
-			else if (mute != AMP_IN_MUTE(idx))
-				snd_hda_codec_write(codec, nid, 0,
-						    AC_VERB_SET_CONNECT_SEL,
-						    idx);
-		}
-	}
-}
-
-/* add mic boosts if needed */
-static int alc_auto_add_mic_boost(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	int err;
-	hda_nid_t nid;
-
-	nid = spec->autocfg.input_pins[AUTO_PIN_MIC];
-	if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) {
-		err = add_control(spec, ALC_CTL_WIDGET_VOL,
-				  "Mic Boost",
-				  HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
-		if (err < 0)
-			return err;
-	}
-	nid = spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC];
-	if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) {
-		err = add_control(spec, ALC_CTL_WIDGET_VOL,
-				  "Front Mic Boost",
-				  HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
-		if (err < 0)
-			return err;
-	}
-	return 0;
-}
-
-/* almost identical with ALC880 parser... */
-static int alc882_parse_auto_config(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	int err = alc880_parse_auto_config(codec);
-
-	if (err < 0)
-		return err;
-	else if (!err)
-		return 0; /* no config found */
-
-	err = alc_auto_add_mic_boost(codec);
-	if (err < 0)
-		return err;
-
-	/* hack - override the init verbs */
-	spec->init_verbs[0] = alc882_auto_init_verbs;
-
-	return 1; /* config found */
-}
-
-/* additional initialization for auto-configuration model */
-static void alc882_auto_init(struct hda_codec *codec)
-{
-	struct alc_spec *spec = codec->spec;
-	alc882_auto_init_multi_out(codec);
-	alc882_auto_init_hp_out(codec);
-	alc882_auto_init_analog_input(codec);
-	alc882_auto_init_input_src(codec);
-	if (spec->unsol_event)
-		alc_inithook(codec);
-}
-
-static int patch_alc883(struct hda_codec *codec); /* called in patch_alc882() */
-
-static int patch_alc882(struct hda_codec *codec)
-{
-	struct alc_spec *spec;
-	int err, board_config;
-
-	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-	if (spec == NULL)
-		return -ENOMEM;
-
-	codec->spec = spec;
-
-	board_config = snd_hda_check_board_config(codec, ALC882_MODEL_LAST,
-						  alc882_models,
-						  alc882_cfg_tbl);
-
-	if (board_config < 0 || board_config >= ALC882_MODEL_LAST) {
-		/* Pick up systems that don't supply PCI SSID */
-		switch (codec->subsystem_id) {
-		case 0x106b0c00: /* Mac Pro */
-			board_config = ALC885_MACPRO;
-			break;
-		case 0x106b1000: /* iMac 24 */
-		case 0x106b2800: /* AppleTV */
-		case 0x106b3e00: /* iMac 24 Aluminium */
-			board_config = ALC885_IMAC24;
-			break;
-		case 0x106b00a0: /* MacBookPro3,1 - Another revision */
-		case 0x106b00a1: /* Macbook (might be wrong - PCI SSID?) */
-		case 0x106b00a4: /* MacbookPro4,1 */
-		case 0x106b2c00: /* Macbook Pro rev3 */
-		/* Macbook 3.1 (0x106b3600) is handled by patch_alc883() */
-		case 0x106b3800: /* MacbookPro4,1 - latter revision */
-			board_config = ALC885_MBP3;
-			break;
-		case 0x106b3f00: /* Macbook 5,1 */
-		case 0x106b4000: /* Macbook Pro 5,1 - FIXME: HP jack sense
-				  *   seems not working, so apparently
-				  *   no perfect solution yet
-				  */
-			board_config = ALC885_MB5;
-			break;
-		default:
-			/* ALC889A is handled better as ALC888-compatible */
-			if (codec->revision_id == 0x100101 ||
-			    codec->revision_id == 0x100103) {
-				alc_free(codec);
-				return patch_alc883(codec);
-			}
-			printk(KERN_INFO "hda_codec: Unknown model for %s, "
-			       "trying auto-probe from BIOS...\n",
-			       codec->chip_name);
-			board_config = ALC882_AUTO;
-		}
-	}
-
-	alc_fix_pincfg(codec, alc882_pinfix_tbl, alc882_pin_fixes);
-
-	if (board_config == ALC882_AUTO) {
-		/* automatic parse from the BIOS config */
-		err = alc882_parse_auto_config(codec);
-		if (err < 0) {
-			alc_free(codec);
-			return err;
-		} else if (!err) {
-			printk(KERN_INFO
-			       "hda_codec: Cannot set up configuration "
-			       "from BIOS.  Using base mode...\n");
-			board_config = ALC882_3ST_DIG;
-		}
-	}
-
-	err = snd_hda_attach_beep_device(codec, 0x1);
-	if (err < 0) {
-		alc_free(codec);
-		return err;
-	}
-
-	if (board_config != ALC882_AUTO)
-		setup_preset(spec, &alc882_presets[board_config]);
-
-	spec->stream_analog_playback = &alc882_pcm_analog_playback;
-	spec->stream_analog_capture = &alc882_pcm_analog_capture;
-	/* FIXME: setup DAC5 */
-	/*spec->stream_analog_alt_playback = &alc880_pcm_analog_alt_playback;*/
-	spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture;
-
-	spec->stream_digital_playback = &alc882_pcm_digital_playback;
-	spec->stream_digital_capture = &alc882_pcm_digital_capture;
-
-	if (!spec->adc_nids && spec->input_mux) {
-		/* check whether NID 0x07 is valid */
-		unsigned int wcap = get_wcaps(codec, 0x07);
-		/* get type */
-		wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
-		if (wcap != AC_WID_AUD_IN) {
-			spec->adc_nids = alc882_adc_nids_alt;
-			spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids_alt);
-			spec->capsrc_nids = alc882_capsrc_nids_alt;
-		} else {
-			spec->adc_nids = alc882_adc_nids;
-			spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids);
-			spec->capsrc_nids = alc882_capsrc_nids;
-		}
-	}
-	set_capture_mixer(spec);
-	set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
-
-	spec->vmaster_nid = 0x0c;
-
-	codec->patch_ops = alc_patch_ops;
-	if (board_config == ALC882_AUTO)
-		spec->init_hook = alc882_auto_init;
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-	if (!spec->loopback.amplist)
-		spec->loopback.amplist = alc882_loopbacks;
-#endif
-	codec->proc_widget_hook = print_realtek_coef;
-
-	return 0;
-}
-
-/*
- * ALC883 support
- *
- * ALC883 is almost identical with ALC880 but has cleaner and more flexible
- * configuration.  Each pin widget can choose any input DACs and a mixer.
- * Each ADC is connected from a mixer of all inputs.  This makes possible
- * 6-channel independent captures.
- *
- * In addition, an independent DAC for the multi-playback (not used in this
- * driver yet).
- */
-#define ALC883_DIGOUT_NID	0x06
-#define ALC883_DIGIN_NID	0x0a
-
-#define ALC1200_DIGOUT_NID	0x10
-
-static hda_nid_t alc883_dac_nids[4] = {
-	/* front, rear, clfe, rear_surr */
-	0x02, 0x03, 0x04, 0x05
-};
-
-static hda_nid_t alc883_adc_nids[2] = {
-	/* ADC1-2 */
-	0x08, 0x09,
-};
-
-static hda_nid_t alc883_adc_nids_alt[1] = {
-	/* ADC1 */
-	0x08,
-};
-
-static hda_nid_t alc883_adc_nids_rev[2] = {
-	/* ADC2-1 */
-	0x09, 0x08
-};
-
-#define alc889_adc_nids		alc880_adc_nids
-
-static hda_nid_t alc883_capsrc_nids[2] = { 0x23, 0x22 };
-
-static hda_nid_t alc883_capsrc_nids_rev[2] = { 0x22, 0x23 };
-
-#define alc889_capsrc_nids	alc882_capsrc_nids
-
-/* input MUX */
-/* FIXME: should be a matrix-type input source selection */
-
-static struct hda_input_mux alc883_capture_source = {
-	.num_items = 4,
-	.items = {
-		{ "Mic", 0x0 },
-		{ "Front Mic", 0x1 },
-		{ "Line", 0x2 },
-		{ "CD", 0x4 },
-	},
-};
-
-static struct hda_input_mux alc883_3stack_6ch_intel = {
-	.num_items = 4,
-	.items = {
-		{ "Mic", 0x1 },
-		{ "Front Mic", 0x0 },
-		{ "Line", 0x2 },
-		{ "CD", 0x4 },
-	},
-};
-
-static struct hda_input_mux alc883_lenovo_101e_capture_source = {
-	.num_items = 2,
-	.items = {
-		{ "Mic", 0x1 },
-		{ "Line", 0x2 },
-	},
-};
-
-static struct hda_input_mux alc883_lenovo_nb0763_capture_source = {
-	.num_items = 4,
-	.items = {
-		{ "Mic", 0x0 },
-		{ "iMic", 0x1 },
-		{ "Line", 0x2 },
-		{ "CD", 0x4 },
-	},
-};
-
-static struct hda_input_mux alc883_fujitsu_pi2515_capture_source = {
-	.num_items = 2,
-	.items = {
-		{ "Mic", 0x0 },
-		{ "Int Mic", 0x1 },
-	},
-};
-
-static struct hda_input_mux alc883_lenovo_sky_capture_source = {
-	.num_items = 3,
-	.items = {
-		{ "Mic", 0x0 },
-		{ "Front Mic", 0x1 },
-		{ "Line", 0x4 },
-	},
-};
-
-static struct hda_input_mux alc883_asus_eee1601_capture_source = {
-	.num_items = 2,
-	.items = {
-		{ "Mic", 0x0 },
-		{ "Line", 0x2 },
-	},
-};
-
-static struct hda_input_mux alc889A_mb31_capture_source = {
-	.num_items = 2,
-	.items = {
-		{ "Mic", 0x0 },
-		/* Front Mic (0x01) unused */
-		{ "Line", 0x2 },
-		/* Line 2 (0x03) unused */
-		/* CD (0x04) unsused? */
-	},
-};
-
-/*
- * 2ch mode
- */
-static struct hda_channel_mode alc883_3ST_2ch_modes[1] = {
-	{ 2, NULL }
-};
-
-/*
- * 2ch mode
- */
-static struct hda_verb alc883_3ST_ch2_init[] = {
-	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
-	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
-	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
-	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
-	{ } /* end */
-};
-
-/*
- * 4ch mode
- */
-static struct hda_verb alc883_3ST_ch4_init[] = {
-	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
-	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
-	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-	{ 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
-	{ } /* end */
-};
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc883_3ST_ch6_init[] = {
-	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-	{ 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
-	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-	{ 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
-	{ } /* end */
-};
-
-static struct hda_channel_mode alc883_3ST_6ch_modes[3] = {
-	{ 2, alc883_3ST_ch2_init },
-	{ 4, alc883_3ST_ch4_init },
-	{ 6, alc883_3ST_ch6_init },
-};
-
-
-/*
- * 2ch mode
- */
-static struct hda_verb alc883_4ST_ch2_init[] = {
-	{ 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-	{ 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
-	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
-	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
-	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
-	{ } /* end */
-};
-
-/*
- * 4ch mode
- */
-static struct hda_verb alc883_4ST_ch4_init[] = {
-	{ 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-	{ 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
-	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
-	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-	{ 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
-	{ } /* end */
-};
-
-/*
- * 6ch mode
- */
-static struct hda_verb alc883_4ST_ch6_init[] = {
-	{ 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-	{ 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-	{ 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
-	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-	{ 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
-	{ } /* end */
-};
-
-/*
- * 8ch mode
- */
-static struct hda_verb alc883_4ST_ch8_init[] = {
-	{ 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-	{ 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-	{ 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 },
-	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-	{ 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
-	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-	{ 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
-	{ } /* end */
-};
-
-static struct hda_channel_mode alc883_4ST_8ch_modes[4] = {
-	{ 2, alc883_4ST_ch2_init },
-	{ 4, alc883_4ST_ch4_init },
-	{ 6, alc883_4ST_ch6_init },
-	{ 8, alc883_4ST_ch8_init },
-};
-
-
-/*
- * 2ch mode
- */
-static struct hda_verb alc883_3ST_ch2_intel_init[] = {
-	{ 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
-	{ 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
-	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
-	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
-	{ } /* end */
-};
+	msleep(1);
 
 
-/*
- * 4ch mode
- */
-static struct hda_verb alc883_3ST_ch4_intel_init[] = {
-	{ 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
-	{ 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
-	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-	{ 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
-	{ } /* end */
-};
+	snd_hda_codec_write(codec, codec->afg, 0,
+			    AC_VERB_SET_GPIO_DATA, gpiostate);
+}
 
 
-/*
- * 6ch mode
- */
-static struct hda_verb alc883_3ST_ch6_intel_init[] = {
-	{ 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-	{ 0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-	{ 0x19, AC_VERB_SET_CONNECT_SEL, 0x02 },
-	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-	{ 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
-	{ } /* end */
-};
+/* set up GPIO at initialization */
+static void alc885_macpro_init_hook(struct hda_codec *codec)
+{
+	alc882_gpio_mute(codec, 0, 0);
+	alc882_gpio_mute(codec, 1, 0);
+}
 
 
-static struct hda_channel_mode alc883_3ST_6ch_intel_modes[3] = {
-	{ 2, alc883_3ST_ch2_intel_init },
-	{ 4, alc883_3ST_ch4_intel_init },
-	{ 6, alc883_3ST_ch6_intel_init },
-};
+/* set up GPIO and update auto-muting at initialization */
+static void alc885_imac24_init_hook(struct hda_codec *codec)
+{
+	alc885_macpro_init_hook(codec);
+	alc_automute_amp(codec);
+}
 
 
 /*
 /*
- * 6ch mode
+ * generic initialization of ADC, input mixers and output mixers
  */
  */
-static struct hda_verb alc883_sixstack_ch6_init[] = {
-	{ 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 },
-	{ 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-	{ 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-	{ 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-	{ } /* end */
-};
+static struct hda_verb alc883_auto_init_verbs[] = {
+	/*
+	 * Unmute ADC0-2 and set the default input to mic-in
+	 */
+	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 
 
-/*
- * 8ch mode
- */
-static struct hda_verb alc883_sixstack_ch8_init[] = {
-	{ 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-	{ 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-	{ 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-	{ 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-	{ } /* end */
-};
+	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+	 * mixer widget
+	 * Note: PASD motherboards uses the Line In 2 as the input for
+	 * front panel mic (mic 2)
+	 */
+	/* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
 
 
-static struct hda_channel_mode alc883_sixstack_modes[2] = {
-	{ 6, alc883_sixstack_ch6_init },
-	{ 8, alc883_sixstack_ch8_init },
+	/*
+	 * Set up output mixers (0x0c - 0x0f)
+	 */
+	/* set vol=0 to output mixers */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	/* set up input amps for analog loopback */
+	/* Amp Indices: DAC = 0, mixer = 1 */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+	/* FIXME: use matrix-type input source selection */
+	/* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
+	/* Input mixer2 */
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+	/* Input mixer3 */
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+
+	{ }
 };
 };
 
 
 /* 2ch mode (Speaker:front, Subwoofer:CLFE, Line:input, Headphones:front) */
 /* 2ch mode (Speaker:front, Subwoofer:CLFE, Line:input, Headphones:front) */
@@ -7972,34 +7767,7 @@ static struct hda_verb alc883_medion_eapd_verbs[] = {
 	{ }
 	{ }
 };
 };
 
 
-/* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17
- *                 Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b
- */
-
-static struct snd_kcontrol_new alc883_base_mixer[] = {
-	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
-	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
-	HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
-	HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
-	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
-	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
-	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
-	HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
-	HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
-	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
-	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
-	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
-	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
-	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
-	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
-	HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
-	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-	{ } /* end */
-};
+#define alc883_base_mixer	alc882_base_mixer
 
 
 static struct snd_kcontrol_new alc883_mitac_mixer[] = {
 static struct snd_kcontrol_new alc883_mitac_mixer[] = {
 	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
@@ -8110,6 +7878,30 @@ static struct snd_kcontrol_new alc883_3ST_6ch_intel_mixer[] = {
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
+static struct snd_kcontrol_new alc885_8ch_intel_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0,
+			      HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Speaker Playback Switch", 0x0f, 2, HDA_INPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x3, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x1b, 0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x3, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Boost", 0x18, 0, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	{ } /* end */
+};
+
 static struct snd_kcontrol_new alc883_fivestack_mixer[] = {
 static struct snd_kcontrol_new alc883_fivestack_mixer[] = {
 	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
 	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
@@ -8350,93 +8142,14 @@ static struct snd_kcontrol_new alc883_chmode_mixer[] = {
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
-static struct hda_verb alc883_init_verbs[] = {
-	/* ADC1: mute amp left and right */
-	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
-	/* ADC2: mute amp left and right */
-	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-	{0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
-	/* Front mixer: unmute input/output amp left and right (volume = 0) */
-	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-	/* Rear mixer */
-	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-	/* CLFE mixer */
-	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-	/* Side mixer */
-	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
-	/* mute analog input loopbacks */
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
-	/* Front Pin: output 0 (0x0c) */
-	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-	{0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
-	/* Rear Pin: output 1 (0x0d) */
-	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-	{0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
-	/* CLFE Pin: output 2 (0x0e) */
-	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-	{0x16, AC_VERB_SET_CONNECT_SEL, 0x02},
-	/* Side Pin: output 3 (0x0f) */
-	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-	{0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
-	/* Mic (rear) pin: input vref at 80% */
-	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-	/* Front Mic pin: input vref at 80% */
-	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-	/* Line In pin: input */
-	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-	/* Line-2 In: Headphone output (output 0 - 0x0c) */
-	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
-	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-	{0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
-	/* CD pin widget for input */
-	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
-	/* FIXME: use matrix-type input source selection */
-	/* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
-	/* Input mixer2 */
-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-	/* Input mixer3 */
-	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-	{ }
-};
-
 /* toggle speaker-output according to the hp-jack state */
 /* toggle speaker-output according to the hp-jack state */
-static void alc883_mitac_init_hook(struct hda_codec *codec)
+static void alc883_mitac_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
 	spec->autocfg.hp_pins[0] = 0x15;
 	spec->autocfg.hp_pins[0] = 0x15;
 	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[1] = 0x17;
 	spec->autocfg.speaker_pins[1] = 0x17;
-	alc_automute_amp(codec);
 }
 }
 
 
 /* auto-toggle front mic */
 /* auto-toggle front mic */
@@ -8453,17 +8166,33 @@ static void alc883_mitac_mic_automute(struct hda_codec *codec)
 }
 }
 */
 */
 
 
-static struct hda_verb alc883_mitac_verbs[] = {
+static struct hda_verb alc883_mitac_verbs[] = {
+	/* HP */
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	/* Subwoofer */
+	{0x17, AC_VERB_SET_CONNECT_SEL, 0x02},
+	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+
+	/* enable unsolicited event */
+	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+	/* {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, */
+
+	{ } /* end */
+};
+
+static struct hda_verb alc883_clevo_m540r_verbs[] = {
 	/* HP */
 	/* HP */
 	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
 	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
 	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
 	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
-	/* Subwoofer */
-	{0x17, AC_VERB_SET_CONNECT_SEL, 0x02},
-	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	/* Int speaker */
+	/*{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},*/
 
 
 	/* enable unsolicited event */
 	/* enable unsolicited event */
+	/*
 	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
 	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
-	/* {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN}, */
+	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
+	*/
 
 
 	{ } /* end */
 	{ } /* end */
 };
 };
@@ -8591,7 +8320,7 @@ static struct hda_verb alc883_vaiott_verbs[] = {
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
-static void alc888_3st_hp_init_hook(struct hda_codec *codec)
+static void alc888_3st_hp_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
@@ -8599,7 +8328,6 @@ static void alc888_3st_hp_init_hook(struct hda_codec *codec)
 	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[1] = 0x16;
 	spec->autocfg.speaker_pins[1] = 0x16;
 	spec->autocfg.speaker_pins[2] = 0x18;
 	spec->autocfg.speaker_pins[2] = 0x18;
-	alc_automute_amp(codec);
 }
 }
 
 
 static struct hda_verb alc888_3st_hp_verbs[] = {
 static struct hda_verb alc888_3st_hp_verbs[] = {
@@ -8696,13 +8424,12 @@ static struct hda_verb alc883_medion_md2_verbs[] = {
 };
 };
 
 
 /* toggle speaker-output according to the hp-jack state */
 /* toggle speaker-output according to the hp-jack state */
-static void alc883_medion_md2_init_hook(struct hda_codec *codec)
+static void alc883_medion_md2_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
 	spec->autocfg.hp_pins[0] = 0x14;
 	spec->autocfg.hp_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[0] = 0x15;
 	spec->autocfg.speaker_pins[0] = 0x15;
-	alc_automute_amp(codec);
 }
 }
 
 
 /* toggle speaker-output according to the hp-jack state */
 /* toggle speaker-output according to the hp-jack state */
@@ -8719,12 +8446,16 @@ static void alc883_clevo_m720_mic_automute(struct hda_codec *codec)
 				 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
 				 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
 }
 }
 
 
-static void alc883_clevo_m720_init_hook(struct hda_codec *codec)
+static void alc883_clevo_m720_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
 	spec->autocfg.hp_pins[0] = 0x15;
 	spec->autocfg.hp_pins[0] = 0x15;
 	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[0] = 0x14;
+}
+
+static void alc883_clevo_m720_init_hook(struct hda_codec *codec)
+{
 	alc_automute_amp(codec);
 	alc_automute_amp(codec);
 	alc883_clevo_m720_mic_automute(codec);
 	alc883_clevo_m720_mic_automute(codec);
 }
 }
@@ -8743,22 +8474,20 @@ static void alc883_clevo_m720_unsol_event(struct hda_codec *codec,
 }
 }
 
 
 /* toggle speaker-output according to the hp-jack state */
 /* toggle speaker-output according to the hp-jack state */
-static void alc883_2ch_fujitsu_pi2515_init_hook(struct hda_codec *codec)
+static void alc883_2ch_fujitsu_pi2515_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
 	spec->autocfg.hp_pins[0] = 0x14;
 	spec->autocfg.hp_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[0] = 0x15;
 	spec->autocfg.speaker_pins[0] = 0x15;
-	alc_automute_amp(codec);
 }
 }
 
 
-static void alc883_haier_w66_init_hook(struct hda_codec *codec)
+static void alc883_haier_w66_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
 	spec->autocfg.hp_pins[0] = 0x1b;
 	spec->autocfg.hp_pins[0] = 0x1b;
 	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[0] = 0x14;
-	alc_automute_amp(codec);
 }
 }
 
 
 static void alc883_lenovo_101e_ispeaker_automute(struct hda_codec *codec)
 static void alc883_lenovo_101e_ispeaker_automute(struct hda_codec *codec)
@@ -8797,14 +8526,13 @@ static void alc883_lenovo_101e_unsol_event(struct hda_codec *codec,
 }
 }
 
 
 /* toggle speaker-output according to the hp-jack state */
 /* toggle speaker-output according to the hp-jack state */
-static void alc883_acer_aspire_init_hook(struct hda_codec *codec)
+static void alc883_acer_aspire_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
 	spec->autocfg.hp_pins[0] = 0x14;
 	spec->autocfg.hp_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[0] = 0x15;
 	spec->autocfg.speaker_pins[0] = 0x15;
 	spec->autocfg.speaker_pins[1] = 0x16;
 	spec->autocfg.speaker_pins[1] = 0x16;
-	alc_automute_amp(codec);
 }
 }
 
 
 static struct hda_verb alc883_acer_eapd_verbs[] = {
 static struct hda_verb alc883_acer_eapd_verbs[] = {
@@ -8825,7 +8553,14 @@ static struct hda_verb alc883_acer_eapd_verbs[] = {
 	{ }
 	{ }
 };
 };
 
 
-static void alc888_6st_dell_init_hook(struct hda_codec *codec)
+static struct hda_verb alc888_acer_aspire_7730G_verbs[] = {
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x17, AC_VERB_SET_CONNECT_SEL, 0x02},
+	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+	{ } /* end */
+};
+
+static void alc888_6st_dell_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
@@ -8834,10 +8569,9 @@ static void alc888_6st_dell_init_hook(struct hda_codec *codec)
 	spec->autocfg.speaker_pins[1] = 0x15;
 	spec->autocfg.speaker_pins[1] = 0x15;
 	spec->autocfg.speaker_pins[2] = 0x16;
 	spec->autocfg.speaker_pins[2] = 0x16;
 	spec->autocfg.speaker_pins[3] = 0x17;
 	spec->autocfg.speaker_pins[3] = 0x17;
-	alc_automute_amp(codec);
 }
 }
 
 
-static void alc888_lenovo_sky_init_hook(struct hda_codec *codec)
+static void alc888_lenovo_sky_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
@@ -8847,82 +8581,17 @@ static void alc888_lenovo_sky_init_hook(struct hda_codec *codec)
 	spec->autocfg.speaker_pins[2] = 0x16;
 	spec->autocfg.speaker_pins[2] = 0x16;
 	spec->autocfg.speaker_pins[3] = 0x17;
 	spec->autocfg.speaker_pins[3] = 0x17;
 	spec->autocfg.speaker_pins[4] = 0x1a;
 	spec->autocfg.speaker_pins[4] = 0x1a;
-	alc_automute_amp(codec);
 }
 }
 
 
-static void alc883_vaiott_init_hook(struct hda_codec *codec)
+static void alc883_vaiott_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
 	spec->autocfg.hp_pins[0] = 0x15;
 	spec->autocfg.hp_pins[0] = 0x15;
 	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[1] = 0x17;
 	spec->autocfg.speaker_pins[1] = 0x17;
-	alc_automute_amp(codec);
 }
 }
 
 
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc883_auto_init_verbs[] = {
-	/*
-	 * Unmute ADC0-2 and set the default input to mic-in
-	 */
-	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
-	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
-	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
-	 * mixer widget
-	 * Note: PASD motherboards uses the Line In 2 as the input for
-	 * front panel mic (mic 2)
-	 */
-	/* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
-	/*
-	 * Set up output mixers (0x0c - 0x0f)
-	 */
-	/* set vol=0 to output mixers */
-	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-	/* set up input amps for analog loopback */
-	/* Amp Indices: DAC = 0, mixer = 1 */
-	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
-	/* FIXME: use matrix-type input source selection */
-	/* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
-	/* Input mixer1 */
-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-	/* {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, */
-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-	/* Input mixer2 */
-	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-	/* {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, */
-	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
-
-	{ }
-};
-
 static struct hda_verb alc888_asus_m90v_verbs[] = {
 static struct hda_verb alc888_asus_m90v_verbs[] = {
 	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
 	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
 	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
 	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
@@ -8933,19 +8602,7 @@ static struct hda_verb alc888_asus_m90v_verbs[] = {
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
-static void alc883_nb_mic_automute(struct hda_codec *codec)
-{
-	unsigned int present;
-
-	present = snd_hda_codec_read(codec, 0x18, 0,
-				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-			    0x7000 | (0x00 << 8) | (present ? 0 : 0x80));
-	snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-			    0x7000 | (0x01 << 8) | (present ? 0x80 : 0));
-}
-
-static void alc883_M90V_init_hook(struct hda_codec *codec)
+static void alc883_mode2_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
@@ -8953,26 +8610,11 @@ static void alc883_M90V_init_hook(struct hda_codec *codec)
 	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[1] = 0x15;
 	spec->autocfg.speaker_pins[1] = 0x15;
 	spec->autocfg.speaker_pins[2] = 0x16;
 	spec->autocfg.speaker_pins[2] = 0x16;
-	alc_automute_pin(codec);
-}
-
-static void alc883_mode2_unsol_event(struct hda_codec *codec,
-					   unsigned int res)
-{
-	switch (res >> 26) {
-	case ALC880_MIC_EVENT:
-		alc883_nb_mic_automute(codec);
-		break;
-	default:
-		alc_sku_unsol_event(codec, res);
-		break;
-	}
-}
-
-static void alc883_mode2_inithook(struct hda_codec *codec)
-{
-	alc883_M90V_init_hook(codec);
-	alc883_nb_mic_automute(codec);
+	spec->ext_mic.pin = 0x18;
+	spec->int_mic.pin = 0x19;
+	spec->ext_mic.mux_idx = 0;
+	spec->int_mic.mux_idx = 1;
+	spec->auto_mic = 1;
 }
 }
 
 
 static struct hda_verb alc888_asus_eee1601_verbs[] = {
 static struct hda_verb alc888_asus_eee1601_verbs[] = {
@@ -9033,25 +8675,44 @@ static void alc889A_mb31_unsol_event(struct hda_codec *codec, unsigned int res)
 		alc889A_mb31_automute(codec);
 		alc889A_mb31_automute(codec);
 }
 }
 
 
+
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #ifdef CONFIG_SND_HDA_POWER_SAVE
-#define alc883_loopbacks	alc880_loopbacks
+#define alc882_loopbacks	alc880_loopbacks
 #endif
 #endif
 
 
 /* pcm configuration: identical with ALC880 */
 /* pcm configuration: identical with ALC880 */
-#define alc883_pcm_analog_playback	alc880_pcm_analog_playback
-#define alc883_pcm_analog_capture	alc880_pcm_analog_capture
-#define alc883_pcm_analog_alt_capture	alc880_pcm_analog_alt_capture
-#define alc883_pcm_digital_playback	alc880_pcm_digital_playback
-#define alc883_pcm_digital_capture	alc880_pcm_digital_capture
+#define alc882_pcm_analog_playback	alc880_pcm_analog_playback
+#define alc882_pcm_analog_capture	alc880_pcm_analog_capture
+#define alc882_pcm_digital_playback	alc880_pcm_digital_playback
+#define alc882_pcm_digital_capture	alc880_pcm_digital_capture
+
+static hda_nid_t alc883_slave_dig_outs[] = {
+	ALC1200_DIGOUT_NID, 0,
+};
+
+static hda_nid_t alc1200_slave_dig_outs[] = {
+	ALC883_DIGOUT_NID, 0,
+};
 
 
 /*
 /*
  * configuration and preset
  * configuration and preset
  */
  */
-static const char *alc883_models[ALC883_MODEL_LAST] = {
-	[ALC883_3ST_2ch_DIG]	= "3stack-dig",
+static const char *alc882_models[ALC882_MODEL_LAST] = {
+	[ALC882_3ST_DIG]	= "3stack-dig",
+	[ALC882_6ST_DIG]	= "6stack-dig",
+	[ALC882_ARIMA]		= "arima",
+	[ALC882_W2JC]		= "w2jc",
+	[ALC882_TARGA]		= "targa",
+	[ALC882_ASUS_A7J]	= "asus-a7j",
+	[ALC882_ASUS_A7M]	= "asus-a7m",
+	[ALC885_MACPRO]		= "macpro",
+	[ALC885_MB5]		= "mb5",
+	[ALC885_MBP3]		= "mbp3",
+	[ALC885_IMAC24]		= "imac24",
+	[ALC883_3ST_2ch_DIG]	= "3stack-2ch-dig",
 	[ALC883_3ST_6ch_DIG]	= "3stack-6ch-dig",
 	[ALC883_3ST_6ch_DIG]	= "3stack-6ch-dig",
 	[ALC883_3ST_6ch]	= "3stack-6ch",
 	[ALC883_3ST_6ch]	= "3stack-6ch",
-	[ALC883_6ST_DIG]	= "6stack-dig",
+	[ALC883_6ST_DIG]	= "alc883-6stack-dig",
 	[ALC883_TARGA_DIG]	= "targa-dig",
 	[ALC883_TARGA_DIG]	= "targa-dig",
 	[ALC883_TARGA_2ch_DIG]	= "targa-2ch-dig",
 	[ALC883_TARGA_2ch_DIG]	= "targa-2ch-dig",
 	[ALC883_TARGA_8ch_DIG]	= "targa-8ch-dig",
 	[ALC883_TARGA_8ch_DIG]	= "targa-8ch-dig",
@@ -9060,6 +8721,7 @@ static const char *alc883_models[ALC883_MODEL_LAST] = {
 	[ALC888_ACER_ASPIRE_4930G]	= "acer-aspire-4930g",
 	[ALC888_ACER_ASPIRE_4930G]	= "acer-aspire-4930g",
 	[ALC888_ACER_ASPIRE_6530G]	= "acer-aspire-6530g",
 	[ALC888_ACER_ASPIRE_6530G]	= "acer-aspire-6530g",
 	[ALC888_ACER_ASPIRE_8930G]	= "acer-aspire-8930g",
 	[ALC888_ACER_ASPIRE_8930G]	= "acer-aspire-8930g",
+	[ALC888_ACER_ASPIRE_7730G]	= "acer-aspire-7730g",
 	[ALC883_MEDION]		= "medion",
 	[ALC883_MEDION]		= "medion",
 	[ALC883_MEDION_MD2]	= "medion-md2",
 	[ALC883_MEDION_MD2]	= "medion-md2",
 	[ALC883_LAPTOP_EAPD]	= "laptop-eapd",
 	[ALC883_LAPTOP_EAPD]	= "laptop-eapd",
@@ -9071,18 +8733,22 @@ static const char *alc883_models[ALC883_MODEL_LAST] = {
 	[ALC888_3ST_HP]		= "3stack-hp",
 	[ALC888_3ST_HP]		= "3stack-hp",
 	[ALC888_6ST_DELL]	= "6stack-dell",
 	[ALC888_6ST_DELL]	= "6stack-dell",
 	[ALC883_MITAC]		= "mitac",
 	[ALC883_MITAC]		= "mitac",
+	[ALC883_CLEVO_M540R]	= "clevo-m540r",
 	[ALC883_CLEVO_M720]	= "clevo-m720",
 	[ALC883_CLEVO_M720]	= "clevo-m720",
 	[ALC883_FUJITSU_PI2515] = "fujitsu-pi2515",
 	[ALC883_FUJITSU_PI2515] = "fujitsu-pi2515",
 	[ALC888_FUJITSU_XA3530] = "fujitsu-xa3530",
 	[ALC888_FUJITSU_XA3530] = "fujitsu-xa3530",
 	[ALC883_3ST_6ch_INTEL]	= "3stack-6ch-intel",
 	[ALC883_3ST_6ch_INTEL]	= "3stack-6ch-intel",
+	[ALC889A_INTEL]		= "intel-alc889a",
+	[ALC889_INTEL]		= "intel-x58",
 	[ALC1200_ASUS_P5Q]	= "asus-p5q",
 	[ALC1200_ASUS_P5Q]	= "asus-p5q",
 	[ALC889A_MB31]		= "mb31",
 	[ALC889A_MB31]		= "mb31",
 	[ALC883_SONY_VAIO_TT]	= "sony-vaio-tt",
 	[ALC883_SONY_VAIO_TT]	= "sony-vaio-tt",
-	[ALC883_AUTO]		= "auto",
+	[ALC882_AUTO]		= "auto",
 };
 };
 
 
-static struct snd_pci_quirk alc883_cfg_tbl[] = {
-	SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC883_3ST_6ch_DIG),
+static struct snd_pci_quirk alc882_cfg_tbl[] = {
+	SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC882_6ST_DIG),
+
 	SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE),
 	SND_PCI_QUIRK(0x1025, 0x006c, "Acer Aspire 9810", ALC883_ACER_ASPIRE),
 	SND_PCI_QUIRK(0x1025, 0x0090, "Acer Aspire", ALC883_ACER_ASPIRE),
 	SND_PCI_QUIRK(0x1025, 0x0090, "Acer Aspire", ALC883_ACER_ASPIRE),
 	SND_PCI_QUIRK(0x1025, 0x010a, "Acer Ferrari 5000", ALC883_ACER_ASPIRE),
 	SND_PCI_QUIRK(0x1025, 0x010a, "Acer Ferrari 5000", ALC883_ACER_ASPIRE),
@@ -9097,40 +8763,56 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
 		ALC888_ACER_ASPIRE_8930G),
 		ALC888_ACER_ASPIRE_8930G),
 	SND_PCI_QUIRK(0x1025, 0x0146, "Acer Aspire 6935G",
 	SND_PCI_QUIRK(0x1025, 0x0146, "Acer Aspire 6935G",
 		ALC888_ACER_ASPIRE_8930G),
 		ALC888_ACER_ASPIRE_8930G),
-	SND_PCI_QUIRK(0x1025, 0x0157, "Acer X3200", ALC883_AUTO),
-	SND_PCI_QUIRK(0x1025, 0x0158, "Acer AX1700-U3700A", ALC883_AUTO),
+	SND_PCI_QUIRK(0x1025, 0x0157, "Acer X3200", ALC882_AUTO),
+	SND_PCI_QUIRK(0x1025, 0x0158, "Acer AX1700-U3700A", ALC882_AUTO),
 	SND_PCI_QUIRK(0x1025, 0x015e, "Acer Aspire 6930G",
 	SND_PCI_QUIRK(0x1025, 0x015e, "Acer Aspire 6930G",
 		ALC888_ACER_ASPIRE_6530G),
 		ALC888_ACER_ASPIRE_6530G),
 	SND_PCI_QUIRK(0x1025, 0x0166, "Acer Aspire 6530G",
 	SND_PCI_QUIRK(0x1025, 0x0166, "Acer Aspire 6530G",
 		ALC888_ACER_ASPIRE_6530G),
 		ALC888_ACER_ASPIRE_6530G),
+	SND_PCI_QUIRK(0x1025, 0x0142, "Acer Aspire 7730G",
+		ALC888_ACER_ASPIRE_7730G),
 	/* default Acer -- disabled as it causes more problems.
 	/* default Acer -- disabled as it causes more problems.
 	 *    model=auto should work fine now
 	 *    model=auto should work fine now
 	 */
 	 */
 	/* SND_PCI_QUIRK_VENDOR(0x1025, "Acer laptop", ALC883_ACER), */
 	/* SND_PCI_QUIRK_VENDOR(0x1025, "Acer laptop", ALC883_ACER), */
+
 	SND_PCI_QUIRK(0x1028, 0x020d, "Dell Inspiron 530", ALC888_6ST_DELL),
 	SND_PCI_QUIRK(0x1028, 0x020d, "Dell Inspiron 530", ALC888_6ST_DELL),
+
 	SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavillion", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavillion", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP),
 	SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP),
 	SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP),
 	SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP),
 	SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x103c, 0x2a66, "HP Acacia", ALC888_3ST_HP),
 	SND_PCI_QUIRK(0x103c, 0x2a66, "HP Acacia", ALC888_3ST_HP),
 	SND_PCI_QUIRK(0x103c, 0x2a72, "HP Educ.ar", ALC888_3ST_HP),
 	SND_PCI_QUIRK(0x103c, 0x2a72, "HP Educ.ar", ALC888_3ST_HP),
+
+	SND_PCI_QUIRK(0x1043, 0x060d, "Asus A7J", ALC882_ASUS_A7J),
+	SND_PCI_QUIRK(0x1043, 0x1243, "Asus A7J", ALC882_ASUS_A7J),
+	SND_PCI_QUIRK(0x1043, 0x13c2, "Asus A7M", ALC882_ASUS_A7M),
 	SND_PCI_QUIRK(0x1043, 0x1873, "Asus M90V", ALC888_ASUS_M90V),
 	SND_PCI_QUIRK(0x1043, 0x1873, "Asus M90V", ALC888_ASUS_M90V),
+	SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_W2JC),
+	SND_PCI_QUIRK(0x1043, 0x817f, "Asus P5LD2", ALC882_6ST_DIG),
+	SND_PCI_QUIRK(0x1043, 0x81d8, "Asus P5WD", ALC882_6ST_DIG),
 	SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG),
 	SND_PCI_QUIRK(0x1043, 0x8249, "Asus M2A-VM HDMI", ALC883_3ST_6ch_DIG),
 	SND_PCI_QUIRK(0x1043, 0x8284, "Asus Z37E", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x1043, 0x8284, "Asus Z37E", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x1043, 0x82fe, "Asus P5Q-EM HDMI", ALC1200_ASUS_P5Q),
 	SND_PCI_QUIRK(0x1043, 0x82fe, "Asus P5Q-EM HDMI", ALC1200_ASUS_P5Q),
 	SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_ASUS_EEE1601),
 	SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_ASUS_EEE1601),
+
+	SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC883_SONY_VAIO_TT),
 	SND_PCI_QUIRK(0x105b, 0x0ce8, "Foxconn P35AX-S", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x105b, 0x0ce8, "Foxconn P35AX-S", ALC883_6ST_DIG),
-	SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG),
+	SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC882_6ST_DIG),
 	SND_PCI_QUIRK(0x1071, 0x8227, "Mitac 82801H", ALC883_MITAC),
 	SND_PCI_QUIRK(0x1071, 0x8227, "Mitac 82801H", ALC883_MITAC),
 	SND_PCI_QUIRK(0x1071, 0x8253, "Mitac 8252d", ALC883_MITAC),
 	SND_PCI_QUIRK(0x1071, 0x8253, "Mitac 8252d", ALC883_MITAC),
 	SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD),
 	SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD),
 	SND_PCI_QUIRK(0x10f1, 0x2350, "TYAN-S2350", ALC888_6ST_DELL),
 	SND_PCI_QUIRK(0x10f1, 0x2350, "TYAN-S2350", ALC888_6ST_DELL),
 	SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch),
 	SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch),
-	SND_PCI_QUIRK(0x1458, 0xa002, "MSI", ALC883_6ST_DIG),
+	SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P35 DS3R", ALC882_6ST_DIG),
+
 	SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG),
 	SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG),
 	SND_PCI_QUIRK(0x1462, 0x040d, "MSI", ALC883_TARGA_2ch_DIG),
 	SND_PCI_QUIRK(0x1462, 0x040d, "MSI", ALC883_TARGA_2ch_DIG),
 	SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG),
 	SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG),
+	SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8  */
 	SND_PCI_QUIRK(0x1462, 0x2fb3, "MSI", ALC883_TARGA_2ch_DIG),
 	SND_PCI_QUIRK(0x1462, 0x2fb3, "MSI", ALC883_TARGA_2ch_DIG),
+	SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC882_6ST_DIG),
 	SND_PCI_QUIRK(0x1462, 0x3729, "MSI S420", ALC883_TARGA_DIG),
 	SND_PCI_QUIRK(0x1462, 0x3729, "MSI S420", ALC883_TARGA_DIG),
 	SND_PCI_QUIRK(0x1462, 0x3783, "NEC S970", ALC883_TARGA_DIG),
 	SND_PCI_QUIRK(0x1462, 0x3783, "NEC S970", ALC883_TARGA_DIG),
 	SND_PCI_QUIRK(0x1462, 0x3b7f, "MSI", ALC883_TARGA_2ch_DIG),
 	SND_PCI_QUIRK(0x1462, 0x3b7f, "MSI", ALC883_TARGA_2ch_DIG),
@@ -9139,6 +8821,7 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x1462, 0x3fc3, "MSI", ALC883_TARGA_DIG),
 	SND_PCI_QUIRK(0x1462, 0x3fc3, "MSI", ALC883_TARGA_DIG),
 	SND_PCI_QUIRK(0x1462, 0x3fcc, "MSI", ALC883_TARGA_DIG),
 	SND_PCI_QUIRK(0x1462, 0x3fcc, "MSI", ALC883_TARGA_DIG),
 	SND_PCI_QUIRK(0x1462, 0x3fdf, "MSI", ALC883_TARGA_DIG),
 	SND_PCI_QUIRK(0x1462, 0x3fdf, "MSI", ALC883_TARGA_DIG),
+	SND_PCI_QUIRK(0x1462, 0x42cd, "MSI", ALC883_TARGA_DIG),
 	SND_PCI_QUIRK(0x1462, 0x4314, "MSI", ALC883_TARGA_DIG),
 	SND_PCI_QUIRK(0x1462, 0x4314, "MSI", ALC883_TARGA_DIG),
 	SND_PCI_QUIRK(0x1462, 0x4319, "MSI", ALC883_TARGA_DIG),
 	SND_PCI_QUIRK(0x1462, 0x4319, "MSI", ALC883_TARGA_DIG),
 	SND_PCI_QUIRK(0x1462, 0x4324, "MSI", ALC883_TARGA_DIG),
 	SND_PCI_QUIRK(0x1462, 0x4324, "MSI", ALC883_TARGA_DIG),
@@ -9152,11 +8835,15 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x1462, 0x7350, "MSI", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x1462, 0x7350, "MSI", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x1462, 0xa422, "MSI", ALC883_TARGA_2ch_DIG),
 	SND_PCI_QUIRK(0x1462, 0xa422, "MSI", ALC883_TARGA_2ch_DIG),
+	SND_PCI_QUIRK(0x1462, 0xaa08, "MSI", ALC883_TARGA_2ch_DIG),
+
 	SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x1558, 0x0721, "Clevo laptop M720R", ALC883_CLEVO_M720),
 	SND_PCI_QUIRK(0x1558, 0x0721, "Clevo laptop M720R", ALC883_CLEVO_M720),
 	SND_PCI_QUIRK(0x1558, 0x0722, "Clevo laptop M720SR", ALC883_CLEVO_M720),
 	SND_PCI_QUIRK(0x1558, 0x0722, "Clevo laptop M720SR", ALC883_CLEVO_M720),
+	SND_PCI_QUIRK(0x1558, 0x5409, "Clevo laptop M540R", ALC883_CLEVO_M540R),
 	SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC883_LAPTOP_EAPD),
 	SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC883_LAPTOP_EAPD),
 	SND_PCI_QUIRK(0x15d9, 0x8780, "Supermicro PDSBA", ALC883_3ST_6ch),
 	SND_PCI_QUIRK(0x15d9, 0x8780, "Supermicro PDSBA", ALC883_3ST_6ch),
+	/* SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA), */
 	SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION),
 	SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION),
 	SND_PCI_QUIRK_MASK(0x1734, 0xfff0, 0x1100, "FSC AMILO Xi/Pi25xx",
 	SND_PCI_QUIRK_MASK(0x1734, 0xfff0, 0x1100, "FSC AMILO Xi/Pi25xx",
 		      ALC883_FUJITSU_PI2515),
 		      ALC883_FUJITSU_PI2515),
@@ -9171,24 +8858,186 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x17c0, 0x4085, "MEDION MD96630", ALC888_LENOVO_MS7195_DIG),
 	SND_PCI_QUIRK(0x17c0, 0x4085, "MEDION MD96630", ALC888_LENOVO_MS7195_DIG),
 	SND_PCI_QUIRK(0x17f2, 0x5000, "Albatron KI690-AM2", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x17f2, 0x5000, "Albatron KI690-AM2", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x1991, 0x5625, "Haier W66", ALC883_HAIER_W66),
 	SND_PCI_QUIRK(0x1991, 0x5625, "Haier W66", ALC883_HAIER_W66),
+
 	SND_PCI_QUIRK(0x8086, 0x0001, "DG33BUC", ALC883_3ST_6ch_INTEL),
 	SND_PCI_QUIRK(0x8086, 0x0001, "DG33BUC", ALC883_3ST_6ch_INTEL),
 	SND_PCI_QUIRK(0x8086, 0x0002, "DG33FBC", ALC883_3ST_6ch_INTEL),
 	SND_PCI_QUIRK(0x8086, 0x0002, "DG33FBC", ALC883_3ST_6ch_INTEL),
 	SND_PCI_QUIRK(0x8086, 0x2503, "82801H", ALC883_MITAC),
 	SND_PCI_QUIRK(0x8086, 0x2503, "82801H", ALC883_MITAC),
-	SND_PCI_QUIRK(0x8086, 0x0022, "DX58SO", ALC883_3ST_6ch_INTEL),
+	SND_PCI_QUIRK(0x8086, 0x0022, "DX58SO", ALC889_INTEL),
+	SND_PCI_QUIRK(0x8086, 0x0021, "Intel IbexPeak", ALC889A_INTEL),
+	SND_PCI_QUIRK(0x8086, 0x3b56, "Intel IbexPeak", ALC889A_INTEL),
 	SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch),
 	SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch),
-	SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC883_SONY_VAIO_TT),
-	{}
-};
 
 
-static hda_nid_t alc883_slave_dig_outs[] = {
-	ALC1200_DIGOUT_NID, 0,
+	{}
 };
 };
 
 
-static hda_nid_t alc1200_slave_dig_outs[] = {
-	ALC883_DIGOUT_NID, 0,
+/* codec SSID table for Intel Mac */
+static struct snd_pci_quirk alc882_ssid_cfg_tbl[] = {
+	SND_PCI_QUIRK(0x106b, 0x00a0, "MacBookPro 3,1", ALC885_MBP3),
+	SND_PCI_QUIRK(0x106b, 0x00a1, "Macbook", ALC885_MBP3),
+	SND_PCI_QUIRK(0x106b, 0x00a4, "MacbookPro 4,1", ALC885_MBP3),
+	SND_PCI_QUIRK(0x106b, 0x0c00, "Mac Pro", ALC885_MACPRO),
+	SND_PCI_QUIRK(0x106b, 0x1000, "iMac 24", ALC885_IMAC24),
+	SND_PCI_QUIRK(0x106b, 0x2800, "AppleTV", ALC885_IMAC24),
+	SND_PCI_QUIRK(0x106b, 0x2c00, "MacbookPro rev3", ALC885_MBP3),
+	SND_PCI_QUIRK(0x106b, 0x3600, "Macbook 3,1", ALC889A_MB31),
+	SND_PCI_QUIRK(0x106b, 0x3800, "MacbookPro 4,1", ALC885_MBP3),
+	SND_PCI_QUIRK(0x106b, 0x3e00, "iMac 24 Aluminum", ALC885_IMAC24),
+	SND_PCI_QUIRK(0x106b, 0x3f00, "Macbook 5,1", ALC885_MB5),
+	/* FIXME: HP jack sense seems not working for MBP 5,1, so apparently
+	 * no perfect solution yet
+	 */
+	SND_PCI_QUIRK(0x106b, 0x4000, "MacbookPro 5,1", ALC885_MB5),
+	{} /* terminator */
 };
 };
 
 
-static struct alc_config_preset alc883_presets[] = {
+static struct alc_config_preset alc882_presets[] = {
+	[ALC882_3ST_DIG] = {
+		.mixers = { alc882_base_mixer },
+		.init_verbs = { alc882_base_init_verbs,
+				alc882_adc1_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
+		.dac_nids = alc882_dac_nids,
+		.dig_out_nid = ALC882_DIGOUT_NID,
+		.dig_in_nid = ALC882_DIGIN_NID,
+		.num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
+		.channel_mode = alc882_ch_modes,
+		.need_dac_fix = 1,
+		.input_mux = &alc882_capture_source,
+	},
+	[ALC882_6ST_DIG] = {
+		.mixers = { alc882_base_mixer, alc882_chmode_mixer },
+		.init_verbs = { alc882_base_init_verbs,
+				alc882_adc1_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
+		.dac_nids = alc882_dac_nids,
+		.dig_out_nid = ALC882_DIGOUT_NID,
+		.dig_in_nid = ALC882_DIGIN_NID,
+		.num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes),
+		.channel_mode = alc882_sixstack_modes,
+		.input_mux = &alc882_capture_source,
+	},
+	[ALC882_ARIMA] = {
+		.mixers = { alc882_base_mixer, alc882_chmode_mixer },
+		.init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs,
+				alc882_eapd_verbs },
+		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
+		.dac_nids = alc882_dac_nids,
+		.num_channel_mode = ARRAY_SIZE(alc882_sixstack_modes),
+		.channel_mode = alc882_sixstack_modes,
+		.input_mux = &alc882_capture_source,
+	},
+	[ALC882_W2JC] = {
+		.mixers = { alc882_w2jc_mixer, alc882_chmode_mixer },
+		.init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs,
+				alc882_eapd_verbs, alc880_gpio1_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
+		.dac_nids = alc882_dac_nids,
+		.num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
+		.channel_mode = alc880_threestack_modes,
+		.need_dac_fix = 1,
+		.input_mux = &alc882_capture_source,
+		.dig_out_nid = ALC882_DIGOUT_NID,
+	},
+	[ALC885_MBP3] = {
+		.mixers = { alc885_mbp3_mixer, alc882_chmode_mixer },
+		.init_verbs = { alc885_mbp3_init_verbs,
+				alc880_gpio1_init_verbs },
+		.num_dacs = 2,
+		.dac_nids = alc882_dac_nids,
+		.hp_nid = 0x04,
+		.channel_mode = alc885_mbp_4ch_modes,
+		.num_channel_mode = ARRAY_SIZE(alc885_mbp_4ch_modes),
+		.input_mux = &alc882_capture_source,
+		.dig_out_nid = ALC882_DIGOUT_NID,
+		.dig_in_nid = ALC882_DIGIN_NID,
+		.unsol_event = alc_automute_amp_unsol_event,
+		.setup = alc885_mbp3_setup,
+		.init_hook = alc_automute_amp,
+	},
+	[ALC885_MB5] = {
+		.mixers = { alc885_mb5_mixer, alc882_chmode_mixer },
+		.init_verbs = { alc885_mb5_init_verbs,
+				alc880_gpio1_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
+		.dac_nids = alc882_dac_nids,
+		.channel_mode = alc885_mb5_6ch_modes,
+		.num_channel_mode = ARRAY_SIZE(alc885_mb5_6ch_modes),
+		.input_mux = &mb5_capture_source,
+		.dig_out_nid = ALC882_DIGOUT_NID,
+		.dig_in_nid = ALC882_DIGIN_NID,
+	},
+	[ALC885_MACPRO] = {
+		.mixers = { alc882_macpro_mixer },
+		.init_verbs = { alc882_macpro_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
+		.dac_nids = alc882_dac_nids,
+		.dig_out_nid = ALC882_DIGOUT_NID,
+		.dig_in_nid = ALC882_DIGIN_NID,
+		.num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
+		.channel_mode = alc882_ch_modes,
+		.input_mux = &alc882_capture_source,
+		.init_hook = alc885_macpro_init_hook,
+	},
+	[ALC885_IMAC24] = {
+		.mixers = { alc885_imac24_mixer },
+		.init_verbs = { alc885_imac24_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
+		.dac_nids = alc882_dac_nids,
+		.dig_out_nid = ALC882_DIGOUT_NID,
+		.dig_in_nid = ALC882_DIGIN_NID,
+		.num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
+		.channel_mode = alc882_ch_modes,
+		.input_mux = &alc882_capture_source,
+		.unsol_event = alc_automute_amp_unsol_event,
+		.setup = alc885_imac24_setup,
+		.init_hook = alc885_imac24_init_hook,
+	},
+	[ALC882_TARGA] = {
+		.mixers = { alc882_targa_mixer, alc882_chmode_mixer },
+		.init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs,
+				alc880_gpio3_init_verbs, alc882_targa_verbs},
+		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
+		.dac_nids = alc882_dac_nids,
+		.dig_out_nid = ALC882_DIGOUT_NID,
+		.num_adc_nids = ARRAY_SIZE(alc882_adc_nids),
+		.adc_nids = alc882_adc_nids,
+		.capsrc_nids = alc882_capsrc_nids,
+		.num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes),
+		.channel_mode = alc882_3ST_6ch_modes,
+		.need_dac_fix = 1,
+		.input_mux = &alc882_capture_source,
+		.unsol_event = alc882_targa_unsol_event,
+		.setup = alc882_targa_setup,
+		.init_hook = alc882_targa_automute,
+	},
+	[ALC882_ASUS_A7J] = {
+		.mixers = { alc882_asus_a7j_mixer, alc882_chmode_mixer },
+		.init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs,
+				alc882_asus_a7j_verbs},
+		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
+		.dac_nids = alc882_dac_nids,
+		.dig_out_nid = ALC882_DIGOUT_NID,
+		.num_adc_nids = ARRAY_SIZE(alc882_adc_nids),
+		.adc_nids = alc882_adc_nids,
+		.capsrc_nids = alc882_capsrc_nids,
+		.num_channel_mode = ARRAY_SIZE(alc882_3ST_6ch_modes),
+		.channel_mode = alc882_3ST_6ch_modes,
+		.need_dac_fix = 1,
+		.input_mux = &alc882_capture_source,
+	},
+	[ALC882_ASUS_A7M] = {
+		.mixers = { alc882_asus_a7m_mixer, alc882_chmode_mixer },
+		.init_verbs = { alc882_base_init_verbs, alc882_adc1_init_verbs,
+				alc882_eapd_verbs, alc880_gpio1_init_verbs,
+				alc882_asus_a7m_verbs },
+		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
+		.dac_nids = alc882_dac_nids,
+		.dig_out_nid = ALC882_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
+		.channel_mode = alc880_threestack_modes,
+		.need_dac_fix = 1,
+		.input_mux = &alc882_capture_source,
+	},
 	[ALC883_3ST_2ch_DIG] = {
 	[ALC883_3ST_2ch_DIG] = {
 		.mixers = { alc883_3ST_2ch_mixer },
 		.mixers = { alc883_3ST_2ch_mixer },
 		.init_verbs = { alc883_init_verbs },
 		.init_verbs = { alc883_init_verbs },
@@ -9235,6 +9084,46 @@ static struct alc_config_preset alc883_presets[] = {
 		.need_dac_fix = 1,
 		.need_dac_fix = 1,
 		.input_mux = &alc883_3stack_6ch_intel,
 		.input_mux = &alc883_3stack_6ch_intel,
 	},
 	},
+	[ALC889A_INTEL] = {
+		.mixers = { alc885_8ch_intel_mixer, alc883_chmode_mixer },
+		.init_verbs = { alc885_init_verbs, alc885_init_input_verbs,
+				alc_hp15_unsol_verbs },
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.num_adc_nids = ARRAY_SIZE(alc889_adc_nids),
+		.adc_nids = alc889_adc_nids,
+		.dig_out_nid = ALC883_DIGOUT_NID,
+		.dig_in_nid = ALC883_DIGIN_NID,
+		.slave_dig_outs = alc883_slave_dig_outs,
+		.num_channel_mode = ARRAY_SIZE(alc889_8ch_intel_modes),
+		.channel_mode = alc889_8ch_intel_modes,
+		.capsrc_nids = alc889_capsrc_nids,
+		.input_mux = &alc889_capture_source,
+		.setup = alc889_automute_setup,
+		.init_hook = alc_automute_amp,
+		.unsol_event = alc_automute_amp_unsol_event,
+		.need_dac_fix = 1,
+	},
+	[ALC889_INTEL] = {
+		.mixers = { alc885_8ch_intel_mixer, alc883_chmode_mixer },
+		.init_verbs = { alc885_init_verbs, alc889_init_input_verbs,
+				alc889_eapd_verbs, alc_hp15_unsol_verbs},
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.num_adc_nids = ARRAY_SIZE(alc889_adc_nids),
+		.adc_nids = alc889_adc_nids,
+		.dig_out_nid = ALC883_DIGOUT_NID,
+		.dig_in_nid = ALC883_DIGIN_NID,
+		.slave_dig_outs = alc883_slave_dig_outs,
+		.num_channel_mode = ARRAY_SIZE(alc889_8ch_intel_modes),
+		.channel_mode = alc889_8ch_intel_modes,
+		.capsrc_nids = alc889_capsrc_nids,
+		.input_mux = &alc889_capture_source,
+		.setup = alc889_automute_setup,
+		.init_hook = alc889_intel_init_hook,
+		.unsol_event = alc_automute_amp_unsol_event,
+		.need_dac_fix = 1,
+	},
 	[ALC883_6ST_DIG] = {
 	[ALC883_6ST_DIG] = {
 		.mixers = { alc883_base_mixer, alc883_chmode_mixer },
 		.mixers = { alc883_base_mixer, alc883_chmode_mixer },
 		.init_verbs = { alc883_init_verbs },
 		.init_verbs = { alc883_init_verbs },
@@ -9258,7 +9147,8 @@ static struct alc_config_preset alc883_presets[] = {
 		.need_dac_fix = 1,
 		.need_dac_fix = 1,
 		.input_mux = &alc883_capture_source,
 		.input_mux = &alc883_capture_source,
 		.unsol_event = alc883_targa_unsol_event,
 		.unsol_event = alc883_targa_unsol_event,
-		.init_hook = alc883_targa_init_hook,
+		.setup = alc882_targa_setup,
+		.init_hook = alc882_targa_automute,
 	},
 	},
 	[ALC883_TARGA_2ch_DIG] = {
 	[ALC883_TARGA_2ch_DIG] = {
 		.mixers = { alc883_targa_2ch_mixer},
 		.mixers = { alc883_targa_2ch_mixer},
@@ -9273,7 +9163,8 @@ static struct alc_config_preset alc883_presets[] = {
 		.channel_mode = alc883_3ST_2ch_modes,
 		.channel_mode = alc883_3ST_2ch_modes,
 		.input_mux = &alc883_capture_source,
 		.input_mux = &alc883_capture_source,
 		.unsol_event = alc883_targa_unsol_event,
 		.unsol_event = alc883_targa_unsol_event,
-		.init_hook = alc883_targa_init_hook,
+		.setup = alc882_targa_setup,
+		.init_hook = alc882_targa_automute,
 	},
 	},
 	[ALC883_TARGA_8ch_DIG] = {
 	[ALC883_TARGA_8ch_DIG] = {
 		.mixers = { alc883_base_mixer, alc883_chmode_mixer },
 		.mixers = { alc883_base_mixer, alc883_chmode_mixer },
@@ -9291,7 +9182,8 @@ static struct alc_config_preset alc883_presets[] = {
 		.need_dac_fix = 1,
 		.need_dac_fix = 1,
 		.input_mux = &alc883_capture_source,
 		.input_mux = &alc883_capture_source,
 		.unsol_event = alc883_targa_unsol_event,
 		.unsol_event = alc883_targa_unsol_event,
-		.init_hook = alc883_targa_init_hook,
+		.setup = alc882_targa_setup,
+		.init_hook = alc882_targa_automute,
 	},
 	},
 	[ALC883_ACER] = {
 	[ALC883_ACER] = {
 		.mixers = { alc883_base_mixer },
 		.mixers = { alc883_base_mixer },
@@ -9317,7 +9209,8 @@ static struct alc_config_preset alc883_presets[] = {
 		.channel_mode = alc883_3ST_2ch_modes,
 		.channel_mode = alc883_3ST_2ch_modes,
 		.input_mux = &alc883_capture_source,
 		.input_mux = &alc883_capture_source,
 		.unsol_event = alc_automute_amp_unsol_event,
 		.unsol_event = alc_automute_amp_unsol_event,
-		.init_hook = alc883_acer_aspire_init_hook,
+		.setup = alc883_acer_aspire_setup,
+		.init_hook = alc_automute_amp,
 	},
 	},
 	[ALC888_ACER_ASPIRE_4930G] = {
 	[ALC888_ACER_ASPIRE_4930G] = {
 		.mixers = { alc888_base_mixer,
 		.mixers = { alc888_base_mixer,
@@ -9337,7 +9230,8 @@ static struct alc_config_preset alc883_presets[] = {
 			ARRAY_SIZE(alc888_2_capture_sources),
 			ARRAY_SIZE(alc888_2_capture_sources),
 		.input_mux = alc888_2_capture_sources,
 		.input_mux = alc888_2_capture_sources,
 		.unsol_event = alc_automute_amp_unsol_event,
 		.unsol_event = alc_automute_amp_unsol_event,
-		.init_hook = alc888_acer_aspire_4930g_init_hook,
+		.setup = alc888_acer_aspire_4930g_setup,
+		.init_hook = alc_automute_amp,
 	},
 	},
 	[ALC888_ACER_ASPIRE_6530G] = {
 	[ALC888_ACER_ASPIRE_6530G] = {
 		.mixers = { alc888_acer_aspire_6530_mixer },
 		.mixers = { alc888_acer_aspire_6530_mixer },
@@ -9355,7 +9249,8 @@ static struct alc_config_preset alc883_presets[] = {
 			ARRAY_SIZE(alc888_2_capture_sources),
 			ARRAY_SIZE(alc888_2_capture_sources),
 		.input_mux = alc888_acer_aspire_6530_sources,
 		.input_mux = alc888_acer_aspire_6530_sources,
 		.unsol_event = alc_automute_amp_unsol_event,
 		.unsol_event = alc_automute_amp_unsol_event,
-		.init_hook = alc888_acer_aspire_6530g_init_hook,
+		.setup = alc888_acer_aspire_6530g_setup,
+		.init_hook = alc_automute_amp,
 	},
 	},
 	[ALC888_ACER_ASPIRE_8930G] = {
 	[ALC888_ACER_ASPIRE_8930G] = {
 		.mixers = { alc888_base_mixer,
 		.mixers = { alc888_base_mixer,
@@ -9376,7 +9271,28 @@ static struct alc_config_preset alc883_presets[] = {
 			ARRAY_SIZE(alc889_capture_sources),
 			ARRAY_SIZE(alc889_capture_sources),
 		.input_mux = alc889_capture_sources,
 		.input_mux = alc889_capture_sources,
 		.unsol_event = alc_automute_amp_unsol_event,
 		.unsol_event = alc_automute_amp_unsol_event,
-		.init_hook = alc889_acer_aspire_8930g_init_hook,
+		.setup = alc889_acer_aspire_8930g_setup,
+		.init_hook = alc_automute_amp,
+	},
+	[ALC888_ACER_ASPIRE_7730G] = {
+		.mixers = { alc883_3ST_6ch_mixer,
+				alc883_chmode_mixer },
+		.init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs,
+				alc888_acer_aspire_7730G_verbs },
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev),
+		.adc_nids = alc883_adc_nids_rev,
+		.capsrc_nids = alc883_capsrc_nids_rev,
+		.dig_out_nid = ALC883_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
+		.channel_mode = alc883_3ST_6ch_modes,
+		.need_dac_fix = 1,
+		.const_channel_count = 6,
+		.input_mux = &alc883_capture_source,
+		.unsol_event = alc_automute_amp_unsol_event,
+		.setup = alc888_acer_aspire_6530g_setup,
+		.init_hook = alc_automute_amp,
 	},
 	},
 	[ALC883_MEDION] = {
 	[ALC883_MEDION] = {
 		.mixers = { alc883_fivestack_mixer,
 		.mixers = { alc883_fivestack_mixer,
@@ -9401,7 +9317,8 @@ static struct alc_config_preset alc883_presets[] = {
 		.channel_mode = alc883_3ST_2ch_modes,
 		.channel_mode = alc883_3ST_2ch_modes,
 		.input_mux = &alc883_capture_source,
 		.input_mux = &alc883_capture_source,
 		.unsol_event = alc_automute_amp_unsol_event,
 		.unsol_event = alc_automute_amp_unsol_event,
-		.init_hook = alc883_medion_md2_init_hook,
+		.setup = alc883_medion_md2_setup,
+		.init_hook = alc_automute_amp,
 	},
 	},
 	[ALC883_LAPTOP_EAPD] = {
 	[ALC883_LAPTOP_EAPD] = {
 		.mixers = { alc883_base_mixer },
 		.mixers = { alc883_base_mixer },
@@ -9412,6 +9329,21 @@ static struct alc_config_preset alc883_presets[] = {
 		.channel_mode = alc883_3ST_2ch_modes,
 		.channel_mode = alc883_3ST_2ch_modes,
 		.input_mux = &alc883_capture_source,
 		.input_mux = &alc883_capture_source,
 	},
 	},
+	[ALC883_CLEVO_M540R] = {
+		.mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
+		.init_verbs = { alc883_init_verbs, alc883_clevo_m540r_verbs },
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.dig_out_nid = ALC883_DIGOUT_NID,
+		.dig_in_nid = ALC883_DIGIN_NID,
+		.num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_clevo_modes),
+		.channel_mode = alc883_3ST_6ch_clevo_modes,
+		.need_dac_fix = 1,
+		.input_mux = &alc883_capture_source,
+		/* This machine has the hardware HP auto-muting, thus
+		 * we need no software mute via unsol event
+		 */
+	},
 	[ALC883_CLEVO_M720] = {
 	[ALC883_CLEVO_M720] = {
 		.mixers = { alc883_clevo_m720_mixer },
 		.mixers = { alc883_clevo_m720_mixer },
 		.init_verbs = { alc883_init_verbs, alc883_clevo_m720_verbs },
 		.init_verbs = { alc883_init_verbs, alc883_clevo_m720_verbs },
@@ -9422,6 +9354,7 @@ static struct alc_config_preset alc883_presets[] = {
 		.channel_mode = alc883_3ST_2ch_modes,
 		.channel_mode = alc883_3ST_2ch_modes,
 		.input_mux = &alc883_capture_source,
 		.input_mux = &alc883_capture_source,
 		.unsol_event = alc883_clevo_m720_unsol_event,
 		.unsol_event = alc883_clevo_m720_unsol_event,
+		.setup = alc883_clevo_m720_setup,
 		.init_hook = alc883_clevo_m720_init_hook,
 		.init_hook = alc883_clevo_m720_init_hook,
 	},
 	},
 	[ALC883_LENOVO_101E_2ch] = {
 	[ALC883_LENOVO_101E_2ch] = {
@@ -9447,7 +9380,8 @@ static struct alc_config_preset alc883_presets[] = {
 		.need_dac_fix = 1,
 		.need_dac_fix = 1,
 		.input_mux = &alc883_lenovo_nb0763_capture_source,
 		.input_mux = &alc883_lenovo_nb0763_capture_source,
 		.unsol_event = alc_automute_amp_unsol_event,
 		.unsol_event = alc_automute_amp_unsol_event,
-		.init_hook = alc883_medion_md2_init_hook,
+		.setup = alc883_medion_md2_setup,
+		.init_hook = alc_automute_amp,
 	},
 	},
 	[ALC888_LENOVO_MS7195_DIG] = {
 	[ALC888_LENOVO_MS7195_DIG] = {
 		.mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
 		.mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
@@ -9472,7 +9406,8 @@ static struct alc_config_preset alc883_presets[] = {
 		.channel_mode = alc883_3ST_2ch_modes,
 		.channel_mode = alc883_3ST_2ch_modes,
 		.input_mux = &alc883_capture_source,
 		.input_mux = &alc883_capture_source,
 		.unsol_event = alc_automute_amp_unsol_event,
 		.unsol_event = alc_automute_amp_unsol_event,
-		.init_hook = alc883_haier_w66_init_hook,
+		.setup = alc883_haier_w66_setup,
+		.init_hook = alc_automute_amp,
 	},
 	},
 	[ALC888_3ST_HP] = {
 	[ALC888_3ST_HP] = {
 		.mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
 		.mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
@@ -9484,7 +9419,8 @@ static struct alc_config_preset alc883_presets[] = {
 		.need_dac_fix = 1,
 		.need_dac_fix = 1,
 		.input_mux = &alc883_capture_source,
 		.input_mux = &alc883_capture_source,
 		.unsol_event = alc_automute_amp_unsol_event,
 		.unsol_event = alc_automute_amp_unsol_event,
-		.init_hook = alc888_3st_hp_init_hook,
+		.setup = alc888_3st_hp_setup,
+		.init_hook = alc_automute_amp,
 	},
 	},
 	[ALC888_6ST_DELL] = {
 	[ALC888_6ST_DELL] = {
 		.mixers = { alc883_base_mixer, alc883_chmode_mixer },
 		.mixers = { alc883_base_mixer, alc883_chmode_mixer },
@@ -9497,7 +9433,8 @@ static struct alc_config_preset alc883_presets[] = {
 		.channel_mode = alc883_sixstack_modes,
 		.channel_mode = alc883_sixstack_modes,
 		.input_mux = &alc883_capture_source,
 		.input_mux = &alc883_capture_source,
 		.unsol_event = alc_automute_amp_unsol_event,
 		.unsol_event = alc_automute_amp_unsol_event,
-		.init_hook = alc888_6st_dell_init_hook,
+		.setup = alc888_6st_dell_setup,
+		.init_hook = alc_automute_amp,
 	},
 	},
 	[ALC883_MITAC] = {
 	[ALC883_MITAC] = {
 		.mixers = { alc883_mitac_mixer },
 		.mixers = { alc883_mitac_mixer },
@@ -9508,7 +9445,8 @@ static struct alc_config_preset alc883_presets[] = {
 		.channel_mode = alc883_3ST_2ch_modes,
 		.channel_mode = alc883_3ST_2ch_modes,
 		.input_mux = &alc883_capture_source,
 		.input_mux = &alc883_capture_source,
 		.unsol_event = alc_automute_amp_unsol_event,
 		.unsol_event = alc_automute_amp_unsol_event,
-		.init_hook = alc883_mitac_init_hook,
+		.setup = alc883_mitac_setup,
+		.init_hook = alc_automute_amp,
 	},
 	},
 	[ALC883_FUJITSU_PI2515] = {
 	[ALC883_FUJITSU_PI2515] = {
 		.mixers = { alc883_2ch_fujitsu_pi2515_mixer },
 		.mixers = { alc883_2ch_fujitsu_pi2515_mixer },
@@ -9521,7 +9459,8 @@ static struct alc_config_preset alc883_presets[] = {
 		.channel_mode = alc883_3ST_2ch_modes,
 		.channel_mode = alc883_3ST_2ch_modes,
 		.input_mux = &alc883_fujitsu_pi2515_capture_source,
 		.input_mux = &alc883_fujitsu_pi2515_capture_source,
 		.unsol_event = alc_automute_amp_unsol_event,
 		.unsol_event = alc_automute_amp_unsol_event,
-		.init_hook = alc883_2ch_fujitsu_pi2515_init_hook,
+		.setup = alc883_2ch_fujitsu_pi2515_setup,
+		.init_hook = alc_automute_amp,
 	},
 	},
 	[ALC888_FUJITSU_XA3530] = {
 	[ALC888_FUJITSU_XA3530] = {
 		.mixers = { alc888_base_mixer, alc883_chmode_mixer },
 		.mixers = { alc888_base_mixer, alc883_chmode_mixer },
@@ -9539,7 +9478,8 @@ static struct alc_config_preset alc883_presets[] = {
 			ARRAY_SIZE(alc888_2_capture_sources),
 			ARRAY_SIZE(alc888_2_capture_sources),
 		.input_mux = alc888_2_capture_sources,
 		.input_mux = alc888_2_capture_sources,
 		.unsol_event = alc_automute_amp_unsol_event,
 		.unsol_event = alc_automute_amp_unsol_event,
-		.init_hook = alc888_fujitsu_xa3530_init_hook,
+		.setup = alc888_fujitsu_xa3530_setup,
+		.init_hook = alc_automute_amp,
 	},
 	},
 	[ALC888_LENOVO_SKY] = {
 	[ALC888_LENOVO_SKY] = {
 		.mixers = { alc888_lenovo_sky_mixer, alc883_chmode_mixer },
 		.mixers = { alc888_lenovo_sky_mixer, alc883_chmode_mixer },
@@ -9552,7 +9492,8 @@ static struct alc_config_preset alc883_presets[] = {
 		.need_dac_fix = 1,
 		.need_dac_fix = 1,
 		.input_mux = &alc883_lenovo_sky_capture_source,
 		.input_mux = &alc883_lenovo_sky_capture_source,
 		.unsol_event = alc_automute_amp_unsol_event,
 		.unsol_event = alc_automute_amp_unsol_event,
-		.init_hook = alc888_lenovo_sky_init_hook,
+		.setup = alc888_lenovo_sky_setup,
+		.init_hook = alc_automute_amp,
 	},
 	},
 	[ALC888_ASUS_M90V] = {
 	[ALC888_ASUS_M90V] = {
 		.mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
 		.mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
@@ -9565,8 +9506,9 @@ static struct alc_config_preset alc883_presets[] = {
 		.channel_mode = alc883_3ST_6ch_modes,
 		.channel_mode = alc883_3ST_6ch_modes,
 		.need_dac_fix = 1,
 		.need_dac_fix = 1,
 		.input_mux = &alc883_fujitsu_pi2515_capture_source,
 		.input_mux = &alc883_fujitsu_pi2515_capture_source,
-		.unsol_event = alc883_mode2_unsol_event,
-		.init_hook = alc883_mode2_inithook,
+		.unsol_event = alc_sku_unsol_event,
+		.setup = alc883_mode2_setup,
+		.init_hook = alc_inithook,
 	},
 	},
 	[ALC888_ASUS_EEE1601] = {
 	[ALC888_ASUS_EEE1601] = {
 		.mixers = { alc883_asus_eee1601_mixer },
 		.mixers = { alc883_asus_eee1601_mixer },
@@ -9619,15 +9561,45 @@ static struct alc_config_preset alc883_presets[] = {
 		.channel_mode = alc883_3ST_2ch_modes,
 		.channel_mode = alc883_3ST_2ch_modes,
 		.input_mux = &alc883_capture_source,
 		.input_mux = &alc883_capture_source,
 		.unsol_event = alc_automute_amp_unsol_event,
 		.unsol_event = alc_automute_amp_unsol_event,
-		.init_hook = alc883_vaiott_init_hook,
+		.setup = alc883_vaiott_setup,
+		.init_hook = alc_automute_amp,
 	},
 	},
 };
 };
 
 
 
 
+/*
+ * Pin config fixes
+ */
+enum {
+	PINFIX_ABIT_AW9D_MAX
+};
+
+static struct alc_pincfg alc882_abit_aw9d_pinfix[] = {
+	{ 0x15, 0x01080104 }, /* side */
+	{ 0x16, 0x01011012 }, /* rear */
+	{ 0x17, 0x01016011 }, /* clfe */
+	{ }
+};
+
+static const struct alc_pincfg *alc882_pin_fixes[] = {
+	[PINFIX_ABIT_AW9D_MAX] = alc882_abit_aw9d_pinfix,
+};
+
+static struct snd_pci_quirk alc882_pinfix_tbl[] = {
+	SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX),
+	{}
+};
+
 /*
 /*
  * BIOS auto configuration
  * BIOS auto configuration
  */
  */
-static void alc883_auto_set_output_and_unmute(struct hda_codec *codec,
+static int alc882_auto_create_input_ctls(struct hda_codec *codec,
+						const struct auto_pin_cfg *cfg)
+{
+	return alc_auto_create_input_ctls(codec, cfg, 0x0b, 0x23, 0x22);
+}
+
+static void alc882_auto_set_output_and_unmute(struct hda_codec *codec,
 					      hda_nid_t nid, int pin_type,
 					      hda_nid_t nid, int pin_type,
 					      int dac_idx)
 					      int dac_idx)
 {
 {
@@ -9644,7 +9616,7 @@ static void alc883_auto_set_output_and_unmute(struct hda_codec *codec,
 
 
 }
 }
 
 
-static void alc883_auto_init_multi_out(struct hda_codec *codec)
+static void alc882_auto_init_multi_out(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 	int i;
 	int i;
@@ -9653,12 +9625,12 @@ static void alc883_auto_init_multi_out(struct hda_codec *codec)
 		hda_nid_t nid = spec->autocfg.line_out_pins[i];
 		hda_nid_t nid = spec->autocfg.line_out_pins[i];
 		int pin_type = get_pin_type(spec->autocfg.line_out_type);
 		int pin_type = get_pin_type(spec->autocfg.line_out_type);
 		if (nid)
 		if (nid)
-			alc883_auto_set_output_and_unmute(codec, nid, pin_type,
+			alc882_auto_set_output_and_unmute(codec, nid, pin_type,
 							  i);
 							  i);
 	}
 	}
 }
 }
 
 
-static void alc883_auto_init_hp_out(struct hda_codec *codec)
+static void alc882_auto_init_hp_out(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 	hda_nid_t pin;
 	hda_nid_t pin;
@@ -9666,91 +9638,191 @@ static void alc883_auto_init_hp_out(struct hda_codec *codec)
 	pin = spec->autocfg.hp_pins[0];
 	pin = spec->autocfg.hp_pins[0];
 	if (pin) /* connect to front */
 	if (pin) /* connect to front */
 		/* use dac 0 */
 		/* use dac 0 */
-		alc883_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
+		alc882_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
 	pin = spec->autocfg.speaker_pins[0];
 	pin = spec->autocfg.speaker_pins[0];
 	if (pin)
 	if (pin)
-		alc883_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
+		alc882_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
 }
 }
 
 
-#define alc883_is_input_pin(nid)	alc880_is_input_pin(nid)
-#define ALC883_PIN_CD_NID		ALC880_PIN_CD_NID
-
-static void alc883_auto_init_analog_input(struct hda_codec *codec)
+static void alc882_auto_init_analog_input(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 	int i;
 	int i;
 
 
 	for (i = 0; i < AUTO_PIN_LAST; i++) {
 	for (i = 0; i < AUTO_PIN_LAST; i++) {
 		hda_nid_t nid = spec->autocfg.input_pins[i];
 		hda_nid_t nid = spec->autocfg.input_pins[i];
-		if (alc883_is_input_pin(nid)) {
-			alc_set_input_pin(codec, nid, i);
-			if (nid != ALC883_PIN_CD_NID &&
-			    (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
+		if (!nid)
+			continue;
+		alc_set_input_pin(codec, nid, i);
+		if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
+			snd_hda_codec_write(codec, nid, 0,
+					    AC_VERB_SET_AMP_GAIN_MUTE,
+					    AMP_OUT_MUTE);
+	}
+}
+
+static void alc882_auto_init_input_src(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	int c;
+
+	for (c = 0; c < spec->num_adc_nids; c++) {
+		hda_nid_t conn_list[HDA_MAX_NUM_INPUTS];
+		hda_nid_t nid = spec->capsrc_nids[c];
+		unsigned int mux_idx;
+		const struct hda_input_mux *imux;
+		int conns, mute, idx, item;
+
+		conns = snd_hda_get_connections(codec, nid, conn_list,
+						ARRAY_SIZE(conn_list));
+		if (conns < 0)
+			continue;
+		mux_idx = c >= spec->num_mux_defs ? 0 : c;
+		imux = &spec->input_mux[mux_idx];
+		for (idx = 0; idx < conns; idx++) {
+			/* if the current connection is the selected one,
+			 * unmute it as default - otherwise mute it
+			 */
+			mute = AMP_IN_MUTE(idx);
+			for (item = 0; item < imux->num_items; item++) {
+				if (imux->items[item].index == idx) {
+					if (spec->cur_mux[c] == item)
+						mute = AMP_IN_UNMUTE(idx);
+					break;
+				}
+			}
+			/* check if we have a selector or mixer
+			 * we could check for the widget type instead, but
+			 * just check for Amp-In presence (in case of mixer
+			 * without amp-in there is something wrong, this
+			 * function shouldn't be used or capsrc nid is wrong)
+			 */
+			if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)
 				snd_hda_codec_write(codec, nid, 0,
 				snd_hda_codec_write(codec, nid, 0,
 						    AC_VERB_SET_AMP_GAIN_MUTE,
 						    AC_VERB_SET_AMP_GAIN_MUTE,
-						    AMP_OUT_MUTE);
+						    mute);
+			else if (mute != AMP_IN_MUTE(idx))
+				snd_hda_codec_write(codec, nid, 0,
+						    AC_VERB_SET_CONNECT_SEL,
+						    idx);
 		}
 		}
 	}
 	}
 }
 }
 
 
-#define alc883_auto_init_input_src	alc882_auto_init_input_src
+/* add mic boosts if needed */
+static int alc_auto_add_mic_boost(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	int err;
+	hda_nid_t nid;
+
+	nid = spec->autocfg.input_pins[AUTO_PIN_MIC];
+	if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) {
+		err = add_control(spec, ALC_CTL_WIDGET_VOL,
+				  "Mic Boost",
+				  HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
+		if (err < 0)
+			return err;
+	}
+	nid = spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC];
+	if (nid && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) {
+		err = add_control(spec, ALC_CTL_WIDGET_VOL,
+				  "Front Mic Boost",
+				  HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
 
 
 /* almost identical with ALC880 parser... */
 /* almost identical with ALC880 parser... */
-static int alc883_parse_auto_config(struct hda_codec *codec)
+static int alc882_parse_auto_config(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
-	int err = alc880_parse_auto_config(codec);
-	struct auto_pin_cfg *cfg = &spec->autocfg;
-	int i;
+	static hda_nid_t alc882_ignore[] = { 0x1d, 0 };
+	int i, err;
+
+	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
+					   alc882_ignore);
+	if (err < 0)
+		return err;
+	if (!spec->autocfg.line_outs)
+		return 0; /* can't find valid BIOS pin config */
 
 
+	err = alc880_auto_fill_dac_nids(spec, &spec->autocfg);
+	if (err < 0)
+		return err;
+	err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg);
+	if (err < 0)
+		return err;
+	err = alc880_auto_create_extra_out(spec,
+					   spec->autocfg.speaker_pins[0],
+					   "Speaker");
+	if (err < 0)
+		return err;
+	err = alc880_auto_create_extra_out(spec, spec->autocfg.hp_pins[0],
+					   "Headphone");
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
-	else if (!err)
-		return 0; /* no config found */
+	err = alc882_auto_create_input_ctls(codec, &spec->autocfg);
+	if (err < 0)
+		return err;
+
+	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+
+	/* check multiple SPDIF-out (for recent codecs) */
+	for (i = 0; i < spec->autocfg.dig_outs; i++) {
+		hda_nid_t dig_nid;
+		err = snd_hda_get_connections(codec,
+					      spec->autocfg.dig_out_pins[i],
+					      &dig_nid, 1);
+		if (err < 0)
+			continue;
+		if (!i)
+			spec->multiout.dig_out_nid = dig_nid;
+		else {
+			spec->multiout.slave_dig_outs = spec->slave_dig_outs;
+			spec->slave_dig_outs[i - 1] = dig_nid;
+			if (i == ARRAY_SIZE(spec->slave_dig_outs) - 1)
+				break;
+		}
+	}
+	if (spec->autocfg.dig_in_pin)
+		spec->dig_in_nid = ALC880_DIGIN_NID;
+
+	if (spec->kctls.list)
+		add_mixer(spec, spec->kctls.list);
+
+	add_verb(spec, alc883_auto_init_verbs);
+	/* if ADC 0x07 is available, initialize it, too */
+	if (get_wcaps_type(get_wcaps(codec, 0x07)) == AC_WID_AUD_IN)
+		add_verb(spec, alc882_adc1_init_verbs);
+
+	spec->num_mux_defs = 1;
+	spec->input_mux = &spec->private_imux[0];
+
+	alc_ssid_check(codec, 0x15, 0x1b, 0x14);
 
 
 	err = alc_auto_add_mic_boost(codec);
 	err = alc_auto_add_mic_boost(codec);
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
 
 
-	/* hack - override the init verbs */
-	spec->init_verbs[0] = alc883_auto_init_verbs;
-
-	/* setup input_mux for ALC889 */
-	if (codec->vendor_id == 0x10ec0889) {
-		/* digital-mic input pin is excluded in alc880_auto_create..()
-		 * because it's under 0x18
-		 */
-		if (cfg->input_pins[AUTO_PIN_MIC] == 0x12 ||
-		    cfg->input_pins[AUTO_PIN_FRONT_MIC] == 0x12) {
-			struct hda_input_mux *imux = &spec->private_imux[0];
-			for (i = 1; i < 3; i++)
-				memcpy(&spec->private_imux[i],
-				       &spec->private_imux[0],
-				       sizeof(spec->private_imux[0]));
-			imux->items[imux->num_items].label = "Int DMic";
-			imux->items[imux->num_items].index = 0x0b;
-			imux->num_items++;
-			spec->num_mux_defs = 3;
-			spec->input_mux = spec->private_imux;
-		}
-	}
-
 	return 1; /* config found */
 	return 1; /* config found */
 }
 }
 
 
 /* additional initialization for auto-configuration model */
 /* additional initialization for auto-configuration model */
-static void alc883_auto_init(struct hda_codec *codec)
+static void alc882_auto_init(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
-	alc883_auto_init_multi_out(codec);
-	alc883_auto_init_hp_out(codec);
-	alc883_auto_init_analog_input(codec);
-	alc883_auto_init_input_src(codec);
+	alc882_auto_init_multi_out(codec);
+	alc882_auto_init_hp_out(codec);
+	alc882_auto_init_analog_input(codec);
+	alc882_auto_init_input_src(codec);
 	if (spec->unsol_event)
 	if (spec->unsol_event)
 		alc_inithook(codec);
 		alc_inithook(codec);
 }
 }
 
 
-static int patch_alc883(struct hda_codec *codec)
+static int patch_alc882(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec;
 	struct alc_spec *spec;
 	int err, board_config;
 	int err, board_config;
@@ -9761,28 +9833,35 @@ static int patch_alc883(struct hda_codec *codec)
 
 
 	codec->spec = spec;
 	codec->spec = spec;
 
 
-	alc_fix_pll_init(codec, 0x20, 0x0a, 10);
+	switch (codec->vendor_id) {
+	case 0x10ec0882:
+	case 0x10ec0885:
+		break;
+	default:
+		/* ALC883 and variants */
+		alc_fix_pll_init(codec, 0x20, 0x0a, 10);
+		break;
+	}
 
 
-	board_config = snd_hda_check_board_config(codec, ALC883_MODEL_LAST,
-						  alc883_models,
-						  alc883_cfg_tbl);
-	if (board_config < 0 || board_config >= ALC883_MODEL_LAST) {
-		/* Pick up systems that don't supply PCI SSID */
-		switch (codec->subsystem_id) {
-		case 0x106b3600: /* Macbook 3.1 */
-			board_config = ALC889A_MB31;
-			break;
-		default:
-			printk(KERN_INFO
-				"hda_codec: Unknown model for %s, trying "
-				"auto-probe from BIOS...\n", codec->chip_name);
-			board_config = ALC883_AUTO;
-		}
+	board_config = snd_hda_check_board_config(codec, ALC882_MODEL_LAST,
+						  alc882_models,
+						  alc882_cfg_tbl);
+
+	if (board_config < 0 || board_config >= ALC882_MODEL_LAST)
+		board_config = snd_hda_check_board_codec_sid_config(codec,
+			ALC882_MODEL_LAST, alc882_models, alc882_ssid_cfg_tbl);
+
+	if (board_config < 0 || board_config >= ALC882_MODEL_LAST) {
+		printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+		       codec->chip_name);
+		board_config = ALC882_AUTO;
 	}
 	}
 
 
-	if (board_config == ALC883_AUTO) {
+	alc_fix_pincfg(codec, alc882_pinfix_tbl, alc882_pin_fixes);
+
+	if (board_config == ALC882_AUTO) {
 		/* automatic parse from the BIOS config */
 		/* automatic parse from the BIOS config */
-		err = alc883_parse_auto_config(codec);
+		err = alc882_parse_auto_config(codec);
 		if (err < 0) {
 		if (err < 0) {
 			alc_free(codec);
 			alc_free(codec);
 			return err;
 			return err;
@@ -9790,7 +9869,7 @@ static int patch_alc883(struct hda_codec *codec)
 			printk(KERN_INFO
 			printk(KERN_INFO
 			       "hda_codec: Cannot set up configuration "
 			       "hda_codec: Cannot set up configuration "
 			       "from BIOS.  Using base mode...\n");
 			       "from BIOS.  Using base mode...\n");
-			board_config = ALC883_3ST_2ch_DIG;
+			board_config = ALC882_3ST_DIG;
 		}
 		}
 	}
 	}
 
 
@@ -9800,63 +9879,61 @@ static int patch_alc883(struct hda_codec *codec)
 		return err;
 		return err;
 	}
 	}
 
 
-	if (board_config != ALC883_AUTO)
-		setup_preset(spec, &alc883_presets[board_config]);
+	if (board_config != ALC882_AUTO)
+		setup_preset(codec, &alc882_presets[board_config]);
 
 
-	switch (codec->vendor_id) {
-	case 0x10ec0888:
-		if (!spec->num_adc_nids) {
-			spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids);
-			spec->adc_nids = alc883_adc_nids;
-		}
-		if (!spec->capsrc_nids)
-			spec->capsrc_nids = alc883_capsrc_nids;
+	spec->stream_analog_playback = &alc882_pcm_analog_playback;
+	spec->stream_analog_capture = &alc882_pcm_analog_capture;
+	/* FIXME: setup DAC5 */
+	/*spec->stream_analog_alt_playback = &alc880_pcm_analog_alt_playback;*/
+	spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture;
+
+	spec->stream_digital_playback = &alc882_pcm_digital_playback;
+	spec->stream_digital_capture = &alc882_pcm_digital_capture;
+
+	if (codec->vendor_id == 0x10ec0888)
 		spec->init_amp = ALC_INIT_DEFAULT; /* always initialize */
 		spec->init_amp = ALC_INIT_DEFAULT; /* always initialize */
-		break;
-	case 0x10ec0889:
-		if (!spec->num_adc_nids) {
-			spec->num_adc_nids = ARRAY_SIZE(alc889_adc_nids);
-			spec->adc_nids = alc889_adc_nids;
-		}
-		if (!spec->capsrc_nids)
-			spec->capsrc_nids = alc889_capsrc_nids;
-		break;
-	default:
-		if (!spec->num_adc_nids) {
-			spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids);
-			spec->adc_nids = alc883_adc_nids;
+
+	if (!spec->adc_nids && spec->input_mux) {
+		int i;
+		spec->num_adc_nids = 0;
+		for (i = 0; i < ARRAY_SIZE(alc882_adc_nids); i++) {
+			hda_nid_t cap;
+			hda_nid_t nid = alc882_adc_nids[i];
+			unsigned int wcap = get_wcaps(codec, nid);
+			/* get type */
+			wcap = get_wcaps_type(wcap);
+			if (wcap != AC_WID_AUD_IN)
+				continue;
+			spec->private_adc_nids[spec->num_adc_nids] = nid;
+			err = snd_hda_get_connections(codec, nid, &cap, 1);
+			if (err < 0)
+				continue;
+			spec->private_capsrc_nids[spec->num_adc_nids] = cap;
+			spec->num_adc_nids++;
 		}
 		}
-		if (!spec->capsrc_nids)
-			spec->capsrc_nids = alc883_capsrc_nids;
-		break;
+		spec->adc_nids = spec->private_adc_nids;
+		spec->capsrc_nids = spec->private_capsrc_nids;
 	}
 	}
 
 
-	spec->stream_analog_playback = &alc883_pcm_analog_playback;
-	spec->stream_analog_capture = &alc883_pcm_analog_capture;
-	spec->stream_analog_alt_capture = &alc883_pcm_analog_alt_capture;
-
-	spec->stream_digital_playback = &alc883_pcm_digital_playback;
-	spec->stream_digital_capture = &alc883_pcm_digital_capture;
-
-	if (!spec->cap_mixer)
-		set_capture_mixer(spec);
+	set_capture_mixer(codec);
 	set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
 	set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
 
 
 	spec->vmaster_nid = 0x0c;
 	spec->vmaster_nid = 0x0c;
 
 
 	codec->patch_ops = alc_patch_ops;
 	codec->patch_ops = alc_patch_ops;
-	if (board_config == ALC883_AUTO)
-		spec->init_hook = alc883_auto_init;
-
+	if (board_config == ALC882_AUTO)
+		spec->init_hook = alc882_auto_init;
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 	if (!spec->loopback.amplist)
 	if (!spec->loopback.amplist)
-		spec->loopback.amplist = alc883_loopbacks;
+		spec->loopback.amplist = alc882_loopbacks;
 #endif
 #endif
 	codec->proc_widget_hook = print_realtek_coef;
 	codec->proc_widget_hook = print_realtek_coef;
 
 
 	return 0;
 	return 0;
 }
 }
 
 
+
 /*
 /*
  * ALC262 support
  * ALC262 support
  */
  */
@@ -10032,13 +10109,12 @@ static struct snd_kcontrol_new alc262_HP_BPC_WildWest_option_mixer[] = {
 };
 };
 
 
 /* mute/unmute internal speaker according to the hp jack and mute state */
 /* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc262_hp_t5735_init_hook(struct hda_codec *codec)
+static void alc262_hp_t5735_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
 	spec->autocfg.hp_pins[0] = 0x15;
 	spec->autocfg.hp_pins[0] = 0x15;
 	spec->autocfg.speaker_pins[0] = 0x0c; /* HACK: not actually a pin */
 	spec->autocfg.speaker_pins[0] = 0x0c; /* HACK: not actually a pin */
-	alc_automute_amp(codec);
 }
 }
 
 
 static struct snd_kcontrol_new alc262_hp_t5735_mixer[] = {
 static struct snd_kcontrol_new alc262_hp_t5735_mixer[] = {
@@ -10195,22 +10271,20 @@ static void alc262_hippo_unsol_event(struct hda_codec *codec, unsigned int res)
 	alc262_hippo_automute(codec);
 	alc262_hippo_automute(codec);
 }
 }
 
 
-static void alc262_hippo_init_hook(struct hda_codec *codec)
+static void alc262_hippo_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
 	spec->autocfg.hp_pins[0] = 0x15;
 	spec->autocfg.hp_pins[0] = 0x15;
 	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[0] = 0x14;
-	alc262_hippo_automute(codec);
 }
 }
 
 
-static void alc262_hippo1_init_hook(struct hda_codec *codec)
+static void alc262_hippo1_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
 	spec->autocfg.hp_pins[0] = 0x1b;
 	spec->autocfg.hp_pins[0] = 0x1b;
 	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[0] = 0x14;
-	alc262_hippo_automute(codec);
 }
 }
 
 
 
 
@@ -10267,13 +10341,12 @@ static struct hda_verb alc262_tyan_verbs[] = {
 };
 };
 
 
 /* unsolicited event for HP jack sensing */
 /* unsolicited event for HP jack sensing */
-static void alc262_tyan_init_hook(struct hda_codec *codec)
+static void alc262_tyan_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
 	spec->autocfg.hp_pins[0] = 0x1b;
 	spec->autocfg.hp_pins[0] = 0x1b;
 	spec->autocfg.speaker_pins[0] = 0x15;
 	spec->autocfg.speaker_pins[0] = 0x15;
-	alc_automute_amp(codec);
 }
 }
 
 
 
 
@@ -10365,12 +10438,6 @@ static struct hda_verb alc262_eapd_verbs[] = {
 	{ }
 	{ }
 };
 };
 
 
-static struct hda_verb alc262_hippo_unsol_verbs[] = {
-	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
-	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
-	{}
-};
-
 static struct hda_verb alc262_hippo1_unsol_verbs[] = {
 static struct hda_verb alc262_hippo1_unsol_verbs[] = {
 	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
 	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
 	{0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
 	{0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
@@ -10391,14 +10458,6 @@ static struct hda_verb alc262_sony_unsol_verbs[] = {
 	{}
 	{}
 };
 };
 
 
-static struct hda_input_mux alc262_dmic_capture_source = {
-	.num_items = 2,
-	.items = {
-		{ "Int DMic", 0x9 },
-		{ "Mic", 0x0 },
-	},
-};
-
 static struct snd_kcontrol_new alc262_toshiba_s06_mixer[] = {
 static struct snd_kcontrol_new alc262_toshiba_s06_mixer[] = {
 	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
@@ -10420,35 +10479,17 @@ static struct hda_verb alc262_toshiba_s06_verbs[] = {
 	{}
 	{}
 };
 };
 
 
-static void alc262_dmic_automute(struct hda_codec *codec)
-{
-	unsigned int present;
-
-	present = snd_hda_codec_read(codec, 0x18, 0,
-					AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	snd_hda_codec_write(codec, 0x22, 0,
-				AC_VERB_SET_CONNECT_SEL, present ? 0x0 : 0x09);
-}
-
-
-/* unsolicited event for HP jack sensing */
-static void alc262_toshiba_s06_unsol_event(struct hda_codec *codec,
-				       unsigned int res)
-{
-	if ((res >> 26) == ALC880_MIC_EVENT)
-		alc262_dmic_automute(codec);
-	else
-		alc_sku_unsol_event(codec, res);
-}
-
-static void alc262_toshiba_s06_init_hook(struct hda_codec *codec)
+static void alc262_toshiba_s06_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
 	spec->autocfg.hp_pins[0] = 0x15;
 	spec->autocfg.hp_pins[0] = 0x15;
 	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[0] = 0x14;
-	alc_automute_pin(codec);
-	alc262_dmic_automute(codec);
+	spec->ext_mic.pin = 0x18;
+	spec->ext_mic.mux_idx = 0;
+	spec->int_mic.pin = 0x12;
+	spec->int_mic.mux_idx = 9;
+	spec->auto_mic = 1;
 }
 }
 
 
 /*
 /*
@@ -10866,104 +10907,111 @@ static struct snd_kcontrol_new alc262_ultra_capture_mixer[] = {
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
+/* We use two mixers depending on the output pin; 0x16 is a mono output
+ * and thus it's bound with a different mixer.
+ * This function returns which mixer amp should be used.
+ */
+static int alc262_check_volbit(hda_nid_t nid)
+{
+	if (!nid)
+		return 0;
+	else if (nid == 0x16)
+		return 2;
+	else
+		return 1;
+}
+
+static int alc262_add_out_vol_ctl(struct alc_spec *spec, hda_nid_t nid,
+				  const char *pfx, int *vbits)
+{
+	char name[32];
+	unsigned long val;
+	int vbit;
+
+	vbit = alc262_check_volbit(nid);
+	if (!vbit)
+		return 0;
+	if (*vbits & vbit) /* a volume control for this mixer already there */
+		return 0;
+	*vbits |= vbit;
+	snprintf(name, sizeof(name), "%s Playback Volume", pfx);
+	if (vbit == 2)
+		val = HDA_COMPOSE_AMP_VAL(0x0e, 2, 0, HDA_OUTPUT);
+	else
+		val = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT);
+	return add_control(spec, ALC_CTL_WIDGET_VOL, name, val);
+}
+
+static int alc262_add_out_sw_ctl(struct alc_spec *spec, hda_nid_t nid,
+				 const char *pfx)
+{
+	char name[32];
+	unsigned long val;
+
+	if (!nid)
+		return 0;
+	snprintf(name, sizeof(name), "%s Playback Switch", pfx);
+	if (nid == 0x16)
+		val = HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT);
+	else
+		val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+	return add_control(spec, ALC_CTL_WIDGET_MUTE, name, val);
+}
+
 /* add playback controls from the parsed DAC table */
 /* add playback controls from the parsed DAC table */
 static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec,
 static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec,
 					     const struct auto_pin_cfg *cfg)
 					     const struct auto_pin_cfg *cfg)
 {
 {
-	hda_nid_t nid;
+	const char *pfx;
+	int vbits;
 	int err;
 	int err;
 
 
 	spec->multiout.num_dacs = 1;	/* only use one dac */
 	spec->multiout.num_dacs = 1;	/* only use one dac */
 	spec->multiout.dac_nids = spec->private_dac_nids;
 	spec->multiout.dac_nids = spec->private_dac_nids;
 	spec->multiout.dac_nids[0] = 2;
 	spec->multiout.dac_nids[0] = 2;
 
 
-	nid = cfg->line_out_pins[0];
-	if (nid) {
-		err = add_control(spec, ALC_CTL_WIDGET_VOL,
-				  "Front Playback Volume",
-				  HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT));
-		if (err < 0)
-			return err;
-		err = add_control(spec, ALC_CTL_WIDGET_MUTE,
-				  "Front Playback Switch",
-				  HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
-		if (err < 0)
-			return err;
-	}
-
-	nid = cfg->speaker_pins[0];
-	if (nid) {
-		if (nid == 0x16) {
-			err = add_control(spec, ALC_CTL_WIDGET_VOL,
-					  "Speaker Playback Volume",
-					  HDA_COMPOSE_AMP_VAL(0x0e, 2, 0,
-							      HDA_OUTPUT));
-			if (err < 0)
-				return err;
-			err = add_control(spec, ALC_CTL_WIDGET_MUTE,
-					  "Speaker Playback Switch",
-					  HDA_COMPOSE_AMP_VAL(nid, 2, 0,
-							      HDA_OUTPUT));
-			if (err < 0)
-				return err;
-		} else {
-			err = add_control(spec, ALC_CTL_WIDGET_MUTE,
-					  "Speaker Playback Switch",
-					  HDA_COMPOSE_AMP_VAL(nid, 3, 0,
-							      HDA_OUTPUT));
-			if (err < 0)
-				return err;
-		}
-	}
-	nid = cfg->hp_pins[0];
-	if (nid) {
-		/* spec->multiout.hp_nid = 2; */
-		if (nid == 0x16) {
-			err = add_control(spec, ALC_CTL_WIDGET_VOL,
-					  "Headphone Playback Volume",
-					  HDA_COMPOSE_AMP_VAL(0x0e, 2, 0,
-							      HDA_OUTPUT));
-			if (err < 0)
-				return err;
-			err = add_control(spec, ALC_CTL_WIDGET_MUTE,
-					  "Headphone Playback Switch",
-					  HDA_COMPOSE_AMP_VAL(nid, 2, 0,
-							      HDA_OUTPUT));
-			if (err < 0)
-				return err;
-		} else {
-			err = add_control(spec, ALC_CTL_WIDGET_MUTE,
-					  "Headphone Playback Switch",
-					  HDA_COMPOSE_AMP_VAL(nid, 3, 0,
-							      HDA_OUTPUT));
-			if (err < 0)
-				return err;
-		}
-	}
-	return 0;
-}
-
-static int alc262_auto_create_analog_input_ctls(struct alc_spec *spec,
-						const struct auto_pin_cfg *cfg)
-{
-	int err;
+	if (!cfg->speaker_pins[0] && !cfg->hp_pins[0])
+		pfx = "Master";
+	else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+		pfx = "Speaker";
+	else
+		pfx = "Front";
+	err = alc262_add_out_sw_ctl(spec, cfg->line_out_pins[0], pfx);
+	if (err < 0)
+		return err;
+	err = alc262_add_out_sw_ctl(spec, cfg->speaker_pins[0], "Speaker");
+	if (err < 0)
+		return err;
+	err = alc262_add_out_sw_ctl(spec, cfg->hp_pins[0], "Headphone");
+	if (err < 0)
+		return err;
 
 
-	err = alc880_auto_create_analog_input_ctls(spec, cfg);
+	vbits = alc262_check_volbit(cfg->line_out_pins[0]) |
+		alc262_check_volbit(cfg->speaker_pins[0]) |
+		alc262_check_volbit(cfg->hp_pins[0]);
+	if (vbits == 1 || vbits == 2)
+		pfx = "Master"; /* only one mixer is used */
+	else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+		pfx = "Speaker";
+	else
+		pfx = "Front";
+	vbits = 0;
+	err = alc262_add_out_vol_ctl(spec, cfg->line_out_pins[0], pfx, &vbits);
+	if (err < 0)
+		return err;
+	err = alc262_add_out_vol_ctl(spec, cfg->speaker_pins[0], "Speaker",
+				     &vbits);
+	if (err < 0)
+		return err;
+	err = alc262_add_out_vol_ctl(spec, cfg->hp_pins[0], "Headphone",
+				     &vbits);
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
-	/* digital-mic input pin is excluded in alc880_auto_create..()
-	 * because it's under 0x18
-	 */
-	if (cfg->input_pins[AUTO_PIN_MIC] == 0x12 ||
-	    cfg->input_pins[AUTO_PIN_FRONT_MIC] == 0x12) {
-		struct hda_input_mux *imux = &spec->private_imux[0];
-		imux->items[imux->num_items].label = "Int Mic";
-		imux->items[imux->num_items].index = 0x09;
-		imux->num_items++;
-	}
 	return 0;
 	return 0;
 }
 }
 
 
+#define alc262_auto_create_input_ctls \
+	alc880_auto_create_input_ctls
 
 
 /*
 /*
  * generic initialization of ADC, input mixers and output mixers
  * generic initialization of ADC, input mixers and output mixers
@@ -11281,7 +11329,7 @@ static int alc262_parse_auto_config(struct hda_codec *codec)
 	err = alc262_auto_create_multi_out_ctls(spec, &spec->autocfg);
 	err = alc262_auto_create_multi_out_ctls(spec, &spec->autocfg);
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
-	err = alc262_auto_create_analog_input_ctls(spec, &spec->autocfg);
+	err = alc262_auto_create_input_ctls(codec, &spec->autocfg);
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
 
 
@@ -11412,7 +11460,7 @@ static struct alc_config_preset alc262_presets[] = {
 	},
 	},
 	[ALC262_HIPPO] = {
 	[ALC262_HIPPO] = {
 		.mixers = { alc262_hippo_mixer },
 		.mixers = { alc262_hippo_mixer },
-		.init_verbs = { alc262_init_verbs, alc262_hippo_unsol_verbs},
+		.init_verbs = { alc262_init_verbs, alc_hp15_unsol_verbs},
 		.num_dacs = ARRAY_SIZE(alc262_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc262_dac_nids),
 		.dac_nids = alc262_dac_nids,
 		.dac_nids = alc262_dac_nids,
 		.hp_nid = 0x03,
 		.hp_nid = 0x03,
@@ -11421,7 +11469,8 @@ static struct alc_config_preset alc262_presets[] = {
 		.channel_mode = alc262_modes,
 		.channel_mode = alc262_modes,
 		.input_mux = &alc262_capture_source,
 		.input_mux = &alc262_capture_source,
 		.unsol_event = alc262_hippo_unsol_event,
 		.unsol_event = alc262_hippo_unsol_event,
-		.init_hook = alc262_hippo_init_hook,
+		.setup = alc262_hippo_setup,
+		.init_hook = alc262_hippo_automute,
 	},
 	},
 	[ALC262_HIPPO_1] = {
 	[ALC262_HIPPO_1] = {
 		.mixers = { alc262_hippo1_mixer },
 		.mixers = { alc262_hippo1_mixer },
@@ -11434,7 +11483,8 @@ static struct alc_config_preset alc262_presets[] = {
 		.channel_mode = alc262_modes,
 		.channel_mode = alc262_modes,
 		.input_mux = &alc262_capture_source,
 		.input_mux = &alc262_capture_source,
 		.unsol_event = alc262_hippo_unsol_event,
 		.unsol_event = alc262_hippo_unsol_event,
-		.init_hook = alc262_hippo1_init_hook,
+		.setup = alc262_hippo1_setup,
+		.init_hook = alc262_hippo_automute,
 	},
 	},
 	[ALC262_FUJITSU] = {
 	[ALC262_FUJITSU] = {
 		.mixers = { alc262_fujitsu_mixer },
 		.mixers = { alc262_fujitsu_mixer },
@@ -11497,7 +11547,8 @@ static struct alc_config_preset alc262_presets[] = {
 		.channel_mode = alc262_modes,
 		.channel_mode = alc262_modes,
 		.input_mux = &alc262_capture_source,
 		.input_mux = &alc262_capture_source,
 		.unsol_event = alc_automute_amp_unsol_event,
 		.unsol_event = alc_automute_amp_unsol_event,
-		.init_hook = alc262_hp_t5735_init_hook,
+		.setup = alc262_hp_t5735_setup,
+		.init_hook = alc_automute_amp,
 	},
 	},
 	[ALC262_HP_RP5700] = {
 	[ALC262_HP_RP5700] = {
 		.mixers = { alc262_hp_rp5700_mixer },
 		.mixers = { alc262_hp_rp5700_mixer },
@@ -11528,11 +11579,13 @@ static struct alc_config_preset alc262_presets[] = {
 		.channel_mode = alc262_modes,
 		.channel_mode = alc262_modes,
 		.input_mux = &alc262_capture_source,
 		.input_mux = &alc262_capture_source,
 		.unsol_event = alc262_hippo_unsol_event,
 		.unsol_event = alc262_hippo_unsol_event,
-		.init_hook = alc262_hippo_init_hook,
+		.setup = alc262_hippo_setup,
+		.init_hook = alc262_hippo_automute,
 	},
 	},
 	[ALC262_BENQ_T31] = {
 	[ALC262_BENQ_T31] = {
 		.mixers = { alc262_benq_t31_mixer },
 		.mixers = { alc262_benq_t31_mixer },
-		.init_verbs = { alc262_init_verbs, alc262_benq_t31_EAPD_verbs, alc262_hippo_unsol_verbs },
+		.init_verbs = { alc262_init_verbs, alc262_benq_t31_EAPD_verbs,
+				alc_hp15_unsol_verbs },
 		.num_dacs = ARRAY_SIZE(alc262_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc262_dac_nids),
 		.dac_nids = alc262_dac_nids,
 		.dac_nids = alc262_dac_nids,
 		.hp_nid = 0x03,
 		.hp_nid = 0x03,
@@ -11540,7 +11593,8 @@ static struct alc_config_preset alc262_presets[] = {
 		.channel_mode = alc262_modes,
 		.channel_mode = alc262_modes,
 		.input_mux = &alc262_capture_source,
 		.input_mux = &alc262_capture_source,
 		.unsol_event = alc262_hippo_unsol_event,
 		.unsol_event = alc262_hippo_unsol_event,
-		.init_hook = alc262_hippo_init_hook,
+		.setup = alc262_hippo_setup,
+		.init_hook = alc262_hippo_automute,
 	},
 	},
 	[ALC262_ULTRA] = {
 	[ALC262_ULTRA] = {
 		.mixers = { alc262_ultra_mixer },
 		.mixers = { alc262_ultra_mixer },
@@ -11592,9 +11646,9 @@ static struct alc_config_preset alc262_presets[] = {
 		.dig_out_nid = ALC262_DIGOUT_NID,
 		.dig_out_nid = ALC262_DIGOUT_NID,
 		.num_channel_mode = ARRAY_SIZE(alc262_modes),
 		.num_channel_mode = ARRAY_SIZE(alc262_modes),
 		.channel_mode = alc262_modes,
 		.channel_mode = alc262_modes,
-		.input_mux = &alc262_dmic_capture_source,
-		.unsol_event = alc262_toshiba_s06_unsol_event,
-		.init_hook = alc262_toshiba_s06_init_hook,
+		.unsol_event = alc_sku_unsol_event,
+		.setup = alc262_toshiba_s06_setup,
+		.init_hook = alc_inithook,
 	},
 	},
 	[ALC262_TOSHIBA_RX1] = {
 	[ALC262_TOSHIBA_RX1] = {
 		.mixers = { alc262_toshiba_rx1_mixer },
 		.mixers = { alc262_toshiba_rx1_mixer },
@@ -11606,7 +11660,8 @@ static struct alc_config_preset alc262_presets[] = {
 		.channel_mode = alc262_modes,
 		.channel_mode = alc262_modes,
 		.input_mux = &alc262_capture_source,
 		.input_mux = &alc262_capture_source,
 		.unsol_event = alc262_hippo_unsol_event,
 		.unsol_event = alc262_hippo_unsol_event,
-		.init_hook = alc262_hippo_init_hook,
+		.setup = alc262_hippo_setup,
+		.init_hook = alc262_hippo_automute,
 	},
 	},
 	[ALC262_TYAN] = {
 	[ALC262_TYAN] = {
 		.mixers = { alc262_tyan_mixer },
 		.mixers = { alc262_tyan_mixer },
@@ -11619,7 +11674,8 @@ static struct alc_config_preset alc262_presets[] = {
 		.channel_mode = alc262_modes,
 		.channel_mode = alc262_modes,
 		.input_mux = &alc262_capture_source,
 		.input_mux = &alc262_capture_source,
 		.unsol_event = alc_automute_amp_unsol_event,
 		.unsol_event = alc_automute_amp_unsol_event,
-		.init_hook = alc262_tyan_init_hook,
+		.setup = alc262_tyan_setup,
+		.init_hook = alc_automute_amp,
 	},
 	},
 };
 };
 
 
@@ -11654,8 +11710,8 @@ static int patch_alc262(struct hda_codec *codec)
 						  alc262_cfg_tbl);
 						  alc262_cfg_tbl);
 
 
 	if (board_config < 0) {
 	if (board_config < 0) {
-		printk(KERN_INFO "hda_codec: Unknown model for %s, "
-		       "trying auto-probe from BIOS...\n", codec->chip_name);
+		printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+		       codec->chip_name);
 		board_config = ALC262_AUTO;
 		board_config = ALC262_AUTO;
 	}
 	}
 
 
@@ -11682,7 +11738,7 @@ static int patch_alc262(struct hda_codec *codec)
 	}
 	}
 
 
 	if (board_config != ALC262_AUTO)
 	if (board_config != ALC262_AUTO)
-		setup_preset(spec, &alc262_presets[board_config]);
+		setup_preset(codec, &alc262_presets[board_config]);
 
 
 	spec->stream_analog_playback = &alc262_pcm_analog_playback;
 	spec->stream_analog_playback = &alc262_pcm_analog_playback;
 	spec->stream_analog_capture = &alc262_pcm_analog_capture;
 	spec->stream_analog_capture = &alc262_pcm_analog_capture;
@@ -11708,7 +11764,7 @@ static int patch_alc262(struct hda_codec *codec)
 			unsigned int wcap = get_wcaps(codec, 0x07);
 			unsigned int wcap = get_wcaps(codec, 0x07);
 
 
 			/* get type */
 			/* get type */
-			wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+			wcap = get_wcaps_type(wcap);
 			if (wcap != AC_WID_AUD_IN) {
 			if (wcap != AC_WID_AUD_IN) {
 				spec->adc_nids = alc262_adc_nids_alt;
 				spec->adc_nids = alc262_adc_nids_alt;
 				spec->num_adc_nids =
 				spec->num_adc_nids =
@@ -11723,7 +11779,7 @@ static int patch_alc262(struct hda_codec *codec)
 		}
 		}
 	}
 	}
 	if (!spec->cap_mixer && !spec->no_analog)
 	if (!spec->cap_mixer && !spec->no_analog)
-		set_capture_mixer(spec);
+		set_capture_mixer(codec);
 	if (!spec->no_analog)
 	if (!spec->no_analog)
 		set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
 		set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
 
 
@@ -11815,14 +11871,6 @@ static struct hda_verb alc268_toshiba_verbs[] = {
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
-static struct hda_input_mux alc268_acer_lc_capture_source = {
-	.num_items = 2,
-	.items = {
-		{ "i-Mic", 0x6 },
-		{ "E-Mic", 0x0 },
-	},
-};
-
 /* Acer specific */
 /* Acer specific */
 /* bind volumes of both NID 0x02 and 0x03 */
 /* bind volumes of both NID 0x02 and 0x03 */
 static struct hda_bind_ctls alc268_acer_bind_master_vol = {
 static struct hda_bind_ctls alc268_acer_bind_master_vol = {
@@ -11941,7 +11989,8 @@ static struct hda_verb alc268_acer_verbs[] = {
 
 
 /* unsolicited event for HP jack sensing */
 /* unsolicited event for HP jack sensing */
 #define alc268_toshiba_unsol_event	alc262_hippo_unsol_event
 #define alc268_toshiba_unsol_event	alc262_hippo_unsol_event
-#define alc268_toshiba_init_hook	alc262_hippo_init_hook
+#define alc268_toshiba_setup		alc262_hippo_setup
+#define alc268_toshiba_automute		alc262_hippo_automute
 
 
 static void alc268_acer_unsol_event(struct hda_codec *codec,
 static void alc268_acer_unsol_event(struct hda_codec *codec,
 				       unsigned int res)
 				       unsigned int res)
@@ -11971,30 +12020,33 @@ static void alc268_aspire_one_speaker_automute(struct hda_codec *codec)
 				AMP_IN_MUTE(0), bits);
 				AMP_IN_MUTE(0), bits);
 }
 }
 
 
-
-static void alc268_acer_mic_automute(struct hda_codec *codec)
-{
-	unsigned int present;
-
-	present = snd_hda_codec_read(codec, 0x18, 0,
-				AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_CONNECT_SEL,
-			    present ? 0x0 : 0x6);
-}
-
 static void alc268_acer_lc_unsol_event(struct hda_codec *codec,
 static void alc268_acer_lc_unsol_event(struct hda_codec *codec,
 				    unsigned int res)
 				    unsigned int res)
 {
 {
-	if ((res >> 26) == ALC880_HP_EVENT)
+	switch (res >> 26) {
+	case ALC880_HP_EVENT:
 		alc268_aspire_one_speaker_automute(codec);
 		alc268_aspire_one_speaker_automute(codec);
-	if ((res >> 26) == ALC880_MIC_EVENT)
-		alc268_acer_mic_automute(codec);
+		break;
+	case ALC880_MIC_EVENT:
+		alc_mic_automute(codec);
+		break;
+	}
+}
+
+static void alc268_acer_lc_setup(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	spec->ext_mic.pin = 0x18;
+	spec->ext_mic.mux_idx = 0;
+	spec->int_mic.pin = 0x12;
+	spec->int_mic.mux_idx = 6;
+	spec->auto_mic = 1;
 }
 }
 
 
 static void alc268_acer_lc_init_hook(struct hda_codec *codec)
 static void alc268_acer_lc_init_hook(struct hda_codec *codec)
 {
 {
 	alc268_aspire_one_speaker_automute(codec);
 	alc268_aspire_one_speaker_automute(codec);
-	alc268_acer_mic_automute(codec);
+	alc_mic_automute(codec);
 }
 }
 
 
 static struct snd_kcontrol_new alc268_dell_mixer[] = {
 static struct snd_kcontrol_new alc268_dell_mixer[] = {
@@ -12012,17 +12064,22 @@ static struct hda_verb alc268_dell_verbs[] = {
 	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
 	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
 	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
 	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
 	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
 	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_MIC_EVENT | AC_USRSP_EN},
 	{ }
 	{ }
 };
 };
 
 
 /* mute/unmute internal speaker according to the hp jack and mute state */
 /* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc268_dell_init_hook(struct hda_codec *codec)
+static void alc268_dell_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
 	spec->autocfg.hp_pins[0] = 0x15;
 	spec->autocfg.hp_pins[0] = 0x15;
 	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[0] = 0x14;
-	alc_automute_pin(codec);
+	spec->ext_mic.pin = 0x18;
+	spec->ext_mic.mux_idx = 0;
+	spec->int_mic.pin = 0x19;
+	spec->int_mic.mux_idx = 1;
+	spec->auto_mic = 1;
 }
 }
 
 
 static struct snd_kcontrol_new alc267_quanta_il1_mixer[] = {
 static struct snd_kcontrol_new alc267_quanta_il1_mixer[] = {
@@ -12043,38 +12100,16 @@ static struct hda_verb alc267_quanta_il1_verbs[] = {
 	{ }
 	{ }
 };
 };
 
 
-static void alc267_quanta_il1_mic_automute(struct hda_codec *codec)
-{
-	unsigned int present;
-
-	present = snd_hda_codec_read(codec, 0x18, 0,
-				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	snd_hda_codec_write(codec, 0x23, 0,
-			    AC_VERB_SET_CONNECT_SEL,
-			    present ? 0x00 : 0x01);
-}
-
-static void alc267_quanta_il1_init_hook(struct hda_codec *codec)
+static void alc267_quanta_il1_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
-
 	spec->autocfg.hp_pins[0] = 0x15;
 	spec->autocfg.hp_pins[0] = 0x15;
 	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[0] = 0x14;
-	alc_automute_pin(codec);
-	alc267_quanta_il1_mic_automute(codec);
-}
-
-static void alc267_quanta_il1_unsol_event(struct hda_codec *codec,
-					   unsigned int res)
-{
-	switch (res >> 26) {
-	case ALC880_MIC_EVENT:
-		alc267_quanta_il1_mic_automute(codec);
-		break;
-	default:
-		alc_sku_unsol_event(codec, res);
-		break;
-	}
+	spec->ext_mic.pin = 0x18;
+	spec->ext_mic.mux_idx = 0;
+	spec->int_mic.pin = 0x19;
+	spec->int_mic.mux_idx = 1;
+	spec->auto_mic = 1;
 }
 }
 
 
 /*
 /*
@@ -12154,21 +12189,16 @@ static struct hda_verb alc268_volume_init_verbs[] = {
 	{ }
 	{ }
 };
 };
 
 
+static struct snd_kcontrol_new alc268_capture_nosrc_mixer[] = {
+	HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT),
+	{ } /* end */
+};
+
 static struct snd_kcontrol_new alc268_capture_alt_mixer[] = {
 static struct snd_kcontrol_new alc268_capture_alt_mixer[] = {
 	HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		/* The multiple "Capture Source" controls confuse alsamixer
-		 * So call somewhat different..
-		 */
-		/* .name = "Capture Source", */
-		.name = "Input Source",
-		.count = 1,
-		.info = alc_mux_enum_info,
-		.get = alc_mux_enum_get,
-		.put = alc_mux_enum_put,
-	},
+	_DEFINE_CAPSRC(1),
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
@@ -12177,18 +12207,7 @@ static struct snd_kcontrol_new alc268_capture_mixer[] = {
 	HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x24, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x24, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x24, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x24, 0x0, HDA_OUTPUT),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		/* The multiple "Capture Source" controls confuse alsamixer
-		 * So call somewhat different..
-		 */
-		/* .name = "Capture Source", */
-		.name = "Input Source",
-		.count = 2,
-		.info = alc_mux_enum_info,
-		.get = alc_mux_enum_get,
-		.put = alc_mux_enum_put,
-	},
+	_DEFINE_CAPSRC(2),
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
@@ -12275,26 +12294,38 @@ static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
 				    const char *ctlname, int idx)
 				    const char *ctlname, int idx)
 {
 {
 	char name[32];
 	char name[32];
+	hda_nid_t dac;
 	int err;
 	int err;
 
 
 	sprintf(name, "%s Playback Volume", ctlname);
 	sprintf(name, "%s Playback Volume", ctlname);
-	if (nid == 0x14) {
-		err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
-				  HDA_COMPOSE_AMP_VAL(0x02, 3, idx,
-						      HDA_OUTPUT));
-		if (err < 0)
-			return err;
-	} else if (nid == 0x15) {
+	switch (nid) {
+	case 0x14:
+	case 0x16:
+		dac = 0x02;
+		break;
+	case 0x15:
+		dac = 0x03;
+		break;
+	default:
+		return 0;
+	}
+	if (spec->multiout.dac_nids[0] != dac &&
+	    spec->multiout.dac_nids[1] != dac) {
 		err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
 		err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
-				  HDA_COMPOSE_AMP_VAL(0x03, 3, idx,
+				  HDA_COMPOSE_AMP_VAL(dac, 3, idx,
 						      HDA_OUTPUT));
 						      HDA_OUTPUT));
 		if (err < 0)
 		if (err < 0)
 			return err;
 			return err;
-	} else
-		return -1;
+		spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac;
+	}
+
 	sprintf(name, "%s Playback Switch", ctlname);
 	sprintf(name, "%s Playback Switch", ctlname);
-	err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+	if (nid != 0x16)
+		err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
 			  HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT));
 			  HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT));
+	else /* mono */
+		err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+			  HDA_COMPOSE_AMP_VAL(nid, 2, idx, HDA_OUTPUT));
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
 	return 0;
 	return 0;
@@ -12307,14 +12338,19 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec,
 	hda_nid_t nid;
 	hda_nid_t nid;
 	int err;
 	int err;
 
 
-	spec->multiout.num_dacs = 2;	/* only use one dac */
 	spec->multiout.dac_nids = spec->private_dac_nids;
 	spec->multiout.dac_nids = spec->private_dac_nids;
-	spec->multiout.dac_nids[0] = 2;
-	spec->multiout.dac_nids[1] = 3;
 
 
 	nid = cfg->line_out_pins[0];
 	nid = cfg->line_out_pins[0];
-	if (nid)
-		alc268_new_analog_output(spec, nid, "Front", 0);
+	if (nid) {
+		const char *name;
+		if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+			name = "Speaker";
+		else
+			name = "Front";
+		err = alc268_new_analog_output(spec, nid, name, 0);
+		if (err < 0)
+			return err;
+	}
 
 
 	nid = cfg->speaker_pins[0];
 	nid = cfg->speaker_pins[0];
 	if (nid == 0x1d) {
 	if (nid == 0x1d) {
@@ -12323,16 +12359,23 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec,
 				  HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
 				  HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
 		if (err < 0)
 		if (err < 0)
 			return err;
 			return err;
+	} else {
+		err = alc268_new_analog_output(spec, nid, "Speaker", 0);
+		if (err < 0)
+			return err;
 	}
 	}
 	nid = cfg->hp_pins[0];
 	nid = cfg->hp_pins[0];
-	if (nid)
-		alc268_new_analog_output(spec, nid, "Headphone", 0);
+	if (nid) {
+		err = alc268_new_analog_output(spec, nid, "Headphone", 0);
+		if (err < 0)
+			return err;
+	}
 
 
 	nid = cfg->line_out_pins[1] | cfg->line_out_pins[2];
 	nid = cfg->line_out_pins[1] | cfg->line_out_pins[2];
 	if (nid == 0x16) {
 	if (nid == 0x16) {
 		err = add_control(spec, ALC_CTL_WIDGET_MUTE,
 		err = add_control(spec, ALC_CTL_WIDGET_MUTE,
 				  "Mono Playback Switch",
 				  "Mono Playback Switch",
-				  HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_INPUT));
+				  HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT));
 		if (err < 0)
 		if (err < 0)
 			return err;
 			return err;
 	}
 	}
@@ -12340,38 +12383,46 @@ static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec,
 }
 }
 
 
 /* create playback/capture controls for input pins */
 /* create playback/capture controls for input pins */
-static int alc268_auto_create_analog_input_ctls(struct alc_spec *spec,
+static int alc268_auto_create_input_ctls(struct hda_codec *codec,
 						const struct auto_pin_cfg *cfg)
 						const struct auto_pin_cfg *cfg)
 {
 {
-	struct hda_input_mux *imux = &spec->private_imux[0];
-	int i, idx1;
+	return alc_auto_create_input_ctls(codec, cfg, 0, 0x23, 0x24);
+}
 
 
-	for (i = 0; i < AUTO_PIN_LAST; i++) {
-		switch(cfg->input_pins[i]) {
-		case 0x18:
-			idx1 = 0;	/* Mic 1 */
-			break;
-		case 0x19:
-			idx1 = 1;	/* Mic 2 */
-			break;
-		case 0x1a:
-			idx1 = 2;	/* Line In */
-			break;
-		case 0x1c:
-			idx1 = 3;	/* CD */
-			break;
-		case 0x12:
-		case 0x13:
-			idx1 = 6;	/* digital mics */
-			break;
-		default:
-			continue;
-		}
-		imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
-		imux->items[imux->num_items].index = idx1;
-		imux->num_items++;
+static void alc268_auto_set_output_and_unmute(struct hda_codec *codec,
+					      hda_nid_t nid, int pin_type)
+{
+	int idx;
+
+	alc_set_pin_output(codec, nid, pin_type);
+	if (nid == 0x14 || nid == 0x16)
+		idx = 0;
+	else
+		idx = 1;
+	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, idx);
+}
+
+static void alc268_auto_init_multi_out(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	hda_nid_t nid = spec->autocfg.line_out_pins[0];
+	if (nid) {
+		int pin_type = get_pin_type(spec->autocfg.line_out_type);
+		alc268_auto_set_output_and_unmute(codec, nid, pin_type);
 	}
 	}
-	return 0;
+}
+
+static void alc268_auto_init_hp_out(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	hda_nid_t pin;
+
+	pin = spec->autocfg.hp_pins[0];
+	if (pin)
+		alc268_auto_set_output_and_unmute(codec, pin, PIN_HP);
+	pin = spec->autocfg.speaker_pins[0];
+	if (pin)
+		alc268_auto_set_output_and_unmute(codec, pin, PIN_OUT);
 }
 }
 
 
 static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec)
 static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec)
@@ -12382,9 +12433,10 @@ static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec)
 	hda_nid_t line_nid = spec->autocfg.line_out_pins[0];
 	hda_nid_t line_nid = spec->autocfg.line_out_pins[0];
 	unsigned int	dac_vol1, dac_vol2;
 	unsigned int	dac_vol1, dac_vol2;
 
 
-	if (speaker_nid) {
+	if (line_nid == 0x1d || speaker_nid == 0x1d) {
 		snd_hda_codec_write(codec, speaker_nid, 0,
 		snd_hda_codec_write(codec, speaker_nid, 0,
 				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
 				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+		/* mute mixer inputs from 0x1d */
 		snd_hda_codec_write(codec, 0x0f, 0,
 		snd_hda_codec_write(codec, 0x0f, 0,
 				    AC_VERB_SET_AMP_GAIN_MUTE,
 				    AC_VERB_SET_AMP_GAIN_MUTE,
 				    AMP_IN_UNMUTE(1));
 				    AMP_IN_UNMUTE(1));
@@ -12392,6 +12444,7 @@ static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec)
 				    AC_VERB_SET_AMP_GAIN_MUTE,
 				    AC_VERB_SET_AMP_GAIN_MUTE,
 				    AMP_IN_UNMUTE(1));
 				    AMP_IN_UNMUTE(1));
 	} else {
 	} else {
+		/* unmute mixer inputs from 0x1d */
 		snd_hda_codec_write(codec, 0x0f, 0,
 		snd_hda_codec_write(codec, 0x0f, 0,
 				    AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1));
 				    AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1));
 		snd_hda_codec_write(codec, 0x10, 0,
 		snd_hda_codec_write(codec, 0x10, 0,
@@ -12448,7 +12501,7 @@ static int alc268_parse_auto_config(struct hda_codec *codec)
 	err = alc268_auto_create_multi_out_ctls(spec, &spec->autocfg);
 	err = alc268_auto_create_multi_out_ctls(spec, &spec->autocfg);
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
-	err = alc268_auto_create_analog_input_ctls(spec, &spec->autocfg);
+	err = alc268_auto_create_input_ctls(codec, &spec->autocfg);
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
 
 
@@ -12467,7 +12520,7 @@ static int alc268_parse_auto_config(struct hda_codec *codec)
 		add_mixer(spec, alc268_beep_mixer);
 		add_mixer(spec, alc268_beep_mixer);
 
 
 	add_verb(spec, alc268_volume_init_verbs);
 	add_verb(spec, alc268_volume_init_verbs);
-	spec->num_mux_defs = 1;
+	spec->num_mux_defs = 2;
 	spec->input_mux = &spec->private_imux[0];
 	spec->input_mux = &spec->private_imux[0];
 
 
 	err = alc_auto_add_mic_boost(codec);
 	err = alc_auto_add_mic_boost(codec);
@@ -12479,8 +12532,6 @@ static int alc268_parse_auto_config(struct hda_codec *codec)
 	return 1;
 	return 1;
 }
 }
 
 
-#define alc268_auto_init_multi_out	alc882_auto_init_multi_out
-#define alc268_auto_init_hp_out		alc882_auto_init_hp_out
 #define alc268_auto_init_analog_input	alc882_auto_init_analog_input
 #define alc268_auto_init_analog_input	alc882_auto_init_analog_input
 
 
 /* init callback for auto-configuration model -- overriding the default init */
 /* init callback for auto-configuration model -- overriding the default init */
@@ -12523,8 +12574,11 @@ static struct snd_pci_quirk alc268_cfg_tbl[] = {
 						ALC268_ACER_ASPIRE_ONE),
 						ALC268_ACER_ASPIRE_ONE),
 	SND_PCI_QUIRK(0x1028, 0x0253, "Dell OEM", ALC268_DELL),
 	SND_PCI_QUIRK(0x1028, 0x0253, "Dell OEM", ALC268_DELL),
 	SND_PCI_QUIRK(0x1028, 0x02b0, "Dell Inspiron Mini9", ALC268_DELL),
 	SND_PCI_QUIRK(0x1028, 0x02b0, "Dell Inspiron Mini9", ALC268_DELL),
+	/* almost compatible with toshiba but with optional digital outs;
+	 * auto-probing seems working fine
+	 */
 	SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP TX25xx series",
 	SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP TX25xx series",
-			   ALC268_TOSHIBA),
+			   ALC268_AUTO),
 	SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST),
 	SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST),
 	SND_PCI_QUIRK(0x1170, 0x0040, "ZEPTO", ALC268_ZEPTO),
 	SND_PCI_QUIRK(0x1170, 0x0040, "ZEPTO", ALC268_ZEPTO),
 	SND_PCI_QUIRK(0x14c0, 0x0025, "COMPAL IFL90/JFL-92", ALC268_TOSHIBA),
 	SND_PCI_QUIRK(0x14c0, 0x0025, "COMPAL IFL90/JFL-92", ALC268_TOSHIBA),
@@ -12545,7 +12599,8 @@ static struct snd_pci_quirk alc268_ssid_cfg_tbl[] = {
 
 
 static struct alc_config_preset alc268_presets[] = {
 static struct alc_config_preset alc268_presets[] = {
 	[ALC267_QUANTA_IL1] = {
 	[ALC267_QUANTA_IL1] = {
-		.mixers = { alc267_quanta_il1_mixer, alc268_beep_mixer },
+		.mixers = { alc267_quanta_il1_mixer, alc268_beep_mixer,
+			    alc268_capture_nosrc_mixer },
 		.init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
 		.init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
 				alc267_quanta_il1_verbs },
 				alc267_quanta_il1_verbs },
 		.num_dacs = ARRAY_SIZE(alc268_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc268_dac_nids),
@@ -12555,9 +12610,9 @@ static struct alc_config_preset alc268_presets[] = {
 		.hp_nid = 0x03,
 		.hp_nid = 0x03,
 		.num_channel_mode = ARRAY_SIZE(alc268_modes),
 		.num_channel_mode = ARRAY_SIZE(alc268_modes),
 		.channel_mode = alc268_modes,
 		.channel_mode = alc268_modes,
-		.input_mux = &alc268_capture_source,
-		.unsol_event = alc267_quanta_il1_unsol_event,
-		.init_hook = alc267_quanta_il1_init_hook,
+		.unsol_event = alc_sku_unsol_event,
+		.setup = alc267_quanta_il1_setup,
+		.init_hook = alc_inithook,
 	},
 	},
 	[ALC268_3ST] = {
 	[ALC268_3ST] = {
 		.mixers = { alc268_base_mixer, alc268_capture_alt_mixer,
 		.mixers = { alc268_base_mixer, alc268_capture_alt_mixer,
@@ -12589,10 +12644,11 @@ static struct alc_config_preset alc268_presets[] = {
 		.channel_mode = alc268_modes,
 		.channel_mode = alc268_modes,
 		.input_mux = &alc268_capture_source,
 		.input_mux = &alc268_capture_source,
 		.unsol_event = alc268_toshiba_unsol_event,
 		.unsol_event = alc268_toshiba_unsol_event,
-		.init_hook = alc268_toshiba_init_hook,
+		.setup = alc268_toshiba_setup,
+		.init_hook = alc268_toshiba_automute,
 	},
 	},
 	[ALC268_ACER] = {
 	[ALC268_ACER] = {
-		.mixers = { alc268_acer_mixer, alc268_capture_alt_mixer,
+		.mixers = { alc268_acer_mixer, alc268_capture_nosrc_mixer,
 			    alc268_beep_mixer },
 			    alc268_beep_mixer },
 		.init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
 		.init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
 				alc268_acer_verbs },
 				alc268_acer_verbs },
@@ -12628,7 +12684,7 @@ static struct alc_config_preset alc268_presets[] = {
 	[ALC268_ACER_ASPIRE_ONE] = {
 	[ALC268_ACER_ASPIRE_ONE] = {
 		.mixers = { alc268_acer_aspire_one_mixer,
 		.mixers = { alc268_acer_aspire_one_mixer,
 			    alc268_beep_mixer,
 			    alc268_beep_mixer,
-			    alc268_capture_alt_mixer },
+			    alc268_capture_nosrc_mixer },
 		.init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
 		.init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
 				alc268_acer_aspire_one_verbs },
 				alc268_acer_aspire_one_verbs },
 		.num_dacs = ARRAY_SIZE(alc268_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc268_dac_nids),
@@ -12639,22 +12695,26 @@ static struct alc_config_preset alc268_presets[] = {
 		.hp_nid = 0x03,
 		.hp_nid = 0x03,
 		.num_channel_mode = ARRAY_SIZE(alc268_modes),
 		.num_channel_mode = ARRAY_SIZE(alc268_modes),
 		.channel_mode = alc268_modes,
 		.channel_mode = alc268_modes,
-		.input_mux = &alc268_acer_lc_capture_source,
 		.unsol_event = alc268_acer_lc_unsol_event,
 		.unsol_event = alc268_acer_lc_unsol_event,
+		.setup = alc268_acer_lc_setup,
 		.init_hook = alc268_acer_lc_init_hook,
 		.init_hook = alc268_acer_lc_init_hook,
 	},
 	},
 	[ALC268_DELL] = {
 	[ALC268_DELL] = {
-		.mixers = { alc268_dell_mixer, alc268_beep_mixer },
+		.mixers = { alc268_dell_mixer, alc268_beep_mixer,
+			    alc268_capture_nosrc_mixer },
 		.init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
 		.init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
 				alc268_dell_verbs },
 				alc268_dell_verbs },
 		.num_dacs = ARRAY_SIZE(alc268_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc268_dac_nids),
 		.dac_nids = alc268_dac_nids,
 		.dac_nids = alc268_dac_nids,
+		.num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
+		.adc_nids = alc268_adc_nids_alt,
+		.capsrc_nids = alc268_capsrc_nids,
 		.hp_nid = 0x02,
 		.hp_nid = 0x02,
 		.num_channel_mode = ARRAY_SIZE(alc268_modes),
 		.num_channel_mode = ARRAY_SIZE(alc268_modes),
 		.channel_mode = alc268_modes,
 		.channel_mode = alc268_modes,
 		.unsol_event = alc_sku_unsol_event,
 		.unsol_event = alc_sku_unsol_event,
-		.init_hook = alc268_dell_init_hook,
-		.input_mux = &alc268_capture_source,
+		.setup = alc268_dell_setup,
+		.init_hook = alc_inithook,
 	},
 	},
 	[ALC268_ZEPTO] = {
 	[ALC268_ZEPTO] = {
 		.mixers = { alc268_base_mixer, alc268_capture_alt_mixer,
 		.mixers = { alc268_base_mixer, alc268_capture_alt_mixer,
@@ -12671,8 +12731,8 @@ static struct alc_config_preset alc268_presets[] = {
 		.num_channel_mode = ARRAY_SIZE(alc268_modes),
 		.num_channel_mode = ARRAY_SIZE(alc268_modes),
 		.channel_mode = alc268_modes,
 		.channel_mode = alc268_modes,
 		.input_mux = &alc268_capture_source,
 		.input_mux = &alc268_capture_source,
-		.unsol_event = alc268_toshiba_unsol_event,
-		.init_hook = alc268_toshiba_init_hook
+		.setup = alc268_toshiba_setup,
+		.init_hook = alc268_toshiba_automute,
 	},
 	},
 #ifdef CONFIG_SND_DEBUG
 #ifdef CONFIG_SND_DEBUG
 	[ALC268_TEST] = {
 	[ALC268_TEST] = {
@@ -12714,8 +12774,8 @@ static int patch_alc268(struct hda_codec *codec)
 			ALC882_MODEL_LAST, alc268_models, alc268_ssid_cfg_tbl);
 			ALC882_MODEL_LAST, alc268_models, alc268_ssid_cfg_tbl);
 
 
 	if (board_config < 0 || board_config >= ALC268_MODEL_LAST) {
 	if (board_config < 0 || board_config >= ALC268_MODEL_LAST) {
-		printk(KERN_INFO "hda_codec: Unknown model for %s, "
-		       "trying auto-probe from BIOS...\n", codec->chip_name);
+		printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+		       codec->chip_name);
 		board_config = ALC268_AUTO;
 		board_config = ALC268_AUTO;
 	}
 	}
 
 
@@ -12734,7 +12794,7 @@ static int patch_alc268(struct hda_codec *codec)
 	}
 	}
 
 
 	if (board_config != ALC268_AUTO)
 	if (board_config != ALC268_AUTO)
-		setup_preset(spec, &alc268_presets[board_config]);
+		setup_preset(codec, &alc268_presets[board_config]);
 
 
 	spec->stream_analog_playback = &alc268_pcm_analog_playback;
 	spec->stream_analog_playback = &alc268_pcm_analog_playback;
 	spec->stream_analog_capture = &alc268_pcm_analog_capture;
 	spec->stream_analog_capture = &alc268_pcm_analog_capture;
@@ -12771,11 +12831,15 @@ static int patch_alc268(struct hda_codec *codec)
 		int i;
 		int i;
 
 
 		/* get type */
 		/* get type */
-		wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
-		if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) {
+		wcap = get_wcaps_type(wcap);
+		if (spec->auto_mic ||
+		    wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) {
 			spec->adc_nids = alc268_adc_nids_alt;
 			spec->adc_nids = alc268_adc_nids_alt;
 			spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt);
 			spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt);
-			add_mixer(spec, alc268_capture_alt_mixer);
+			if (spec->auto_mic || spec->input_mux->num_items == 1)
+				add_mixer(spec, alc268_capture_nosrc_mixer);
+			else
+				add_mixer(spec, alc268_capture_alt_mixer);
 		} else {
 		} else {
 			spec->adc_nids = alc268_adc_nids;
 			spec->adc_nids = alc268_adc_nids;
 			spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids);
 			spec->num_adc_nids = ARRAY_SIZE(alc268_adc_nids);
@@ -12786,6 +12850,8 @@ static int patch_alc268(struct hda_codec *codec)
 		for (i = 0; i < spec->num_adc_nids; i++)
 		for (i = 0; i < spec->num_adc_nids; i++)
 			snd_hda_codec_write_cache(codec, alc268_capsrc_nids[i],
 			snd_hda_codec_write_cache(codec, alc268_capsrc_nids[i],
 				0, AC_VERB_SET_CONNECT_SEL,
 				0, AC_VERB_SET_CONNECT_SEL,
+				i < spec->num_mux_defs ?
+				spec->input_mux[i].items[0].index :
 				spec->input_mux->items[0].index);
 				spec->input_mux->items[0].index);
 	}
 	}
 
 
@@ -12820,22 +12886,6 @@ static hda_nid_t alc269_capsrc_nids[1] = {
  *       not a mux!
  *       not a mux!
  */
  */
 
 
-static struct hda_input_mux alc269_eeepc_dmic_capture_source = {
-	.num_items = 2,
-	.items = {
-		{ "i-Mic", 0x5 },
-		{ "e-Mic", 0x0 },
-	},
-};
-
-static struct hda_input_mux alc269_eeepc_amic_capture_source = {
-	.num_items = 2,
-	.items = {
-		{ "i-Mic", 0x1 },
-		{ "e-Mic", 0x0 },
-	},
-};
-
 #define alc269_modes		alc260_modes
 #define alc269_modes		alc260_modes
 #define alc269_capture_source	alc880_lg_lw_capture_source
 #define alc269_capture_source	alc880_lg_lw_capture_source
 
 
@@ -12997,16 +13047,6 @@ static void alc269_lifebook_speaker_automute(struct hda_codec *codec)
 			AC_VERB_SET_PROC_COEF, 0x480);
 			AC_VERB_SET_PROC_COEF, 0x480);
 }
 }
 
 
-static void alc269_quanta_fl1_mic_automute(struct hda_codec *codec)
-{
-	unsigned int present;
-
-	present = snd_hda_codec_read(codec, 0x18, 0,
-				AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	snd_hda_codec_write(codec, 0x23, 0,
-			    AC_VERB_SET_CONNECT_SEL, present ? 0x0 : 0x1);
-}
-
 static void alc269_lifebook_mic_autoswitch(struct hda_codec *codec)
 static void alc269_lifebook_mic_autoswitch(struct hda_codec *codec)
 {
 {
 	unsigned int present_laptop;
 	unsigned int present_laptop;
@@ -13033,10 +13073,14 @@ static void alc269_lifebook_mic_autoswitch(struct hda_codec *codec)
 static void alc269_quanta_fl1_unsol_event(struct hda_codec *codec,
 static void alc269_quanta_fl1_unsol_event(struct hda_codec *codec,
 				    unsigned int res)
 				    unsigned int res)
 {
 {
-	if ((res >> 26) == ALC880_HP_EVENT)
+	switch (res >> 26) {
+	case ALC880_HP_EVENT:
 		alc269_quanta_fl1_speaker_automute(codec);
 		alc269_quanta_fl1_speaker_automute(codec);
-	if ((res >> 26) == ALC880_MIC_EVENT)
-		alc269_quanta_fl1_mic_automute(codec);
+		break;
+	case ALC880_MIC_EVENT:
+		alc_mic_automute(codec);
+		break;
+	}
 }
 }
 
 
 static void alc269_lifebook_unsol_event(struct hda_codec *codec,
 static void alc269_lifebook_unsol_event(struct hda_codec *codec,
@@ -13048,10 +13092,20 @@ static void alc269_lifebook_unsol_event(struct hda_codec *codec,
 		alc269_lifebook_mic_autoswitch(codec);
 		alc269_lifebook_mic_autoswitch(codec);
 }
 }
 
 
+static void alc269_quanta_fl1_setup(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	spec->ext_mic.pin = 0x18;
+	spec->ext_mic.mux_idx = 0;
+	spec->int_mic.pin = 0x19;
+	spec->int_mic.mux_idx = 1;
+	spec->auto_mic = 1;
+}
+
 static void alc269_quanta_fl1_init_hook(struct hda_codec *codec)
 static void alc269_quanta_fl1_init_hook(struct hda_codec *codec)
 {
 {
 	alc269_quanta_fl1_speaker_automute(codec);
 	alc269_quanta_fl1_speaker_automute(codec);
-	alc269_quanta_fl1_mic_automute(codec);
+	alc_mic_automute(codec);
 }
 }
 
 
 static void alc269_lifebook_init_hook(struct hda_codec *codec)
 static void alc269_lifebook_init_hook(struct hda_codec *codec)
@@ -13096,60 +13150,44 @@ static void alc269_speaker_automute(struct hda_codec *codec)
 				AMP_IN_MUTE(0), bits);
 				AMP_IN_MUTE(0), bits);
 }
 }
 
 
-static void alc269_eeepc_dmic_automute(struct hda_codec *codec)
-{
-	unsigned int present;
-
-	present = snd_hda_codec_read(codec, 0x18, 0,
-				AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	snd_hda_codec_write(codec, 0x23, 0,
-				AC_VERB_SET_CONNECT_SEL,  (present ? 0 : 5));
-}
-
-static void alc269_eeepc_amic_automute(struct hda_codec *codec)
-{
-	unsigned int present;
-
-	present = snd_hda_codec_read(codec, 0x18, 0,
-				AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	snd_hda_codec_write(codec, 0x24, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-				0x7000 | (0x00 << 8) | (present ? 0 : 0x80));
-	snd_hda_codec_write(codec, 0x24, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-				0x7000 | (0x01 << 8) | (present ? 0x80 : 0));
-}
-
 /* unsolicited event for HP jack sensing */
 /* unsolicited event for HP jack sensing */
-static void alc269_eeepc_dmic_unsol_event(struct hda_codec *codec,
+static void alc269_eeepc_unsol_event(struct hda_codec *codec,
 				     unsigned int res)
 				     unsigned int res)
 {
 {
-	if ((res >> 26) == ALC880_HP_EVENT)
+	switch (res >> 26) {
+	case ALC880_HP_EVENT:
 		alc269_speaker_automute(codec);
 		alc269_speaker_automute(codec);
-
-	if ((res >> 26) == ALC880_MIC_EVENT)
-		alc269_eeepc_dmic_automute(codec);
+		break;
+	case ALC880_MIC_EVENT:
+		alc_mic_automute(codec);
+		break;
+	}
 }
 }
 
 
-static void alc269_eeepc_dmic_inithook(struct hda_codec *codec)
+static void alc269_eeepc_dmic_setup(struct hda_codec *codec)
 {
 {
-	alc269_speaker_automute(codec);
-	alc269_eeepc_dmic_automute(codec);
+	struct alc_spec *spec = codec->spec;
+	spec->ext_mic.pin = 0x18;
+	spec->ext_mic.mux_idx = 0;
+	spec->int_mic.pin = 0x12;
+	spec->int_mic.mux_idx = 5;
+	spec->auto_mic = 1;
 }
 }
 
 
-/* unsolicited event for HP jack sensing */
-static void alc269_eeepc_amic_unsol_event(struct hda_codec *codec,
-				     unsigned int res)
+static void alc269_eeepc_amic_setup(struct hda_codec *codec)
 {
 {
-	if ((res >> 26) == ALC880_HP_EVENT)
-		alc269_speaker_automute(codec);
-
-	if ((res >> 26) == ALC880_MIC_EVENT)
-		alc269_eeepc_amic_automute(codec);
+	struct alc_spec *spec = codec->spec;
+	spec->ext_mic.pin = 0x18;
+	spec->ext_mic.mux_idx = 0;
+	spec->int_mic.pin = 0x19;
+	spec->int_mic.mux_idx = 1;
+	spec->auto_mic = 1;
 }
 }
 
 
-static void alc269_eeepc_amic_inithook(struct hda_codec *codec)
+static void alc269_eeepc_inithook(struct hda_codec *codec)
 {
 {
 	alc269_speaker_automute(codec);
 	alc269_speaker_automute(codec);
-	alc269_eeepc_amic_automute(codec);
+	alc_mic_automute(codec);
 }
 }
 
 
 /*
 /*
@@ -13178,133 +13216,54 @@ static struct hda_verb alc269_init_verbs[] = {
 	 */
 	 */
 	/* set vol=0 to output mixers */
 	/* set vol=0 to output mixers */
 	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
 	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
-	/* set up input amps for analog loopback */
-	/* Amp Indices: DAC = 0, mixer = 1 */
-	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
-	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
-	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-
-	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-
-	{0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
-	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
-
-	/* FIXME: use matrix-type input source selection */
-	/* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */
-	/* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
-	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-
-	/* set EAPD */
-	{0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
-	{0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
-	{ }
-};
-
-/* add playback controls from the parsed DAC table */
-static int alc269_auto_create_multi_out_ctls(struct alc_spec *spec,
-					     const struct auto_pin_cfg *cfg)
-{
-	hda_nid_t nid;
-	int err;
-
-	spec->multiout.num_dacs = 1;	/* only use one dac */
-	spec->multiout.dac_nids = spec->private_dac_nids;
-	spec->multiout.dac_nids[0] = 2;
-
-	nid = cfg->line_out_pins[0];
-	if (nid) {
-		err = add_control(spec, ALC_CTL_WIDGET_VOL,
-				  "Front Playback Volume",
-				  HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT));
-		if (err < 0)
-			return err;
-		err = add_control(spec, ALC_CTL_WIDGET_MUTE,
-				  "Front Playback Switch",
-				  HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
-		if (err < 0)
-			return err;
-	}
-
-	nid = cfg->speaker_pins[0];
-	if (nid) {
-		if (!cfg->line_out_pins[0]) {
-			err = add_control(spec, ALC_CTL_WIDGET_VOL,
-					  "Speaker Playback Volume",
-					  HDA_COMPOSE_AMP_VAL(0x02, 3, 0,
-							      HDA_OUTPUT));
-			if (err < 0)
-				return err;
-		}
-		if (nid == 0x16) {
-			err = add_control(spec, ALC_CTL_WIDGET_MUTE,
-					  "Speaker Playback Switch",
-					  HDA_COMPOSE_AMP_VAL(nid, 2, 0,
-							      HDA_OUTPUT));
-			if (err < 0)
-				return err;
-		} else {
-			err = add_control(spec, ALC_CTL_WIDGET_MUTE,
-					  "Speaker Playback Switch",
-					  HDA_COMPOSE_AMP_VAL(nid, 3, 0,
-							      HDA_OUTPUT));
-			if (err < 0)
-				return err;
-		}
-	}
-	nid = cfg->hp_pins[0];
-	if (nid) {
-		/* spec->multiout.hp_nid = 2; */
-		if (!cfg->line_out_pins[0] && !cfg->speaker_pins[0]) {
-			err = add_control(spec, ALC_CTL_WIDGET_VOL,
-					  "Headphone Playback Volume",
-					  HDA_COMPOSE_AMP_VAL(0x02, 3, 0,
-							      HDA_OUTPUT));
-			if (err < 0)
-				return err;
-		}
-		if (nid == 0x16) {
-			err = add_control(spec, ALC_CTL_WIDGET_MUTE,
-					  "Headphone Playback Switch",
-					  HDA_COMPOSE_AMP_VAL(nid, 2, 0,
-							      HDA_OUTPUT));
-			if (err < 0)
-				return err;
-		} else {
-			err = add_control(spec, ALC_CTL_WIDGET_MUTE,
-					  "Headphone Playback Switch",
-					  HDA_COMPOSE_AMP_VAL(nid, 3, 0,
-							      HDA_OUTPUT));
-			if (err < 0)
-				return err;
-		}
-	}
-	return 0;
-}
+	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+
+	/* set up input amps for analog loopback */
+	/* Amp Indices: DAC = 0, mixer = 1 */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+
+	{0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+	/* FIXME: use matrix-type input source selection */
+	/* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */
+	/* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+
+	/* set EAPD */
+	{0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
+	{0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
+	{ }
+};
 
 
-#define alc269_auto_create_analog_input_ctls \
-	alc262_auto_create_analog_input_ctls
+#define alc269_auto_create_multi_out_ctls \
+	alc268_auto_create_multi_out_ctls
+#define alc269_auto_create_input_ctls \
+	alc268_auto_create_input_ctls
 
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #define alc269_loopbacks	alc880_loopbacks
 #define alc269_loopbacks	alc880_loopbacks
@@ -13354,7 +13313,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
 	err = alc269_auto_create_multi_out_ctls(spec, &spec->autocfg);
 	err = alc269_auto_create_multi_out_ctls(spec, &spec->autocfg);
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
-	err = alc269_auto_create_analog_input_ctls(spec, &spec->autocfg);
+	err = alc269_auto_create_input_ctls(codec, &spec->autocfg);
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
 
 
@@ -13379,15 +13338,15 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
 		return err;
 		return err;
 
 
 	if (!spec->cap_mixer && !spec->no_analog)
 	if (!spec->cap_mixer && !spec->no_analog)
-		set_capture_mixer(spec);
+		set_capture_mixer(codec);
 
 
 	alc_ssid_check(codec, 0x15, 0x1b, 0x14);
 	alc_ssid_check(codec, 0x15, 0x1b, 0x14);
 
 
 	return 1;
 	return 1;
 }
 }
 
 
-#define alc269_auto_init_multi_out	alc882_auto_init_multi_out
-#define alc269_auto_init_hp_out		alc882_auto_init_hp_out
+#define alc269_auto_init_multi_out	alc268_auto_init_multi_out
+#define alc269_auto_init_hp_out		alc268_auto_init_hp_out
 #define alc269_auto_init_analog_input	alc882_auto_init_analog_input
 #define alc269_auto_init_analog_input	alc882_auto_init_analog_input
 
 
 
 
@@ -13455,6 +13414,7 @@ static struct alc_config_preset alc269_presets[] = {
 		.channel_mode = alc269_modes,
 		.channel_mode = alc269_modes,
 		.input_mux = &alc269_capture_source,
 		.input_mux = &alc269_capture_source,
 		.unsol_event = alc269_quanta_fl1_unsol_event,
 		.unsol_event = alc269_quanta_fl1_unsol_event,
+		.setup = alc269_quanta_fl1_setup,
 		.init_hook = alc269_quanta_fl1_init_hook,
 		.init_hook = alc269_quanta_fl1_init_hook,
 	},
 	},
 	[ALC269_ASUS_EEEPC_P703] = {
 	[ALC269_ASUS_EEEPC_P703] = {
@@ -13467,9 +13427,9 @@ static struct alc_config_preset alc269_presets[] = {
 		.hp_nid = 0x03,
 		.hp_nid = 0x03,
 		.num_channel_mode = ARRAY_SIZE(alc269_modes),
 		.num_channel_mode = ARRAY_SIZE(alc269_modes),
 		.channel_mode = alc269_modes,
 		.channel_mode = alc269_modes,
-		.input_mux = &alc269_eeepc_amic_capture_source,
-		.unsol_event = alc269_eeepc_amic_unsol_event,
-		.init_hook = alc269_eeepc_amic_inithook,
+		.unsol_event = alc269_eeepc_unsol_event,
+		.setup = alc269_eeepc_amic_setup,
+		.init_hook = alc269_eeepc_inithook,
 	},
 	},
 	[ALC269_ASUS_EEEPC_P901] = {
 	[ALC269_ASUS_EEEPC_P901] = {
 		.mixers = { alc269_eeepc_mixer },
 		.mixers = { alc269_eeepc_mixer },
@@ -13481,9 +13441,9 @@ static struct alc_config_preset alc269_presets[] = {
 		.hp_nid = 0x03,
 		.hp_nid = 0x03,
 		.num_channel_mode = ARRAY_SIZE(alc269_modes),
 		.num_channel_mode = ARRAY_SIZE(alc269_modes),
 		.channel_mode = alc269_modes,
 		.channel_mode = alc269_modes,
-		.input_mux = &alc269_eeepc_dmic_capture_source,
-		.unsol_event = alc269_eeepc_dmic_unsol_event,
-		.init_hook = alc269_eeepc_dmic_inithook,
+		.unsol_event = alc269_eeepc_unsol_event,
+		.setup = alc269_eeepc_dmic_setup,
+		.init_hook = alc269_eeepc_inithook,
 	},
 	},
 	[ALC269_FUJITSU] = {
 	[ALC269_FUJITSU] = {
 		.mixers = { alc269_fujitsu_mixer },
 		.mixers = { alc269_fujitsu_mixer },
@@ -13495,9 +13455,9 @@ static struct alc_config_preset alc269_presets[] = {
 		.hp_nid = 0x03,
 		.hp_nid = 0x03,
 		.num_channel_mode = ARRAY_SIZE(alc269_modes),
 		.num_channel_mode = ARRAY_SIZE(alc269_modes),
 		.channel_mode = alc269_modes,
 		.channel_mode = alc269_modes,
-		.input_mux = &alc269_eeepc_dmic_capture_source,
-		.unsol_event = alc269_eeepc_dmic_unsol_event,
-		.init_hook = alc269_eeepc_dmic_inithook,
+		.unsol_event = alc269_eeepc_unsol_event,
+		.setup = alc269_eeepc_dmic_setup,
+		.init_hook = alc269_eeepc_inithook,
 	},
 	},
 	[ALC269_LIFEBOOK] = {
 	[ALC269_LIFEBOOK] = {
 		.mixers = { alc269_lifebook_mixer },
 		.mixers = { alc269_lifebook_mixer },
@@ -13532,8 +13492,8 @@ static int patch_alc269(struct hda_codec *codec)
 						  alc269_cfg_tbl);
 						  alc269_cfg_tbl);
 
 
 	if (board_config < 0) {
 	if (board_config < 0) {
-		printk(KERN_INFO "hda_codec: Unknown model for %s, "
-		       "trying auto-probe from BIOS...\n", codec->chip_name);
+		printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+		       codec->chip_name);
 		board_config = ALC269_AUTO;
 		board_config = ALC269_AUTO;
 	}
 	}
 
 
@@ -13558,7 +13518,7 @@ static int patch_alc269(struct hda_codec *codec)
 	}
 	}
 
 
 	if (board_config != ALC269_AUTO)
 	if (board_config != ALC269_AUTO)
-		setup_preset(spec, &alc269_presets[board_config]);
+		setup_preset(codec, &alc269_presets[board_config]);
 
 
 	if (codec->subsystem_id == 0x17aa3bf8) {
 	if (codec->subsystem_id == 0x17aa3bf8) {
 		/* Due to a hardware problem on Lenovo Ideadpad, we need to
 		/* Due to a hardware problem on Lenovo Ideadpad, we need to
@@ -13577,7 +13537,7 @@ static int patch_alc269(struct hda_codec *codec)
 	spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids);
 	spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids);
 	spec->capsrc_nids = alc269_capsrc_nids;
 	spec->capsrc_nids = alc269_capsrc_nids;
 	if (!spec->cap_mixer)
 	if (!spec->cap_mixer)
-		set_capture_mixer(spec);
+		set_capture_mixer(codec);
 	set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT);
 	set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT);
 
 
 	spec->vmaster_nid = 0x02;
 	spec->vmaster_nid = 0x02;
@@ -14127,23 +14087,23 @@ static struct hda_verb alc861_auto_init_verbs[] = {
 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c},
 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c},
 
 
-	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
 
 
 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
 
 
 	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},	/* set Mic 1 */
 	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},	/* set Mic 1 */
 
 
@@ -14215,64 +14175,96 @@ static struct hda_input_mux alc861_capture_source = {
 	},
 	},
 };
 };
 
 
+static hda_nid_t alc861_look_for_dac(struct hda_codec *codec, hda_nid_t pin)
+{
+	struct alc_spec *spec = codec->spec;
+	hda_nid_t mix, srcs[5];
+	int i, j, num;
+
+	if (snd_hda_get_connections(codec, pin, &mix, 1) != 1)
+		return 0;
+	num = snd_hda_get_connections(codec, mix, srcs, ARRAY_SIZE(srcs));
+	if (num < 0)
+		return 0;
+	for (i = 0; i < num; i++) {
+		unsigned int type;
+		type = get_wcaps_type(get_wcaps(codec, srcs[i]));
+		if (type != AC_WID_AUD_OUT)
+			continue;
+		for (j = 0; j < spec->multiout.num_dacs; j++)
+			if (spec->multiout.dac_nids[j] == srcs[i])
+				break;
+		if (j >= spec->multiout.num_dacs)
+			return srcs[i];
+	}
+	return 0;
+}
+
 /* fill in the dac_nids table from the parsed pin configuration */
 /* fill in the dac_nids table from the parsed pin configuration */
-static int alc861_auto_fill_dac_nids(struct alc_spec *spec,
+static int alc861_auto_fill_dac_nids(struct hda_codec *codec,
 				     const struct auto_pin_cfg *cfg)
 				     const struct auto_pin_cfg *cfg)
 {
 {
+	struct alc_spec *spec = codec->spec;
 	int i;
 	int i;
-	hda_nid_t nid;
+	hda_nid_t nid, dac;
 
 
 	spec->multiout.dac_nids = spec->private_dac_nids;
 	spec->multiout.dac_nids = spec->private_dac_nids;
 	for (i = 0; i < cfg->line_outs; i++) {
 	for (i = 0; i < cfg->line_outs; i++) {
 		nid = cfg->line_out_pins[i];
 		nid = cfg->line_out_pins[i];
-		if (nid) {
-			if (i >= ARRAY_SIZE(alc861_dac_nids))
-				continue;
-			spec->multiout.dac_nids[i] = alc861_dac_nids[i];
-		}
+		dac = alc861_look_for_dac(codec, nid);
+		if (!dac)
+			continue;
+		spec->multiout.dac_nids[spec->multiout.num_dacs++] = dac;
 	}
 	}
-	spec->multiout.num_dacs = cfg->line_outs;
 	return 0;
 	return 0;
 }
 }
 
 
+static int alc861_create_out_sw(struct hda_codec *codec, const char *pfx,
+				hda_nid_t nid, unsigned int chs)
+{
+	char name[32];
+	snprintf(name, sizeof(name), "%s Playback Switch", pfx);
+	return add_control(codec->spec, ALC_CTL_WIDGET_MUTE, name,
+			   HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
+}
+
 /* add playback controls from the parsed DAC table */
 /* add playback controls from the parsed DAC table */
-static int alc861_auto_create_multi_out_ctls(struct alc_spec *spec,
+static int alc861_auto_create_multi_out_ctls(struct hda_codec *codec,
 					     const struct auto_pin_cfg *cfg)
 					     const struct auto_pin_cfg *cfg)
 {
 {
-	char name[32];
+	struct alc_spec *spec = codec->spec;
 	static const char *chname[4] = {
 	static const char *chname[4] = {
 		"Front", "Surround", NULL /*CLFE*/, "Side"
 		"Front", "Surround", NULL /*CLFE*/, "Side"
 	};
 	};
 	hda_nid_t nid;
 	hda_nid_t nid;
-	int i, idx, err;
+	int i, err;
+
+	if (cfg->line_outs == 1) {
+		const char *pfx = NULL;
+		if (!cfg->hp_outs)
+			pfx = "Master";
+		else if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+			pfx = "Speaker";
+		if (pfx) {
+			nid = spec->multiout.dac_nids[0];
+			return alc861_create_out_sw(codec, pfx, nid, 3);
+		}
+	}
 
 
 	for (i = 0; i < cfg->line_outs; i++) {
 	for (i = 0; i < cfg->line_outs; i++) {
 		nid = spec->multiout.dac_nids[i];
 		nid = spec->multiout.dac_nids[i];
 		if (!nid)
 		if (!nid)
 			continue;
 			continue;
-		if (nid == 0x05) {
+		if (i == 2) {
 			/* Center/LFE */
 			/* Center/LFE */
-			err = add_control(spec, ALC_CTL_BIND_MUTE,
-					  "Center Playback Switch",
-					  HDA_COMPOSE_AMP_VAL(nid, 1, 0,
-							      HDA_OUTPUT));
+			err = alc861_create_out_sw(codec, "Center", nid, 1);
 			if (err < 0)
 			if (err < 0)
 				return err;
 				return err;
-			err = add_control(spec, ALC_CTL_BIND_MUTE,
-					  "LFE Playback Switch",
-					  HDA_COMPOSE_AMP_VAL(nid, 2, 0,
-							      HDA_OUTPUT));
+			err = alc861_create_out_sw(codec, "LFE", nid, 2);
 			if (err < 0)
 			if (err < 0)
 				return err;
 				return err;
 		} else {
 		} else {
-			for (idx = 0; idx < ARRAY_SIZE(alc861_dac_nids) - 1;
-			     idx++)
-				if (nid == alc861_dac_nids[idx])
-					break;
-			sprintf(name, "%s Playback Switch", chname[idx]);
-			err = add_control(spec, ALC_CTL_BIND_MUTE, name,
-					  HDA_COMPOSE_AMP_VAL(nid, 3, 0,
-							      HDA_OUTPUT));
+			err = alc861_create_out_sw(codec, chname[i], nid, 3);
 			if (err < 0)
 			if (err < 0)
 				return err;
 				return err;
 		}
 		}
@@ -14280,8 +14272,9 @@ static int alc861_auto_create_multi_out_ctls(struct alc_spec *spec,
 	return 0;
 	return 0;
 }
 }
 
 
-static int alc861_auto_create_hp_ctls(struct alc_spec *spec, hda_nid_t pin)
+static int alc861_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
 {
 {
+	struct alc_spec *spec = codec->spec;
 	int err;
 	int err;
 	hda_nid_t nid;
 	hda_nid_t nid;
 
 
@@ -14289,70 +14282,49 @@ static int alc861_auto_create_hp_ctls(struct alc_spec *spec, hda_nid_t pin)
 		return 0;
 		return 0;
 
 
 	if ((pin >= 0x0b && pin <= 0x10) || pin == 0x1f || pin == 0x20) {
 	if ((pin >= 0x0b && pin <= 0x10) || pin == 0x1f || pin == 0x20) {
-		nid = 0x03;
-		err = add_control(spec, ALC_CTL_WIDGET_MUTE,
-				  "Headphone Playback Switch",
-				  HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
-		if (err < 0)
-			return err;
-		spec->multiout.hp_nid = nid;
+		nid = alc861_look_for_dac(codec, pin);
+		if (nid) {
+			err = alc861_create_out_sw(codec, "Headphone", nid, 3);
+			if (err < 0)
+				return err;
+			spec->multiout.hp_nid = nid;
+		}
 	}
 	}
 	return 0;
 	return 0;
 }
 }
 
 
 /* create playback/capture controls for input pins */
 /* create playback/capture controls for input pins */
-static int alc861_auto_create_analog_input_ctls(struct alc_spec *spec,
+static int alc861_auto_create_input_ctls(struct hda_codec *codec,
 						const struct auto_pin_cfg *cfg)
 						const struct auto_pin_cfg *cfg)
 {
 {
-	struct hda_input_mux *imux = &spec->private_imux[0];
-	int i, err, idx, idx1;
-
-	for (i = 0; i < AUTO_PIN_LAST; i++) {
-		switch (cfg->input_pins[i]) {
-		case 0x0c:
-			idx1 = 1;
-			idx = 2;	/* Line In */
-			break;
-		case 0x0f:
-			idx1 = 2;
-			idx = 2;	/* Line In */
-			break;
-		case 0x0d:
-			idx1 = 0;
-			idx = 1;	/* Mic In */
-			break;
-		case 0x10:
-			idx1 = 3;
-			idx = 1;	/* Mic In */
-			break;
-		case 0x11:
-			idx1 = 4;
-			idx = 0;	/* CD */
-			break;
-		default:
-			continue;
-		}
-
-		err = new_analog_input(spec, cfg->input_pins[i],
-				       auto_pin_cfg_labels[i], idx, 0x15);
-		if (err < 0)
-			return err;
-
-		imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
-		imux->items[imux->num_items].index = idx1;
-		imux->num_items++;
-	}
-	return 0;
+	return alc_auto_create_input_ctls(codec, cfg, 0x15, 0x08, 0);
 }
 }
 
 
 static void alc861_auto_set_output_and_unmute(struct hda_codec *codec,
 static void alc861_auto_set_output_and_unmute(struct hda_codec *codec,
 					      hda_nid_t nid,
 					      hda_nid_t nid,
-					      int pin_type, int dac_idx)
+					      int pin_type, hda_nid_t dac)
 {
 {
+	hda_nid_t mix, srcs[5];
+	int i, num;
+
 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
 			    pin_type);
 			    pin_type);
-	snd_hda_codec_write(codec, dac_idx, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+	snd_hda_codec_write(codec, dac, 0, AC_VERB_SET_AMP_GAIN_MUTE,
 			    AMP_OUT_UNMUTE);
 			    AMP_OUT_UNMUTE);
+	if (snd_hda_get_connections(codec, nid, &mix, 1) != 1)
+		return;
+	num = snd_hda_get_connections(codec, mix, srcs, ARRAY_SIZE(srcs));
+	if (num < 0)
+		return;
+	for (i = 0; i < num; i++) {
+		unsigned int mute;
+		if (srcs[i] == dac || srcs[i] == 0x15)
+			mute = AMP_IN_UNMUTE(i);
+		else
+			mute = AMP_IN_MUTE(i);
+		snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+				    mute);
+	}
 }
 }
 
 
 static void alc861_auto_init_multi_out(struct hda_codec *codec)
 static void alc861_auto_init_multi_out(struct hda_codec *codec)
@@ -14375,12 +14347,13 @@ static void alc861_auto_init_hp_out(struct hda_codec *codec)
 	hda_nid_t pin;
 	hda_nid_t pin;
 
 
 	pin = spec->autocfg.hp_pins[0];
 	pin = spec->autocfg.hp_pins[0];
-	if (pin) /* connect to front */
+	if (pin)
 		alc861_auto_set_output_and_unmute(codec, pin, PIN_HP,
 		alc861_auto_set_output_and_unmute(codec, pin, PIN_HP,
-						  spec->multiout.dac_nids[0]);
+						  spec->multiout.hp_nid);
 	pin = spec->autocfg.speaker_pins[0];
 	pin = spec->autocfg.speaker_pins[0];
 	if (pin)
 	if (pin)
-		alc861_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
+		alc861_auto_set_output_and_unmute(codec, pin, PIN_OUT,
+						  spec->multiout.dac_nids[0]);
 }
 }
 
 
 static void alc861_auto_init_analog_input(struct hda_codec *codec)
 static void alc861_auto_init_analog_input(struct hda_codec *codec)
@@ -14412,16 +14385,16 @@ static int alc861_parse_auto_config(struct hda_codec *codec)
 	if (!spec->autocfg.line_outs)
 	if (!spec->autocfg.line_outs)
 		return 0; /* can't find valid BIOS pin config */
 		return 0; /* can't find valid BIOS pin config */
 
 
-	err = alc861_auto_fill_dac_nids(spec, &spec->autocfg);
+	err = alc861_auto_fill_dac_nids(codec, &spec->autocfg);
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
-	err = alc861_auto_create_multi_out_ctls(spec, &spec->autocfg);
+	err = alc861_auto_create_multi_out_ctls(codec, &spec->autocfg);
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
-	err = alc861_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
+	err = alc861_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
-	err = alc861_auto_create_analog_input_ctls(spec, &spec->autocfg);
+	err = alc861_auto_create_input_ctls(codec, &spec->autocfg);
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
 
 
@@ -14440,7 +14413,7 @@ static int alc861_parse_auto_config(struct hda_codec *codec)
 
 
 	spec->adc_nids = alc861_adc_nids;
 	spec->adc_nids = alc861_adc_nids;
 	spec->num_adc_nids = ARRAY_SIZE(alc861_adc_nids);
 	spec->num_adc_nids = ARRAY_SIZE(alc861_adc_nids);
-	set_capture_mixer(spec);
+	set_capture_mixer(codec);
 
 
 	alc_ssid_check(codec, 0x0e, 0x0f, 0x0b);
 	alc_ssid_check(codec, 0x0e, 0x0f, 0x0b);
 
 
@@ -14633,8 +14606,8 @@ static int patch_alc861(struct hda_codec *codec)
 						  alc861_cfg_tbl);
 						  alc861_cfg_tbl);
 
 
 	if (board_config < 0) {
 	if (board_config < 0) {
-		printk(KERN_INFO "hda_codec: Unknown model for %s, "
-		       "trying auto-probe from BIOS...\n", codec->chip_name);
+		printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+		       codec->chip_name);
 		board_config = ALC861_AUTO;
 		board_config = ALC861_AUTO;
 	}
 	}
 
 
@@ -14659,7 +14632,7 @@ static int patch_alc861(struct hda_codec *codec)
 	}
 	}
 
 
 	if (board_config != ALC861_AUTO)
 	if (board_config != ALC861_AUTO)
-		setup_preset(spec, &alc861_presets[board_config]);
+		setup_preset(codec, &alc861_presets[board_config]);
 
 
 	spec->stream_analog_playback = &alc861_pcm_analog_playback;
 	spec->stream_analog_playback = &alc861_pcm_analog_playback;
 	spec->stream_analog_capture = &alc861_pcm_analog_capture;
 	spec->stream_analog_capture = &alc861_pcm_analog_capture;
@@ -15062,12 +15035,15 @@ static void alc861vd_lenovo_mic_automute(struct hda_codec *codec)
 				 HDA_AMP_MUTE, bits);
 				 HDA_AMP_MUTE, bits);
 }
 }
 
 
-static void alc861vd_lenovo_init_hook(struct hda_codec *codec)
+static void alc861vd_lenovo_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
-
 	spec->autocfg.hp_pins[0] = 0x1b;
 	spec->autocfg.hp_pins[0] = 0x1b;
 	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[0] = 0x14;
+}
+
+static void alc861vd_lenovo_init_hook(struct hda_codec *codec)
+{
 	alc_automute_amp(codec);
 	alc_automute_amp(codec);
 	alc861vd_lenovo_mic_automute(codec);
 	alc861vd_lenovo_mic_automute(codec);
 }
 }
@@ -15131,13 +15107,12 @@ static struct hda_verb alc861vd_dallas_verbs[] = {
 };
 };
 
 
 /* toggle speaker-output according to the hp-jack state */
 /* toggle speaker-output according to the hp-jack state */
-static void alc861vd_dallas_init_hook(struct hda_codec *codec)
+static void alc861vd_dallas_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
 	spec->autocfg.hp_pins[0] = 0x15;
 	spec->autocfg.hp_pins[0] = 0x15;
 	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[0] = 0x14;
-	alc_automute_amp(codec);
 }
 }
 
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -15251,6 +15226,7 @@ static struct alc_config_preset alc861vd_presets[] = {
 		.channel_mode = alc861vd_3stack_2ch_modes,
 		.channel_mode = alc861vd_3stack_2ch_modes,
 		.input_mux = &alc861vd_capture_source,
 		.input_mux = &alc861vd_capture_source,
 		.unsol_event = alc861vd_lenovo_unsol_event,
 		.unsol_event = alc861vd_lenovo_unsol_event,
+		.setup = alc861vd_lenovo_setup,
 		.init_hook = alc861vd_lenovo_init_hook,
 		.init_hook = alc861vd_lenovo_init_hook,
 	},
 	},
 	[ALC861VD_DALLAS] = {
 	[ALC861VD_DALLAS] = {
@@ -15262,7 +15238,8 @@ static struct alc_config_preset alc861vd_presets[] = {
 		.channel_mode = alc861vd_3stack_2ch_modes,
 		.channel_mode = alc861vd_3stack_2ch_modes,
 		.input_mux = &alc861vd_dallas_capture_source,
 		.input_mux = &alc861vd_dallas_capture_source,
 		.unsol_event = alc_automute_amp_unsol_event,
 		.unsol_event = alc_automute_amp_unsol_event,
-		.init_hook = alc861vd_dallas_init_hook,
+		.setup = alc861vd_dallas_setup,
+		.init_hook = alc_automute_amp,
 	},
 	},
 	[ALC861VD_HP] = {
 	[ALC861VD_HP] = {
 		.mixers = { alc861vd_hp_mixer },
 		.mixers = { alc861vd_hp_mixer },
@@ -15274,7 +15251,8 @@ static struct alc_config_preset alc861vd_presets[] = {
 		.channel_mode = alc861vd_3stack_2ch_modes,
 		.channel_mode = alc861vd_3stack_2ch_modes,
 		.input_mux = &alc861vd_hp_capture_source,
 		.input_mux = &alc861vd_hp_capture_source,
 		.unsol_event = alc_automute_amp_unsol_event,
 		.unsol_event = alc_automute_amp_unsol_event,
-		.init_hook = alc861vd_dallas_init_hook,
+		.setup = alc861vd_dallas_setup,
+		.init_hook = alc_automute_amp,
 	},
 	},
 	[ALC660VD_ASUS_V1S] = {
 	[ALC660VD_ASUS_V1S] = {
 		.mixers = { alc861vd_lenovo_mixer },
 		.mixers = { alc861vd_lenovo_mixer },
@@ -15289,6 +15267,7 @@ static struct alc_config_preset alc861vd_presets[] = {
 		.channel_mode = alc861vd_3stack_2ch_modes,
 		.channel_mode = alc861vd_3stack_2ch_modes,
 		.input_mux = &alc861vd_capture_source,
 		.input_mux = &alc861vd_capture_source,
 		.unsol_event = alc861vd_lenovo_unsol_event,
 		.unsol_event = alc861vd_lenovo_unsol_event,
+		.setup = alc861vd_lenovo_setup,
 		.init_hook = alc861vd_lenovo_init_hook,
 		.init_hook = alc861vd_lenovo_init_hook,
 	},
 	},
 };
 };
@@ -15296,6 +15275,13 @@ static struct alc_config_preset alc861vd_presets[] = {
 /*
 /*
  * BIOS auto configuration
  * BIOS auto configuration
  */
  */
+static int alc861vd_auto_create_input_ctls(struct hda_codec *codec,
+						const struct auto_pin_cfg *cfg)
+{
+	return alc_auto_create_input_ctls(codec, cfg, 0x15, 0x09, 0);
+}
+
+
 static void alc861vd_auto_set_output_and_unmute(struct hda_codec *codec,
 static void alc861vd_auto_set_output_and_unmute(struct hda_codec *codec,
 				hda_nid_t nid, int pin_type, int dac_idx)
 				hda_nid_t nid, int pin_type, int dac_idx)
 {
 {
@@ -15330,7 +15316,6 @@ static void alc861vd_auto_init_hp_out(struct hda_codec *codec)
 		alc861vd_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
 		alc861vd_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0);
 }
 }
 
 
-#define alc861vd_is_input_pin(nid)	alc880_is_input_pin(nid)
 #define ALC861VD_PIN_CD_NID		ALC880_PIN_CD_NID
 #define ALC861VD_PIN_CD_NID		ALC880_PIN_CD_NID
 
 
 static void alc861vd_auto_init_analog_input(struct hda_codec *codec)
 static void alc861vd_auto_init_analog_input(struct hda_codec *codec)
@@ -15340,7 +15325,7 @@ static void alc861vd_auto_init_analog_input(struct hda_codec *codec)
 
 
 	for (i = 0; i < AUTO_PIN_LAST; i++) {
 	for (i = 0; i < AUTO_PIN_LAST; i++) {
 		hda_nid_t nid = spec->autocfg.input_pins[i];
 		hda_nid_t nid = spec->autocfg.input_pins[i];
-		if (alc861vd_is_input_pin(nid)) {
+		if (alc_is_input_pin(codec, nid)) {
 			alc_set_input_pin(codec, nid, i);
 			alc_set_input_pin(codec, nid, i);
 			if (nid != ALC861VD_PIN_CD_NID &&
 			if (nid != ALC861VD_PIN_CD_NID &&
 			    (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
 			    (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
@@ -15404,13 +15389,25 @@ static int alc861vd_auto_create_multi_out_ctls(struct alc_spec *spec,
 			if (err < 0)
 			if (err < 0)
 				return err;
 				return err;
 		} else {
 		} else {
-			sprintf(name, "%s Playback Volume", chname[i]);
+			const char *pfx;
+			if (cfg->line_outs == 1 &&
+			    cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
+				if (!cfg->hp_pins)
+					pfx = "Speaker";
+				else
+					pfx = "PCM";
+			} else
+				pfx = chname[i];
+			sprintf(name, "%s Playback Volume", pfx);
 			err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
 			err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
 					  HDA_COMPOSE_AMP_VAL(nid_v, 3, 0,
 					  HDA_COMPOSE_AMP_VAL(nid_v, 3, 0,
 							      HDA_OUTPUT));
 							      HDA_OUTPUT));
 			if (err < 0)
 			if (err < 0)
 				return err;
 				return err;
-			sprintf(name, "%s Playback Switch", chname[i]);
+			if (cfg->line_outs == 1 &&
+			    cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+				pfx = "Speaker";
+			sprintf(name, "%s Playback Switch", pfx);
 			err = add_control(spec, ALC_CTL_BIND_MUTE, name,
 			err = add_control(spec, ALC_CTL_BIND_MUTE, name,
 					  HDA_COMPOSE_AMP_VAL(nid_s, 3, 2,
 					  HDA_COMPOSE_AMP_VAL(nid_s, 3, 2,
 							      HDA_INPUT));
 							      HDA_INPUT));
@@ -15503,7 +15500,7 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec)
 					     "Headphone");
 					     "Headphone");
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
-	err = alc880_auto_create_analog_input_ctls(spec, &spec->autocfg);
+	err = alc861vd_auto_create_input_ctls(codec, &spec->autocfg);
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
 
 
@@ -15557,8 +15554,8 @@ static int patch_alc861vd(struct hda_codec *codec)
 						  alc861vd_cfg_tbl);
 						  alc861vd_cfg_tbl);
 
 
 	if (board_config < 0 || board_config >= ALC861VD_MODEL_LAST) {
 	if (board_config < 0 || board_config >= ALC861VD_MODEL_LAST) {
-		printk(KERN_INFO "hda_codec: Unknown model for %s, "
-		       "trying auto-probe from BIOS...\n", codec->chip_name);
+		printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+		       codec->chip_name);
 		board_config = ALC861VD_AUTO;
 		board_config = ALC861VD_AUTO;
 	}
 	}
 
 
@@ -15583,7 +15580,7 @@ static int patch_alc861vd(struct hda_codec *codec)
 	}
 	}
 
 
 	if (board_config != ALC861VD_AUTO)
 	if (board_config != ALC861VD_AUTO)
-		setup_preset(spec, &alc861vd_presets[board_config]);
+		setup_preset(codec, &alc861vd_presets[board_config]);
 
 
 	if (codec->vendor_id == 0x10ec0660) {
 	if (codec->vendor_id == 0x10ec0660) {
 		/* always turn on EAPD */
 		/* always turn on EAPD */
@@ -15603,7 +15600,7 @@ static int patch_alc861vd(struct hda_codec *codec)
 	if (!spec->capsrc_nids)
 	if (!spec->capsrc_nids)
 		spec->capsrc_nids = alc861vd_capsrc_nids;
 		spec->capsrc_nids = alc861vd_capsrc_nids;
 
 
-	set_capture_mixer(spec);
+	set_capture_mixer(codec);
 	set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
 	set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
 
 
 	spec->vmaster_nid = 0x02;
 	spec->vmaster_nid = 0x02;
@@ -15644,9 +15641,9 @@ static hda_nid_t alc272_dac_nids[2] = {
 	0x02, 0x03
 	0x02, 0x03
 };
 };
 
 
-static hda_nid_t alc662_adc_nids[1] = {
+static hda_nid_t alc662_adc_nids[2] = {
 	/* ADC1-2 */
 	/* ADC1-2 */
-	0x09,
+	0x09, 0x08
 };
 };
 
 
 static hda_nid_t alc272_adc_nids[1] = {
 static hda_nid_t alc272_adc_nids[1] = {
@@ -15654,7 +15651,7 @@ static hda_nid_t alc272_adc_nids[1] = {
 	0x08,
 	0x08,
 };
 };
 
 
-static hda_nid_t alc662_capsrc_nids[1] = { 0x22 };
+static hda_nid_t alc662_capsrc_nids[2] = { 0x22, 0x23 };
 static hda_nid_t alc272_capsrc_nids[1] = { 0x23 };
 static hda_nid_t alc272_capsrc_nids[1] = { 0x23 };
 
 
 
 
@@ -15678,14 +15675,6 @@ static struct hda_input_mux alc662_lenovo_101e_capture_source = {
 	},
 	},
 };
 };
 
 
-static struct hda_input_mux alc662_eeepc_capture_source = {
-	.num_items = 2,
-	.items = {
-		{ "i-Mic", 0x1 },
-		{ "e-Mic", 0x0 },
-	},
-};
-
 static struct hda_input_mux alc663_capture_source = {
 static struct hda_input_mux alc663_capture_source = {
 	.num_items = 3,
 	.num_items = 3,
 	.items = {
 	.items = {
@@ -15695,23 +15684,7 @@ static struct hda_input_mux alc663_capture_source = {
 	},
 	},
 };
 };
 
 
-static struct hda_input_mux alc663_m51va_capture_source = {
-	.num_items = 2,
-	.items = {
-		{ "Ext-Mic", 0x0 },
-		{ "D-Mic", 0x9 },
-	},
-};
-
-#if 1 /* set to 0 for testing other input sources below */
-static struct hda_input_mux alc272_nc10_capture_source = {
-	.num_items = 2,
-	.items = {
-		{ "Autoselect Mic", 0x0 },
-		{ "Internal Mic", 0x1 },
-	},
-};
-#else
+#if 0 /* set to 1 for testing other input sources below */
 static struct hda_input_mux alc272_nc10_capture_source = {
 static struct hda_input_mux alc272_nc10_capture_source = {
 	.num_items = 16,
 	.num_items = 16,
 	.items = {
 	.items = {
@@ -16380,47 +16353,44 @@ static void alc662_lenovo_101e_unsol_event(struct hda_codec *codec,
 		alc662_lenovo_101e_ispeaker_automute(codec);
 		alc662_lenovo_101e_ispeaker_automute(codec);
 }
 }
 
 
-static void alc662_eeepc_mic_automute(struct hda_codec *codec)
-{
-	unsigned int present;
-
-	present = snd_hda_codec_read(codec, 0x18, 0,
-				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-			    0x7000 | (0x00 << 8) | (present ? 0 : 0x80));
-	snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-			    0x7000 | (0x00 << 8) | (present ? 0 : 0x80));
-	snd_hda_codec_write(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-			    0x7000 | (0x01 << 8) | (present ? 0x80 : 0));
-	snd_hda_codec_write(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-			    0x7000 | (0x01 << 8) | (present ? 0x80 : 0));
-}
-
 /* unsolicited event for HP jack sensing */
 /* unsolicited event for HP jack sensing */
 static void alc662_eeepc_unsol_event(struct hda_codec *codec,
 static void alc662_eeepc_unsol_event(struct hda_codec *codec,
 				     unsigned int res)
 				     unsigned int res)
 {
 {
 	if ((res >> 26) == ALC880_MIC_EVENT)
 	if ((res >> 26) == ALC880_MIC_EVENT)
-		alc662_eeepc_mic_automute(codec);
+		alc_mic_automute(codec);
 	else
 	else
 		alc262_hippo_unsol_event(codec, res);
 		alc262_hippo_unsol_event(codec, res);
 }
 }
 
 
+static void alc662_eeepc_setup(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+
+	alc262_hippo1_setup(codec);
+	spec->ext_mic.pin = 0x18;
+	spec->ext_mic.mux_idx = 0;
+	spec->int_mic.pin = 0x19;
+	spec->int_mic.mux_idx = 1;
+	spec->auto_mic = 1;
+}
+
 static void alc662_eeepc_inithook(struct hda_codec *codec)
 static void alc662_eeepc_inithook(struct hda_codec *codec)
 {
 {
-	alc262_hippo1_init_hook(codec);
-	alc662_eeepc_mic_automute(codec);
+	alc262_hippo_automute(codec);
+	alc_mic_automute(codec);
 }
 }
 
 
-static void alc662_eeepc_ep20_inithook(struct hda_codec *codec)
+static void alc662_eeepc_ep20_setup(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 
 
 	spec->autocfg.hp_pins[0] = 0x14;
 	spec->autocfg.hp_pins[0] = 0x14;
 	spec->autocfg.speaker_pins[0] = 0x1b;
 	spec->autocfg.speaker_pins[0] = 0x1b;
-	alc262_hippo_master_update(codec);
 }
 }
 
 
+#define alc662_eeepc_ep20_inithook	alc262_hippo_master_update
+
 static void alc663_m51va_speaker_automute(struct hda_codec *codec)
 static void alc663_m51va_speaker_automute(struct hda_codec *codec)
 {
 {
 	unsigned int present;
 	unsigned int present;
@@ -16531,23 +16501,6 @@ static void alc663_two_hp_m2_speaker_automute(struct hda_codec *codec)
 	}
 	}
 }
 }
 
 
-static void alc663_m51va_mic_automute(struct hda_codec *codec)
-{
-	unsigned int present;
-
-	present = snd_hda_codec_read(codec, 0x18, 0,
-			AC_VERB_GET_PIN_SENSE, 0)
-			& AC_PINSENSE_PRESENCE;
-	snd_hda_codec_write_cache(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-			0x7000 | (0x00 << 8) | (present ? 0 : 0x80));
-	snd_hda_codec_write_cache(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-			0x7000 | (0x00 << 8) | (present ? 0 : 0x80));
-	snd_hda_codec_write_cache(codec, 0x22, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-			0x7000 | (0x09 << 8) | (present ? 0x80 : 0));
-	snd_hda_codec_write_cache(codec, 0x23, 0, AC_VERB_SET_AMP_GAIN_MUTE,
-			0x7000 | (0x09 << 8) | (present ? 0x80 : 0));
-}
-
 static void alc663_m51va_unsol_event(struct hda_codec *codec,
 static void alc663_m51va_unsol_event(struct hda_codec *codec,
 					   unsigned int res)
 					   unsigned int res)
 {
 {
@@ -16556,36 +16509,32 @@ static void alc663_m51va_unsol_event(struct hda_codec *codec,
 		alc663_m51va_speaker_automute(codec);
 		alc663_m51va_speaker_automute(codec);
 		break;
 		break;
 	case ALC880_MIC_EVENT:
 	case ALC880_MIC_EVENT:
-		alc663_m51va_mic_automute(codec);
+		alc_mic_automute(codec);
 		break;
 		break;
 	}
 	}
 }
 }
 
 
+static void alc663_m51va_setup(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	spec->ext_mic.pin = 0x18;
+	spec->ext_mic.mux_idx = 0;
+	spec->int_mic.pin = 0x12;
+	spec->int_mic.mux_idx = 1;
+	spec->auto_mic = 1;
+}
+
 static void alc663_m51va_inithook(struct hda_codec *codec)
 static void alc663_m51va_inithook(struct hda_codec *codec)
 {
 {
 	alc663_m51va_speaker_automute(codec);
 	alc663_m51va_speaker_automute(codec);
-	alc663_m51va_mic_automute(codec);
+	alc_mic_automute(codec);
 }
 }
 
 
 /* ***************** Mode1 ******************************/
 /* ***************** Mode1 ******************************/
-static void alc663_mode1_unsol_event(struct hda_codec *codec,
-					   unsigned int res)
-{
-	switch (res >> 26) {
-	case ALC880_HP_EVENT:
-		alc663_m51va_speaker_automute(codec);
-		break;
-	case ALC880_MIC_EVENT:
-		alc662_eeepc_mic_automute(codec);
-		break;
-	}
-}
+#define alc663_mode1_unsol_event	alc663_m51va_unsol_event
+#define alc663_mode1_setup		alc663_m51va_setup
+#define alc663_mode1_inithook		alc663_m51va_inithook
 
 
-static void alc663_mode1_inithook(struct hda_codec *codec)
-{
-	alc663_m51va_speaker_automute(codec);
-	alc662_eeepc_mic_automute(codec);
-}
 /* ***************** Mode2 ******************************/
 /* ***************** Mode2 ******************************/
 static void alc662_mode2_unsol_event(struct hda_codec *codec,
 static void alc662_mode2_unsol_event(struct hda_codec *codec,
 					   unsigned int res)
 					   unsigned int res)
@@ -16595,15 +16544,17 @@ static void alc662_mode2_unsol_event(struct hda_codec *codec,
 		alc662_f5z_speaker_automute(codec);
 		alc662_f5z_speaker_automute(codec);
 		break;
 		break;
 	case ALC880_MIC_EVENT:
 	case ALC880_MIC_EVENT:
-		alc662_eeepc_mic_automute(codec);
+		alc_mic_automute(codec);
 		break;
 		break;
 	}
 	}
 }
 }
 
 
+#define alc662_mode2_setup	alc663_m51va_setup
+
 static void alc662_mode2_inithook(struct hda_codec *codec)
 static void alc662_mode2_inithook(struct hda_codec *codec)
 {
 {
 	alc662_f5z_speaker_automute(codec);
 	alc662_f5z_speaker_automute(codec);
-	alc662_eeepc_mic_automute(codec);
+	alc_mic_automute(codec);
 }
 }
 /* ***************** Mode3 ******************************/
 /* ***************** Mode3 ******************************/
 static void alc663_mode3_unsol_event(struct hda_codec *codec,
 static void alc663_mode3_unsol_event(struct hda_codec *codec,
@@ -16614,15 +16565,17 @@ static void alc663_mode3_unsol_event(struct hda_codec *codec,
 		alc663_two_hp_m1_speaker_automute(codec);
 		alc663_two_hp_m1_speaker_automute(codec);
 		break;
 		break;
 	case ALC880_MIC_EVENT:
 	case ALC880_MIC_EVENT:
-		alc662_eeepc_mic_automute(codec);
+		alc_mic_automute(codec);
 		break;
 		break;
 	}
 	}
 }
 }
 
 
+#define alc663_mode3_setup	alc663_m51va_setup
+
 static void alc663_mode3_inithook(struct hda_codec *codec)
 static void alc663_mode3_inithook(struct hda_codec *codec)
 {
 {
 	alc663_two_hp_m1_speaker_automute(codec);
 	alc663_two_hp_m1_speaker_automute(codec);
-	alc662_eeepc_mic_automute(codec);
+	alc_mic_automute(codec);
 }
 }
 /* ***************** Mode4 ******************************/
 /* ***************** Mode4 ******************************/
 static void alc663_mode4_unsol_event(struct hda_codec *codec,
 static void alc663_mode4_unsol_event(struct hda_codec *codec,
@@ -16633,15 +16586,17 @@ static void alc663_mode4_unsol_event(struct hda_codec *codec,
 		alc663_21jd_two_speaker_automute(codec);
 		alc663_21jd_two_speaker_automute(codec);
 		break;
 		break;
 	case ALC880_MIC_EVENT:
 	case ALC880_MIC_EVENT:
-		alc662_eeepc_mic_automute(codec);
+		alc_mic_automute(codec);
 		break;
 		break;
 	}
 	}
 }
 }
 
 
+#define alc663_mode4_setup	alc663_m51va_setup
+
 static void alc663_mode4_inithook(struct hda_codec *codec)
 static void alc663_mode4_inithook(struct hda_codec *codec)
 {
 {
 	alc663_21jd_two_speaker_automute(codec);
 	alc663_21jd_two_speaker_automute(codec);
-	alc662_eeepc_mic_automute(codec);
+	alc_mic_automute(codec);
 }
 }
 /* ***************** Mode5 ******************************/
 /* ***************** Mode5 ******************************/
 static void alc663_mode5_unsol_event(struct hda_codec *codec,
 static void alc663_mode5_unsol_event(struct hda_codec *codec,
@@ -16652,15 +16607,17 @@ static void alc663_mode5_unsol_event(struct hda_codec *codec,
 		alc663_15jd_two_speaker_automute(codec);
 		alc663_15jd_two_speaker_automute(codec);
 		break;
 		break;
 	case ALC880_MIC_EVENT:
 	case ALC880_MIC_EVENT:
-		alc662_eeepc_mic_automute(codec);
+		alc_mic_automute(codec);
 		break;
 		break;
 	}
 	}
 }
 }
 
 
+#define alc663_mode5_setup	alc663_m51va_setup
+
 static void alc663_mode5_inithook(struct hda_codec *codec)
 static void alc663_mode5_inithook(struct hda_codec *codec)
 {
 {
 	alc663_15jd_two_speaker_automute(codec);
 	alc663_15jd_two_speaker_automute(codec);
-	alc662_eeepc_mic_automute(codec);
+	alc_mic_automute(codec);
 }
 }
 /* ***************** Mode6 ******************************/
 /* ***************** Mode6 ******************************/
 static void alc663_mode6_unsol_event(struct hda_codec *codec,
 static void alc663_mode6_unsol_event(struct hda_codec *codec,
@@ -16671,15 +16628,17 @@ static void alc663_mode6_unsol_event(struct hda_codec *codec,
 		alc663_two_hp_m2_speaker_automute(codec);
 		alc663_two_hp_m2_speaker_automute(codec);
 		break;
 		break;
 	case ALC880_MIC_EVENT:
 	case ALC880_MIC_EVENT:
-		alc662_eeepc_mic_automute(codec);
+		alc_mic_automute(codec);
 		break;
 		break;
 	}
 	}
 }
 }
 
 
+#define alc663_mode6_setup	alc663_m51va_setup
+
 static void alc663_mode6_inithook(struct hda_codec *codec)
 static void alc663_mode6_inithook(struct hda_codec *codec)
 {
 {
 	alc663_two_hp_m2_speaker_automute(codec);
 	alc663_two_hp_m2_speaker_automute(codec);
-	alc662_eeepc_mic_automute(codec);
+	alc_mic_automute(codec);
 }
 }
 
 
 static void alc663_g71v_hp_automute(struct hda_codec *codec)
 static void alc663_g71v_hp_automute(struct hda_codec *codec)
@@ -16721,16 +16680,18 @@ static void alc663_g71v_unsol_event(struct hda_codec *codec,
 		alc663_g71v_front_automute(codec);
 		alc663_g71v_front_automute(codec);
 		break;
 		break;
 	case ALC880_MIC_EVENT:
 	case ALC880_MIC_EVENT:
-		alc662_eeepc_mic_automute(codec);
+		alc_mic_automute(codec);
 		break;
 		break;
 	}
 	}
 }
 }
 
 
+#define alc663_g71v_setup	alc663_m51va_setup
+
 static void alc663_g71v_inithook(struct hda_codec *codec)
 static void alc663_g71v_inithook(struct hda_codec *codec)
 {
 {
 	alc663_g71v_front_automute(codec);
 	alc663_g71v_front_automute(codec);
 	alc663_g71v_hp_automute(codec);
 	alc663_g71v_hp_automute(codec);
-	alc662_eeepc_mic_automute(codec);
+	alc_mic_automute(codec);
 }
 }
 
 
 static void alc663_g50v_unsol_event(struct hda_codec *codec,
 static void alc663_g50v_unsol_event(struct hda_codec *codec,
@@ -16741,15 +16702,17 @@ static void alc663_g50v_unsol_event(struct hda_codec *codec,
 		alc663_m51va_speaker_automute(codec);
 		alc663_m51va_speaker_automute(codec);
 		break;
 		break;
 	case ALC880_MIC_EVENT:
 	case ALC880_MIC_EVENT:
-		alc662_eeepc_mic_automute(codec);
+		alc_mic_automute(codec);
 		break;
 		break;
 	}
 	}
 }
 }
 
 
+#define alc663_g50v_setup	alc663_m51va_setup
+
 static void alc663_g50v_inithook(struct hda_codec *codec)
 static void alc663_g50v_inithook(struct hda_codec *codec)
 {
 {
 	alc663_m51va_speaker_automute(codec);
 	alc663_m51va_speaker_automute(codec);
-	alc662_eeepc_mic_automute(codec);
+	alc_mic_automute(codec);
 }
 }
 
 
 static struct snd_kcontrol_new alc662_ecs_mixer[] = {
 static struct snd_kcontrol_new alc662_ecs_mixer[] = {
@@ -16953,8 +16916,8 @@ static struct alc_config_preset alc662_presets[] = {
 		.dac_nids = alc662_dac_nids,
 		.dac_nids = alc662_dac_nids,
 		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
 		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
 		.channel_mode = alc662_3ST_2ch_modes,
 		.channel_mode = alc662_3ST_2ch_modes,
-		.input_mux = &alc662_eeepc_capture_source,
 		.unsol_event = alc662_eeepc_unsol_event,
 		.unsol_event = alc662_eeepc_unsol_event,
+		.setup = alc662_eeepc_setup,
 		.init_hook = alc662_eeepc_inithook,
 		.init_hook = alc662_eeepc_inithook,
 	},
 	},
 	[ALC662_ASUS_EEEPC_EP20] = {
 	[ALC662_ASUS_EEEPC_EP20] = {
@@ -16968,6 +16931,7 @@ static struct alc_config_preset alc662_presets[] = {
 		.channel_mode = alc662_3ST_6ch_modes,
 		.channel_mode = alc662_3ST_6ch_modes,
 		.input_mux = &alc662_lenovo_101e_capture_source,
 		.input_mux = &alc662_lenovo_101e_capture_source,
 		.unsol_event = alc662_eeepc_unsol_event,
 		.unsol_event = alc662_eeepc_unsol_event,
+		.setup = alc662_eeepc_ep20_setup,
 		.init_hook = alc662_eeepc_ep20_inithook,
 		.init_hook = alc662_eeepc_ep20_inithook,
 	},
 	},
 	[ALC662_ECS] = {
 	[ALC662_ECS] = {
@@ -16978,8 +16942,8 @@ static struct alc_config_preset alc662_presets[] = {
 		.dac_nids = alc662_dac_nids,
 		.dac_nids = alc662_dac_nids,
 		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
 		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
 		.channel_mode = alc662_3ST_2ch_modes,
 		.channel_mode = alc662_3ST_2ch_modes,
-		.input_mux = &alc662_eeepc_capture_source,
 		.unsol_event = alc662_eeepc_unsol_event,
 		.unsol_event = alc662_eeepc_unsol_event,
+		.setup = alc662_eeepc_setup,
 		.init_hook = alc662_eeepc_inithook,
 		.init_hook = alc662_eeepc_inithook,
 	},
 	},
 	[ALC663_ASUS_M51VA] = {
 	[ALC663_ASUS_M51VA] = {
@@ -16990,8 +16954,8 @@ static struct alc_config_preset alc662_presets[] = {
 		.dig_out_nid = ALC662_DIGOUT_NID,
 		.dig_out_nid = ALC662_DIGOUT_NID,
 		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
 		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
 		.channel_mode = alc662_3ST_2ch_modes,
 		.channel_mode = alc662_3ST_2ch_modes,
-		.input_mux = &alc663_m51va_capture_source,
 		.unsol_event = alc663_m51va_unsol_event,
 		.unsol_event = alc663_m51va_unsol_event,
+		.setup = alc663_m51va_setup,
 		.init_hook = alc663_m51va_inithook,
 		.init_hook = alc663_m51va_inithook,
 	},
 	},
 	[ALC663_ASUS_G71V] = {
 	[ALC663_ASUS_G71V] = {
@@ -17002,8 +16966,8 @@ static struct alc_config_preset alc662_presets[] = {
 		.dig_out_nid = ALC662_DIGOUT_NID,
 		.dig_out_nid = ALC662_DIGOUT_NID,
 		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
 		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
 		.channel_mode = alc662_3ST_2ch_modes,
 		.channel_mode = alc662_3ST_2ch_modes,
-		.input_mux = &alc662_eeepc_capture_source,
 		.unsol_event = alc663_g71v_unsol_event,
 		.unsol_event = alc663_g71v_unsol_event,
+		.setup = alc663_g71v_setup,
 		.init_hook = alc663_g71v_inithook,
 		.init_hook = alc663_g71v_inithook,
 	},
 	},
 	[ALC663_ASUS_H13] = {
 	[ALC663_ASUS_H13] = {
@@ -17013,7 +16977,6 @@ static struct alc_config_preset alc662_presets[] = {
 		.dac_nids = alc662_dac_nids,
 		.dac_nids = alc662_dac_nids,
 		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
 		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
 		.channel_mode = alc662_3ST_2ch_modes,
 		.channel_mode = alc662_3ST_2ch_modes,
-		.input_mux = &alc663_m51va_capture_source,
 		.unsol_event = alc663_m51va_unsol_event,
 		.unsol_event = alc663_m51va_unsol_event,
 		.init_hook = alc663_m51va_inithook,
 		.init_hook = alc663_m51va_inithook,
 	},
 	},
@@ -17027,6 +16990,7 @@ static struct alc_config_preset alc662_presets[] = {
 		.channel_mode = alc662_3ST_6ch_modes,
 		.channel_mode = alc662_3ST_6ch_modes,
 		.input_mux = &alc663_capture_source,
 		.input_mux = &alc663_capture_source,
 		.unsol_event = alc663_g50v_unsol_event,
 		.unsol_event = alc663_g50v_unsol_event,
+		.setup = alc663_g50v_setup,
 		.init_hook = alc663_g50v_inithook,
 		.init_hook = alc663_g50v_inithook,
 	},
 	},
 	[ALC663_ASUS_MODE1] = {
 	[ALC663_ASUS_MODE1] = {
@@ -17040,8 +17004,8 @@ static struct alc_config_preset alc662_presets[] = {
 		.dig_out_nid = ALC662_DIGOUT_NID,
 		.dig_out_nid = ALC662_DIGOUT_NID,
 		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
 		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
 		.channel_mode = alc662_3ST_2ch_modes,
 		.channel_mode = alc662_3ST_2ch_modes,
-		.input_mux = &alc662_eeepc_capture_source,
 		.unsol_event = alc663_mode1_unsol_event,
 		.unsol_event = alc663_mode1_unsol_event,
+		.setup = alc663_mode1_setup,
 		.init_hook = alc663_mode1_inithook,
 		.init_hook = alc663_mode1_inithook,
 	},
 	},
 	[ALC662_ASUS_MODE2] = {
 	[ALC662_ASUS_MODE2] = {
@@ -17054,8 +17018,8 @@ static struct alc_config_preset alc662_presets[] = {
 		.dig_out_nid = ALC662_DIGOUT_NID,
 		.dig_out_nid = ALC662_DIGOUT_NID,
 		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
 		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
 		.channel_mode = alc662_3ST_2ch_modes,
 		.channel_mode = alc662_3ST_2ch_modes,
-		.input_mux = &alc662_eeepc_capture_source,
 		.unsol_event = alc662_mode2_unsol_event,
 		.unsol_event = alc662_mode2_unsol_event,
+		.setup = alc662_mode2_setup,
 		.init_hook = alc662_mode2_inithook,
 		.init_hook = alc662_mode2_inithook,
 	},
 	},
 	[ALC663_ASUS_MODE3] = {
 	[ALC663_ASUS_MODE3] = {
@@ -17069,8 +17033,8 @@ static struct alc_config_preset alc662_presets[] = {
 		.dig_out_nid = ALC662_DIGOUT_NID,
 		.dig_out_nid = ALC662_DIGOUT_NID,
 		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
 		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
 		.channel_mode = alc662_3ST_2ch_modes,
 		.channel_mode = alc662_3ST_2ch_modes,
-		.input_mux = &alc662_eeepc_capture_source,
 		.unsol_event = alc663_mode3_unsol_event,
 		.unsol_event = alc663_mode3_unsol_event,
+		.setup = alc663_mode3_setup,
 		.init_hook = alc663_mode3_inithook,
 		.init_hook = alc663_mode3_inithook,
 	},
 	},
 	[ALC663_ASUS_MODE4] = {
 	[ALC663_ASUS_MODE4] = {
@@ -17084,8 +17048,8 @@ static struct alc_config_preset alc662_presets[] = {
 		.dig_out_nid = ALC662_DIGOUT_NID,
 		.dig_out_nid = ALC662_DIGOUT_NID,
 		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
 		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
 		.channel_mode = alc662_3ST_2ch_modes,
 		.channel_mode = alc662_3ST_2ch_modes,
-		.input_mux = &alc662_eeepc_capture_source,
 		.unsol_event = alc663_mode4_unsol_event,
 		.unsol_event = alc663_mode4_unsol_event,
+		.setup = alc663_mode4_setup,
 		.init_hook = alc663_mode4_inithook,
 		.init_hook = alc663_mode4_inithook,
 	},
 	},
 	[ALC663_ASUS_MODE5] = {
 	[ALC663_ASUS_MODE5] = {
@@ -17099,8 +17063,8 @@ static struct alc_config_preset alc662_presets[] = {
 		.dig_out_nid = ALC662_DIGOUT_NID,
 		.dig_out_nid = ALC662_DIGOUT_NID,
 		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
 		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
 		.channel_mode = alc662_3ST_2ch_modes,
 		.channel_mode = alc662_3ST_2ch_modes,
-		.input_mux = &alc662_eeepc_capture_source,
 		.unsol_event = alc663_mode5_unsol_event,
 		.unsol_event = alc663_mode5_unsol_event,
+		.setup = alc663_mode5_setup,
 		.init_hook = alc663_mode5_inithook,
 		.init_hook = alc663_mode5_inithook,
 	},
 	},
 	[ALC663_ASUS_MODE6] = {
 	[ALC663_ASUS_MODE6] = {
@@ -17114,8 +17078,8 @@ static struct alc_config_preset alc662_presets[] = {
 		.dig_out_nid = ALC662_DIGOUT_NID,
 		.dig_out_nid = ALC662_DIGOUT_NID,
 		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
 		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
 		.channel_mode = alc662_3ST_2ch_modes,
 		.channel_mode = alc662_3ST_2ch_modes,
-		.input_mux = &alc662_eeepc_capture_source,
 		.unsol_event = alc663_mode6_unsol_event,
 		.unsol_event = alc663_mode6_unsol_event,
+		.setup = alc663_mode6_setup,
 		.init_hook = alc663_mode6_inithook,
 		.init_hook = alc663_mode6_inithook,
 	},
 	},
 	[ALC272_DELL] = {
 	[ALC272_DELL] = {
@@ -17129,8 +17093,8 @@ static struct alc_config_preset alc662_presets[] = {
 		.num_adc_nids = ARRAY_SIZE(alc272_adc_nids),
 		.num_adc_nids = ARRAY_SIZE(alc272_adc_nids),
 		.capsrc_nids = alc272_capsrc_nids,
 		.capsrc_nids = alc272_capsrc_nids,
 		.channel_mode = alc662_3ST_2ch_modes,
 		.channel_mode = alc662_3ST_2ch_modes,
-		.input_mux = &alc663_m51va_capture_source,
 		.unsol_event = alc663_m51va_unsol_event,
 		.unsol_event = alc663_m51va_unsol_event,
+		.setup = alc663_m51va_setup,
 		.init_hook = alc663_m51va_inithook,
 		.init_hook = alc663_m51va_inithook,
 	},
 	},
 	[ALC272_DELL_ZM1] = {
 	[ALC272_DELL_ZM1] = {
@@ -17141,11 +17105,11 @@ static struct alc_config_preset alc662_presets[] = {
 		.dac_nids = alc662_dac_nids,
 		.dac_nids = alc662_dac_nids,
 		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
 		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
 		.adc_nids = alc662_adc_nids,
 		.adc_nids = alc662_adc_nids,
-		.num_adc_nids = ARRAY_SIZE(alc662_adc_nids),
+		.num_adc_nids = 1,
 		.capsrc_nids = alc662_capsrc_nids,
 		.capsrc_nids = alc662_capsrc_nids,
 		.channel_mode = alc662_3ST_2ch_modes,
 		.channel_mode = alc662_3ST_2ch_modes,
-		.input_mux = &alc663_m51va_capture_source,
 		.unsol_event = alc663_m51va_unsol_event,
 		.unsol_event = alc663_m51va_unsol_event,
+		.setup = alc663_m51va_setup,
 		.init_hook = alc663_m51va_inithook,
 		.init_hook = alc663_m51va_inithook,
 	},
 	},
 	[ALC272_SAMSUNG_NC10] = {
 	[ALC272_SAMSUNG_NC10] = {
@@ -17156,8 +17120,9 @@ static struct alc_config_preset alc662_presets[] = {
 		.dac_nids = alc272_dac_nids,
 		.dac_nids = alc272_dac_nids,
 		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
 		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
 		.channel_mode = alc662_3ST_2ch_modes,
 		.channel_mode = alc662_3ST_2ch_modes,
-		.input_mux = &alc272_nc10_capture_source,
+		/*.input_mux = &alc272_nc10_capture_source,*/
 		.unsol_event = alc663_mode4_unsol_event,
 		.unsol_event = alc663_mode4_unsol_event,
+		.setup = alc663_mode4_setup,
 		.init_hook = alc663_mode4_inithook,
 		.init_hook = alc663_mode4_inithook,
 	},
 	},
 };
 };
@@ -17209,13 +17174,25 @@ static int alc662_auto_create_multi_out_ctls(struct alc_spec *spec,
 			if (err < 0)
 			if (err < 0)
 				return err;
 				return err;
 		} else {
 		} else {
-			sprintf(name, "%s Playback Volume", chname[i]);
+			const char *pfx;
+			if (cfg->line_outs == 1 &&
+			    cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) {
+				if (!cfg->hp_pins)
+					pfx = "Speaker";
+				else
+					pfx = "PCM";
+			} else
+				pfx = chname[i];
+			sprintf(name, "%s Playback Volume", pfx);
 			err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
 			err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
 					  HDA_COMPOSE_AMP_VAL(nid, 3, 0,
 					  HDA_COMPOSE_AMP_VAL(nid, 3, 0,
 							      HDA_OUTPUT));
 							      HDA_OUTPUT));
 			if (err < 0)
 			if (err < 0)
 				return err;
 				return err;
-			sprintf(name, "%s Playback Switch", chname[i]);
+			if (cfg->line_outs == 1 &&
+			    cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+				pfx = "Speaker";
+			sprintf(name, "%s Playback Switch", pfx);
 			err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
 			err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
 				HDA_COMPOSE_AMP_VAL(alc880_idx_to_mixer(i),
 				HDA_COMPOSE_AMP_VAL(alc880_idx_to_mixer(i),
 						    3, 0, HDA_INPUT));
 						    3, 0, HDA_INPUT));
@@ -17277,62 +17254,9 @@ static int alc662_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin,
 	return 0;
 	return 0;
 }
 }
 
 
-/* return the index of the src widget from the connection list of the nid.
- * return -1 if not found
- */
-static int alc662_input_pin_idx(struct hda_codec *codec, hda_nid_t nid,
-				hda_nid_t src)
-{
-	hda_nid_t conn_list[HDA_MAX_CONNECTIONS];
-	int i, conns;
-
-	conns = snd_hda_get_connections(codec, nid, conn_list,
-					ARRAY_SIZE(conn_list));
-	if (conns < 0)
-		return -1;
-	for (i = 0; i < conns; i++)
-		if (conn_list[i] == src)
-			return i;
-	return -1;
-}
-
-static int alc662_is_input_pin(struct hda_codec *codec, hda_nid_t nid)
-{
-	unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
-	return (pincap & AC_PINCAP_IN) != 0;
-}
-
 /* create playback/capture controls for input pins */
 /* create playback/capture controls for input pins */
-static int alc662_auto_create_analog_input_ctls(struct hda_codec *codec,
-						const struct auto_pin_cfg *cfg)
-{
-	struct alc_spec *spec = codec->spec;
-	struct hda_input_mux *imux = &spec->private_imux[0];
-	int i, err, idx;
-
-	for (i = 0; i < AUTO_PIN_LAST; i++) {
-		if (alc662_is_input_pin(codec, cfg->input_pins[i])) {
-			idx = alc662_input_pin_idx(codec, 0x0b,
-						   cfg->input_pins[i]);
-			if (idx >= 0) {
-				err = new_analog_input(spec, cfg->input_pins[i],
-						       auto_pin_cfg_labels[i],
-						       idx, 0x0b);
-				if (err < 0)
-					return err;
-			}
-			idx = alc662_input_pin_idx(codec, 0x22,
-						   cfg->input_pins[i]);
-			if (idx >= 0) {
-				imux->items[imux->num_items].label =
-					auto_pin_cfg_labels[i];
-				imux->items[imux->num_items].index = idx;
-				imux->num_items++;
-			}
-		}
-	}
-	return 0;
-}
+#define alc662_auto_create_input_ctls \
+	alc880_auto_create_input_ctls
 
 
 static void alc662_auto_set_output_and_unmute(struct hda_codec *codec,
 static void alc662_auto_set_output_and_unmute(struct hda_codec *codec,
 					      hda_nid_t nid, int pin_type,
 					      hda_nid_t nid, int pin_type,
@@ -17386,7 +17310,7 @@ static void alc662_auto_init_analog_input(struct hda_codec *codec)
 
 
 	for (i = 0; i < AUTO_PIN_LAST; i++) {
 	for (i = 0; i < AUTO_PIN_LAST; i++) {
 		hda_nid_t nid = spec->autocfg.input_pins[i];
 		hda_nid_t nid = spec->autocfg.input_pins[i];
-		if (alc662_is_input_pin(codec, nid)) {
+		if (alc_is_input_pin(codec, nid)) {
 			alc_set_input_pin(codec, nid, i);
 			alc_set_input_pin(codec, nid, i);
 			if (nid != ALC662_PIN_CD_NID &&
 			if (nid != ALC662_PIN_CD_NID &&
 			    (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
 			    (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP))
@@ -17427,7 +17351,7 @@ static int alc662_parse_auto_config(struct hda_codec *codec)
 					   "Headphone");
 					   "Headphone");
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
-	err = alc662_auto_create_analog_input_ctls(codec, &spec->autocfg);
+	err = alc662_auto_create_input_ctls(codec, &spec->autocfg);
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
 
 
@@ -17484,8 +17408,8 @@ static int patch_alc662(struct hda_codec *codec)
 						  alc662_models,
 						  alc662_models,
 			  	                  alc662_cfg_tbl);
 			  	                  alc662_cfg_tbl);
 	if (board_config < 0) {
 	if (board_config < 0) {
-		printk(KERN_INFO "hda_codec: Unknown model for %s, "
-		       "trying auto-probe from BIOS...\n", codec->chip_name);
+		printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+		       codec->chip_name);
 		board_config = ALC662_AUTO;
 		board_config = ALC662_AUTO;
 	}
 	}
 
 
@@ -17510,7 +17434,7 @@ static int patch_alc662(struct hda_codec *codec)
 	}
 	}
 
 
 	if (board_config != ALC662_AUTO)
 	if (board_config != ALC662_AUTO)
-		setup_preset(spec, &alc662_presets[board_config]);
+		setup_preset(codec, &alc662_presets[board_config]);
 
 
 	spec->stream_analog_playback = &alc662_pcm_analog_playback;
 	spec->stream_analog_playback = &alc662_pcm_analog_playback;
 	spec->stream_analog_capture = &alc662_pcm_analog_capture;
 	spec->stream_analog_capture = &alc662_pcm_analog_capture;
@@ -17526,7 +17450,7 @@ static int patch_alc662(struct hda_codec *codec)
 		spec->capsrc_nids = alc662_capsrc_nids;
 		spec->capsrc_nids = alc662_capsrc_nids;
 
 
 	if (!spec->cap_mixer)
 	if (!spec->cap_mixer)
-		set_capture_mixer(spec);
+		set_capture_mixer(codec);
 	if (codec->vendor_id == 0x10ec0662)
 	if (codec->vendor_id == 0x10ec0662)
 		set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
 		set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
 	else
 	else
@@ -17562,23 +17486,23 @@ static struct hda_codec_preset snd_hda_preset_realtek[] = {
 	{ .id = 0x10ec0861, .name = "ALC861", .patch = patch_alc861 },
 	{ .id = 0x10ec0861, .name = "ALC861", .patch = patch_alc861 },
 	{ .id = 0x10ec0862, .name = "ALC861-VD", .patch = patch_alc861vd },
 	{ .id = 0x10ec0862, .name = "ALC861-VD", .patch = patch_alc861vd },
 	{ .id = 0x10ec0662, .rev = 0x100002, .name = "ALC662 rev2",
 	{ .id = 0x10ec0662, .rev = 0x100002, .name = "ALC662 rev2",
-	  .patch = patch_alc883 },
+	  .patch = patch_alc882 },
 	{ .id = 0x10ec0662, .rev = 0x100101, .name = "ALC662 rev1",
 	{ .id = 0x10ec0662, .rev = 0x100101, .name = "ALC662 rev1",
 	  .patch = patch_alc662 },
 	  .patch = patch_alc662 },
 	{ .id = 0x10ec0663, .name = "ALC663", .patch = patch_alc662 },
 	{ .id = 0x10ec0663, .name = "ALC663", .patch = patch_alc662 },
 	{ .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
 	{ .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
 	{ .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
 	{ .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
-	{ .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc883 },
+	{ .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 },
 	{ .id = 0x10ec0885, .rev = 0x100101, .name = "ALC889A",
 	{ .id = 0x10ec0885, .rev = 0x100101, .name = "ALC889A",
-	  .patch = patch_alc882 }, /* should be patch_alc883() in future */
+	  .patch = patch_alc882 },
 	{ .id = 0x10ec0885, .rev = 0x100103, .name = "ALC889A",
 	{ .id = 0x10ec0885, .rev = 0x100103, .name = "ALC889A",
-	  .patch = patch_alc882 }, /* should be patch_alc883() in future */
+	  .patch = patch_alc882 },
 	{ .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 },
 	{ .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 },
-	{ .id = 0x10ec0887, .name = "ALC887", .patch = patch_alc883 },
+	{ .id = 0x10ec0887, .name = "ALC887", .patch = patch_alc882 },
 	{ .id = 0x10ec0888, .rev = 0x100101, .name = "ALC1200",
 	{ .id = 0x10ec0888, .rev = 0x100101, .name = "ALC1200",
-	  .patch = patch_alc883 },
-	{ .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc883 },
-	{ .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc883 },
+	  .patch = patch_alc882 },
+	{ .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc882 },
+	{ .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc882 },
 	{} /* terminator */
 	{} /* terminator */
 };
 };
 
 

+ 574 - 632
sound/pci/hda/patch_sigmatel.c

@@ -40,6 +40,8 @@ enum {
 	STAC_INSERT_EVENT,
 	STAC_INSERT_EVENT,
 	STAC_PWR_EVENT,
 	STAC_PWR_EVENT,
 	STAC_HP_EVENT,
 	STAC_HP_EVENT,
+	STAC_LO_EVENT,
+	STAC_MIC_EVENT,
 };
 };
 
 
 enum {
 enum {
@@ -81,6 +83,7 @@ enum {
 	STAC_DELL_M6_DMIC,
 	STAC_DELL_M6_DMIC,
 	STAC_DELL_M6_BOTH,
 	STAC_DELL_M6_BOTH,
 	STAC_DELL_EQ,
 	STAC_DELL_EQ,
+	STAC_ALIENWARE_M17X,
 	STAC_92HD73XX_MODELS
 	STAC_92HD73XX_MODELS
 };
 };
 
 
@@ -177,6 +180,12 @@ struct sigmatel_jack {
 	struct snd_jack *jack;
 	struct snd_jack *jack;
 };
 };
 
 
+struct sigmatel_mic_route {
+	hda_nid_t pin;
+	unsigned char mux_idx;
+	unsigned char dmux_idx;
+};
+
 struct sigmatel_spec {
 struct sigmatel_spec {
 	struct snd_kcontrol_new *mixers[4];
 	struct snd_kcontrol_new *mixers[4];
 	unsigned int num_mixers;
 	unsigned int num_mixers;
@@ -188,6 +197,7 @@ struct sigmatel_spec {
 	unsigned int hp_detect: 1;
 	unsigned int hp_detect: 1;
 	unsigned int spdif_mute: 1;
 	unsigned int spdif_mute: 1;
 	unsigned int check_volume_offset:1;
 	unsigned int check_volume_offset:1;
+	unsigned int auto_mic:1;
 
 
 	/* gpio lines */
 	/* gpio lines */
 	unsigned int eapd_mask;
 	unsigned int eapd_mask;
@@ -219,7 +229,6 @@ struct sigmatel_spec {
 
 
 	/* playback */
 	/* playback */
 	struct hda_input_mux *mono_mux;
 	struct hda_input_mux *mono_mux;
-	struct hda_input_mux *amp_mux;
 	unsigned int cur_mmux;
 	unsigned int cur_mmux;
 	struct hda_multi_out multiout;
 	struct hda_multi_out multiout;
 	hda_nid_t dac_nids[5];
 	hda_nid_t dac_nids[5];
@@ -239,6 +248,15 @@ struct sigmatel_spec {
 	unsigned int num_dmuxes;
 	unsigned int num_dmuxes;
 	hda_nid_t *smux_nids;
 	hda_nid_t *smux_nids;
 	unsigned int num_smuxes;
 	unsigned int num_smuxes;
+	unsigned int num_analog_muxes;
+
+	unsigned long *capvols; /* amp-volume attr: HDA_COMPOSE_AMP_VAL() */
+	unsigned long *capsws; /* amp-mute attr: HDA_COMPOSE_AMP_VAL() */
+	unsigned int num_caps; /* number of capture volume/switch elements */
+
+	struct sigmatel_mic_route ext_mic;
+	struct sigmatel_mic_route int_mic;
+
 	const char **spdif_labels;
 	const char **spdif_labels;
 
 
 	hda_nid_t dig_in_nid;
 	hda_nid_t dig_in_nid;
@@ -263,7 +281,6 @@ struct sigmatel_spec {
 	unsigned int cur_smux[2];
 	unsigned int cur_smux[2];
 	unsigned int cur_amux;
 	unsigned int cur_amux;
 	hda_nid_t *amp_nids;
 	hda_nid_t *amp_nids;
-	unsigned int num_amps;
 	unsigned int powerdown_adcs;
 	unsigned int powerdown_adcs;
 
 
 	/* i/o switches */
 	/* i/o switches */
@@ -282,7 +299,6 @@ struct sigmatel_spec {
 	struct hda_input_mux private_dimux;
 	struct hda_input_mux private_dimux;
 	struct hda_input_mux private_imux;
 	struct hda_input_mux private_imux;
 	struct hda_input_mux private_smux;
 	struct hda_input_mux private_smux;
-	struct hda_input_mux private_amp_mux;
 	struct hda_input_mux private_mono_mux;
 	struct hda_input_mux private_mono_mux;
 };
 };
 
 
@@ -311,11 +327,6 @@ static hda_nid_t stac92hd73xx_adc_nids[2] = {
 	0x1a, 0x1b
 	0x1a, 0x1b
 };
 };
 
 
-#define DELL_M6_AMP 2
-static hda_nid_t stac92hd73xx_amp_nids[3] = {
-	0x0b, 0x0c, 0x0e
-};
-
 #define STAC92HD73XX_NUM_DMICS	2
 #define STAC92HD73XX_NUM_DMICS	2
 static hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = {
 static hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = {
 	0x13, 0x14, 0
 	0x13, 0x14, 0
@@ -323,8 +334,8 @@ static hda_nid_t stac92hd73xx_dmic_nids[STAC92HD73XX_NUM_DMICS + 1] = {
 
 
 #define STAC92HD73_DAC_COUNT 5
 #define STAC92HD73_DAC_COUNT 5
 
 
-static hda_nid_t stac92hd73xx_mux_nids[4] = {
-	0x28, 0x29, 0x2a, 0x2b,
+static hda_nid_t stac92hd73xx_mux_nids[2] = {
+	0x20, 0x21,
 };
 };
 
 
 static hda_nid_t stac92hd73xx_dmux_nids[2] = {
 static hda_nid_t stac92hd73xx_dmux_nids[2] = {
@@ -335,14 +346,16 @@ static hda_nid_t stac92hd73xx_smux_nids[2] = {
 	0x22, 0x23,
 	0x22, 0x23,
 };
 };
 
 
-#define STAC92HD83XXX_NUM_DMICS	2
-static hda_nid_t stac92hd83xxx_dmic_nids[STAC92HD83XXX_NUM_DMICS + 1] = {
-	0x11, 0x12, 0
+#define STAC92HD73XX_NUM_CAPS	2
+static unsigned long stac92hd73xx_capvols[] = {
+	HDA_COMPOSE_AMP_VAL(0x20, 3, 0, HDA_OUTPUT),
+	HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
 };
 };
+#define stac92hd73xx_capsws	stac92hd73xx_capvols
 
 
 #define STAC92HD83_DAC_COUNT 3
 #define STAC92HD83_DAC_COUNT 3
 
 
-static hda_nid_t stac92hd83xxx_dmux_nids[2] = {
+static hda_nid_t stac92hd83xxx_mux_nids[2] = {
 	0x17, 0x18,
 	0x17, 0x18,
 };
 };
 
 
@@ -362,9 +375,12 @@ static unsigned int stac92hd83xxx_pwr_mapping[4] = {
 	0x03, 0x0c, 0x20, 0x40,
 	0x03, 0x0c, 0x20, 0x40,
 };
 };
 
 
-static hda_nid_t stac92hd83xxx_amp_nids[1] = {
-	0xc,
+#define STAC92HD83XXX_NUM_CAPS	2
+static unsigned long stac92hd83xxx_capvols[] = {
+	HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_OUTPUT),
+	HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_OUTPUT),
 };
 };
+#define stac92hd83xxx_capsws	stac92hd83xxx_capvols
 
 
 static hda_nid_t stac92hd71bxx_pwr_nids[3] = {
 static hda_nid_t stac92hd71bxx_pwr_nids[3] = {
 	0x0a, 0x0d, 0x0f
 	0x0a, 0x0d, 0x0f
@@ -395,6 +411,13 @@ static hda_nid_t stac92hd71bxx_slave_dig_outs[2] = {
 	0x22, 0
 	0x22, 0
 };
 };
 
 
+#define STAC92HD71BXX_NUM_CAPS		2
+static unsigned long stac92hd71bxx_capvols[] = {
+	HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_OUTPUT),
+	HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT),
+};
+#define stac92hd71bxx_capsws	stac92hd71bxx_capvols
+
 static hda_nid_t stac925x_adc_nids[1] = {
 static hda_nid_t stac925x_adc_nids[1] = {
         0x03,
         0x03,
 };
 };
@@ -416,6 +439,13 @@ static hda_nid_t stac925x_dmux_nids[1] = {
 	0x14,
 	0x14,
 };
 };
 
 
+static unsigned long stac925x_capvols[] = {
+	HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_OUTPUT),
+};
+static unsigned long stac925x_capsws[] = {
+	HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
+};
+
 static hda_nid_t stac922x_adc_nids[2] = {
 static hda_nid_t stac922x_adc_nids[2] = {
         0x06, 0x07,
         0x06, 0x07,
 };
 };
@@ -424,6 +454,13 @@ static hda_nid_t stac922x_mux_nids[2] = {
         0x12, 0x13,
         0x12, 0x13,
 };
 };
 
 
+#define STAC922X_NUM_CAPS	2
+static unsigned long stac922x_capvols[] = {
+	HDA_COMPOSE_AMP_VAL(0x17, 3, 0, HDA_INPUT),
+	HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT),
+};
+#define stac922x_capsws		stac922x_capvols
+
 static hda_nid_t stac927x_slave_dig_outs[2] = {
 static hda_nid_t stac927x_slave_dig_outs[2] = {
 	0x1f, 0,
 	0x1f, 0,
 };
 };
@@ -453,6 +490,18 @@ static hda_nid_t stac927x_dmic_nids[STAC927X_NUM_DMICS + 1] = {
 	0x13, 0x14, 0
 	0x13, 0x14, 0
 };
 };
 
 
+#define STAC927X_NUM_CAPS	3
+static unsigned long stac927x_capvols[] = {
+	HDA_COMPOSE_AMP_VAL(0x18, 3, 0, HDA_INPUT),
+	HDA_COMPOSE_AMP_VAL(0x19, 3, 0, HDA_INPUT),
+	HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_INPUT),
+};
+static unsigned long stac927x_capsws[] = {
+	HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
+	HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_OUTPUT),
+	HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT),
+};
+
 static const char *stac927x_spdif_labels[5] = {
 static const char *stac927x_spdif_labels[5] = {
 	"Digital Playback", "ADAT", "Analog Mux 1",
 	"Digital Playback", "ADAT", "Analog Mux 1",
 	"Analog Mux 2", "Analog Mux 3"
 	"Analog Mux 2", "Analog Mux 3"
@@ -479,6 +528,16 @@ static hda_nid_t stac9205_dmic_nids[STAC9205_NUM_DMICS + 1] = {
         0x17, 0x18, 0
         0x17, 0x18, 0
 };
 };
 
 
+#define STAC9205_NUM_CAPS	2
+static unsigned long stac9205_capvols[] = {
+	HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_INPUT),
+	HDA_COMPOSE_AMP_VAL(0x1c, 3, 0, HDA_INPUT),
+};
+static unsigned long stac9205_capsws[] = {
+	HDA_COMPOSE_AMP_VAL(0x1d, 3, 0, HDA_OUTPUT),
+	HDA_COMPOSE_AMP_VAL(0x1e, 3, 0, HDA_OUTPUT),
+};
+
 static hda_nid_t stac9200_pin_nids[8] = {
 static hda_nid_t stac9200_pin_nids[8] = {
 	0x08, 0x09, 0x0d, 0x0e, 
 	0x08, 0x09, 0x0d, 0x0e, 
 	0x0f, 0x10, 0x11, 0x12,
 	0x0f, 0x10, 0x11, 0x12,
@@ -529,34 +588,6 @@ static hda_nid_t stac9205_pin_nids[12] = {
 	0x21, 0x22,
 	0x21, 0x22,
 };
 };
 
 
-#define stac92xx_amp_volume_info snd_hda_mixer_amp_volume_info
-
-static int stac92xx_amp_volume_get(struct snd_kcontrol *kcontrol,
-				 struct snd_ctl_elem_value *ucontrol)
-{
-	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct sigmatel_spec *spec = codec->spec;
-	hda_nid_t nid = spec->amp_nids[spec->cur_amux];
-
-	kcontrol->private_value ^= get_amp_nid(kcontrol);
-	kcontrol->private_value |= nid;
-
-	return snd_hda_mixer_amp_volume_get(kcontrol, ucontrol);
-}
-
-static int stac92xx_amp_volume_put(struct snd_kcontrol *kcontrol,
-				 struct snd_ctl_elem_value *ucontrol)
-{
-	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct sigmatel_spec *spec = codec->spec;
-	hda_nid_t nid = spec->amp_nids[spec->cur_amux];
-
-	kcontrol->private_value ^= get_amp_nid(kcontrol);
-	kcontrol->private_value |= nid;
-
-	return snd_hda_mixer_amp_volume_put(kcontrol, ucontrol);
-}
-
 static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol,
 static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol,
 				   struct snd_ctl_elem_info *uinfo)
 				   struct snd_ctl_elem_info *uinfo)
 {
 {
@@ -693,9 +724,35 @@ static int stac92xx_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct sigmatel_spec *spec = codec->spec;
 	struct sigmatel_spec *spec = codec->spec;
 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
-	return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
-				     spec->mux_nids[adc_idx], &spec->cur_mux[adc_idx]);
+	const struct hda_input_mux *imux = spec->input_mux;
+	unsigned int idx, prev_idx;
+
+	idx = ucontrol->value.enumerated.item[0];
+	if (idx >= imux->num_items)
+		idx = imux->num_items - 1;
+	prev_idx = spec->cur_mux[adc_idx];
+	if (prev_idx == idx)
+		return 0;
+	if (idx < spec->num_analog_muxes) {
+		snd_hda_codec_write_cache(codec, spec->mux_nids[adc_idx], 0,
+					  AC_VERB_SET_CONNECT_SEL,
+					  imux->items[idx].index);
+		if (prev_idx >= spec->num_analog_muxes) {
+			imux = spec->dinput_mux;
+			/* 0 = analog */
+			snd_hda_codec_write_cache(codec,
+						  spec->dmux_nids[adc_idx], 0,
+						  AC_VERB_SET_CONNECT_SEL,
+						  imux->items[0].index);
+		}
+	} else {
+		imux = spec->dinput_mux;
+		snd_hda_codec_write_cache(codec, spec->dmux_nids[adc_idx], 0,
+					  AC_VERB_SET_CONNECT_SEL,
+					  imux->items[idx - 1].index);
+	}
+	spec->cur_mux[adc_idx] = idx;
+	return 1;
 }
 }
 
 
 static int stac92xx_mono_mux_enum_info(struct snd_kcontrol *kcontrol,
 static int stac92xx_mono_mux_enum_info(struct snd_kcontrol *kcontrol,
@@ -726,41 +783,6 @@ static int stac92xx_mono_mux_enum_put(struct snd_kcontrol *kcontrol,
 				     spec->mono_nid, &spec->cur_mmux);
 				     spec->mono_nid, &spec->cur_mmux);
 }
 }
 
 
-static int stac92xx_amp_mux_enum_info(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_info *uinfo)
-{
-	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct sigmatel_spec *spec = codec->spec;
-	return snd_hda_input_mux_info(spec->amp_mux, uinfo);
-}
-
-static int stac92xx_amp_mux_enum_get(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol)
-{
-	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct sigmatel_spec *spec = codec->spec;
-
-	ucontrol->value.enumerated.item[0] = spec->cur_amux;
-	return 0;
-}
-
-static int stac92xx_amp_mux_enum_put(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol)
-{
-	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct sigmatel_spec *spec = codec->spec;
-	struct snd_kcontrol *ctl =
-		snd_hda_find_mixer_ctl(codec, "Amp Capture Volume");
-	if (!ctl)
-		return -EINVAL;
-
-	snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE |
-		SNDRV_CTL_EVENT_MASK_INFO, &ctl->id);
-
-	return snd_hda_input_mux_put(codec, spec->amp_mux, ucontrol,
-				     0, &spec->cur_amux);
-}
-
 #define stac92xx_aloopback_info snd_ctl_boolean_mono_info
 #define stac92xx_aloopback_info snd_ctl_boolean_mono_info
 
 
 static int stac92xx_aloopback_get(struct snd_kcontrol *kcontrol,
 static int stac92xx_aloopback_get(struct snd_kcontrol *kcontrol,
@@ -828,84 +850,16 @@ static struct hda_verb stac9200_eapd_init[] = {
 	{}
 	{}
 };
 };
 
 
-static struct hda_verb stac92hd73xx_6ch_core_init[] = {
-	/* set master volume and direct control */
-	{ 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
-	/* setup adcs to point to mixer */
-	{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
-	{ 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
-	{ 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-	{ 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-	{ 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-	/* setup import muxs */
-	{ 0x28, AC_VERB_SET_CONNECT_SEL, 0x01},
-	{ 0x29, AC_VERB_SET_CONNECT_SEL, 0x01},
-	{ 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01},
-	{ 0x2b, AC_VERB_SET_CONNECT_SEL, 0x00},
-	{}
-};
-
 static struct hda_verb dell_eq_core_init[] = {
 static struct hda_verb dell_eq_core_init[] = {
 	/* set master volume to max value without distortion
 	/* set master volume to max value without distortion
 	 * and direct control */
 	 * and direct control */
 	{ 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xec},
 	{ 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xec},
-	/* setup adcs to point to mixer */
-	{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
-	{ 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
-	/* setup import muxs */
-	{ 0x28, AC_VERB_SET_CONNECT_SEL, 0x01},
-	{ 0x29, AC_VERB_SET_CONNECT_SEL, 0x01},
-	{ 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01},
-	{ 0x2b, AC_VERB_SET_CONNECT_SEL, 0x00},
-	{}
-};
-
-static struct hda_verb dell_m6_core_init[] = {
-	{ 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
-	/* setup adcs to point to mixer */
-	{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
-	{ 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
-	/* setup import muxs */
-	{ 0x28, AC_VERB_SET_CONNECT_SEL, 0x01},
-	{ 0x29, AC_VERB_SET_CONNECT_SEL, 0x01},
-	{ 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01},
-	{ 0x2b, AC_VERB_SET_CONNECT_SEL, 0x00},
-	{}
-};
-
-static struct hda_verb stac92hd73xx_8ch_core_init[] = {
-	/* set master volume and direct control */
-	{ 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
-	/* setup adcs to point to mixer */
-	{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
-	{ 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
-	{ 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-	{ 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-	{ 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-	/* setup import muxs */
-	{ 0x28, AC_VERB_SET_CONNECT_SEL, 0x01},
-	{ 0x29, AC_VERB_SET_CONNECT_SEL, 0x01},
-	{ 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01},
-	{ 0x2b, AC_VERB_SET_CONNECT_SEL, 0x03},
 	{}
 	{}
 };
 };
 
 
-static struct hda_verb stac92hd73xx_10ch_core_init[] = {
+static struct hda_verb stac92hd73xx_core_init[] = {
 	/* set master volume and direct control */
 	/* set master volume and direct control */
 	{ 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
 	{ 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
-	/* dac3 is connected to import3 mux */
-	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb07f},
-	/* setup adcs to point to mixer */
-	{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
-	{ 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
-	{ 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-	{ 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-	{ 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-	/* setup import muxs */
-	{ 0x28, AC_VERB_SET_CONNECT_SEL, 0x01},
-	{ 0x29, AC_VERB_SET_CONNECT_SEL, 0x01},
-	{ 0x2a, AC_VERB_SET_CONNECT_SEL, 0x01},
-	{ 0x2b, AC_VERB_SET_CONNECT_SEL, 0x03},
 	{}
 	{}
 };
 };
 
 
@@ -925,19 +879,6 @@ static struct hda_verb stac92hd71bxx_core_init[] = {
 	{}
 	{}
 };
 };
 
 
-#define HD_DISABLE_PORTF 1
-static struct hda_verb stac92hd71bxx_analog_core_init[] = {
-	/* start of config #1 */
-
-	/* connect port 0f to audio mixer */
-	{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2},
-	/* start of config #2 */
-
-	/* set master volume and direct control */
-	{ 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
-	{}
-};
-
 static struct hda_verb stac92hd71bxx_unmute_core_init[] = {
 static struct hda_verb stac92hd71bxx_unmute_core_init[] = {
 	/* unmute right and left channels for nodes 0x0f, 0xa, 0x0d */
 	/* unmute right and left channels for nodes 0x0f, 0xa, 0x0d */
 	{ 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{ 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
@@ -996,31 +937,6 @@ static struct hda_verb stac9205_core_init[] = {
 		.put = stac92xx_mono_mux_enum_put, \
 		.put = stac92xx_mono_mux_enum_put, \
 	}
 	}
 
 
-#define STAC_AMP_MUX \
-	{ \
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
-		.name = "Amp Selector Capture Switch", \
-		.count = 1, \
-		.info = stac92xx_amp_mux_enum_info, \
-		.get = stac92xx_amp_mux_enum_get, \
-		.put = stac92xx_amp_mux_enum_put, \
-	}
-
-#define STAC_AMP_VOL(xname, nid, chs, idx, dir) \
-	{ \
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
-		.name = xname, \
-		.index = 0, \
-		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
-			SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
-			SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
-		.info = stac92xx_amp_volume_info, \
-		.get = stac92xx_amp_volume_get, \
-		.put = stac92xx_amp_volume_put, \
-		.tlv = { .c = snd_hda_mixer_amp_tlv }, \
-		.private_value = HDA_COMPOSE_AMP_VAL(nid, chs, idx, dir) \
-	}
-
 #define STAC_ANALOG_LOOPBACK(verb_read, verb_write, cnt) \
 #define STAC_ANALOG_LOOPBACK(verb_read, verb_write, cnt) \
 	{ \
 	{ \
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
@@ -1051,34 +967,6 @@ static struct snd_kcontrol_new stac9200_mixer[] = {
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
-#define DELL_M6_MIXER 6
-static struct snd_kcontrol_new stac92hd73xx_6ch_mixer[] = {
-	/* start of config #1 */
-	HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT),
-	HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT),
-
-	HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT),
-	HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT),
-
-	HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT),
-	HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT),
-
-	/* start of config #2 */
-	HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT),
-	HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT),
-
-	HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT),
-	HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT),
-
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
-
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT),
-
-	{ } /* end */
-};
-
 static struct snd_kcontrol_new stac92hd73xx_6ch_loopback[] = {
 static struct snd_kcontrol_new stac92hd73xx_6ch_loopback[] = {
 	STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3),
 	STAC_ANALOG_LOOPBACK(0xFA0, 0x7A1, 3),
 	{}
 	{}
@@ -1094,134 +982,14 @@ static struct snd_kcontrol_new stac92hd73xx_10ch_loopback[] = {
 	{}
 	{}
 };
 };
 
 
-static struct snd_kcontrol_new stac92hd73xx_8ch_mixer[] = {
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
-
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT),
-
-	HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT),
-	HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT),
-
-	HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT),
-	HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT),
-
-	HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT),
-	HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT),
-
-	HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT),
-	HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT),
-
-	HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT),
-	HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT),
-	{ } /* end */
-};
-
-static struct snd_kcontrol_new stac92hd73xx_10ch_mixer[] = {
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x20, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x20, 0x0, HDA_OUTPUT),
-
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x21, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x21, 0x0, HDA_OUTPUT),
-
-	HDA_CODEC_VOLUME("Front Mic Mixer Capture Volume", 0x1d, 0, HDA_INPUT),
-	HDA_CODEC_MUTE("Front Mic Mixer Capture Switch", 0x1d, 0, HDA_INPUT),
-
-	HDA_CODEC_VOLUME("Mic Mixer Capture Volume", 0x1d, 0x1, HDA_INPUT),
-	HDA_CODEC_MUTE("Mic Mixer Capture Switch", 0x1d, 0x1, HDA_INPUT),
-
-	HDA_CODEC_VOLUME("Line In Mixer Capture Volume", 0x1d, 0x2, HDA_INPUT),
-	HDA_CODEC_MUTE("Line In Mixer Capture Switch", 0x1d, 0x2, HDA_INPUT),
-
-	HDA_CODEC_VOLUME("DAC Mixer Capture Volume", 0x1d, 0x3, HDA_INPUT),
-	HDA_CODEC_MUTE("DAC Mixer Capture Switch", 0x1d, 0x3, HDA_INPUT),
-
-	HDA_CODEC_VOLUME("CD Mixer Capture Volume", 0x1d, 0x4, HDA_INPUT),
-	HDA_CODEC_MUTE("CD Mixer Capture Switch", 0x1d, 0x4, HDA_INPUT),
-	{ } /* end */
-};
-
-
-static struct snd_kcontrol_new stac92hd83xxx_mixer[] = {
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x17, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x17, 0x0, HDA_OUTPUT),
-
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x18, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x18, 0x0, HDA_OUTPUT),
-
-	HDA_CODEC_VOLUME("DAC0 Capture Volume", 0x1b, 0x3, HDA_INPUT),
-	HDA_CODEC_MUTE("DAC0 Capture Switch", 0x1b, 0x3, HDA_INPUT),
-
-	HDA_CODEC_VOLUME("DAC1 Capture Volume", 0x1b, 0x4, HDA_INPUT),
-	HDA_CODEC_MUTE("DAC1 Capture Switch", 0x1b, 0x4, HDA_INPUT),
-
-	HDA_CODEC_VOLUME("Front Mic Capture Volume", 0x1b, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Front Mic Capture Switch", 0x1b, 0x0, HDA_INPUT),
-
-	HDA_CODEC_VOLUME("Line In Capture Volume", 0x1b, 0x2, HDA_INPUT),
-	HDA_CODEC_MUTE("Line In Capture Switch", 0x1b, 0x2, HDA_INPUT),
-
-	/*
-	HDA_CODEC_VOLUME("Mic Capture Volume", 0x1b, 0x1, HDA_INPUT),
-	HDA_CODEC_MUTE("Mic Capture Switch", 0x1b 0x1, HDA_INPUT),
-	*/
-	{ } /* end */
-};
-
-static struct snd_kcontrol_new stac92hd71bxx_analog_mixer[] = {
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT),
-
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT),
-	/* analog pc-beep replaced with digital beep support */
-	/*
-	HDA_CODEC_VOLUME("PC Beep Volume", 0x17, 0x2, HDA_INPUT),
-	HDA_CODEC_MUTE("PC Beep Switch", 0x17, 0x2, HDA_INPUT),
-	*/
-
-	HDA_CODEC_MUTE("Import0 Mux Capture Switch", 0x17, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME("Import0 Mux Capture Volume", 0x17, 0x0, HDA_INPUT),
-
-	HDA_CODEC_MUTE("Import1 Mux Capture Switch", 0x17, 0x1, HDA_INPUT),
-	HDA_CODEC_VOLUME("Import1 Mux Capture Volume", 0x17, 0x1, HDA_INPUT),
-
-	HDA_CODEC_MUTE("DAC0 Capture Switch", 0x17, 0x3, HDA_INPUT),
-	HDA_CODEC_VOLUME("DAC0 Capture Volume", 0x17, 0x3, HDA_INPUT),
-
-	HDA_CODEC_MUTE("DAC1 Capture Switch", 0x17, 0x4, HDA_INPUT),
-	HDA_CODEC_VOLUME("DAC1 Capture Volume", 0x17, 0x4, HDA_INPUT),
-	{ } /* end */
-};
 
 
 static struct snd_kcontrol_new stac92hd71bxx_loopback[] = {
 static struct snd_kcontrol_new stac92hd71bxx_loopback[] = {
 	STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2)
 	STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2)
 };
 };
 
 
-static struct snd_kcontrol_new stac92hd71bxx_mixer[] = {
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT),
-
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1d, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1d, 0x0, HDA_OUTPUT),
-	{ } /* end */
-};
-
 static struct snd_kcontrol_new stac925x_mixer[] = {
 static struct snd_kcontrol_new stac925x_mixer[] = {
 	HDA_CODEC_VOLUME("Master Playback Volume", 0x0e, 0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Master Playback Volume", 0x0e, 0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Master Playback Switch", 0x0e, 0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Master Playback Switch", 0x0e, 0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_OUTPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x14, 0, HDA_OUTPUT),
-	{ } /* end */
-};
-
-static struct snd_kcontrol_new stac9205_mixer[] = {
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1b, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1d, 0x0, HDA_OUTPUT),
-
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x1c, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1e, 0x0, HDA_OUTPUT),
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
@@ -1230,29 +998,6 @@ static struct snd_kcontrol_new stac9205_loopback[] = {
 	{}
 	{}
 };
 };
 
 
-/* This needs to be generated dynamically based on sequence */
-static struct snd_kcontrol_new stac922x_mixer[] = {
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x17, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x17, 0x0, HDA_INPUT),
-
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x18, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x18, 0x0, HDA_INPUT),
-	{ } /* end */
-};
-
-
-static struct snd_kcontrol_new stac927x_mixer[] = {
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x18, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1b, 0x0, HDA_OUTPUT),
-
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x19, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 0x1, 0x1c, 0x0, HDA_OUTPUT),
-
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x2, 0x1A, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 0x2, 0x1d, 0x0, HDA_OUTPUT),
-	{ } /* end */
-};
-
 static struct snd_kcontrol_new stac927x_loopback[] = {
 static struct snd_kcontrol_new stac927x_loopback[] = {
 	STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1),
 	STAC_ANALOG_LOOPBACK(0xFEB, 0x7EB, 1),
 	{}
 	{}
@@ -1310,16 +1055,19 @@ static int stac92xx_build_controls(struct hda_codec *codec)
 	int err;
 	int err;
 	int i;
 	int i;
 
 
-	err = snd_hda_add_new_ctls(codec, spec->mixer);
-	if (err < 0)
-		return err;
+	if (spec->mixer) {
+		err = snd_hda_add_new_ctls(codec, spec->mixer);
+		if (err < 0)
+			return err;
+	}
 
 
 	for (i = 0; i < spec->num_mixers; i++) {
 	for (i = 0; i < spec->num_mixers; i++) {
 		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
 		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
 		if (err < 0)
 		if (err < 0)
 			return err;
 			return err;
 	}
 	}
-	if (spec->num_dmuxes > 0) {
+	if (!spec->auto_mic && spec->num_dmuxes > 0 &&
+	    snd_hda_get_bool_hint(codec, "separate_dmux") == 1) {
 		stac_dmux_mixer.count = spec->num_dmuxes;
 		stac_dmux_mixer.count = spec->num_dmuxes;
 		err = snd_hda_ctl_add(codec,
 		err = snd_hda_ctl_add(codec,
 				  snd_ctl_new1(&stac_dmux_mixer, codec));
 				  snd_ctl_new1(&stac_dmux_mixer, codec));
@@ -1766,12 +1514,20 @@ static unsigned int dell_m6_pin_configs[13] = {
 	0x4f0000f0,
 	0x4f0000f0,
 };
 };
 
 
+static unsigned int alienware_m17x_pin_configs[13] = {
+	0x0321101f, 0x0321101f, 0x03a11020, 0x03014020,
+	0x90170110, 0x4f0000f0, 0x4f0000f0, 0x4f0000f0,
+	0x4f0000f0, 0x90a60160, 0x4f0000f0, 0x4f0000f0,
+	0x904601b0,
+};
+
 static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = {
 static unsigned int *stac92hd73xx_brd_tbl[STAC_92HD73XX_MODELS] = {
 	[STAC_92HD73XX_REF]	= ref92hd73xx_pin_configs,
 	[STAC_92HD73XX_REF]	= ref92hd73xx_pin_configs,
 	[STAC_DELL_M6_AMIC]	= dell_m6_pin_configs,
 	[STAC_DELL_M6_AMIC]	= dell_m6_pin_configs,
 	[STAC_DELL_M6_DMIC]	= dell_m6_pin_configs,
 	[STAC_DELL_M6_DMIC]	= dell_m6_pin_configs,
 	[STAC_DELL_M6_BOTH]	= dell_m6_pin_configs,
 	[STAC_DELL_M6_BOTH]	= dell_m6_pin_configs,
 	[STAC_DELL_EQ]	= dell_m6_pin_configs,
 	[STAC_DELL_EQ]	= dell_m6_pin_configs,
+	[STAC_ALIENWARE_M17X]	= alienware_m17x_pin_configs,
 };
 };
 
 
 static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = {
 static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = {
@@ -1783,6 +1539,7 @@ static const char *stac92hd73xx_models[STAC_92HD73XX_MODELS] = {
 	[STAC_DELL_M6_DMIC] = "dell-m6-dmic",
 	[STAC_DELL_M6_DMIC] = "dell-m6-dmic",
 	[STAC_DELL_M6_BOTH] = "dell-m6",
 	[STAC_DELL_M6_BOTH] = "dell-m6",
 	[STAC_DELL_EQ] = "dell-eq",
 	[STAC_DELL_EQ] = "dell-eq",
+	[STAC_ALIENWARE_M17X] = "alienware",
 };
 };
 
 
 static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {
 static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {
@@ -1820,6 +1577,12 @@ static struct snd_pci_quirk stac92hd73xx_cfg_tbl[] = {
 	{} /* terminator */
 	{} /* terminator */
 };
 };
 
 
+static struct snd_pci_quirk stac92hd73xx_codec_id_cfg_tbl[] = {
+	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x02a1,
+		      "Alienware M17x", STAC_ALIENWARE_M17X),
+	{} /* terminator */
+};
+
 static unsigned int ref92hd83xxx_pin_configs[10] = {
 static unsigned int ref92hd83xxx_pin_configs[10] = {
 	0x02214030, 0x02211010, 0x02a19020, 0x02170130,
 	0x02214030, 0x02211010, 0x02a19020, 0x02170130,
 	0x01014050, 0x01819040, 0x01014020, 0x90a3014e,
 	0x01014050, 0x01819040, 0x01014020, 0x90a3014e,
@@ -1927,6 +1690,8 @@ static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
 		      "HP mini 1000", STAC_HP_M4),
 		      "HP mini 1000", STAC_HP_M4),
 	SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361b,
 	SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361b,
 		      "HP HDX", STAC_HP_HDX),  /* HDX16 */
 		      "HP HDX", STAC_HP_HDX),  /* HDX16 */
+	SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x7010,
+		      "HP", STAC_HP_DV5),
 	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233,
 	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233,
 				"unknown Dell", STAC_DELL_M4_1),
 				"unknown Dell", STAC_DELL_M4_1),
 	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0234,
 	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0234,
@@ -2642,8 +2407,7 @@ static int stac92xx_hp_switch_get(struct snd_kcontrol *kcontrol,
 	return 0;
 	return 0;
 }
 }
 
 
-static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid,
-				   unsigned char type);
+static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid);
 
 
 static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol,
 static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol,
 			struct snd_ctl_elem_value *ucontrol)
 			struct snd_ctl_elem_value *ucontrol)
@@ -2657,7 +2421,7 @@ static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol,
 	/* check to be sure that the ports are upto date with
 	/* check to be sure that the ports are upto date with
 	 * switch changes
 	 * switch changes
 	 */
 	 */
-	stac_issue_unsol_event(codec, nid, STAC_HP_EVENT);
+	stac_issue_unsol_event(codec, nid);
 
 
 	return 1;
 	return 1;
 }
 }
@@ -2790,7 +2554,7 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
 	 * appropriately according to the pin direction
 	 * appropriately according to the pin direction
 	 */
 	 */
 	if (spec->hp_detect)
 	if (spec->hp_detect)
-		stac_issue_unsol_event(codec, nid, STAC_HP_EVENT);
+		stac_issue_unsol_event(codec, nid);
 
 
         return 1;
         return 1;
 }
 }
@@ -2859,8 +2623,6 @@ enum {
 	STAC_CTL_WIDGET_VOL,
 	STAC_CTL_WIDGET_VOL,
 	STAC_CTL_WIDGET_MUTE,
 	STAC_CTL_WIDGET_MUTE,
 	STAC_CTL_WIDGET_MONO_MUX,
 	STAC_CTL_WIDGET_MONO_MUX,
-	STAC_CTL_WIDGET_AMP_MUX,
-	STAC_CTL_WIDGET_AMP_VOL,
 	STAC_CTL_WIDGET_HP_SWITCH,
 	STAC_CTL_WIDGET_HP_SWITCH,
 	STAC_CTL_WIDGET_IO_SWITCH,
 	STAC_CTL_WIDGET_IO_SWITCH,
 	STAC_CTL_WIDGET_CLFE_SWITCH,
 	STAC_CTL_WIDGET_CLFE_SWITCH,
@@ -2871,8 +2633,6 @@ static struct snd_kcontrol_new stac92xx_control_templates[] = {
 	HDA_CODEC_VOLUME(NULL, 0, 0, 0),
 	HDA_CODEC_VOLUME(NULL, 0, 0, 0),
 	HDA_CODEC_MUTE(NULL, 0, 0, 0),
 	HDA_CODEC_MUTE(NULL, 0, 0, 0),
 	STAC_MONO_MUX,
 	STAC_MONO_MUX,
-	STAC_AMP_MUX,
-	STAC_AMP_VOL(NULL, 0, 0, 0, 0),
 	STAC_CODEC_HP_SWITCH(NULL),
 	STAC_CODEC_HP_SWITCH(NULL),
 	STAC_CODEC_IO_SWITCH(NULL, 0),
 	STAC_CODEC_IO_SWITCH(NULL, 0),
 	STAC_CODEC_CLFE_SWITCH(NULL, 0),
 	STAC_CODEC_CLFE_SWITCH(NULL, 0),
@@ -2973,6 +2733,8 @@ static int stac92xx_add_input_source(struct sigmatel_spec *spec)
 	struct snd_kcontrol_new *knew;
 	struct snd_kcontrol_new *knew;
 	struct hda_input_mux *imux = &spec->private_imux;
 	struct hda_input_mux *imux = &spec->private_imux;
 
 
+	if (spec->auto_mic)
+		return 0; /* no need for input source */
 	if (!spec->num_adcs || imux->num_items <= 1)
 	if (!spec->num_adcs || imux->num_items <= 1)
 		return 0; /* no need for input source control */
 		return 0; /* no need for input source control */
 	knew = stac_control_new(spec, &stac_input_src_temp,
 	knew = stac_control_new(spec, &stac_input_src_temp,
@@ -3066,7 +2828,7 @@ static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid)
 					   HDA_MAX_CONNECTIONS);
 					   HDA_MAX_CONNECTIONS);
 	for (j = 0; j < conn_len; j++) {
 	for (j = 0; j < conn_len; j++) {
 		wcaps = get_wcaps(codec, conn[j]);
 		wcaps = get_wcaps(codec, conn[j]);
-		wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+		wtype = get_wcaps_type(wcaps);
 		/* we check only analog outputs */
 		/* we check only analog outputs */
 		if (wtype != AC_WID_AUD_OUT || (wcaps & AC_WCAP_DIGITAL))
 		if (wtype != AC_WID_AUD_OUT || (wcaps & AC_WCAP_DIGITAL))
 			continue;
 			continue;
@@ -3325,6 +3087,21 @@ static int create_multi_out_ctls(struct hda_codec *codec, int num_outs,
 	return 0;
 	return 0;
 }
 }
 
 
+static int stac92xx_add_capvol_ctls(struct hda_codec *codec, unsigned long vol,
+				    unsigned long sw, int idx)
+{
+	int err;
+	err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_VOL, idx,
+				       "Capture Volume", vol);
+	if (err < 0)
+		return err;
+	err = stac92xx_add_control_idx(codec->spec, STAC_CTL_WIDGET_MUTE, idx,
+				       "Capture Switch", sw);
+	if (err < 0)
+		return err;
+	return 0;
+}
+
 /* add playback controls from the parsed DAC table */
 /* add playback controls from the parsed DAC table */
 static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
 static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
 					       const struct auto_pin_cfg *cfg)
 					       const struct auto_pin_cfg *cfg)
@@ -3398,7 +3175,7 @@ static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec)
 				spec->mono_nid,
 				spec->mono_nid,
 				con_lst,
 				con_lst,
 				HDA_MAX_NUM_INPUTS);
 				HDA_MAX_NUM_INPUTS);
-	if (!num_cons || num_cons > ARRAY_SIZE(stac92xx_mono_labels))
+	if (num_cons <= 0 || num_cons > ARRAY_SIZE(stac92xx_mono_labels))
 		return -EINVAL;
 		return -EINVAL;
 
 
 	for (i = 0; i < num_cons; i++) {
 	for (i = 0; i < num_cons; i++) {
@@ -3412,37 +3189,6 @@ static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec)
 				"Mono Mux", spec->mono_nid);
 				"Mono Mux", spec->mono_nid);
 }
 }
 
 
-/* labels for amp mux outputs */
-static const char *stac92xx_amp_labels[3] = {
-	"Front Microphone", "Microphone", "Line In",
-};
-
-/* create amp out controls mux on capable codecs */
-static int stac92xx_auto_create_amp_output_ctls(struct hda_codec *codec)
-{
-	struct sigmatel_spec *spec = codec->spec;
-	struct hda_input_mux *amp_mux = &spec->private_amp_mux;
-	int i, err;
-
-	for (i = 0; i < spec->num_amps; i++) {
-		amp_mux->items[amp_mux->num_items].label =
-					stac92xx_amp_labels[i];
-		amp_mux->items[amp_mux->num_items].index = i;
-		amp_mux->num_items++;
-	}
-
-	if (spec->num_amps > 1) {
-		err = stac92xx_add_control(spec, STAC_CTL_WIDGET_AMP_MUX,
-			"Amp Selector Capture Switch", 0);
-		if (err < 0)
-			return err;
-	}
-	return stac92xx_add_control(spec, STAC_CTL_WIDGET_AMP_VOL,
-		"Amp Capture Volume",
-		HDA_COMPOSE_AMP_VAL(spec->amp_nids[0], 3, 0, HDA_INPUT));
-}
-
-
 /* create PC beep volume controls */
 /* create PC beep volume controls */
 static int stac92xx_auto_create_beep_ctls(struct hda_codec *codec,
 static int stac92xx_auto_create_beep_ctls(struct hda_codec *codec,
 						hda_nid_t nid)
 						hda_nid_t nid)
@@ -3511,19 +3257,33 @@ static int stac92xx_beep_switch_ctl(struct hda_codec *codec)
 static int stac92xx_auto_create_mux_input_ctls(struct hda_codec *codec)
 static int stac92xx_auto_create_mux_input_ctls(struct hda_codec *codec)
 {
 {
 	struct sigmatel_spec *spec = codec->spec;
 	struct sigmatel_spec *spec = codec->spec;
-	int wcaps, nid, i, err = 0;
+	int i, j, err = 0;
 
 
 	for (i = 0; i < spec->num_muxes; i++) {
 	for (i = 0; i < spec->num_muxes; i++) {
+		hda_nid_t nid;
+		unsigned int wcaps;
+		unsigned long val;
+
 		nid = spec->mux_nids[i];
 		nid = spec->mux_nids[i];
 		wcaps = get_wcaps(codec, nid);
 		wcaps = get_wcaps(codec, nid);
+		if (!(wcaps & AC_WCAP_OUT_AMP))
+			continue;
 
 
-		if (wcaps & AC_WCAP_OUT_AMP) {
-			err = stac92xx_add_control_idx(spec,
-				STAC_CTL_WIDGET_VOL, i, "Mux Capture Volume",
-				HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT));
-			if (err < 0)
-				return err;
+		/* check whether already the same control was created as
+		 * normal Capture Volume.
+		 */
+		val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT);
+		for (j = 0; j < spec->num_caps; j++) {
+			if (spec->capvols[j] == val)
+				break;
 		}
 		}
+		if (j < spec->num_caps)
+			continue;
+
+		err = stac92xx_add_control_idx(spec, STAC_CTL_WIDGET_VOL, i,
+					       "Mux Capture Volume", val);
+		if (err < 0)
+			return err;
 	}
 	}
 	return 0;
 	return 0;
 };
 };
@@ -3544,7 +3304,7 @@ static int stac92xx_auto_create_spdif_mux_ctls(struct hda_codec *codec)
 				spec->smux_nids[0],
 				spec->smux_nids[0],
 				con_lst,
 				con_lst,
 				HDA_MAX_NUM_INPUTS);
 				HDA_MAX_NUM_INPUTS);
-	if (!num_cons)
+	if (num_cons <= 0)
 		return -EINVAL;
 		return -EINVAL;
 
 
 	if (!labels)
 	if (!labels)
@@ -3565,101 +3325,231 @@ static const char *stac92xx_dmic_labels[5] = {
 	"Digital Mic 3", "Digital Mic 4"
 	"Digital Mic 3", "Digital Mic 4"
 };
 };
 
 
+static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
+				hda_nid_t nid)
+{
+	hda_nid_t conn[HDA_MAX_NUM_INPUTS];
+	int i, nums;
+
+	nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
+	for (i = 0; i < nums; i++)
+		if (conn[i] == nid)
+			return i;
+	return -1;
+}
+
+/* create a volume assigned to the given pin (only if supported) */
+/* return 1 if the volume control is created */
+static int create_elem_capture_vol(struct hda_codec *codec, hda_nid_t nid,
+				   const char *label, int direction)
+{
+	unsigned int caps, nums;
+	char name[32];
+	int err;
+
+	if (direction == HDA_OUTPUT)
+		caps = AC_WCAP_OUT_AMP;
+	else
+		caps = AC_WCAP_IN_AMP;
+	if (!(get_wcaps(codec, nid) & caps))
+		return 0;
+	caps = query_amp_caps(codec, nid, direction);
+	nums = (caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT;
+	if (!nums)
+		return 0;
+	snprintf(name, sizeof(name), "%s Capture Volume", label);
+	err = stac92xx_add_control(codec->spec, STAC_CTL_WIDGET_VOL, name,
+				    HDA_COMPOSE_AMP_VAL(nid, 3, 0, direction));
+	if (err < 0)
+		return err;
+	return 1;
+}
+
 /* create playback/capture controls for input pins on dmic capable codecs */
 /* create playback/capture controls for input pins on dmic capable codecs */
 static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
 static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec,
 						const struct auto_pin_cfg *cfg)
 						const struct auto_pin_cfg *cfg)
 {
 {
 	struct sigmatel_spec *spec = codec->spec;
 	struct sigmatel_spec *spec = codec->spec;
+	struct hda_input_mux *imux = &spec->private_imux;
 	struct hda_input_mux *dimux = &spec->private_dimux;
 	struct hda_input_mux *dimux = &spec->private_dimux;
-	hda_nid_t con_lst[HDA_MAX_NUM_INPUTS];
-	int err, i, j;
-	char name[32];
+	int err, i, active_mics;
+	unsigned int def_conf;
 
 
 	dimux->items[dimux->num_items].label = stac92xx_dmic_labels[0];
 	dimux->items[dimux->num_items].label = stac92xx_dmic_labels[0];
 	dimux->items[dimux->num_items].index = 0;
 	dimux->items[dimux->num_items].index = 0;
 	dimux->num_items++;
 	dimux->num_items++;
 
 
+	active_mics = 0;
+	for (i = 0; i < spec->num_dmics; i++) {
+		/* check the validity: sometimes it's a dead vendor-spec node */
+		if (get_wcaps_type(get_wcaps(codec, spec->dmic_nids[i]))
+		    != AC_WID_PIN)
+			continue;
+		def_conf = snd_hda_codec_get_pincfg(codec, spec->dmic_nids[i]);
+		if (get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)
+			active_mics++;
+	}
+
 	for (i = 0; i < spec->num_dmics; i++) {
 	for (i = 0; i < spec->num_dmics; i++) {
 		hda_nid_t nid;
 		hda_nid_t nid;
 		int index;
 		int index;
-		int num_cons;
-		unsigned int wcaps;
-		unsigned int def_conf;
+		const char *label;
 
 
-		def_conf = snd_hda_codec_get_pincfg(codec, spec->dmic_nids[i]);
+		nid = spec->dmic_nids[i];
+		if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
+			continue;
+		def_conf = snd_hda_codec_get_pincfg(codec, nid);
 		if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
 		if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
 			continue;
 			continue;
 
 
-		nid = spec->dmic_nids[i];
-		num_cons = snd_hda_get_connections(codec,
-				spec->dmux_nids[0],
-				con_lst,
-				HDA_MAX_NUM_INPUTS);
-		for (j = 0; j < num_cons; j++)
-			if (con_lst[j] == nid) {
-				index = j;
-				goto found;
-			}
-		continue;
-found:
-		wcaps = get_wcaps(codec, nid) &
-			(AC_WCAP_OUT_AMP | AC_WCAP_IN_AMP);
+		index = get_connection_index(codec, spec->dmux_nids[0], nid);
+		if (index < 0)
+			continue;
 
 
-		if (wcaps) {
-			sprintf(name, "%s Capture Volume",
-				stac92xx_dmic_labels[dimux->num_items]);
+		if (active_mics == 1)
+			label = "Digital Mic";
+		else
+			label = stac92xx_dmic_labels[dimux->num_items];
 
 
-			err = stac92xx_add_control(spec,
-				STAC_CTL_WIDGET_VOL,
-				name,
-				HDA_COMPOSE_AMP_VAL(nid, 3, 0,
-				(wcaps & AC_WCAP_OUT_AMP) ?
-				HDA_OUTPUT : HDA_INPUT));
+		err = create_elem_capture_vol(codec, nid, label, HDA_INPUT);
+		if (err < 0)
+			return err;
+		if (!err) {
+			err = create_elem_capture_vol(codec, nid, label,
+						      HDA_OUTPUT);
 			if (err < 0)
 			if (err < 0)
 				return err;
 				return err;
 		}
 		}
 
 
-		dimux->items[dimux->num_items].label =
-			stac92xx_dmic_labels[dimux->num_items];
+		dimux->items[dimux->num_items].label = label;
 		dimux->items[dimux->num_items].index = index;
 		dimux->items[dimux->num_items].index = index;
 		dimux->num_items++;
 		dimux->num_items++;
+		if (snd_hda_get_bool_hint(codec, "separate_dmux") != 1) {
+			imux->items[imux->num_items].label = label;
+			imux->items[imux->num_items].index = index;
+			imux->num_items++;
+		}
 	}
 	}
 
 
 	return 0;
 	return 0;
 }
 }
 
 
+static int check_mic_pin(struct hda_codec *codec, hda_nid_t nid,
+			 hda_nid_t *fixed, hda_nid_t *ext)
+{
+	unsigned int cfg;
+
+	if (!nid)
+		return 0;
+	cfg = snd_hda_codec_get_pincfg(codec, nid);
+	switch (get_defcfg_connect(cfg)) {
+	case AC_JACK_PORT_FIXED:
+		if (*fixed)
+			return 1; /* already occupied */
+		*fixed = nid;
+		break;
+	case AC_JACK_PORT_COMPLEX:
+		if (*ext)
+			return 1; /* already occupied */
+		*ext = nid;
+		break;
+	}
+	return 0;
+}
+
+static int set_mic_route(struct hda_codec *codec,
+			 struct sigmatel_mic_route *mic,
+			 hda_nid_t pin)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	int i;
+
+	mic->pin = pin;
+	for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++)
+		if (pin == cfg->input_pins[i])
+			break;
+	if (i <= AUTO_PIN_FRONT_MIC) {
+		/* analog pin */
+		mic->dmux_idx = 0;
+		i = get_connection_index(codec, spec->mux_nids[0], pin);
+		if (i < 0)
+			return -1;
+		mic->mux_idx = i;
+	}  else if (spec->dmux_nids) {
+		/* digital pin */
+		mic->mux_idx = 0;
+		i = get_connection_index(codec, spec->dmux_nids[0], pin);
+		if (i < 0)
+			return -1;
+		mic->dmux_idx = i;
+	}
+	return 0;
+}
+
+/* return non-zero if the device is for automatic mic switch */
+static int stac_check_auto_mic(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	hda_nid_t fixed, ext;
+	int i;
+
+	for (i = AUTO_PIN_LINE; i < AUTO_PIN_LAST; i++) {
+		if (cfg->input_pins[i])
+			return 0; /* must be exclusively mics */
+	}
+	fixed = ext = 0;
+	for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++)
+		if (check_mic_pin(codec, cfg->input_pins[i], &fixed, &ext))
+			return 0;
+	for (i = 0; i < spec->num_dmics; i++)
+		if (check_mic_pin(codec, spec->dmic_nids[i], &fixed, &ext))
+			return 0;
+	if (!fixed || !ext)
+		return 0;
+	if (!(get_wcaps(codec, ext) & AC_WCAP_UNSOL_CAP))
+		return 0; /* no unsol support */
+	if (set_mic_route(codec, &spec->ext_mic, ext) ||
+	    set_mic_route(codec, &spec->int_mic, fixed))
+		return 0; /* something is wrong */
+	return 1;
+}
+
 /* create playback/capture controls for input pins */
 /* create playback/capture controls for input pins */
 static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
 static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
 {
 {
 	struct sigmatel_spec *spec = codec->spec;
 	struct sigmatel_spec *spec = codec->spec;
 	struct hda_input_mux *imux = &spec->private_imux;
 	struct hda_input_mux *imux = &spec->private_imux;
-	hda_nid_t con_lst[HDA_MAX_NUM_INPUTS];
-	int i, j, k;
+	int i, j;
 
 
 	for (i = 0; i < AUTO_PIN_LAST; i++) {
 	for (i = 0; i < AUTO_PIN_LAST; i++) {
-		int index;
+		hda_nid_t nid = cfg->input_pins[i];
+		int index, err;
 
 
-		if (!cfg->input_pins[i])
+		if (!nid)
 			continue;
 			continue;
 		index = -1;
 		index = -1;
 		for (j = 0; j < spec->num_muxes; j++) {
 		for (j = 0; j < spec->num_muxes; j++) {
-			int num_cons;
-			num_cons = snd_hda_get_connections(codec,
-							   spec->mux_nids[j],
-							   con_lst,
-							   HDA_MAX_NUM_INPUTS);
-			for (k = 0; k < num_cons; k++)
-				if (con_lst[k] == cfg->input_pins[i]) {
-					index = k;
-					goto found;
-				}
+			index = get_connection_index(codec, spec->mux_nids[j],
+						     nid);
+			if (index >= 0)
+				break;
 		}
 		}
-		continue;
-	found:
+		if (index < 0)
+			continue;
+
+		err = create_elem_capture_vol(codec, nid,
+					      auto_pin_cfg_labels[i],
+					      HDA_INPUT);
+		if (err < 0)
+			return err;
+
 		imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
 		imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
 		imux->items[imux->num_items].index = index;
 		imux->items[imux->num_items].index = index;
 		imux->num_items++;
 		imux->num_items++;
 	}
 	}
+	spec->num_analog_muxes = imux->num_items;
 
 
 	if (imux->num_items) {
 	if (imux->num_items) {
 		/*
 		/*
@@ -3711,7 +3601,7 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
 {
 {
 	struct sigmatel_spec *spec = codec->spec;
 	struct sigmatel_spec *spec = codec->spec;
 	int hp_swap = 0;
 	int hp_swap = 0;
-	int err;
+	int i, err;
 
 
 	if ((err = snd_hda_parse_pin_def_config(codec,
 	if ((err = snd_hda_parse_pin_def_config(codec,
 						&spec->autocfg,
 						&spec->autocfg,
@@ -3751,11 +3641,10 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
 		if (snd_hda_get_connections(codec,
 		if (snd_hda_get_connections(codec,
 				spec->autocfg.mono_out_pin, conn_list, 1) &&
 				spec->autocfg.mono_out_pin, conn_list, 1) &&
 				snd_hda_get_connections(codec, conn_list[0],
 				snd_hda_get_connections(codec, conn_list[0],
-				conn_list, 1)) {
+				conn_list, 1) > 0) {
 
 
 				int wcaps = get_wcaps(codec, conn_list[0]);
 				int wcaps = get_wcaps(codec, conn_list[0]);
-				int wid_type = (wcaps & AC_WCAP_TYPE)
-					>> AC_WCAP_TYPE_SHIFT;
+				int wid_type = get_wcaps_type(wcaps);
 				/* LR swap check, some stac925x have a mux that
 				/* LR swap check, some stac925x have a mux that
  				 * changes the DACs output path instead of the
  				 * changes the DACs output path instead of the
  				 * mono-mux path.
  				 * mono-mux path.
@@ -3846,6 +3735,21 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
 		spec->autocfg.line_outs = 0;
 		spec->autocfg.line_outs = 0;
 	}
 	}
 
 
+	if (stac_check_auto_mic(codec)) {
+		spec->auto_mic = 1;
+		/* only one capture for auto-mic */
+		spec->num_adcs = 1;
+		spec->num_caps = 1;
+		spec->num_muxes = 1;
+	}
+
+	for (i = 0; i < spec->num_caps; i++) {
+		err = stac92xx_add_capvol_ctls(codec, spec->capvols[i],
+					       spec->capsws[i], i);
+		if (err < 0)
+			return err;
+	}
+
 	err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg);
 	err = stac92xx_auto_create_analog_input_ctls(codec, &spec->autocfg);
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
@@ -3855,11 +3759,6 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
 		if (err < 0)
 		if (err < 0)
 			return err;
 			return err;
 	}
 	}
-	if (spec->num_amps > 0) {
-		err = stac92xx_auto_create_amp_output_ctls(codec);
-		if (err < 0)
-			return err;
-	}
 	if (spec->num_dmics > 0 && !spec->dinput_mux)
 	if (spec->num_dmics > 0 && !spec->dinput_mux)
 		if ((err = stac92xx_auto_create_dmic_input_ctls(codec,
 		if ((err = stac92xx_auto_create_dmic_input_ctls(codec,
 						&spec->autocfg)) < 0)
 						&spec->autocfg)) < 0)
@@ -3896,7 +3795,6 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
 		spec->dinput_mux = &spec->private_dimux;
 		spec->dinput_mux = &spec->private_dimux;
 	spec->sinput_mux = &spec->private_smux;
 	spec->sinput_mux = &spec->private_smux;
 	spec->mono_mux = &spec->private_mono_mux;
 	spec->mono_mux = &spec->private_mono_mux;
-	spec->amp_mux = &spec->private_amp_mux;
 	return 1;
 	return 1;
 }
 }
 
 
@@ -4108,14 +4006,14 @@ static int stac_add_event(struct sigmatel_spec *spec, hda_nid_t nid,
 }
 }
 
 
 static struct sigmatel_event *stac_get_event(struct hda_codec *codec,
 static struct sigmatel_event *stac_get_event(struct hda_codec *codec,
-					     hda_nid_t nid, unsigned char type)
+					     hda_nid_t nid)
 {
 {
 	struct sigmatel_spec *spec = codec->spec;
 	struct sigmatel_spec *spec = codec->spec;
 	struct sigmatel_event *event = spec->events.list;
 	struct sigmatel_event *event = spec->events.list;
 	int i;
 	int i;
 
 
 	for (i = 0; i < spec->events.used; i++, event++) {
 	for (i = 0; i < spec->events.used; i++, event++) {
-		if (event->nid == nid && event->type == type)
+		if (event->nid == nid)
 			return event;
 			return event;
 	}
 	}
 	return NULL;
 	return NULL;
@@ -4135,24 +4033,32 @@ static struct sigmatel_event *stac_get_event_from_tag(struct hda_codec *codec,
 	return NULL;
 	return NULL;
 }
 }
 
 
-static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid,
-			      unsigned int type)
+/* check if given nid is a valid pin and no other events are assigned
+ * to it.  If OK, assign the event, set the unsol flag, and returns 1.
+ * Otherwise, returns zero.
+ */
+static int enable_pin_detect(struct hda_codec *codec, hda_nid_t nid,
+			     unsigned int type)
 {
 {
 	struct sigmatel_event *event;
 	struct sigmatel_event *event;
 	int tag;
 	int tag;
 
 
 	if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP))
 	if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP))
-		return;
-	event = stac_get_event(codec, nid, type);
-	if (event)
+		return 0;
+	event = stac_get_event(codec, nid);
+	if (event) {
+		if (event->type != type)
+			return 0;
 		tag = event->tag;
 		tag = event->tag;
-	else
+	} else {
 		tag = stac_add_event(codec->spec, nid, type, 0);
 		tag = stac_add_event(codec->spec, nid, type, 0);
-	if (tag < 0)
-		return;
+		if (tag < 0)
+			return 0;
+	}
 	snd_hda_codec_write_cache(codec, nid, 0,
 	snd_hda_codec_write_cache(codec, nid, 0,
 				  AC_VERB_SET_UNSOLICITED_ENABLE,
 				  AC_VERB_SET_UNSOLICITED_ENABLE,
 				  AC_USRSP_EN | tag);
 				  AC_USRSP_EN | tag);
+	return 1;
 }
 }
 
 
 static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid)
 static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid)
@@ -4245,20 +4151,36 @@ static int stac92xx_init(struct hda_codec *codec)
 			hda_nid_t nid = cfg->hp_pins[i];
 			hda_nid_t nid = cfg->hp_pins[i];
 			enable_pin_detect(codec, nid, STAC_HP_EVENT);
 			enable_pin_detect(codec, nid, STAC_HP_EVENT);
 		}
 		}
+		if (cfg->line_out_type == AUTO_PIN_LINE_OUT &&
+		    cfg->speaker_outs > 0) {
+			/* enable pin-detect for line-outs as well */
+			for (i = 0; i < cfg->line_outs; i++) {
+				hda_nid_t nid = cfg->line_out_pins[i];
+				enable_pin_detect(codec, nid, STAC_LO_EVENT);
+			}
+		}
+
 		/* force to enable the first line-out; the others are set up
 		/* force to enable the first line-out; the others are set up
 		 * in unsol_event
 		 * in unsol_event
 		 */
 		 */
 		stac92xx_auto_set_pinctl(codec, spec->autocfg.line_out_pins[0],
 		stac92xx_auto_set_pinctl(codec, spec->autocfg.line_out_pins[0],
 				AC_PINCTL_OUT_EN);
 				AC_PINCTL_OUT_EN);
 		/* fake event to set up pins */
 		/* fake event to set up pins */
-		stac_issue_unsol_event(codec, spec->autocfg.hp_pins[0],
-				       STAC_HP_EVENT);
+		stac_issue_unsol_event(codec, spec->autocfg.hp_pins[0]);
 	} else {
 	} else {
 		stac92xx_auto_init_multi_out(codec);
 		stac92xx_auto_init_multi_out(codec);
 		stac92xx_auto_init_hp_out(codec);
 		stac92xx_auto_init_hp_out(codec);
 		for (i = 0; i < cfg->hp_outs; i++)
 		for (i = 0; i < cfg->hp_outs; i++)
 			stac_toggle_power_map(codec, cfg->hp_pins[i], 1);
 			stac_toggle_power_map(codec, cfg->hp_pins[i], 1);
 	}
 	}
+	if (spec->auto_mic) {
+		/* initialize connection to analog input */
+		if (spec->dmux_nids)
+			snd_hda_codec_write_cache(codec, spec->dmux_nids[0], 0,
+					  AC_VERB_SET_CONNECT_SEL, 0);
+		if (enable_pin_detect(codec, spec->ext_mic.pin, STAC_MIC_EVENT))
+			stac_issue_unsol_event(codec, spec->ext_mic.pin);
+	}
 	for (i = 0; i < AUTO_PIN_LAST; i++) {
 	for (i = 0; i < AUTO_PIN_LAST; i++) {
 		hda_nid_t nid = cfg->input_pins[i];
 		hda_nid_t nid = cfg->input_pins[i];
 		if (nid) {
 		if (nid) {
@@ -4285,10 +4207,9 @@ static int stac92xx_init(struct hda_codec *codec)
 			}
 			}
 			conf = snd_hda_codec_get_pincfg(codec, nid);
 			conf = snd_hda_codec_get_pincfg(codec, nid);
 			if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) {
 			if (get_defcfg_connect(conf) != AC_JACK_PORT_FIXED) {
-				enable_pin_detect(codec, nid,
-						  STAC_INSERT_EVENT);
-				stac_issue_unsol_event(codec, nid,
-						       STAC_INSERT_EVENT);
+				if (enable_pin_detect(codec, nid,
+						      STAC_INSERT_EVENT))
+					stac_issue_unsol_event(codec, nid);
 			}
 			}
 		}
 		}
 	}
 	}
@@ -4333,10 +4254,8 @@ static int stac92xx_init(struct hda_codec *codec)
 				stac_toggle_power_map(codec, nid, 1);
 				stac_toggle_power_map(codec, nid, 1);
 			continue;
 			continue;
 		}
 		}
-		if (!stac_get_event(codec, nid, STAC_INSERT_EVENT)) {
-			enable_pin_detect(codec, nid, STAC_PWR_EVENT);
-			stac_issue_unsol_event(codec, nid, STAC_PWR_EVENT);
-		}
+		if (enable_pin_detect(codec, nid, STAC_PWR_EVENT))
+			stac_issue_unsol_event(codec, nid);
 	}
 	}
 	if (spec->dac_list)
 	if (spec->dac_list)
 		stac92xx_power_down(codec);
 		stac92xx_power_down(codec);
@@ -4440,6 +4359,48 @@ static int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
 	return 0;
 	return 0;
 }
 }
 
 
+static void stac92xx_line_out_detect(struct hda_codec *codec,
+				     int presence)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	int i;
+
+	for (i = 0; i < cfg->line_outs; i++) {
+		if (presence)
+			break;
+		presence = get_pin_presence(codec, cfg->line_out_pins[i]);
+		if (presence) {
+			unsigned int pinctl;
+			pinctl = snd_hda_codec_read(codec,
+						    cfg->line_out_pins[i], 0,
+					    AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+			if (pinctl & AC_PINCTL_IN_EN)
+				presence = 0; /* mic- or line-input */
+		}
+	}
+
+	if (presence) {
+		/* disable speakers */
+		for (i = 0; i < cfg->speaker_outs; i++)
+			stac92xx_reset_pinctl(codec, cfg->speaker_pins[i],
+						AC_PINCTL_OUT_EN);
+		if (spec->eapd_mask && spec->eapd_switch)
+			stac_gpio_set(codec, spec->gpio_mask,
+				spec->gpio_dir, spec->gpio_data &
+				~spec->eapd_mask);
+	} else {
+		/* enable speakers */
+		for (i = 0; i < cfg->speaker_outs; i++)
+			stac92xx_set_pinctl(codec, cfg->speaker_pins[i],
+						AC_PINCTL_OUT_EN);
+		if (spec->eapd_mask && spec->eapd_switch)
+			stac_gpio_set(codec, spec->gpio_mask,
+				spec->gpio_dir, spec->gpio_data |
+				spec->eapd_mask);
+	}
+} 
+
 /* return non-zero if the hp-pin of the given array index isn't
 /* return non-zero if the hp-pin of the given array index isn't
  * a jack-detection target
  * a jack-detection target
  */
  */
@@ -4492,13 +4453,6 @@ static void stac92xx_hp_detect(struct hda_codec *codec)
 		for (i = 0; i < cfg->line_outs; i++)
 		for (i = 0; i < cfg->line_outs; i++)
 			stac92xx_reset_pinctl(codec, cfg->line_out_pins[i],
 			stac92xx_reset_pinctl(codec, cfg->line_out_pins[i],
 						AC_PINCTL_OUT_EN);
 						AC_PINCTL_OUT_EN);
-		for (i = 0; i < cfg->speaker_outs; i++)
-			stac92xx_reset_pinctl(codec, cfg->speaker_pins[i],
-						AC_PINCTL_OUT_EN);
-		if (spec->eapd_mask && spec->eapd_switch)
-			stac_gpio_set(codec, spec->gpio_mask,
-				spec->gpio_dir, spec->gpio_data &
-				~spec->eapd_mask);
 	} else {
 	} else {
 		/* enable lineouts */
 		/* enable lineouts */
 		if (spec->hp_switch)
 		if (spec->hp_switch)
@@ -4507,14 +4461,8 @@ static void stac92xx_hp_detect(struct hda_codec *codec)
 		for (i = 0; i < cfg->line_outs; i++)
 		for (i = 0; i < cfg->line_outs; i++)
 			stac92xx_set_pinctl(codec, cfg->line_out_pins[i],
 			stac92xx_set_pinctl(codec, cfg->line_out_pins[i],
 						AC_PINCTL_OUT_EN);
 						AC_PINCTL_OUT_EN);
-		for (i = 0; i < cfg->speaker_outs; i++)
-			stac92xx_set_pinctl(codec, cfg->speaker_pins[i],
-						AC_PINCTL_OUT_EN);
-		if (spec->eapd_mask && spec->eapd_switch)
-			stac_gpio_set(codec, spec->gpio_mask,
-				spec->gpio_dir, spec->gpio_data |
-				spec->eapd_mask);
 	}
 	}
+	stac92xx_line_out_detect(codec, presence);
 	/* toggle hp outs */
 	/* toggle hp outs */
 	for (i = 0; i < cfg->hp_outs; i++) {
 	for (i = 0; i < cfg->hp_outs; i++) {
 		unsigned int val = AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN;
 		unsigned int val = AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN;
@@ -4599,10 +4547,28 @@ static void stac92xx_report_jack(struct hda_codec *codec, hda_nid_t nid)
 	}
 	}
 }
 }
 
 
-static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid,
-				   unsigned char type)
+static void stac92xx_mic_detect(struct hda_codec *codec)
 {
 {
-	struct sigmatel_event *event = stac_get_event(codec, nid, type);
+	struct sigmatel_spec *spec = codec->spec;
+	struct sigmatel_mic_route *mic;
+
+	if (get_pin_presence(codec, spec->ext_mic.pin))
+		mic = &spec->ext_mic;
+	else
+		mic = &spec->int_mic;
+	if (mic->dmux_idx)
+		snd_hda_codec_write_cache(codec, spec->dmux_nids[0], 0,
+					  AC_VERB_SET_CONNECT_SEL,
+					  mic->dmux_idx);
+	else
+		snd_hda_codec_write_cache(codec, spec->mux_nids[0], 0,
+					  AC_VERB_SET_CONNECT_SEL,
+					  mic->mux_idx);
+}
+
+static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid)
+{
+	struct sigmatel_event *event = stac_get_event(codec, nid);
 	if (!event)
 	if (!event)
 		return;
 		return;
 	codec->patch_ops.unsol_event(codec, (unsigned)event->tag << 26);
 	codec->patch_ops.unsol_event(codec, (unsigned)event->tag << 26);
@@ -4621,8 +4587,18 @@ static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
 
 
 	switch (event->type) {
 	switch (event->type) {
 	case STAC_HP_EVENT:
 	case STAC_HP_EVENT:
+	case STAC_LO_EVENT:
 		stac92xx_hp_detect(codec);
 		stac92xx_hp_detect(codec);
-		/* fallthru */
+		break;
+	case STAC_MIC_EVENT:
+		stac92xx_mic_detect(codec);
+		break;
+	}
+
+	switch (event->type) {
+	case STAC_HP_EVENT:
+	case STAC_LO_EVENT:
+	case STAC_MIC_EVENT:
 	case STAC_INSERT_EVENT:
 	case STAC_INSERT_EVENT:
 	case STAC_PWR_EVENT:
 	case STAC_PWR_EVENT:
 		if (spec->num_pwrs > 0)
 		if (spec->num_pwrs > 0)
@@ -4713,8 +4689,7 @@ static int stac92xx_resume(struct hda_codec *codec)
 	snd_hda_codec_resume_cache(codec);
 	snd_hda_codec_resume_cache(codec);
 	/* fake event to set up pins again to override cached values */
 	/* fake event to set up pins again to override cached values */
 	if (spec->hp_detect)
 	if (spec->hp_detect)
-		stac_issue_unsol_event(codec, spec->autocfg.hp_pins[0],
-				       STAC_HP_EVENT);
+		stac_issue_unsol_event(codec, spec->autocfg.hp_pins[0]);
 	return 0;
 	return 0;
 }
 }
 
 
@@ -4754,6 +4729,19 @@ static int stac92xx_hp_check_power_status(struct hda_codec *codec,
 static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
 static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
 {
 {
 	struct sigmatel_spec *spec = codec->spec;
 	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)
 	if (spec->eapd_mask)
 		stac_gpio_set(codec, spec->gpio_mask,
 		stac_gpio_set(codec, spec->gpio_mask,
 				spec->gpio_dir, spec->gpio_data &
 				spec->gpio_dir, spec->gpio_data &
@@ -4790,7 +4778,8 @@ static int patch_stac9200(struct hda_codec *codec)
 							stac9200_models,
 							stac9200_models,
 							stac9200_cfg_tbl);
 							stac9200_cfg_tbl);
 	if (spec->board_config < 0)
 	if (spec->board_config < 0)
-		snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9200, using BIOS defaults\n");
+		snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+			    codec->chip_name);
 	else
 	else
 		stac92xx_set_config_regs(codec,
 		stac92xx_set_config_regs(codec,
 					 stac9200_brd_tbl[spec->board_config]);
 					 stac9200_brd_tbl[spec->board_config]);
@@ -4862,8 +4851,8 @@ static int patch_stac925x(struct hda_codec *codec)
 							stac925x_cfg_tbl);
 							stac925x_cfg_tbl);
  again:
  again:
 	if (spec->board_config < 0)
 	if (spec->board_config < 0)
-		snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC925x,"
-				      "using BIOS defaults\n");
+		snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+			    codec->chip_name);
 	else
 	else
 		stac92xx_set_config_regs(codec,
 		stac92xx_set_config_regs(codec,
 					 stac925x_brd_tbl[spec->board_config]);
 					 stac925x_brd_tbl[spec->board_config]);
@@ -4893,6 +4882,9 @@ static int patch_stac925x(struct hda_codec *codec)
 
 
 	spec->init = stac925x_core_init;
 	spec->init = stac925x_core_init;
 	spec->mixer = stac925x_mixer;
 	spec->mixer = stac925x_mixer;
+	spec->num_caps = 1;
+	spec->capvols = stac925x_capvols;
+	spec->capsws = stac925x_capsws;
 
 
 	err = stac92xx_parse_auto_config(codec, 0x8, 0x7);
 	err = stac92xx_parse_auto_config(codec, 0x8, 0x7);
 	if (!err) {
 	if (!err) {
@@ -4914,16 +4906,6 @@ static int patch_stac925x(struct hda_codec *codec)
 	return 0;
 	return 0;
 }
 }
 
 
-static struct hda_input_mux stac92hd73xx_dmux = {
-	.num_items = 4,
-	.items = {
-		{ "Analog Inputs", 0x0b },
-		{ "Digital Mic 1", 0x09 },
-		{ "Digital Mic 2", 0x0a },
-		{ "CD", 0x08 },
-	}
-};
-
 static int patch_stac92hd73xx(struct hda_codec *codec)
 static int patch_stac92hd73xx(struct hda_codec *codec)
 {
 {
 	struct sigmatel_spec *spec;
 	struct sigmatel_spec *spec;
@@ -4943,10 +4925,16 @@ static int patch_stac92hd73xx(struct hda_codec *codec)
 							STAC_92HD73XX_MODELS,
 							STAC_92HD73XX_MODELS,
 							stac92hd73xx_models,
 							stac92hd73xx_models,
 							stac92hd73xx_cfg_tbl);
 							stac92hd73xx_cfg_tbl);
+	/* check codec subsystem id if not found */
+	if (spec->board_config < 0)
+		spec->board_config =
+			snd_hda_check_board_codec_sid_config(codec,
+				STAC_92HD73XX_MODELS, stac92hd73xx_models,
+				stac92hd73xx_codec_id_cfg_tbl);
 again:
 again:
 	if (spec->board_config < 0)
 	if (spec->board_config < 0)
-		snd_printdd(KERN_INFO "hda_codec: Unknown model for"
-			" STAC92HD73XX, using BIOS defaults\n");
+		snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+			    codec->chip_name);
 	else
 	else
 		stac92xx_set_config_regs(codec,
 		stac92xx_set_config_regs(codec,
 				stac92hd73xx_brd_tbl[spec->board_config]);
 				stac92hd73xx_brd_tbl[spec->board_config]);
@@ -4959,20 +4947,15 @@ again:
 		       "number of channels defaulting to DAC count\n");
 		       "number of channels defaulting to DAC count\n");
 		num_dacs = STAC92HD73_DAC_COUNT;
 		num_dacs = STAC92HD73_DAC_COUNT;
 	}
 	}
+	spec->init = stac92hd73xx_core_init;
 	switch (num_dacs) {
 	switch (num_dacs) {
 	case 0x3: /* 6 Channel */
 	case 0x3: /* 6 Channel */
-		spec->mixer = stac92hd73xx_6ch_mixer;
-		spec->init = stac92hd73xx_6ch_core_init;
 		spec->aloopback_ctl = stac92hd73xx_6ch_loopback;
 		spec->aloopback_ctl = stac92hd73xx_6ch_loopback;
 		break;
 		break;
 	case 0x4: /* 8 Channel */
 	case 0x4: /* 8 Channel */
-		spec->mixer = stac92hd73xx_8ch_mixer;
-		spec->init = stac92hd73xx_8ch_core_init;
 		spec->aloopback_ctl = stac92hd73xx_8ch_loopback;
 		spec->aloopback_ctl = stac92hd73xx_8ch_loopback;
 		break;
 		break;
 	case 0x5: /* 10 Channel */
 	case 0x5: /* 10 Channel */
-		spec->mixer = stac92hd73xx_10ch_mixer;
-		spec->init = stac92hd73xx_10ch_core_init;
 		spec->aloopback_ctl = stac92hd73xx_10ch_loopback;
 		spec->aloopback_ctl = stac92hd73xx_10ch_loopback;
 		break;
 		break;
 	}
 	}
@@ -4987,14 +4970,14 @@ again:
 	spec->dmic_nids = stac92hd73xx_dmic_nids;
 	spec->dmic_nids = stac92hd73xx_dmic_nids;
 	spec->dmux_nids = stac92hd73xx_dmux_nids;
 	spec->dmux_nids = stac92hd73xx_dmux_nids;
 	spec->smux_nids = stac92hd73xx_smux_nids;
 	spec->smux_nids = stac92hd73xx_smux_nids;
-	spec->amp_nids = stac92hd73xx_amp_nids;
-	spec->num_amps = ARRAY_SIZE(stac92hd73xx_amp_nids);
 
 
 	spec->num_muxes = ARRAY_SIZE(stac92hd73xx_mux_nids);
 	spec->num_muxes = ARRAY_SIZE(stac92hd73xx_mux_nids);
 	spec->num_adcs = ARRAY_SIZE(stac92hd73xx_adc_nids);
 	spec->num_adcs = ARRAY_SIZE(stac92hd73xx_adc_nids);
 	spec->num_dmuxes = ARRAY_SIZE(stac92hd73xx_dmux_nids);
 	spec->num_dmuxes = ARRAY_SIZE(stac92hd73xx_dmux_nids);
-	memcpy(&spec->private_dimux, &stac92hd73xx_dmux,
-			sizeof(stac92hd73xx_dmux));
+
+	spec->num_caps = STAC92HD73XX_NUM_CAPS;
+	spec->capvols = stac92hd73xx_capvols;
+	spec->capsws = stac92hd73xx_capsws;
 
 
 	switch (spec->board_config) {
 	switch (spec->board_config) {
 	case STAC_DELL_EQ:
 	case STAC_DELL_EQ:
@@ -5004,43 +4987,40 @@ again:
 	case STAC_DELL_M6_DMIC:
 	case STAC_DELL_M6_DMIC:
 	case STAC_DELL_M6_BOTH:
 	case STAC_DELL_M6_BOTH:
 		spec->num_smuxes = 0;
 		spec->num_smuxes = 0;
-		spec->mixer = &stac92hd73xx_6ch_mixer[DELL_M6_MIXER];
-		spec->amp_nids = &stac92hd73xx_amp_nids[DELL_M6_AMP];
 		spec->eapd_switch = 0;
 		spec->eapd_switch = 0;
-		spec->num_amps = 1;
 
 
-		if (spec->board_config != STAC_DELL_EQ)
-			spec->init = dell_m6_core_init;
 		switch (spec->board_config) {
 		switch (spec->board_config) {
 		case STAC_DELL_M6_AMIC: /* Analog Mics */
 		case STAC_DELL_M6_AMIC: /* Analog Mics */
 			snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170);
 			snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170);
 			spec->num_dmics = 0;
 			spec->num_dmics = 0;
-			spec->private_dimux.num_items = 1;
 			break;
 			break;
 		case STAC_DELL_M6_DMIC: /* Digital Mics */
 		case STAC_DELL_M6_DMIC: /* Digital Mics */
 			snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160);
 			snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160);
 			spec->num_dmics = 1;
 			spec->num_dmics = 1;
-			spec->private_dimux.num_items = 2;
 			break;
 			break;
 		case STAC_DELL_M6_BOTH: /* Both */
 		case STAC_DELL_M6_BOTH: /* Both */
 			snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170);
 			snd_hda_codec_set_pincfg(codec, 0x0b, 0x90A70170);
 			snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160);
 			snd_hda_codec_set_pincfg(codec, 0x13, 0x90A60160);
 			spec->num_dmics = 1;
 			spec->num_dmics = 1;
-			spec->private_dimux.num_items = 2;
 			break;
 			break;
 		}
 		}
 		break;
 		break;
+	case STAC_ALIENWARE_M17X:
+		spec->num_dmics = STAC92HD73XX_NUM_DMICS;
+		spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids);
+		spec->eapd_switch = 0;
+		break;
 	default:
 	default:
 		spec->num_dmics = STAC92HD73XX_NUM_DMICS;
 		spec->num_dmics = STAC92HD73XX_NUM_DMICS;
 		spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids);
 		spec->num_smuxes = ARRAY_SIZE(stac92hd73xx_smux_nids);
 		spec->eapd_switch = 1;
 		spec->eapd_switch = 1;
+		break;
 	}
 	}
 	if (spec->board_config > STAC_92HD73XX_REF) {
 	if (spec->board_config > STAC_92HD73XX_REF) {
 		/* GPIO0 High = Enable EAPD */
 		/* GPIO0 High = Enable EAPD */
 		spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
 		spec->eapd_mask = spec->gpio_mask = spec->gpio_dir = 0x1;
 		spec->gpio_data = 0x01;
 		spec->gpio_data = 0x01;
 	}
 	}
-	spec->dinput_mux = &spec->private_dimux;
 
 
 	spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids);
 	spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids);
 	spec->pwr_nids = stac92hd73xx_pwr_nids;
 	spec->pwr_nids = stac92hd73xx_pwr_nids;
@@ -5072,15 +5052,6 @@ again:
 	return 0;
 	return 0;
 }
 }
 
 
-static struct hda_input_mux stac92hd83xxx_dmux = {
-	.num_items = 3,
-	.items = {
-		{ "Analog Inputs", 0x03 },
-		{ "Digital Mic 1", 0x04 },
-		{ "Digital Mic 2", 0x05 },
-	}
-};
-
 static int patch_stac92hd83xxx(struct hda_codec *codec)
 static int patch_stac92hd83xxx(struct hda_codec *codec)
 {
 {
 	struct sigmatel_spec *spec;
 	struct sigmatel_spec *spec;
@@ -5097,32 +5068,30 @@ static int patch_stac92hd83xxx(struct hda_codec *codec)
 	codec->slave_dig_outs = stac92hd83xxx_slave_dig_outs;
 	codec->slave_dig_outs = stac92hd83xxx_slave_dig_outs;
 	spec->mono_nid = 0x19;
 	spec->mono_nid = 0x19;
 	spec->digbeep_nid = 0x21;
 	spec->digbeep_nid = 0x21;
-	spec->dmic_nids = stac92hd83xxx_dmic_nids;
-	spec->dmux_nids = stac92hd83xxx_dmux_nids;
+	spec->mux_nids = stac92hd83xxx_mux_nids;
+	spec->num_muxes = ARRAY_SIZE(stac92hd83xxx_mux_nids);
 	spec->adc_nids = stac92hd83xxx_adc_nids;
 	spec->adc_nids = stac92hd83xxx_adc_nids;
+	spec->num_adcs = ARRAY_SIZE(stac92hd83xxx_adc_nids);
 	spec->pwr_nids = stac92hd83xxx_pwr_nids;
 	spec->pwr_nids = stac92hd83xxx_pwr_nids;
-	spec->amp_nids = stac92hd83xxx_amp_nids;
 	spec->pwr_mapping = stac92hd83xxx_pwr_mapping;
 	spec->pwr_mapping = stac92hd83xxx_pwr_mapping;
 	spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids);
 	spec->num_pwrs = ARRAY_SIZE(stac92hd83xxx_pwr_nids);
 	spec->multiout.dac_nids = spec->dac_nids;
 	spec->multiout.dac_nids = spec->dac_nids;
 
 
 	spec->init = stac92hd83xxx_core_init;
 	spec->init = stac92hd83xxx_core_init;
-	spec->mixer = stac92hd83xxx_mixer;
 	spec->num_pins = ARRAY_SIZE(stac92hd83xxx_pin_nids);
 	spec->num_pins = ARRAY_SIZE(stac92hd83xxx_pin_nids);
-	spec->num_dmuxes = ARRAY_SIZE(stac92hd83xxx_dmux_nids);
-	spec->num_adcs = ARRAY_SIZE(stac92hd83xxx_adc_nids);
-	spec->num_amps = ARRAY_SIZE(stac92hd83xxx_amp_nids);
-	spec->num_dmics = STAC92HD83XXX_NUM_DMICS;
-	spec->dinput_mux = &stac92hd83xxx_dmux;
 	spec->pin_nids = stac92hd83xxx_pin_nids;
 	spec->pin_nids = stac92hd83xxx_pin_nids;
+	spec->num_caps = STAC92HD83XXX_NUM_CAPS;
+	spec->capvols = stac92hd83xxx_capvols;
+	spec->capsws = stac92hd83xxx_capsws;
+
 	spec->board_config = snd_hda_check_board_config(codec,
 	spec->board_config = snd_hda_check_board_config(codec,
 							STAC_92HD83XXX_MODELS,
 							STAC_92HD83XXX_MODELS,
 							stac92hd83xxx_models,
 							stac92hd83xxx_models,
 							stac92hd83xxx_cfg_tbl);
 							stac92hd83xxx_cfg_tbl);
 again:
 again:
 	if (spec->board_config < 0)
 	if (spec->board_config < 0)
-		snd_printdd(KERN_INFO "hda_codec: Unknown model for"
-			" STAC92HD83XXX, using BIOS defaults\n");
+		snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+			    codec->chip_name);
 	else
 	else
 		stac92xx_set_config_regs(codec,
 		stac92xx_set_config_regs(codec,
 				stac92hd83xxx_brd_tbl[spec->board_config]);
 				stac92hd83xxx_brd_tbl[spec->board_config]);
@@ -5164,6 +5133,8 @@ again:
 
 
 	num_dacs = snd_hda_get_connections(codec, nid,
 	num_dacs = snd_hda_get_connections(codec, nid,
 				conn, STAC92HD83_DAC_COUNT + 1) - 1;
 				conn, STAC92HD83_DAC_COUNT + 1) - 1;
+	if (num_dacs < 0)
+		num_dacs = STAC92HD83_DAC_COUNT;
 
 
 	/* set port X to select the last DAC
 	/* set port X to select the last DAC
 	 */
 	 */
@@ -5177,25 +5148,6 @@ again:
 	return 0;
 	return 0;
 }
 }
 
 
-static struct hda_input_mux stac92hd71bxx_dmux_nomixer = {
-	.num_items = 3,
-	.items = {
-		{ "Analog Inputs", 0x00 },
-		{ "Digital Mic 1", 0x02 },
-		{ "Digital Mic 2", 0x03 },
-	}
-};
-
-static struct hda_input_mux stac92hd71bxx_dmux_amixer = {
-	.num_items = 4,
-	.items = {
-		{ "Analog Inputs", 0x00 },
-		{ "Mixer", 0x01 },
-		{ "Digital Mic 1", 0x02 },
-		{ "Digital Mic 2", 0x03 },
-	}
-};
-
 /* get the pin connection (fixed, none, etc) */
 /* get the pin connection (fixed, none, etc) */
 static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx)
 static unsigned int stac_get_defcfg_connect(struct hda_codec *codec, int idx)
 {
 {
@@ -5256,7 +5208,6 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
 	struct sigmatel_spec *spec;
 	struct sigmatel_spec *spec;
 	struct hda_verb *unmute_init = stac92hd71bxx_unmute_core_init;
 	struct hda_verb *unmute_init = stac92hd71bxx_unmute_core_init;
 	int err = 0;
 	int err = 0;
-	unsigned int ndmic_nids = 0;
 
 
 	spec  = kzalloc(sizeof(*spec), GFP_KERNEL);
 	spec  = kzalloc(sizeof(*spec), GFP_KERNEL);
 	if (spec == NULL)
 	if (spec == NULL)
@@ -5285,8 +5236,8 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
 							stac92hd71bxx_cfg_tbl);
 							stac92hd71bxx_cfg_tbl);
 again:
 again:
 	if (spec->board_config < 0)
 	if (spec->board_config < 0)
-		snd_printdd(KERN_INFO "hda_codec: Unknown model for"
-			" STAC92HD71BXX, using BIOS defaults\n");
+		snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+			    codec->chip_name);
 	else
 	else
 		stac92xx_set_config_regs(codec,
 		stac92xx_set_config_regs(codec,
 				stac92hd71bxx_brd_tbl[spec->board_config]);
 				stac92hd71bxx_brd_tbl[spec->board_config]);
@@ -5301,6 +5252,10 @@ again:
 	spec->dmic_nids = stac92hd71bxx_dmic_nids;
 	spec->dmic_nids = stac92hd71bxx_dmic_nids;
 	spec->dmux_nids = stac92hd71bxx_dmux_nids;
 	spec->dmux_nids = stac92hd71bxx_dmux_nids;
 
 
+	spec->num_caps = STAC92HD71BXX_NUM_CAPS;
+	spec->capvols = stac92hd71bxx_capvols;
+	spec->capsws = stac92hd71bxx_capsws;
+
 	switch (codec->vendor_id) {
 	switch (codec->vendor_id) {
 	case 0x111d76b6: /* 4 Port without Analog Mixer */
 	case 0x111d76b6: /* 4 Port without Analog Mixer */
 	case 0x111d76b7:
 	case 0x111d76b7:
@@ -5308,24 +5263,13 @@ again:
 		/* fallthru */
 		/* fallthru */
 	case 0x111d76b4: /* 6 Port without Analog Mixer */
 	case 0x111d76b4: /* 6 Port without Analog Mixer */
 	case 0x111d76b5:
 	case 0x111d76b5:
-		memcpy(&spec->private_dimux, &stac92hd71bxx_dmux_nomixer,
-		       sizeof(stac92hd71bxx_dmux_nomixer));
-		spec->mixer = stac92hd71bxx_mixer;
 		spec->init = stac92hd71bxx_core_init;
 		spec->init = stac92hd71bxx_core_init;
 		codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
 		codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
 		spec->num_dmics = stac92hd71bxx_connected_ports(codec,
 		spec->num_dmics = stac92hd71bxx_connected_ports(codec,
 					stac92hd71bxx_dmic_nids,
 					stac92hd71bxx_dmic_nids,
 					STAC92HD71BXX_NUM_DMICS);
 					STAC92HD71BXX_NUM_DMICS);
-		if (spec->num_dmics) {
-			spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
-			spec->dinput_mux = &spec->private_dimux;
-			ndmic_nids = ARRAY_SIZE(stac92hd71bxx_dmic_nids) - 1;
-		}
 		break;
 		break;
 	case 0x111d7608: /* 5 Port with Analog Mixer */
 	case 0x111d7608: /* 5 Port with Analog Mixer */
-		memcpy(&spec->private_dimux, &stac92hd71bxx_dmux_amixer,
-		       sizeof(stac92hd71bxx_dmux_amixer));
-		spec->private_dimux.num_items--;
 		switch (spec->board_config) {
 		switch (spec->board_config) {
 		case STAC_HP_M4:
 		case STAC_HP_M4:
 			/* Enable VREF power saving on GPIO1 detect */
 			/* Enable VREF power saving on GPIO1 detect */
@@ -5347,11 +5291,8 @@ again:
 
 
 		/* no output amps */
 		/* no output amps */
 		spec->num_pwrs = 0;
 		spec->num_pwrs = 0;
-		spec->mixer = stac92hd71bxx_analog_mixer;
-		spec->dinput_mux = &spec->private_dimux;
-
 		/* disable VSW */
 		/* disable VSW */
-		spec->init = &stac92hd71bxx_analog_core_init[HD_DISABLE_PORTF];
+		spec->init = stac92hd71bxx_core_init;
 		unmute_init++;
 		unmute_init++;
 		snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0);
 		snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0);
 		snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3);
 		snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3);
@@ -5359,8 +5300,6 @@ again:
 		spec->num_dmics = stac92hd71bxx_connected_ports(codec,
 		spec->num_dmics = stac92hd71bxx_connected_ports(codec,
 					stac92hd71bxx_dmic_nids,
 					stac92hd71bxx_dmic_nids,
 					STAC92HD71BXX_NUM_DMICS - 1);
 					STAC92HD71BXX_NUM_DMICS - 1);
-		spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
-		ndmic_nids = ARRAY_SIZE(stac92hd71bxx_dmic_nids) - 2;
 		break;
 		break;
 	case 0x111d7603: /* 6 Port with Analog Mixer */
 	case 0x111d7603: /* 6 Port with Analog Mixer */
 		if ((codec->revision_id & 0xf) == 1)
 		if ((codec->revision_id & 0xf) == 1)
@@ -5370,17 +5309,12 @@ again:
 		spec->num_pwrs = 0;
 		spec->num_pwrs = 0;
 		/* fallthru */
 		/* fallthru */
 	default:
 	default:
-		memcpy(&spec->private_dimux, &stac92hd71bxx_dmux_amixer,
-		       sizeof(stac92hd71bxx_dmux_amixer));
-		spec->dinput_mux = &spec->private_dimux;
-		spec->mixer = stac92hd71bxx_analog_mixer;
-		spec->init = stac92hd71bxx_analog_core_init;
+		spec->init = stac92hd71bxx_core_init;
 		codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
 		codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
 		spec->num_dmics = stac92hd71bxx_connected_ports(codec,
 		spec->num_dmics = stac92hd71bxx_connected_ports(codec,
 					stac92hd71bxx_dmic_nids,
 					stac92hd71bxx_dmic_nids,
 					STAC92HD71BXX_NUM_DMICS);
 					STAC92HD71BXX_NUM_DMICS);
-		spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
-		ndmic_nids = ARRAY_SIZE(stac92hd71bxx_dmic_nids) - 1;
+		break;
 	}
 	}
 
 
 	if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP)
 	if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP)
@@ -5408,6 +5342,7 @@ again:
 
 
 	spec->num_muxes = ARRAY_SIZE(stac92hd71bxx_mux_nids);
 	spec->num_muxes = ARRAY_SIZE(stac92hd71bxx_mux_nids);
 	spec->num_adcs = ARRAY_SIZE(stac92hd71bxx_adc_nids);
 	spec->num_adcs = ARRAY_SIZE(stac92hd71bxx_adc_nids);
+	spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
 	spec->num_smuxes = stac92hd71bxx_connected_smuxes(codec, 0x1e);
 	spec->num_smuxes = stac92hd71bxx_connected_smuxes(codec, 0x1e);
 
 
 	switch (spec->board_config) {
 	switch (spec->board_config) {
@@ -5462,8 +5397,6 @@ again:
 #endif	
 #endif	
 
 
 	spec->multiout.dac_nids = spec->dac_nids;
 	spec->multiout.dac_nids = spec->dac_nids;
-	if (spec->dinput_mux)
-		spec->private_dimux.num_items += spec->num_dmics - ndmic_nids;
 
 
 	err = stac92xx_parse_auto_config(codec, 0x21, 0);
 	err = stac92xx_parse_auto_config(codec, 0x21, 0);
 	if (!err) {
 	if (!err) {
@@ -5541,8 +5474,8 @@ static int patch_stac922x(struct hda_codec *codec)
 
 
  again:
  again:
 	if (spec->board_config < 0)
 	if (spec->board_config < 0)
-		snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, "
-			"using BIOS defaults\n");
+		snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+			    codec->chip_name);
 	else
 	else
 		stac92xx_set_config_regs(codec,
 		stac92xx_set_config_regs(codec,
 				stac922x_brd_tbl[spec->board_config]);
 				stac922x_brd_tbl[spec->board_config]);
@@ -5555,7 +5488,10 @@ static int patch_stac922x(struct hda_codec *codec)
 	spec->num_pwrs = 0;
 	spec->num_pwrs = 0;
 
 
 	spec->init = stac922x_core_init;
 	spec->init = stac922x_core_init;
-	spec->mixer = stac922x_mixer;
+
+	spec->num_caps = STAC922X_NUM_CAPS;
+	spec->capvols = stac922x_capvols;
+	spec->capsws = stac922x_capsws;
 
 
 	spec->multiout.dac_nids = spec->dac_nids;
 	spec->multiout.dac_nids = spec->dac_nids;
 	
 	
@@ -5604,8 +5540,8 @@ static int patch_stac927x(struct hda_codec *codec)
 							stac927x_cfg_tbl);
 							stac927x_cfg_tbl);
  again:
  again:
 	if (spec->board_config < 0)
 	if (spec->board_config < 0)
-		snd_printdd(KERN_INFO "hda_codec: Unknown model for"
-			    "STAC927x, using BIOS defaults\n");
+		snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+			    codec->chip_name);
 	else
 	else
 		stac92xx_set_config_regs(codec,
 		stac92xx_set_config_regs(codec,
 				stac927x_brd_tbl[spec->board_config]);
 				stac927x_brd_tbl[spec->board_config]);
@@ -5630,7 +5566,6 @@ static int patch_stac927x(struct hda_codec *codec)
 		spec->num_dmics = 0;
 		spec->num_dmics = 0;
 
 
 		spec->init = d965_core_init;
 		spec->init = d965_core_init;
-		spec->mixer = stac927x_mixer;
 		break;
 		break;
 	case STAC_DELL_BIOS:
 	case STAC_DELL_BIOS:
 		switch (codec->subsystem_id) {
 		switch (codec->subsystem_id) {
@@ -5662,7 +5597,6 @@ static int patch_stac927x(struct hda_codec *codec)
 		spec->num_dmics = STAC927X_NUM_DMICS;
 		spec->num_dmics = STAC927X_NUM_DMICS;
 
 
 		spec->init = d965_core_init;
 		spec->init = d965_core_init;
-		spec->mixer = stac927x_mixer;
 		spec->dmux_nids = stac927x_dmux_nids;
 		spec->dmux_nids = stac927x_dmux_nids;
 		spec->num_dmuxes = ARRAY_SIZE(stac927x_dmux_nids);
 		spec->num_dmuxes = ARRAY_SIZE(stac927x_dmux_nids);
 		break;
 		break;
@@ -5675,9 +5609,12 @@ static int patch_stac927x(struct hda_codec *codec)
 		spec->num_dmics = 0;
 		spec->num_dmics = 0;
 
 
 		spec->init = stac927x_core_init;
 		spec->init = stac927x_core_init;
-		spec->mixer = stac927x_mixer;
 	}
 	}
 
 
+	spec->num_caps = STAC927X_NUM_CAPS;
+	spec->capvols = stac927x_capvols;
+	spec->capsws = stac927x_capsws;
+
 	spec->num_pwrs = 0;
 	spec->num_pwrs = 0;
 	spec->aloopback_ctl = stac927x_loopback;
 	spec->aloopback_ctl = stac927x_loopback;
 	spec->aloopback_mask = 0x40;
 	spec->aloopback_mask = 0x40;
@@ -5739,7 +5676,8 @@ static int patch_stac9205(struct hda_codec *codec)
 							stac9205_cfg_tbl);
 							stac9205_cfg_tbl);
  again:
  again:
 	if (spec->board_config < 0)
 	if (spec->board_config < 0)
-		snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9205, using BIOS defaults\n");
+		snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+			    codec->chip_name);
 	else
 	else
 		stac92xx_set_config_regs(codec,
 		stac92xx_set_config_regs(codec,
 					 stac9205_brd_tbl[spec->board_config]);
 					 stac9205_brd_tbl[spec->board_config]);
@@ -5758,9 +5696,12 @@ static int patch_stac9205(struct hda_codec *codec)
 	spec->num_pwrs = 0;
 	spec->num_pwrs = 0;
 
 
 	spec->init = stac9205_core_init;
 	spec->init = stac9205_core_init;
-	spec->mixer = stac9205_mixer;
 	spec->aloopback_ctl = stac9205_loopback;
 	spec->aloopback_ctl = stac9205_loopback;
 
 
+	spec->num_caps = STAC9205_NUM_CAPS;
+	spec->capvols = stac9205_capvols;
+	spec->capsws = stac9205_capsws;
+
 	spec->aloopback_mask = 0x40;
 	spec->aloopback_mask = 0x40;
 	spec->aloopback_shift = 0;
 	spec->aloopback_shift = 0;
 	/* Turn on/off EAPD per HP plugging */
 	/* Turn on/off EAPD per HP plugging */
@@ -5835,12 +5776,6 @@ static struct hda_verb stac9872_core_init[] = {
 	{}
 	{}
 };
 };
 
 
-static struct snd_kcontrol_new stac9872_mixer[] = {
-	HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT),
-	{ } /* end */
-};
-
 static hda_nid_t stac9872_pin_nids[] = {
 static hda_nid_t stac9872_pin_nids[] = {
 	0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
 	0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
 	0x11, 0x13, 0x14,
 	0x11, 0x13, 0x14,
@@ -5854,6 +5789,11 @@ static hda_nid_t stac9872_mux_nids[] = {
 	0x15
 	0x15
 };
 };
 
 
+static unsigned long stac9872_capvols[] = {
+	HDA_COMPOSE_AMP_VAL(0x09, 3, 0, HDA_INPUT),
+};
+#define stac9872_capsws		stac9872_capvols
+
 static unsigned int stac9872_vaio_pin_configs[9] = {
 static unsigned int stac9872_vaio_pin_configs[9] = {
 	0x03211020, 0x411111f0, 0x411111f0, 0x03a15030,
 	0x03211020, 0x411111f0, 0x411111f0, 0x03a15030,
 	0x411111f0, 0x90170110, 0x411111f0, 0x411111f0,
 	0x411111f0, 0x90170110, 0x411111f0, 0x411111f0,
@@ -5891,8 +5831,8 @@ static int patch_stac9872(struct hda_codec *codec)
 							stac9872_models,
 							stac9872_models,
 							stac9872_cfg_tbl);
 							stac9872_cfg_tbl);
 	if (spec->board_config < 0)
 	if (spec->board_config < 0)
-		snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC9872, "
-			    "using BIOS defaults\n");
+		snd_printdd(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
+			    codec->chip_name);
 	else
 	else
 		stac92xx_set_config_regs(codec,
 		stac92xx_set_config_regs(codec,
 					 stac9872_brd_tbl[spec->board_config]);
 					 stac9872_brd_tbl[spec->board_config]);
@@ -5902,8 +5842,10 @@ static int patch_stac9872(struct hda_codec *codec)
 	spec->adc_nids = stac9872_adc_nids;
 	spec->adc_nids = stac9872_adc_nids;
 	spec->num_muxes = ARRAY_SIZE(stac9872_mux_nids);
 	spec->num_muxes = ARRAY_SIZE(stac9872_mux_nids);
 	spec->mux_nids = stac9872_mux_nids;
 	spec->mux_nids = stac9872_mux_nids;
-	spec->mixer = stac9872_mixer;
 	spec->init = stac9872_core_init;
 	spec->init = stac9872_core_init;
+	spec->num_caps = 1;
+	spec->capvols = stac9872_capvols;
+	spec->capsws = stac9872_capsws;
 
 
 	err = stac92xx_parse_auto_config(codec, 0x10, 0x12);
 	err = stac92xx_parse_auto_config(codec, 0x10, 0x12);
 	if (err < 0) {
 	if (err < 0) {

+ 1 - 2
sound/pci/hda/patch_via.c

@@ -1339,8 +1339,7 @@ static int get_mux_nids(struct hda_codec *codec)
 	for (i = 0; i < spec->num_adc_nids; i++) {
 	for (i = 0; i < spec->num_adc_nids; i++) {
 		nid = spec->adc_nids[i];
 		nid = spec->adc_nids[i];
 		while (nid) {
 		while (nid) {
-			type = (get_wcaps(codec, nid) & AC_WCAP_TYPE)
-				>> AC_WCAP_TYPE_SHIFT;
+			type = get_wcaps_type(get_wcaps(codec, nid));
 			if (type == AC_WID_PIN)
 			if (type == AC_WID_PIN)
 				break;
 				break;
 			n = snd_hda_get_connections(codec, nid, conn,
 			n = snd_hda_get_connections(codec, nid, conn,

+ 9 - 0
sound/pci/ice1712/ice1712.h

@@ -379,6 +379,15 @@ struct snd_ice1712 {
 	unsigned char (*set_mclk)(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);
 	void (*set_spdif_clock)(struct snd_ice1712 *ice);
 
 
+#ifdef CONFIG_PM
+	int (*pm_suspend)(struct snd_ice1712 *);
+	int (*pm_resume)(struct snd_ice1712 *);
+	int pm_suspend_enabled:1;
+	int pm_saved_is_spdif_master:1;
+	unsigned int pm_saved_spdif_ctrl;
+	unsigned char pm_saved_spdif_cfg;
+	unsigned int pm_saved_route;
+#endif
 };
 };
 
 
 
 

+ 102 - 10
sound/pci/ice1712/ice1724.c

@@ -560,6 +560,7 @@ static int snd_vt1724_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 
 
 	case SNDRV_PCM_TRIGGER_START:
 	case SNDRV_PCM_TRIGGER_START:
 	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
 		spin_lock(&ice->reg_lock);
 		spin_lock(&ice->reg_lock);
 		old = inb(ICEMT1724(ice, DMA_CONTROL));
 		old = inb(ICEMT1724(ice, DMA_CONTROL));
 		if (cmd == SNDRV_PCM_TRIGGER_START)
 		if (cmd == SNDRV_PCM_TRIGGER_START)
@@ -570,6 +571,10 @@ static int snd_vt1724_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 		spin_unlock(&ice->reg_lock);
 		spin_unlock(&ice->reg_lock);
 		break;
 		break;
 
 
+	case SNDRV_PCM_TRIGGER_RESUME:
+		/* apps will have to restart stream */
+		break;
+
 	default:
 	default:
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
@@ -2262,7 +2267,7 @@ static int __devinit snd_vt1724_read_eeprom(struct snd_ice1712 *ice,
 
 
 
 
 
 
-static void __devinit snd_vt1724_chip_reset(struct snd_ice1712 *ice)
+static void snd_vt1724_chip_reset(struct snd_ice1712 *ice)
 {
 {
 	outb(VT1724_RESET , ICEREG1724(ice, CONTROL));
 	outb(VT1724_RESET , ICEREG1724(ice, CONTROL));
 	inb(ICEREG1724(ice, CONTROL)); /* pci posting flush */
 	inb(ICEREG1724(ice, CONTROL)); /* pci posting flush */
@@ -2272,7 +2277,7 @@ static void __devinit snd_vt1724_chip_reset(struct snd_ice1712 *ice)
 	msleep(10);
 	msleep(10);
 }
 }
 
 
-static int __devinit snd_vt1724_chip_init(struct snd_ice1712 *ice)
+static int snd_vt1724_chip_init(struct snd_ice1712 *ice)
 {
 {
 	outb(ice->eeprom.data[ICE_EEP2_SYSCONF], ICEREG1724(ice, SYS_CFG));
 	outb(ice->eeprom.data[ICE_EEP2_SYSCONF], ICEREG1724(ice, SYS_CFG));
 	outb(ice->eeprom.data[ICE_EEP2_ACLINK], ICEREG1724(ice, AC97_CFG));
 	outb(ice->eeprom.data[ICE_EEP2_ACLINK], ICEREG1724(ice, AC97_CFG));
@@ -2287,6 +2292,14 @@ static int __devinit snd_vt1724_chip_init(struct snd_ice1712 *ice)
 
 
 	outb(0, ICEREG1724(ice, POWERDOWN));
 	outb(0, ICEREG1724(ice, POWERDOWN));
 
 
+	/* MPU_RX and TX irq masks are cleared later dynamically */
+	outb(VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX , ICEREG1724(ice, IRQMASK));
+
+	/* don't handle FIFO overrun/underruns (just yet),
+	 * since they cause machine lockups
+	 */
+	outb(VT1724_MULTI_FIFO_ERR, ICEMT1724(ice, DMA_INT_MASK));
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -2431,6 +2444,8 @@ static int __devinit snd_vt1724_create(struct snd_card *card,
 	snd_vt1724_proc_init(ice);
 	snd_vt1724_proc_init(ice);
 	synchronize_irq(pci->irq);
 	synchronize_irq(pci->irq);
 
 
+	card->private_data = ice;
+
 	err = pci_request_regions(pci, "ICE1724");
 	err = pci_request_regions(pci, "ICE1724");
 	if (err < 0) {
 	if (err < 0) {
 		kfree(ice);
 		kfree(ice);
@@ -2459,14 +2474,6 @@ static int __devinit snd_vt1724_create(struct snd_card *card,
 		return -EIO;
 		return -EIO;
 	}
 	}
 
 
-	/* MPU_RX and TX irq masks are cleared later dynamically */
-	outb(VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX , ICEREG1724(ice, IRQMASK));
-
-	/* don't handle FIFO overrun/underruns (just yet),
-	 * since they cause machine lockups
-	 */
-	outb(VT1724_MULTI_FIFO_ERR, ICEMT1724(ice, DMA_INT_MASK));
-
 	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops);
 	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops);
 	if (err < 0) {
 	if (err < 0) {
 		snd_vt1724_free(ice);
 		snd_vt1724_free(ice);
@@ -2650,11 +2657,96 @@ static void __devexit snd_vt1724_remove(struct pci_dev *pci)
 	pci_set_drvdata(pci, NULL);
 	pci_set_drvdata(pci, NULL);
 }
 }
 
 
+#ifdef CONFIG_PM
+static int snd_vt1724_suspend(struct pci_dev *pci, pm_message_t state)
+{
+	struct snd_card *card = pci_get_drvdata(pci);
+	struct snd_ice1712 *ice = card->private_data;
+
+	if (!ice->pm_suspend_enabled)
+		return 0;
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+
+	snd_pcm_suspend_all(ice->pcm);
+	snd_pcm_suspend_all(ice->pcm_pro);
+	snd_pcm_suspend_all(ice->pcm_ds);
+	snd_ac97_suspend(ice->ac97);
+
+	spin_lock_irq(&ice->reg_lock);
+	ice->pm_saved_is_spdif_master = ice->is_spdif_master(ice);
+	ice->pm_saved_spdif_ctrl = inw(ICEMT1724(ice, SPDIF_CTRL));
+	ice->pm_saved_spdif_cfg = inb(ICEREG1724(ice, SPDIF_CFG));
+	ice->pm_saved_route = inl(ICEMT1724(ice, ROUTE_PLAYBACK));
+	spin_unlock_irq(&ice->reg_lock);
+
+	if (ice->pm_suspend)
+		ice->pm_suspend(ice);
+
+	pci_disable_device(pci);
+	pci_save_state(pci);
+	pci_set_power_state(pci, pci_choose_state(pci, state));
+	return 0;
+}
+
+static int snd_vt1724_resume(struct pci_dev *pci)
+{
+	struct snd_card *card = pci_get_drvdata(pci);
+	struct snd_ice1712 *ice = card->private_data;
+
+	if (!ice->pm_suspend_enabled)
+		return 0;
+
+	pci_set_power_state(pci, PCI_D0);
+	pci_restore_state(pci);
+
+	if (pci_enable_device(pci) < 0) {
+		snd_card_disconnect(card);
+		return -EIO;
+	}
+
+	pci_set_master(pci);
+
+	snd_vt1724_chip_reset(ice);
+
+	if (snd_vt1724_chip_init(ice) < 0) {
+		snd_card_disconnect(card);
+		return -EIO;
+	}
+
+	if (ice->pm_resume)
+		ice->pm_resume(ice);
+
+	if (ice->pm_saved_is_spdif_master) {
+		/* switching to external clock via SPDIF */
+		ice->set_spdif_clock(ice);
+	} else {
+		/* internal on-card clock */
+		snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 1);
+	}
+
+	update_spdif_bits(ice, ice->pm_saved_spdif_ctrl);
+
+	outb(ice->pm_saved_spdif_cfg, ICEREG1724(ice, SPDIF_CFG));
+	outl(ice->pm_saved_route, ICEMT1724(ice, ROUTE_PLAYBACK));
+
+	if (ice->ac97)
+		snd_ac97_resume(ice->ac97);
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+	return 0;
+}
+#endif
+
 static struct pci_driver driver = {
 static struct pci_driver driver = {
 	.name = "ICE1724",
 	.name = "ICE1724",
 	.id_table = snd_vt1724_ids,
 	.id_table = snd_vt1724_ids,
 	.probe = snd_vt1724_probe,
 	.probe = snd_vt1724_probe,
 	.remove = __devexit_p(snd_vt1724_remove),
 	.remove = __devexit_p(snd_vt1724_remove),
+#ifdef CONFIG_PM
+	.suspend = snd_vt1724_suspend,
+	.resume = snd_vt1724_resume,
+#endif
 };
 };
 
 
 static int __init alsa_card_ice1724_init(void)
 static int __init alsa_card_ice1724_init(void)

+ 36 - 10
sound/pci/ice1712/prodigy_hifi.c

@@ -1077,7 +1077,7 @@ static int __devinit prodigy_hifi_init(struct snd_ice1712 *ice)
 /*
 /*
  * initialize the chip
  * initialize the chip
  */
  */
-static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice)
+static void ak4396_init(struct snd_ice1712 *ice)
 {
 {
 	static unsigned short ak4396_inits[] = {
 	static unsigned short ak4396_inits[] = {
 		AK4396_CTRL1,	   0x87,   /* I2S Normal Mode, 24 bit */
 		AK4396_CTRL1,	   0x87,   /* I2S Normal Mode, 24 bit */
@@ -1087,9 +1087,37 @@ static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice)
 		AK4396_RCH_ATT,	 0x00,
 		AK4396_RCH_ATT,	 0x00,
 	};
 	};
 
 
-	struct prodigy_hifi_spec *spec;
 	unsigned int i;
 	unsigned int i;
 
 
+	/* initialize ak4396 codec */
+	/* reset codec */
+	ak4396_write(ice, AK4396_CTRL1, 0x86);
+	msleep(100);
+	ak4396_write(ice, AK4396_CTRL1, 0x87);
+
+	for (i = 0; i < ARRAY_SIZE(ak4396_inits); i += 2)
+		ak4396_write(ice, ak4396_inits[i], ak4396_inits[i+1]);
+}
+
+#ifdef CONFIG_PM
+static int __devinit prodigy_hd2_resume(struct snd_ice1712 *ice)
+{
+	/* initialize ak4396 codec and restore previous mixer volumes */
+	struct prodigy_hifi_spec *spec = ice->spec;
+	int i;
+	mutex_lock(&ice->gpio_mutex);
+	ak4396_init(ice);
+	for (i = 0; i < 2; i++)
+		ak4396_write(ice, AK4396_LCH_ATT + i, spec->vol[i] & 0xff);
+	mutex_unlock(&ice->gpio_mutex);
+	return 0;
+}
+#endif
+
+static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice)
+{
+	struct prodigy_hifi_spec *spec;
+
 	ice->vt1720 = 0;
 	ice->vt1720 = 0;
 	ice->vt1724 = 1;
 	ice->vt1724 = 1;
 
 
@@ -1112,14 +1140,12 @@ static int __devinit prodigy_hd2_init(struct snd_ice1712 *ice)
 		return -ENOMEM;
 		return -ENOMEM;
 	ice->spec = spec;
 	ice->spec = spec;
 
 
-	/* initialize ak4396 codec */
-	/* reset codec */
-	ak4396_write(ice, AK4396_CTRL1, 0x86);
-	msleep(100);
-	ak4396_write(ice, AK4396_CTRL1, 0x87);
-			
-	for (i = 0; i < ARRAY_SIZE(ak4396_inits); i += 2)
-		ak4396_write(ice, ak4396_inits[i], ak4396_inits[i+1]);
+#ifdef CONFIG_PM
+	ice->pm_resume = &prodigy_hd2_resume;
+	ice->pm_suspend_enabled = 1;
+#endif
+
+	ak4396_init(ice);
 
 
 	return 0;
 	return 0;
 }
 }

+ 1 - 10
sound/pci/oxygen/oxygen_io.c

@@ -215,17 +215,8 @@ EXPORT_SYMBOL(oxygen_write_spi);
 
 
 void oxygen_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data)
 void oxygen_write_i2c(struct oxygen *chip, u8 device, u8 map, u8 data)
 {
 {
-	unsigned long timeout;
-
 	/* should not need more than about 300 us */
 	/* should not need more than about 300 us */
-	timeout = jiffies + msecs_to_jiffies(1);
-	do {
-		if (!(oxygen_read16(chip, OXYGEN_2WIRE_BUS_STATUS)
-		      & OXYGEN_2WIRE_BUSY))
-			break;
-		udelay(1);
-		cond_resched();
-	} while (time_after_eq(timeout, jiffies));
+	msleep(1);
 
 
 	oxygen_write8(chip, OXYGEN_2WIRE_MAP, map);
 	oxygen_write8(chip, OXYGEN_2WIRE_MAP, map);
 	oxygen_write8(chip, OXYGEN_2WIRE_DATA, data);
 	oxygen_write8(chip, OXYGEN_2WIRE_DATA, data);

+ 22 - 17
sound/pci/rme9652/hdsp.c

@@ -3294,15 +3294,33 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
 	char *clock_source;
 	char *clock_source;
 	int x;
 	int x;
 
 
-	if (hdsp_check_for_iobox (hdsp)) {
-		snd_iprintf(buffer, "No I/O box connected.\nPlease connect one and upload firmware.\n");
+	status = hdsp_read(hdsp, HDSP_statusRegister);
+	status2 = hdsp_read(hdsp, HDSP_status2Register);
+
+	snd_iprintf(buffer, "%s (Card #%d)\n", hdsp->card_name,
+		    hdsp->card->number + 1);
+	snd_iprintf(buffer, "Buffers: capture %p playback %p\n",
+		    hdsp->capture_buffer, hdsp->playback_buffer);
+	snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n",
+		    hdsp->irq, hdsp->port, (unsigned long)hdsp->iobase);
+	snd_iprintf(buffer, "Control register: 0x%x\n", hdsp->control_register);
+	snd_iprintf(buffer, "Control2 register: 0x%x\n",
+		    hdsp->control2_register);
+	snd_iprintf(buffer, "Status register: 0x%x\n", status);
+	snd_iprintf(buffer, "Status2 register: 0x%x\n", status2);
+
+	if (hdsp_check_for_iobox(hdsp)) {
+		snd_iprintf(buffer, "No I/O box connected.\n"
+			    "Please connect one and upload firmware.\n");
 		return;
 		return;
-        }
+	}
 
 
 	if (hdsp_check_for_firmware(hdsp, 0)) {
 	if (hdsp_check_for_firmware(hdsp, 0)) {
 		if (hdsp->state & HDSP_FirmwareCached) {
 		if (hdsp->state & HDSP_FirmwareCached) {
 			if (snd_hdsp_load_firmware_from_cache(hdsp) != 0) {
 			if (snd_hdsp_load_firmware_from_cache(hdsp) != 0) {
-				snd_iprintf(buffer, "Firmware loading from cache failed, please upload manually.\n");
+				snd_iprintf(buffer, "Firmware loading from "
+					    "cache failed, "
+					    "please upload manually.\n");
 				return;
 				return;
 			}
 			}
 		} else {
 		} else {
@@ -3319,18 +3337,6 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
 		}
 		}
 	}
 	}
 
 
-	status = hdsp_read(hdsp, HDSP_statusRegister);
-	status2 = hdsp_read(hdsp, HDSP_status2Register);
-
-	snd_iprintf(buffer, "%s (Card #%d)\n", hdsp->card_name, hdsp->card->number + 1);
-	snd_iprintf(buffer, "Buffers: capture %p playback %p\n",
-		    hdsp->capture_buffer, hdsp->playback_buffer);
-	snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n",
-		    hdsp->irq, hdsp->port, (unsigned long)hdsp->iobase);
-	snd_iprintf(buffer, "Control register: 0x%x\n", hdsp->control_register);
-	snd_iprintf(buffer, "Control2 register: 0x%x\n", hdsp->control2_register);
-	snd_iprintf(buffer, "Status register: 0x%x\n", status);
-	snd_iprintf(buffer, "Status2 register: 0x%x\n", status2);
 	snd_iprintf(buffer, "FIFO status: %d\n", hdsp_read(hdsp, HDSP_fifoStatus) & 0xff);
 	snd_iprintf(buffer, "FIFO status: %d\n", hdsp_read(hdsp, HDSP_fifoStatus) & 0xff);
 	snd_iprintf(buffer, "MIDI1 Output status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusOut0));
 	snd_iprintf(buffer, "MIDI1 Output status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusOut0));
 	snd_iprintf(buffer, "MIDI1 Input status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusIn0));
 	snd_iprintf(buffer, "MIDI1 Input status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusIn0));
@@ -3351,7 +3357,6 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
 
 
 	snd_iprintf(buffer, "\n");
 	snd_iprintf(buffer, "\n");
 
 
-
 	switch (hdsp_clock_source(hdsp)) {
 	switch (hdsp_clock_source(hdsp)) {
 	case HDSP_CLOCK_SOURCE_AUTOSYNC:
 	case HDSP_CLOCK_SOURCE_AUTOSYNC:
 		clock_source = "AutoSync";
 		clock_source = "AutoSync";

+ 15 - 5
sound/pci/ymfpci/ymfpci_main.c

@@ -834,7 +834,7 @@ static irqreturn_t snd_ymfpci_interrupt(int irq, void *dev_id)
 	status = snd_ymfpci_readw(chip, YDSXGR_INTFLAG);
 	status = snd_ymfpci_readw(chip, YDSXGR_INTFLAG);
 	if (status & 1) {
 	if (status & 1) {
 		if (chip->timer)
 		if (chip->timer)
-			snd_timer_interrupt(chip->timer, chip->timer->sticks);
+			snd_timer_interrupt(chip->timer, chip->timer_ticks);
 	}
 	}
 	snd_ymfpci_writew(chip, YDSXGR_INTFLAG, status);
 	snd_ymfpci_writew(chip, YDSXGR_INTFLAG, status);
 
 
@@ -1885,8 +1885,18 @@ static int snd_ymfpci_timer_start(struct snd_timer *timer)
 	unsigned int count;
 	unsigned int count;
 
 
 	chip = snd_timer_chip(timer);
 	chip = snd_timer_chip(timer);
-	count = (timer->sticks << 1) - 1;
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	spin_lock_irqsave(&chip->reg_lock, flags);
+	if (timer->sticks > 1) {
+		chip->timer_ticks = timer->sticks;
+		count = timer->sticks - 1;
+	} else {
+		/*
+		 * Divisor 1 is not allowed; fake it by using divisor 2 and
+		 * counting two ticks for each interrupt.
+		 */
+		chip->timer_ticks = 2;
+		count = 2 - 1;
+	}
 	snd_ymfpci_writew(chip, YDSXGR_TIMERCOUNT, count);
 	snd_ymfpci_writew(chip, YDSXGR_TIMERCOUNT, count);
 	snd_ymfpci_writeb(chip, YDSXGR_TIMERCTRL, 0x03);
 	snd_ymfpci_writeb(chip, YDSXGR_TIMERCTRL, 0x03);
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
@@ -1909,14 +1919,14 @@ static int snd_ymfpci_timer_precise_resolution(struct snd_timer *timer,
 					       unsigned long *num, unsigned long *den)
 					       unsigned long *num, unsigned long *den)
 {
 {
 	*num = 1;
 	*num = 1;
-	*den = 48000;
+	*den = 96000;
 	return 0;
 	return 0;
 }
 }
 
 
 static struct snd_timer_hardware snd_ymfpci_timer_hw = {
 static struct snd_timer_hardware snd_ymfpci_timer_hw = {
 	.flags = SNDRV_TIMER_HW_AUTO,
 	.flags = SNDRV_TIMER_HW_AUTO,
-	.resolution = 20833, /* 1/fs = 20.8333...us */
-	.ticks = 0x8000,
+	.resolution = 10417, /* 1 / 96 kHz = 10.41666...us */
+	.ticks = 0x10000,
 	.start = snd_ymfpci_timer_start,
 	.start = snd_ymfpci_timer_start,
 	.stop = snd_ymfpci_timer_stop,
 	.stop = snd_ymfpci_timer_stop,
 	.precise_resolution = snd_ymfpci_timer_precise_resolution,
 	.precise_resolution = snd_ymfpci_timer_precise_resolution,

+ 1 - 0
sound/soc/Kconfig

@@ -29,6 +29,7 @@ source "sound/soc/au1x/Kconfig"
 source "sound/soc/blackfin/Kconfig"
 source "sound/soc/blackfin/Kconfig"
 source "sound/soc/davinci/Kconfig"
 source "sound/soc/davinci/Kconfig"
 source "sound/soc/fsl/Kconfig"
 source "sound/soc/fsl/Kconfig"
+source "sound/soc/imx/Kconfig"
 source "sound/soc/omap/Kconfig"
 source "sound/soc/omap/Kconfig"
 source "sound/soc/pxa/Kconfig"
 source "sound/soc/pxa/Kconfig"
 source "sound/soc/s3c24xx/Kconfig"
 source "sound/soc/s3c24xx/Kconfig"

+ 2 - 1
sound/soc/Makefile

@@ -1,4 +1,4 @@
-snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o
+snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o
 
 
 obj-$(CONFIG_SND_SOC)	+= snd-soc-core.o
 obj-$(CONFIG_SND_SOC)	+= snd-soc-core.o
 obj-$(CONFIG_SND_SOC)	+= codecs/
 obj-$(CONFIG_SND_SOC)	+= codecs/
@@ -7,6 +7,7 @@ obj-$(CONFIG_SND_SOC)	+= au1x/
 obj-$(CONFIG_SND_SOC)	+= blackfin/
 obj-$(CONFIG_SND_SOC)	+= blackfin/
 obj-$(CONFIG_SND_SOC)	+= davinci/
 obj-$(CONFIG_SND_SOC)	+= davinci/
 obj-$(CONFIG_SND_SOC)	+= fsl/
 obj-$(CONFIG_SND_SOC)	+= fsl/
+obj-$(CONFIG_SND_SOC)   += imx/
 obj-$(CONFIG_SND_SOC)	+= omap/
 obj-$(CONFIG_SND_SOC)	+= omap/
 obj-$(CONFIG_SND_SOC)	+= pxa/
 obj-$(CONFIG_SND_SOC)	+= pxa/
 obj-$(CONFIG_SND_SOC)	+= s3c24xx/
 obj-$(CONFIG_SND_SOC)	+= s3c24xx/

+ 25 - 113
sound/soc/atmel/sam9g20_wm8731.c

@@ -56,30 +56,14 @@
 
 
 #define MCLK_RATE 12000000
 #define MCLK_RATE 12000000
 
 
-static struct clk *mclk;
-
-static int at91sam9g20ek_startup(struct snd_pcm_substream *substream)
-{
-	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
-	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
-	int ret;
-
-	ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK,
-		MCLK_RATE, SND_SOC_CLOCK_IN);
-	if (ret < 0) {
-		clk_disable(mclk);
-		return ret;
-	}
-
-	return 0;
-}
-
-static void at91sam9g20ek_shutdown(struct snd_pcm_substream *substream)
-{
-	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+/*
+ * As shipped the board does not have inputs.  However, it is relatively
+ * straightforward to modify the board to hook them up so support is left
+ * in the driver.
+ */
+#undef ENABLE_MIC_INPUT
 
 
-	dev_dbg(rtd->socdev->dev, "shutdown");
-}
+static struct clk *mclk;
 
 
 static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream,
 static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream,
 	struct snd_pcm_hw_params *params)
 	struct snd_pcm_hw_params *params)
@@ -87,102 +71,17 @@ static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream,
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
 	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
 	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
-	struct atmel_ssc_info *ssc_p = cpu_dai->private_data;
-	struct ssc_device *ssc = ssc_p->ssc;
 	int ret;
 	int ret;
 
 
-	unsigned int rate;
-	int cmr_div, period;
-
-	if (ssc == NULL) {
-		printk(KERN_INFO "at91sam9g20ek_hw_params: ssc is NULL!\n");
-		return -EINVAL;
-	}
-
 	/* set codec DAI configuration */
 	/* set codec DAI configuration */
 	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
 	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
-		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
 	if (ret < 0)
 	if (ret < 0)
 		return ret;
 		return ret;
 
 
 	/* set cpu DAI configuration */
 	/* set cpu DAI configuration */
 	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
 	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
-		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
-	if (ret < 0)
-		return ret;
-
-	/*
-	 * The SSC clock dividers depend on the sample rate.  The CMR.DIV
-	 * field divides the system master clock MCK to drive the SSC TK
-	 * signal which provides the codec BCLK.  The TCMR.PERIOD and
-	 * RCMR.PERIOD fields further divide the BCLK signal to drive
-	 * the SSC TF and RF signals which provide the codec DACLRC and
-	 * ADCLRC clocks.
-	 *
-	 * The dividers were determined through trial and error, where a
-	 * CMR.DIV value is chosen such that the resulting BCLK value is
-	 * divisible, or almost divisible, by (2 * sample rate), and then
-	 * the TCMR.PERIOD or RCMR.PERIOD is BCLK / (2 * sample rate) - 1.
-	 */
-	rate = params_rate(params);
-
-	switch (rate) {
-	case 8000:
-		cmr_div = 55;	/* BCLK = 133MHz/(2*55) = 1.209MHz */
-		period = 74;	/* LRC = BCLK/(2*(74+1)) ~= 8060,6Hz */
-		break;
-	case 11025:
-		cmr_div = 67;	/* BCLK = 133MHz/(2*60) = 1.108MHz */
-		period = 45;	/* LRC = BCLK/(2*(49+1)) = 11083,3Hz */
-		break;
-	case 16000:
-		cmr_div = 63;	/* BCLK = 133MHz/(2*63) = 1.055MHz */
-		period = 32;	/* LRC = BCLK/(2*(32+1)) = 15993,2Hz */
-		break;
-	case 22050:
-		cmr_div = 52;	/* BCLK = 133MHz/(2*52) = 1.278MHz */
-		period = 28;	/* LRC = BCLK/(2*(28+1)) = 22049Hz */
-		break;
-	case 32000:
-		cmr_div = 66;	/* BCLK = 133MHz/(2*66) = 1.007MHz */
-		period = 15;	/* LRC = BCLK/(2*(15+1)) = 31486,742Hz */
-		break;
-	case 44100:
-		cmr_div = 29;	/* BCLK = 133MHz/(2*29) = 2.293MHz */
-		period = 25;	/* LRC = BCLK/(2*(25+1)) = 44098Hz */
-		break;
-	case 48000:
-		cmr_div = 33;	/* BCLK = 133MHz/(2*33) = 2.015MHz */
-		period = 20;	/* LRC = BCLK/(2*(20+1)) = 47979,79Hz */
-		break;
-	case 88200:
-		cmr_div = 29;	/* BCLK = 133MHz/(2*29) = 2.293MHz */
-		period = 12;	/* LRC = BCLK/(2*(12+1)) = 88196Hz */
-		break;
-	case 96000:
-		cmr_div = 23;	/* BCLK = 133MHz/(2*23) = 2.891MHz */
-		period = 14;	/* LRC = BCLK/(2*(14+1)) = 96376Hz */
-		break;
-	default:
-		printk(KERN_WARNING "unsupported rate %d"
-				" on at91sam9g20ek board\n", rate);
-		return -EINVAL;
-	}
-
-	/* set the MCK divider for BCLK */
-	ret = snd_soc_dai_set_clkdiv(cpu_dai, ATMEL_SSC_CMR_DIV, cmr_div);
-	if (ret < 0)
-		return ret;
-
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-		/* set the BCLK divider for DACLRC */
-		ret = snd_soc_dai_set_clkdiv(cpu_dai,
-						ATMEL_SSC_TCMR_PERIOD, period);
-	} else {
-		/* set the BCLK divider for ADCLRC */
-		ret = snd_soc_dai_set_clkdiv(cpu_dai,
-						ATMEL_SSC_RCMR_PERIOD, period);
-	}
+		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
 	if (ret < 0)
 	if (ret < 0)
 		return ret;
 		return ret;
 
 
@@ -190,9 +89,7 @@ static int at91sam9g20ek_hw_params(struct snd_pcm_substream *substream,
 }
 }
 
 
 static struct snd_soc_ops at91sam9g20ek_ops = {
 static struct snd_soc_ops at91sam9g20ek_ops = {
-	.startup = at91sam9g20ek_startup,
 	.hw_params = at91sam9g20ek_hw_params,
 	.hw_params = at91sam9g20ek_hw_params,
-	.shutdown = at91sam9g20ek_shutdown,
 };
 };
 
 
 static int at91sam9g20ek_set_bias_level(struct snd_soc_card *card,
 static int at91sam9g20ek_set_bias_level(struct snd_soc_card *card,
@@ -241,10 +138,20 @@ static const struct snd_soc_dapm_route intercon[] = {
  */
  */
 static int at91sam9g20ek_wm8731_init(struct snd_soc_codec *codec)
 static int at91sam9g20ek_wm8731_init(struct snd_soc_codec *codec)
 {
 {
+	struct snd_soc_dai *codec_dai = &codec->dai[0];
+	int ret;
+
 	printk(KERN_DEBUG
 	printk(KERN_DEBUG
 			"at91sam9g20ek_wm8731 "
 			"at91sam9g20ek_wm8731 "
 			": at91sam9g20ek_wm8731_init() called\n");
 			": at91sam9g20ek_wm8731_init() called\n");
 
 
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK,
+		MCLK_RATE, SND_SOC_CLOCK_IN);
+	if (ret < 0) {
+		printk(KERN_ERR "Failed to set WM8731 SYSCLK: %d\n", ret);
+		return ret;
+	}
+
 	/* Add specific widgets */
 	/* Add specific widgets */
 	snd_soc_dapm_new_controls(codec, at91sam9g20ek_dapm_widgets,
 	snd_soc_dapm_new_controls(codec, at91sam9g20ek_dapm_widgets,
 				  ARRAY_SIZE(at91sam9g20ek_dapm_widgets));
 				  ARRAY_SIZE(at91sam9g20ek_dapm_widgets));
@@ -255,8 +162,13 @@ static int at91sam9g20ek_wm8731_init(struct snd_soc_codec *codec)
 	snd_soc_dapm_nc_pin(codec, "RLINEIN");
 	snd_soc_dapm_nc_pin(codec, "RLINEIN");
 	snd_soc_dapm_nc_pin(codec, "LLINEIN");
 	snd_soc_dapm_nc_pin(codec, "LLINEIN");
 
 
-	/* always connected */
+#ifdef ENABLE_MIC_INPUT
 	snd_soc_dapm_enable_pin(codec, "Int Mic");
 	snd_soc_dapm_enable_pin(codec, "Int Mic");
+#else
+	snd_soc_dapm_nc_pin(codec, "Int Mic");
+#endif
+
+	/* always connected */
 	snd_soc_dapm_enable_pin(codec, "Ext Spk");
 	snd_soc_dapm_enable_pin(codec, "Ext Spk");
 
 
 	snd_soc_dapm_sync(codec);
 	snd_soc_dapm_sync(codec);

+ 96 - 33
sound/soc/au1x/psc-ac97.c

@@ -1,8 +1,8 @@
 /*
 /*
  * Au12x0/Au1550 PSC ALSA ASoC audio support.
  * Au12x0/Au1550 PSC ALSA ASoC audio support.
  *
  *
- * (c) 2007-2008 MSC Vertriebsges.m.b.H.,
- *	Manuel Lauss <mano@roarinelk.homelinux.net>
+ * (c) 2007-2009 MSC Vertriebsges.m.b.H.,
+ *	Manuel Lauss <manuel.lauss@gmail.com>
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  * it under the terms of the GNU General Public License version 2 as
@@ -19,6 +19,7 @@
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/device.h>
 #include <linux/device.h>
 #include <linux/delay.h>
 #include <linux/delay.h>
+#include <linux/mutex.h>
 #include <linux/suspend.h>
 #include <linux/suspend.h>
 #include <sound/core.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm.h>
@@ -29,6 +30,9 @@
 
 
 #include "psc.h"
 #include "psc.h"
 
 
+/* how often to retry failed codec register reads/writes */
+#define AC97_RW_RETRIES	5
+
 #define AC97_DIR	\
 #define AC97_DIR	\
 	(SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
 	(SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
 
 
@@ -45,6 +49,9 @@
 #define AC97PCR_CLRFIFO(stype)	\
 #define AC97PCR_CLRFIFO(stype)	\
 	((stype) == PCM_TX ? PSC_AC97PCR_TC : PSC_AC97PCR_RC)
 	((stype) == PCM_TX ? PSC_AC97PCR_TC : PSC_AC97PCR_RC)
 
 
+#define AC97STAT_BUSY(stype)	\
+	((stype) == PCM_TX ? PSC_AC97STAT_TB : PSC_AC97STAT_RB)
+
 /* instance data. There can be only one, MacLeod!!!! */
 /* instance data. There can be only one, MacLeod!!!! */
 static struct au1xpsc_audio_data *au1xpsc_ac97_workdata;
 static struct au1xpsc_audio_data *au1xpsc_ac97_workdata;
 
 
@@ -54,24 +61,33 @@ static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97,
 {
 {
 	/* FIXME */
 	/* FIXME */
 	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
 	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
-	unsigned short data, tmo;
+	unsigned short data, retry, tmo;
 
 
-	au_writel(PSC_AC97CDC_RD | PSC_AC97CDC_INDX(reg), AC97_CDC(pscdata));
+	au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
 	au_sync();
 	au_sync();
 
 
-	tmo = 1000;
-	while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) && --tmo)
-		udelay(2);
+	retry = AC97_RW_RETRIES;
+	do {
+		mutex_lock(&pscdata->lock);
+
+		au_writel(PSC_AC97CDC_RD | PSC_AC97CDC_INDX(reg),
+			  AC97_CDC(pscdata));
+		au_sync();
+
+		tmo = 2000;
+		while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD))
+			&& --tmo)
+			udelay(2);
 
 
-	if (!tmo)
-		data = 0xffff;
-	else
 		data = au_readl(AC97_CDC(pscdata)) & 0xffff;
 		data = au_readl(AC97_CDC(pscdata)) & 0xffff;
 
 
-	au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
-	au_sync();
+		au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
+		au_sync();
+
+		mutex_unlock(&pscdata->lock);
+	} while (--retry && !tmo);
 
 
-	return data;
+	return retry ? data : 0xffff;
 }
 }
 
 
 /* AC97 controller writes to codec register */
 /* AC97 controller writes to codec register */
@@ -80,16 +96,29 @@ static void au1xpsc_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
 {
 {
 	/* FIXME */
 	/* FIXME */
 	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
 	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
-	unsigned int tmo;
+	unsigned int tmo, retry;
 
 
-	au_writel(PSC_AC97CDC_INDX(reg) | (val & 0xffff), AC97_CDC(pscdata));
+	au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
 	au_sync();
 	au_sync();
-	tmo = 1000;
-	while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) && --tmo)
+
+	retry = AC97_RW_RETRIES;
+	do {
+		mutex_lock(&pscdata->lock);
+
+		au_writel(PSC_AC97CDC_INDX(reg) | (val & 0xffff),
+			  AC97_CDC(pscdata));
 		au_sync();
 		au_sync();
 
 
-	au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
-	au_sync();
+		tmo = 2000;
+		while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD))
+		       && --tmo)
+			udelay(2);
+
+		au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
+		au_sync();
+
+		mutex_unlock(&pscdata->lock);
+	} while (--retry && !tmo);
 }
 }
 
 
 /* AC97 controller asserts a warm reset */
 /* AC97 controller asserts a warm reset */
@@ -129,9 +158,9 @@ static void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97)
 	au_sync();
 	au_sync();
 
 
 	/* wait for PSC to indicate it's ready */
 	/* wait for PSC to indicate it's ready */
-	i = 100000;
+	i = 1000;
 	while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_SR)) && (--i))
 	while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_SR)) && (--i))
-		au_sync();
+		msleep(1);
 
 
 	if (i == 0) {
 	if (i == 0) {
 		printk(KERN_ERR "au1xpsc-ac97: PSC not ready!\n");
 		printk(KERN_ERR "au1xpsc-ac97: PSC not ready!\n");
@@ -143,9 +172,9 @@ static void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97)
 	au_sync();
 	au_sync();
 
 
 	/* wait for AC97 core to become ready */
 	/* wait for AC97 core to become ready */
-	i = 100000;
+	i = 1000;
 	while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && (--i))
 	while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && (--i))
-		au_sync();
+		msleep(1);
 	if (i == 0)
 	if (i == 0)
 		printk(KERN_ERR "au1xpsc-ac97: AC97 ctrl not ready\n");
 		printk(KERN_ERR "au1xpsc-ac97: AC97 ctrl not ready\n");
 }
 }
@@ -165,12 +194,12 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
 {
 {
 	/* FIXME */
 	/* FIXME */
 	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
 	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
-	unsigned long r, stat;
+	unsigned long r, ro, stat;
 	int chans, stype = SUBSTREAM_TYPE(substream);
 	int chans, stype = SUBSTREAM_TYPE(substream);
 
 
 	chans = params_channels(params);
 	chans = params_channels(params);
 
 
-	r = au_readl(AC97_CFG(pscdata));
+	r = ro = au_readl(AC97_CFG(pscdata));
 	stat = au_readl(AC97_STAT(pscdata));
 	stat = au_readl(AC97_STAT(pscdata));
 
 
 	/* already active? */
 	/* already active? */
@@ -180,9 +209,6 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
 		    (pscdata->rate != params_rate(params)))
 		    (pscdata->rate != params_rate(params)))
 			return -EINVAL;
 			return -EINVAL;
 	} else {
 	} else {
-		/* disable AC97 device controller first */
-		au_writel(r & ~PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
-		au_sync();
 
 
 		/* set sample bitdepth: REG[24:21]=(BITS-2)/2 */
 		/* set sample bitdepth: REG[24:21]=(BITS-2)/2 */
 		r &= ~PSC_AC97CFG_LEN_MASK;
 		r &= ~PSC_AC97CFG_LEN_MASK;
@@ -199,14 +225,40 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
 			r |= PSC_AC97CFG_RXSLOT_ENA(4);
 			r |= PSC_AC97CFG_RXSLOT_ENA(4);
 		}
 		}
 
 
-		/* finally enable the AC97 controller again */
+		/* do we need to poke the hardware? */
+		if (!(r ^ ro))
+			goto out;
+
+		/* ac97 engine is about to be disabled */
+		mutex_lock(&pscdata->lock);
+
+		/* disable AC97 device controller first... */
+		au_writel(r & ~PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
+		au_sync();
+
+		/* ...wait for it... */
+		while (au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)
+			asm volatile ("nop");
+
+		/* ...write config... */
+		au_writel(r, AC97_CFG(pscdata));
+		au_sync();
+
+		/* ...enable the AC97 controller again... */
 		au_writel(r | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
 		au_writel(r | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata));
 		au_sync();
 		au_sync();
 
 
+		/* ...and wait for ready bit */
+		while (!(au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR))
+			asm volatile ("nop");
+
+		mutex_unlock(&pscdata->lock);
+
 		pscdata->cfg = r;
 		pscdata->cfg = r;
 		pscdata->rate = params_rate(params);
 		pscdata->rate = params_rate(params);
 	}
 	}
 
 
+out:
 	return 0;
 	return 0;
 }
 }
 
 
@@ -222,6 +274,8 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
 	switch (cmd) {
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
 	case SNDRV_PCM_TRIGGER_START:
 	case SNDRV_PCM_TRIGGER_RESUME:
 	case SNDRV_PCM_TRIGGER_RESUME:
+		au_writel(AC97PCR_CLRFIFO(stype), AC97_PCR(pscdata));
+		au_sync();
 		au_writel(AC97PCR_START(stype), AC97_PCR(pscdata));
 		au_writel(AC97PCR_START(stype), AC97_PCR(pscdata));
 		au_sync();
 		au_sync();
 		break;
 		break;
@@ -229,6 +283,13 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 		au_writel(AC97PCR_STOP(stype), AC97_PCR(pscdata));
 		au_writel(AC97PCR_STOP(stype), AC97_PCR(pscdata));
 		au_sync();
 		au_sync();
+
+		while (au_readl(AC97_STAT(pscdata)) & AC97STAT_BUSY(stype))
+			asm volatile ("nop");
+
+		au_writel(AC97PCR_CLRFIFO(stype), AC97_PCR(pscdata));
+		au_sync();
+
 		break;
 		break;
 	default:
 	default:
 		ret = -EINVAL;
 		ret = -EINVAL;
@@ -251,6 +312,8 @@ static int au1xpsc_ac97_probe(struct platform_device *pdev,
 	if (!au1xpsc_ac97_workdata)
 	if (!au1xpsc_ac97_workdata)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
+	mutex_init(&au1xpsc_ac97_workdata->lock);
+
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!r) {
 	if (!r) {
 		ret = -ENODEV;
 		ret = -ENODEV;
@@ -269,9 +332,9 @@ static int au1xpsc_ac97_probe(struct platform_device *pdev,
 		goto out1;
 		goto out1;
 
 
 	/* configuration: max dma trigger threshold, enable ac97 */
 	/* configuration: max dma trigger threshold, enable ac97 */
-	 au1xpsc_ac97_workdata->cfg = PSC_AC97CFG_RT_FIFO8 |
-				      PSC_AC97CFG_TT_FIFO8 |
-				      PSC_AC97CFG_DE_ENABLE;
+	au1xpsc_ac97_workdata->cfg = PSC_AC97CFG_RT_FIFO8 |
+				     PSC_AC97CFG_TT_FIFO8 |
+				     PSC_AC97CFG_DE_ENABLE;
 
 
 	/* preserve PSC clock source set up by platform (dev.platform_data
 	/* preserve PSC clock source set up by platform (dev.platform_data
 	 * is already occupied by soc layer)
 	 * is already occupied by soc layer)
@@ -386,4 +449,4 @@ module_exit(au1xpsc_ac97_exit);
 
 
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver");
 MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver");
-MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
+MODULE_AUTHOR("Manuel Lauss <manuel.lauss@gmail.com>");

部分文件因为文件数量过多而无法显示