Browse Source

Merge branch 'topic/hda' into to-push

Takashi Iwai 16 years ago
parent
commit
a65056205c

+ 14 - 313
Documentation/sound/alsa/ALSA-Configuration.txt

@@ -757,6 +757,8 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     model	- force the model name
     model	- force the model name
     position_fix - Fix DMA pointer (0 = auto, 1 = use LPIB, 2 = POSBUF)
     position_fix - Fix DMA pointer (0 = auto, 1 = use LPIB, 2 = POSBUF)
     probe_mask  - Bitmask to probe codecs (default = -1, meaning all slots)
     probe_mask  - Bitmask to probe codecs (default = -1, meaning all slots)
+    probe_only	- Only probing and no codec initialization (default=off);
+		  Useful to check the initial codec status for debugging
     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.
@@ -772,327 +774,23 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
 
     This module supports multiple cards and autoprobe.
     This module supports multiple cards and autoprobe.
     
     
+    See Documentation/sound/alsa/HD-Audio.txt for more details about
+    HD-audio driver.
+
     Each codec may have a model table for different configurations.
     Each codec may have a model table for different configurations.
     If your machine isn't listed there, the default (usually minimal)
     If your machine isn't listed there, the default (usually minimal)
     configuration is set up.  You can pass "model=<name>" option to
     configuration is set up.  You can pass "model=<name>" option to
     specify a certain model in such a case.  There are different
     specify a certain model in such a case.  There are different
-    models depending on the codec chip.
-
-	  Model name	Description
-	  ----------    -----------
-	ALC880
-	  3stack	3-jack in back and a headphone out
-	  3stack-digout	3-jack in back, a HP out and a SPDIF out
-	  5stack	5-jack in back, 2-jack in front
-	  5stack-digout	5-jack in back, 2-jack in front, a SPDIF out
-	  6stack	6-jack in back, 2-jack in front
-	  6stack-digout	6-jack with a SPDIF out
-	  w810		3-jack
-	  z71v		3-jack (HP shared SPDIF)
-	  asus		3-jack (ASUS Mobo)
-	  asus-w1v	ASUS W1V
-	  asus-dig	ASUS with SPDIF out
-	  asus-dig2	ASUS with SPDIF out (using GPIO2)
-	  uniwill	3-jack
-	  fujitsu	Fujitsu Laptops (Pi1536)
-	  F1734		2-jack
-	  lg		LG laptop (m1 express dual)
-	  lg-lw		LG LW20/LW25 laptop
-	  tcl		TCL S700
-	  clevo		Clevo laptops (m520G, m665n)
-	  medion	Medion Rim 2150
-	  test		for testing/debugging purpose, almost all controls can be
-			adjusted.  Appearing only when compiled with
-			$CONFIG_SND_DEBUG=y
-	  auto		auto-config reading BIOS (default)
-
-	ALC260
-	  hp		HP machines
-	  hp-3013	HP machines (3013-variant)
-	  hp-dc7600	HP DC7600
-	  fujitsu	Fujitsu S7020
-	  acer		Acer TravelMate
-	  will		Will laptops (PB V7900)
-	  replacer	Replacer 672V
-	  basic		fixed pin assignment (old default model)
-	  test		for testing/debugging purpose, almost all controls can
-			adjusted.  Appearing only when compiled with
-			$CONFIG_SND_DEBUG=y
-	  auto		auto-config reading BIOS (default)
-
-	ALC262
-	  fujitsu	Fujitsu Laptop
-	  hp-bpc	HP xw4400/6400/8400/9400 laptops
-	  hp-bpc-d7000	HP BPC D7000
-	  hp-tc-t5735	HP Thin Client T5735
-	  hp-rp5700	HP RP5700
-	  benq		Benq ED8
-	  benq-t31	Benq T31
-	  hippo		Hippo (ATI) with jack detection, Sony UX-90s
-	  hippo_1	Hippo (Benq) with jack detection
-	  sony-assamd	Sony ASSAMD
-	  toshiba-s06	Toshiba S06
-	  toshiba-rx1	Toshiba RX1
-	  ultra		Samsung Q1 Ultra Vista model
-	  lenovo-3000	Lenovo 3000 y410
-	  nec		NEC Versa S9100
-	  basic		fixed pin assignment w/o SPDIF
-	  auto		auto-config reading BIOS (default)
-
-	ALC267/268
-	  quanta-il1	Quanta IL1 mini-notebook
-	  3stack	3-stack model
-	  toshiba	Toshiba A205
-	  acer		Acer laptops
-	  acer-aspire	Acer Aspire One
-	  dell		Dell OEM laptops (Vostro 1200)
-	  zepto		Zepto laptops
-	  test		for testing/debugging purpose, almost all controls can
-			adjusted.  Appearing only when compiled with
-			$CONFIG_SND_DEBUG=y
-	  auto		auto-config reading BIOS (default)
-
-	ALC269
-	  basic		Basic preset
-	  quanta	Quanta FL1
-	  eeepc-p703	ASUS Eeepc P703 P900A
-	  eeepc-p901	ASUS Eeepc P901 S101
-
-	ALC662/663
-	  3stack-dig	3-stack (2-channel) with SPDIF
-	  3stack-6ch	 3-stack (6-channel)
-	  3stack-6ch-dig 3-stack (6-channel) with SPDIF
-	  6stack-dig	 6-stack with SPDIF
-	  lenovo-101e	 Lenovo laptop
-	  eeepc-p701	ASUS Eeepc P701
-	  eeepc-ep20	ASUS Eeepc EP20
-	  ecs		ECS/Foxconn mobo
-	  m51va		ASUS M51VA
-	  g71v		ASUS G71V
-	  h13		ASUS H13
-	  g50v		ASUS G50V
-	  asus-mode1	ASUS
-	  asus-mode2	ASUS
-	  asus-mode3	ASUS
-	  asus-mode4	ASUS
-	  asus-mode5	ASUS
-	  asus-mode6	ASUS
-	  auto		auto-config reading BIOS (default)
-
-	ALC882/885
-	  3stack-dig	3-jack with SPDIF I/O
-	  6stack-dig	6-jack digital with SPDIF I/O
-	  arima		Arima W820Di1
-	  targa		Targa T8, MSI-1049 T8
-	  asus-a7j	ASUS A7J
-	  asus-a7m	ASUS A7M
-	  macpro	MacPro support
-	  mbp3		Macbook Pro rev3
-	  imac24	iMac 24'' with jack detection
-	  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-6ch    3-jack 6-channel
-	  3stack-6ch-dig 3-jack 6-channel with SPDIF I/O
-	  6stack-dig-demo  6-jack digital for Intel demo board
-	  acer		Acer laptops (Travelmate 3012WTMi, Aspire 5600, etc)
-	  acer-aspire	Acer Aspire 9810
-	  medion	Medion Laptops
-	  medion-md2	Medion MD2
-	  targa-dig	Targa/MSI
-	  targa-2ch-dig	Targs/MSI with 2-channel
-	  laptop-eapd   3-jack with SPDIF I/O and EAPD (Clevo M540JE, M550JE)
-	  lenovo-101e	Lenovo 101E
-	  lenovo-nb0763	Lenovo NB0763
-	  lenovo-ms7195-dig Lenovo MS7195
-	  lenovo-sky	Lenovo Sky
-	  haier-w66	Haier W66
-	  3stack-hp	HP machines with 3stack (Lucknow, Samba boards)
-	  6stack-dell	Dell machines with 6stack (Inspiron 530)
-	  mitac		Mitac 8252D
-	  clevo-m720	Clevo M720 laptop series
-	  fujitsu-pi2515 Fujitsu AMILO Pi2515
-	  3stack-6ch-intel Intel DG33* boards
-	  auto		auto-config reading BIOS (default)
-
-	ALC861/660
-	  3stack	3-jack
-	  3stack-dig	3-jack with SPDIF I/O
-	  6stack-dig	6-jack with SPDIF I/O
-	  3stack-660	3-jack (for ALC660)
-	  uniwill-m31	Uniwill M31 laptop
-	  toshiba	Toshiba laptop support
-	  asus		Asus laptop support
-	  asus-laptop	ASUS F2/F3 laptops
-	  auto		auto-config reading BIOS (default)
-
-	ALC861VD/660VD
-	  3stack	3-jack
-	  3stack-dig	3-jack with SPDIF OUT
-	  6stack-dig	6-jack with SPDIF OUT
-	  3stack-660	3-jack (for ALC660VD)
-	  3stack-660-digout 3-jack with SPDIF OUT (for ALC660VD)
-	  lenovo	Lenovo 3000 C200
-	  dallas	Dallas laptops
-	  hp		HP TX1000
-	  auto		auto-config reading BIOS (default)
-
-	CMI9880
-	  minimal	3-jack in back
-	  min_fp	3-jack in back, 2-jack in front
-	  full		6-jack in back, 2-jack in front
-	  full_dig	6-jack in back, 2-jack in front, SPDIF I/O
-	  allout	5-jack in back, 2-jack in front, SPDIF out
-	  auto		auto-config reading BIOS (default)
-
-	AD1882 / AD1882A
-	  3stack	3-stack mode (default)
-	  6stack	6-stack mode
-
-	AD1884A / AD1883 / AD1984A / AD1984B
-	  desktop	3-stack desktop (default)
-	  laptop	laptop with HP jack sensing
-	  mobile	mobile devices with HP jack sensing
-	  thinkpad	Lenovo Thinkpad X300
-
-	AD1884
-	  N/A
-
-	AD1981
-	  basic		3-jack (default)
-	  hp		HP nx6320
-	  thinkpad	Lenovo Thinkpad T60/X60/Z60
-	  toshiba	Toshiba U205
-
-	AD1983
-	  N/A
-
-	AD1984
-	  basic		default configuration
-	  thinkpad	Lenovo Thinkpad T61/X61
-	  dell		Dell T3400
-
-	AD1986A
-	  6stack	6-jack, separate surrounds (default)
-	  3stack	3-stack, shared surrounds
-	  laptop	2-channel only (FSC V2060, Samsung M50)
-	  laptop-eapd	2-channel with EAPD (Samsung R65, ASUS A6J)
-	  laptop-automute 2-channel with EAPD and HP-automute (Lenovo N100)
-	  ultra		2-channel with EAPD (Samsung Ultra tablet PC)
-
-	AD1988/AD1988B/AD1989A/AD1989B
-	  6stack	6-jack
-	  6stack-dig	ditto with SPDIF
-	  3stack	3-jack
-	  3stack-dig	ditto with SPDIF
-	  laptop	3-jack with hp-jack automute
-	  laptop-dig	ditto with SPDIF
-	  auto		auto-config reading BIOS (default)
-	
-	Conexant 5045
-	  laptop-hpsense    Laptop with HP sense (old model laptop)
-	  laptop-micsense   Laptop with Mic sense (old model fujitsu)
-	  laptop-hpmicsense Laptop with HP and Mic senses
-	  benq		Benq R55E
-	  test		for testing/debugging purpose, almost all controls
-			can be adjusted.  Appearing only when compiled with
-			$CONFIG_SND_DEBUG=y
-
-	Conexant 5047
-	  laptop	Basic Laptop config 
-	  laptop-hp	Laptop config for some HP models (subdevice 30A5)
-	  laptop-eapd	Laptop config with EAPD support
-	  test		for testing/debugging purpose, almost all controls
-			can be adjusted.  Appearing only when compiled with
-			$CONFIG_SND_DEBUG=y
-
-	Conexant 5051
-	  laptop	Basic Laptop config (default)
-	  hp		HP Spartan laptop
-
-	STAC9200
-	  ref		Reference board
-	  dell-d21	Dell (unknown)
-	  dell-d22	Dell (unknown)
-	  dell-d23	Dell (unknown)
-	  dell-m21	Dell Inspiron 630m, Dell Inspiron 640m
-	  dell-m22	Dell Latitude D620, Dell Latitude D820
-	  dell-m23	Dell XPS M1710, Dell Precision M90
-	  dell-m24	Dell Latitude 120L
-	  dell-m25	Dell Inspiron E1505n
-	  dell-m26	Dell Inspiron 1501
-	  dell-m27	Dell Inspiron E1705/9400
-	  gateway	Gateway laptops with EAPD control
-	  panasonic	Panasonic CF-74
-
-	STAC9205/9254
-	  ref		Reference board
-	  dell-m42	Dell (unknown)
-	  dell-m43	Dell Precision
-	  dell-m44	Dell Inspiron
-
-	STAC9220/9221
-	  ref		Reference board
-	  3stack	D945 3stack
-	  5stack	D945 5stack + SPDIF
-	  intel-mac-v1	Intel Mac Type 1
-	  intel-mac-v2	Intel Mac Type 2
-	  intel-mac-v3	Intel Mac Type 3
-	  intel-mac-v4	Intel Mac Type 4
-	  intel-mac-v5	Intel Mac Type 5
-	  intel-mac-auto Intel Mac (detect type according to subsystem id)
-	  macmini	Intel Mac Mini (equivalent with type 3)
-	  macbook	Intel Mac Book (eq. type 5)
-	  macbook-pro-v1 Intel Mac Book Pro 1st generation (eq. type 3)
-	  macbook-pro	Intel Mac Book Pro 2nd generation (eq. type 3)
-	  imac-intel	Intel iMac (eq. type 2)
-	  imac-intel-20	Intel iMac (newer version) (eq. type 3)
-	  dell-d81	Dell (unknown)
-	  dell-d82	Dell (unknown)
-	  dell-m81	Dell (unknown)
-	  dell-m82	Dell XPS M1210
-
-	STAC9202/9250/9251
-	  ref		Reference board, base config
-	  m2-2		Some Gateway MX series laptops
-	  m6		Some Gateway NX series laptops
-	  pa6		Gateway NX860 series
-
-	STAC9227/9228/9229/927x
-	  ref		Reference board
-	  ref-no-jd	Reference board without HP/Mic jack detection
-	  3stack	D965 3stack
-	  5stack	D965 5stack + SPDIF
-	  dell-3stack	Dell Dimension E520
-	  dell-bios	Fixes with Dell BIOS setup
-
-	STAC92HD71B*
-	  ref		Reference board
-	  dell-m4-1	Dell desktops
-	  dell-m4-2	Dell desktops
-	  dell-m4-3	Dell desktops
-
-	STAC92HD73*
-	  ref		Reference board
-	  no-jd		BIOS setup but without jack-detection
-	  dell-m6-amic	Dell desktops/laptops with analog mics
-	  dell-m6-dmic	Dell desktops/laptops with digital mics
-	  dell-m6	Dell desktops/laptops with both type of mics
-
-	STAC9872
-	  vaio		Setup for VAIO FE550G/SZ110
-	  vaio-ar Setup for VAIO AR
+    models depending on the codec chip.  The list of available models
+    is found in HD-Audio-Models.txt
 
 
     The model name "genric" is treated as a special case.  When this
     The model name "genric" is treated as a special case.  When this
     model is given, the driver uses the generic codec parser without
     model is given, the driver uses the generic codec parser without
     "codec-patch".  It's sometimes good for testing and debugging.
     "codec-patch".  It's sometimes good for testing and debugging.
 
 
     If the default configuration doesn't work and one of the above
     If the default configuration doesn't work and one of the above
-    matches with your device, report it together with the PCI
-    subsystem ID (output of "lspci -nv") to ALSA BTS or alsa-devel
+    matches with your device, report it together with alsa-info.sh
+    output (with --no-upload option) to kernel bugzilla or alsa-devel
     ML (see the section "Links and Addresses").
     ML (see the section "Links and Addresses").
 
 
     power_save and power_save_controller options are for power-saving
     power_save and power_save_controller options are for power-saving
@@ -2409,8 +2107,11 @@ Links and Addresses
   ALSA project homepage
   ALSA project homepage
        http://www.alsa-project.org
        http://www.alsa-project.org
 
 
-  ALSA Bug Tracking System
-       https://bugtrack.alsa-project.org/bugs/
+  Kernel Bugzilla
+       http://bugzilla.kernel.org/
 
 
   ALSA Developers ML
   ALSA Developers ML
        mailto:alsa-devel@alsa-project.org
        mailto:alsa-devel@alsa-project.org
+
+  alsa-info.sh script
+       http://www.alsa-project.org/alsa-info.sh

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

@@ -0,0 +1,348 @@
+  Model name	Description
+  ----------    -----------
+ALC880
+======
+  3stack	3-jack in back and a headphone out
+  3stack-digout	3-jack in back, a HP out and a SPDIF out
+  5stack	5-jack in back, 2-jack in front
+  5stack-digout	5-jack in back, 2-jack in front, a SPDIF out
+  6stack	6-jack in back, 2-jack in front
+  6stack-digout	6-jack with a SPDIF out
+  w810		3-jack
+  z71v		3-jack (HP shared SPDIF)
+  asus		3-jack (ASUS Mobo)
+  asus-w1v	ASUS W1V
+  asus-dig	ASUS with SPDIF out
+  asus-dig2	ASUS with SPDIF out (using GPIO2)
+  uniwill	3-jack
+  fujitsu	Fujitsu Laptops (Pi1536)
+  F1734		2-jack
+  lg		LG laptop (m1 express dual)
+  lg-lw		LG LW20/LW25 laptop
+  tcl		TCL S700
+  clevo		Clevo laptops (m520G, m665n)
+  medion	Medion Rim 2150
+  test		for testing/debugging purpose, almost all controls can be
+		adjusted.  Appearing only when compiled with
+		$CONFIG_SND_DEBUG=y
+  auto		auto-config reading BIOS (default)
+
+ALC260
+======
+  hp		HP machines
+  hp-3013	HP machines (3013-variant)
+  hp-dc7600	HP DC7600
+  fujitsu	Fujitsu S7020
+  acer		Acer TravelMate
+  will		Will laptops (PB V7900)
+  replacer	Replacer 672V
+  basic		fixed pin assignment (old default model)
+  test		for testing/debugging purpose, almost all controls can
+		adjusted.  Appearing only when compiled with
+		$CONFIG_SND_DEBUG=y
+  auto		auto-config reading BIOS (default)
+
+ALC262
+======
+  fujitsu	Fujitsu Laptop
+  hp-bpc	HP xw4400/6400/8400/9400 laptops
+  hp-bpc-d7000	HP BPC D7000
+  hp-tc-t5735	HP Thin Client T5735
+  hp-rp5700	HP RP5700
+  benq		Benq ED8
+  benq-t31	Benq T31
+  hippo		Hippo (ATI) with jack detection, Sony UX-90s
+  hippo_1	Hippo (Benq) with jack detection
+  sony-assamd	Sony ASSAMD
+  toshiba-s06	Toshiba S06
+  toshiba-rx1	Toshiba RX1
+  ultra		Samsung Q1 Ultra Vista model
+  lenovo-3000	Lenovo 3000 y410
+  nec		NEC Versa S9100
+  basic		fixed pin assignment w/o SPDIF
+  auto		auto-config reading BIOS (default)
+
+ALC267/268
+==========
+  quanta-il1	Quanta IL1 mini-notebook
+  3stack	3-stack model
+  toshiba	Toshiba A205
+  acer		Acer laptops
+  acer-dmic	Acer laptops with digital-mic
+  acer-aspire	Acer Aspire One
+  dell		Dell OEM laptops (Vostro 1200)
+  zepto		Zepto laptops
+  test		for testing/debugging purpose, almost all controls can
+		adjusted.  Appearing only when compiled with
+		$CONFIG_SND_DEBUG=y
+  auto		auto-config reading BIOS (default)
+
+ALC269
+======
+  basic		Basic preset
+  quanta	Quanta FL1
+  eeepc-p703	ASUS Eeepc P703 P900A
+  eeepc-p901	ASUS Eeepc P901 S101
+  fujitsu	FSC Amilo
+  auto		auto-config reading BIOS (default)
+
+ALC662/663
+==========
+  3stack-dig	3-stack (2-channel) with SPDIF
+  3stack-6ch	 3-stack (6-channel)
+  3stack-6ch-dig 3-stack (6-channel) with SPDIF
+  6stack-dig	 6-stack with SPDIF
+  lenovo-101e	 Lenovo laptop
+  eeepc-p701	ASUS Eeepc P701
+  eeepc-ep20	ASUS Eeepc EP20
+  ecs		ECS/Foxconn mobo
+  m51va		ASUS M51VA
+  g71v		ASUS G71V
+  h13		ASUS H13
+  g50v		ASUS G50V
+  asus-mode1	ASUS
+  asus-mode2	ASUS
+  asus-mode3	ASUS
+  asus-mode4	ASUS
+  asus-mode5	ASUS
+  asus-mode6	ASUS
+  auto		auto-config reading BIOS (default)
+
+ALC882/885
+==========
+  3stack-dig	3-jack with SPDIF I/O
+  6stack-dig	6-jack digital with SPDIF I/O
+  arima		Arima W820Di1
+  targa		Targa T8, MSI-1049 T8
+  asus-a7j	ASUS A7J
+  asus-a7m	ASUS A7M
+  macpro	MacPro support
+  mbp3		Macbook Pro rev3
+  imac24	iMac 24'' with jack detection
+  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-6ch    3-jack 6-channel
+  3stack-6ch-dig 3-jack 6-channel with SPDIF I/O
+  6stack-dig-demo  6-jack digital for Intel demo board
+  acer		Acer laptops (Travelmate 3012WTMi, Aspire 5600, etc)
+  acer-aspire	Acer Aspire 9810
+  acer-aspire-4930g Acer Aspire 4930G
+  medion	Medion Laptops
+  medion-md2	Medion MD2
+  targa-dig	Targa/MSI
+  targa-2ch-dig	Targs/MSI with 2-channel
+  laptop-eapd   3-jack with SPDIF I/O and EAPD (Clevo M540JE, M550JE)
+  lenovo-101e	Lenovo 101E
+  lenovo-nb0763	Lenovo NB0763
+  lenovo-ms7195-dig Lenovo MS7195
+  lenovo-sky	Lenovo Sky
+  haier-w66	Haier W66
+  3stack-hp	HP machines with 3stack (Lucknow, Samba boards)
+  6stack-dell	Dell machines with 6stack (Inspiron 530)
+  mitac		Mitac 8252D
+  clevo-m720	Clevo M720 laptop series
+  fujitsu-pi2515 Fujitsu AMILO Pi2515
+  fujitsu-xa3530 Fujitsu AMILO XA3530
+  3stack-6ch-intel Intel DG33* boards
+  auto		auto-config reading BIOS (default)
+
+ALC861/660
+==========
+  3stack	3-jack
+  3stack-dig	3-jack with SPDIF I/O
+  6stack-dig	6-jack with SPDIF I/O
+  3stack-660	3-jack (for ALC660)
+  uniwill-m31	Uniwill M31 laptop
+  toshiba	Toshiba laptop support
+  asus		Asus laptop support
+  asus-laptop	ASUS F2/F3 laptops
+  auto		auto-config reading BIOS (default)
+
+ALC861VD/660VD
+==============
+  3stack	3-jack
+  3stack-dig	3-jack with SPDIF OUT
+  6stack-dig	6-jack with SPDIF OUT
+  3stack-660	3-jack (for ALC660VD)
+  3stack-660-digout 3-jack with SPDIF OUT (for ALC660VD)
+  lenovo	Lenovo 3000 C200
+  dallas	Dallas laptops
+  hp		HP TX1000
+  asus-v1s	ASUS V1Sn
+  auto		auto-config reading BIOS (default)
+
+CMI9880
+=======
+  minimal	3-jack in back
+  min_fp	3-jack in back, 2-jack in front
+  full		6-jack in back, 2-jack in front
+  full_dig	6-jack in back, 2-jack in front, SPDIF I/O
+  allout	5-jack in back, 2-jack in front, SPDIF out
+  auto		auto-config reading BIOS (default)
+
+AD1882 / AD1882A
+================
+  3stack	3-stack mode (default)
+  6stack	6-stack mode
+
+AD1884A / AD1883 / AD1984A / AD1984B
+====================================
+  desktop	3-stack desktop (default)
+  laptop	laptop with HP jack sensing
+  mobile	mobile devices with HP jack sensing
+  thinkpad	Lenovo Thinkpad X300
+
+AD1884
+======
+  N/A
+
+AD1981
+======
+  basic		3-jack (default)
+  hp		HP nx6320
+  thinkpad	Lenovo Thinkpad T60/X60/Z60
+  toshiba	Toshiba U205
+
+AD1983
+======
+  N/A
+
+AD1984
+======
+  basic		default configuration
+  thinkpad	Lenovo Thinkpad T61/X61
+  dell		Dell T3400
+
+AD1986A
+=======
+  6stack	6-jack, separate surrounds (default)
+  3stack	3-stack, shared surrounds
+  laptop	2-channel only (FSC V2060, Samsung M50)
+  laptop-eapd	2-channel with EAPD (ASUS A6J)
+  laptop-automute 2-channel with EAPD and HP-automute (Lenovo N100)
+  ultra		2-channel with EAPD (Samsung Ultra tablet PC)
+  samsung	2-channel with EAPD (Samsung R65)
+
+AD1988/AD1988B/AD1989A/AD1989B
+==============================
+  6stack	6-jack
+  6stack-dig	ditto with SPDIF
+  3stack	3-jack
+  3stack-dig	ditto with SPDIF
+  laptop	3-jack with hp-jack automute
+  laptop-dig	ditto with SPDIF
+  auto		auto-config reading BIOS (default)
+
+Conexant 5045
+=============
+  laptop-hpsense    Laptop with HP sense (old model laptop)
+  laptop-micsense   Laptop with Mic sense (old model fujitsu)
+  laptop-hpmicsense Laptop with HP and Mic senses
+  benq		Benq R55E
+  test		for testing/debugging purpose, almost all controls
+		can be adjusted.  Appearing only when compiled with
+		$CONFIG_SND_DEBUG=y
+
+Conexant 5047
+=============
+  laptop	Basic Laptop config 
+  laptop-hp	Laptop config for some HP models (subdevice 30A5)
+  laptop-eapd	Laptop config with EAPD support
+  test		for testing/debugging purpose, almost all controls
+		can be adjusted.  Appearing only when compiled with
+		$CONFIG_SND_DEBUG=y
+
+Conexant 5051
+=============
+  laptop	Basic Laptop config (default)
+  hp		HP Spartan laptop
+
+STAC9200
+========
+  ref		Reference board
+  dell-d21	Dell (unknown)
+  dell-d22	Dell (unknown)
+  dell-d23	Dell (unknown)
+  dell-m21	Dell Inspiron 630m, Dell Inspiron 640m
+  dell-m22	Dell Latitude D620, Dell Latitude D820
+  dell-m23	Dell XPS M1710, Dell Precision M90
+  dell-m24	Dell Latitude 120L
+  dell-m25	Dell Inspiron E1505n
+  dell-m26	Dell Inspiron 1501
+  dell-m27	Dell Inspiron E1705/9400
+  gateway	Gateway laptops with EAPD control
+  panasonic	Panasonic CF-74
+
+STAC9205/9254
+=============
+  ref		Reference board
+  dell-m42	Dell (unknown)
+  dell-m43	Dell Precision
+  dell-m44	Dell Inspiron
+
+STAC9220/9221
+=============
+  ref		Reference board
+  3stack	D945 3stack
+  5stack	D945 5stack + SPDIF
+  intel-mac-v1	Intel Mac Type 1
+  intel-mac-v2	Intel Mac Type 2
+  intel-mac-v3	Intel Mac Type 3
+  intel-mac-v4	Intel Mac Type 4
+  intel-mac-v5	Intel Mac Type 5
+  intel-mac-auto Intel Mac (detect type according to subsystem id)
+  macmini	Intel Mac Mini (equivalent with type 3)
+  macbook	Intel Mac Book (eq. type 5)
+  macbook-pro-v1 Intel Mac Book Pro 1st generation (eq. type 3)
+  macbook-pro	Intel Mac Book Pro 2nd generation (eq. type 3)
+  imac-intel	Intel iMac (eq. type 2)
+  imac-intel-20	Intel iMac (newer version) (eq. type 3)
+  dell-d81	Dell (unknown)
+  dell-d82	Dell (unknown)
+  dell-m81	Dell (unknown)
+  dell-m82	Dell XPS M1210
+
+STAC9202/9250/9251
+==================
+  ref		Reference board, base config
+  m2-2		Some Gateway MX series laptops
+  m6		Some Gateway NX series laptops
+  pa6		Gateway NX860 series
+
+STAC9227/9228/9229/927x
+=======================
+  ref		Reference board
+  ref-no-jd	Reference board without HP/Mic jack detection
+  3stack	D965 3stack
+  5stack	D965 5stack + SPDIF
+  dell-3stack	Dell Dimension E520
+  dell-bios	Fixes with Dell BIOS setup
+
+STAC92HD71B*
+============
+  ref		Reference board
+  dell-m4-1	Dell desktops
+  dell-m4-2	Dell desktops
+  dell-m4-3	Dell desktops
+
+STAC92HD73*
+===========
+  ref		Reference board
+  no-jd		BIOS setup but without jack-detection
+  dell-m6-amic	Dell desktops/laptops with analog mics
+  dell-m6-dmic	Dell desktops/laptops with digital mics
+  dell-m6	Dell desktops/laptops with both type of mics
+
+STAC92HD83*
+===========
+  ref		Reference board
+
+STAC9872
+========
+  vaio		Setup for VAIO FE550G/SZ110
+  vaio-ar Setup for VAIO AR

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

@@ -0,0 +1,577 @@
+MORE NOTES ON HD-AUDIO DRIVER
+=============================
+					Takashi Iwai <tiwai@suse.de>
+
+
+GENERAL
+-------
+
+HD-audio is the new standard on-board audio component on modern PCs
+after AC97.  Although Linux has been supporting HD-audio since long
+time ago, there are often problems with new machines.  A part of the
+problem is broken BIOS, and the rest is the driver implementation.
+This document explains the brief trouble-shooting and debugging
+methods for the	HD-audio hardware.
+
+The HD-audio component consists of two parts: the controller chip and 
+the codec chips on the HD-audio bus.  Linux provides a single driver
+for all controllers, snd-hda-intel.  Although the driver name contains
+a word of a well-known harware vendor, it's not specific to it but for
+all controller chips by other companies.  Since the HD-audio
+controllers are supposed to be compatible, the single snd-hda-driver
+should work in most cases.  But, not surprisingly, there are known
+bugs and issues specific to each controller type.  The snd-hda-intel
+driver has a bunch of workarounds for these as described below.
+
+A controller may have multiple codecs.  Usually you have one audio
+codec and optionally one modem codec.  In theory, there might be
+multiple audio codecs, e.g. for analog and digital outputs, and the
+driver might not work properly because of conflict of mixer elements.
+This should be fixed in future if such hardware really exists.
+
+The snd-hda-intel driver has several different codec parsers depending
+on the codec.  It has a generic parser as a fallback, but this
+functionality is fairly limited until now.  Instead of the generic
+parser, usually the codec-specific parser (coded in patch_*.c) is used
+for the codec-specific implementations.  The details about the
+codec-specific problems are explained in the later sections.
+
+If you are interested in the deep debugging of HD-audio, read the
+HD-audio specification at first.  The specification is found on
+Intel's web page, for example:
+
+- http://www.intel.com/standards/hdaudio/
+
+
+HD-AUDIO CONTROLLER
+-------------------
+
+DMA-Position Problem
+~~~~~~~~~~~~~~~~~~~~
+The most common problem of the controller is the inaccurate DMA
+pointer reporting.  The DMA pointer for playback and capture can be
+read in two ways, either via a LPIB register or via a position-buffer
+map.  As default the driver tries to read from the io-mapped
+position-buffer, and falls back to LPIB if the position-buffer appears
+dead.  However, this detection isn't perfect on some devices.  In such
+a case, you can change the default method via `position_fix` option.
+
+`position_fix=1` means to use LPIB method explicitly.
+`position_fix=2` means to use the position-buffer.  0 is the default
+value, the automatic check and fallback to LPIB as described in the
+above.  If you get a problem of repeated sounds, this option might
+help.
+
+In addition to that, every controller is known to be broken regarding
+the wake-up timing.  It wakes up a few samples before actually
+processing the data on the buffer.  This caused a lot of problems, for
+example, with ALSA dmix or JACK.  Since 2.6.27 kernel, the driver puts
+an artificial delay to the wake up timing.  This delay is controlled
+via `bdl_pos_adj` option. 
+
+When `bdl_pos_adj` is a negative value (as default), it's assigned to
+an appropriate value depending on the controller chip.  For Intel
+chips, it'd be 1 while it'd be 32 for others.  Usually this works.
+Only in case it doesn't work and you get warning messages, you should
+change this parameter to other values.
+
+
+Codec-Probing Problem
+~~~~~~~~~~~~~~~~~~~~~
+A less often but a more severe problem is the codec probing.  When
+BIOS reports the available codec slots wrongly, the driver gets
+confused and tries to access the non-existing codec slot.  This often
+results in the total screw-up, and destructs the further communication
+with the codec chips.  The symptom appears usually as error messages
+like:
+------------------------------------------------------------------------
+  hda_intel: azx_get_response timeout, switching to polling mode:
+        last cmd=0x12345678
+  hda_intel: azx_get_response timeout, switching to single_cmd mode:
+        last cmd=0x12345678
+------------------------------------------------------------------------
+
+The first line is a warning, and this is usually relatively harmless.
+It means that the codec response isn't notified via an IRQ.  The
+driver uses explicit polling method to read the response.  It gives
+very slight CPU overhead, but you'd unlikely notice it.
+
+The second line is, however, a fatal error.  If this happens, usually
+it means that something is really wrong.  Most likely you are
+accessing a non-existing codec slot.
+
+Thus, if the second error message appears, try to narrow the probed
+codec slots via `probe_mask` option.  It's a bitmask, and each bit
+corresponds to the codec slot.  For example, to probe only the first
+slot, pass `probe_mask=1`.  For the first and the third slots, pass
+`probe_mask=5` (where 5 = 1 | 4), and so on.
+
+Since 2.6.29 kernel, the driver has a more robust probing method, so
+this error might happen rarely, though.
+
+
+Interrupt Handling
+~~~~~~~~~~~~~~~~~~
+In rare but some cases, the interrupt isn't properly handled as
+default.  You would notice this by the DMA transfer error reported by
+ALSA PCM core, for example.  Using MSI might help in such a case.
+Pass `enable_msi=1` option for enabling MSI.
+
+
+HD-AUDIO CODEC
+--------------
+
+Model Option
+~~~~~~~~~~~~
+The most common problem regarding the HD-audio driver is the
+unsupported codec features or the mismatched device configuration.
+Most of codec-specific code has several preset models, either to
+override the BIOS setup or to provide more comprehensive features.
+
+The driver checks PCI SSID and looks through the static configuration
+table until any matching entry is found.  If you have a new machine,
+you may see a message like below:
+------------------------------------------------------------------------
+    hda_codec: Unknown model for ALC880, trying auto-probe from BIOS...
+------------------------------------------------------------------------
+Even if you see such a message, DON'T PANIC.  Take a deep breath and
+keep your towel.  First of all, it's an informational message, no
+warning, no error.  This means that the PCI SSID of your device isn't
+listed in the known preset model (white-)list.  But, this doesn't mean
+that the driver is broken.  Many codec-drivers provide the automatic
+configuration mechanism based on the BIOS setup.
+
+The HD-audio codec has usually "pin" widgets, and BIOS sets the default
+configuration of each pin, which indicates the location, the
+connection type, the jack color, etc.  The HD-audio driver can guess
+the right connection judging from these default configuration values.
+However -- some codec-support codes, such as patch_analog.c, don't
+support the automatic probing (yet as of 2.6.28).  And, BIOS is often,
+yes, pretty often broken.  It sets up wrong values and screws up the
+driver.
+
+The preset model is provided basically to overcome such a situation.
+When the matching preset model is found in the white-list, the driver
+assumes the static configuration of that preset and builds the mixer
+elements and PCM streams based on the static information.  Thus, if
+you have a newer machine with a slightly different PCI SSID from the
+existing one, you may have a good chance to re-use the same model.
+You can pass the `model` option to specify the preset model instead of
+PCI SSID look-up.
+
+What `model` option values are available depends on the codec chip.
+Check your codec chip from the codec proc file (see "Codec Proc-File"
+section below).  It will show the vendor/product name of your codec
+chip.  Then, see Documentation/sound/alsa/HD-Audio-Modelstxt file,
+the section of HD-audio driver.  You can find a list of codecs
+and `model` options belonging to each codec.  For example, for Realtek
+ALC262 codec chip, pass `model=ultra` for devices that are compatible
+with Samsung Q1 Ultra.
+
+Thus, the first thing you can do for any brand-new, unsupported and
+non-working HD-audio hardware is to check HD-audio codec and several
+different `model` option values.  If you have a luck, some of them
+might suit with your device well.
+
+Some codecs such as ALC880 have a special model option `model=test`.
+This configures the driver to provide as many mixer controls as
+possible for every single pin feature except for the unsolicited
+events (and maybe some other specials).  Adjust each mixer element and
+try the I/O in the way of trial-and-error until figuring out the whole
+I/O pin mappings.
+
+Note that `model=generic` has a special meaning.  It means to use the
+generic parser regardless of the codec.  Usually the codec-specific
+parser is much better than the generic parser (as now).  Thus this
+option is more about the debugging purpose.
+
+
+Speaker and Headphone Output
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+One of the most frequent (and obvious) bugs with HD-audio is the
+silent output from either or both of a built-in speaker and a
+headphone jack.  In general, you should try a headphone output at
+first.  A speaker output often requires more additional controls like
+the external amplifier bits.  Thus a headphone output has a slightly
+better chance.
+
+Before making a bug report, double-check whether the mixer is set up
+correctly.  The recent version of snd-hda-intel driver provides mostly
+"Master" volume control as well as "Front" volume (where Front
+indicates the front-channels).  In addition, there can be individual
+"Headphone" and "Speaker" controls.
+
+Ditto for the speaker output.  There can be "External Amplifier"
+switch on some codecs.  Turn on this if present.
+
+Another related problem is the automatic mute of speaker output by
+headphone plugging.  This feature is implemented in most cases, but
+not on every preset model or codec-support code.
+
+In anyway, try a different model option if you have such a problem.
+Some other models may match better and give you more matching
+functionality.  If none of the available models works, send a bug
+report.  See the bug report section for details.
+
+If you are masochistic enough to debug the driver problem, note the
+following:
+
+- The speaker (and the headphone, too) output often requires the
+  external amplifier.  This can be set usually via EAPD verb or a
+  certain GPIO.  If the codec pin supports EAPD, you have a better
+  chance via SET_EAPD_BTL verb (0x70c).  On others, GPIO pin (mostly
+  it's either GPIO0 or GPIO1) may turn on/off EAPD.
+- Some Realtek codecs require special vendor-specific coefficients to
+  turn on the amplifier.  See patch_realtek.c.
+- IDT codecs may have extra power-enable/disable controls on each
+  analog pin.  See patch_sigmatel.c.
+- Very rare but some devices don't accept the pin-detection verb until
+  triggered.  Issuing GET_PIN_SENSE verb (0xf09) may result in the
+  codec-communication stall.  Some examples are found in
+  patch_realtek.c.
+
+
+Capture Problems
+~~~~~~~~~~~~~~~~
+The capture problems are often because of missing setups of mixers.
+Thus, before submitting a bug report, make sure that you set up the
+mixer correctly.  For example, both "Capture Volume" and "Capture
+Switch" have to be set properly in addition to the right "Capture
+Source" or "Input Source" selection.  Some devices have "Mic Boost"
+volume or switch.
+
+When the PCM device is opened via "default" PCM (without pulse-audio
+plugin), you'll likely have "Digital Capture Volume" control as well.
+This is provided for the extra gain/attenuation of the signal in
+software, especially for the inputs without the hardware volume
+control such as digital microphones.  Unless really needed, this
+should be set to exactly 50%, corresponding to 0dB -- neither extra
+gain nor attenuation.  When you use "hw" PCM, i.e., a raw access PCM,
+this control will have no influence, though.
+
+It's known that some codecs / devices have fairly bad analog circuits,
+and the recorded sound contains a certain DC-offset.  This is no bug
+of the driver.
+
+Most of modern laptops have no analog CD-input connection.  Thus, the
+recording from CD input won't work in many cases although the driver
+provides it as the capture source.  Use CDDA instead.
+
+The automatic switching of the built-in and external mic per plugging
+is implemented on some codec models but not on every model.  Partly
+because of my laziness but mostly lack of testers.  Feel free to
+submit the improvement patch to the author.
+
+
+Direct Debugging
+~~~~~~~~~~~~~~~~
+If no model option gives you a better result, and you are a tough guy
+to fight against evil, try debugging via hitting the raw HD-audio
+codec verbs to the device.  Some tools are available: hda-emu and
+hda-analyzer.  The detailed description is found in the sections
+below.  You'd need to enable hwdep for using these tools.  See "Kernel
+Configuration" section.
+
+
+OTHER ISSUES
+------------
+
+Kernel Configuration
+~~~~~~~~~~~~~~~~~~~~
+In general, I recommend you to enable the sound debug option,
+`CONFIG_SND_DEBUG=y`, no matter whether you are debugging or not.
+This enables snd_printd() macro and others, and you'll get additional
+kernel messages at probing.
+
+In addition, you can enable `CONFIG_SND_DEBUG_VERBOSE=y`.  But this
+will give you far more messages.  Thus turn this on only when you are
+sure to want it.
+
+Don't forget to turn on the appropriate `CONFIG_SND_HDA_CODEC_*`
+options.  Note that each of them corresponds to the codec chip, not
+the controller chip.  Thus, even if lspci shows the Nvidia controller,
+you may need to choose the option for other vendors.  If you are
+unsure, just select all yes.
+
+`CONFIG_SND_HDA_HWDEP` is a useful option for debugging the driver.
+When this is enabled, the driver creates hardware-dependent devices
+(one per each codec), and you have a raw access to the device via
+these device files.  For example, `hwC0D2` will be created for the
+codec slot #2 of the first card (#0).  For debug-tools such as
+hda-verb and hda-analyzer, the hwdep device has to be enabled.
+Thus, it'd be better to turn this on always.
+
+`CONFIG_SND_HDA_RECONFIG` is a new option, and this depends on the
+hwdep option above.  When enabled, you'll have some sysfs files under
+the corresponding hwdep directory.  See "HD-audio reconfiguration"
+section below.
+
+`CONFIG_SND_HDA_POWER_SAVE` option enables the power-saving feature.
+See "Power-saving" section below.
+
+
+Codec Proc-File
+~~~~~~~~~~~~~~~
+The codec proc-file is a treasure-chest for debugging HD-audio.
+It shows most of useful information of each codec widget.
+
+The proc file is located in /proc/asound/card*/codec#*, one file per
+each codec slot.  You can know the codec vendor, product id and
+names, the type of each widget, capabilities and so on.
+This file, however, doesn't show the jack sensing state, so far.  This
+is because the jack-sensing might be depending on the trigger state.
+
+This file will be picked up by the debug tools, and also it can be fed
+to the emulator as the primary codec information.  See the debug tools
+section below.
+
+This proc file can be also used to check whether the generic parser is
+used.  When the generic parser is used, the vendor/product ID name
+will appear as "Realtek ID 0262", instead of "Realtek ALC262".
+
+
+HD-Audio Reconfiguration
+~~~~~~~~~~~~~~~~~~~~~~~~
+This is an experimental feature to allow you re-configure the HD-audio
+codec dynamically without reloading the driver.  The following sysfs
+files are available under each codec-hwdep device directory (e.g. 
+/sys/class/sound/hwC0D0):
+
+vendor_id::
+  Shows the 32bit codec vendor-id hex number.  You can change the
+  vendor-id value by writing to this file.
+subsystem_id::
+  Shows the 32bit codec subsystem-id hex number.  You can change the
+  subsystem-id value by writing to this file.
+revision_id::
+  Shows the 32bit codec revision-id hex number.  You can change the
+  revision-id value by writing to this file.
+afg::
+  Shows the AFG ID.  This is read-only.
+mfg::
+  Shows the MFG ID.  This is read-only.
+name::
+  Shows the codec name string.  Can be changed by writing to this
+  file.
+modelname::
+  Shows the currently set `model` option.  Can be changed by writing
+  to this file.
+init_verbs::
+  The extra verbs to execute at initialization.  You can add a verb by
+  writing to this file.  Pass tree numbers, nid, verb and parameter.
+hints::
+  Shows hint strings for codec parsers for any use.  Right now it's
+  not used.
+reconfig::
+  Triggers the codec re-configuration.  When any value is written to
+  this file, the driver re-initialize and parses the codec tree
+  again.  All the changes done by the sysfs entries above are taken
+  into account.
+clear::
+  Resets the codec, removes the mixer elements and PCM stuff of the
+  specified codec, and clear all init verbs and hints.
+
+
+Power-Saving
+~~~~~~~~~~~~
+The power-saving is a kind of auto-suspend of the device.  When the
+device is inactive for a certain time, the device is automatically
+turned off to save the power.  The time to go down is specified via
+`power_save` module option, and this option can be changed dynamically
+via sysfs.
+
+The power-saving won't work when the analog loopback is enabled on
+some codecs.  Make sure that you mute all unneeded signal routes when
+you want the power-saving.
+
+The power-saving feature might cause audible click noises at each
+power-down/up depending on the device.  Some of them might be
+solvable, but some are hard, I'm afraid.  Some distros such as
+openSUSE enables the power-saving feature automatically when the power
+cable is unplugged.  Thus, if you hear noises, suspect first the
+power-saving.  See /sys/module/snd_hda_intel/parameters/power_save to
+check the current value.  If it's non-zero, the feature is turned on.
+
+
+Development Tree
+~~~~~~~~~~~~~~~~
+The latest development codes for HD-audio are found on sound git tree:
+
+- git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6.git
+
+The master branch or for-next branches can be used as the main
+development branches in general while the HD-audio specific patches
+are committed in topic/hda branch.
+
+If you are using the latest Linus tree, it'd be better to pull the
+above GIT tree onto it.  If you are using the older kernels, an easy
+way to try the latest ALSA code is to build from the snapshot
+tarball.  There are daily tarballs and the latest snapshot tarball.
+All can be built just like normal alsa-driver release packages, that
+is, installed via the usual spells: configure, make and make
+install(-modules).  See INSTALL in the package.  The snapshot tarballs
+are found at:
+
+- ftp://ftp.kernel.org/pub/linux/kernel/people/tiwai/snapshot/
+
+
+Sending a Bug Report
+~~~~~~~~~~~~~~~~~~~~
+If any model or module options don't work for your device, it's time
+to send a bug report to the developers.  Give the following in your
+bug report:
+
+- Hardware vendor, product and model names
+- Kernel version (and ALSA-driver version if you built externally)
+- `alsa-info.sh` output; run with `--no-upload` option.  See the
+  section below about alsa-info
+
+If it's a regression, at best, send alsa-info outputs of both working
+and non-working kernels.  This is really helpful because we can
+compare the codec registers directly.
+
+Send a bug report either the followings:
+
+kernel-bugzilla::
+  http://bugme.linux-foundation.org/
+alsa-devel ML::
+  alsa-devel@alsa-project.org
+
+
+DEBUG TOOLS
+-----------
+
+This section describes some tools available for debugging HD-audio
+problems.
+
+alsa-info
+~~~~~~~~~
+The script `alsa-info.sh` is a very useful tool to gather the audio
+device information.  You can fetch the latest version from:
+
+- http://www.alsa-project.org/alsa-info.sh
+
+Run this script as root, and it will gather the important information
+such as the module lists, module parameters, proc file contents
+including the codec proc files, mixer outputs and the control
+elements.  As default, it will store the information onto a web server
+on alsa-project.org.  But, if you send a bug report, it'd be better to
+run with `--no-upload` option, and attach the generated file.
+
+There are some other useful options.  See `--help` option output for
+details.
+
+
+hda-verb
+~~~~~~~~
+hda-verb is a tiny program that allows you to access the HD-audio
+codec directly.  You can execute a raw HD-audio codec verb with this.
+This program accesses the hwdep device, thus you need to enable the
+kernel config `CONFIG_SND_HDA_HWDEP=y` beforehand.
+
+The hda-verb program takes four arguments: the hwdep device file, the
+widget NID, the verb and the parameter.  When you access to the codec
+on the slot 2 of the card 0, pass /dev/snd/hwC0D2 to the first
+argument, typically.  (However, the real path name depends on the
+system.)
+
+The second parameter is the widget number-id to access.  The third
+parameter can be either a hex/digit number or a string corresponding
+to a verb.  Similarly, the last parameter is the value to write, or
+can be a string for the parameter type.
+
+------------------------------------------------------------------------
+  % hda-verb /dev/snd/hwC0D0 0x12 0x701 2
+  nid = 0x12, verb = 0x701, param = 0x2
+  value = 0x0
+
+  % hda-verb /dev/snd/hwC0D0 0x0 PARAMETERS VENDOR_ID
+  nid = 0x0, verb = 0xf00, param = 0x0
+  value = 0x10ec0262
+
+  % hda-verb /dev/snd/hwC0D0 2 set_a 0xb080
+  nid = 0x2, verb = 0x300, param = 0xb080
+  value = 0x0
+------------------------------------------------------------------------
+
+Although you can issue any verbs with this program, the driver state
+won't be always updated.  For example, the volume values are usually
+cached in the driver, and thus changing the widget amp value directly
+via hda-verb won't change the mixer value.
+
+The hda-verb program is found in the ftp directory:
+
+- ftp://ftp.kernel.org/pub/linux/kernel/people/tiwai/misc/
+
+Also a git repository is available:
+
+- git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/hda-verb.git
+
+See README file in the tarball for more details about hda-verb
+program.
+
+
+hda-analyzer
+~~~~~~~~~~~~
+hda-analyzer provides a graphical interface to access the raw HD-audio
+control, based on pyGTK2 binding.  It's a more powerful version of
+hda-verb.  The program gives you an easy-to-use GUI stuff for showing
+the widget information and adjusting the amp values, as well as the
+proc-compatible output.
+
+The hda-analyzer is a part of alsa.git repository in
+alsa-project.org:
+
+- http://git.alsa-project.org/?p=alsa.git;a=tree;f=hda-analyzer
+
+
+Codecgraph
+~~~~~~~~~~
+Codecgraph is a utility program to generate a graph and visualizes the
+codec-node connection of a codec chip.  It's especially useful when
+you analyze or debug a codec without a proper datasheet.  The program
+parses the given codec proc file and converts to SVG via graphiz
+program.
+
+The tarball and GIT trees are found in the web page at:
+
+- http://helllabs.org/codecgraph/
+
+
+hda-emu
+~~~~~~~
+hda-emu is an HD-audio emulator.  The main purpose of this program is
+to debug an HD-audio codec without the real hardware.  Thus, it
+doesn't emulate the behavior with the real audio I/O, but it just
+dumps the codec register changes and the ALSA-driver internal changes
+at probing and operating the HD-audio driver.
+
+The program requires a codec proc-file to simulate.  Get a proc file
+for the target codec beforehand, or pick up an example codec from the
+codec proc collections in the tarball.  Then, run the program with the
+proc file, and the hda-emu program will start parsing the codec file
+and simulates the HD-audio driver:
+
+------------------------------------------------------------------------
+  % hda-emu codecs/stac9200-dell-d820-laptop
+  # Parsing..
+  hda_codec: Unknown model for STAC9200, using BIOS defaults
+  hda_codec: pin nid 08 bios pin config 40c003fa
+  ....
+------------------------------------------------------------------------
+
+The program gives you only a very dumb command-line interface.  You
+can get a proc-file dump at the current state, get a list of control
+(mixer) elements, set/get the control element value, simulate the PCM
+operation, the jack plugging simulation, etc.
+
+The package is found in:
+
+- ftp://ftp.kernel.org/pub/linux/kernel/people/tiwai/misc/
+
+A git repository is available:
+
+- git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/hda-emu.git
+
+See README file in the tarball for more details about hda-emu
+program.

+ 10 - 0
Documentation/sound/alsa/Procfile.txt

@@ -153,6 +153,16 @@ card*/codec#*
 	Shows the general codec information and the attribute of each
 	Shows the general codec information and the attribute of each
 	widget node.
 	widget node.
 
 
+card*/eld#*
+	Available for HDMI or DisplayPort interfaces.
+	Shows ELD(EDID Like Data) info retrieved from the attached HDMI sink,
+	and describes its audio capabilities and configurations.
+
+	Some ELD fields may be modified by doing `echo name hex_value > eld#*`.
+	Only do this if you are sure the HDMI sink provided value is wrong.
+	And if that makes your HDMI audio work, please report to us so that we
+	can fix it in future kernel releases.
+
 
 
 Sequencer Information
 Sequencer Information
 ---------------------
 ---------------------

+ 1 - 0
include/linux/input.h

@@ -659,6 +659,7 @@ struct input_absinfo {
 #define SW_RADIO		SW_RFKILL_ALL	/* deprecated */
 #define SW_RADIO		SW_RFKILL_ALL	/* deprecated */
 #define SW_MICROPHONE_INSERT	0x04  /* set = inserted */
 #define SW_MICROPHONE_INSERT	0x04  /* set = inserted */
 #define SW_DOCK			0x05  /* set = plugged into dock */
 #define SW_DOCK			0x05  /* set = plugged into dock */
+#define SW_LINEOUT_INSERT	0x06  /* set = inserted */
 #define SW_MAX			0x0f
 #define SW_MAX			0x0f
 #define SW_CNT			(SW_MAX+1)
 #define SW_CNT			(SW_MAX+1)
 
 

+ 1 - 0
include/sound/jack.h

@@ -35,6 +35,7 @@ enum snd_jack_types {
 	SND_JACK_HEADPHONE	= 0x0001,
 	SND_JACK_HEADPHONE	= 0x0001,
 	SND_JACK_MICROPHONE	= 0x0002,
 	SND_JACK_MICROPHONE	= 0x0002,
 	SND_JACK_HEADSET	= SND_JACK_HEADPHONE | SND_JACK_MICROPHONE,
 	SND_JACK_HEADSET	= SND_JACK_HEADPHONE | SND_JACK_MICROPHONE,
+	SND_JACK_LINEOUT	= 0x0004,
 };
 };
 
 
 struct snd_jack {
 struct snd_jack {

+ 8 - 1
sound/core/jack.c

@@ -34,6 +34,7 @@ static int snd_jack_dev_free(struct snd_device *device)
 	else
 	else
 		input_free_device(jack->input_dev);
 		input_free_device(jack->input_dev);
 
 
+	kfree(jack->id);
 	kfree(jack);
 	kfree(jack);
 
 
 	return 0;
 	return 0;
@@ -87,7 +88,7 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
 	if (jack == NULL)
 	if (jack == NULL)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
-	jack->id = id;
+	jack->id = kstrdup(id, GFP_KERNEL);
 
 
 	jack->input_dev = input_allocate_device();
 	jack->input_dev = input_allocate_device();
 	if (jack->input_dev == NULL) {
 	if (jack->input_dev == NULL) {
@@ -102,6 +103,9 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
 	if (type & SND_JACK_HEADPHONE)
 	if (type & SND_JACK_HEADPHONE)
 		input_set_capability(jack->input_dev, EV_SW,
 		input_set_capability(jack->input_dev, EV_SW,
 				     SW_HEADPHONE_INSERT);
 				     SW_HEADPHONE_INSERT);
+	if (type & SND_JACK_LINEOUT)
+		input_set_capability(jack->input_dev, EV_SW,
+				     SW_LINEOUT_INSERT);
 	if (type & SND_JACK_MICROPHONE)
 	if (type & SND_JACK_MICROPHONE)
 		input_set_capability(jack->input_dev, EV_SW,
 		input_set_capability(jack->input_dev, EV_SW,
 				     SW_MICROPHONE_INSERT);
 				     SW_MICROPHONE_INSERT);
@@ -153,6 +157,9 @@ void snd_jack_report(struct snd_jack *jack, int status)
 	if (jack->type & SND_JACK_HEADPHONE)
 	if (jack->type & SND_JACK_HEADPHONE)
 		input_report_switch(jack->input_dev, SW_HEADPHONE_INSERT,
 		input_report_switch(jack->input_dev, SW_HEADPHONE_INSERT,
 				    status & SND_JACK_HEADPHONE);
 				    status & SND_JACK_HEADPHONE);
+	if (jack->type & SND_JACK_LINEOUT)
+		input_report_switch(jack->input_dev, SW_LINEOUT_INSERT,
+				    status & SND_JACK_LINEOUT);
 	if (jack->type & SND_JACK_MICROPHONE)
 	if (jack->type & SND_JACK_MICROPHONE)
 		input_report_switch(jack->input_dev, SW_MICROPHONE_INSERT,
 		input_report_switch(jack->input_dev, SW_MICROPHONE_INSERT,
 				    status & SND_JACK_MICROPHONE);
 				    status & SND_JACK_MICROPHONE);

+ 1 - 123
sound/pci/Kconfig

@@ -497,129 +497,7 @@ config SND_FM801_TEA575X
 	depends on SND_FM801_TEA575X_BOOL
 	depends on SND_FM801_TEA575X_BOOL
 	default SND_FM801
 	default SND_FM801
 
 
-config SND_HDA_INTEL
-	tristate "Intel HD Audio"
-	select SND_PCM
-	select SND_VMASTER
-	help
-	  Say Y here to include support for Intel "High Definition
-	  Audio" (Azalia) motherboard devices.
-
-	  To compile this driver as a module, choose M here: the module
-	  will be called snd-hda-intel.
-
-config SND_HDA_HWDEP
-	bool "Build hwdep interface for HD-audio driver"
-	depends on SND_HDA_INTEL
-	select SND_HWDEP
-	help
-	  Say Y here to build a hwdep interface for HD-audio driver.
-	  This interface can be used for out-of-band communication
-	  with codecs for debugging purposes.
-
-config SND_HDA_INPUT_BEEP
-	bool "Support digital beep via input layer"
-	depends on SND_HDA_INTEL
-	depends on INPUT=y || INPUT=SND_HDA_INTEL
-	help
-	  Say Y here to build a digital beep interface for HD-audio
-	  driver. This interface is used to generate digital beeps.
-
-config SND_HDA_CODEC_REALTEK
-	bool "Build Realtek HD-audio codec support"
-	depends on SND_HDA_INTEL
-	default y
-	help
-	  Say Y here to include Realtek HD-audio codec support in
-	  snd-hda-intel driver, such as ALC880.
-
-config SND_HDA_CODEC_ANALOG
-	bool "Build Analog Device HD-audio codec support"
-	depends on SND_HDA_INTEL
-	default y
-	help
-	  Say Y here to include Analog Device HD-audio codec support in
-	  snd-hda-intel driver, such as AD1986A.
-
-config SND_HDA_CODEC_SIGMATEL
-	bool "Build IDT/Sigmatel HD-audio codec support"
-	depends on SND_HDA_INTEL
-	default y
-	help
-	  Say Y here to include IDT (Sigmatel) HD-audio codec support in
-	  snd-hda-intel driver, such as STAC9200.
-
-config SND_HDA_CODEC_VIA
-	bool "Build VIA HD-audio codec support"
-	depends on SND_HDA_INTEL
-	default y
-	help
-	  Say Y here to include VIA HD-audio codec support in
-	  snd-hda-intel driver, such as VT1708.
-
-config SND_HDA_CODEC_ATIHDMI
-	bool "Build ATI HDMI HD-audio codec support"
-	depends on SND_HDA_INTEL
-	default y
-	help
-	  Say Y here to include ATI HDMI HD-audio codec support in
-	  snd-hda-intel driver, such as ATI RS600 HDMI.
-
-config SND_HDA_CODEC_NVHDMI
-	bool "Build NVIDIA HDMI HD-audio codec support"
-	depends on SND_HDA_INTEL
-	default y
-	help
-	  Say Y here to include NVIDIA HDMI HD-audio codec support in
-	  snd-hda-intel driver, such as NVIDIA MCP78 HDMI.
-
-config SND_HDA_CODEC_CONEXANT
-	bool "Build Conexant HD-audio codec support"
-	depends on SND_HDA_INTEL
-	default y
-	help
-	  Say Y here to include Conexant HD-audio codec support in
-	  snd-hda-intel driver, such as CX20549.
-
-config SND_HDA_CODEC_CMEDIA
-	bool "Build C-Media HD-audio codec support"
-	depends on SND_HDA_INTEL
-	default y
-	help
-	  Say Y here to include C-Media HD-audio codec support in
-	  snd-hda-intel driver, such as CMI9880.
-
-config SND_HDA_CODEC_SI3054
-	bool "Build Silicon Labs 3054 HD-modem codec support"
-	depends on SND_HDA_INTEL
-	default y
-	help
-	  Say Y here to include Silicon Labs 3054 HD-modem codec
-	  (and compatibles) support in snd-hda-intel driver.
-
-config SND_HDA_GENERIC
-	bool "Enable generic HD-audio codec parser"
-	depends on SND_HDA_INTEL
-	default y
-	help
-	  Say Y here to enable the generic HD-audio codec parser
-	  in snd-hda-intel driver.
-
-config SND_HDA_POWER_SAVE
-	bool "Aggressive power-saving on HD-audio"
-	depends on SND_HDA_INTEL && EXPERIMENTAL
-	help
-	  Say Y here to enable more aggressive power-saving mode on
-	  HD-audio driver.  The power-saving timeout can be configured
-	  via power_save option or over sysfs on-the-fly.
-
-config SND_HDA_POWER_SAVE_DEFAULT
-	int "Default time-out for HD-audio power-save mode"
-	depends on SND_HDA_POWER_SAVE
-	default 0
-	help
-	  The default time-out value in seconds for HD-audio automatic
-	  power-save mode.  0 means to disable the power-save mode.
+source "sound/pci/hda/Kconfig"
 
 
 config SND_HDSP
 config SND_HDSP
 	tristate "RME Hammerfall DSP Audio"
 	tristate "RME Hammerfall DSP Audio"

+ 188 - 0
sound/pci/hda/Kconfig

@@ -0,0 +1,188 @@
+menuconfig SND_HDA_INTEL
+	tristate "Intel HD Audio"
+	select SND_PCM
+	select SND_VMASTER
+	select SND_JACK if INPUT=y || INPUT=SND
+	help
+	  Say Y here to include support for Intel "High Definition
+	  Audio" (Azalia) and its compatible devices.
+
+	  This option enables the HD-audio controller.  Don't forget
+	  to choose the appropriate codec options below.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd-hda-intel.
+
+if SND_HDA_INTEL
+
+config SND_HDA_HWDEP
+	bool "Build hwdep interface for HD-audio driver"
+	select SND_HWDEP
+	help
+	  Say Y here to build a hwdep interface for HD-audio driver.
+	  This interface can be used for out-of-band communication
+	  with codecs for debugging purposes.
+
+config SND_HDA_RECONFIG
+	bool "Allow dynamic codec reconfiguration (EXPERIMENTAL)"
+	depends on SND_HDA_HWDEP && EXPERIMENTAL
+	help
+	  Say Y here to enable the HD-audio codec re-configuration feature.
+	  This adds the sysfs interfaces to allow user to clear the whole
+	  codec configuration, change the codec setup, add extra verbs,
+	  and re-configure the codec dynamically.
+
+config SND_HDA_INPUT_BEEP
+	bool "Support digital beep via input layer"
+	depends on INPUT=y || INPUT=SND_HDA_INTEL
+	help
+	  Say Y here to build a digital beep interface for HD-audio
+	  driver. This interface is used to generate digital beeps.
+
+config SND_HDA_CODEC_REALTEK
+	bool "Build Realtek HD-audio codec support"
+	default y
+	help
+	  Say Y here to include Realtek HD-audio codec support in
+	  snd-hda-intel driver, such as ALC880.
+
+	  When the HD-audio driver is built as a module, the codec
+	  support code is also built as another module,
+	  snd-hda-codec-realtek.
+	  This module is automatically loaded at probing.
+
+config SND_HDA_CODEC_ANALOG
+	bool "Build Analog Device HD-audio codec support"
+	default y
+	help
+	  Say Y here to include Analog Device HD-audio codec support in
+	  snd-hda-intel driver, such as AD1986A.
+
+	  When the HD-audio driver is built as a module, the codec
+	  support code is also built as another module,
+	  snd-hda-codec-analog.
+	  This module is automatically loaded at probing.
+
+config SND_HDA_CODEC_SIGMATEL
+	bool "Build IDT/Sigmatel HD-audio codec support"
+	default y
+	help
+	  Say Y here to include IDT (Sigmatel) HD-audio codec support in
+	  snd-hda-intel driver, such as STAC9200.
+
+	  When the HD-audio driver is built as a module, the codec
+	  support code is also built as another module,
+	  snd-hda-codec-idt.
+	  This module is automatically loaded at probing.
+
+config SND_HDA_CODEC_VIA
+	bool "Build VIA HD-audio codec support"
+	default y
+	help
+	  Say Y here to include VIA HD-audio codec support in
+	  snd-hda-intel driver, such as VT1708.
+
+	  When the HD-audio driver is built as a module, the codec
+	  support code is also built as another module,
+	  snd-hda-codec-via.
+	  This module is automatically loaded at probing.
+
+config SND_HDA_CODEC_ATIHDMI
+	bool "Build ATI HDMI HD-audio codec support"
+	default y
+	help
+	  Say Y here to include ATI HDMI HD-audio codec support in
+	  snd-hda-intel driver, such as ATI RS600 HDMI.
+
+	  When the HD-audio driver is built as a module, the codec
+	  support code is also built as another module,
+	  snd-hda-codec-atihdmi.
+	  This module is automatically loaded at probing.
+
+config SND_HDA_CODEC_NVHDMI
+	bool "Build NVIDIA HDMI HD-audio codec support"
+	default y
+	help
+	  Say Y here to include NVIDIA HDMI HD-audio codec support in
+	  snd-hda-intel driver, such as NVIDIA MCP78 HDMI.
+
+	  When the HD-audio driver is built as a module, the codec
+	  support code is also built as another module,
+	  snd-hda-codec-nvhdmi.
+	  This module is automatically loaded at probing.
+
+config SND_HDA_CODEC_INTELHDMI
+	bool "Build INTEL HDMI HD-audio codec support"
+	default y
+	help
+	  Say Y here to include INTEL HDMI HD-audio codec support in
+	  snd-hda-intel driver, such as Eaglelake integrated HDMI.
+
+	  When the HD-audio driver is built as a module, the codec
+	  support code is also built as another module,
+	  snd-hda-codec-intelhdmi.
+	  This module is automatically loaded at probing.
+
+config SND_HDA_ELD
+	def_bool y
+	depends on SND_HDA_CODEC_INTELHDMI
+
+config SND_HDA_CODEC_CONEXANT
+	bool "Build Conexant HD-audio codec support"
+	default y
+	help
+	  Say Y here to include Conexant HD-audio codec support in
+	  snd-hda-intel driver, such as CX20549.
+
+	  When the HD-audio driver is built as a module, the codec
+	  support code is also built as another module,
+	  snd-hda-codec-conexant.
+	  This module is automatically loaded at probing.
+
+config SND_HDA_CODEC_CMEDIA
+	bool "Build C-Media HD-audio codec support"
+	default y
+	help
+	  Say Y here to include C-Media HD-audio codec support in
+	  snd-hda-intel driver, such as CMI9880.
+
+	  When the HD-audio driver is built as a module, the codec
+	  support code is also built as another module,
+	  snd-hda-codec-cmedia.
+	  This module is automatically loaded at probing.
+
+config SND_HDA_CODEC_SI3054
+	bool "Build Silicon Labs 3054 HD-modem codec support"
+	default y
+	help
+	  Say Y here to include Silicon Labs 3054 HD-modem codec
+	  (and compatibles) support in snd-hda-intel driver.
+
+	  When the HD-audio driver is built as a module, the codec
+	  support code is also built as another module,
+	  snd-hda-codec-si3054.
+	  This module is automatically loaded at probing.
+
+config SND_HDA_GENERIC
+	bool "Enable generic HD-audio codec parser"
+	default y
+	help
+	  Say Y here to enable the generic HD-audio codec parser
+	  in snd-hda-intel driver.
+
+config SND_HDA_POWER_SAVE
+	bool "Aggressive power-saving on HD-audio"
+	help
+	  Say Y here to enable more aggressive power-saving mode on
+	  HD-audio driver.  The power-saving timeout can be configured
+	  via power_save option or over sysfs on-the-fly.
+
+config SND_HDA_POWER_SAVE_DEFAULT
+	int "Default time-out for HD-audio power-save mode"
+	depends on SND_HDA_POWER_SAVE
+	default 0
+	help
+	  The default time-out value in seconds for HD-audio automatic
+	  power-save mode.  0 means to disable the power-save mode.
+
+endif

+ 57 - 18
sound/pci/hda/Makefile

@@ -1,20 +1,59 @@
-snd-hda-intel-y := hda_intel.o
-# since snd-hda-intel is the only driver using hda-codec,
-# merge it into a single module although it was originally
-# designed to be individual modules
-snd-hda-intel-y += hda_codec.o
-snd-hda-intel-$(CONFIG_PROC_FS) += hda_proc.o
-snd-hda-intel-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
-snd-hda-intel-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o
-snd-hda-intel-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_REALTEK) += patch_realtek.o
-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_CMEDIA) += patch_cmedia.o
-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_ANALOG) += patch_analog.o
-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_SIGMATEL) += patch_sigmatel.o
-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_SI3054) += patch_si3054.o
-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_ATIHDMI) += patch_atihdmi.o
-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_CONEXANT) += patch_conexant.o
-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_VIA) += patch_via.o
-snd-hda-intel-$(CONFIG_SND_HDA_CODEC_NVHDMI) += patch_nvhdmi.o
+snd-hda-intel-objs := hda_intel.o
 
 
+snd-hda-codec-y := hda_codec.o
+snd-hda-codec-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
+snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o
+# snd-hda-codec-$(CONFIG_SND_HDA_ELD) += hda_eld.o
+snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
+snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o
+
+snd-hda-codec-realtek-objs :=	patch_realtek.o
+snd-hda-codec-cmedia-objs :=	patch_cmedia.o
+snd-hda-codec-analog-objs :=	patch_analog.o
+snd-hda-codec-idt-objs :=	patch_sigmatel.o
+snd-hda-codec-si3054-objs :=	patch_si3054.o
+snd-hda-codec-atihdmi-objs :=	patch_atihdmi.o
+snd-hda-codec-conexant-objs :=	patch_conexant.o
+snd-hda-codec-via-objs :=	patch_via.o
+snd-hda-codec-nvhdmi-objs :=	patch_nvhdmi.o
+snd-hda-codec-intelhdmi-objs :=	patch_intelhdmi.o hda_eld.o
+
+# common driver
+obj-$(CONFIG_SND_HDA_INTEL) := snd-hda-codec.o
+
+# codec drivers (note: CONFIG_SND_HDA_CODEC_XXX are booleans)
+ifdef CONFIG_SND_HDA_CODEC_REALTEK
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-realtek.o
+endif
+ifdef CONFIG_SND_HDA_CODEC_CMEDIA
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-cmedia.o
+endif
+ifdef CONFIG_SND_HDA_CODEC_ANALOG
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-analog.o
+endif
+ifdef CONFIG_SND_HDA_CODEC_SIGMATEL
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-idt.o
+endif
+ifdef CONFIG_SND_HDA_CODEC_SI3054
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-si3054.o
+endif
+ifdef CONFIG_SND_HDA_CODEC_ATIHDMI
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-atihdmi.o
+endif
+ifdef CONFIG_SND_HDA_CODEC_CONEXANT
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-conexant.o
+endif
+ifdef CONFIG_SND_HDA_CODEC_VIA
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-via.o
+endif
+ifdef CONFIG_SND_HDA_CODEC_NVHDMI
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-nvhdmi.o
+endif
+ifdef CONFIG_SND_HDA_CODEC_INTELHDMI
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-intelhdmi.o
+endif
+
+# this must be the last entry after codec drivers;
+# otherwise the codec patches won't be hooked before the PCI probe
+# when built in kernel
 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o
 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o

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

@@ -128,6 +128,7 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
 	INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
 	INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_attach_beep_device);
 
 
 void snd_hda_detach_beep_device(struct hda_codec *codec)
 void snd_hda_detach_beep_device(struct hda_codec *codec)
 {
 {
@@ -140,3 +141,4 @@ void snd_hda_detach_beep_device(struct hda_codec *codec)
 		kfree(beep);
 		kfree(beep);
 	}
 	}
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device);

+ 605 - 176
sound/pci/hda/hda_codec.c

@@ -31,15 +31,6 @@
 #include <sound/initval.h>
 #include <sound/initval.h>
 #include "hda_local.h"
 #include "hda_local.h"
 #include <sound/hda_hwdep.h>
 #include <sound/hda_hwdep.h>
-#include "hda_patch.h"	/* codec presets */
-
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-/* define this option here to hide as static */
-static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
-module_param(power_save, int, 0644);
-MODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
-		 "(in second, 0 = disable).");
-#endif
 
 
 /*
 /*
  * vendor / preset table
  * vendor / preset table
@@ -55,6 +46,7 @@ static struct hda_vendor_id hda_vendor_ids[] = {
 	{ 0x1002, "ATI" },
 	{ 0x1002, "ATI" },
 	{ 0x1057, "Motorola" },
 	{ 0x1057, "Motorola" },
 	{ 0x1095, "Silicon Image" },
 	{ 0x1095, "Silicon Image" },
+	{ 0x10de, "Nvidia" },
 	{ 0x10ec, "Realtek" },
 	{ 0x10ec, "Realtek" },
 	{ 0x1106, "VIA" },
 	{ 0x1106, "VIA" },
 	{ 0x111d, "IDT" },
 	{ 0x111d, "IDT" },
@@ -66,40 +58,31 @@ static struct hda_vendor_id hda_vendor_ids[] = {
 	{ 0x1854, "LG" },
 	{ 0x1854, "LG" },
 	{ 0x1aec, "Wolfson Microelectronics" },
 	{ 0x1aec, "Wolfson Microelectronics" },
 	{ 0x434d, "C-Media" },
 	{ 0x434d, "C-Media" },
+	{ 0x8086, "Intel" },
 	{ 0x8384, "SigmaTel" },
 	{ 0x8384, "SigmaTel" },
 	{} /* terminator */
 	{} /* terminator */
 };
 };
 
 
-static const struct hda_codec_preset *hda_preset_tables[] = {
-#ifdef CONFIG_SND_HDA_CODEC_REALTEK
-	snd_hda_preset_realtek,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_CMEDIA
-	snd_hda_preset_cmedia,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_ANALOG
-	snd_hda_preset_analog,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_SIGMATEL
-	snd_hda_preset_sigmatel,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_SI3054
-	snd_hda_preset_si3054,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_ATIHDMI
-	snd_hda_preset_atihdmi,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_CONEXANT
-	snd_hda_preset_conexant,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_VIA
-	snd_hda_preset_via,
-#endif
-#ifdef CONFIG_SND_HDA_CODEC_NVHDMI
-	snd_hda_preset_nvhdmi,
-#endif
-	NULL
-};
+static DEFINE_MUTEX(preset_mutex);
+static LIST_HEAD(hda_preset_tables);
+
+int snd_hda_add_codec_preset(struct hda_codec_preset_list *preset)
+{
+	mutex_lock(&preset_mutex);
+	list_add_tail(&preset->list, &hda_preset_tables);
+	mutex_unlock(&preset_mutex);
+	return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_add_codec_preset);
+
+int snd_hda_delete_codec_preset(struct hda_codec_preset_list *preset)
+{
+	mutex_lock(&preset_mutex);
+	list_del(&preset->list);
+	mutex_unlock(&preset_mutex);
+	return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_delete_codec_preset);
 
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 static void hda_power_work(struct work_struct *work);
 static void hda_power_work(struct work_struct *work);
@@ -108,6 +91,72 @@ static void hda_keep_power_on(struct hda_codec *codec);
 static inline void hda_keep_power_on(struct hda_codec *codec) {}
 static inline void hda_keep_power_on(struct hda_codec *codec) {}
 #endif
 #endif
 
 
+const char *snd_hda_get_jack_location(u32 cfg)
+{
+	static char *bases[7] = {
+		"N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom",
+	};
+	static unsigned char specials_idx[] = {
+		0x07, 0x08,
+		0x17, 0x18, 0x19,
+		0x37, 0x38
+	};
+	static char *specials[] = {
+		"Rear Panel", "Drive Bar",
+		"Riser", "HDMI", "ATAPI",
+		"Mobile-In", "Mobile-Out"
+	};
+	int i;
+	cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT;
+	if ((cfg & 0x0f) < 7)
+		return bases[cfg & 0x0f];
+	for (i = 0; i < ARRAY_SIZE(specials_idx); i++) {
+		if (cfg == specials_idx[i])
+			return specials[i];
+	}
+	return "UNKNOWN";
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_jack_location);
+
+const char *snd_hda_get_jack_connectivity(u32 cfg)
+{
+	static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" };
+
+	return jack_locations[(cfg >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3];
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_jack_connectivity);
+
+const char *snd_hda_get_jack_type(u32 cfg)
+{
+	static char *jack_types[16] = {
+		"Line Out", "Speaker", "HP Out", "CD",
+		"SPDIF Out", "Digital Out", "Modem Line", "Modem Hand",
+		"Line In", "Aux", "Mic", "Telephony",
+		"SPDIF In", "Digitial In", "Reserved", "Other"
+	};
+
+	return jack_types[(cfg & AC_DEFCFG_DEVICE)
+				>> AC_DEFCFG_DEVICE_SHIFT];
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_jack_type);
+
+/*
+ * Compose a 32bit command word to be sent to the HD-audio controller
+ */
+static inline unsigned int
+make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
+	       unsigned int verb, unsigned int parm)
+{
+	u32 val;
+
+	val = (u32)(codec->addr & 0x0f) << 28;
+	val |= (u32)direct << 27;
+	val |= (u32)nid << 20;
+	val |= verb << 8;
+	val |= parm;
+	return val;
+}
+
 /**
 /**
  * snd_hda_codec_read - send a command and get the response
  * snd_hda_codec_read - send a command and get the response
  * @codec: the HDA codec
  * @codec: the HDA codec
@@ -124,17 +173,21 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
 				int direct,
 				int direct,
 				unsigned int verb, unsigned int parm)
 				unsigned int verb, unsigned int parm)
 {
 {
+	struct hda_bus *bus = codec->bus;
 	unsigned int res;
 	unsigned int res;
+
+	res = make_codec_cmd(codec, nid, direct, verb, parm);
 	snd_hda_power_up(codec);
 	snd_hda_power_up(codec);
-	mutex_lock(&codec->bus->cmd_mutex);
-	if (!codec->bus->ops.command(codec, nid, direct, verb, parm))
-		res = codec->bus->ops.get_response(codec);
+	mutex_lock(&bus->cmd_mutex);
+	if (!bus->ops.command(bus, res))
+		res = bus->ops.get_response(bus);
 	else
 	else
 		res = (unsigned int)-1;
 		res = (unsigned int)-1;
-	mutex_unlock(&codec->bus->cmd_mutex);
+	mutex_unlock(&bus->cmd_mutex);
 	snd_hda_power_down(codec);
 	snd_hda_power_down(codec);
 	return res;
 	return res;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_codec_read);
 
 
 /**
 /**
  * snd_hda_codec_write - send a single command without waiting for response
  * snd_hda_codec_write - send a single command without waiting for response
@@ -151,14 +204,19 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
 int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct,
 int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct,
 			 unsigned int verb, unsigned int parm)
 			 unsigned int verb, unsigned int parm)
 {
 {
+	struct hda_bus *bus = codec->bus;
+	unsigned int res;
 	int err;
 	int err;
+
+	res = make_codec_cmd(codec, nid, direct, verb, parm);
 	snd_hda_power_up(codec);
 	snd_hda_power_up(codec);
-	mutex_lock(&codec->bus->cmd_mutex);
-	err = codec->bus->ops.command(codec, nid, direct, verb, parm);
-	mutex_unlock(&codec->bus->cmd_mutex);
+	mutex_lock(&bus->cmd_mutex);
+	err = bus->ops.command(bus, res);
+	mutex_unlock(&bus->cmd_mutex);
 	snd_hda_power_down(codec);
 	snd_hda_power_down(codec);
 	return err;
 	return err;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_codec_write);
 
 
 /**
 /**
  * snd_hda_sequence_write - sequence writes
  * snd_hda_sequence_write - sequence writes
@@ -173,6 +231,7 @@ void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq)
 	for (; seq->nid; seq++)
 	for (; seq->nid; seq++)
 		snd_hda_codec_write(codec, seq->nid, 0, seq->verb, seq->param);
 		snd_hda_codec_write(codec, seq->nid, 0, seq->verb, seq->param);
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_sequence_write);
 
 
 /**
 /**
  * snd_hda_get_sub_nodes - get the range of sub nodes
  * snd_hda_get_sub_nodes - get the range of sub nodes
@@ -194,6 +253,7 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid,
 	*start_id = (parm >> 16) & 0x7fff;
 	*start_id = (parm >> 16) & 0x7fff;
 	return (int)(parm & 0x7fff);
 	return (int)(parm & 0x7fff);
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes);
 
 
 /**
 /**
  * snd_hda_get_connections - get connection list
  * snd_hda_get_connections - get connection list
@@ -282,6 +342,7 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
 	}
 	}
 	return conns;
 	return conns;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_get_connections);
 
 
 
 
 /**
 /**
@@ -316,6 +377,7 @@ int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex)
 
 
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_queue_unsol_event);
 
 
 /*
 /*
  * process queued unsolicited events
  * process queued unsolicited events
@@ -345,7 +407,7 @@ static void process_unsol_events(struct work_struct *work)
 /*
 /*
  * initialize unsolicited queue
  * initialize unsolicited queue
  */
  */
-static int __devinit init_unsol_queue(struct hda_bus *bus)
+static int init_unsol_queue(struct hda_bus *bus)
 {
 {
 	struct hda_bus_unsolicited *unsol;
 	struct hda_bus_unsolicited *unsol;
 
 
@@ -391,9 +453,24 @@ static int snd_hda_bus_free(struct hda_bus *bus)
 static int snd_hda_bus_dev_free(struct snd_device *device)
 static int snd_hda_bus_dev_free(struct snd_device *device)
 {
 {
 	struct hda_bus *bus = device->device_data;
 	struct hda_bus *bus = device->device_data;
+	bus->shutdown = 1;
 	return snd_hda_bus_free(bus);
 	return snd_hda_bus_free(bus);
 }
 }
 
 
+#ifdef CONFIG_SND_HDA_HWDEP
+static int snd_hda_bus_dev_register(struct snd_device *device)
+{
+	struct hda_bus *bus = device->device_data;
+	struct hda_codec *codec;
+	list_for_each_entry(codec, &bus->codec_list, list) {
+		snd_hda_hwdep_add_sysfs(codec);
+	}
+	return 0;
+}
+#else
+#define snd_hda_bus_dev_register	NULL
+#endif
+
 /**
 /**
  * snd_hda_bus_new - create a HDA bus
  * snd_hda_bus_new - create a HDA bus
  * @card: the card entry
  * @card: the card entry
@@ -402,13 +479,14 @@ static int snd_hda_bus_dev_free(struct snd_device *device)
  *
  *
  * Returns 0 if successful, or a negative error code.
  * Returns 0 if successful, or a negative error code.
  */
  */
-int __devinit snd_hda_bus_new(struct snd_card *card,
+int /*__devinit*/ snd_hda_bus_new(struct snd_card *card,
 			      const struct hda_bus_template *temp,
 			      const struct hda_bus_template *temp,
 			      struct hda_bus **busp)
 			      struct hda_bus **busp)
 {
 {
 	struct hda_bus *bus;
 	struct hda_bus *bus;
 	int err;
 	int err;
 	static struct snd_device_ops dev_ops = {
 	static struct snd_device_ops dev_ops = {
+		.dev_register = snd_hda_bus_dev_register,
 		.dev_free = snd_hda_bus_dev_free,
 		.dev_free = snd_hda_bus_dev_free,
 	};
 	};
 
 
@@ -430,6 +508,7 @@ int __devinit snd_hda_bus_new(struct snd_card *card,
 	bus->private_data = temp->private_data;
 	bus->private_data = temp->private_data;
 	bus->pci = temp->pci;
 	bus->pci = temp->pci;
 	bus->modelname = temp->modelname;
 	bus->modelname = temp->modelname;
+	bus->power_save = temp->power_save;
 	bus->ops = temp->ops;
 	bus->ops = temp->ops;
 
 
 	mutex_init(&bus->cmd_mutex);
 	mutex_init(&bus->cmd_mutex);
@@ -444,27 +523,42 @@ int __devinit snd_hda_bus_new(struct snd_card *card,
 		*busp = bus;
 		*busp = bus;
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_bus_new);
 
 
 #ifdef CONFIG_SND_HDA_GENERIC
 #ifdef CONFIG_SND_HDA_GENERIC
 #define is_generic_config(codec) \
 #define is_generic_config(codec) \
-	(codec->bus->modelname && !strcmp(codec->bus->modelname, "generic"))
+	(codec->modelname && !strcmp(codec->modelname, "generic"))
 #else
 #else
 #define is_generic_config(codec)	0
 #define is_generic_config(codec)	0
 #endif
 #endif
 
 
+#ifdef MODULE
+#define HDA_MODREQ_MAX_COUNT	2	/* two request_modules()'s */
+#else
+#define HDA_MODREQ_MAX_COUNT	0	/* all presets are statically linked */
+#endif
+
 /*
 /*
  * find a matching codec preset
  * find a matching codec preset
  */
  */
-static const struct hda_codec_preset __devinit *
+static const struct hda_codec_preset *
 find_codec_preset(struct hda_codec *codec)
 find_codec_preset(struct hda_codec *codec)
 {
 {
-	const struct hda_codec_preset **tbl, *preset;
+	struct hda_codec_preset_list *tbl;
+	const struct hda_codec_preset *preset;
+	int mod_requested = 0;
 
 
 	if (is_generic_config(codec))
 	if (is_generic_config(codec))
 		return NULL; /* use the generic parser */
 		return NULL; /* use the generic parser */
 
 
-	for (tbl = hda_preset_tables; *tbl; tbl++) {
-		for (preset = *tbl; preset->id; preset++) {
+ again:
+	mutex_lock(&preset_mutex);
+	list_for_each_entry(tbl, &hda_preset_tables, list) {
+		if (!try_module_get(tbl->owner)) {
+			snd_printk(KERN_ERR "hda_codec: cannot module_get\n");
+			continue;
+		}
+		for (preset = tbl->preset; preset->id; preset++) {
 			u32 mask = preset->mask;
 			u32 mask = preset->mask;
 			if (preset->afg && preset->afg != codec->afg)
 			if (preset->afg && preset->afg != codec->afg)
 				continue;
 				continue;
@@ -474,23 +568,40 @@ find_codec_preset(struct hda_codec *codec)
 				mask = ~0;
 				mask = ~0;
 			if (preset->id == (codec->vendor_id & mask) &&
 			if (preset->id == (codec->vendor_id & mask) &&
 			    (!preset->rev ||
 			    (!preset->rev ||
-			     preset->rev == codec->revision_id))
+			     preset->rev == codec->revision_id)) {
+				mutex_unlock(&preset_mutex);
+				codec->owner = tbl->owner;
 				return preset;
 				return preset;
+			}
 		}
 		}
+		module_put(tbl->owner);
+	}
+	mutex_unlock(&preset_mutex);
+
+	if (mod_requested < HDA_MODREQ_MAX_COUNT) {
+		char name[32];
+		if (!mod_requested)
+			snprintf(name, sizeof(name), "snd-hda-codec-id:%08x",
+				 codec->vendor_id);
+		else
+			snprintf(name, sizeof(name), "snd-hda-codec-id:%04x*",
+				 (codec->vendor_id >> 16) & 0xffff);
+		request_module(name);
+		mod_requested++;
+		goto again;
 	}
 	}
 	return NULL;
 	return NULL;
 }
 }
 
 
 /*
 /*
- * snd_hda_get_codec_name - store the codec name
+ * get_codec_name - store the codec name
  */
  */
-void snd_hda_get_codec_name(struct hda_codec *codec,
-			    char *name, int namelen)
+static int get_codec_name(struct hda_codec *codec)
 {
 {
 	const struct hda_vendor_id *c;
 	const struct hda_vendor_id *c;
 	const char *vendor = NULL;
 	const char *vendor = NULL;
 	u16 vendor_id = codec->vendor_id >> 16;
 	u16 vendor_id = codec->vendor_id >> 16;
-	char tmp[16];
+	char tmp[16], name[32];
 
 
 	for (c = hda_vendor_ids; c->id; c++) {
 	for (c = hda_vendor_ids; c->id; c++) {
 		if (c->id == vendor_id) {
 		if (c->id == vendor_id) {
@@ -503,16 +614,21 @@ void snd_hda_get_codec_name(struct hda_codec *codec,
 		vendor = tmp;
 		vendor = tmp;
 	}
 	}
 	if (codec->preset && codec->preset->name)
 	if (codec->preset && codec->preset->name)
-		snprintf(name, namelen, "%s %s", vendor, codec->preset->name);
+		snprintf(name, sizeof(name), "%s %s", vendor,
+			 codec->preset->name);
 	else
 	else
-		snprintf(name, namelen, "%s ID %x", vendor,
+		snprintf(name, sizeof(name), "%s ID %x", vendor,
 			 codec->vendor_id & 0xffff);
 			 codec->vendor_id & 0xffff);
+	codec->name = kstrdup(name, GFP_KERNEL);
+	if (!codec->name)
+		return -ENOMEM;
+	return 0;
 }
 }
 
 
 /*
 /*
  * look for an AFG and MFG nodes
  * look for an AFG and MFG nodes
  */
  */
-static void __devinit setup_fg_nodes(struct hda_codec *codec)
+static void /*__devinit*/ setup_fg_nodes(struct hda_codec *codec)
 {
 {
 	int i, total_nodes;
 	int i, total_nodes;
 	hda_nid_t nid;
 	hda_nid_t nid;
@@ -571,11 +687,15 @@ static void snd_hda_codec_free(struct hda_codec *codec)
 	flush_scheduled_work();
 	flush_scheduled_work();
 #endif
 #endif
 	list_del(&codec->list);
 	list_del(&codec->list);
+	snd_array_free(&codec->mixers);
 	codec->bus->caddr_tbl[codec->addr] = NULL;
 	codec->bus->caddr_tbl[codec->addr] = NULL;
 	if (codec->patch_ops.free)
 	if (codec->patch_ops.free)
 		codec->patch_ops.free(codec);
 		codec->patch_ops.free(codec);
+	module_put(codec->owner);
 	free_hda_cache(&codec->amp_cache);
 	free_hda_cache(&codec->amp_cache);
 	free_hda_cache(&codec->cmd_cache);
 	free_hda_cache(&codec->cmd_cache);
+	kfree(codec->name);
+	kfree(codec->modelname);
 	kfree(codec->wcaps);
 	kfree(codec->wcaps);
 	kfree(codec);
 	kfree(codec);
 }
 }
@@ -588,8 +708,8 @@ static void snd_hda_codec_free(struct hda_codec *codec)
  *
  *
  * 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,
-				struct hda_codec **codecp)
+int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
+				    int do_init, struct hda_codec **codecp)
 {
 {
 	struct hda_codec *codec;
 	struct hda_codec *codec;
 	char component[31];
 	char component[31];
@@ -617,6 +737,14 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
 	mutex_init(&codec->spdif_mutex);
 	mutex_init(&codec->spdif_mutex);
 	init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
 	init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
 	init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
 	init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
+	snd_array_init(&codec->mixers, sizeof(struct snd_kcontrol *), 32);
+	if (codec->bus->modelname) {
+		codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
+		if (!codec->modelname) {
+			snd_hda_codec_free(codec);
+			return -ENODEV;
+		}
+	}
 
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 	INIT_DELAYED_WORK(&codec->power_work, hda_power_work);
 	INIT_DELAYED_WORK(&codec->power_work, hda_power_work);
@@ -662,12 +790,44 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
 			snd_hda_codec_read(codec, nid, 0,
 			snd_hda_codec_read(codec, nid, 0,
 					   AC_VERB_GET_SUBSYSTEM_ID, 0);
 					   AC_VERB_GET_SUBSYSTEM_ID, 0);
 	}
 	}
+	if (bus->modelname)
+		codec->modelname = kstrdup(bus->modelname, GFP_KERNEL);
+
+	if (do_init) {
+		err = snd_hda_codec_configure(codec);
+		if (err < 0) {
+			snd_hda_codec_free(codec);
+			return err;
+		}
+	}
+	snd_hda_codec_proc_new(codec);
+
+	snd_hda_create_hwdep(codec);
+
+	sprintf(component, "HDA:%08x,%08x,%08x", codec->vendor_id,
+		codec->subsystem_id, codec->revision_id);
+	snd_component_add(codec->bus->card, component);
+
+	if (codecp)
+		*codecp = codec;
+	return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_codec_new);
+
+int snd_hda_codec_configure(struct hda_codec *codec)
+{
+	int err;
 
 
 	codec->preset = find_codec_preset(codec);
 	codec->preset = find_codec_preset(codec);
+	if (!codec->name) {
+		err = get_codec_name(codec);
+		if (err < 0)
+			return err;
+	}
 	/* audio codec should override the mixer name */
 	/* audio codec should override the mixer name */
-	if (codec->afg || !*bus->card->mixername)
-		snd_hda_get_codec_name(codec, bus->card->mixername,
-				       sizeof(bus->card->mixername));
+	if (codec->afg || !*codec->bus->card->mixername)
+		strlcpy(codec->bus->card->mixername, codec->name,
+			sizeof(codec->bus->card->mixername));
 
 
 	if (is_generic_config(codec)) {
 	if (is_generic_config(codec)) {
 		err = snd_hda_parse_generic_codec(codec);
 		err = snd_hda_parse_generic_codec(codec);
@@ -684,25 +844,9 @@ int __devinit snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
 		printk(KERN_ERR "hda-codec: No codec parser is available\n");
 		printk(KERN_ERR "hda-codec: No codec parser is available\n");
 
 
  patched:
  patched:
-	if (err < 0) {
-		snd_hda_codec_free(codec);
-		return err;
-	}
-
-	if (codec->patch_ops.unsol_event)
-		init_unsol_queue(bus);
-
-	snd_hda_codec_proc_new(codec);
-#ifdef CONFIG_SND_HDA_HWDEP
-	snd_hda_create_hwdep(codec);
-#endif
-
-	sprintf(component, "HDA:%08x,%08x,%08x", codec->vendor_id, codec->subsystem_id, codec->revision_id);
-	snd_component_add(codec->bus->card, component);
-
-	if (codecp)
-		*codecp = codec;
-	return 0;
+	if (!err && codec->patch_ops.unsol_event)
+		err = init_unsol_queue(codec->bus);
+	return err;
 }
 }
 
 
 /**
 /**
@@ -728,6 +872,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
 	msleep(1);
 	msleep(1);
 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, format);
 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, format);
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_codec_setup_stream);
 
 
 void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
 void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
 {
 {
@@ -741,6 +886,7 @@ void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0);
 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0);
 #endif
 #endif
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup_stream);
 
 
 /*
 /*
  * amp access functions
  * amp access functions
@@ -752,17 +898,17 @@ void snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
 #define INFO_AMP_VOL(ch)	(1 << (1 + (ch)))
 #define INFO_AMP_VOL(ch)	(1 << (1 + (ch)))
 
 
 /* initialize the hash table */
 /* initialize the hash table */
-static void __devinit init_hda_cache(struct hda_cache_rec *cache,
+static void /*__devinit*/ init_hda_cache(struct hda_cache_rec *cache,
 				     unsigned int record_size)
 				     unsigned int record_size)
 {
 {
 	memset(cache, 0, sizeof(*cache));
 	memset(cache, 0, sizeof(*cache));
 	memset(cache->hash, 0xff, sizeof(cache->hash));
 	memset(cache->hash, 0xff, sizeof(cache->hash));
-	cache->record_size = record_size;
+	snd_array_init(&cache->buf, record_size, 64);
 }
 }
 
 
 static void free_hda_cache(struct hda_cache_rec *cache)
 static void free_hda_cache(struct hda_cache_rec *cache)
 {
 {
-	kfree(cache->buffer);
+	snd_array_free(&cache->buf);
 }
 }
 
 
 /* query the hash.  allocate an entry if not found. */
 /* query the hash.  allocate an entry if not found. */
@@ -774,35 +920,17 @@ static struct hda_cache_head  *get_alloc_hash(struct hda_cache_rec *cache,
 	struct hda_cache_head *info;
 	struct hda_cache_head *info;
 
 
 	while (cur != 0xffff) {
 	while (cur != 0xffff) {
-		info = (struct hda_cache_head *)(cache->buffer +
-						 cur * cache->record_size);
+		info = snd_array_elem(&cache->buf, cur);
 		if (info->key == key)
 		if (info->key == key)
 			return info;
 			return info;
 		cur = info->next;
 		cur = info->next;
 	}
 	}
 
 
 	/* add a new hash entry */
 	/* add a new hash entry */
-	if (cache->num_entries >= cache->size) {
-		/* reallocate the array */
-		unsigned int new_size = cache->size + 64;
-		void *new_buffer;
-		new_buffer = kcalloc(new_size, cache->record_size, GFP_KERNEL);
-		if (!new_buffer) {
-			snd_printk(KERN_ERR "hda_codec: "
-				   "can't malloc amp_info\n");
-			return NULL;
-		}
-		if (cache->buffer) {
-			memcpy(new_buffer, cache->buffer,
-			       cache->size * cache->record_size);
-			kfree(cache->buffer);
-		}
-		cache->size = new_size;
-		cache->buffer = new_buffer;
-	}
-	cur = cache->num_entries++;
-	info = (struct hda_cache_head *)(cache->buffer +
-					 cur * cache->record_size);
+	info = snd_array_new(&cache->buf);
+	if (!info)
+		return NULL;
+	cur = snd_array_index(&cache->buf, info);
 	info->key = key;
 	info->key = key;
 	info->val = 0;
 	info->val = 0;
 	info->next = cache->hash[idx];
 	info->next = cache->hash[idx];
@@ -840,6 +968,7 @@ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
 	}
 	}
 	return info->amp_caps;
 	return info->amp_caps;
 }
 }
+EXPORT_SYMBOL_HDA(query_amp_caps);
 
 
 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)
@@ -853,6 +982,7 @@ int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
 	info->head.val |= INFO_AMP_CAPS;
 	info->head.val |= INFO_AMP_CAPS;
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_override_amp_caps);
 
 
 /*
 /*
  * read the current volume to info
  * read the current volume to info
@@ -906,6 +1036,7 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
 		return 0;
 		return 0;
 	return get_vol_mute(codec, info, nid, ch, direction, index);
 	return get_vol_mute(codec, info, nid, ch, direction, index);
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read);
 
 
 /*
 /*
  * update the AMP value, mask = bit mask to set, val = the value
  * update the AMP value, mask = bit mask to set, val = the value
@@ -925,6 +1056,7 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
 	put_vol_mute(codec, info, nid, ch, direction, idx, val);
 	put_vol_mute(codec, info, nid, ch, direction, idx, val);
 	return 1;
 	return 1;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update);
 
 
 /*
 /*
  * update the AMP stereo with the same mask and value
  * update the AMP stereo with the same mask and value
@@ -938,15 +1070,16 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid,
 						idx, mask, val);
 						idx, mask, val);
 	return ret;
 	return ret;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo);
 
 
 #ifdef SND_HDA_NEEDS_RESUME
 #ifdef SND_HDA_NEEDS_RESUME
 /* resume the all amp commands from the cache */
 /* resume the all amp commands from the cache */
 void snd_hda_codec_resume_amp(struct hda_codec *codec)
 void snd_hda_codec_resume_amp(struct hda_codec *codec)
 {
 {
-	struct hda_amp_info *buffer = codec->amp_cache.buffer;
+	struct hda_amp_info *buffer = codec->amp_cache.buf.list;
 	int i;
 	int i;
 
 
-	for (i = 0; i < codec->amp_cache.size; i++, buffer++) {
+	for (i = 0; i < codec->amp_cache.buf.used; i++, buffer++) {
 		u32 key = buffer->head.key;
 		u32 key = buffer->head.key;
 		hda_nid_t nid;
 		hda_nid_t nid;
 		unsigned int idx, dir, ch;
 		unsigned int idx, dir, ch;
@@ -963,6 +1096,7 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec)
 		}
 		}
 	}
 	}
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp);
 #endif /* SND_HDA_NEEDS_RESUME */
 #endif /* SND_HDA_NEEDS_RESUME */
 
 
 /* volume */
 /* volume */
@@ -990,6 +1124,7 @@ int snd_hda_mixer_amp_volume_info(struct snd_kcontrol *kcontrol,
 	uinfo->value.integer.max = caps;
 	uinfo->value.integer.max = caps;
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_info);
 
 
 int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
 int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
 				 struct snd_ctl_elem_value *ucontrol)
 				 struct snd_ctl_elem_value *ucontrol)
@@ -1009,6 +1144,7 @@ int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol,
 			& HDA_AMP_VOLMASK;
 			& HDA_AMP_VOLMASK;
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_get);
 
 
 int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
 int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
 				 struct snd_ctl_elem_value *ucontrol)
 				 struct snd_ctl_elem_value *ucontrol)
@@ -1033,6 +1169,7 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol,
 	snd_hda_power_down(codec);
 	snd_hda_power_down(codec);
 	return change;
 	return change;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_volume_put);
 
 
 int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
 int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
 			  unsigned int size, unsigned int __user *_tlv)
 			  unsigned int size, unsigned int __user *_tlv)
@@ -1059,6 +1196,7 @@ int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag,
 		return -EFAULT;
 		return -EFAULT;
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_tlv);
 
 
 /*
 /*
  * set (static) TLV for virtual master volume; recalculated as max 0dB
  * set (static) TLV for virtual master volume; recalculated as max 0dB
@@ -1078,6 +1216,7 @@ void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
 	tlv[2] = -nums * step;
 	tlv[2] = -nums * step;
 	tlv[3] = step;
 	tlv[3] = step;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_set_vmaster_tlv);
 
 
 /* find a mixer control element with the given name */
 /* find a mixer control element with the given name */
 static struct snd_kcontrol *
 static struct snd_kcontrol *
@@ -1097,6 +1236,69 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
 {
 {
 	return _snd_hda_find_mixer_ctl(codec, name, 0);
 	return _snd_hda_find_mixer_ctl(codec, name, 0);
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl);
+
+/* Add a control element and assign to the codec */
+int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl)
+{
+	int err;
+	struct snd_kcontrol **knewp;
+
+	err = snd_ctl_add(codec->bus->card, kctl);
+	if (err < 0)
+		return err;
+	knewp = snd_array_new(&codec->mixers);
+	if (!knewp)
+		return -ENOMEM;
+	*knewp = kctl;
+	return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_ctl_add);
+
+#ifdef CONFIG_SND_HDA_RECONFIG
+/* Clear all controls assigned to the given codec */
+void snd_hda_ctls_clear(struct hda_codec *codec)
+{
+	int i;
+	struct snd_kcontrol **kctls = codec->mixers.list;
+	for (i = 0; i < codec->mixers.used; i++)
+		snd_ctl_remove(codec->bus->card, kctls[i]);
+	snd_array_free(&codec->mixers);
+}
+
+void snd_hda_codec_reset(struct hda_codec *codec)
+{
+	int i;
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+	cancel_delayed_work(&codec->power_work);
+	flush_scheduled_work();
+#endif
+	snd_hda_ctls_clear(codec);
+	/* relase PCMs */
+	for (i = 0; i < codec->num_pcms; i++) {
+		if (codec->pcm_info[i].pcm) {
+			snd_device_free(codec->bus->card,
+					codec->pcm_info[i].pcm);
+			clear_bit(codec->pcm_info[i].device,
+				  codec->bus->pcm_dev_bits);
+		}
+	}
+	if (codec->patch_ops.free)
+		codec->patch_ops.free(codec);
+	codec->proc_widget_hook = NULL;
+	codec->spec = NULL;
+	free_hda_cache(&codec->amp_cache);
+	free_hda_cache(&codec->cmd_cache);
+	init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
+	init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
+	codec->num_pcms = 0;
+	codec->pcm_info = NULL;
+	codec->preset = NULL;
+	module_put(codec->owner);
+	codec->owner = NULL;
+}
+#endif /* CONFIG_SND_HDA_RECONFIG */
 
 
 /* create a virtual master control and add slaves */
 /* create a virtual master control and add slaves */
 int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
 int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
@@ -1115,7 +1317,7 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
 	kctl = snd_ctl_make_virtual_master(name, tlv);
 	kctl = snd_ctl_make_virtual_master(name, tlv);
 	if (!kctl)
 	if (!kctl)
 		return -ENOMEM;
 		return -ENOMEM;
-	err = snd_ctl_add(codec->bus->card, kctl);
+	err = snd_hda_ctl_add(codec, kctl);
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
 	
 	
@@ -1133,6 +1335,7 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
 	}
 	}
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_add_vmaster);
 
 
 /* switch */
 /* switch */
 int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
 int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
@@ -1146,6 +1349,7 @@ int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol,
 	uinfo->value.integer.max = 1;
 	uinfo->value.integer.max = 1;
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_info);
 
 
 int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
 int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
 				 struct snd_ctl_elem_value *ucontrol)
 				 struct snd_ctl_elem_value *ucontrol)
@@ -1165,6 +1369,7 @@ int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol,
 			 HDA_AMP_MUTE) ? 0 : 1;
 			 HDA_AMP_MUTE) ? 0 : 1;
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_get);
 
 
 int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
 int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
 				 struct snd_ctl_elem_value *ucontrol)
 				 struct snd_ctl_elem_value *ucontrol)
@@ -1195,6 +1400,7 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol,
 	snd_hda_power_down(codec);
 	snd_hda_power_down(codec);
 	return change;
 	return change;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put);
 
 
 /*
 /*
  * bound volume controls
  * bound volume controls
@@ -1220,6 +1426,7 @@ int snd_hda_mixer_bind_switch_get(struct snd_kcontrol *kcontrol,
 	mutex_unlock(&codec->spdif_mutex);
 	mutex_unlock(&codec->spdif_mutex);
 	return err;
 	return err;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_get);
 
 
 int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol,
 int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol,
 				  struct snd_ctl_elem_value *ucontrol)
 				  struct snd_ctl_elem_value *ucontrol)
@@ -1243,6 +1450,7 @@ int snd_hda_mixer_bind_switch_put(struct snd_kcontrol *kcontrol,
 	mutex_unlock(&codec->spdif_mutex);
 	mutex_unlock(&codec->spdif_mutex);
 	return err < 0 ? err : change;
 	return err < 0 ? err : change;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_switch_put);
 
 
 /*
 /*
  * generic bound volume/swtich controls
  * generic bound volume/swtich controls
@@ -1262,6 +1470,7 @@ int snd_hda_mixer_bind_ctls_info(struct snd_kcontrol *kcontrol,
 	mutex_unlock(&codec->spdif_mutex);
 	mutex_unlock(&codec->spdif_mutex);
 	return err;
 	return err;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_info);
 
 
 int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol,
 int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol,
 				struct snd_ctl_elem_value *ucontrol)
 				struct snd_ctl_elem_value *ucontrol)
@@ -1278,6 +1487,7 @@ int snd_hda_mixer_bind_ctls_get(struct snd_kcontrol *kcontrol,
 	mutex_unlock(&codec->spdif_mutex);
 	mutex_unlock(&codec->spdif_mutex);
 	return err;
 	return err;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_get);
 
 
 int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol,
 int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol,
 				struct snd_ctl_elem_value *ucontrol)
 				struct snd_ctl_elem_value *ucontrol)
@@ -1300,6 +1510,7 @@ int snd_hda_mixer_bind_ctls_put(struct snd_kcontrol *kcontrol,
 	mutex_unlock(&codec->spdif_mutex);
 	mutex_unlock(&codec->spdif_mutex);
 	return err < 0 ? err : change;
 	return err < 0 ? err : change;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_ctls_put);
 
 
 int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag,
 int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag,
 			   unsigned int size, unsigned int __user *tlv)
 			   unsigned int size, unsigned int __user *tlv)
@@ -1316,6 +1527,7 @@ int snd_hda_mixer_bind_tlv(struct snd_kcontrol *kcontrol, int op_flag,
 	mutex_unlock(&codec->spdif_mutex);
 	mutex_unlock(&codec->spdif_mutex);
 	return err;
 	return err;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_mixer_bind_tlv);
 
 
 struct hda_ctl_ops snd_hda_bind_vol = {
 struct hda_ctl_ops snd_hda_bind_vol = {
 	.info = snd_hda_mixer_amp_volume_info,
 	.info = snd_hda_mixer_amp_volume_info,
@@ -1323,6 +1535,7 @@ struct hda_ctl_ops snd_hda_bind_vol = {
 	.put = snd_hda_mixer_amp_volume_put,
 	.put = snd_hda_mixer_amp_volume_put,
 	.tlv = snd_hda_mixer_amp_tlv
 	.tlv = snd_hda_mixer_amp_tlv
 };
 };
+EXPORT_SYMBOL_HDA(snd_hda_bind_vol);
 
 
 struct hda_ctl_ops snd_hda_bind_sw = {
 struct hda_ctl_ops snd_hda_bind_sw = {
 	.info = snd_hda_mixer_amp_switch_info,
 	.info = snd_hda_mixer_amp_switch_info,
@@ -1330,6 +1543,7 @@ struct hda_ctl_ops snd_hda_bind_sw = {
 	.put = snd_hda_mixer_amp_switch_put,
 	.put = snd_hda_mixer_amp_switch_put,
 	.tlv = snd_hda_mixer_amp_tlv
 	.tlv = snd_hda_mixer_amp_tlv
 };
 };
+EXPORT_SYMBOL_HDA(snd_hda_bind_sw);
 
 
 /*
 /*
  * SPDIF out controls
  * SPDIF out controls
@@ -1577,9 +1791,11 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
 	}
 	}
 	for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) {
 	for (dig_mix = dig_mixes; dig_mix->name; dig_mix++) {
 		kctl = snd_ctl_new1(dig_mix, codec);
 		kctl = snd_ctl_new1(dig_mix, codec);
+		if (!kctl)
+			return -ENOMEM;
 		kctl->id.index = idx;
 		kctl->id.index = idx;
 		kctl->private_value = nid;
 		kctl->private_value = nid;
-		err = snd_ctl_add(codec->bus->card, kctl);
+		err = snd_hda_ctl_add(codec, kctl);
 		if (err < 0)
 		if (err < 0)
 			return err;
 			return err;
 	}
 	}
@@ -1589,6 +1805,7 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec, hda_nid_t nid)
 	codec->spdif_status = convert_to_spdif_status(codec->spdif_ctls);
 	codec->spdif_status = convert_to_spdif_status(codec->spdif_ctls);
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_create_spdif_out_ctls);
 
 
 /*
 /*
  * SPDIF sharing with analog output
  * SPDIF sharing with analog output
@@ -1623,9 +1840,10 @@ int snd_hda_create_spdif_share_sw(struct hda_codec *codec,
 	if (!mout->dig_out_nid)
 	if (!mout->dig_out_nid)
 		return 0;
 		return 0;
 	/* ATTENTION: here mout is passed as private_data, instead of codec */
 	/* ATTENTION: here mout is passed as private_data, instead of codec */
-	return snd_ctl_add(codec->bus->card,
+	return snd_hda_ctl_add(codec,
 			   snd_ctl_new1(&spdif_share_sw, mout));
 			   snd_ctl_new1(&spdif_share_sw, mout));
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_create_spdif_share_sw);
 
 
 /*
 /*
  * SPDIF input
  * SPDIF input
@@ -1725,7 +1943,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
 	for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) {
 	for (dig_mix = dig_in_ctls; dig_mix->name; dig_mix++) {
 		kctl = snd_ctl_new1(dig_mix, codec);
 		kctl = snd_ctl_new1(dig_mix, codec);
 		kctl->private_value = nid;
 		kctl->private_value = nid;
-		err = snd_ctl_add(codec->bus->card, kctl);
+		err = snd_hda_ctl_add(codec, kctl);
 		if (err < 0)
 		if (err < 0)
 			return err;
 			return err;
 	}
 	}
@@ -1735,6 +1953,7 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
 		AC_DIG1_ENABLE;
 		AC_DIG1_ENABLE;
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls);
 
 
 #ifdef SND_HDA_NEEDS_RESUME
 #ifdef SND_HDA_NEEDS_RESUME
 /*
 /*
@@ -1761,10 +1980,14 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid)
 int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
 int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
 			      int direct, unsigned int verb, unsigned int parm)
 			      int direct, unsigned int verb, unsigned int parm)
 {
 {
+	struct hda_bus *bus = codec->bus;
+	unsigned int res;
 	int err;
 	int err;
+
+	res = make_codec_cmd(codec, nid, direct, verb, parm);
 	snd_hda_power_up(codec);
 	snd_hda_power_up(codec);
-	mutex_lock(&codec->bus->cmd_mutex);
-	err = codec->bus->ops.command(codec, nid, direct, verb, parm);
+	mutex_lock(&bus->cmd_mutex);
+	err = bus->ops.command(bus, res);
 	if (!err) {
 	if (!err) {
 		struct hda_cache_head *c;
 		struct hda_cache_head *c;
 		u32 key = build_cmd_cache_key(nid, verb);
 		u32 key = build_cmd_cache_key(nid, verb);
@@ -1772,18 +1995,19 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
 		if (c)
 		if (c)
 			c->val = parm;
 			c->val = parm;
 	}
 	}
-	mutex_unlock(&codec->bus->cmd_mutex);
+	mutex_unlock(&bus->cmd_mutex);
 	snd_hda_power_down(codec);
 	snd_hda_power_down(codec);
 	return err;
 	return err;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache);
 
 
 /* resume the all commands from the cache */
 /* resume the all commands from the cache */
 void snd_hda_codec_resume_cache(struct hda_codec *codec)
 void snd_hda_codec_resume_cache(struct hda_codec *codec)
 {
 {
-	struct hda_cache_head *buffer = codec->cmd_cache.buffer;
+	struct hda_cache_head *buffer = codec->cmd_cache.buf.list;
 	int i;
 	int i;
 
 
-	for (i = 0; i < codec->cmd_cache.size; i++, buffer++) {
+	for (i = 0; i < codec->cmd_cache.buf.used; i++, buffer++) {
 		u32 key = buffer->key;
 		u32 key = buffer->key;
 		if (!key)
 		if (!key)
 			continue;
 			continue;
@@ -1791,6 +2015,7 @@ void snd_hda_codec_resume_cache(struct hda_codec *codec)
 				    get_cmd_cache_cmd(key), buffer->val);
 				    get_cmd_cache_cmd(key), buffer->val);
 	}
 	}
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_codec_resume_cache);
 
 
 /**
 /**
  * snd_hda_sequence_write_cache - sequence writes with caching
  * snd_hda_sequence_write_cache - sequence writes with caching
@@ -1808,6 +2033,7 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec,
 		snd_hda_codec_write_cache(codec, seq->nid, 0, seq->verb,
 		snd_hda_codec_write_cache(codec, seq->nid, 0, seq->verb,
 					  seq->param);
 					  seq->param);
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache);
 #endif /* SND_HDA_NEEDS_RESUME */
 #endif /* SND_HDA_NEEDS_RESUME */
 
 
 /*
 /*
@@ -1868,6 +2094,17 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
 	}
 	}
 }
 }
 
 
+#ifdef CONFIG_SND_HDA_HWDEP
+/* execute additional init verbs */
+static void hda_exec_init_verbs(struct hda_codec *codec)
+{
+	if (codec->init_verbs.list)
+		snd_hda_sequence_write(codec, codec->init_verbs.list);
+}
+#else
+static inline void hda_exec_init_verbs(struct hda_codec *codec) {}
+#endif
+
 #ifdef SND_HDA_NEEDS_RESUME
 #ifdef SND_HDA_NEEDS_RESUME
 /*
 /*
  * call suspend and power-down; used both from PM and power-save
  * call suspend and power-down; used both from PM and power-save
@@ -1894,6 +2131,7 @@ static void hda_call_codec_resume(struct hda_codec *codec)
 	hda_set_power_state(codec,
 	hda_set_power_state(codec,
 			    codec->afg ? codec->afg : codec->mfg,
 			    codec->afg ? codec->afg : codec->mfg,
 			    AC_PWRST_D0);
 			    AC_PWRST_D0);
+	hda_exec_init_verbs(codec);
 	if (codec->patch_ops.resume)
 	if (codec->patch_ops.resume)
 		codec->patch_ops.resume(codec);
 		codec->patch_ops.resume(codec);
 	else {
 	else {
@@ -1914,28 +2152,37 @@ static void hda_call_codec_resume(struct hda_codec *codec)
  *
  *
  * Returns 0 if successful, otherwise a negative error code.
  * Returns 0 if successful, otherwise a negative error code.
  */
  */
-int __devinit snd_hda_build_controls(struct hda_bus *bus)
+int /*__devinit*/ snd_hda_build_controls(struct hda_bus *bus)
 {
 {
 	struct hda_codec *codec;
 	struct hda_codec *codec;
 
 
 	list_for_each_entry(codec, &bus->codec_list, list) {
 	list_for_each_entry(codec, &bus->codec_list, list) {
-		int err = 0;
-		/* fake as if already powered-on */
-		hda_keep_power_on(codec);
-		/* then fire up */
-		hda_set_power_state(codec,
-				    codec->afg ? codec->afg : codec->mfg,
-				    AC_PWRST_D0);
-		/* continue to initialize... */
-		if (codec->patch_ops.init)
-			err = codec->patch_ops.init(codec);
-		if (!err && codec->patch_ops.build_controls)
-			err = codec->patch_ops.build_controls(codec);
-		snd_hda_power_down(codec);
+		int err = snd_hda_codec_build_controls(codec);
 		if (err < 0)
 		if (err < 0)
 			return err;
 			return err;
 	}
 	}
+	return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_build_controls);
 
 
+int snd_hda_codec_build_controls(struct hda_codec *codec)
+{
+	int err = 0;
+	/* fake as if already powered-on */
+	hda_keep_power_on(codec);
+	/* then fire up */
+	hda_set_power_state(codec,
+			    codec->afg ? codec->afg : codec->mfg,
+			    AC_PWRST_D0);
+	hda_exec_init_verbs(codec);
+	/* continue to initialize... */
+	if (codec->patch_ops.init)
+		err = codec->patch_ops.init(codec);
+	if (!err && codec->patch_ops.build_controls)
+		err = codec->patch_ops.build_controls(codec);
+	snd_hda_power_down(codec);
+	if (err < 0)
+		return err;
 	return 0;
 	return 0;
 }
 }
 
 
@@ -2028,6 +2275,7 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
 
 
 	return val;
 	return val;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format);
 
 
 /**
 /**
  * snd_hda_query_supported_pcm - query the supported PCM rates and formats
  * snd_hda_query_supported_pcm - query the supported PCM rates and formats
@@ -2042,7 +2290,7 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
  *
  *
  * Returns 0 if successful, otherwise a negative error code.
  * Returns 0 if successful, otherwise a negative error code.
  */
  */
-int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
+static int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
 				u32 *ratesp, u64 *formatsp, unsigned int *bpsp)
 				u32 *ratesp, u64 *formatsp, unsigned int *bpsp)
 {
 {
 	int i;
 	int i;
@@ -2207,6 +2455,7 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
 
 
 	return 1;
 	return 1;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_is_supported_format);
 
 
 /*
 /*
  * PCM stuff
  * PCM stuff
@@ -2236,8 +2485,8 @@ static int hda_pcm_default_cleanup(struct hda_pcm_stream *hinfo,
 	return 0;
 	return 0;
 }
 }
 
 
-static int __devinit set_pcm_default_values(struct hda_codec *codec,
-					    struct hda_pcm_stream *info)
+static int set_pcm_default_values(struct hda_codec *codec,
+				  struct hda_pcm_stream *info)
 {
 {
 	/* query support PCM information from the given NID */
 	/* query support PCM information from the given NID */
 	if (info->nid && (!info->rates || !info->formats)) {
 	if (info->nid && (!info->rates || !info->formats)) {
@@ -2263,6 +2512,110 @@ static int __devinit set_pcm_default_values(struct hda_codec *codec,
 	return 0;
 	return 0;
 }
 }
 
 
+/*
+ * get the empty PCM device number to assign
+ */
+static int get_empty_pcm_device(struct hda_bus *bus, int type)
+{
+	static const char *dev_name[HDA_PCM_NTYPES] = {
+		"Audio", "SPDIF", "HDMI", "Modem"
+	};
+	/* starting device index for each PCM type */
+	static int dev_idx[HDA_PCM_NTYPES] = {
+		[HDA_PCM_TYPE_AUDIO] = 0,
+		[HDA_PCM_TYPE_SPDIF] = 1,
+		[HDA_PCM_TYPE_HDMI] = 3,
+		[HDA_PCM_TYPE_MODEM] = 6
+	};
+	/* normal audio device indices; not linear to keep compatibility */
+	static int audio_idx[4] = { 0, 2, 4, 5 };
+	int i, dev;
+
+	switch (type) {
+	case HDA_PCM_TYPE_AUDIO:
+		for (i = 0; i < ARRAY_SIZE(audio_idx); i++) {
+			dev = audio_idx[i];
+			if (!test_bit(dev, bus->pcm_dev_bits))
+				break;
+		}
+		if (i >= ARRAY_SIZE(audio_idx)) {
+			snd_printk(KERN_WARNING "Too many audio devices\n");
+			return -EAGAIN;
+		}
+		break;
+	case HDA_PCM_TYPE_SPDIF:
+	case HDA_PCM_TYPE_HDMI:
+	case HDA_PCM_TYPE_MODEM:
+		dev = dev_idx[type];
+		if (test_bit(dev, bus->pcm_dev_bits)) {
+			snd_printk(KERN_WARNING "%s already defined\n",
+				   dev_name[type]);
+			return -EAGAIN;
+		}
+		break;
+	default:
+		snd_printk(KERN_WARNING "Invalid PCM type %d\n", type);
+		return -EINVAL;
+	}
+	set_bit(dev, bus->pcm_dev_bits);
+	return dev;
+}
+
+/*
+ * attach a new PCM stream
+ */
+static int snd_hda_attach_pcm(struct hda_codec *codec, struct hda_pcm *pcm)
+{
+	struct hda_bus *bus = codec->bus;
+	struct hda_pcm_stream *info;
+	int stream, err;
+
+	if (snd_BUG_ON(!pcm->name))
+		return -EINVAL;
+	for (stream = 0; stream < 2; stream++) {
+		info = &pcm->stream[stream];
+		if (info->substreams) {
+			err = set_pcm_default_values(codec, info);
+			if (err < 0)
+				return err;
+		}
+	}
+	return bus->ops.attach_pcm(bus, codec, pcm);
+}
+
+/* assign all PCMs of the given codec */
+int snd_hda_codec_build_pcms(struct hda_codec *codec)
+{
+	unsigned int pcm;
+	int err;
+
+	if (!codec->num_pcms) {
+		if (!codec->patch_ops.build_pcms)
+			return 0;
+		err = codec->patch_ops.build_pcms(codec);
+		if (err < 0)
+			return err;
+	}
+	for (pcm = 0; pcm < codec->num_pcms; pcm++) {
+		struct hda_pcm *cpcm = &codec->pcm_info[pcm];
+		int dev;
+
+		if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
+			return 0; /* no substreams assigned */
+
+		if (!cpcm->pcm) {
+			dev = get_empty_pcm_device(codec->bus, cpcm->pcm_type);
+			if (dev < 0)
+				return 0;
+			cpcm->device = dev;
+			err = snd_hda_attach_pcm(codec, cpcm);
+			if (err < 0)
+				return err;
+		}
+	}
+	return 0;
+}
+
 /**
 /**
  * snd_hda_build_pcms - build PCM information
  * snd_hda_build_pcms - build PCM information
  * @bus: the BUS
  * @bus: the BUS
@@ -2294,27 +2647,13 @@ int __devinit snd_hda_build_pcms(struct hda_bus *bus)
 	struct hda_codec *codec;
 	struct hda_codec *codec;
 
 
 	list_for_each_entry(codec, &bus->codec_list, list) {
 	list_for_each_entry(codec, &bus->codec_list, list) {
-		unsigned int pcm, s;
-		int err;
-		if (!codec->patch_ops.build_pcms)
-			continue;
-		err = codec->patch_ops.build_pcms(codec);
+		int err = snd_hda_codec_build_pcms(codec);
 		if (err < 0)
 		if (err < 0)
 			return err;
 			return err;
-		for (pcm = 0; pcm < codec->num_pcms; pcm++) {
-			for (s = 0; s < 2; s++) {
-				struct hda_pcm_stream *info;
-				info = &codec->pcm_info[pcm].stream[s];
-				if (!info->substreams)
-					continue;
-				err = set_pcm_default_values(codec, info);
-				if (err < 0)
-					return err;
-			}
-		}
 	}
 	}
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_build_pcms);
 
 
 /**
 /**
  * snd_hda_check_board_config - compare the current codec with the config table
  * snd_hda_check_board_config - compare the current codec with the config table
@@ -2333,11 +2672,11 @@ int snd_hda_check_board_config(struct hda_codec *codec,
 			       int num_configs, const char **models,
 			       int num_configs, const char **models,
 			       const struct snd_pci_quirk *tbl)
 			       const struct snd_pci_quirk *tbl)
 {
 {
-	if (codec->bus->modelname && models) {
+	if (codec->modelname && models) {
 		int i;
 		int i;
 		for (i = 0; i < num_configs; i++) {
 		for (i = 0; i < num_configs; i++) {
 			if (models[i] &&
 			if (models[i] &&
-			    !strcmp(codec->bus->modelname, models[i])) {
+			    !strcmp(codec->modelname, models[i])) {
 				snd_printd(KERN_INFO "hda_codec: model '%s' is "
 				snd_printd(KERN_INFO "hda_codec: model '%s' is "
 					   "selected\n", models[i]);
 					   "selected\n", models[i]);
 				return i;
 				return i;
@@ -2370,6 +2709,7 @@ int snd_hda_check_board_config(struct hda_codec *codec,
 	}
 	}
 	return -1;
 	return -1;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_check_board_config);
 
 
 /**
 /**
  * snd_hda_add_new_ctls - create controls from the array
  * snd_hda_add_new_ctls - create controls from the array
@@ -2390,7 +2730,7 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
 		kctl = snd_ctl_new1(knew, codec);
 		kctl = snd_ctl_new1(knew, codec);
 		if (!kctl)
 		if (!kctl)
 			return -ENOMEM;
 			return -ENOMEM;
-		err = snd_ctl_add(codec->bus->card, kctl);
+		err = snd_hda_ctl_add(codec, kctl);
 		if (err < 0) {
 		if (err < 0) {
 			if (!codec->addr)
 			if (!codec->addr)
 				return err;
 				return err;
@@ -2398,13 +2738,14 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
 			if (!kctl)
 			if (!kctl)
 				return -ENOMEM;
 				return -ENOMEM;
 			kctl->id.device = codec->addr;
 			kctl->id.device = codec->addr;
-			err = snd_ctl_add(codec->bus->card, kctl);
+			err = snd_hda_ctl_add(codec, kctl);
 			if (err < 0)
 			if (err < 0)
 				return err;
 				return err;
 		}
 		}
 	}
 	}
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_add_new_ctls);
 
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
 static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
@@ -2414,6 +2755,7 @@ static void hda_power_work(struct work_struct *work)
 {
 {
 	struct hda_codec *codec =
 	struct hda_codec *codec =
 		container_of(work, struct hda_codec, power_work.work);
 		container_of(work, struct hda_codec, power_work.work);
+	struct hda_bus *bus = codec->bus;
 
 
 	if (!codec->power_on || codec->power_count) {
 	if (!codec->power_on || codec->power_count) {
 		codec->power_transition = 0;
 		codec->power_transition = 0;
@@ -2421,8 +2763,8 @@ static void hda_power_work(struct work_struct *work)
 	}
 	}
 
 
 	hda_call_codec_suspend(codec);
 	hda_call_codec_suspend(codec);
-	if (codec->bus->ops.pm_notify)
-		codec->bus->ops.pm_notify(codec);
+	if (bus->ops.pm_notify)
+		bus->ops.pm_notify(bus);
 }
 }
 
 
 static void hda_keep_power_on(struct hda_codec *codec)
 static void hda_keep_power_on(struct hda_codec *codec)
@@ -2433,29 +2775,39 @@ static void hda_keep_power_on(struct hda_codec *codec)
 
 
 void snd_hda_power_up(struct hda_codec *codec)
 void snd_hda_power_up(struct hda_codec *codec)
 {
 {
+	struct hda_bus *bus = codec->bus;
+
 	codec->power_count++;
 	codec->power_count++;
 	if (codec->power_on || codec->power_transition)
 	if (codec->power_on || codec->power_transition)
 		return;
 		return;
 
 
 	codec->power_on = 1;
 	codec->power_on = 1;
-	if (codec->bus->ops.pm_notify)
-		codec->bus->ops.pm_notify(codec);
+	if (bus->ops.pm_notify)
+		bus->ops.pm_notify(bus);
 	hda_call_codec_resume(codec);
 	hda_call_codec_resume(codec);
 	cancel_delayed_work(&codec->power_work);
 	cancel_delayed_work(&codec->power_work);
 	codec->power_transition = 0;
 	codec->power_transition = 0;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_power_up);
+
+#define power_save(codec)	\
+	((codec)->bus->power_save ? *(codec)->bus->power_save : 0)
+
+#define power_save(codec)	\
+	((codec)->bus->power_save ? *(codec)->bus->power_save : 0)
 
 
 void snd_hda_power_down(struct hda_codec *codec)
 void snd_hda_power_down(struct hda_codec *codec)
 {
 {
 	--codec->power_count;
 	--codec->power_count;
 	if (!codec->power_on || codec->power_count || codec->power_transition)
 	if (!codec->power_on || codec->power_count || codec->power_transition)
 		return;
 		return;
-	if (power_save) {
+	if (power_save(codec)) {
 		codec->power_transition = 1; /* avoid reentrance */
 		codec->power_transition = 1; /* avoid reentrance */
 		schedule_delayed_work(&codec->power_work,
 		schedule_delayed_work(&codec->power_work,
-				      msecs_to_jiffies(power_save * 1000));
+				msecs_to_jiffies(power_save(codec) * 1000));
 	}
 	}
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_power_down);
 
 
 int snd_hda_check_amp_list_power(struct hda_codec *codec,
 int snd_hda_check_amp_list_power(struct hda_codec *codec,
 				 struct hda_loopback_check *check,
 				 struct hda_loopback_check *check,
@@ -2492,6 +2844,7 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
 	}
 	}
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_check_amp_list_power);
 #endif
 #endif
 
 
 /*
 /*
@@ -2511,6 +2864,7 @@ int snd_hda_ch_mode_info(struct hda_codec *codec,
 		chmode[uinfo->value.enumerated.item].channels);
 		chmode[uinfo->value.enumerated.item].channels);
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_ch_mode_info);
 
 
 int snd_hda_ch_mode_get(struct hda_codec *codec,
 int snd_hda_ch_mode_get(struct hda_codec *codec,
 			struct snd_ctl_elem_value *ucontrol,
 			struct snd_ctl_elem_value *ucontrol,
@@ -2528,6 +2882,7 @@ int snd_hda_ch_mode_get(struct hda_codec *codec,
 	}
 	}
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_ch_mode_get);
 
 
 int snd_hda_ch_mode_put(struct hda_codec *codec,
 int snd_hda_ch_mode_put(struct hda_codec *codec,
 			struct snd_ctl_elem_value *ucontrol,
 			struct snd_ctl_elem_value *ucontrol,
@@ -2548,6 +2903,7 @@ int snd_hda_ch_mode_put(struct hda_codec *codec,
 		snd_hda_sequence_write_cache(codec, chmode[mode].sequence);
 		snd_hda_sequence_write_cache(codec, chmode[mode].sequence);
 	return 1;
 	return 1;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_ch_mode_put);
 
 
 /*
 /*
  * input MUX helper
  * input MUX helper
@@ -2568,6 +2924,7 @@ int snd_hda_input_mux_info(const struct hda_input_mux *imux,
 	strcpy(uinfo->value.enumerated.name, imux->items[index].label);
 	strcpy(uinfo->value.enumerated.name, imux->items[index].label);
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_input_mux_info);
 
 
 int snd_hda_input_mux_put(struct hda_codec *codec,
 int snd_hda_input_mux_put(struct hda_codec *codec,
 			  const struct hda_input_mux *imux,
 			  const struct hda_input_mux *imux,
@@ -2589,6 +2946,7 @@ int snd_hda_input_mux_put(struct hda_codec *codec,
 	*cur_val = idx;
 	*cur_val = idx;
 	return 1;
 	return 1;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_input_mux_put);
 
 
 
 
 /*
 /*
@@ -2641,6 +2999,7 @@ int snd_hda_multi_out_dig_open(struct hda_codec *codec,
 	mutex_unlock(&codec->spdif_mutex);
 	mutex_unlock(&codec->spdif_mutex);
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_open);
 
 
 int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
 int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
 				  struct hda_multi_out *mout,
 				  struct hda_multi_out *mout,
@@ -2653,6 +3012,7 @@ int snd_hda_multi_out_dig_prepare(struct hda_codec *codec,
 	mutex_unlock(&codec->spdif_mutex);
 	mutex_unlock(&codec->spdif_mutex);
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_prepare);
 
 
 /*
 /*
  * release the digital out
  * release the digital out
@@ -2665,6 +3025,7 @@ int snd_hda_multi_out_dig_close(struct hda_codec *codec,
 	mutex_unlock(&codec->spdif_mutex);
 	mutex_unlock(&codec->spdif_mutex);
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_multi_out_dig_close);
 
 
 /*
 /*
  * set up more restrictions for analog out
  * set up more restrictions for analog out
@@ -2704,6 +3065,7 @@ int snd_hda_multi_out_analog_open(struct hda_codec *codec,
 	return snd_pcm_hw_constraint_step(substream->runtime, 0,
 	return snd_pcm_hw_constraint_step(substream->runtime, 0,
 					  SNDRV_PCM_HW_PARAM_CHANNELS, 2);
 					  SNDRV_PCM_HW_PARAM_CHANNELS, 2);
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_open);
 
 
 /*
 /*
  * set up the i/o for analog out
  * set up the i/o for analog out
@@ -2762,6 +3124,7 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
 	}
 	}
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_prepare);
 
 
 /*
 /*
  * clean up the setting for analog out
  * clean up the setting for analog out
@@ -2788,6 +3151,7 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
 	mutex_unlock(&codec->spdif_mutex);
 	mutex_unlock(&codec->spdif_mutex);
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_cleanup);
 
 
 /*
 /*
  * Helper for automatic pin configuration
  * Helper for automatic pin configuration
@@ -3073,11 +3437,13 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
 
 
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_parse_pin_def_config);
 
 
 /* labels for input pins */
 /* labels for input pins */
 const char *auto_pin_cfg_labels[AUTO_PIN_LAST] = {
 const char *auto_pin_cfg_labels[AUTO_PIN_LAST] = {
 	"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux"
 	"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux"
 };
 };
+EXPORT_SYMBOL_HDA(auto_pin_cfg_labels);
 
 
 
 
 #ifdef CONFIG_PM
 #ifdef CONFIG_PM
@@ -3105,11 +3471,11 @@ int snd_hda_suspend(struct hda_bus *bus, pm_message_t state)
 	}
 	}
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL_HDA(snd_hda_suspend);
 
 
 /**
 /**
  * snd_hda_resume - resume the codecs
  * snd_hda_resume - resume the codecs
  * @bus: the HDA bus
  * @bus: the HDA bus
- * @state: resume state
  *
  *
  * Returns 0 if successful.
  * Returns 0 if successful.
  *
  *
@@ -3126,16 +3492,79 @@ int snd_hda_resume(struct hda_bus *bus)
 	}
 	}
 	return 0;
 	return 0;
 }
 }
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-int snd_hda_codecs_inuse(struct hda_bus *bus)
-{
-	struct hda_codec *codec;
+EXPORT_SYMBOL_HDA(snd_hda_resume);
+#endif /* CONFIG_PM */
 
 
-	list_for_each_entry(codec, &bus->codec_list, list) {
-		if (snd_hda_codec_needs_resume(codec))
-			return 1;
+/*
+ * generic arrays
+ */
+
+/* get a new element from the given array
+ * if it exceeds the pre-allocated array size, re-allocate the array
+ */
+void *snd_array_new(struct snd_array *array)
+{
+	if (array->used >= array->alloced) {
+		int num = array->alloced + array->alloc_align;
+		void *nlist;
+		if (snd_BUG_ON(num >= 4096))
+			return NULL;
+		nlist = kcalloc(num + 1, array->elem_size, GFP_KERNEL);
+		if (!nlist)
+			return NULL;
+		if (array->list) {
+			memcpy(nlist, array->list,
+			       array->elem_size * array->alloced);
+			kfree(array->list);
+		}
+		array->list = nlist;
+		array->alloced = num;
 	}
 	}
-	return 0;
+	return snd_array_elem(array, array->used++);
 }
 }
-#endif
-#endif
+EXPORT_SYMBOL_HDA(snd_array_new);
+
+/* free the given array elements */
+void snd_array_free(struct snd_array *array)
+{
+	kfree(array->list);
+	array->used = 0;
+	array->alloced = 0;
+	array->list = NULL;
+}
+EXPORT_SYMBOL_HDA(snd_array_free);
+
+/*
+ * used by hda_proc.c and hda_eld.c
+ */
+void snd_print_pcm_rates(int pcm, char *buf, int buflen)
+{
+	static unsigned int rates[] = {
+		8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200,
+		96000, 176400, 192000, 384000
+	};
+	int i, j;
+
+	for (i = 0, j = 0; i < ARRAY_SIZE(rates); i++)
+		if (pcm & (1 << i))
+			j += snprintf(buf + j, buflen - j,  " %d", rates[i]);
+
+	buf[j] = '\0'; /* necessary when j == 0 */
+}
+EXPORT_SYMBOL_HDA(snd_print_pcm_rates);
+
+void snd_print_pcm_bits(int pcm, char *buf, int buflen)
+{
+	static unsigned int bits[] = { 8, 16, 20, 24, 32 };
+	int i, j;
+
+	for (i = 0, j = 0; i < ARRAY_SIZE(bits); i++)
+		if (pcm & (AC_SUPPCM_BITS_8 << i))
+			j += snprintf(buf + j, buflen - j,  " %d", bits[i]);
+
+	buf[j] = '\0'; /* necessary when j == 0 */
+}
+EXPORT_SYMBOL_HDA(snd_print_pcm_bits);
+
+MODULE_DESCRIPTION("HDA codec core");
+MODULE_LICENSE("GPL");

+ 92 - 15
sound/pci/hda/hda_codec.h

@@ -519,6 +519,36 @@ enum {
 /* max. codec address */
 /* max. codec address */
 #define HDA_MAX_CODEC_ADDRESS	0x0f
 #define HDA_MAX_CODEC_ADDRESS	0x0f
 
 
+/*
+ * generic arrays
+ */
+struct snd_array {
+	unsigned int used;
+	unsigned int alloced;
+	unsigned int elem_size;
+	unsigned int alloc_align;
+	void *list;
+};
+
+void *snd_array_new(struct snd_array *array);
+void snd_array_free(struct snd_array *array);
+static inline void snd_array_init(struct snd_array *array, unsigned int size,
+				  unsigned int align)
+{
+	array->elem_size = size;
+	array->alloc_align = align;
+}
+
+static inline void *snd_array_elem(struct snd_array *array, unsigned int idx)
+{
+	return array->list + idx * array->elem_size;
+}
+
+static inline unsigned int snd_array_index(struct snd_array *array, void *ptr)
+{
+	return (unsigned long)(ptr - array->list) / array->elem_size;
+}
+
 /*
 /*
  * Structures
  * Structures
  */
  */
@@ -536,15 +566,17 @@ typedef u16 hda_nid_t;
 /* bus operators */
 /* bus operators */
 struct hda_bus_ops {
 struct hda_bus_ops {
 	/* send a single command */
 	/* send a single command */
-	int (*command)(struct hda_codec *codec, hda_nid_t nid, int direct,
-		       unsigned int verb, unsigned int parm);
+	int (*command)(struct hda_bus *bus, unsigned int cmd);
 	/* get a response from the last command */
 	/* get a response from the last command */
-	unsigned int (*get_response)(struct hda_codec *codec);
+	unsigned int (*get_response)(struct hda_bus *bus);
 	/* free the private data */
 	/* free the private data */
 	void (*private_free)(struct hda_bus *);
 	void (*private_free)(struct hda_bus *);
+	/* attach a PCM stream */
+	int (*attach_pcm)(struct hda_bus *bus, struct hda_codec *codec,
+			  struct hda_pcm *pcm);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 	/* notify power-up/down from codec to controller */
 	/* notify power-up/down from codec to controller */
-	void (*pm_notify)(struct hda_codec *codec);
+	void (*pm_notify)(struct hda_bus *bus);
 #endif
 #endif
 };
 };
 
 
@@ -553,6 +585,7 @@ struct hda_bus_template {
 	void *private_data;
 	void *private_data;
 	struct pci_dev *pci;
 	struct pci_dev *pci;
 	const char *modelname;
 	const char *modelname;
+	int *power_save;
 	struct hda_bus_ops ops;
 	struct hda_bus_ops ops;
 };
 };
 
 
@@ -569,6 +602,7 @@ struct hda_bus {
 	void *private_data;
 	void *private_data;
 	struct pci_dev *pci;
 	struct pci_dev *pci;
 	const char *modelname;
 	const char *modelname;
+	int *power_save;
 	struct hda_bus_ops ops;
 	struct hda_bus_ops ops;
 
 
 	/* codec linked list */
 	/* codec linked list */
@@ -581,10 +615,12 @@ struct hda_bus {
 	/* unsolicited event queue */
 	/* unsolicited event queue */
 	struct hda_bus_unsolicited *unsol;
 	struct hda_bus_unsolicited *unsol;
 
 
-	struct snd_info_entry *proc;
+	/* assigned PCMs */
+	DECLARE_BITMAP(pcm_dev_bits, SNDRV_PCM_DEVICES);
 
 
 	/* misc op flags */
 	/* misc op flags */
 	unsigned int needs_damn_long_delay :1;
 	unsigned int needs_damn_long_delay :1;
+	unsigned int shutdown :1;	/* being unloaded */
 };
 };
 
 
 /*
 /*
@@ -604,6 +640,16 @@ struct hda_codec_preset {
 	int (*patch)(struct hda_codec *codec);
 	int (*patch)(struct hda_codec *codec);
 };
 };
 	
 	
+struct hda_codec_preset_list {
+	const struct hda_codec_preset *preset;
+	struct module *owner;
+	struct list_head list;
+};
+
+/* initial hook */
+int snd_hda_add_codec_preset(struct hda_codec_preset_list *preset);
+int snd_hda_delete_codec_preset(struct hda_codec_preset_list *preset);
+
 /* ops set by the preset patch */
 /* ops set by the preset patch */
 struct hda_codec_ops {
 struct hda_codec_ops {
 	int (*build_controls)(struct hda_codec *codec);
 	int (*build_controls)(struct hda_codec *codec);
@@ -635,10 +681,7 @@ struct hda_amp_info {
 
 
 struct hda_cache_rec {
 struct hda_cache_rec {
 	u16 hash[64];			/* hash table for index */
 	u16 hash[64];			/* hash table for index */
-	unsigned int num_entries;	/* number of assigned entries */
-	unsigned int size;		/* allocated size */
-	unsigned int record_size;	/* record size (including header) */
-	void *buffer;			/* hash table entries */
+	struct snd_array buf;		/* record entries */
 };
 };
 
 
 /* PCM callbacks */
 /* PCM callbacks */
@@ -680,7 +723,8 @@ struct hda_pcm {
 	char *name;
 	char *name;
 	struct hda_pcm_stream stream[2];
 	struct hda_pcm_stream stream[2];
 	unsigned int pcm_type;	/* HDA_PCM_TYPE_XXX */
 	unsigned int pcm_type;	/* HDA_PCM_TYPE_XXX */
-	int device;	/* assigned device number */
+	int device;		/* device number to assign */
+	struct snd_pcm *pcm;	/* assigned PCM instance */
 };
 };
 
 
 /* codec information */
 /* codec information */
@@ -699,6 +743,9 @@ struct hda_codec {
 
 
 	/* detected preset */
 	/* detected preset */
 	const struct hda_codec_preset *preset;
 	const struct hda_codec_preset *preset;
+	struct module *owner;
+	const char *name;	/* codec name */
+	const char *modelname;	/* model name for preset */
 
 
 	/* set by patch */
 	/* set by patch */
 	struct hda_codec_ops patch_ops;
 	struct hda_codec_ops patch_ops;
@@ -718,6 +765,8 @@ struct hda_codec {
 	hda_nid_t start_nid;
 	hda_nid_t start_nid;
 	u32 *wcaps;
 	u32 *wcaps;
 
 
+	struct snd_array mixers;	/* list of assigned mixer elements */
+
 	struct hda_cache_rec amp_cache;	/* cache for amp access */
 	struct hda_cache_rec amp_cache;	/* cache for amp access */
 	struct hda_cache_rec cmd_cache;	/* cache for other commands */
 	struct hda_cache_rec cmd_cache;	/* cache for other commands */
 
 
@@ -727,7 +776,11 @@ struct hda_codec {
 	unsigned int spdif_in_enable;	/* SPDIF input enable? */
 	unsigned int spdif_in_enable;	/* SPDIF input enable? */
 	hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
 	hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
 
 
+#ifdef CONFIG_SND_HDA_HWDEP
 	struct snd_hwdep *hwdep;	/* assigned hwdep device */
 	struct snd_hwdep *hwdep;	/* assigned hwdep device */
+	struct snd_array init_verbs;	/* additional init verbs */
+	struct snd_array hints;		/* additional hints */
+#endif
 
 
 	/* misc flags */
 	/* misc flags */
 	unsigned int spdif_status_reset :1; /* needs to toggle SPDIF for each
 	unsigned int spdif_status_reset :1; /* needs to toggle SPDIF for each
@@ -740,6 +793,10 @@ struct hda_codec {
 	int power_count;	/* current (global) power refcount */
 	int power_count;	/* current (global) power refcount */
 	struct delayed_work power_work; /* delayed task for powerdown */
 	struct delayed_work power_work; /* delayed task for powerdown */
 #endif
 #endif
+
+	/* codec-specific additional proc output */
+	void (*proc_widget_hook)(struct snd_info_buffer *buffer,
+				 struct hda_codec *codec, hda_nid_t nid);
 };
 };
 
 
 /* direction */
 /* direction */
@@ -754,7 +811,7 @@ 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,
-		      struct hda_codec **codecp);
+		      int do_init, struct hda_codec **codecp);
 
 
 /*
 /*
  * low level functions
  * low level functions
@@ -799,11 +856,13 @@ void snd_hda_codec_resume_cache(struct hda_codec *codec);
  * Mixer
  * Mixer
  */
  */
 int snd_hda_build_controls(struct hda_bus *bus);
 int snd_hda_build_controls(struct hda_bus *bus);
+int snd_hda_codec_build_controls(struct hda_codec *codec);
 
 
 /*
 /*
  * PCM
  * PCM
  */
  */
 int snd_hda_build_pcms(struct hda_bus *bus);
 int snd_hda_build_pcms(struct hda_bus *bus);
+int snd_hda_codec_build_pcms(struct hda_codec *codec);
 void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
 void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid,
 				u32 stream_tag,
 				u32 stream_tag,
 				int channel_id, int format);
 				int channel_id, int format);
@@ -812,8 +871,6 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
 					unsigned int channels,
 					unsigned int channels,
 					unsigned int format,
 					unsigned int format,
 					unsigned int maxbps);
 					unsigned int maxbps);
-int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
-				u32 *ratesp, u64 *formatsp, unsigned int *bpsp);
 int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
 int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
 				unsigned int format);
 				unsigned int format);
 
 
@@ -830,6 +887,13 @@ int snd_hda_suspend(struct hda_bus *bus, pm_message_t state);
 int snd_hda_resume(struct hda_bus *bus);
 int snd_hda_resume(struct hda_bus *bus);
 #endif
 #endif
 
 
+/*
+ * get widget information
+ */
+const char *snd_hda_get_jack_connectivity(u32 cfg);
+const char *snd_hda_get_jack_type(u32 cfg);
+const char *snd_hda_get_jack_location(u32 cfg);
+
 /*
 /*
  * power saving
  * power saving
  */
  */
@@ -837,12 +901,25 @@ int snd_hda_resume(struct hda_bus *bus);
 void snd_hda_power_up(struct hda_codec *codec);
 void snd_hda_power_up(struct hda_codec *codec);
 void snd_hda_power_down(struct hda_codec *codec);
 void snd_hda_power_down(struct hda_codec *codec);
 #define snd_hda_codec_needs_resume(codec) codec->power_count
 #define snd_hda_codec_needs_resume(codec) codec->power_count
-int snd_hda_codecs_inuse(struct hda_bus *bus);
 #else
 #else
 static inline void snd_hda_power_up(struct hda_codec *codec) {}
 static inline void snd_hda_power_up(struct hda_codec *codec) {}
 static inline void snd_hda_power_down(struct hda_codec *codec) {}
 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
-#define snd_hda_codecs_inuse(bus) 1
+#endif
+
+/*
+ * Codec modularization
+ */
+
+/* Export symbols only for communication with codec drivers;
+ * When built in kernel, all HD-audio drivers are supposed to be statically
+ * linked to the kernel.  Thus, the symbols don't have to (or shouldn't) be
+ * exported unless it's built as a module.
+ */
+#ifdef MODULE
+#define EXPORT_SYMBOL_HDA(sym) EXPORT_SYMBOL_GPL(sym)
+#else
+#define EXPORT_SYMBOL_HDA(sym)
 #endif
 #endif
 
 
 #endif /* __SOUND_HDA_CODEC_H */
 #endif /* __SOUND_HDA_CODEC_H */

+ 590 - 0
sound/pci/hda/hda_eld.c

@@ -0,0 +1,590 @@
+/*
+ * Generic routines and proc interface for ELD(EDID Like Data) information
+ *
+ * Copyright(c) 2008 Intel Corporation.
+ *
+ * Authors:
+ * 		Wu Fengguang <wfg@linux.intel.com>
+ *
+ *  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 <sound/core.h>
+#include <asm/unaligned.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+enum eld_versions {
+	ELD_VER_CEA_861D	= 2,
+	ELD_VER_PARTIAL		= 31,
+};
+
+enum cea_edid_versions {
+	CEA_EDID_VER_NONE	= 0,
+	CEA_EDID_VER_CEA861	= 1,
+	CEA_EDID_VER_CEA861A	= 2,
+	CEA_EDID_VER_CEA861BCD	= 3,
+	CEA_EDID_VER_RESERVED	= 4,
+};
+
+static char *cea_speaker_allocation_names[] = {
+	/*  0 */ "FL/FR",
+	/*  1 */ "LFE",
+	/*  2 */ "FC",
+	/*  3 */ "RL/RR",
+	/*  4 */ "RC",
+	/*  5 */ "FLC/FRC",
+	/*  6 */ "RLC/RRC",
+	/*  7 */ "FLW/FRW",
+	/*  8 */ "FLH/FRH",
+	/*  9 */ "TC",
+	/* 10 */ "FCH",
+};
+
+static char *eld_connection_type_names[4] = {
+	"HDMI",
+	"DisplayPort",
+	"2-reserved",
+	"3-reserved"
+};
+
+enum cea_audio_coding_types {
+	AUDIO_CODING_TYPE_REF_STREAM_HEADER	=  0,
+	AUDIO_CODING_TYPE_LPCM			=  1,
+	AUDIO_CODING_TYPE_AC3			=  2,
+	AUDIO_CODING_TYPE_MPEG1			=  3,
+	AUDIO_CODING_TYPE_MP3			=  4,
+	AUDIO_CODING_TYPE_MPEG2			=  5,
+	AUDIO_CODING_TYPE_AACLC			=  6,
+	AUDIO_CODING_TYPE_DTS			=  7,
+	AUDIO_CODING_TYPE_ATRAC			=  8,
+	AUDIO_CODING_TYPE_SACD			=  9,
+	AUDIO_CODING_TYPE_EAC3			= 10,
+	AUDIO_CODING_TYPE_DTS_HD		= 11,
+	AUDIO_CODING_TYPE_MLP			= 12,
+	AUDIO_CODING_TYPE_DST			= 13,
+	AUDIO_CODING_TYPE_WMAPRO		= 14,
+	AUDIO_CODING_TYPE_REF_CXT		= 15,
+	/* also include valid xtypes below */
+	AUDIO_CODING_TYPE_HE_AAC		= 15,
+	AUDIO_CODING_TYPE_HE_AAC2		= 16,
+	AUDIO_CODING_TYPE_MPEG_SURROUND		= 17,
+};
+
+enum cea_audio_coding_xtypes {
+	AUDIO_CODING_XTYPE_HE_REF_CT		= 0,
+	AUDIO_CODING_XTYPE_HE_AAC		= 1,
+	AUDIO_CODING_XTYPE_HE_AAC2		= 2,
+	AUDIO_CODING_XTYPE_MPEG_SURROUND	= 3,
+	AUDIO_CODING_XTYPE_FIRST_RESERVED	= 4,
+};
+
+static char *cea_audio_coding_type_names[] = {
+	/*  0 */ "undefined",
+	/*  1 */ "LPCM",
+	/*  2 */ "AC-3",
+	/*  3 */ "MPEG1",
+	/*  4 */ "MP3",
+	/*  5 */ "MPEG2",
+	/*  6 */ "AAC-LC",
+	/*  7 */ "DTS",
+	/*  8 */ "ATRAC",
+	/*  9 */ "DSD (One Bit Audio)",
+	/* 10 */ "E-AC-3/DD+ (Dolby Digital Plus)",
+	/* 11 */ "DTS-HD",
+	/* 12 */ "MLP (Dolby TrueHD)",
+	/* 13 */ "DST",
+	/* 14 */ "WMAPro",
+	/* 15 */ "HE-AAC",
+	/* 16 */ "HE-AACv2",
+	/* 17 */ "MPEG Surround",
+};
+
+/*
+ * The following two lists are shared between
+ * 	- HDMI audio InfoFrame (source to sink)
+ * 	- CEA E-EDID Extension (sink to source)
+ */
+
+/*
+ * SS1:SS0 index => sample size
+ */
+static int cea_sample_sizes[4] = {
+	0,	 		/* 0: Refer to Stream Header */
+	AC_SUPPCM_BITS_16,	/* 1: 16 bits */
+	AC_SUPPCM_BITS_20,	/* 2: 20 bits */
+	AC_SUPPCM_BITS_24,	/* 3: 24 bits */
+};
+
+/*
+ * SF2:SF1:SF0 index => sampling frequency
+ */
+static int cea_sampling_frequencies[8] = {
+	0,			/* 0: Refer to Stream Header */
+	SNDRV_PCM_RATE_32000,	/* 1:  32000Hz */
+	SNDRV_PCM_RATE_44100,	/* 2:  44100Hz */
+	SNDRV_PCM_RATE_48000,	/* 3:  48000Hz */
+	SNDRV_PCM_RATE_88200,	/* 4:  88200Hz */
+	SNDRV_PCM_RATE_96000,	/* 5:  96000Hz */
+	SNDRV_PCM_RATE_176400,	/* 6: 176400Hz */
+	SNDRV_PCM_RATE_192000,	/* 7: 192000Hz */
+};
+
+static unsigned char hdmi_get_eld_byte(struct hda_codec *codec, hda_nid_t nid,
+					int byte_index)
+{
+	unsigned int val;
+
+	val = snd_hda_codec_read(codec, nid, 0,
+					AC_VERB_GET_HDMI_ELDD, byte_index);
+
+#ifdef BE_PARANOID
+	printk(KERN_INFO "HDMI: ELD data byte %d: 0x%x\n", byte_index, val);
+#endif
+
+	if ((val & AC_ELDD_ELD_VALID) == 0) {
+		snd_printd(KERN_INFO "HDMI: invalid ELD data byte %d\n",
+								byte_index);
+		val = 0;
+	}
+
+	return val & AC_ELDD_ELD_DATA;
+}
+
+#define GRAB_BITS(buf, byte, lowbit, bits) 		\
+({							\
+	BUILD_BUG_ON(lowbit > 7);			\
+	BUILD_BUG_ON(bits > 8);				\
+	BUILD_BUG_ON(bits <= 0);			\
+							\
+	(buf[byte] >> (lowbit)) & ((1 << (bits)) - 1);	\
+})
+
+static void hdmi_update_short_audio_desc(struct cea_sad *a,
+					 const unsigned char *buf)
+{
+	int i;
+	int val;
+
+	val = GRAB_BITS(buf, 1, 0, 7);
+	a->rates = 0;
+	for (i = 0; i < 7; i++)
+		if (val & (1 << i))
+			a->rates |= cea_sampling_frequencies[i + 1];
+
+	a->channels = GRAB_BITS(buf, 0, 0, 3);
+	a->channels++;
+
+	a->format = GRAB_BITS(buf, 0, 3, 4);
+	switch (a->format) {
+	case AUDIO_CODING_TYPE_REF_STREAM_HEADER:
+		snd_printd(KERN_INFO
+				"HDMI: audio coding type 0 not expected\n");
+		break;
+
+	case AUDIO_CODING_TYPE_LPCM:
+		val = GRAB_BITS(buf, 2, 0, 3);
+		a->sample_bits = 0;
+		for (i = 0; i < 3; i++)
+			if (val & (1 << i))
+				a->sample_bits |= cea_sample_sizes[i + 1];
+		break;
+
+	case AUDIO_CODING_TYPE_AC3:
+	case AUDIO_CODING_TYPE_MPEG1:
+	case AUDIO_CODING_TYPE_MP3:
+	case AUDIO_CODING_TYPE_MPEG2:
+	case AUDIO_CODING_TYPE_AACLC:
+	case AUDIO_CODING_TYPE_DTS:
+	case AUDIO_CODING_TYPE_ATRAC:
+		a->max_bitrate = GRAB_BITS(buf, 2, 0, 8);
+		a->max_bitrate *= 8000;
+		break;
+
+	case AUDIO_CODING_TYPE_SACD:
+		break;
+
+	case AUDIO_CODING_TYPE_EAC3:
+		break;
+
+	case AUDIO_CODING_TYPE_DTS_HD:
+		break;
+
+	case AUDIO_CODING_TYPE_MLP:
+		break;
+
+	case AUDIO_CODING_TYPE_DST:
+		break;
+
+	case AUDIO_CODING_TYPE_WMAPRO:
+		a->profile = GRAB_BITS(buf, 2, 0, 3);
+		break;
+
+	case AUDIO_CODING_TYPE_REF_CXT:
+		a->format = GRAB_BITS(buf, 2, 3, 5);
+		if (a->format == AUDIO_CODING_XTYPE_HE_REF_CT ||
+		    a->format >= AUDIO_CODING_XTYPE_FIRST_RESERVED) {
+			snd_printd(KERN_INFO
+				"HDMI: audio coding xtype %d not expected\n",
+				a->format);
+			a->format = 0;
+		} else
+			a->format += AUDIO_CODING_TYPE_HE_AAC -
+				     AUDIO_CODING_XTYPE_HE_AAC;
+		break;
+	}
+}
+
+/*
+ * Be careful, ELD buf could be totally rubbish!
+ */
+static int hdmi_update_eld(struct hdmi_eld *e,
+			   const unsigned char *buf, int size)
+{
+	int mnl;
+	int i;
+
+	e->eld_ver = GRAB_BITS(buf, 0, 3, 5);
+	if (e->eld_ver != ELD_VER_CEA_861D &&
+	    e->eld_ver != ELD_VER_PARTIAL) {
+		snd_printd(KERN_INFO "HDMI: Unknown ELD version %d\n",
+								e->eld_ver);
+		goto out_fail;
+	}
+
+	e->eld_size = size;
+	e->baseline_len = GRAB_BITS(buf, 2, 0, 8);
+	mnl		= GRAB_BITS(buf, 4, 0, 5);
+	e->cea_edid_ver	= GRAB_BITS(buf, 4, 5, 3);
+
+	e->support_hdcp	= GRAB_BITS(buf, 5, 0, 1);
+	e->support_ai	= GRAB_BITS(buf, 5, 1, 1);
+	e->conn_type	= GRAB_BITS(buf, 5, 2, 2);
+	e->sad_count	= GRAB_BITS(buf, 5, 4, 4);
+
+	e->aud_synch_delay = GRAB_BITS(buf, 6, 0, 8) * 2;
+	e->spk_alloc	= GRAB_BITS(buf, 7, 0, 7);
+
+	e->port_id	  = get_unaligned_le64(buf + 8);
+
+	/* not specified, but the spec's tendency is little endian */
+	e->manufacture_id = get_unaligned_le16(buf + 16);
+	e->product_id	  = get_unaligned_le16(buf + 18);
+
+	if (mnl > ELD_MAX_MNL) {
+		snd_printd(KERN_INFO "HDMI: MNL is reserved value %d\n", mnl);
+		goto out_fail;
+	} else if (ELD_FIXED_BYTES + mnl > size) {
+		snd_printd(KERN_INFO "HDMI: out of range MNL %d\n", mnl);
+		goto out_fail;
+	} else
+		strlcpy(e->monitor_name, buf + ELD_FIXED_BYTES, mnl);
+
+	for (i = 0; i < e->sad_count; i++) {
+		if (ELD_FIXED_BYTES + mnl + 3 * (i + 1) > size) {
+			snd_printd(KERN_INFO "HDMI: out of range SAD %d\n", i);
+			goto out_fail;
+		}
+		hdmi_update_short_audio_desc(e->sad + i,
+					buf + ELD_FIXED_BYTES + mnl + 3 * i);
+	}
+
+	return 0;
+
+out_fail:
+	e->eld_ver = 0;
+	return -EINVAL;
+}
+
+static int hdmi_present_sense(struct hda_codec *codec, hda_nid_t nid)
+{
+	return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0);
+}
+
+static int hdmi_eld_valid(struct hda_codec *codec, hda_nid_t nid)
+{
+	int eldv;
+	int present;
+
+	present = hdmi_present_sense(codec, nid);
+	eldv    = (present & AC_PINSENSE_ELDV);
+	present = (present & AC_PINSENSE_PRESENCE);
+
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+	printk(KERN_INFO "HDMI: sink_present = %d, eld_valid = %d\n",
+			!!present, !!eldv);
+#endif
+
+	return eldv && present;
+}
+
+int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid)
+{
+	return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE,
+						 AC_DIPSIZE_ELD_BUF);
+}
+
+int snd_hdmi_get_eld(struct hdmi_eld *eld,
+		     struct hda_codec *codec, hda_nid_t nid)
+{
+	int i;
+	int ret;
+	int size;
+	unsigned char *buf;
+
+	if (!hdmi_eld_valid(codec, nid))
+		return -ENOENT;
+
+	size = snd_hdmi_get_eld_size(codec, nid);
+	if (size == 0) {
+		/* wfg: workaround for ASUS P5E-VM HDMI board */
+		snd_printd(KERN_INFO "HDMI: ELD buf size is 0, force 128\n");
+		size = 128;
+	}
+	if (size < ELD_FIXED_BYTES || size > PAGE_SIZE) {
+		snd_printd(KERN_INFO "HDMI: invalid ELD buf size %d\n", size);
+		return -ERANGE;
+	}
+
+	buf = kmalloc(size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	for (i = 0; i < size; i++)
+		buf[i] = hdmi_get_eld_byte(codec, nid, i);
+
+	ret = hdmi_update_eld(eld, buf, size);
+
+	kfree(buf);
+	return ret;
+}
+
+static void hdmi_show_short_audio_desc(struct cea_sad *a)
+{
+	char buf[SND_PRINT_RATES_ADVISED_BUFSIZE];
+	char buf2[8 + SND_PRINT_BITS_ADVISED_BUFSIZE] = ", bits =";
+
+	if (!a->format)
+		return;
+
+	snd_print_pcm_rates(a->rates, buf, sizeof(buf));
+
+	if (a->format == AUDIO_CODING_TYPE_LPCM)
+		snd_print_pcm_bits(a->sample_bits, buf2 + 8, sizeof(buf2 - 8));
+	else if (a->max_bitrate)
+		snprintf(buf2, sizeof(buf2),
+				", max bitrate = %d", a->max_bitrate);
+	else
+		buf2[0] = '\0';
+
+	printk(KERN_INFO "HDMI: supports coding type %s:"
+			" channels = %d, rates =%s%s\n",
+			cea_audio_coding_type_names[a->format],
+			a->channels,
+			buf,
+			buf2);
+}
+
+void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen)
+{
+	int i, j;
+
+	for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) {
+		if (spk_alloc & (1 << i))
+			j += snprintf(buf + j, buflen - j,  " %s",
+					cea_speaker_allocation_names[i]);
+	}
+	buf[j] = '\0';	/* necessary when j == 0 */
+}
+
+void snd_hdmi_show_eld(struct hdmi_eld *e)
+{
+	int i;
+
+	printk(KERN_INFO "HDMI: detected monitor %s at connection type %s\n",
+			e->monitor_name,
+			eld_connection_type_names[e->conn_type]);
+
+	if (e->spk_alloc) {
+		char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
+		snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
+		printk(KERN_INFO "HDMI: available speakers:%s\n", buf);
+	}
+
+	for (i = 0; i < e->sad_count; i++)
+		hdmi_show_short_audio_desc(e->sad + i);
+}
+
+#ifdef CONFIG_PROC_FS
+
+static void hdmi_print_sad_info(int i, struct cea_sad *a,
+				struct snd_info_buffer *buffer)
+{
+	char buf[SND_PRINT_RATES_ADVISED_BUFSIZE];
+
+	snd_iprintf(buffer, "sad%d_coding_type\t[0x%x] %s\n",
+			i, a->format, cea_audio_coding_type_names[a->format]);
+	snd_iprintf(buffer, "sad%d_channels\t\t%d\n", i, a->channels);
+
+	snd_print_pcm_rates(a->rates, buf, sizeof(buf));
+	snd_iprintf(buffer, "sad%d_rates\t\t[0x%x]%s\n", i, a->rates, buf);
+
+	if (a->format == AUDIO_CODING_TYPE_LPCM) {
+		snd_print_pcm_bits(a->sample_bits, buf, sizeof(buf));
+		snd_iprintf(buffer, "sad%d_bits\t\t[0x%x]%s\n",
+							i, a->sample_bits, buf);
+	}
+
+	if (a->max_bitrate)
+		snd_iprintf(buffer, "sad%d_max_bitrate\t%d\n",
+							i, a->max_bitrate);
+
+	if (a->profile)
+		snd_iprintf(buffer, "sad%d_profile\t\t%d\n", i, a->profile);
+}
+
+static void hdmi_print_eld_info(struct snd_info_entry *entry,
+				struct snd_info_buffer *buffer)
+{
+	struct hdmi_eld *e = entry->private_data;
+	char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
+	int i;
+	static char *eld_versoin_names[32] = {
+		"reserved",
+		"reserved",
+		"CEA-861D or below",
+		[3 ... 30] = "reserved",
+		[31] = "partial"
+	};
+	static char *cea_edid_version_names[8] = {
+		"no CEA EDID Timing Extension block present",
+		"CEA-861",
+		"CEA-861-A",
+		"CEA-861-B, C or D",
+		[4 ... 7] = "reserved"
+	};
+
+	snd_iprintf(buffer, "monitor_name\t\t%s\n", e->monitor_name);
+	snd_iprintf(buffer, "connection_type\t\t%s\n",
+				eld_connection_type_names[e->conn_type]);
+	snd_iprintf(buffer, "eld_version\t\t[0x%x] %s\n", e->eld_ver,
+					eld_versoin_names[e->eld_ver]);
+	snd_iprintf(buffer, "edid_version\t\t[0x%x] %s\n", e->cea_edid_ver,
+				cea_edid_version_names[e->cea_edid_ver]);
+	snd_iprintf(buffer, "manufacture_id\t\t0x%x\n", e->manufacture_id);
+	snd_iprintf(buffer, "product_id\t\t0x%x\n", e->product_id);
+	snd_iprintf(buffer, "port_id\t\t\t0x%llx\n", (long long)e->port_id);
+	snd_iprintf(buffer, "support_hdcp\t\t%d\n", e->support_hdcp);
+	snd_iprintf(buffer, "support_ai\t\t%d\n", e->support_ai);
+	snd_iprintf(buffer, "audio_sync_delay\t%d\n", e->aud_synch_delay);
+
+	snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
+	snd_iprintf(buffer, "speakers\t\t[0x%x]%s\n", e->spk_alloc, buf);
+
+	snd_iprintf(buffer, "sad_count\t\t%d\n", e->sad_count);
+
+	for (i = 0; i < e->sad_count; i++)
+		hdmi_print_sad_info(i, e->sad + i, buffer);
+}
+
+static void hdmi_write_eld_info(struct snd_info_entry *entry,
+				struct snd_info_buffer *buffer)
+{
+	struct hdmi_eld *e = entry->private_data;
+	char line[64];
+	char name[64];
+	char *sname;
+	long long val;
+	int n;
+
+	while (!snd_info_get_line(buffer, line, sizeof(line))) {
+		if (sscanf(line, "%s %llx", name, &val) != 2)
+			continue;
+		/*
+		 * We don't allow modification to these fields:
+		 * 	monitor_name manufacture_id product_id
+		 * 	eld_version edid_version
+		 */
+		if (!strcmp(name, "connection_type"))
+			e->conn_type = val;
+		else if (!strcmp(name, "port_id"))
+			e->port_id = val;
+		else if (!strcmp(name, "support_hdcp"))
+			e->support_hdcp = val;
+		else if (!strcmp(name, "support_ai"))
+			e->support_ai = val;
+		else if (!strcmp(name, "audio_sync_delay"))
+			e->aud_synch_delay = val;
+		else if (!strcmp(name, "speakers"))
+			e->spk_alloc = val;
+		else if (!strcmp(name, "sad_count"))
+			e->sad_count = val;
+		else if (!strncmp(name, "sad", 3)) {
+			sname = name + 4;
+			n = name[3] - '0';
+			if (name[4] >= '0' && name[4] <= '9') {
+				sname++;
+				n = 10 * n + name[4] - '0';
+			}
+			if (n < 0 || n > 31) /* double the CEA limit */
+				continue;
+			if (!strcmp(sname, "_coding_type"))
+				e->sad[n].format = val;
+			else if (!strcmp(sname, "_channels"))
+				e->sad[n].channels = val;
+			else if (!strcmp(sname, "_rates"))
+				e->sad[n].rates = val;
+			else if (!strcmp(sname, "_bits"))
+				e->sad[n].sample_bits = val;
+			else if (!strcmp(sname, "_max_bitrate"))
+				e->sad[n].max_bitrate = val;
+			else if (!strcmp(sname, "_profile"))
+				e->sad[n].profile = val;
+			if (n >= e->sad_count)
+				e->sad_count = n + 1;
+		}
+	}
+}
+
+
+int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld)
+{
+	char name[32];
+	struct snd_info_entry *entry;
+	int err;
+
+	snprintf(name, sizeof(name), "eld#%d", codec->addr);
+	err = snd_card_proc_new(codec->bus->card, name, &entry);
+	if (err < 0)
+		return err;
+
+	snd_info_set_text_ops(entry, eld, hdmi_print_eld_info);
+	entry->c.text.write = hdmi_write_eld_info;
+	entry->mode |= S_IWUSR;
+	eld->proc_entry = entry;
+
+	return 0;
+}
+
+void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld)
+{
+	if (!codec->bus->shutdown && eld->proc_entry) {
+		snd_device_free(codec->bus->card, eld->proc_entry);
+		eld->proc_entry = NULL;
+	}
+}
+
+#endif /* CONFIG_PROC_FS */

+ 13 - 8
sound/pci/hda/hda_generic.c

@@ -723,7 +723,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
 		if (is_loopback)
 		if (is_loopback)
 			add_input_loopback(codec, node->nid, HDA_INPUT, index);
 			add_input_loopback(codec, node->nid, HDA_INPUT, index);
 		snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
 		snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
-		if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
+		err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+		if (err < 0)
 			return err;
 			return err;
 		created = 1;
 		created = 1;
 	} else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
 	} else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
@@ -732,7 +733,8 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
 		if (is_loopback)
 		if (is_loopback)
 			add_input_loopback(codec, node->nid, HDA_OUTPUT, 0);
 			add_input_loopback(codec, node->nid, HDA_OUTPUT, 0);
 		snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
 		snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
-		if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
+		err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+		if (err < 0)
 			return err;
 			return err;
 		created = 1;
 		created = 1;
 	}
 	}
@@ -745,14 +747,16 @@ static int create_mixer(struct hda_codec *codec, struct hda_gnode *node,
 	    (node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) {
 	    (node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) {
 		knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, index, HDA_INPUT);
 		knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, index, HDA_INPUT);
 		snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
 		snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index);
-		if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
+		err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+		if (err < 0)
 			return err;
 			return err;
 		created = 1;
 		created = 1;
 	} else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
 	} else if ((node->wid_caps & AC_WCAP_OUT_AMP) &&
 		   (node->amp_out_caps & AC_AMPCAP_NUM_STEPS)) {
 		   (node->amp_out_caps & AC_AMPCAP_NUM_STEPS)) {
 		knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, 0, HDA_OUTPUT);
 		knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, 0, HDA_OUTPUT);
 		snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
 		snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid);
-		if ((err = snd_ctl_add(codec->bus->card, snd_ctl_new1(&knew, codec))) < 0)
+		err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+		if (err < 0)
 			return err;
 			return err;
 		created = 1;
 		created = 1;
 	}
 	}
@@ -849,8 +853,8 @@ static int build_input_controls(struct hda_codec *codec)
 	}
 	}
 
 
 	/* create input MUX if multiple sources are available */
 	/* create input MUX if multiple sources are available */
-	if ((err = snd_ctl_add(codec->bus->card,
-			       snd_ctl_new1(&cap_sel, codec))) < 0)
+	err = snd_hda_ctl_add(codec, snd_ctl_new1(&cap_sel, codec));
+	if (err < 0)
 		return err;
 		return err;
 
 
 	/* no volume control? */
 	/* no volume control? */
@@ -867,8 +871,8 @@ static int build_input_controls(struct hda_codec *codec)
 			HDA_CODEC_VOLUME(name, adc_node->nid,
 			HDA_CODEC_VOLUME(name, adc_node->nid,
 					 spec->input_mux.items[i].index,
 					 spec->input_mux.items[i].index,
 					 HDA_INPUT);
 					 HDA_INPUT);
-		if ((err = snd_ctl_add(codec->bus->card,
-				       snd_ctl_new1(&knew, codec))) < 0)
+		err = snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+		if (err < 0)
 			return err;
 			return err;
 	}
 	}
 
 
@@ -1097,3 +1101,4 @@ int snd_hda_parse_generic_codec(struct hda_codec *codec)
 	snd_hda_generic_free(codec);
 	snd_hda_generic_free(codec);
 	return err;
 	return err;
 }
 }
+EXPORT_SYMBOL(snd_hda_parse_generic_codec);

+ 233 - 1
sound/pci/hda/hda_hwdep.c

@@ -23,10 +23,12 @@
 #include <linux/pci.h>
 #include <linux/pci.h>
 #include <linux/compat.h>
 #include <linux/compat.h>
 #include <linux/mutex.h>
 #include <linux/mutex.h>
+#include <linux/ctype.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"
 #include <sound/hda_hwdep.h>
 #include <sound/hda_hwdep.h>
+#include <sound/minors.h>
 
 
 /*
 /*
  * write/read an out-of-bound verb
  * write/read an out-of-bound verb
@@ -95,7 +97,26 @@ static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file)
 	return 0;
 	return 0;
 }
 }
 
 
-int __devinit snd_hda_create_hwdep(struct hda_codec *codec)
+static void clear_hwdep_elements(struct hda_codec *codec)
+{
+	char **head;
+	int i;
+
+	/* clear init verbs */
+	snd_array_free(&codec->init_verbs);
+	/* clear hints */
+	head = codec->hints.list;
+	for (i = 0; i < codec->hints.used; i++, head++)
+		kfree(*head);
+	snd_array_free(&codec->hints);
+}
+
+static void hwdep_free(struct snd_hwdep *hwdep)
+{
+	clear_hwdep_elements(hwdep->private_data);
+}
+
+int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec)
 {
 {
 	char hwname[16];
 	char hwname[16];
 	struct snd_hwdep *hwdep;
 	struct snd_hwdep *hwdep;
@@ -109,6 +130,7 @@ int __devinit snd_hda_create_hwdep(struct hda_codec *codec)
 	sprintf(hwdep->name, "HDA Codec %d", codec->addr);
 	sprintf(hwdep->name, "HDA Codec %d", codec->addr);
 	hwdep->iface = SNDRV_HWDEP_IFACE_HDA;
 	hwdep->iface = SNDRV_HWDEP_IFACE_HDA;
 	hwdep->private_data = codec;
 	hwdep->private_data = codec;
+	hwdep->private_free = hwdep_free;
 	hwdep->exclusive = 1;
 	hwdep->exclusive = 1;
 
 
 	hwdep->ops.open = hda_hwdep_open;
 	hwdep->ops.open = hda_hwdep_open;
@@ -117,5 +139,215 @@ int __devinit snd_hda_create_hwdep(struct hda_codec *codec)
 	hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat;
 	hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat;
 #endif
 #endif
 
 
+	snd_array_init(&codec->init_verbs, sizeof(struct hda_verb), 32);
+	snd_array_init(&codec->hints, sizeof(char *), 32);
+
 	return 0;
 	return 0;
 }
 }
+
+#ifdef CONFIG_SND_HDA_RECONFIG
+
+/*
+ * sysfs interface
+ */
+
+static int clear_codec(struct hda_codec *codec)
+{
+	snd_hda_codec_reset(codec);
+	clear_hwdep_elements(codec);
+	return 0;
+}
+
+static int reconfig_codec(struct hda_codec *codec)
+{
+	int err;
+
+	snd_printk(KERN_INFO "hda-codec: reconfiguring\n");
+	snd_hda_codec_reset(codec);
+	err = snd_hda_codec_configure(codec);
+	if (err < 0)
+		return err;
+	/* rebuild PCMs */
+	err = snd_hda_codec_build_pcms(codec);
+	if (err < 0)
+		return err;
+	/* rebuild mixers */
+	err = snd_hda_codec_build_controls(codec);
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+/*
+ * allocate a string at most len chars, and remove the trailing EOL
+ */
+static char *kstrndup_noeol(const char *src, size_t len)
+{
+	char *s = kstrndup(src, len, GFP_KERNEL);
+	char *p;
+	if (!s)
+		return NULL;
+	p = strchr(s, '\n');
+	if (p)
+		*p = 0;
+	return s;
+}
+
+#define CODEC_INFO_SHOW(type)					\
+static ssize_t type##_show(struct device *dev,			\
+			   struct device_attribute *attr,	\
+			   char *buf)				\
+{								\
+	struct snd_hwdep *hwdep = dev_get_drvdata(dev);		\
+	struct hda_codec *codec = hwdep->private_data;		\
+	return sprintf(buf, "0x%x\n", codec->type);		\
+}
+
+#define CODEC_INFO_STR_SHOW(type)				\
+static ssize_t type##_show(struct device *dev,			\
+			     struct device_attribute *attr,	\
+					char *buf)		\
+{								\
+	struct snd_hwdep *hwdep = dev_get_drvdata(dev);		\
+	struct hda_codec *codec = hwdep->private_data;		\
+	return sprintf(buf, "%s\n",				\
+		       codec->type ? codec->type : "");		\
+}
+
+CODEC_INFO_SHOW(vendor_id);
+CODEC_INFO_SHOW(subsystem_id);
+CODEC_INFO_SHOW(revision_id);
+CODEC_INFO_SHOW(afg);
+CODEC_INFO_SHOW(mfg);
+CODEC_INFO_STR_SHOW(name);
+CODEC_INFO_STR_SHOW(modelname);
+
+#define CODEC_INFO_STORE(type)					\
+static ssize_t type##_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;		\
+	char *after;						\
+	codec->type = simple_strtoul(buf, &after, 0);		\
+	return count;						\
+}
+
+#define CODEC_INFO_STR_STORE(type)				\
+static ssize_t type##_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;		\
+	char *s = kstrndup_noeol(buf, 64);			\
+	if (!s)							\
+		return -ENOMEM;					\
+	kfree(codec->type);					\
+	codec->type = s;					\
+	return count;						\
+}
+
+CODEC_INFO_STORE(vendor_id);
+CODEC_INFO_STORE(subsystem_id);
+CODEC_INFO_STORE(revision_id);
+CODEC_INFO_STR_STORE(name);
+CODEC_INFO_STR_STORE(modelname);
+
+#define CODEC_ACTION_STORE(type)				\
+static ssize_t type##_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 = 0;						\
+	if (*buf)						\
+		err = type##_codec(codec);			\
+	return err < 0 ? err : count;				\
+}
+
+CODEC_ACTION_STORE(reconfig);
+CODEC_ACTION_STORE(clear);
+
+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;
+	char *p;
+	struct hda_verb verb, *v;
+
+	verb.nid = simple_strtoul(buf, &p, 0);
+	verb.verb = simple_strtoul(p, &p, 0);
+	verb.param = simple_strtoul(p, &p, 0);
+	if (!verb.nid || !verb.verb || !verb.param)
+		return -EINVAL;
+	v = snd_array_new(&codec->init_verbs);
+	if (!v)
+		return -ENOMEM;
+	*v = verb;
+	return count;
+}
+
+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;
+	char *p;
+	char **hint;
+
+	if (!*buf || isspace(*buf) || *buf == '#' || *buf == '\n')
+		return count;
+	p = kstrndup_noeol(buf, 1024);
+	if (!p)
+		return -ENOMEM;
+	hint = snd_array_new(&codec->hints);
+	if (!hint) {
+		kfree(p);
+		return -ENOMEM;
+	}
+	*hint = p;
+	return count;
+}
+
+#define CODEC_ATTR_RW(type) \
+	__ATTR(type, 0644, type##_show, type##_store)
+#define CODEC_ATTR_RO(type) \
+	__ATTR_RO(type)
+#define CODEC_ATTR_WO(type) \
+	__ATTR(type, 0200, NULL, type##_store)
+
+static struct device_attribute codec_attrs[] = {
+	CODEC_ATTR_RW(vendor_id),
+	CODEC_ATTR_RW(subsystem_id),
+	CODEC_ATTR_RW(revision_id),
+	CODEC_ATTR_RO(afg),
+	CODEC_ATTR_RO(mfg),
+	CODEC_ATTR_RW(name),
+	CODEC_ATTR_RW(modelname),
+	CODEC_ATTR_WO(init_verbs),
+	CODEC_ATTR_WO(hints),
+	CODEC_ATTR_WO(reconfig),
+	CODEC_ATTR_WO(clear),
+};
+
+/*
+ * create sysfs files on hwdep directory
+ */
+int snd_hda_hwdep_add_sysfs(struct hda_codec *codec)
+{
+	struct snd_hwdep *hwdep = codec->hwdep;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(codec_attrs); i++)
+		snd_add_device_sysfs_file(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card,
+					  hwdep->device, &codec_attrs[i]);
+	return 0;
+}
+
+#endif /* CONFIG_SND_HDA_RECONFIG */

+ 176 - 172
sound/pci/hda/hda_intel.c

@@ -58,6 +58,7 @@ static char *model[SNDRV_CARDS];
 static int position_fix[SNDRV_CARDS];
 static int position_fix[SNDRV_CARDS];
 static int bdl_pos_adj[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
 static int bdl_pos_adj[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
 static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
 static int probe_mask[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = -1};
+static int probe_only[SNDRV_CARDS];
 static int single_cmd;
 static int single_cmd;
 static int enable_msi;
 static int enable_msi;
 
 
@@ -76,6 +77,8 @@ module_param_array(bdl_pos_adj, int, NULL, 0644);
 MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset.");
 MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset.");
 module_param_array(probe_mask, int, NULL, 0444);
 module_param_array(probe_mask, int, NULL, 0444);
 MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1).");
 MODULE_PARM_DESC(probe_mask, "Bitmask to probe codecs (default = -1).");
+module_param_array(probe_only, bool, NULL, 0444);
+MODULE_PARM_DESC(probe_only, "Only probing and no codec initialization.");
 module_param(single_cmd, bool, 0444);
 module_param(single_cmd, bool, 0444);
 MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs "
 MODULE_PARM_DESC(single_cmd, "Use single command to communicate with codecs "
 		 "(for debugging only).");
 		 "(for debugging only).");
@@ -83,7 +86,10 @@ 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_POWER_SAVE
 #ifdef CONFIG_SND_HDA_POWER_SAVE
-/* power_save option is defined in hda_codec.c */
+static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
+module_param(power_save, int, 0644);
+MODULE_PARM_DESC(power_save, "Automatic power-saving timeout "
+		 "(in second, 0 = disable).");
 
 
 /* reset the HD-audio controller in power save mode.
 /* reset the HD-audio controller in power save mode.
  * this may give more power-saving, but will take longer time to
  * this may give more power-saving, but will take longer time to
@@ -292,6 +298,8 @@ enum {
 /* Define VIA HD Audio Device ID*/
 /* Define VIA HD Audio Device ID*/
 #define VIA_HDAC_DEVICE_ID		0x3288
 #define VIA_HDAC_DEVICE_ID		0x3288
 
 
+/* HD Audio class code */
+#define PCI_CLASS_MULTIMEDIA_HD_AUDIO	0x0403
 
 
 /*
 /*
  */
  */
@@ -392,6 +400,7 @@ struct azx {
 	unsigned int msi :1;
 	unsigned int msi :1;
 	unsigned int irq_pending_warned :1;
 	unsigned int irq_pending_warned :1;
 	unsigned int via_dmapos_patch :1; /* enable DMA-position fix for VIA */
 	unsigned int via_dmapos_patch :1; /* enable DMA-position fix for VIA */
+	unsigned int probing :1; /* codec probing phase */
 
 
 	/* for debugging */
 	/* for debugging */
 	unsigned int last_cmd;	/* last issued command (to sync) */
 	unsigned int last_cmd;	/* last issued command (to sync) */
@@ -414,6 +423,7 @@ enum {
 	AZX_DRIVER_ULI,
 	AZX_DRIVER_ULI,
 	AZX_DRIVER_NVIDIA,
 	AZX_DRIVER_NVIDIA,
 	AZX_DRIVER_TERA,
 	AZX_DRIVER_TERA,
+	AZX_DRIVER_GENERIC,
 	AZX_NUM_DRIVERS, /* keep this as last entry */
 	AZX_NUM_DRIVERS, /* keep this as last entry */
 };
 };
 
 
@@ -427,6 +437,7 @@ static char *driver_short_names[] __devinitdata = {
 	[AZX_DRIVER_ULI] = "HDA ULI M5461",
 	[AZX_DRIVER_ULI] = "HDA ULI M5461",
 	[AZX_DRIVER_NVIDIA] = "HDA NVidia",
 	[AZX_DRIVER_NVIDIA] = "HDA NVidia",
 	[AZX_DRIVER_TERA] = "HDA Teradici", 
 	[AZX_DRIVER_TERA] = "HDA Teradici", 
+	[AZX_DRIVER_GENERIC] = "HD-Audio Generic",
 };
 };
 
 
 /*
 /*
@@ -527,9 +538,9 @@ static void azx_free_cmd_io(struct azx *chip)
 }
 }
 
 
 /* send a command */
 /* send a command */
-static int azx_corb_send_cmd(struct hda_codec *codec, u32 val)
+static int azx_corb_send_cmd(struct hda_bus *bus, u32 val)
 {
 {
-	struct azx *chip = codec->bus->private_data;
+	struct azx *chip = bus->private_data;
 	unsigned int wp;
 	unsigned int wp;
 
 
 	/* add command to corb */
 	/* add command to corb */
@@ -577,9 +588,9 @@ static void azx_update_rirb(struct azx *chip)
 }
 }
 
 
 /* receive a response */
 /* receive a response */
-static unsigned int azx_rirb_get_response(struct hda_codec *codec)
+static unsigned int azx_rirb_get_response(struct hda_bus *bus)
 {
 {
-	struct azx *chip = codec->bus->private_data;
+	struct azx *chip = bus->private_data;
 	unsigned long timeout;
 	unsigned long timeout;
 
 
  again:
  again:
@@ -596,7 +607,7 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
 		}
 		}
 		if (time_after(jiffies, timeout))
 		if (time_after(jiffies, timeout))
 			break;
 			break;
-		if (codec->bus->needs_damn_long_delay)
+		if (bus->needs_damn_long_delay)
 			msleep(2); /* temporary workaround */
 			msleep(2); /* temporary workaround */
 		else {
 		else {
 			udelay(10);
 			udelay(10);
@@ -624,6 +635,14 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
 		goto again;
 		goto again;
 	}
 	}
 
 
+	if (chip->probing) {
+		/* If this critical timeout happens during the codec probing
+		 * phase, this is likely an access to a non-existing codec
+		 * slot.  Better to return an error and reset the system.
+		 */
+		return -1;
+	}
+
 	snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, "
 	snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, "
 		   "switching to single_cmd mode: last cmd=0x%08x\n",
 		   "switching to single_cmd mode: last cmd=0x%08x\n",
 		   chip->last_cmd);
 		   chip->last_cmd);
@@ -646,9 +665,9 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
  */
  */
 
 
 /* send a command */
 /* send a command */
-static int azx_single_send_cmd(struct hda_codec *codec, u32 val)
+static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
 {
 {
-	struct azx *chip = codec->bus->private_data;
+	struct azx *chip = bus->private_data;
 	int timeout = 50;
 	int timeout = 50;
 
 
 	while (timeout--) {
 	while (timeout--) {
@@ -671,9 +690,9 @@ static int azx_single_send_cmd(struct hda_codec *codec, u32 val)
 }
 }
 
 
 /* receive a response */
 /* receive a response */
-static unsigned int azx_single_get_response(struct hda_codec *codec)
+static unsigned int azx_single_get_response(struct hda_bus *bus)
 {
 {
-	struct azx *chip = codec->bus->private_data;
+	struct azx *chip = bus->private_data;
 	int timeout = 50;
 	int timeout = 50;
 
 
 	while (timeout--) {
 	while (timeout--) {
@@ -696,38 +715,29 @@ static unsigned int azx_single_get_response(struct hda_codec *codec)
  */
  */
 
 
 /* send a command */
 /* send a command */
-static int azx_send_cmd(struct hda_codec *codec, hda_nid_t nid,
-			int direct, unsigned int verb,
-			unsigned int para)
+static int azx_send_cmd(struct hda_bus *bus, unsigned int val)
 {
 {
-	struct azx *chip = codec->bus->private_data;
-	u32 val;
-
-	val = (u32)(codec->addr & 0x0f) << 28;
-	val |= (u32)direct << 27;
-	val |= (u32)nid << 20;
-	val |= verb << 8;
-	val |= para;
-	chip->last_cmd = val;
+	struct azx *chip = bus->private_data;
 
 
+	chip->last_cmd = val;
 	if (chip->single_cmd)
 	if (chip->single_cmd)
-		return azx_single_send_cmd(codec, val);
+		return azx_single_send_cmd(bus, val);
 	else
 	else
-		return azx_corb_send_cmd(codec, val);
+		return azx_corb_send_cmd(bus, val);
 }
 }
 
 
 /* get a response */
 /* get a response */
-static unsigned int azx_get_response(struct hda_codec *codec)
+static unsigned int azx_get_response(struct hda_bus *bus)
 {
 {
-	struct azx *chip = codec->bus->private_data;
+	struct azx *chip = bus->private_data;
 	if (chip->single_cmd)
 	if (chip->single_cmd)
-		return azx_single_get_response(codec);
+		return azx_single_get_response(bus);
 	else
 	else
-		return azx_rirb_get_response(codec);
+		return azx_rirb_get_response(bus);
 }
 }
 
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #ifdef CONFIG_SND_HDA_POWER_SAVE
-static void azx_power_notify(struct hda_codec *codec);
+static void azx_power_notify(struct hda_bus *bus);
 #endif
 #endif
 
 
 /* reset codec link */
 /* reset codec link */
@@ -1184,6 +1194,28 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
 	return 0;
 	return 0;
 }
 }
 
 
+/*
+ * Probe the given codec address
+ */
+static int probe_codec(struct azx *chip, int addr)
+{
+	unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) |
+		(AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
+	unsigned int res;
+
+	chip->probing = 1;
+	azx_send_cmd(chip->bus, cmd);
+	res = azx_get_response(chip->bus);
+	chip->probing = 0;
+	if (res == -1)
+		return -EIO;
+	snd_printdd("hda_intel: codec #%d probed OK\n", addr);
+	return 0;
+}
+
+static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
+				 struct hda_pcm *cpcm);
+static void azx_stop_chip(struct azx *chip);
 
 
 /*
 /*
  * Codec initialization
  * Codec initialization
@@ -1194,21 +1226,13 @@ static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] __devinitdata = {
 	[AZX_DRIVER_TERA] = 1,
 	[AZX_DRIVER_TERA] = 1,
 };
 };
 
 
-/* number of slots to probe as default
- * this can be different from azx_max_codecs[] -- e.g. some boards
- * report wrongly the non-existing 4th slot availability
- */
-static unsigned int azx_default_codecs[AZX_NUM_DRIVERS] __devinitdata = {
-	[AZX_DRIVER_ICH] = 3,
-	[AZX_DRIVER_ATI] = 3,
-};
-
 static int __devinit azx_codec_create(struct azx *chip, const char *model,
 static int __devinit azx_codec_create(struct azx *chip, const char *model,
-				      unsigned int codec_probe_mask)
+				      unsigned int codec_probe_mask,
+				      int no_init)
 {
 {
 	struct hda_bus_template bus_temp;
 	struct hda_bus_template bus_temp;
-	int c, codecs, audio_codecs, err;
-	int def_slots, max_slots;
+	int c, codecs, err;
+	int max_slots;
 
 
 	memset(&bus_temp, 0, sizeof(bus_temp));
 	memset(&bus_temp, 0, sizeof(bus_temp));
 	bus_temp.private_data = chip;
 	bus_temp.private_data = chip;
@@ -1216,7 +1240,9 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
 	bus_temp.pci = chip->pci;
 	bus_temp.pci = chip->pci;
 	bus_temp.ops.command = azx_send_cmd;
 	bus_temp.ops.command = azx_send_cmd;
 	bus_temp.ops.get_response = azx_get_response;
 	bus_temp.ops.get_response = azx_get_response;
+	bus_temp.ops.attach_pcm = azx_attach_pcm_stream;
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #ifdef CONFIG_SND_HDA_POWER_SAVE
+	bus_temp.power_save = &power_save;
 	bus_temp.ops.pm_notify = azx_power_notify;
 	bus_temp.ops.pm_notify = azx_power_notify;
 #endif
 #endif
 
 
@@ -1227,33 +1253,43 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
 	if (chip->driver_type == AZX_DRIVER_NVIDIA)
 	if (chip->driver_type == AZX_DRIVER_NVIDIA)
 		chip->bus->needs_damn_long_delay = 1;
 		chip->bus->needs_damn_long_delay = 1;
 
 
-	codecs = audio_codecs = 0;
+	codecs = 0;
 	max_slots = azx_max_codecs[chip->driver_type];
 	max_slots = azx_max_codecs[chip->driver_type];
 	if (!max_slots)
 	if (!max_slots)
 		max_slots = AZX_MAX_CODECS;
 		max_slots = AZX_MAX_CODECS;
-	def_slots = azx_default_codecs[chip->driver_type];
-	if (!def_slots)
-		def_slots = max_slots;
-	for (c = 0; c < def_slots; c++) {
+
+	/* First try to probe all given codec slots */
+	for (c = 0; c < max_slots; c++) {
+		if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
+			if (probe_codec(chip, c) < 0) {
+				/* Some BIOSen give you wrong codec addresses
+				 * that don't exist
+				 */
+				snd_printk(KERN_WARNING
+					   "hda_intel: Codec #%d probe error; "
+					   "disabling it...\n", c);
+				chip->codec_mask &= ~(1 << c);
+				/* More badly, accessing to a non-existing
+				 * codec often screws up the controller chip,
+				 * and distrubs the further communications.
+				 * Thus if an error occurs during probing,
+				 * better to reset the controller chip to
+				 * get back to the sanity state.
+				 */
+				azx_stop_chip(chip);
+				azx_init_chip(chip);
+			}
+		}
+	}
+
+	/* Then create codec instances */
+	for (c = 0; c < max_slots; c++) {
 		if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
 		if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
 			struct hda_codec *codec;
 			struct hda_codec *codec;
-			err = snd_hda_codec_new(chip->bus, c, &codec);
+			err = snd_hda_codec_new(chip->bus, c, !no_init, &codec);
 			if (err < 0)
 			if (err < 0)
 				continue;
 				continue;
 			codecs++;
 			codecs++;
-			if (codec->afg)
-				audio_codecs++;
-		}
-	}
-	if (!audio_codecs) {
-		/* probe additional slots if no codec is found */
-		for (; c < max_slots; c++) {
-			if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
-				err = snd_hda_codec_new(chip->bus, c, NULL);
-				if (err < 0)
-					continue;
-				codecs++;
-			}
 		}
 		}
 	}
 	}
 	if (!codecs) {
 	if (!codecs) {
@@ -1722,111 +1758,59 @@ static struct snd_pcm_ops azx_pcm_ops = {
 
 
 static void azx_pcm_free(struct snd_pcm *pcm)
 static void azx_pcm_free(struct snd_pcm *pcm)
 {
 {
-	kfree(pcm->private_data);
+	struct azx_pcm *apcm = pcm->private_data;
+	if (apcm) {
+		apcm->chip->pcm[pcm->device] = NULL;
+		kfree(apcm);
+	}
 }
 }
 
 
-static int __devinit create_codec_pcm(struct azx *chip, struct hda_codec *codec,
-				      struct hda_pcm *cpcm)
+static int
+azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
+		      struct hda_pcm *cpcm)
 {
 {
-	int err;
+	struct azx *chip = bus->private_data;
 	struct snd_pcm *pcm;
 	struct snd_pcm *pcm;
 	struct azx_pcm *apcm;
 	struct azx_pcm *apcm;
+	int pcm_dev = cpcm->device;
+	int s, err;
 
 
-	/* if no substreams are defined for both playback and capture,
-	 * it's just a placeholder.  ignore it.
-	 */
-	if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams)
-		return 0;
-
-	if (snd_BUG_ON(!cpcm->name))
+	if (pcm_dev >= AZX_MAX_PCMS) {
+		snd_printk(KERN_ERR SFX "Invalid PCM device number %d\n",
+			   pcm_dev);
 		return -EINVAL;
 		return -EINVAL;
-
-	err = snd_pcm_new(chip->card, cpcm->name, cpcm->device,
-			  cpcm->stream[0].substreams,
-			  cpcm->stream[1].substreams,
+	}
+	if (chip->pcm[pcm_dev]) {
+		snd_printk(KERN_ERR SFX "PCM %d already exists\n", pcm_dev);
+		return -EBUSY;
+	}
+	err = snd_pcm_new(chip->card, cpcm->name, pcm_dev,
+			  cpcm->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams,
+			  cpcm->stream[SNDRV_PCM_STREAM_CAPTURE].substreams,
 			  &pcm);
 			  &pcm);
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
 	strcpy(pcm->name, cpcm->name);
 	strcpy(pcm->name, cpcm->name);
-	apcm = kmalloc(sizeof(*apcm), GFP_KERNEL);
+	apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
 	if (apcm == NULL)
 	if (apcm == NULL)
 		return -ENOMEM;
 		return -ENOMEM;
 	apcm->chip = chip;
 	apcm->chip = chip;
 	apcm->codec = codec;
 	apcm->codec = codec;
-	apcm->hinfo[0] = &cpcm->stream[0];
-	apcm->hinfo[1] = &cpcm->stream[1];
 	pcm->private_data = apcm;
 	pcm->private_data = apcm;
 	pcm->private_free = azx_pcm_free;
 	pcm->private_free = azx_pcm_free;
-	if (cpcm->stream[0].substreams)
-		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &azx_pcm_ops);
-	if (cpcm->stream[1].substreams)
-		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &azx_pcm_ops);
+	if (cpcm->pcm_type == HDA_PCM_TYPE_MODEM)
+		pcm->dev_class = SNDRV_PCM_CLASS_MODEM;
+	chip->pcm[pcm_dev] = pcm;
+	cpcm->pcm = pcm;
+	for (s = 0; s < 2; s++) {
+		apcm->hinfo[s] = &cpcm->stream[s];
+		if (cpcm->stream[s].substreams)
+			snd_pcm_set_ops(pcm, s, &azx_pcm_ops);
+	}
+	/* buffer pre-allocation */
 	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
 	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
 					      snd_dma_pci_data(chip->pci),
 					      snd_dma_pci_data(chip->pci),
 					      1024 * 64, 32 * 1024 * 1024);
 					      1024 * 64, 32 * 1024 * 1024);
-	chip->pcm[cpcm->device] = pcm;
-	return 0;
-}
-
-static int __devinit azx_pcm_create(struct azx *chip)
-{
-	static const char *dev_name[HDA_PCM_NTYPES] = {
-		"Audio", "SPDIF", "HDMI", "Modem"
-	};
-	/* starting device index for each PCM type */
-	static int dev_idx[HDA_PCM_NTYPES] = {
-		[HDA_PCM_TYPE_AUDIO] = 0,
-		[HDA_PCM_TYPE_SPDIF] = 1,
-		[HDA_PCM_TYPE_HDMI] = 3,
-		[HDA_PCM_TYPE_MODEM] = 6
-	};
-	/* normal audio device indices; not linear to keep compatibility */
-	static int audio_idx[4] = { 0, 2, 4, 5 };
-	struct hda_codec *codec;
-	int c, err;
-	int num_devs[HDA_PCM_NTYPES];
-
-	err = snd_hda_build_pcms(chip->bus);
-	if (err < 0)
-		return err;
-
-	/* create audio PCMs */
-	memset(num_devs, 0, sizeof(num_devs));
-	list_for_each_entry(codec, &chip->bus->codec_list, list) {
-		for (c = 0; c < codec->num_pcms; c++) {
-			struct hda_pcm *cpcm = &codec->pcm_info[c];
-			int type = cpcm->pcm_type;
-			switch (type) {
-			case HDA_PCM_TYPE_AUDIO:
-				if (num_devs[type] >= ARRAY_SIZE(audio_idx)) {
-					snd_printk(KERN_WARNING
-						   "Too many audio devices\n");
-					continue;
-				}
-				cpcm->device = audio_idx[num_devs[type]];
-				break;
-			case HDA_PCM_TYPE_SPDIF:
-			case HDA_PCM_TYPE_HDMI:
-			case HDA_PCM_TYPE_MODEM:
-				if (num_devs[type]) {
-					snd_printk(KERN_WARNING
-						   "%s already defined\n",
-						   dev_name[type]);
-					continue;
-				}
-				cpcm->device = dev_idx[type];
-				break;
-			default:
-				snd_printk(KERN_WARNING
-					   "Invalid PCM type %d\n", type);
-				continue;
-			}
-			num_devs[type]++;
-			err = create_codec_pcm(chip, codec, cpcm);
-			if (err < 0)
-				return err;
-		}
-	}
 	return 0;
 	return 0;
 }
 }
 
 
@@ -1903,13 +1887,13 @@ static void azx_stop_chip(struct azx *chip)
 
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 /* power-up/down the controller */
 /* power-up/down the controller */
-static void azx_power_notify(struct hda_codec *codec)
+static void azx_power_notify(struct hda_bus *bus)
 {
 {
-	struct azx *chip = codec->bus->private_data;
+	struct azx *chip = bus->private_data;
 	struct hda_codec *c;
 	struct hda_codec *c;
 	int power_on = 0;
 	int power_on = 0;
 
 
-	list_for_each_entry(c, &codec->bus->codec_list, list) {
+	list_for_each_entry(c, &bus->codec_list, list) {
 		if (c->power_on) {
 		if (c->power_on) {
 			power_on = 1;
 			power_on = 1;
 			break;
 			break;
@@ -1926,6 +1910,18 @@ static void azx_power_notify(struct hda_codec *codec)
 /*
 /*
  * power management
  * power management
  */
  */
+
+static int snd_hda_codecs_inuse(struct hda_bus *bus)
+{
+	struct hda_codec *codec;
+
+	list_for_each_entry(codec, &bus->codec_list, list) {
+		if (snd_hda_codec_needs_resume(codec))
+			return 1;
+	}
+	return 0;
+}
+
 static int azx_suspend(struct pci_dev *pci, pm_message_t state)
 static int azx_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);
@@ -1951,13 +1947,16 @@ static int azx_suspend(struct pci_dev *pci, pm_message_t state)
 	return 0;
 	return 0;
 }
 }
 
 
+static int azx_resume_early(struct pci_dev *pci)
+{
+	return pci_restore_state(pci);
+}
+
 static int azx_resume(struct pci_dev *pci)
 static int azx_resume(struct pci_dev *pci)
 {
 {
 	struct snd_card *card = pci_get_drvdata(pci);
 	struct snd_card *card = pci_get_drvdata(pci);
 	struct azx *chip = card->private_data;
 	struct azx *chip = card->private_data;
 
 
-	pci_set_power_state(pci, PCI_D0);
-	pci_restore_state(pci);
 	if (pci_enable_device(pci) < 0) {
 	if (pci_enable_device(pci) < 0) {
 		printk(KERN_ERR "hda-intel: pci_enable_device failed, "
 		printk(KERN_ERR "hda-intel: pci_enable_device failed, "
 		       "disabling device\n");
 		       "disabling device\n");
@@ -2095,6 +2094,10 @@ static struct snd_pci_quirk probe_mask_list[] __devinitdata = {
 	SND_PCI_QUIRK(0x1014, 0x05b7, "Thinkpad Z60", 0x01),
 	SND_PCI_QUIRK(0x1014, 0x05b7, "Thinkpad Z60", 0x01),
 	SND_PCI_QUIRK(0x17aa, 0x2010, "Thinkpad X/T/R60", 0x01),
 	SND_PCI_QUIRK(0x17aa, 0x2010, "Thinkpad X/T/R60", 0x01),
 	SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X/T/R61", 0x01),
 	SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X/T/R61", 0x01),
+	/* broken BIOS */
+	SND_PCI_QUIRK(0x1028, 0x20ac, "Dell Studio Desktop", 0x01),
+	/* including bogus ALC268 in slot#2 that conflicts with ALC888 */
+	SND_PCI_QUIRK(0x17c0, 0x4085, "Medion MD96630", 0x01),
 	{}
 	{}
 };
 };
 
 
@@ -2229,6 +2232,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
 			chip->playback_streams = ATIHDMI_NUM_PLAYBACK;
 			chip->playback_streams = ATIHDMI_NUM_PLAYBACK;
 			chip->capture_streams = ATIHDMI_NUM_CAPTURE;
 			chip->capture_streams = ATIHDMI_NUM_CAPTURE;
 			break;
 			break;
+		case AZX_DRIVER_GENERIC:
 		default:
 		default:
 			chip->playback_streams = ICH6_NUM_PLAYBACK;
 			chip->playback_streams = ICH6_NUM_PLAYBACK;
 			chip->capture_streams = ICH6_NUM_CAPTURE;
 			chip->capture_streams = ICH6_NUM_CAPTURE;
@@ -2338,40 +2342,31 @@ static int __devinit azx_probe(struct pci_dev *pci,
 	}
 	}
 
 
 	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) {
-		snd_card_free(card);
-		return err;
-	}
+	if (err < 0)
+		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_mask[dev]);
-	if (err < 0) {
-		snd_card_free(card);
-		return err;
-	}
+	err = azx_codec_create(chip, model[dev], probe_mask[dev],
+			       probe_only[dev]);
+	if (err < 0)
+		goto out_free;
 
 
 	/* create PCM streams */
 	/* create PCM streams */
-	err = azx_pcm_create(chip);
-	if (err < 0) {
-		snd_card_free(card);
-		return err;
-	}
+	err = snd_hda_build_pcms(chip->bus);
+	if (err < 0)
+		goto out_free;
 
 
 	/* create mixer controls */
 	/* create mixer controls */
 	err = azx_mixer_create(chip);
 	err = azx_mixer_create(chip);
-	if (err < 0) {
-		snd_card_free(card);
-		return err;
-	}
+	if (err < 0)
+		goto out_free;
 
 
 	snd_card_set_dev(card, &pci->dev);
 	snd_card_set_dev(card, &pci->dev);
 
 
 	err = snd_card_register(card);
 	err = snd_card_register(card);
-	if (err < 0) {
-		snd_card_free(card);
-		return err;
-	}
+	if (err < 0)
+		goto out_free;
 
 
 	pci_set_drvdata(pci, card);
 	pci_set_drvdata(pci, card);
 	chip->running = 1;
 	chip->running = 1;
@@ -2380,6 +2375,9 @@ static int __devinit azx_probe(struct pci_dev *pci,
 
 
 	dev++;
 	dev++;
 	return err;
 	return err;
+out_free:
+	snd_card_free(card);
+	return err;
 }
 }
 
 
 static void __devexit azx_remove(struct pci_dev *pci)
 static void __devexit azx_remove(struct pci_dev *pci)
@@ -2453,6 +2451,11 @@ static struct pci_device_id azx_ids[] = {
 	{ PCI_DEVICE(0x10de, 0x0bd7), .driver_data = AZX_DRIVER_NVIDIA },
 	{ PCI_DEVICE(0x10de, 0x0bd7), .driver_data = AZX_DRIVER_NVIDIA },
 	/* Teradici */
 	/* Teradici */
 	{ PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA },
 	{ PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA },
+	/* AMD Generic, PCI class code and Vendor ID for HD Audio */
+	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, 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);
@@ -2465,6 +2468,7 @@ static struct pci_driver driver = {
 	.remove = __devexit_p(azx_remove),
 	.remove = __devexit_p(azx_remove),
 #ifdef CONFIG_PM
 #ifdef CONFIG_PM
 	.suspend = azx_suspend,
 	.suspend = azx_suspend,
+	.resume_early = azx_resume_early,
 	.resume = azx_resume,
 	.resume = azx_resume,
 #endif
 #endif
 };
 };

+ 93 - 7
sound/pci/hda/hda_local.h

@@ -96,6 +96,8 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
 					    const char *name);
 					    const char *name);
 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);
+void 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
@@ -282,6 +284,12 @@ int snd_hda_codec_proc_new(struct hda_codec *codec);
 static inline int snd_hda_codec_proc_new(struct hda_codec *codec) { return 0; }
 static inline int snd_hda_codec_proc_new(struct hda_codec *codec) { return 0; }
 #endif
 #endif
 
 
+#define SND_PRINT_RATES_ADVISED_BUFSIZE	80
+void snd_print_pcm_rates(int pcm, char *buf, int buflen);
+
+#define SND_PRINT_BITS_ADVISED_BUFSIZE	16
+void snd_print_pcm_bits(int pcm, char *buf, int buflen);
+
 /*
 /*
  * Misc
  * Misc
  */
  */
@@ -364,17 +372,17 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
 /* amp values */
 /* amp values */
 #define AMP_IN_MUTE(idx)	(0x7080 | ((idx)<<8))
 #define AMP_IN_MUTE(idx)	(0x7080 | ((idx)<<8))
 #define AMP_IN_UNMUTE(idx)	(0x7000 | ((idx)<<8))
 #define AMP_IN_UNMUTE(idx)	(0x7000 | ((idx)<<8))
-#define AMP_OUT_MUTE	0xb080
-#define AMP_OUT_UNMUTE	0xb000
-#define AMP_OUT_ZERO	0xb000
+#define AMP_OUT_MUTE		0xb080
+#define AMP_OUT_UNMUTE		0xb000
+#define AMP_OUT_ZERO		0xb000
 /* pinctl values */
 /* pinctl values */
 #define PIN_IN			(AC_PINCTL_IN_EN)
 #define PIN_IN			(AC_PINCTL_IN_EN)
-#define PIN_VREFHIZ	(AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ)
+#define PIN_VREFHIZ		(AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ)
 #define PIN_VREF50		(AC_PINCTL_IN_EN | AC_PINCTL_VREF_50)
 #define PIN_VREF50		(AC_PINCTL_IN_EN | AC_PINCTL_VREF_50)
-#define PIN_VREFGRD	(AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD)
+#define PIN_VREFGRD		(AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD)
 #define PIN_VREF80		(AC_PINCTL_IN_EN | AC_PINCTL_VREF_80)
 #define PIN_VREF80		(AC_PINCTL_IN_EN | AC_PINCTL_VREF_80)
-#define PIN_VREF100	(AC_PINCTL_IN_EN | AC_PINCTL_VREF_100)
-#define PIN_OUT		(AC_PINCTL_OUT_EN)
+#define PIN_VREF100		(AC_PINCTL_IN_EN | AC_PINCTL_VREF_100)
+#define PIN_OUT			(AC_PINCTL_OUT_EN)
 #define PIN_HP			(AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN)
 #define PIN_HP			(AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN)
 #define PIN_HP_AMP		(AC_PINCTL_HP_EN)
 #define PIN_HP_AMP		(AC_PINCTL_HP_EN)
 
 
@@ -393,10 +401,26 @@ 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);
 
 
+int snd_hda_ctl_add(struct hda_codec *codec, struct snd_kcontrol *kctl);
+void snd_hda_ctls_clear(struct hda_codec *codec);
+
 /*
 /*
  * hwdep interface
  * hwdep interface
  */
  */
+#ifdef CONFIG_SND_HDA_HWDEP
 int snd_hda_create_hwdep(struct hda_codec *codec);
 int snd_hda_create_hwdep(struct hda_codec *codec);
+#else
+static inline int snd_hda_create_hwdep(struct hda_codec *codec) { return 0; }
+#endif
+
+#ifdef CONFIG_SND_HDA_RECONFIG
+int snd_hda_hwdep_add_sysfs(struct hda_codec *codec);
+#else
+static inline int snd_hda_hwdep_add_sysfs(struct hda_codec *codec)
+{
+	return 0;
+}
+#endif
 
 
 /*
 /*
  * power-management
  * power-management
@@ -430,4 +454,66 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
 #define get_amp_direction(kc)	(((kc)->private_value >> 18) & 0x1)
 #define get_amp_direction(kc)	(((kc)->private_value >> 18) & 0x1)
 #define get_amp_index(kc)	(((kc)->private_value >> 19) & 0xf)
 #define get_amp_index(kc)	(((kc)->private_value >> 19) & 0xf)
 
 
+/*
+ * CEA Short Audio Descriptor data
+ */
+struct cea_sad {
+	int	channels;
+	int	format;		/* (format == 0) indicates invalid SAD */
+	int	rates;
+	int	sample_bits;	/* for LPCM */
+	int	max_bitrate;	/* for AC3...ATRAC */
+	int	profile;	/* for WMAPRO */
+};
+
+#define ELD_FIXED_BYTES	20
+#define ELD_MAX_MNL	16
+#define ELD_MAX_SAD	16
+
+/*
+ * ELD: EDID Like Data
+ */
+struct hdmi_eld {
+	int	eld_size;
+	int	baseline_len;
+	int	eld_ver;	/* (eld_ver == 0) indicates invalid ELD */
+	int	cea_edid_ver;
+	char	monitor_name[ELD_MAX_MNL + 1];
+	int	manufacture_id;
+	int	product_id;
+	u64	port_id;
+	int	support_hdcp;
+	int	support_ai;
+	int	conn_type;
+	int	aud_synch_delay;
+	int	spk_alloc;
+	int	sad_count;
+	struct cea_sad sad[ELD_MAX_SAD];
+#ifdef CONFIG_PROC_FS
+	struct snd_info_entry *proc_entry;
+#endif
+};
+
+int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid);
+int snd_hdmi_get_eld(struct hdmi_eld *, struct hda_codec *, hda_nid_t);
+void snd_hdmi_show_eld(struct hdmi_eld *eld);
+
+#ifdef CONFIG_PROC_FS
+int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld);
+void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld);
+#else
+static inline int snd_hda_eld_proc_new(struct hda_codec *codec,
+				       struct hdmi_eld *eld)
+{
+	return 0;
+}
+static inline void snd_hda_eld_proc_free(struct hda_codec *codec,
+					 struct hdmi_eld *eld)
+{
+}
+#endif
+
+#define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80
+void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen);
+
 #endif /* __SOUND_HDA_LOCAL_H */
 #endif /* __SOUND_HDA_LOCAL_H */

+ 0 - 22
sound/pci/hda/hda_patch.h

@@ -1,22 +0,0 @@
-/*
- * HDA Patches - included by hda_codec.c
- */
-
-/* Realtek codecs */
-extern struct hda_codec_preset snd_hda_preset_realtek[];
-/* C-Media codecs */
-extern struct hda_codec_preset snd_hda_preset_cmedia[];
-/* Analog Devices codecs */
-extern struct hda_codec_preset snd_hda_preset_analog[];
-/* SigmaTel codecs */
-extern struct hda_codec_preset snd_hda_preset_sigmatel[];
-/* SiLabs 3054/3055 modem codecs */
-extern struct hda_codec_preset snd_hda_preset_si3054[];
-/* ATI HDMI codecs */
-extern struct hda_codec_preset snd_hda_preset_atihdmi[];
-/* Conexant audio codec */
-extern struct hda_codec_preset snd_hda_preset_conexant[];
-/* VIA codecs */
-extern struct hda_codec_preset snd_hda_preset_via[];
-/* NVIDIA HDMI codecs */
-extern struct hda_codec_preset snd_hda_preset_nvhdmi[];

+ 19 - 72
sound/pci/hda/hda_proc.c

@@ -91,31 +91,21 @@ static void print_amp_vals(struct snd_info_buffer *buffer,
 
 
 static void print_pcm_rates(struct snd_info_buffer *buffer, unsigned int pcm)
 static void print_pcm_rates(struct snd_info_buffer *buffer, unsigned int pcm)
 {
 {
-	static unsigned int rates[] = {
-		8000, 11025, 16000, 22050, 32000, 44100, 48000, 88200,
-		96000, 176400, 192000, 384000
-	};
-	int i;
+	char buf[SND_PRINT_RATES_ADVISED_BUFSIZE];
 
 
 	pcm &= AC_SUPPCM_RATES;
 	pcm &= AC_SUPPCM_RATES;
 	snd_iprintf(buffer, "    rates [0x%x]:", pcm);
 	snd_iprintf(buffer, "    rates [0x%x]:", pcm);
-	for (i = 0; i < ARRAY_SIZE(rates); i++) 
-		if (pcm & (1 << i))
-			snd_iprintf(buffer, " %d", rates[i]);
-	snd_iprintf(buffer, "\n");
+	snd_print_pcm_rates(pcm, buf, sizeof(buf));
+	snd_iprintf(buffer, "%s\n", buf);
 }
 }
 
 
 static void print_pcm_bits(struct snd_info_buffer *buffer, unsigned int pcm)
 static void print_pcm_bits(struct snd_info_buffer *buffer, unsigned int pcm)
 {
 {
-	static unsigned int bits[] = { 8, 16, 20, 24, 32 };
-	int i;
+	char buf[SND_PRINT_BITS_ADVISED_BUFSIZE];
 
 
-	pcm = (pcm >> 16) & 0xff;
-	snd_iprintf(buffer, "    bits [0x%x]:", pcm);
-	for (i = 0; i < ARRAY_SIZE(bits); i++)
-		if (pcm & (1 << i))
-			snd_iprintf(buffer, " %d", bits[i]);
-	snd_iprintf(buffer, "\n");
+	snd_iprintf(buffer, "    bits [0x%x]:", (pcm >> 16) & 0xff);
+	snd_print_pcm_bits(pcm, buf, sizeof(buf));
+	snd_iprintf(buffer, "%s\n", buf);
 }
 }
 
 
 static void print_pcm_formats(struct snd_info_buffer *buffer,
 static void print_pcm_formats(struct snd_info_buffer *buffer,
@@ -145,32 +135,6 @@ static void print_pcm_caps(struct snd_info_buffer *buffer,
 	print_pcm_formats(buffer, stream);
 	print_pcm_formats(buffer, stream);
 }
 }
 
 
-static const char *get_jack_location(u32 cfg)
-{
-	static char *bases[7] = {
-		"N/A", "Rear", "Front", "Left", "Right", "Top", "Bottom",
-	};
-	static unsigned char specials_idx[] = {
-		0x07, 0x08,
-		0x17, 0x18, 0x19,
-		0x37, 0x38
-	};
-	static char *specials[] = {
-		"Rear Panel", "Drive Bar",
-		"Riser", "HDMI", "ATAPI",
-		"Mobile-In", "Mobile-Out"
-	};
-	int i;
-	cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT;
-	if ((cfg & 0x0f) < 7)
-		return bases[cfg & 0x0f];
-	for (i = 0; i < ARRAY_SIZE(specials_idx); i++) {
-		if (cfg == specials_idx[i])
-			return specials[i];
-	}
-	return "UNKNOWN";
-}
-
 static const char *get_jack_connection(u32 cfg)
 static const char *get_jack_connection(u32 cfg)
 {
 {
 	static char *names[16] = {
 	static char *names[16] = {
@@ -206,13 +170,6 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
 			   int *supports_vref)
 			   int *supports_vref)
 {
 {
 	static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" };
 	static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" };
-	static char *jack_types[16] = {
-		"Line Out", "Speaker", "HP Out", "CD",
-		"SPDIF Out", "Digital Out", "Modem Line", "Modem Hand",
-		"Line In", "Aux", "Mic", "Telephony",
-		"SPDIF In", "Digitial In", "Reserved", "Other"
-	};
-	static char *jack_locations[4] = { "Ext", "Int", "Sep", "Oth" };
 	unsigned int caps, val;
 	unsigned int caps, val;
 
 
 	caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
 	caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
@@ -274,9 +231,9 @@ static void print_pin_caps(struct snd_info_buffer *buffer,
 	caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
 	caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
 	snd_iprintf(buffer, "  Pin Default 0x%08x: [%s] %s at %s %s\n", caps,
 	snd_iprintf(buffer, "  Pin Default 0x%08x: [%s] %s at %s %s\n", caps,
 		    jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT],
 		    jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT],
-		    jack_types[(caps & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT],
-		    jack_locations[(caps >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3],
-		    get_jack_location(caps));
+		    snd_hda_get_jack_type(caps),
+		    snd_hda_get_jack_connectivity(caps),
+		    snd_hda_get_jack_location(caps));
 	snd_iprintf(buffer, "    Conn = %s, Color = %s\n",
 	snd_iprintf(buffer, "    Conn = %s, Color = %s\n",
 		    get_jack_connection(caps),
 		    get_jack_connection(caps),
 		    get_jack_color(caps));
 		    get_jack_color(caps));
@@ -457,17 +414,6 @@ static void print_conn_list(struct snd_info_buffer *buffer,
 	}
 	}
 }
 }
 
 
-static void print_realtek_coef(struct snd_info_buffer *buffer,
-			       struct hda_codec *codec, hda_nid_t nid)
-{
-	int coeff = snd_hda_codec_read(codec, nid, 0,
-				       AC_VERB_GET_PROC_COEF, 0);
-	snd_iprintf(buffer, "  Processing Coefficient: 0x%02x\n", coeff);
-	coeff = snd_hda_codec_read(codec, nid, 0,
-				   AC_VERB_GET_COEF_INDEX, 0);
-	snd_iprintf(buffer, "  Coefficient Index: 0x%02x\n", coeff);
-}
-
 static void print_gpio(struct snd_info_buffer *buffer,
 static void print_gpio(struct snd_info_buffer *buffer,
 		       struct hda_codec *codec, hda_nid_t nid)
 		       struct hda_codec *codec, hda_nid_t nid)
 {
 {
@@ -500,12 +446,13 @@ static void print_gpio(struct snd_info_buffer *buffer,
 	for (i = 0; i < max; ++i)
 	for (i = 0; i < max; ++i)
 		snd_iprintf(buffer,
 		snd_iprintf(buffer,
 			    "  IO[%d]: enable=%d, dir=%d, wake=%d, "
 			    "  IO[%d]: enable=%d, dir=%d, wake=%d, "
-			    "sticky=%d, data=%d\n", i,
+			    "sticky=%d, data=%d, unsol=%d\n", i,
 			    (enable & (1<<i)) ? 1 : 0,
 			    (enable & (1<<i)) ? 1 : 0,
 			    (direction & (1<<i)) ? 1 : 0,
 			    (direction & (1<<i)) ? 1 : 0,
 			    (wake & (1<<i)) ? 1 : 0,
 			    (wake & (1<<i)) ? 1 : 0,
 			    (sticky & (1<<i)) ? 1 : 0,
 			    (sticky & (1<<i)) ? 1 : 0,
-			    (data & (1<<i)) ? 1 : 0);
+			    (data & (1<<i)) ? 1 : 0,
+			    (unsol & (1<<i)) ? 1 : 0);
 	/* FIXME: add GPO and GPI pin information */
 	/* FIXME: add GPO and GPI pin information */
 }
 }
 
 
@@ -513,12 +460,11 @@ static void print_codec_info(struct snd_info_entry *entry,
 			     struct snd_info_buffer *buffer)
 			     struct snd_info_buffer *buffer)
 {
 {
 	struct hda_codec *codec = entry->private_data;
 	struct hda_codec *codec = entry->private_data;
-	char buf[32];
 	hda_nid_t nid;
 	hda_nid_t nid;
 	int i, nodes;
 	int i, nodes;
 
 
-	snd_hda_get_codec_name(codec, buf, sizeof(buf));
-	snd_iprintf(buffer, "Codec: %s\n", buf);
+	snd_iprintf(buffer, "Codec: %s\n",
+		    codec->name ? codec->name : "Not Set");
 	snd_iprintf(buffer, "Address: %d\n", codec->addr);
 	snd_iprintf(buffer, "Address: %d\n", codec->addr);
 	snd_iprintf(buffer, "Vendor Id: 0x%x\n", codec->vendor_id);
 	snd_iprintf(buffer, "Vendor Id: 0x%x\n", codec->vendor_id);
 	snd_iprintf(buffer, "Subsystem Id: 0x%x\n", codec->subsystem_id);
 	snd_iprintf(buffer, "Subsystem Id: 0x%x\n", codec->subsystem_id);
@@ -547,6 +493,8 @@ static void print_codec_info(struct snd_info_entry *entry,
 	}
 	}
 
 
 	print_gpio(buffer, codec, codec->afg);
 	print_gpio(buffer, codec, codec->afg);
+	if (codec->proc_widget_hook)
+		codec->proc_widget_hook(buffer, codec, codec->afg);
 
 
 	for (i = 0; i < nodes; i++, nid++) {
 	for (i = 0; i < nodes; i++, nid++) {
 		unsigned int wid_caps =
 		unsigned int wid_caps =
@@ -649,9 +597,8 @@ static void print_codec_info(struct snd_info_entry *entry,
 		if (wid_caps & AC_WCAP_PROC_WID)
 		if (wid_caps & AC_WCAP_PROC_WID)
 			print_proc_caps(buffer, codec, nid);
 			print_proc_caps(buffer, codec, nid);
 
 
-		/* NID 0x20 == Realtek Define Registers */
-		if (codec->vendor_id == 0x10ec && nid == 0x20)
-			print_realtek_coef(buffer, codec, nid);
+		if (codec->proc_widget_hook)
+			codec->proc_widget_hook(buffer, codec, nid);
 	}
 	}
 	snd_hda_power_down(codec);
 	snd_hda_power_down(codec);
 }
 }

+ 98 - 34
sound/pci/hda/patch_analog.c

@@ -27,7 +27,6 @@
 #include <sound/core.h>
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_codec.h"
 #include "hda_local.h"
 #include "hda_local.h"
-#include "hda_patch.h"
 
 
 struct ad198x_spec {
 struct ad198x_spec {
 	struct snd_kcontrol_new *mixers[5];
 	struct snd_kcontrol_new *mixers[5];
@@ -67,8 +66,7 @@ struct ad198x_spec {
 
 
 	/* dynamic controls, init_verbs and input_mux */
 	/* dynamic controls, init_verbs and input_mux */
 	struct auto_pin_cfg autocfg;
 	struct auto_pin_cfg autocfg;
-	unsigned int num_kctl_alloc, num_kctl_used;
-	struct snd_kcontrol_new *kctl_alloc;
+	struct snd_array kctls;
 	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];
 
 
@@ -154,6 +152,8 @@ static const char *ad_slave_sws[] = {
 	NULL
 	NULL
 };
 };
 
 
+static void ad198x_free_kctls(struct hda_codec *codec);
+
 static int ad198x_build_controls(struct hda_codec *codec)
 static int ad198x_build_controls(struct hda_codec *codec)
 {
 {
 	struct ad198x_spec *spec = codec->spec;
 	struct ad198x_spec *spec = codec->spec;
@@ -202,6 +202,7 @@ static int ad198x_build_controls(struct hda_codec *codec)
 			return err;
 			return err;
 	}
 	}
 
 
+	ad198x_free_kctls(codec); /* no longer needed */
 	return 0;
 	return 0;
 }
 }
 
 
@@ -375,16 +376,27 @@ static int ad198x_build_pcms(struct hda_codec *codec)
 	return 0;
 	return 0;
 }
 }
 
 
-static void ad198x_free(struct hda_codec *codec)
+static void ad198x_free_kctls(struct hda_codec *codec)
 {
 {
 	struct ad198x_spec *spec = codec->spec;
 	struct ad198x_spec *spec = codec->spec;
-	unsigned int i;
 
 
-	if (spec->kctl_alloc) {
-		for (i = 0; i < spec->num_kctl_used; i++)
-			kfree(spec->kctl_alloc[i].name);
-		kfree(spec->kctl_alloc);
+	if (spec->kctls.list) {
+		struct snd_kcontrol_new *kctl = spec->kctls.list;
+		int i;
+		for (i = 0; i < spec->kctls.used; i++)
+			kfree(kctl[i].name);
 	}
 	}
+	snd_array_free(&spec->kctls);
+}
+
+static void ad198x_free(struct hda_codec *codec)
+{
+	struct ad198x_spec *spec = codec->spec;
+
+	if (!spec)
+		return;
+
+	ad198x_free_kctls(codec);
 	kfree(codec->spec);
 	kfree(codec->spec);
 }
 }
 
 
@@ -625,6 +637,36 @@ static struct hda_input_mux ad1986a_automic_capture_source = {
 };
 };
 
 
 static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
 static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
+	HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
+	HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
+	HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Capture Source",
+		.info = ad198x_mux_enum_info,
+		.get = ad198x_mux_enum_get,
+		.put = ad198x_mux_enum_put,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "External Amplifier",
+		.info = ad198x_eapd_info,
+		.get = ad198x_eapd_get,
+		.put = ad198x_eapd_put,
+		.private_value = 0x1b | (1 << 8), /* port-D, inversed */
+	},
+	{ } /* end */
+};
+
+static struct snd_kcontrol_new ad1986a_samsung_mixers[] = {
 	HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
 	HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
 	HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
 	HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
 	HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
@@ -917,6 +959,7 @@ enum {
 	AD1986A_LAPTOP_EAPD,
 	AD1986A_LAPTOP_EAPD,
 	AD1986A_LAPTOP_AUTOMUTE,
 	AD1986A_LAPTOP_AUTOMUTE,
 	AD1986A_ULTRA,
 	AD1986A_ULTRA,
+	AD1986A_SAMSUNG,
 	AD1986A_MODELS
 	AD1986A_MODELS
 };
 };
 
 
@@ -927,6 +970,7 @@ static const char *ad1986a_models[AD1986A_MODELS] = {
 	[AD1986A_LAPTOP_EAPD]	= "laptop-eapd",
 	[AD1986A_LAPTOP_EAPD]	= "laptop-eapd",
 	[AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
 	[AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
 	[AD1986A_ULTRA]		= "ultra",
 	[AD1986A_ULTRA]		= "ultra",
+	[AD1986A_SAMSUNG]	= "samsung",
 };
 };
 
 
 static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
 static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
@@ -949,9 +993,9 @@ static struct snd_pci_quirk ad1986a_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD),
 	SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD),
 	SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
 	SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
 	SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
 	SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
-	SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_LAPTOP_EAPD),
-	SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_LAPTOP_EAPD),
-	SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_LAPTOP_EAPD),
+	SND_PCI_QUIRK(0x144d, 0xc023, "Samsung X60", AD1986A_SAMSUNG),
+	SND_PCI_QUIRK(0x144d, 0xc024, "Samsung R65", AD1986A_SAMSUNG),
+	SND_PCI_QUIRK(0x144d, 0xc026, "Samsung X11", AD1986A_SAMSUNG),
 	SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
 	SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
 	SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
 	SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
 	SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
 	SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
@@ -1033,6 +1077,17 @@ static int patch_ad1986a(struct hda_codec *codec)
 		break;
 		break;
 	case AD1986A_LAPTOP_EAPD:
 	case AD1986A_LAPTOP_EAPD:
 		spec->mixers[0] = ad1986a_laptop_eapd_mixers;
 		spec->mixers[0] = ad1986a_laptop_eapd_mixers;
+		spec->num_init_verbs = 2;
+		spec->init_verbs[1] = ad1986a_eapd_init_verbs;
+		spec->multiout.max_channels = 2;
+		spec->multiout.num_dacs = 1;
+		spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
+		if (!is_jack_available(codec, 0x25))
+			spec->multiout.dig_out_nid = 0;
+		spec->input_mux = &ad1986a_laptop_eapd_capture_source;
+		break;
+	case AD1986A_SAMSUNG:
+		spec->mixers[0] = ad1986a_samsung_mixers;
 		spec->num_init_verbs = 3;
 		spec->num_init_verbs = 3;
 		spec->init_verbs[1] = ad1986a_eapd_init_verbs;
 		spec->init_verbs[1] = ad1986a_eapd_init_verbs;
 		spec->init_verbs[2] = ad1986a_automic_verbs;
 		spec->init_verbs[2] = ad1986a_automic_verbs;
@@ -2452,9 +2507,6 @@ static struct hda_amp_list ad1988_loopbacks[] = {
  * Automatic parse of I/O pins from the BIOS configuration
  * Automatic parse of I/O pins from the BIOS configuration
  */
  */
 
 
-#define NUM_CONTROL_ALLOC	32
-#define NUM_VERB_ALLOC		32
-
 enum {
 enum {
 	AD_CTL_WIDGET_VOL,
 	AD_CTL_WIDGET_VOL,
 	AD_CTL_WIDGET_MUTE,
 	AD_CTL_WIDGET_MUTE,
@@ -2472,27 +2524,15 @@ static int add_control(struct ad198x_spec *spec, int type, const char *name,
 {
 {
 	struct snd_kcontrol_new *knew;
 	struct snd_kcontrol_new *knew;
 
 
-	if (spec->num_kctl_used >= spec->num_kctl_alloc) {
-		int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
-
-		knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */
-		if (! knew)
-			return -ENOMEM;
-		if (spec->kctl_alloc) {
-			memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc);
-			kfree(spec->kctl_alloc);
-		}
-		spec->kctl_alloc = knew;
-		spec->num_kctl_alloc = num;
-	}
-
-	knew = &spec->kctl_alloc[spec->num_kctl_used];
+	snd_array_init(&spec->kctls, sizeof(*knew), 32);
+	knew = snd_array_new(&spec->kctls);
+	if (!knew)
+		return -ENOMEM;
 	*knew = ad1988_control_templates[type];
 	*knew = ad1988_control_templates[type];
 	knew->name = kstrdup(name, GFP_KERNEL);
 	knew->name = kstrdup(name, GFP_KERNEL);
 	if (! knew->name)
 	if (! knew->name)
 		return -ENOMEM;
 		return -ENOMEM;
 	knew->private_value = val;
 	knew->private_value = val;
-	spec->num_kctl_used++;
 	return 0;
 	return 0;
 }
 }
 
 
@@ -2846,8 +2886,8 @@ static int ad1988_parse_auto_config(struct hda_codec *codec)
 	if (spec->autocfg.dig_in_pin)
 	if (spec->autocfg.dig_in_pin)
 		spec->dig_in_nid = AD1988_SPDIF_IN;
 		spec->dig_in_nid = AD1988_SPDIF_IN;
 
 
-	if (spec->kctl_alloc)
-		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+	if (spec->kctls.list)
+		spec->mixers[spec->num_mixers++] = spec->kctls.list;
 
 
 	spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
 	spec->init_verbs[spec->num_init_verbs++] = ad1988_6stack_init_verbs;
 
 
@@ -3861,6 +3901,7 @@ static const char *ad1884a_models[AD1884A_MODELS] = {
 static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
 static struct snd_pci_quirk ad1884a_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
 	SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
 	SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
 	SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
+	SND_PCI_QUIRK(0x103c, 0x30e6, "HP 6730b", AD1884A_LAPTOP),
 	SND_PCI_QUIRK(0x103c, 0x30e7, "HP EliteBook 8530p", AD1884A_LAPTOP),
 	SND_PCI_QUIRK(0x103c, 0x30e7, "HP EliteBook 8530p", AD1884A_LAPTOP),
 	SND_PCI_QUIRK(0x103c, 0x3614, "HP 6730s", AD1884A_LAPTOP),
 	SND_PCI_QUIRK(0x103c, 0x3614, "HP 6730s", AD1884A_LAPTOP),
 	SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
 	SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
@@ -4267,7 +4308,7 @@ static int patch_ad1882(struct hda_codec *codec)
 /*
 /*
  * patch entries
  * patch entries
  */
  */
-struct hda_codec_preset snd_hda_preset_analog[] = {
+static struct hda_codec_preset snd_hda_preset_analog[] = {
 	{ .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
 	{ .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
 	{ .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
 	{ .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
 	{ .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
 	{ .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
@@ -4285,3 +4326,26 @@ struct hda_codec_preset snd_hda_preset_analog[] = {
 	{ .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
 	{ .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
 	{} /* terminator */
 	{} /* terminator */
 };
 };
+
+MODULE_ALIAS("snd-hda-codec-id:11d4*");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Analog Devices HD-audio codec");
+
+static struct hda_codec_preset_list analog_list = {
+	.preset = snd_hda_preset_analog,
+	.owner = THIS_MODULE,
+};
+
+static int __init patch_analog_init(void)
+{
+	return snd_hda_add_codec_preset(&analog_list);
+}
+
+static void __exit patch_analog_exit(void)
+{
+	snd_hda_delete_codec_preset(&analog_list);
+}
+
+module_init(patch_analog_init)
+module_exit(patch_analog_exit)

+ 33 - 7
sound/pci/hda/patch_atihdmi.c

@@ -27,7 +27,6 @@
 #include <sound/core.h>
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_codec.h"
 #include "hda_local.h"
 #include "hda_local.h"
-#include "hda_patch.h"
 
 
 struct atihdmi_spec {
 struct atihdmi_spec {
 	struct hda_multi_out multiout;
 	struct hda_multi_out multiout;
@@ -187,13 +186,40 @@ static int patch_atihdmi(struct hda_codec *codec)
 /*
 /*
  * patch entries
  * patch entries
  */
  */
-struct hda_codec_preset snd_hda_preset_atihdmi[] = {
-	{ .id = 0x1002793c, .name = "ATI RS600 HDMI", .patch = patch_atihdmi },
-	{ .id = 0x10027919, .name = "ATI RS600 HDMI", .patch = patch_atihdmi },
-	{ .id = 0x1002791a, .name = "ATI RS690/780 HDMI", .patch = patch_atihdmi },
-	{ .id = 0x1002aa01, .name = "ATI R6xx HDMI", .patch = patch_atihdmi },
+static struct hda_codec_preset snd_hda_preset_atihdmi[] = {
+	{ .id = 0x1002793c, .name = "RS600 HDMI", .patch = patch_atihdmi },
+	{ .id = 0x10027919, .name = "RS600 HDMI", .patch = patch_atihdmi },
+	{ .id = 0x1002791a, .name = "RS690/780 HDMI", .patch = patch_atihdmi },
+	{ .id = 0x1002aa01, .name = "R6xx HDMI", .patch = patch_atihdmi },
 	{ .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_atihdmi },
 	{ .id = 0x10951390, .name = "SiI1390 HDMI", .patch = patch_atihdmi },
-	{ .id = 0x10951392, .name = "SiI1392 HDMI", .patch = patch_atihdmi },
 	{ .id = 0x17e80047, .name = "Chrontel HDMI",  .patch = patch_atihdmi },
 	{ .id = 0x17e80047, .name = "Chrontel HDMI",  .patch = patch_atihdmi },
 	{} /* terminator */
 	{} /* terminator */
 };
 };
+
+MODULE_ALIAS("snd-hda-codec-id:1002793c");
+MODULE_ALIAS("snd-hda-codec-id:10027919");
+MODULE_ALIAS("snd-hda-codec-id:1002791a");
+MODULE_ALIAS("snd-hda-codec-id:1002aa01");
+MODULE_ALIAS("snd-hda-codec-id:10951390");
+MODULE_ALIAS("snd-hda-codec-id:17e80047");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ATI HDMI HD-audio codec");
+
+static struct hda_codec_preset_list atihdmi_list = {
+	.preset = snd_hda_preset_atihdmi,
+	.owner = THIS_MODULE,
+};
+
+static int __init patch_atihdmi_init(void)
+{
+	return snd_hda_add_codec_preset(&atihdmi_list);
+}
+
+static void __exit patch_atihdmi_exit(void)
+{
+	snd_hda_delete_codec_preset(&atihdmi_list);
+}
+
+module_init(patch_atihdmi_init)
+module_exit(patch_atihdmi_exit)

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

@@ -28,7 +28,6 @@
 #include <sound/core.h>
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_codec.h"
 #include "hda_local.h"
 #include "hda_local.h"
-#include "hda_patch.h"
 #define NUM_PINS	11
 #define NUM_PINS	11
 
 
 
 
@@ -736,8 +735,32 @@ static int patch_cmi9880(struct hda_codec *codec)
 /*
 /*
  * patch entries
  * patch entries
  */
  */
-struct hda_codec_preset snd_hda_preset_cmedia[] = {
+static struct hda_codec_preset snd_hda_preset_cmedia[] = {
 	{ .id = 0x13f69880, .name = "CMI9880", .patch = patch_cmi9880 },
 	{ .id = 0x13f69880, .name = "CMI9880", .patch = patch_cmi9880 },
  	{ .id = 0x434d4980, .name = "CMI9880", .patch = patch_cmi9880 },
  	{ .id = 0x434d4980, .name = "CMI9880", .patch = patch_cmi9880 },
 	{} /* terminator */
 	{} /* terminator */
 };
 };
+
+MODULE_ALIAS("snd-hda-codec-id:13f69880");
+MODULE_ALIAS("snd-hda-codec-id:434d4980");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("C-Media HD-audio codec");
+
+static struct hda_codec_preset_list cmedia_list = {
+	.preset = snd_hda_preset_cmedia,
+	.owner = THIS_MODULE,
+};
+
+static int __init patch_cmedia_init(void)
+{
+	return snd_hda_add_codec_preset(&cmedia_list);
+}
+
+static void __exit patch_cmedia_exit(void)
+{
+	snd_hda_delete_codec_preset(&cmedia_list);
+}
+
+module_init(patch_cmedia_init)
+module_exit(patch_cmedia_exit)

+ 26 - 13
sound/pci/hda/patch_conexant.c

@@ -27,7 +27,6 @@
 #include <sound/core.h>
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_codec.h"
 #include "hda_local.h"
 #include "hda_local.h"
-#include "hda_patch.h"
 
 
 #define CXT_PIN_DIR_IN              0x00
 #define CXT_PIN_DIR_IN              0x00
 #define CXT_PIN_DIR_OUT             0x01
 #define CXT_PIN_DIR_OUT             0x01
@@ -86,8 +85,6 @@ struct conexant_spec {
 
 
 	/* dynamic controls, init_verbs and input_mux */
 	/* dynamic controls, init_verbs and input_mux */
 	struct auto_pin_cfg autocfg;
 	struct auto_pin_cfg autocfg;
-	unsigned int num_kctl_alloc, num_kctl_used;
-	struct snd_kcontrol_new *kctl_alloc;
 	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];
 
 
@@ -344,15 +341,6 @@ static int conexant_init(struct hda_codec *codec)
 
 
 static void conexant_free(struct hda_codec *codec)
 static void conexant_free(struct hda_codec *codec)
 {
 {
-        struct conexant_spec *spec = codec->spec;
-        unsigned int i;
-
-        if (spec->kctl_alloc) {
-                for (i = 0; i < spec->num_kctl_used; i++)
-                        kfree(spec->kctl_alloc[i].name);
-                kfree(spec->kctl_alloc);
-        }
-
 	kfree(codec->spec);
 	kfree(codec->spec);
 }
 }
 
 
@@ -1782,7 +1770,7 @@ static int patch_cxt5051(struct hda_codec *codec)
 /*
 /*
  */
  */
 
 
-struct hda_codec_preset snd_hda_preset_conexant[] = {
+static struct hda_codec_preset snd_hda_preset_conexant[] = {
 	{ .id = 0x14f15045, .name = "CX20549 (Venice)",
 	{ .id = 0x14f15045, .name = "CX20549 (Venice)",
 	  .patch = patch_cxt5045 },
 	  .patch = patch_cxt5045 },
 	{ .id = 0x14f15047, .name = "CX20551 (Waikiki)",
 	{ .id = 0x14f15047, .name = "CX20551 (Waikiki)",
@@ -1791,3 +1779,28 @@ struct hda_codec_preset snd_hda_preset_conexant[] = {
 	  .patch = patch_cxt5051 },
 	  .patch = patch_cxt5051 },
 	{} /* terminator */
 	{} /* terminator */
 };
 };
+
+MODULE_ALIAS("snd-hda-codec-id:14f15045");
+MODULE_ALIAS("snd-hda-codec-id:14f15047");
+MODULE_ALIAS("snd-hda-codec-id:14f15051");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Conexant HD-audio codec");
+
+static struct hda_codec_preset_list conexant_list = {
+	.preset = snd_hda_preset_conexant,
+	.owner = THIS_MODULE,
+};
+
+static int __init patch_conexant_init(void)
+{
+	return snd_hda_add_codec_preset(&conexant_list);
+}
+
+static void __exit patch_conexant_exit(void)
+{
+	snd_hda_delete_codec_preset(&conexant_list);
+}
+
+module_init(patch_conexant_init)
+module_exit(patch_conexant_exit)

+ 711 - 0
sound/pci/hda/patch_intelhdmi.c

@@ -0,0 +1,711 @@
+/*
+ *
+ *  patch_intelhdmi.c - Patch for Intel HDMI codecs
+ *
+ *  Copyright(c) 2008 Intel Corporation. All rights reserved.
+ *
+ *  Authors:
+ *  			Jiang Zhe <zhe.jiang@intel.com>
+ *  			Wu Fengguang <wfg@linux.intel.com>
+ *
+ *  Maintained by:
+ *  			Wu Fengguang <wfg@linux.intel.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License as published by the Free
+ *  Software Foundation; either version 2 of the License, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ *  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ *  for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software Foundation,
+ *  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+#define CVT_NID		0x02	/* audio converter */
+#define PIN_NID		0x03	/* HDMI output pin */
+
+#define INTEL_HDMI_EVENT_TAG		0x08
+
+struct intel_hdmi_spec {
+	struct hda_multi_out multiout;
+	struct hda_pcm pcm_rec;
+	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 pinout_disable_verb[] = {
+	{PIN_NID, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00},
+	{}
+};
+
+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 {
+	u8 type; /* 0x84 */
+	u8 ver;  /* 0x01 */
+	u8 len;  /* 0x0a */
+
+	u8 checksum;	/* PB0 */
+	u8 CC02_CT47;	/* CC in bits 0:2, CT in 4:7 */
+	u8 SS01_SF24;
+	u8 CXT04;
+	u8 CA;
+	u8 LFEPBL01_LSV36_DM_INH7;
+	u8 reserved[5];	/* PB6 - PB10 */
+};
+
+/*
+ * CEA speaker placement:
+ *
+ *        FLH       FCH        FRH
+ *  FLW    FL  FLC   FC   FRC   FR   FRW
+ *
+ *                                  LFE
+ *                     TC
+ *
+ *          RL  RLC   RC   RRC   RR
+ *
+ * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to
+ * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC.
+ */
+enum cea_speaker_placement {
+	FL  = (1 <<  0),	/* Front Left           */
+	FC  = (1 <<  1),	/* Front Center         */
+	FR  = (1 <<  2),	/* Front Right          */
+	FLC = (1 <<  3),	/* Front Left Center    */
+	FRC = (1 <<  4),	/* Front Right Center   */
+	RL  = (1 <<  5),	/* Rear Left            */
+	RC  = (1 <<  6),	/* Rear Center          */
+	RR  = (1 <<  7),	/* Rear Right           */
+	RLC = (1 <<  8),	/* Rear Left Center     */
+	RRC = (1 <<  9),	/* Rear Right Center    */
+	LFE = (1 << 10),	/* Low Frequency Effect */
+	FLW = (1 << 11),	/* Front Left Wide      */
+	FRW = (1 << 12),	/* Front Right Wide     */
+	FLH = (1 << 13),	/* Front Left High      */
+	FCH = (1 << 14),	/* Front Center High    */
+	FRH = (1 << 15),	/* Front Right High     */
+	TC  = (1 << 16),	/* Top Center           */
+};
+
+/*
+ * ELD SA bits in the CEA Speaker Allocation data block
+ */
+static int eld_speaker_allocation_bits[] = {
+	[0] = FL | FR,
+	[1] = LFE,
+	[2] = FC,
+	[3] = RL | RR,
+	[4] = RC,
+	[5] = FLC | FRC,
+	[6] = RLC | RRC,
+	/* the following are not defined in ELD yet */
+	[7] = FLW | FRW,
+	[8] = FLH | FRH,
+	[9] = TC,
+	[10] = FCH,
+};
+
+struct cea_channel_speaker_allocation {
+	int ca_index;
+	int speakers[8];
+
+	/* derived values, just for convenience */
+	int channels;
+	int spk_mask;
+};
+
+/*
+ * This is an ordered list!
+ *
+ * The preceding ones have better chances to be selected by
+ * hdmi_setup_channel_allocation().
+ */
+static struct cea_channel_speaker_allocation channel_allocations[] = {
+/* 			  channel:   8     7    6    5    4     3    2    1  */
+{ .ca_index = 0x00,  .speakers = {   0,    0,   0,   0,   0,    0,  FR,  FL } },
+				 /* 2.1 */
+{ .ca_index = 0x01,  .speakers = {   0,    0,   0,   0,   0,  LFE,  FR,  FL } },
+				 /* Dolby Surround */
+{ .ca_index = 0x02,  .speakers = {   0,    0,   0,   0,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x03,  .speakers = {   0,    0,   0,   0,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x04,  .speakers = {   0,    0,   0,  RC,   0,    0,  FR,  FL } },
+{ .ca_index = 0x05,  .speakers = {   0,    0,   0,  RC,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x06,  .speakers = {   0,    0,   0,  RC,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x07,  .speakers = {   0,    0,   0,  RC,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x08,  .speakers = {   0,    0,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x09,  .speakers = {   0,    0,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x0a,  .speakers = {   0,    0,  RR,  RL,  FC,    0,  FR,  FL } },
+				 /* 5.1 */
+{ .ca_index = 0x0b,  .speakers = {   0,    0,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x0c,  .speakers = {   0,   RC,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x0d,  .speakers = {   0,   RC,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x0e,  .speakers = {   0,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
+				 /* 6.1 */
+{ .ca_index = 0x0f,  .speakers = {   0,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x10,  .speakers = { RRC,  RLC,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x11,  .speakers = { RRC,  RLC,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x12,  .speakers = { RRC,  RLC,  RR,  RL,  FC,    0,  FR,  FL } },
+				 /* 7.1 */
+{ .ca_index = 0x13,  .speakers = { RRC,  RLC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x14,  .speakers = { FRC,  FLC,   0,   0,   0,    0,  FR,  FL } },
+{ .ca_index = 0x15,  .speakers = { FRC,  FLC,   0,   0,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x16,  .speakers = { FRC,  FLC,   0,   0,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x17,  .speakers = { FRC,  FLC,   0,   0,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x18,  .speakers = { FRC,  FLC,   0,  RC,   0,    0,  FR,  FL } },
+{ .ca_index = 0x19,  .speakers = { FRC,  FLC,   0,  RC,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x1a,  .speakers = { FRC,  FLC,   0,  RC,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x1b,  .speakers = { FRC,  FLC,   0,  RC,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x1c,  .speakers = { FRC,  FLC,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x1d,  .speakers = { FRC,  FLC,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x1e,  .speakers = { FRC,  FLC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x1f,  .speakers = { FRC,  FLC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x20,  .speakers = {   0,  FCH,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x21,  .speakers = {   0,  FCH,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x22,  .speakers = {  TC,    0,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x23,  .speakers = {  TC,    0,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x24,  .speakers = { FRH,  FLH,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x25,  .speakers = { FRH,  FLH,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x26,  .speakers = { FRW,  FLW,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x27,  .speakers = { FRW,  FLW,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x28,  .speakers = {  TC,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x29,  .speakers = {  TC,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x2a,  .speakers = { FCH,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x2b,  .speakers = { FCH,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x2c,  .speakers = {  TC,  FCH,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x2d,  .speakers = {  TC,  FCH,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x2e,  .speakers = { FRH,  FLH,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x2f,  .speakers = { FRH,  FLH,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x30,  .speakers = { FRW,  FLW,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x31,  .speakers = { FRW,  FLW,  RR,  RL,  FC,  LFE,  FR,  FL } },
+};
+
+/*
+ * HDMI routines
+ */
+
+#ifdef BE_PARANOID
+static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t nid,
+				int *packet_index, int *byte_index)
+{
+	int val;
+
+	val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_INDEX, 0);
+
+	*packet_index = val >> 5;
+	*byte_index = val & 0x1f;
+}
+#endif
+
+static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t nid,
+				int packet_index, int byte_index)
+{
+	int val;
+
+	val = (packet_index << 5) | (byte_index & 0x1f);
+
+	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val);
+}
+
+static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t nid,
+				unsigned char val)
+{
+	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val);
+}
+
+static void hdmi_enable_output(struct hda_codec *codec)
+{
+	/* Enable Audio InfoFrame Transmission */
+	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);
+	/* Unmute */
+	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);
+	/* Enable pin out */
+	snd_hda_sequence_write(codec, pinout_enable_verb);
+}
+
+static void hdmi_disable_output(struct hda_codec *codec)
+{
+	snd_hda_sequence_write(codec, pinout_disable_verb);
+	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_MUTE);
+
+	/*
+	 * FIXME: noises may arise when playing music after reloading the
+	 * kernel module, until the next X restart or monitor repower.
+	 */
+}
+
+static int hdmi_get_channel_count(struct hda_codec *codec)
+{
+	return 1 + snd_hda_codec_read(codec, CVT_NID, 0,
+					AC_VERB_GET_CVT_CHAN_COUNT, 0);
+}
+
+static void hdmi_set_channel_count(struct hda_codec *codec, int chs)
+{
+	snd_hda_codec_write(codec, CVT_NID, 0,
+					AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
+
+	if (chs != hdmi_get_channel_count(codec))
+		snd_printd(KERN_INFO "HDMI channel count: expect %d, get %d\n",
+					chs, hdmi_get_channel_count(codec));
+}
+
+static void hdmi_debug_channel_mapping(struct hda_codec *codec)
+{
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+	int i;
+	int slot;
+
+	for (i = 0; i < 8; i++) {
+		slot = snd_hda_codec_read(codec, CVT_NID, 0,
+						AC_VERB_GET_HDMI_CHAN_SLOT, i);
+		printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
+						slot >> 4, slot & 0x7);
+	}
+#endif
+}
+
+static void hdmi_parse_eld(struct hda_codec *codec)
+{
+	struct intel_hdmi_spec *spec = codec->spec;
+	struct hdmi_eld *eld = &spec->sink_eld;
+
+	if (!snd_hdmi_get_eld(eld, codec, PIN_NID))
+		snd_hdmi_show_eld(eld);
+}
+
+
+/*
+ * Audio InfoFrame routines
+ */
+
+static void hdmi_debug_dip_size(struct hda_codec *codec)
+{
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+	int i;
+	int size;
+
+	size = snd_hdmi_get_eld_size(codec, PIN_NID);
+	printk(KERN_DEBUG "HDMI: ELD buf size is %d\n", size);
+
+	for (i = 0; i < 8; i++) {
+		size = snd_hda_codec_read(codec, PIN_NID, 0,
+						AC_VERB_GET_HDMI_DIP_SIZE, i);
+		printk(KERN_DEBUG "HDMI: DIP GP[%d] buf size is %d\n", i, size);
+	}
+#endif
+}
+
+static void hdmi_clear_dip_buffers(struct hda_codec *codec)
+{
+#ifdef BE_PARANOID
+	int i, j;
+	int size;
+	int pi, bi;
+	for (i = 0; i < 8; i++) {
+		size = snd_hda_codec_read(codec, PIN_NID, 0,
+						AC_VERB_GET_HDMI_DIP_SIZE, i);
+		if (size == 0)
+			continue;
+
+		hdmi_set_dip_index(codec, PIN_NID, i, 0x0);
+		for (j = 1; j < 1000; j++) {
+			hdmi_write_dip_byte(codec, PIN_NID, 0x0);
+			hdmi_get_dip_index(codec, PIN_NID, &pi, &bi);
+			if (pi != i)
+				snd_printd(KERN_INFO "dip index %d: %d != %d\n",
+						bi, pi, i);
+			if (bi == 0) /* byte index wrapped around */
+				break;
+		}
+		snd_printd(KERN_INFO
+			"HDMI: DIP GP[%d] buf reported size=%d, written=%d\n",
+			i, size, j);
+	}
+#endif
+}
+
+static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
+					struct hdmi_audio_infoframe *ai)
+{
+	u8 *params = (u8 *)ai;
+	int i;
+
+	hdmi_debug_dip_size(codec);
+	hdmi_clear_dip_buffers(codec); /* be paranoid */
+
+	hdmi_set_dip_index(codec, PIN_NID, 0x0, 0x0);
+	for (i = 0; i < sizeof(ai); i++)
+		hdmi_write_dip_byte(codec, PIN_NID, params[i]);
+}
+
+/*
+ * Compute derived values in channel_allocations[].
+ */
+static void init_channel_allocations(void)
+{
+	int i, j;
+	struct cea_channel_speaker_allocation *p;
+
+	for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+		p = channel_allocations + i;
+		p->channels = 0;
+		p->spk_mask = 0;
+		for (j = 0; j < ARRAY_SIZE(p->speakers); j++)
+			if (p->speakers[j]) {
+				p->channels++;
+				p->spk_mask |= p->speakers[j];
+			}
+	}
+}
+
+/*
+ * The transformation takes two steps:
+ *
+ * 	eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask
+ * 	      spk_mask => (channel_allocations[])         => ai->CA
+ *
+ * TODO: it could select the wrong CA from multiple candidates.
+*/
+static int hdmi_setup_channel_allocation(struct hda_codec *codec,
+					 struct hdmi_audio_infoframe *ai)
+{
+	struct intel_hdmi_spec *spec = codec->spec;
+	struct hdmi_eld *eld = &spec->sink_eld;
+	int i;
+	int spk_mask = 0;
+	int channels = 1 + (ai->CC02_CT47 & 0x7);
+	char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
+
+	/*
+	 * CA defaults to 0 for basic stereo audio
+	 */
+	if (!eld->eld_ver)
+		return 0;
+	if (!eld->spk_alloc)
+		return 0;
+	if (channels <= 2)
+		return 0;
+
+	/*
+	 * expand ELD's speaker allocation mask
+	 *
+	 * ELD tells the speaker mask in a compact(paired) form,
+	 * expand ELD's notions to match the ones used by Audio InfoFrame.
+	 */
+	for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
+		if (eld->spk_alloc & (1 << i))
+			spk_mask |= eld_speaker_allocation_bits[i];
+	}
+
+	/* search for the first working match in the CA table */
+	for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+		if (channels == channel_allocations[i].channels &&
+		    (spk_mask & channel_allocations[i].spk_mask) ==
+				channel_allocations[i].spk_mask) {
+			ai->CA = channel_allocations[i].ca_index;
+			break;
+		}
+	}
+
+	snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf));
+	snd_printdd(KERN_INFO
+			"HDMI: select CA 0x%x for %d-channel allocation: %s\n",
+			ai->CA, channels, buf);
+
+	return ai->CA;
+}
+
+static void hdmi_setup_channel_mapping(struct hda_codec *codec,
+					struct hdmi_audio_infoframe *ai)
+{
+	if (!ai->CA)
+		return;
+
+	/*
+	 * TODO: adjust channel mapping if necessary
+	 * ALSA sequence is front/surr/clfe/side?
+	 */
+
+	snd_hda_sequence_write(codec, def_chan_map);
+	hdmi_debug_channel_mapping(codec);
+}
+
+
+static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
+					struct snd_pcm_substream *substream)
+{
+	struct hdmi_audio_infoframe ai = {
+		.type		= 0x84,
+		.ver		= 0x01,
+		.len		= 0x0a,
+		.CC02_CT47	= substream->runtime->channels - 1,
+	};
+
+	hdmi_setup_channel_allocation(codec, &ai);
+	hdmi_setup_channel_mapping(codec, &ai);
+
+	hdmi_fill_audio_infoframe(codec, &ai);
+}
+
+
+/*
+ * Unsolicited events
+ */
+
+static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
+{
+	int pind = !!(res & AC_UNSOL_RES_PD);
+	int eldv = !!(res & AC_UNSOL_RES_ELDV);
+
+	printk(KERN_INFO
+		"HDMI hot plug event: Presence_Detect=%d ELD_Valid=%d\n",
+		pind, eldv);
+
+	if (pind && eldv) {
+		hdmi_parse_eld(codec);
+		/* TODO: do real things about ELD */
+	}
+}
+
+static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
+{
+	int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
+	int cp_state = !!(res & AC_UNSOL_RES_CP_STATE);
+	int cp_ready = !!(res & AC_UNSOL_RES_CP_READY);
+
+	printk(KERN_INFO
+		"HDMI content protection event: SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
+		subtag,
+		cp_state,
+		cp_ready);
+
+	/* TODO */
+	if (cp_state)
+		;
+	if (cp_ready)
+		;
+}
+
+
+static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+	int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
+	int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
+
+	if (tag != INTEL_HDMI_EVENT_TAG) {
+		snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag);
+		return;
+	}
+
+	if (subtag == 0)
+		hdmi_intrinsic_event(codec, res);
+	else
+		hdmi_non_intrinsic_event(codec, res);
+}
+
+/*
+ * Callbacks
+ */
+
+static int intel_hdmi_playback_pcm_open(struct hda_pcm_stream *hinfo,
+					struct hda_codec *codec,
+					struct snd_pcm_substream *substream)
+{
+	struct intel_hdmi_spec *spec = codec->spec;
+
+	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int intel_hdmi_playback_pcm_close(struct hda_pcm_stream *hinfo,
+					 struct hda_codec *codec,
+					 struct snd_pcm_substream *substream)
+{
+	struct intel_hdmi_spec *spec = codec->spec;
+
+	hdmi_disable_output(codec);
+
+	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static int intel_hdmi_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 intel_hdmi_spec *spec = codec->spec;
+
+	snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
+					     format, substream);
+
+	hdmi_set_channel_count(codec, substream->runtime->channels);
+
+	hdmi_setup_audio_infoframe(codec, substream);
+
+	hdmi_enable_output(codec);
+
+	return 0;
+}
+
+static struct hda_pcm_stream intel_hdmi_pcm_playback = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 8,
+	.nid = CVT_NID, /* NID to query formats and rates and setup streams */
+	.ops = {
+		.open    = intel_hdmi_playback_pcm_open,
+		.close   = intel_hdmi_playback_pcm_close,
+		.prepare = intel_hdmi_playback_pcm_prepare
+	},
+};
+
+static int intel_hdmi_build_pcms(struct hda_codec *codec)
+{
+	struct intel_hdmi_spec *spec = codec->spec;
+	struct hda_pcm *info = &spec->pcm_rec;
+
+	codec->num_pcms = 1;
+	codec->pcm_info = info;
+
+	info->name = "INTEL HDMI";
+	info->pcm_type = HDA_PCM_TYPE_HDMI;
+	info->stream[SNDRV_PCM_STREAM_PLAYBACK] = intel_hdmi_pcm_playback;
+
+	return 0;
+}
+
+static int intel_hdmi_build_controls(struct hda_codec *codec)
+{
+	struct intel_hdmi_spec *spec = codec->spec;
+	int err;
+
+	err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+static int intel_hdmi_init(struct hda_codec *codec)
+{
+	/* disable audio output as early as possible */
+	hdmi_disable_output(codec);
+
+	snd_hda_sequence_write(codec, unsolicited_response_verb);
+
+	return 0;
+}
+
+static void intel_hdmi_free(struct hda_codec *codec)
+{
+	struct intel_hdmi_spec *spec = codec->spec;
+
+	snd_hda_eld_proc_free(codec, &spec->sink_eld);
+	kfree(spec);
+}
+
+static struct hda_codec_ops intel_hdmi_patch_ops = {
+	.init			= intel_hdmi_init,
+	.free			= intel_hdmi_free,
+	.build_pcms		= intel_hdmi_build_pcms,
+	.build_controls 	= intel_hdmi_build_controls,
+	.unsol_event		= intel_hdmi_unsol_event,
+};
+
+static int patch_intel_hdmi(struct hda_codec *codec)
+{
+	struct intel_hdmi_spec *spec;
+
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	spec->multiout.num_dacs = 0;	  /* no analog */
+	spec->multiout.max_channels = 8;
+	spec->multiout.dig_out_nid = CVT_NID;
+
+	codec->spec = spec;
+	codec->patch_ops = intel_hdmi_patch_ops;
+
+	snd_hda_eld_proc_new(codec, &spec->sink_eld);
+
+	init_channel_allocations();
+
+	return 0;
+}
+
+static struct hda_codec_preset snd_hda_preset_intelhdmi[] = {
+	{ .id = 0x808629fb, .name = "G45 DEVCL",  .patch = patch_intel_hdmi },
+	{ .id = 0x80862801, .name = "G45 DEVBLC", .patch = patch_intel_hdmi },
+	{ .id = 0x80862802, .name = "G45 DEVCTG", .patch = patch_intel_hdmi },
+	{ .id = 0x80862803, .name = "G45 DEVELK", .patch = patch_intel_hdmi },
+	{ .id = 0x10951392, .name = "SiI1392 HDMI",     .patch = patch_intel_hdmi },
+	{} /* terminator */
+};
+
+MODULE_ALIAS("snd-hda-codec-id:808629fb");
+MODULE_ALIAS("snd-hda-codec-id:80862801");
+MODULE_ALIAS("snd-hda-codec-id:80862802");
+MODULE_ALIAS("snd-hda-codec-id:80862803");
+MODULE_ALIAS("snd-hda-codec-id:10951392");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Intel HDMI HD-audio codec");
+
+static struct hda_codec_preset_list intel_list = {
+	.preset = snd_hda_preset_intelhdmi,
+	.owner = THIS_MODULE,
+};
+
+static int __init patch_intelhdmi_init(void)
+{
+	return snd_hda_add_codec_preset(&intel_list);
+}
+
+static void __exit patch_intelhdmi_exit(void)
+{
+	snd_hda_delete_codec_preset(&intel_list);
+}
+
+module_init(patch_intelhdmi_init)
+module_exit(patch_intelhdmi_exit)

+ 29 - 3
sound/pci/hda/patch_nvhdmi.c

@@ -158,8 +158,34 @@ static int patch_nvhdmi(struct hda_codec *codec)
 /*
 /*
  * patch entries
  * patch entries
  */
  */
-struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
-	{ .id = 0x10de0002, .name = "NVIDIA MCP78 HDMI", .patch = patch_nvhdmi },
-	{ .id = 0x10de0007, .name = "NVIDIA MCP7A HDMI", .patch = patch_nvhdmi },
+static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
+	{ .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi },
+	{ .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi },
+	{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi },
 	{} /* terminator */
 	{} /* terminator */
 };
 };
+
+MODULE_ALIAS("snd-hda-codec-id:10de0002");
+MODULE_ALIAS("snd-hda-codec-id:10de0007");
+MODULE_ALIAS("snd-hda-codec-id:10de0067");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Nvidia HDMI HD-audio codec");
+
+static struct hda_codec_preset_list nvhdmi_list = {
+	.preset = snd_hda_preset_nvhdmi,
+	.owner = THIS_MODULE,
+};
+
+static int __init patch_nvhdmi_init(void)
+{
+	return snd_hda_add_codec_preset(&nvhdmi_list);
+}
+
+static void __exit patch_nvhdmi_exit(void)
+{
+	snd_hda_delete_codec_preset(&nvhdmi_list);
+}
+
+module_init(patch_nvhdmi_init)
+module_exit(patch_nvhdmi_exit)

+ 932 - 749
sound/pci/hda/patch_realtek.c

@@ -30,7 +30,6 @@
 #include <sound/core.h>
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_codec.h"
 #include "hda_local.h"
 #include "hda_local.h"
-#include "hda_patch.h"
 
 
 #define ALC880_FRONT_EVENT		0x01
 #define ALC880_FRONT_EVENT		0x01
 #define ALC880_DCVOL_EVENT		0x02
 #define ALC880_DCVOL_EVENT		0x02
@@ -114,6 +113,7 @@ enum {
 	ALC268_3ST,
 	ALC268_3ST,
 	ALC268_TOSHIBA,
 	ALC268_TOSHIBA,
 	ALC268_ACER,
 	ALC268_ACER,
+	ALC268_ACER_DMIC,
 	ALC268_ACER_ASPIRE_ONE,
 	ALC268_ACER_ASPIRE_ONE,
 	ALC268_DELL,
 	ALC268_DELL,
 	ALC268_ZEPTO,
 	ALC268_ZEPTO,
@@ -130,6 +130,8 @@ enum {
 	ALC269_QUANTA_FL1,
 	ALC269_QUANTA_FL1,
 	ALC269_ASUS_EEEPC_P703,
 	ALC269_ASUS_EEEPC_P703,
 	ALC269_ASUS_EEEPC_P901,
 	ALC269_ASUS_EEEPC_P901,
+	ALC269_FUJITSU,
+	ALC269_LIFEBOOK,
 	ALC269_AUTO,
 	ALC269_AUTO,
 	ALC269_MODEL_LAST /* last tag */
 	ALC269_MODEL_LAST /* last tag */
 };
 };
@@ -152,6 +154,7 @@ enum {
 enum {
 enum {
 	ALC660VD_3ST,
 	ALC660VD_3ST,
 	ALC660VD_3ST_DIG,
 	ALC660VD_3ST_DIG,
+	ALC660VD_ASUS_V1S,
 	ALC861VD_3ST,
 	ALC861VD_3ST,
 	ALC861VD_3ST_DIG,
 	ALC861VD_3ST_DIG,
 	ALC861VD_6ST_DIG,
 	ALC861VD_6ST_DIG,
@@ -212,6 +215,7 @@ enum {
 	ALC883_TARGA_2ch_DIG,
 	ALC883_TARGA_2ch_DIG,
 	ALC883_ACER,
 	ALC883_ACER,
 	ALC883_ACER_ASPIRE,
 	ALC883_ACER_ASPIRE,
+	ALC888_ACER_ASPIRE_4930G,
 	ALC883_MEDION,
 	ALC883_MEDION,
 	ALC883_MEDION_MD2,
 	ALC883_MEDION_MD2,
 	ALC883_LAPTOP_EAPD,
 	ALC883_LAPTOP_EAPD,
@@ -225,9 +229,11 @@ enum {
 	ALC883_MITAC,
 	ALC883_MITAC,
 	ALC883_CLEVO_M720,
 	ALC883_CLEVO_M720,
 	ALC883_FUJITSU_PI2515,
 	ALC883_FUJITSU_PI2515,
+	ALC888_FUJITSU_XA3530,
 	ALC883_3ST_6ch_INTEL,
 	ALC883_3ST_6ch_INTEL,
 	ALC888_ASUS_M90V,
 	ALC888_ASUS_M90V,
 	ALC888_ASUS_EEE1601,
 	ALC888_ASUS_EEE1601,
+	ALC1200_ASUS_P5Q,
 	ALC883_AUTO,
 	ALC883_AUTO,
 	ALC883_MODEL_LAST,
 	ALC883_MODEL_LAST,
 };
 };
@@ -239,6 +245,7 @@ struct alc_spec {
 	/* codec parameterization */
 	/* codec parameterization */
 	struct snd_kcontrol_new *mixers[5];	/* mixer arrays */
 	struct snd_kcontrol_new *mixers[5];	/* mixer arrays */
 	unsigned int num_mixers;
 	unsigned int num_mixers;
+	struct snd_kcontrol_new *cap_mixer;	/* capture mixer */
 
 
 	const struct hda_verb *init_verbs[5];	/* initialization verbs
 	const struct hda_verb *init_verbs[5];	/* initialization verbs
 						 * don't forget NULL
 						 * don't forget NULL
@@ -268,6 +275,7 @@ struct alc_spec {
 	hda_nid_t *adc_nids;
 	hda_nid_t *adc_nids;
 	hda_nid_t *capsrc_nids;
 	hda_nid_t *capsrc_nids;
 	hda_nid_t dig_in_nid;		/* digital-in NID; optional */
 	hda_nid_t dig_in_nid;		/* digital-in NID; optional */
+	unsigned char is_mix_capture;	/* matrix-style capture (non-mux) */
 
 
 	/* capture source */
 	/* capture source */
 	unsigned int num_mux_defs;
 	unsigned int num_mux_defs;
@@ -284,8 +292,7 @@ struct alc_spec {
 
 
 	/* dynamic controls, init_verbs and input_mux */
 	/* dynamic controls, init_verbs and input_mux */
 	struct auto_pin_cfg autocfg;
 	struct auto_pin_cfg autocfg;
-	unsigned int num_kctl_alloc, num_kctl_used;
-	struct snd_kcontrol_new *kctl_alloc;
+	struct snd_array kctls;
 	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];
 
 
@@ -323,6 +330,7 @@ struct alc_config_preset {
 	struct snd_kcontrol_new *mixers[5]; /* should be identical size
 	struct snd_kcontrol_new *mixers[5]; /* should be identical size
 					     * with spec
 					     * with spec
 					     */
 					     */
+	struct snd_kcontrol_new *cap_mixer; /* capture mixer */
 	const struct hda_verb *init_verbs[5];
 	const struct hda_verb *init_verbs[5];
 	unsigned int num_dacs;
 	unsigned int num_dacs;
 	hda_nid_t *dac_nids;
 	hda_nid_t *dac_nids;
@@ -375,14 +383,39 @@ static int alc_mux_enum_put(struct snd_kcontrol *kcontrol,
 {
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
+	const struct hda_input_mux *imux;
 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-	unsigned int mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx;
+	unsigned int mux_idx;
 	hda_nid_t nid = spec->capsrc_nids ?
 	hda_nid_t nid = spec->capsrc_nids ?
 		spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx];
 		spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx];
-	return snd_hda_input_mux_put(codec, &spec->input_mux[mux_idx], ucontrol,
-				     nid, &spec->cur_mux[adc_idx]);
-}
 
 
+	mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx;
+	imux = &spec->input_mux[mux_idx];
+
+	if (spec->is_mix_capture) {
+		/* Matrix-mixer style (e.g. ALC882) */
+		unsigned int *cur_val = &spec->cur_mux[adc_idx];
+		unsigned int i, idx;
+
+		idx = ucontrol->value.enumerated.item[0];
+		if (idx >= imux->num_items)
+			idx = imux->num_items - 1;
+		if (*cur_val == idx)
+			return 0;
+		for (i = 0; i < imux->num_items; i++) {
+			unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE;
+			snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT,
+						 imux->items[i].index,
+						 HDA_AMP_MUTE, v);
+		}
+		*cur_val = idx;
+		return 1;
+	} else {
+		/* MUX style (e.g. ALC880) */
+		return snd_hda_input_mux_put(codec, imux, ucontrol, nid,
+					     &spec->cur_mux[adc_idx]);
+	}
+}
 
 
 /*
 /*
  * channel mode setting
  * channel mode setting
@@ -716,6 +749,43 @@ static int alc_eapd_ctrl_put(struct snd_kcontrol *kcontrol,
 	  .private_value = nid | (mask<<16) }
 	  .private_value = nid | (mask<<16) }
 #endif   /* CONFIG_SND_DEBUG */
 #endif   /* CONFIG_SND_DEBUG */
 
 
+/*
+ */
+static void add_mixer(struct alc_spec *spec, struct snd_kcontrol_new *mix)
+{
+	if (snd_BUG_ON(spec->num_mixers >= ARRAY_SIZE(spec->mixers)))
+		return;
+	spec->mixers[spec->num_mixers++] = mix;
+}
+
+static void add_verb(struct alc_spec *spec, const struct hda_verb *verb)
+{
+	if (snd_BUG_ON(spec->num_init_verbs >= ARRAY_SIZE(spec->init_verbs)))
+		return;
+	spec->init_verbs[spec->num_init_verbs++] = verb;
+}
+
+#ifdef CONFIG_PROC_FS
+/*
+ * hook for proc
+ */
+static void print_realtek_coef(struct snd_info_buffer *buffer,
+			       struct hda_codec *codec, hda_nid_t nid)
+{
+	int coeff;
+
+	if (nid != 0x20)
+		return;
+	coeff = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PROC_COEF, 0);
+	snd_iprintf(buffer, "  Processing Coefficient: 0x%02x\n", coeff);
+	coeff = snd_hda_codec_read(codec, nid, 0,
+				   AC_VERB_GET_COEF_INDEX, 0);
+	snd_iprintf(buffer, "  Coefficient Index: 0x%02x\n", coeff);
+}
+#else
+#define print_realtek_coef	NULL
+#endif
+
 /*
 /*
  * set up from the preset table
  * set up from the preset table
  */
  */
@@ -725,11 +795,11 @@ static void setup_preset(struct alc_spec *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++)
-		spec->mixers[spec->num_mixers++] = preset->mixers[i];
+		add_mixer(spec, preset->mixers[i]);
+	spec->cap_mixer = preset->cap_mixer;
 	for (i = 0; i < ARRAY_SIZE(preset->init_verbs) && preset->init_verbs[i];
 	for (i = 0; i < ARRAY_SIZE(preset->init_verbs) && preset->init_verbs[i];
 	     i++)
 	     i++)
-		spec->init_verbs[spec->num_init_verbs++] =
-			preset->init_verbs[i];
+		add_verb(spec, preset->init_verbs[i]);
 
 
 	spec->channel_mode = preset->channel_mode;
 	spec->channel_mode = preset->channel_mode;
 	spec->num_channel_mode = preset->num_channel_mode;
 	spec->num_channel_mode = preset->num_channel_mode;
@@ -1106,6 +1176,226 @@ static void alc_fix_pincfg(struct hda_codec *codec,
 	}
 	}
 }
 }
 
 
+/*
+ * ALC888
+ */
+
+/*
+ * 2ch mode
+ */
+static struct hda_verb alc888_4ST_ch2_intel_init[] = {
+/* Mic-in jack as mic in */
+	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+/* Line-in jack as Line in */
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+/* Line-Out as Front */
+	{ 0x17, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{ } /* end */
+};
+
+/*
+ * 4ch mode
+ */
+static struct hda_verb alc888_4ST_ch4_intel_init[] = {
+/* Mic-in jack as mic in */
+	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+/* Line-in jack as Surround */
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+/* Line-Out as Front */
+	{ 0x17, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{ } /* end */
+};
+
+/*
+ * 6ch mode
+ */
+static struct hda_verb alc888_4ST_ch6_intel_init[] = {
+/* Mic-in jack as CLFE */
+	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+/* Line-in jack as Surround */
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+/* Line-Out as CLFE (workaround because Mic-in is not loud enough) */
+	{ 0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
+	{ } /* end */
+};
+
+/*
+ * 8ch mode
+ */
+static struct hda_verb alc888_4ST_ch8_intel_init[] = {
+/* Mic-in jack as CLFE */
+	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+/* Line-in jack as Surround */
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+/* Line-Out as Side */
+	{ 0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
+	{ } /* end */
+};
+
+static struct hda_channel_mode alc888_4ST_8ch_intel_modes[4] = {
+	{ 2, alc888_4ST_ch2_intel_init },
+	{ 4, alc888_4ST_ch4_intel_init },
+	{ 6, alc888_4ST_ch6_intel_init },
+	{ 8, alc888_4ST_ch8_intel_init },
+};
+
+/*
+ * ALC888 Fujitsu Siemens Amillo xa3530
+ */
+
+static struct hda_verb alc888_fujitsu_xa3530_verbs[] = {
+/* Front Mic: set to PIN_IN (empty by default) */
+	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+/* Connect Internal HP to Front */
+	{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},
+/* Connect Bass HP to Front */
+	{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, 0x00},
+/* Connect Line-Out side jack (SPDIF) to Side */
+	{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},
+/* Connect Mic jack to CLFE */
+	{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},
+/* Connect Line-in jack to Surround */
+	{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},
+/* Connect HP out jack to Front */
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
+/* Enable unsolicited event for HP jack and Line-out jack */
+	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+	{0x17, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+	{}
+};
+
+static void alc888_fujitsu_xa3530_automute(struct hda_codec *codec)
+{
+	unsigned int present;
+	unsigned int bits;
+	/* Line out presence */
+	present = snd_hda_codec_read(codec, 0x17, 0,
+				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+	/* HP out presence */
+	present = present || snd_hda_codec_read(codec, 0x1b, 0,
+				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+	bits = present ? HDA_AMP_MUTE : 0;
+	/* Toggle internal speakers muting */
+	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+				 HDA_AMP_MUTE, bits);
+	/* Toggle internal bass muting */
+	snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
+				 HDA_AMP_MUTE, bits);
+}
+
+static void alc888_fujitsu_xa3530_unsol_event(struct hda_codec *codec,
+		unsigned int res)
+{
+	if (res >> 26 == ALC880_HP_EVENT)
+		alc888_fujitsu_xa3530_automute(codec);
+}
+
+
+/*
+ * ALC888 Acer Aspire 4930G model
+ */
+
+static struct hda_verb alc888_acer_aspire_4930g_verbs[] = {
+/* Front Mic: set to PIN_IN (empty by default) */
+	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+/* Unselect Front Mic by default in input mixer 3 */
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)},
+/* Enable unsolicited event for HP jack */
+	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+/* Connect Internal HP to front */
+	{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},
+/* Connect HP out to front */
+	{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, 0x00},
+	{ }
+};
+
+static struct hda_input_mux alc888_2_capture_sources[2] = {
+	/* Front mic only available on one ADC */
+	{
+		.num_items = 4,
+		.items = {
+			{ "Mic", 0x0 },
+			{ "Line", 0x2 },
+			{ "CD", 0x4 },
+			{ "Front Mic", 0xb },
+		},
+	},
+	{
+		.num_items = 3,
+		.items = {
+			{ "Mic", 0x0 },
+			{ "Line", 0x2 },
+			{ "CD", 0x4 },
+		},
+	}
+};
+
+static struct snd_kcontrol_new alc888_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_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("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+	{ } /* end */
+};
+
+static void alc888_acer_aspire_4930g_automute(struct hda_codec *codec)
+{
+	unsigned int present;
+	unsigned int bits;
+	present = snd_hda_codec_read(codec, 0x15, 0,
+				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+	bits = present ? HDA_AMP_MUTE : 0;
+	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
+				 HDA_AMP_MUTE, bits);
+}
+
+static void alc888_acer_aspire_4930g_unsol_event(struct hda_codec *codec,
+		unsigned int res)
+{
+	if (res >> 26 == ALC880_HP_EVENT)
+		alc888_acer_aspire_4930g_automute(codec);
+}
+
 /*
 /*
  * ALC880 3-stack model
  * ALC880 3-stack model
  *
  *
@@ -1205,49 +1495,126 @@ static struct snd_kcontrol_new alc880_three_stack_mixer[] = {
 };
 };
 
 
 /* capture mixer elements */
 /* capture mixer elements */
-static struct snd_kcontrol_new alc880_capture_mixer[] = {
-	HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		/* The multiple "Capture Source" controls confuse alsamixer
-		 * So call somewhat different..
-		 */
-		/* .name = "Capture Source", */
-		.name = "Input Source",
-		.count = 3,
-		.info = alc_mux_enum_info,
-		.get = alc_mux_enum_get,
-		.put = alc_mux_enum_put,
-	},
-	{ } /* end */
-};
+static int alc_cap_vol_info(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_info *uinfo)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct alc_spec *spec = codec->spec;
+	int err;
 
 
-/* capture mixer elements (in case NID 0x07 not available) */
-static struct snd_kcontrol_new alc880_capture_alt_mixer[] = {
-	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
-	{
-		.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,
-	},
-	{ } /* end */
-};
+	mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */
+	kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0,
+						      HDA_INPUT);
+	err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo);
+	mutex_unlock(&codec->spdif_mutex); /* reuse spdif_mutex */
+	return err;
+}
+
+static int alc_cap_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+			   unsigned int size, unsigned int __user *tlv)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct alc_spec *spec = codec->spec;
+	int err;
+
+	mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */
+	kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0,
+						      HDA_INPUT);
+	err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv);
+	mutex_unlock(&codec->spdif_mutex); /* reuse spdif_mutex */
+	return err;
+}
+
+typedef int (*getput_call_t)(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol);
+
+static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol,
+				 getput_call_t func)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct alc_spec *spec = codec->spec;
+	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+	int err;
+
+	mutex_lock(&codec->spdif_mutex); /* reuse spdif_mutex */
+	kcontrol->private_value = HDA_COMPOSE_AMP_VAL(spec->adc_nids[adc_idx],
+						      3, 0, HDA_INPUT);
+	err = func(kcontrol, ucontrol);
+	mutex_unlock(&codec->spdif_mutex); /* reuse spdif_mutex */
+	return err;
+}
 
 
+static int alc_cap_vol_get(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	return alc_cap_getput_caller(kcontrol, ucontrol,
+				     snd_hda_mixer_amp_volume_get);
+}
+
+static int alc_cap_vol_put(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	return alc_cap_getput_caller(kcontrol, ucontrol,
+				     snd_hda_mixer_amp_volume_put);
+}
+
+/* capture mixer elements */
+#define alc_cap_sw_info		snd_ctl_boolean_stereo_info
+
+static int alc_cap_sw_get(struct snd_kcontrol *kcontrol,
+			  struct snd_ctl_elem_value *ucontrol)
+{
+	return alc_cap_getput_caller(kcontrol, ucontrol,
+				     snd_hda_mixer_amp_switch_get);
+}
+
+static int alc_cap_sw_put(struct snd_kcontrol *kcontrol,
+			  struct snd_ctl_elem_value *ucontrol)
+{
+	return alc_cap_getput_caller(kcontrol, ucontrol,
+				     snd_hda_mixer_amp_switch_put);
+}
+
+#define DEFINE_CAPMIX(num) \
+static struct snd_kcontrol_new alc_capture_mixer ## num[] = { \
+	{ \
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+		.name = "Capture Switch", \
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+		.count = num, \
+		.info = alc_cap_sw_info, \
+		.get = alc_cap_sw_get, \
+		.put = alc_cap_sw_put, \
+	}, \
+	{ \
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+		.name = "Capture Volume", \
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+			   SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+			   SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK), \
+		.count = num, \
+		.info = alc_cap_vol_info, \
+		.get = alc_cap_vol_get, \
+		.put = alc_cap_vol_put, \
+		.tlv = { .c = alc_cap_vol_tlv }, \
+	}, \
+	{ \
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+		/* .name = "Capture Source", */ \
+		.name = "Input Source", \
+		.count = num, \
+		.info = alc_mux_enum_info, \
+		.get = alc_mux_enum_get, \
+		.put = alc_mux_enum_put, \
+	}, \
+	{ } /* end */ \
+}
+
+/* up to three ADCs */
+DEFINE_CAPMIX(1);
+DEFINE_CAPMIX(2);
+DEFINE_CAPMIX(3);
 
 
 
 
 /*
 /*
@@ -1533,18 +1900,6 @@ static struct snd_kcontrol_new alc880_tcl_s700_mixer[] = {
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x0B, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x0B, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-	{
-		.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,
-	},
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
@@ -1619,6 +1974,7 @@ static const char *alc_slave_vols[] = {
 	"Speaker Playback Volume",
 	"Speaker Playback Volume",
 	"Mono Playback Volume",
 	"Mono Playback Volume",
 	"Line-Out Playback Volume",
 	"Line-Out Playback Volume",
+	"PCM Playback Volume",
 	NULL,
 	NULL,
 };
 };
 
 
@@ -1638,6 +1994,9 @@ static const char *alc_slave_sws[] = {
 /*
 /*
  * build control elements
  * build control elements
  */
  */
+
+static void alc_free_kctls(struct hda_codec *codec);
+
 static int alc_build_controls(struct hda_codec *codec)
 static int alc_build_controls(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
@@ -1649,7 +2008,11 @@ static int alc_build_controls(struct hda_codec *codec)
 		if (err < 0)
 		if (err < 0)
 			return err;
 			return err;
 	}
 	}
-
+	if (spec->cap_mixer) {
+		err = snd_hda_add_new_ctls(codec, spec->cap_mixer);
+		if (err < 0)
+			return err;
+	}
 	if (spec->multiout.dig_out_nid) {
 	if (spec->multiout.dig_out_nid) {
 		err = snd_hda_create_spdif_out_ctls(codec,
 		err = snd_hda_create_spdif_out_ctls(codec,
 						    spec->multiout.dig_out_nid);
 						    spec->multiout.dig_out_nid);
@@ -1684,6 +2047,7 @@ static int alc_build_controls(struct hda_codec *codec)
 			return err;
 			return err;
 	}
 	}
 
 
+	alc_free_kctls(codec); /* no longer needed */
 	return 0;
 	return 0;
 }
 }
 
 
@@ -2774,19 +3138,27 @@ static int alc_build_pcms(struct hda_codec *codec)
 	return 0;
 	return 0;
 }
 }
 
 
+static void alc_free_kctls(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+
+	if (spec->kctls.list) {
+		struct snd_kcontrol_new *kctl = spec->kctls.list;
+		int i;
+		for (i = 0; i < spec->kctls.used; i++)
+			kfree(kctl[i].name);
+	}
+	snd_array_free(&spec->kctls);
+}
+
 static void alc_free(struct hda_codec *codec)
 static void alc_free(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
-	unsigned int i;
 
 
 	if (!spec)
 	if (!spec)
 		return;
 		return;
 
 
-	if (spec->kctl_alloc) {
-		for (i = 0; i < spec->num_kctl_used; i++)
-			kfree(spec->kctl_alloc[i].name);
-		kfree(spec->kctl_alloc);
-	}
+	alc_free_kctls(codec);
 	kfree(spec);
 	kfree(spec);
 	codec->spec = NULL; /* to be sure */
 	codec->spec = NULL; /* to be sure */
 }
 }
@@ -3268,6 +3640,8 @@ static struct alc_config_preset alc880_presets[] = {
 				alc880_gpio2_init_verbs },
 				alc880_gpio2_init_verbs },
 		.num_dacs = ARRAY_SIZE(alc880_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc880_dac_nids),
 		.dac_nids = alc880_dac_nids,
 		.dac_nids = alc880_dac_nids,
+		.adc_nids = alc880_adc_nids_alt, /* FIXME: correct? */
+		.num_adc_nids = 1, /* single ADC */
 		.hp_nid = 0x03,
 		.hp_nid = 0x03,
 		.num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
 		.num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
 		.channel_mode = alc880_2_jack_modes,
 		.channel_mode = alc880_2_jack_modes,
@@ -3532,9 +3906,6 @@ static struct alc_config_preset alc880_presets[] = {
  * Automatic parse of I/O pins from the BIOS configuration
  * Automatic parse of I/O pins from the BIOS configuration
  */
  */
 
 
-#define NUM_CONTROL_ALLOC	32
-#define NUM_VERB_ALLOC		32
-
 enum {
 enum {
 	ALC_CTL_WIDGET_VOL,
 	ALC_CTL_WIDGET_VOL,
 	ALC_CTL_WIDGET_MUTE,
 	ALC_CTL_WIDGET_MUTE,
@@ -3552,29 +3923,15 @@ static int add_control(struct alc_spec *spec, int type, const char *name,
 {
 {
 	struct snd_kcontrol_new *knew;
 	struct snd_kcontrol_new *knew;
 
 
-	if (spec->num_kctl_used >= spec->num_kctl_alloc) {
-		int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
-
-		/* array + terminator */
-		knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL);
-		if (!knew)
-			return -ENOMEM;
-		if (spec->kctl_alloc) {
-			memcpy(knew, spec->kctl_alloc,
-			       sizeof(*knew) * spec->num_kctl_alloc);
-			kfree(spec->kctl_alloc);
-		}
-		spec->kctl_alloc = knew;
-		spec->num_kctl_alloc = num;
-	}
-
-	knew = &spec->kctl_alloc[spec->num_kctl_used];
+	snd_array_init(&spec->kctls, sizeof(*knew), 32);
+	knew = snd_array_new(&spec->kctls);
+	if (!knew)
+		return -ENOMEM;
 	*knew = alc880_control_templates[type];
 	*knew = alc880_control_templates[type];
 	knew->name = kstrdup(name, GFP_KERNEL);
 	knew->name = kstrdup(name, GFP_KERNEL);
 	if (!knew->name)
 	if (!knew->name)
 		return -ENOMEM;
 		return -ENOMEM;
 	knew->private_value = val;
 	knew->private_value = val;
-	spec->num_kctl_used++;
 	return 0;
 	return 0;
 }
 }
 
 
@@ -3898,10 +4255,10 @@ static int alc880_parse_auto_config(struct hda_codec *codec)
 	if (spec->autocfg.dig_in_pin)
 	if (spec->autocfg.dig_in_pin)
 		spec->dig_in_nid = ALC880_DIGIN_NID;
 		spec->dig_in_nid = ALC880_DIGIN_NID;
 
 
-	if (spec->kctl_alloc)
-		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+	if (spec->kctls.list)
+		add_mixer(spec, spec->kctls.list);
 
 
-	spec->init_verbs[spec->num_init_verbs++] = alc880_volume_init_verbs;
+	add_verb(spec, alc880_volume_init_verbs);
 
 
 	spec->num_mux_defs = 1;
 	spec->num_mux_defs = 1;
 	spec->input_mux = &spec->private_imux;
 	spec->input_mux = &spec->private_imux;
@@ -3925,6 +4282,17 @@ static void alc880_auto_init(struct hda_codec *codec)
  * OK, here we have finally the patch for ALC880
  * OK, here we have finally the patch for ALC880
  */
  */
 
 
+static void set_capture_mixer(struct alc_spec *spec)
+{
+	static struct snd_kcontrol_new *caps[3] = {
+		alc_capture_mixer1,
+		alc_capture_mixer2,
+		alc_capture_mixer3,
+	};
+	if (spec->num_adc_nids > 0 && spec->num_adc_nids <= 3)
+		spec->cap_mixer = caps[spec->num_adc_nids - 1];
+}
+
 static int patch_alc880(struct hda_codec *codec)
 static int patch_alc880(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec;
 	struct alc_spec *spec;
@@ -3980,16 +4348,12 @@ static int patch_alc880(struct hda_codec *codec)
 		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);
-			spec->mixers[spec->num_mixers] =
-				alc880_capture_alt_mixer;
-			spec->num_mixers++;
 		} else {
 		} else {
 			spec->adc_nids = alc880_adc_nids;
 			spec->adc_nids = alc880_adc_nids;
 			spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids);
 			spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids);
-			spec->mixers[spec->num_mixers] = alc880_capture_mixer;
-			spec->num_mixers++;
 		}
 		}
 	}
 	}
+	set_capture_mixer(spec);
 
 
 	spec->vmaster_nid = 0x0c;
 	spec->vmaster_nid = 0x0c;
 
 
@@ -4000,6 +4364,7 @@ static int patch_alc880(struct hda_codec *codec)
 	if (!spec->loopback.amplist)
 	if (!spec->loopback.amplist)
 		spec->loopback.amplist = alc880_loopbacks;
 		spec->loopback.amplist = alc880_loopbacks;
 #endif
 #endif
+	codec->proc_widget_hook = print_realtek_coef;
 
 
 	return 0;
 	return 0;
 }
 }
@@ -4024,11 +4389,6 @@ static hda_nid_t alc260_adc_nids_alt[1] = {
 	0x05,
 	0x05,
 };
 };
 
 
-static hda_nid_t alc260_hp_adc_nids[2] = {
-	/* ADC1, 0 */
-	0x05, 0x04
-};
-
 /* NIDs used when simultaneous access to both ADCs makes sense.  Note that
 /* NIDs used when simultaneous access to both ADCs makes sense.  Note that
  * alc260_capture_mixer assumes ADC0 (nid 0x04) is the first ADC.
  * alc260_capture_mixer assumes ADC0 (nid 0x04) is the first ADC.
  */
  */
@@ -4157,13 +4517,13 @@ static void alc260_hp_master_update(struct hda_codec *codec,
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 	unsigned int val = spec->master_sw ? PIN_HP : 0;
 	unsigned int val = spec->master_sw ? PIN_HP : 0;
 	/* change HP and line-out pins */
 	/* change HP and line-out pins */
-	snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+	snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
 			    val);
 			    val);
-	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+	snd_hda_codec_write(codec, line, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
 			    val);
 			    val);
 	/* mono (speaker) depending on the HP jack sense */
 	/* mono (speaker) depending on the HP jack sense */
 	val = (val && !spec->jack_present) ? PIN_OUT : 0;
 	val = (val && !spec->jack_present) ? PIN_OUT : 0;
-	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+	snd_hda_codec_write(codec, mono, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
 			    val);
 			    val);
 }
 }
 
 
@@ -4242,7 +4602,7 @@ static struct snd_kcontrol_new alc260_hp_3013_mixer[] = {
 		.info = snd_ctl_boolean_mono_info,
 		.info = snd_ctl_boolean_mono_info,
 		.get = alc260_hp_master_sw_get,
 		.get = alc260_hp_master_sw_get,
 		.put = alc260_hp_master_sw_put,
 		.put = alc260_hp_master_sw_put,
-		.private_value = (0x10 << 16) | (0x15 << 8) | 0x11
+		.private_value = (0x15 << 16) | (0x10 << 8) | 0x11
 	},
 	},
 	HDA_CODEC_VOLUME("Front Playback Volume", 0x09, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Front Playback Volume", 0x09, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Front Playback Switch", 0x10, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Front Playback Switch", 0x10, 0x0, HDA_OUTPUT),
@@ -4295,7 +4655,7 @@ static void alc260_hp_3013_automute(struct hda_codec *codec)
 	present = snd_hda_codec_read(codec, 0x15, 0,
 	present = snd_hda_codec_read(codec, 0x15, 0,
 				     AC_VERB_GET_PIN_SENSE, 0);
 				     AC_VERB_GET_PIN_SENSE, 0);
 	spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0;
 	spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0;
-	alc260_hp_master_update(codec, 0x10, 0x15, 0x11);
+	alc260_hp_master_update(codec, 0x15, 0x10, 0x11);
 }
 }
 
 
 static void alc260_hp_3013_unsol_event(struct hda_codec *codec,
 static void alc260_hp_3013_unsol_event(struct hda_codec *codec,
@@ -4427,45 +4787,6 @@ static struct snd_kcontrol_new alc260_replacer_672v_mixer[] = {
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
-/* capture mixer elements */
-static struct snd_kcontrol_new alc260_capture_mixer[] = {
-	HDA_CODEC_VOLUME("Capture Volume", 0x04, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x04, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x05, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x05, 0x0, HDA_INPUT),
-	{
-		.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,
-	},
-	{ } /* end */
-};
-
-static struct snd_kcontrol_new alc260_capture_alt_mixer[] = {
-	HDA_CODEC_VOLUME("Capture Volume", 0x05, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x05, 0x0, HDA_INPUT),
-	{
-		.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,
-	},
-	{ } /* end */
-};
-
 /*
 /*
  * initialization verbs
  * initialization verbs
  */
  */
@@ -5282,7 +5603,6 @@ static struct hda_verb alc260_volume_init_verbs[] = {
 static int alc260_parse_auto_config(struct hda_codec *codec)
 static int alc260_parse_auto_config(struct hda_codec *codec)
 {
 {
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
-	unsigned int wcap;
 	int err;
 	int err;
 	static hda_nid_t alc260_ignore[] = { 0x17, 0 };
 	static hda_nid_t alc260_ignore[] = { 0x17, 0 };
 
 
@@ -5293,7 +5613,7 @@ static int alc260_parse_auto_config(struct hda_codec *codec)
 	err = alc260_auto_create_multi_out_ctls(spec, &spec->autocfg);
 	err = alc260_auto_create_multi_out_ctls(spec, &spec->autocfg);
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
-	if (!spec->kctl_alloc)
+	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_analog_input_ctls(spec, &spec->autocfg);
 	if (err < 0)
 	if (err < 0)
@@ -5303,28 +5623,14 @@ static int alc260_parse_auto_config(struct hda_codec *codec)
 
 
 	if (spec->autocfg.dig_out_pin)
 	if (spec->autocfg.dig_out_pin)
 		spec->multiout.dig_out_nid = ALC260_DIGOUT_NID;
 		spec->multiout.dig_out_nid = ALC260_DIGOUT_NID;
-	if (spec->kctl_alloc)
-		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+	if (spec->kctls.list)
+		add_mixer(spec, spec->kctls.list);
 
 
-	spec->init_verbs[spec->num_init_verbs++] = alc260_volume_init_verbs;
+	add_verb(spec, alc260_volume_init_verbs);
 
 
 	spec->num_mux_defs = 1;
 	spec->num_mux_defs = 1;
 	spec->input_mux = &spec->private_imux;
 	spec->input_mux = &spec->private_imux;
 
 
-	/* check whether NID 0x04 is valid */
-	wcap = get_wcaps(codec, 0x04);
-	wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; /* get type */
-	if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) {
-		spec->adc_nids = alc260_adc_nids_alt;
-		spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt);
-		spec->mixers[spec->num_mixers] = alc260_capture_alt_mixer;
-	} else {
-		spec->adc_nids = alc260_adc_nids;
-		spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids);
-		spec->mixers[spec->num_mixers] = alc260_capture_mixer;
-	}
-	spec->num_mixers++;
-
 	store_pin_configs(codec);
 	store_pin_configs(codec);
 	return 1;
 	return 1;
 }
 }
@@ -5394,12 +5700,11 @@ static struct alc_config_preset alc260_presets[] = {
 	[ALC260_BASIC] = {
 	[ALC260_BASIC] = {
 		.mixers = { alc260_base_output_mixer,
 		.mixers = { alc260_base_output_mixer,
 			    alc260_input_mixer,
 			    alc260_input_mixer,
-			    alc260_pc_beep_mixer,
-			    alc260_capture_mixer },
+			    alc260_pc_beep_mixer },
 		.init_verbs = { alc260_init_verbs },
 		.init_verbs = { alc260_init_verbs },
 		.num_dacs = ARRAY_SIZE(alc260_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc260_dac_nids),
 		.dac_nids = alc260_dac_nids,
 		.dac_nids = alc260_dac_nids,
-		.num_adc_nids = ARRAY_SIZE(alc260_adc_nids),
+		.num_adc_nids = ARRAY_SIZE(alc260_dual_adc_nids),
 		.adc_nids = alc260_adc_nids,
 		.adc_nids = alc260_adc_nids,
 		.num_channel_mode = ARRAY_SIZE(alc260_modes),
 		.num_channel_mode = ARRAY_SIZE(alc260_modes),
 		.channel_mode = alc260_modes,
 		.channel_mode = alc260_modes,
@@ -5407,14 +5712,13 @@ static struct alc_config_preset alc260_presets[] = {
 	},
 	},
 	[ALC260_HP] = {
 	[ALC260_HP] = {
 		.mixers = { alc260_hp_output_mixer,
 		.mixers = { alc260_hp_output_mixer,
-			    alc260_input_mixer,
-			    alc260_capture_alt_mixer },
+			    alc260_input_mixer },
 		.init_verbs = { alc260_init_verbs,
 		.init_verbs = { alc260_init_verbs,
 				alc260_hp_unsol_verbs },
 				alc260_hp_unsol_verbs },
 		.num_dacs = ARRAY_SIZE(alc260_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc260_dac_nids),
 		.dac_nids = alc260_dac_nids,
 		.dac_nids = alc260_dac_nids,
-		.num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
-		.adc_nids = alc260_hp_adc_nids,
+		.num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt),
+		.adc_nids = alc260_adc_nids_alt,
 		.num_channel_mode = ARRAY_SIZE(alc260_modes),
 		.num_channel_mode = ARRAY_SIZE(alc260_modes),
 		.channel_mode = alc260_modes,
 		.channel_mode = alc260_modes,
 		.input_mux = &alc260_capture_source,
 		.input_mux = &alc260_capture_source,
@@ -5423,14 +5727,13 @@ static struct alc_config_preset alc260_presets[] = {
 	},
 	},
 	[ALC260_HP_DC7600] = {
 	[ALC260_HP_DC7600] = {
 		.mixers = { alc260_hp_dc7600_mixer,
 		.mixers = { alc260_hp_dc7600_mixer,
-			    alc260_input_mixer,
-			    alc260_capture_alt_mixer },
+			    alc260_input_mixer },
 		.init_verbs = { alc260_init_verbs,
 		.init_verbs = { alc260_init_verbs,
 				alc260_hp_dc7600_verbs },
 				alc260_hp_dc7600_verbs },
 		.num_dacs = ARRAY_SIZE(alc260_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc260_dac_nids),
 		.dac_nids = alc260_dac_nids,
 		.dac_nids = alc260_dac_nids,
-		.num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
-		.adc_nids = alc260_hp_adc_nids,
+		.num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt),
+		.adc_nids = alc260_adc_nids_alt,
 		.num_channel_mode = ARRAY_SIZE(alc260_modes),
 		.num_channel_mode = ARRAY_SIZE(alc260_modes),
 		.channel_mode = alc260_modes,
 		.channel_mode = alc260_modes,
 		.input_mux = &alc260_capture_source,
 		.input_mux = &alc260_capture_source,
@@ -5439,14 +5742,13 @@ static struct alc_config_preset alc260_presets[] = {
 	},
 	},
 	[ALC260_HP_3013] = {
 	[ALC260_HP_3013] = {
 		.mixers = { alc260_hp_3013_mixer,
 		.mixers = { alc260_hp_3013_mixer,
-			    alc260_input_mixer,
-			    alc260_capture_alt_mixer },
+			    alc260_input_mixer },
 		.init_verbs = { alc260_hp_3013_init_verbs,
 		.init_verbs = { alc260_hp_3013_init_verbs,
 				alc260_hp_3013_unsol_verbs },
 				alc260_hp_3013_unsol_verbs },
 		.num_dacs = ARRAY_SIZE(alc260_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc260_dac_nids),
 		.dac_nids = alc260_dac_nids,
 		.dac_nids = alc260_dac_nids,
-		.num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids),
-		.adc_nids = alc260_hp_adc_nids,
+		.num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt),
+		.adc_nids = alc260_adc_nids_alt,
 		.num_channel_mode = ARRAY_SIZE(alc260_modes),
 		.num_channel_mode = ARRAY_SIZE(alc260_modes),
 		.channel_mode = alc260_modes,
 		.channel_mode = alc260_modes,
 		.input_mux = &alc260_capture_source,
 		.input_mux = &alc260_capture_source,
@@ -5454,8 +5756,7 @@ static struct alc_config_preset alc260_presets[] = {
 		.init_hook = alc260_hp_3013_automute,
 		.init_hook = alc260_hp_3013_automute,
 	},
 	},
 	[ALC260_FUJITSU_S702X] = {
 	[ALC260_FUJITSU_S702X] = {
-		.mixers = { alc260_fujitsu_mixer,
-			    alc260_capture_mixer },
+		.mixers = { alc260_fujitsu_mixer },
 		.init_verbs = { alc260_fujitsu_init_verbs },
 		.init_verbs = { alc260_fujitsu_init_verbs },
 		.num_dacs = ARRAY_SIZE(alc260_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc260_dac_nids),
 		.dac_nids = alc260_dac_nids,
 		.dac_nids = alc260_dac_nids,
@@ -5467,8 +5768,7 @@ static struct alc_config_preset alc260_presets[] = {
 		.input_mux = alc260_fujitsu_capture_sources,
 		.input_mux = alc260_fujitsu_capture_sources,
 	},
 	},
 	[ALC260_ACER] = {
 	[ALC260_ACER] = {
-		.mixers = { alc260_acer_mixer,
-			    alc260_capture_mixer },
+		.mixers = { alc260_acer_mixer },
 		.init_verbs = { alc260_acer_init_verbs },
 		.init_verbs = { alc260_acer_init_verbs },
 		.num_dacs = ARRAY_SIZE(alc260_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc260_dac_nids),
 		.dac_nids = alc260_dac_nids,
 		.dac_nids = alc260_dac_nids,
@@ -5480,8 +5780,7 @@ static struct alc_config_preset alc260_presets[] = {
 		.input_mux = alc260_acer_capture_sources,
 		.input_mux = alc260_acer_capture_sources,
 	},
 	},
 	[ALC260_WILL] = {
 	[ALC260_WILL] = {
-		.mixers = { alc260_will_mixer,
-			    alc260_capture_mixer },
+		.mixers = { alc260_will_mixer },
 		.init_verbs = { alc260_init_verbs, alc260_will_verbs },
 		.init_verbs = { alc260_init_verbs, alc260_will_verbs },
 		.num_dacs = ARRAY_SIZE(alc260_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc260_dac_nids),
 		.dac_nids = alc260_dac_nids,
 		.dac_nids = alc260_dac_nids,
@@ -5493,8 +5792,7 @@ static struct alc_config_preset alc260_presets[] = {
 		.input_mux = &alc260_capture_source,
 		.input_mux = &alc260_capture_source,
 	},
 	},
 	[ALC260_REPLACER_672V] = {
 	[ALC260_REPLACER_672V] = {
-		.mixers = { alc260_replacer_672v_mixer,
-			    alc260_capture_mixer },
+		.mixers = { alc260_replacer_672v_mixer },
 		.init_verbs = { alc260_init_verbs, alc260_replacer_672v_verbs },
 		.init_verbs = { alc260_init_verbs, alc260_replacer_672v_verbs },
 		.num_dacs = ARRAY_SIZE(alc260_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc260_dac_nids),
 		.dac_nids = alc260_dac_nids,
 		.dac_nids = alc260_dac_nids,
@@ -5509,8 +5807,7 @@ static struct alc_config_preset alc260_presets[] = {
 	},
 	},
 #ifdef CONFIG_SND_DEBUG
 #ifdef CONFIG_SND_DEBUG
 	[ALC260_TEST] = {
 	[ALC260_TEST] = {
-		.mixers = { alc260_test_mixer,
-			    alc260_capture_mixer },
+		.mixers = { alc260_test_mixer },
 		.init_verbs = { alc260_test_init_verbs },
 		.init_verbs = { alc260_test_init_verbs },
 		.num_dacs = ARRAY_SIZE(alc260_test_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc260_test_dac_nids),
 		.dac_nids = alc260_test_dac_nids,
 		.dac_nids = alc260_test_dac_nids,
@@ -5569,6 +5866,21 @@ static int patch_alc260(struct hda_codec *codec)
 	spec->stream_digital_playback = &alc260_pcm_digital_playback;
 	spec->stream_digital_playback = &alc260_pcm_digital_playback;
 	spec->stream_digital_capture = &alc260_pcm_digital_capture;
 	spec->stream_digital_capture = &alc260_pcm_digital_capture;
 
 
+	if (!spec->adc_nids && spec->input_mux) {
+		/* check whether NID 0x04 is valid */
+		unsigned int wcap = get_wcaps(codec, 0x04);
+		wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+		/* get type */
+		if (wcap != AC_WID_AUD_IN || spec->input_mux->num_items == 1) {
+			spec->adc_nids = alc260_adc_nids_alt;
+			spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids_alt);
+		} else {
+			spec->adc_nids = alc260_adc_nids;
+			spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids);
+		}
+	}
+	set_capture_mixer(spec);
+
 	spec->vmaster_nid = 0x08;
 	spec->vmaster_nid = 0x08;
 
 
 	codec->patch_ops = alc_patch_ops;
 	codec->patch_ops = alc_patch_ops;
@@ -5578,6 +5890,7 @@ static int patch_alc260(struct hda_codec *codec)
 	if (!spec->loopback.amplist)
 	if (!spec->loopback.amplist)
 		spec->loopback.amplist = alc260_loopbacks;
 		spec->loopback.amplist = alc260_loopbacks;
 #endif
 #endif
+	codec->proc_widget_hook = print_realtek_coef;
 
 
 	return 0;
 	return 0;
 }
 }
@@ -5616,45 +5929,15 @@ static hda_nid_t alc882_capsrc_nids_alt[2] = { 0x23, 0x22 };
 /* input MUX */
 /* input MUX */
 /* FIXME: should be a matrix-type input source selection */
 /* FIXME: should be a matrix-type input source selection */
 
 
-static struct hda_input_mux alc882_capture_source = {
-	.num_items = 4,
-	.items = {
-		{ "Mic", 0x0 },
-		{ "Front Mic", 0x1 },
-		{ "Line", 0x2 },
-		{ "CD", 0x4 },
-	},
-};
-#define alc882_mux_enum_info alc_mux_enum_info
-#define alc882_mux_enum_get alc_mux_enum_get
-
-static int alc882_mux_enum_put(struct snd_kcontrol *kcontrol,
-			       struct snd_ctl_elem_value *ucontrol)
-{
-	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct alc_spec *spec = codec->spec;
-	const struct hda_input_mux *imux = spec->input_mux;
-	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-	hda_nid_t nid = spec->capsrc_nids ?
-		spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx];
-	unsigned int *cur_val = &spec->cur_mux[adc_idx];
-	unsigned int i, idx;
-
-	idx = ucontrol->value.enumerated.item[0];
-	if (idx >= imux->num_items)
-		idx = imux->num_items - 1;
-	if (*cur_val == idx)
-		return 0;
-	for (i = 0; i < imux->num_items; i++) {
-		unsigned int v = (i == idx) ? 0 : HDA_AMP_MUTE;
-		snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT,
-					 imux->items[i].index,
-					 HDA_AMP_MUTE, v);
-	}
-	*cur_val = idx;
-	return 1;
-}
-
+static struct hda_input_mux alc882_capture_source = {
+	.num_items = 4,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "Front Mic", 0x1 },
+		{ "Line", 0x2 },
+		{ "CD", 0x4 },
+	},
+};
 /*
 /*
  * 2ch mode
  * 2ch mode
  */
  */
@@ -6337,49 +6620,6 @@ static struct hda_verb alc882_auto_init_verbs[] = {
 	{ }
 	{ }
 };
 };
 
 
-/* capture mixer elements */
-static struct snd_kcontrol_new alc882_capture_alt_mixer[] = {
-	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
-	{
-		.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 = alc882_mux_enum_info,
-		.get = alc882_mux_enum_get,
-		.put = alc882_mux_enum_put,
-	},
-	{ } /* end */
-};
-
-static struct snd_kcontrol_new alc882_capture_mixer[] = {
-	HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		/* The multiple "Capture Source" controls confuse alsamixer
-		 * So call somewhat different..
-		 */
-		/* .name = "Capture Source", */
-		.name = "Input Source",
-		.count = 3,
-		.info = alc882_mux_enum_info,
-		.get = alc882_mux_enum_get,
-		.put = alc882_mux_enum_put,
-	},
-	{ } /* end */
-};
-
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #define alc882_loopbacks	alc880_loopbacks
 #define alc882_loopbacks	alc880_loopbacks
 #endif
 #endif
@@ -6508,8 +6748,7 @@ static struct alc_config_preset alc882_presets[] = {
 		.init_hook = alc885_imac24_init_hook,
 		.init_hook = alc885_imac24_init_hook,
 	},
 	},
 	[ALC882_TARGA] = {
 	[ALC882_TARGA] = {
-		.mixers = { alc882_targa_mixer, alc882_chmode_mixer,
-			    alc882_capture_mixer },
+		.mixers = { alc882_targa_mixer, alc882_chmode_mixer },
 		.init_verbs = { alc882_init_verbs, alc882_targa_verbs},
 		.init_verbs = { alc882_init_verbs, alc882_targa_verbs},
 		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
 		.dac_nids = alc882_dac_nids,
 		.dac_nids = alc882_dac_nids,
@@ -6525,8 +6764,7 @@ static struct alc_config_preset alc882_presets[] = {
 		.init_hook = alc882_targa_automute,
 		.init_hook = alc882_targa_automute,
 	},
 	},
 	[ALC882_ASUS_A7J] = {
 	[ALC882_ASUS_A7J] = {
-		.mixers = { alc882_asus_a7j_mixer, alc882_chmode_mixer,
-			    alc882_capture_mixer },
+		.mixers = { alc882_asus_a7j_mixer, alc882_chmode_mixer },
 		.init_verbs = { alc882_init_verbs, alc882_asus_a7j_verbs},
 		.init_verbs = { alc882_init_verbs, alc882_asus_a7j_verbs},
 		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
 		.dac_nids = alc882_dac_nids,
 		.dac_nids = alc882_dac_nids,
@@ -6831,6 +7069,7 @@ static int patch_alc882(struct hda_codec *codec)
 	spec->stream_digital_playback = &alc882_pcm_digital_playback;
 	spec->stream_digital_playback = &alc882_pcm_digital_playback;
 	spec->stream_digital_capture = &alc882_pcm_digital_capture;
 	spec->stream_digital_capture = &alc882_pcm_digital_capture;
 
 
+	spec->is_mix_capture = 1; /* matrix-style capture */
 	if (!spec->adc_nids && spec->input_mux) {
 	if (!spec->adc_nids && spec->input_mux) {
 		/* check whether NID 0x07 is valid */
 		/* check whether NID 0x07 is valid */
 		unsigned int wcap = get_wcaps(codec, 0x07);
 		unsigned int wcap = get_wcaps(codec, 0x07);
@@ -6840,17 +7079,13 @@ static int patch_alc882(struct hda_codec *codec)
 			spec->adc_nids = alc882_adc_nids_alt;
 			spec->adc_nids = alc882_adc_nids_alt;
 			spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids_alt);
 			spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids_alt);
 			spec->capsrc_nids = alc882_capsrc_nids_alt;
 			spec->capsrc_nids = alc882_capsrc_nids_alt;
-			spec->mixers[spec->num_mixers] =
-				alc882_capture_alt_mixer;
-			spec->num_mixers++;
 		} else {
 		} else {
 			spec->adc_nids = alc882_adc_nids;
 			spec->adc_nids = alc882_adc_nids;
 			spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids);
 			spec->num_adc_nids = ARRAY_SIZE(alc882_adc_nids);
 			spec->capsrc_nids = alc882_capsrc_nids;
 			spec->capsrc_nids = alc882_capsrc_nids;
-			spec->mixers[spec->num_mixers] = alc882_capture_mixer;
-			spec->num_mixers++;
 		}
 		}
 	}
 	}
+	set_capture_mixer(spec);
 
 
 	spec->vmaster_nid = 0x0c;
 	spec->vmaster_nid = 0x0c;
 
 
@@ -6861,6 +7096,7 @@ static int patch_alc882(struct hda_codec *codec)
 	if (!spec->loopback.amplist)
 	if (!spec->loopback.amplist)
 		spec->loopback.amplist = alc882_loopbacks;
 		spec->loopback.amplist = alc882_loopbacks;
 #endif
 #endif
+	codec->proc_widget_hook = print_realtek_coef;
 
 
 	return 0;
 	return 0;
 }
 }
@@ -6879,6 +7115,8 @@ static int patch_alc882(struct hda_codec *codec)
 #define ALC883_DIGOUT_NID	0x06
 #define ALC883_DIGOUT_NID	0x06
 #define ALC883_DIGIN_NID	0x0a
 #define ALC883_DIGIN_NID	0x0a
 
 
+#define ALC1200_DIGOUT_NID	0x10
+
 static hda_nid_t alc883_dac_nids[4] = {
 static hda_nid_t alc883_dac_nids[4] = {
 	/* front, rear, clfe, rear_surr */
 	/* front, rear, clfe, rear_surr */
 	0x02, 0x03, 0x04, 0x05
 	0x02, 0x03, 0x04, 0x05
@@ -6889,8 +7127,20 @@ static hda_nid_t alc883_adc_nids[2] = {
 	0x08, 0x09,
 	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
+};
+
 static hda_nid_t alc883_capsrc_nids[2] = { 0x23, 0x22 };
 static hda_nid_t alc883_capsrc_nids[2] = { 0x23, 0x22 };
 
 
+static hda_nid_t alc883_capsrc_nids_rev[2] = { 0x22, 0x23 };
+
 /* input MUX */
 /* input MUX */
 /* FIXME: should be a matrix-type input source selection */
 /* FIXME: should be a matrix-type input source selection */
 
 
@@ -6957,11 +7207,6 @@ static struct hda_input_mux alc883_asus_eee1601_capture_source = {
 	},
 	},
 };
 };
 
 
-#define alc883_mux_enum_info alc_mux_enum_info
-#define alc883_mux_enum_get alc_mux_enum_get
-/* ALC883 has the ALC882-type input selection */
-#define alc883_mux_enum_put alc882_mux_enum_put
-
 /*
 /*
  * 2ch mode
  * 2ch mode
  */
  */
@@ -7115,19 +7360,6 @@ static struct snd_kcontrol_new alc883_base_mixer[] = {
 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
 	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
 	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
 	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
 	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
-	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		/* .name = "Capture Source", */
-		.name = "Input Source",
-		.count = 2,
-		.info = alc883_mux_enum_info,
-		.get = alc883_mux_enum_get,
-		.put = alc883_mux_enum_put,
-	},
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
@@ -7145,19 +7377,6 @@ static struct snd_kcontrol_new alc883_mitac_mixer[] = {
 	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, 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_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		/* .name = "Capture Source", */
-		.name = "Input Source",
-		.count = 2,
-		.info = alc883_mux_enum_info,
-		.get = alc883_mux_enum_get,
-		.put = alc883_mux_enum_put,
-	},
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
@@ -7172,19 +7391,6 @@ static struct snd_kcontrol_new alc883_clevo_m720_mixer[] = {
 	HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
 	HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
 	HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
 	HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
 	HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		/* .name = "Capture Source", */
-		.name = "Input Source",
-		.count = 2,
-		.info = alc883_mux_enum_info,
-		.get = alc883_mux_enum_get,
-		.put = alc883_mux_enum_put,
-	},
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
@@ -7199,19 +7405,6 @@ static struct snd_kcontrol_new alc883_2ch_fujitsu_pi2515_mixer[] = {
 	HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
 	HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
 	HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
 	HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
 	HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		/* .name = "Capture Source", */
-		.name = "Input Source",
-		.count = 2,
-		.info = alc883_mux_enum_info,
-		.get = alc883_mux_enum_get,
-		.put = alc883_mux_enum_put,
-	},
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
@@ -7231,19 +7424,6 @@ static struct snd_kcontrol_new alc883_3ST_2ch_mixer[] = {
 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
 	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
 	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
 	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
 	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
-	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		/* .name = "Capture Source", */
-		.name = "Input Source",
-		.count = 2,
-		.info = alc883_mux_enum_info,
-		.get = alc883_mux_enum_get,
-		.put = alc883_mux_enum_put,
-	},
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
@@ -7269,17 +7449,6 @@ static struct snd_kcontrol_new alc883_3ST_6ch_mixer[] = {
 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
 	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
 	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
 	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
 	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
-	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		/* .name = "Capture Source", */
-		.name = "Input Source",
-		.count = 1,
-		.info = alc883_mux_enum_info,
-		.get = alc883_mux_enum_get,
-		.put = alc883_mux_enum_put,
-	},
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
@@ -7306,19 +7475,6 @@ static struct snd_kcontrol_new alc883_3ST_6ch_intel_mixer[] = {
 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
 	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
 	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
 	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
-	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		/* .name = "Capture Source", */
-		.name = "Input Source",
-		.count = 2,
-		.info = alc883_mux_enum_info,
-		.get = alc883_mux_enum_get,
-		.put = alc883_mux_enum_put,
-	},
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
@@ -7344,18 +7500,6 @@ static struct snd_kcontrol_new alc883_fivestack_mixer[] = {
 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
 	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
 	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
 	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
 	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
-	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		/* .name = "Capture Source", */
-		.name = "Input Source",
-		.count = 1,
-		.info = alc883_mux_enum_info,
-		.get = alc883_mux_enum_get,
-		.put = alc883_mux_enum_put,
-	},
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
@@ -7376,19 +7520,6 @@ static struct snd_kcontrol_new alc883_tagra_mixer[] = {
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		/* .name = "Capture Source", */
-		.name = "Input Source",
-		.count = 2,
-		.info = alc883_mux_enum_info,
-		.get = alc883_mux_enum_get,
-		.put = alc883_mux_enum_put,
-	},
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
@@ -7404,19 +7535,6 @@ static struct snd_kcontrol_new alc883_tagra_2ch_mixer[] = {
 	HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
 	HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
 	HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
 	HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
 	HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		/* .name = "Capture Source", */
-		.name = "Input Source",
-		.count = 2,
-		.info = alc883_mux_enum_info,
-		.get = alc883_mux_enum_get,
-		.put = alc883_mux_enum_put,
-	},
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
@@ -7429,17 +7547,6 @@ static struct snd_kcontrol_new alc883_lenovo_101e_2ch_mixer[] = {
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		/* .name = "Capture Source", */
-		.name = "Input Source",
-		.count = 1,
-		.info = alc883_mux_enum_info,
-		.get = alc883_mux_enum_get,
-		.put = alc883_mux_enum_put,
-	},
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
@@ -7453,19 +7560,6 @@ static struct snd_kcontrol_new alc883_lenovo_nb0763_mixer[] = {
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("iMic Playback Volume", 0x0b, 0x1, HDA_INPUT),
 	HDA_CODEC_VOLUME("iMic Playback Volume", 0x0b, 0x1, HDA_INPUT),
 	HDA_CODEC_MUTE("iMic Playback Switch", 0x0b, 0x1, HDA_INPUT),
 	HDA_CODEC_MUTE("iMic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		/* .name = "Capture Source", */
-		.name = "Input Source",
-		.count = 2,
-		.info = alc883_mux_enum_info,
-		.get = alc883_mux_enum_get,
-		.put = alc883_mux_enum_put,
-	},
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
@@ -7479,19 +7573,6 @@ static struct snd_kcontrol_new alc883_medion_md2_mixer[] = {
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
 	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
 	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
 	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
-	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		/* .name = "Capture Source", */
-		.name = "Input Source",
-		.count = 2,
-		.info = alc883_mux_enum_info,
-		.get = alc883_mux_enum_get,
-		.put = alc883_mux_enum_put,
-	},
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
@@ -7504,19 +7585,6 @@ static struct snd_kcontrol_new alc883_acer_aspire_mixer[] = {
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		/* .name = "Capture Source", */
-		.name = "Input Source",
-		.count = 2,
-		.info = alc883_mux_enum_info,
-		.get = alc883_mux_enum_get,
-		.put = alc883_mux_enum_put,
-	},
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
@@ -7544,19 +7612,6 @@ static struct snd_kcontrol_new alc888_lenovo_sky_mixer[] = {
 	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, 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_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
-	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		/* .name = "Capture Source", */
-		.name = "Input Source",
-		.count = 2,
-		.info = alc883_mux_enum_info,
-		.get = alc883_mux_enum_get,
-		.put = alc883_mux_enum_put,
-	},
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
@@ -7587,6 +7642,10 @@ static struct snd_kcontrol_new alc883_asus_eee1601_mixer[] = {
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	{ } /* end */
+};
+
+static struct snd_kcontrol_new alc883_asus_eee1601_cap_mixer[] = {
 	HDA_BIND_VOL("Capture Volume", &alc883_bind_cap_vol),
 	HDA_BIND_VOL("Capture Volume", &alc883_bind_cap_vol),
 	HDA_BIND_SW("Capture Switch", &alc883_bind_cap_switch),
 	HDA_BIND_SW("Capture Switch", &alc883_bind_cap_switch),
 	{
 	{
@@ -7594,9 +7653,9 @@ static struct snd_kcontrol_new alc883_asus_eee1601_mixer[] = {
 		/* .name = "Capture Source", */
 		/* .name = "Capture Source", */
 		.name = "Input Source",
 		.name = "Input Source",
 		.count = 1,
 		.count = 1,
-		.info = alc883_mux_enum_info,
-		.get = alc883_mux_enum_get,
-		.put = alc883_mux_enum_put,
+		.info = alc_mux_enum_info,
+		.get = alc_mux_enum_get,
+		.put = alc_mux_enum_put,
 	},
 	},
 	{ } /* end */
 	{ } /* end */
 };
 };
@@ -8251,27 +8310,6 @@ static struct hda_verb alc883_auto_init_verbs[] = {
 	{ }
 	{ }
 };
 };
 
 
-/* capture mixer elements */
-static struct snd_kcontrol_new alc883_capture_mixer[] = {
-	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
-	{
-		.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 = alc882_mux_enum_info,
-		.get = alc882_mux_enum_get,
-		.put = alc882_mux_enum_put,
-	},
-	{ } /* end */
-};
-
 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)},
@@ -8394,6 +8432,7 @@ static const char *alc883_models[ALC883_MODEL_LAST] = {
 	[ALC883_TARGA_2ch_DIG]	= "targa-2ch-dig",
 	[ALC883_TARGA_2ch_DIG]	= "targa-2ch-dig",
 	[ALC883_ACER]		= "acer",
 	[ALC883_ACER]		= "acer",
 	[ALC883_ACER_ASPIRE]	= "acer-aspire",
 	[ALC883_ACER_ASPIRE]	= "acer-aspire",
+	[ALC888_ACER_ASPIRE_4930G]	= "acer-aspire-4930g",
 	[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",
@@ -8407,7 +8446,9 @@ static const char *alc883_models[ALC883_MODEL_LAST] = {
 	[ALC883_MITAC]		= "mitac",
 	[ALC883_MITAC]		= "mitac",
 	[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",
 	[ALC883_3ST_6ch_INTEL]	= "3stack-6ch-intel",
 	[ALC883_3ST_6ch_INTEL]	= "3stack-6ch-intel",
+	[ALC1200_ASUS_P5Q]	= "asus-p5q",
 	[ALC883_AUTO]		= "auto",
 	[ALC883_AUTO]		= "auto",
 };
 };
 
 
@@ -8418,6 +8459,8 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_ACER_ASPIRE),
 	SND_PCI_QUIRK(0x1025, 0x0110, "Acer Aspire", ALC883_ACER_ASPIRE),
 	SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_ACER_ASPIRE),
 	SND_PCI_QUIRK(0x1025, 0x0112, "Acer Aspire 9303", ALC883_ACER_ASPIRE),
 	SND_PCI_QUIRK(0x1025, 0x0121, "Acer Aspire 5920G", ALC883_ACER_ASPIRE),
 	SND_PCI_QUIRK(0x1025, 0x0121, "Acer Aspire 5920G", ALC883_ACER_ASPIRE),
+	SND_PCI_QUIRK(0x1025, 0x013e, "Acer Aspire 4930G",
+		ALC888_ACER_ASPIRE_4930G),
 	SND_PCI_QUIRK(0x1025, 0, "Acer laptop", ALC883_ACER), /* default Acer */
 	SND_PCI_QUIRK(0x1025, 0, "Acer laptop", ALC883_ACER), /* default 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),
@@ -8426,6 +8469,7 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x1043, 0x1873, "Asus M90V", ALC888_ASUS_M90V),
 	SND_PCI_QUIRK(0x1043, 0x1873, "Asus M90V", ALC888_ASUS_M90V),
 	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, 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(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", ALC883_6ST_DIG),
@@ -8452,6 +8496,7 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG),
+	SND_PCI_QUIRK(0x1462, 0x7260, "MSI 7260", ALC883_TARGA_DIG),
 	SND_PCI_QUIRK(0x1462, 0x7267, "MSI", ALC883_3ST_6ch_DIG),
 	SND_PCI_QUIRK(0x1462, 0x7267, "MSI", ALC883_3ST_6ch_DIG),
 	SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG),
@@ -8463,6 +8508,8 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
 	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, "Medion laptop", ALC883_MEDION),
 	SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION),
 	SND_PCI_QUIRK(0x1734, 0x1108, "Fujitsu AMILO Pi2515", ALC883_FUJITSU_PI2515),
 	SND_PCI_QUIRK(0x1734, 0x1108, "Fujitsu AMILO Pi2515", ALC883_FUJITSU_PI2515),
+	SND_PCI_QUIRK(0x1734, 0x113d, "Fujitsu AMILO Xa3530",
+		ALC888_FUJITSU_XA3530),
 	SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo 101e", ALC883_LENOVO_101E_2ch),
 	SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo 101e", ALC883_LENOVO_101E_2ch),
 	SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763),
 	SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763),
 	SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763),
 	SND_PCI_QUIRK(0x17aa, 0x3bfc, "Lenovo NB0763", ALC883_LENOVO_NB0763),
@@ -8553,6 +8600,8 @@ static struct alc_config_preset alc883_presets[] = {
 		.init_verbs = { alc883_init_verbs, alc883_tagra_verbs},
 		.init_verbs = { alc883_init_verbs, alc883_tagra_verbs},
 		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
 		.dac_nids = alc883_dac_nids,
 		.dac_nids = alc883_dac_nids,
+		.adc_nids = alc883_adc_nids_alt,
+		.num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt),
 		.dig_out_nid = ALC883_DIGOUT_NID,
 		.dig_out_nid = ALC883_DIGOUT_NID,
 		.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
 		.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
 		.channel_mode = alc883_3ST_2ch_modes,
 		.channel_mode = alc883_3ST_2ch_modes,
@@ -8586,6 +8635,26 @@ static struct alc_config_preset alc883_presets[] = {
 		.unsol_event = alc883_acer_aspire_unsol_event,
 		.unsol_event = alc883_acer_aspire_unsol_event,
 		.init_hook = alc883_acer_aspire_automute,
 		.init_hook = alc883_acer_aspire_automute,
 	},
 	},
+	[ALC888_ACER_ASPIRE_4930G] = {
+		.mixers = { alc888_base_mixer,
+				alc883_chmode_mixer },
+		.init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs,
+				alc888_acer_aspire_4930g_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,
+		.num_mux_defs =
+			ARRAY_SIZE(alc888_2_capture_sources),
+		.input_mux = alc888_2_capture_sources,
+		.unsol_event = alc888_acer_aspire_4930g_unsol_event,
+		.init_hook = alc888_acer_aspire_4930g_automute,
+	},
 	[ALC883_MEDION] = {
 	[ALC883_MEDION] = {
 		.mixers = { alc883_fivestack_mixer,
 		.mixers = { alc883_fivestack_mixer,
 			    alc883_chmode_mixer },
 			    alc883_chmode_mixer },
@@ -8593,6 +8662,8 @@ static struct alc_config_preset alc883_presets[] = {
 				alc883_medion_eapd_verbs },
 				alc883_medion_eapd_verbs },
 		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
 		.dac_nids = alc883_dac_nids,
 		.dac_nids = alc883_dac_nids,
+		.adc_nids = alc883_adc_nids_alt,
+		.num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt),
 		.num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
 		.num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
 		.channel_mode = alc883_sixstack_modes,
 		.channel_mode = alc883_sixstack_modes,
 		.input_mux = &alc883_capture_source,
 		.input_mux = &alc883_capture_source,
@@ -8635,6 +8706,8 @@ static struct alc_config_preset alc883_presets[] = {
 		.init_verbs = { alc883_init_verbs, alc883_lenovo_101e_verbs},
 		.init_verbs = { alc883_init_verbs, alc883_lenovo_101e_verbs},
 		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
 		.dac_nids = alc883_dac_nids,
 		.dac_nids = alc883_dac_nids,
+		.adc_nids = alc883_adc_nids_alt,
+		.num_adc_nids = ARRAY_SIZE(alc883_adc_nids_alt),
 		.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
 		.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
 		.channel_mode = alc883_3ST_2ch_modes,
 		.channel_mode = alc883_3ST_2ch_modes,
 		.input_mux = &alc883_lenovo_101e_capture_source,
 		.input_mux = &alc883_lenovo_101e_capture_source,
@@ -8725,14 +8798,30 @@ static struct alc_config_preset alc883_presets[] = {
 		.unsol_event = alc883_2ch_fujitsu_pi2515_unsol_event,
 		.unsol_event = alc883_2ch_fujitsu_pi2515_unsol_event,
 		.init_hook = alc883_2ch_fujitsu_pi2515_automute,
 		.init_hook = alc883_2ch_fujitsu_pi2515_automute,
 	},
 	},
+	[ALC888_FUJITSU_XA3530] = {
+		.mixers = { alc888_base_mixer, alc883_chmode_mixer },
+		.init_verbs = { alc883_init_verbs,
+			alc888_fujitsu_xa3530_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(alc888_4ST_8ch_intel_modes),
+		.channel_mode = alc888_4ST_8ch_intel_modes,
+		.num_mux_defs =
+			ARRAY_SIZE(alc888_2_capture_sources),
+		.input_mux = alc888_2_capture_sources,
+		.unsol_event = alc888_fujitsu_xa3530_unsol_event,
+		.init_hook = alc888_fujitsu_xa3530_automute,
+	},
 	[ALC888_LENOVO_SKY] = {
 	[ALC888_LENOVO_SKY] = {
 		.mixers = { alc888_lenovo_sky_mixer, alc883_chmode_mixer },
 		.mixers = { alc888_lenovo_sky_mixer, alc883_chmode_mixer },
 		.init_verbs = { alc883_init_verbs, alc888_lenovo_sky_verbs},
 		.init_verbs = { alc883_init_verbs, alc888_lenovo_sky_verbs},
 		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
 		.dac_nids = alc883_dac_nids,
 		.dac_nids = alc883_dac_nids,
 		.dig_out_nid = ALC883_DIGOUT_NID,
 		.dig_out_nid = ALC883_DIGOUT_NID,
-		.num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
-		.adc_nids = alc883_adc_nids,
 		.num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
 		.num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
 		.channel_mode = alc883_sixstack_modes,
 		.channel_mode = alc883_sixstack_modes,
 		.need_dac_fix = 1,
 		.need_dac_fix = 1,
@@ -8756,6 +8845,7 @@ static struct alc_config_preset alc883_presets[] = {
 	},
 	},
 	[ALC888_ASUS_EEE1601] = {
 	[ALC888_ASUS_EEE1601] = {
 		.mixers = { alc883_asus_eee1601_mixer },
 		.mixers = { alc883_asus_eee1601_mixer },
+		.cap_mixer = alc883_asus_eee1601_cap_mixer,
 		.init_verbs = { alc883_init_verbs, alc888_asus_eee1601_verbs },
 		.init_verbs = { alc883_init_verbs, alc888_asus_eee1601_verbs },
 		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
 		.dac_nids = alc883_dac_nids,
 		.dac_nids = alc883_dac_nids,
@@ -8768,6 +8858,17 @@ static struct alc_config_preset alc883_presets[] = {
 		.unsol_event = alc883_eee1601_unsol_event,
 		.unsol_event = alc883_eee1601_unsol_event,
 		.init_hook = alc883_eee1601_inithook,
 		.init_hook = alc883_eee1601_inithook,
 	},
 	},
+	[ALC1200_ASUS_P5Q] = {
+		.mixers = { alc883_base_mixer, alc883_chmode_mixer },
+		.init_verbs = { alc883_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.dig_out_nid = ALC1200_DIGOUT_NID,
+		.dig_in_nid = ALC883_DIGIN_NID,
+		.num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
+		.channel_mode = alc883_sixstack_modes,
+		.input_mux = &alc883_capture_source,
+	},
 };
 };
 
 
 
 
@@ -8862,8 +8963,6 @@ static int alc883_parse_auto_config(struct hda_codec *codec)
 
 
 	/* hack - override the init verbs */
 	/* hack - override the init verbs */
 	spec->init_verbs[0] = alc883_auto_init_verbs;
 	spec->init_verbs[0] = alc883_auto_init_verbs;
-	spec->mixers[spec->num_mixers] = alc883_capture_mixer;
-	spec->num_mixers++;
 
 
 	return 1; /* config found */
 	return 1; /* config found */
 }
 }
@@ -8946,9 +9045,15 @@ static int patch_alc883(struct hda_codec *codec)
 	spec->stream_digital_playback = &alc883_pcm_digital_playback;
 	spec->stream_digital_playback = &alc883_pcm_digital_playback;
 	spec->stream_digital_capture = &alc883_pcm_digital_capture;
 	spec->stream_digital_capture = &alc883_pcm_digital_capture;
 
 
-	spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids);
-	spec->adc_nids = alc883_adc_nids;
-	spec->capsrc_nids = alc883_capsrc_nids;
+	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->is_mix_capture = 1; /* matrix-style capture */
+	if (!spec->cap_mixer)
+		set_capture_mixer(spec);
 
 
 	spec->vmaster_nid = 0x0c;
 	spec->vmaster_nid = 0x0c;
 
 
@@ -8960,6 +9065,7 @@ static int patch_alc883(struct hda_codec *codec)
 	if (!spec->loopback.amplist)
 	if (!spec->loopback.amplist)
 		spec->loopback.amplist = alc883_loopbacks;
 		spec->loopback.amplist = alc883_loopbacks;
 #endif
 #endif
+	codec->proc_widget_hook = print_realtek_coef;
 
 
 	return 0;
 	return 0;
 }
 }
@@ -9439,20 +9545,6 @@ static struct snd_kcontrol_new alc262_toshiba_s06_mixer[] = {
 	HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
-	{
-		.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,
-	},
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
@@ -9969,7 +10061,7 @@ static int alc262_ultra_mux_enum_put(struct snd_kcontrol *kcontrol,
 	struct alc_spec *spec = codec->spec;
 	struct alc_spec *spec = codec->spec;
 	int ret;
 	int ret;
 
 
-	ret = alc882_mux_enum_put(kcontrol, ucontrol);
+	ret = alc_mux_enum_put(kcontrol, ucontrol);
 	if (!ret)
 	if (!ret)
 		return 0;
 		return 0;
 	/* reprogram the HP pin as mic or HP according to the input source */
 	/* reprogram the HP pin as mic or HP according to the input source */
@@ -9986,8 +10078,8 @@ static struct snd_kcontrol_new alc262_ultra_capture_mixer[] = {
 	{
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.name = "Capture Source",
 		.name = "Capture Source",
-		.info = alc882_mux_enum_info,
-		.get = alc882_mux_enum_get,
+		.info = alc_mux_enum_info,
+		.get = alc_mux_enum_get,
 		.put = alc262_ultra_mux_enum_put,
 		.put = alc262_ultra_mux_enum_put,
 	},
 	},
 	{ } /* end */
 	{ } /* end */
@@ -10380,10 +10472,10 @@ static int alc262_parse_auto_config(struct hda_codec *codec)
 	if (spec->autocfg.dig_in_pin)
 	if (spec->autocfg.dig_in_pin)
 		spec->dig_in_nid = ALC262_DIGIN_NID;
 		spec->dig_in_nid = ALC262_DIGIN_NID;
 
 
-	if (spec->kctl_alloc)
-		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+	if (spec->kctls.list)
+		add_mixer(spec, spec->kctls.list);
 
 
-	spec->init_verbs[spec->num_init_verbs++] = alc262_volume_init_verbs;
+	add_verb(spec, alc262_volume_init_verbs);
 	spec->num_mux_defs = 1;
 	spec->num_mux_defs = 1;
 	spec->input_mux = &spec->private_imux;
 	spec->input_mux = &spec->private_imux;
 
 
@@ -10466,6 +10558,8 @@ static struct snd_pci_quirk alc262_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD),
 	SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD),
 	SND_PCI_QUIRK(0x104d, 0x900e, "Sony ASSAMD", ALC262_SONY_ASSAMD),
 	SND_PCI_QUIRK(0x104d, 0x900e, "Sony ASSAMD", ALC262_SONY_ASSAMD),
 	SND_PCI_QUIRK(0x104d, 0x9015, "Sony 0x9015", ALC262_SONY_ASSAMD),
 	SND_PCI_QUIRK(0x104d, 0x9015, "Sony 0x9015", ALC262_SONY_ASSAMD),
+	SND_PCI_QUIRK(0x104d, 0x9033, "Sony VAIO VGN-SR19XN",
+		      ALC262_SONY_ASSAMD),
 	SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba dynabook SS RX1",
 	SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba dynabook SS RX1",
 		      ALC262_TOSHIBA_RX1),
 		      ALC262_TOSHIBA_RX1),
 	SND_PCI_QUIRK(0x1179, 0xff7b, "Toshiba S06", ALC262_TOSHIBA_S06),
 	SND_PCI_QUIRK(0x1179, 0xff7b, "Toshiba S06", ALC262_TOSHIBA_S06),
@@ -10624,7 +10718,8 @@ static struct alc_config_preset alc262_presets[] = {
 		.init_hook = alc262_hippo_automute,
 		.init_hook = alc262_hippo_automute,
 	},
 	},
 	[ALC262_ULTRA] = {
 	[ALC262_ULTRA] = {
-		.mixers = { alc262_ultra_mixer, alc262_ultra_capture_mixer },
+		.mixers = { alc262_ultra_mixer },
+		.cap_mixer = alc262_ultra_capture_mixer,
 		.init_verbs = { alc262_ultra_verbs },
 		.init_verbs = { alc262_ultra_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,
@@ -10750,6 +10845,7 @@ static int patch_alc262(struct hda_codec *codec)
 	spec->stream_digital_playback = &alc262_pcm_digital_playback;
 	spec->stream_digital_playback = &alc262_pcm_digital_playback;
 	spec->stream_digital_capture = &alc262_pcm_digital_capture;
 	spec->stream_digital_capture = &alc262_pcm_digital_capture;
 
 
+	spec->is_mix_capture = 1;
 	if (!spec->adc_nids && spec->input_mux) {
 	if (!spec->adc_nids && spec->input_mux) {
 		/* check whether NID 0x07 is valid */
 		/* check whether NID 0x07 is valid */
 		unsigned int wcap = get_wcaps(codec, 0x07);
 		unsigned int wcap = get_wcaps(codec, 0x07);
@@ -10760,17 +10856,14 @@ static int patch_alc262(struct hda_codec *codec)
 			spec->adc_nids = alc262_adc_nids_alt;
 			spec->adc_nids = alc262_adc_nids_alt;
 			spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids_alt);
 			spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids_alt);
 			spec->capsrc_nids = alc262_capsrc_nids_alt;
 			spec->capsrc_nids = alc262_capsrc_nids_alt;
-			spec->mixers[spec->num_mixers] =
-				alc262_capture_alt_mixer;
-			spec->num_mixers++;
 		} else {
 		} else {
 			spec->adc_nids = alc262_adc_nids;
 			spec->adc_nids = alc262_adc_nids;
 			spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids);
 			spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids);
 			spec->capsrc_nids = alc262_capsrc_nids;
 			spec->capsrc_nids = alc262_capsrc_nids;
-			spec->mixers[spec->num_mixers] = alc262_capture_mixer;
-			spec->num_mixers++;
 		}
 		}
 	}
 	}
+	if (!spec->cap_mixer)
+		set_capture_mixer(spec);
 
 
 	spec->vmaster_nid = 0x0c;
 	spec->vmaster_nid = 0x0c;
 
 
@@ -10781,6 +10874,7 @@ static int patch_alc262(struct hda_codec *codec)
 	if (!spec->loopback.amplist)
 	if (!spec->loopback.amplist)
 		spec->loopback.amplist = alc262_loopbacks;
 		spec->loopback.amplist = alc262_loopbacks;
 #endif
 #endif
+	codec->proc_widget_hook = print_realtek_coef;
 
 
 	return 0;
 	return 0;
 }
 }
@@ -10942,6 +11036,22 @@ static struct snd_kcontrol_new alc268_acer_mixer[] = {
 	{ }
 	{ }
 };
 };
 
 
+static struct snd_kcontrol_new alc268_acer_dmic_mixer[] = {
+	/* output mixer control */
+	HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol),
+	{
+		.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 = alc268_acer_master_sw_put,
+		.private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
+	},
+	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line In Boost", 0x1a, 0, HDA_INPUT),
+	{ }
+};
+
 static struct hda_verb alc268_acer_aspire_one_verbs[] = {
 static struct hda_verb alc268_acer_aspire_one_verbs[] = {
 	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
 	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
 	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
 	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
@@ -11218,10 +11328,6 @@ static struct hda_verb alc268_volume_init_verbs[] = {
 	{ }
 	{ }
 };
 };
 
 
-#define alc268_mux_enum_info alc_mux_enum_info
-#define alc268_mux_enum_get alc_mux_enum_get
-#define alc268_mux_enum_put alc_mux_enum_put
-
 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),
@@ -11233,9 +11339,9 @@ static struct snd_kcontrol_new alc268_capture_alt_mixer[] = {
 		/* .name = "Capture Source", */
 		/* .name = "Capture Source", */
 		.name = "Input Source",
 		.name = "Input Source",
 		.count = 1,
 		.count = 1,
-		.info = alc268_mux_enum_info,
-		.get = alc268_mux_enum_get,
-		.put = alc268_mux_enum_put,
+		.info = alc_mux_enum_info,
+		.get = alc_mux_enum_get,
+		.put = alc_mux_enum_put,
 	},
 	},
 	{ } /* end */
 	{ } /* end */
 };
 };
@@ -11253,9 +11359,9 @@ static struct snd_kcontrol_new alc268_capture_mixer[] = {
 		/* .name = "Capture Source", */
 		/* .name = "Capture Source", */
 		.name = "Input Source",
 		.name = "Input Source",
 		.count = 2,
 		.count = 2,
-		.info = alc268_mux_enum_info,
-		.get = alc268_mux_enum_get,
-		.put = alc268_mux_enum_put,
+		.info = alc_mux_enum_info,
+		.get = alc_mux_enum_get,
+		.put = alc_mux_enum_put,
 	},
 	},
 	{ } /* end */
 	{ } /* end */
 };
 };
@@ -11271,6 +11377,15 @@ static struct hda_input_mux alc268_capture_source = {
 };
 };
 
 
 static struct hda_input_mux alc268_acer_capture_source = {
 static struct hda_input_mux alc268_acer_capture_source = {
+	.num_items = 3,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "Internal Mic", 0x1 },
+		{ "Line", 0x2 },
+	},
+};
+
+static struct hda_input_mux alc268_acer_dmic_capture_source = {
 	.num_items = 3,
 	.num_items = 3,
 	.items = {
 	.items = {
 		{ "Mic", 0x0 },
 		{ "Mic", 0x0 },
@@ -11512,13 +11627,13 @@ static int alc268_parse_auto_config(struct hda_codec *codec)
 	if (spec->autocfg.dig_out_pin)
 	if (spec->autocfg.dig_out_pin)
 		spec->multiout.dig_out_nid = ALC268_DIGOUT_NID;
 		spec->multiout.dig_out_nid = ALC268_DIGOUT_NID;
 
 
-	if (spec->kctl_alloc)
-		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+	if (spec->kctls.list)
+		add_mixer(spec, spec->kctls.list);
 
 
 	if (spec->autocfg.speaker_pins[0] != 0x1d)
 	if (spec->autocfg.speaker_pins[0] != 0x1d)
-		spec->mixers[spec->num_mixers++] = alc268_beep_mixer;
+		add_mixer(spec, alc268_beep_mixer);
 
 
-	spec->init_verbs[spec->num_init_verbs++] = alc268_volume_init_verbs;
+	add_verb(spec, alc268_volume_init_verbs);
 	spec->num_mux_defs = 1;
 	spec->num_mux_defs = 1;
 	spec->input_mux = &spec->private_imux;
 	spec->input_mux = &spec->private_imux;
 
 
@@ -11554,6 +11669,7 @@ static const char *alc268_models[ALC268_MODEL_LAST] = {
 	[ALC268_3ST]		= "3stack",
 	[ALC268_3ST]		= "3stack",
 	[ALC268_TOSHIBA]	= "toshiba",
 	[ALC268_TOSHIBA]	= "toshiba",
 	[ALC268_ACER]		= "acer",
 	[ALC268_ACER]		= "acer",
+	[ALC268_ACER_DMIC]	= "acer-dmic",
 	[ALC268_ACER_ASPIRE_ONE]	= "acer-aspire",
 	[ALC268_ACER_ASPIRE_ONE]	= "acer-aspire",
 	[ALC268_DELL]		= "dell",
 	[ALC268_DELL]		= "dell",
 	[ALC268_ZEPTO]		= "zepto",
 	[ALC268_ZEPTO]		= "zepto",
@@ -11649,6 +11765,23 @@ static struct alc_config_preset alc268_presets[] = {
 		.unsol_event = alc268_acer_unsol_event,
 		.unsol_event = alc268_acer_unsol_event,
 		.init_hook = alc268_acer_init_hook,
 		.init_hook = alc268_acer_init_hook,
 	},
 	},
+	[ALC268_ACER_DMIC] = {
+		.mixers = { alc268_acer_dmic_mixer, alc268_capture_alt_mixer,
+			    alc268_beep_mixer },
+		.init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
+				alc268_acer_verbs },
+		.num_dacs = ARRAY_SIZE(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,
+		.num_channel_mode = ARRAY_SIZE(alc268_modes),
+		.channel_mode = alc268_modes,
+		.input_mux = &alc268_acer_dmic_capture_source,
+		.unsol_event = alc268_acer_unsol_event,
+		.init_hook = alc268_acer_init_hook,
+	},
 	[ALC268_ACER_ASPIRE_ONE] = {
 	[ALC268_ACER_ASPIRE_ONE] = {
 		.mixers = { alc268_acer_aspire_one_mixer,
 		.mixers = { alc268_acer_aspire_one_mixer,
 				alc268_capture_alt_mixer },
 				alc268_capture_alt_mixer },
@@ -11787,15 +11920,11 @@ static int patch_alc268(struct hda_codec *codec)
 		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 = 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);
-			spec->mixers[spec->num_mixers] =
-					alc268_capture_alt_mixer;
-			spec->num_mixers++;
+			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);
-			spec->mixers[spec->num_mixers] =
-				alc268_capture_mixer;
-			spec->num_mixers++;
+			add_mixer(spec, alc268_capture_mixer);
 		}
 		}
 		spec->capsrc_nids = alc268_capsrc_nids;
 		spec->capsrc_nids = alc268_capsrc_nids;
 		/* set default input source */
 		/* set default input source */
@@ -11811,6 +11940,8 @@ static int patch_alc268(struct hda_codec *codec)
 	if (board_config == ALC268_AUTO)
 	if (board_config == ALC268_AUTO)
 		spec->init_hook = alc268_auto_init;
 		spec->init_hook = alc268_auto_init;
 
 
+	codec->proc_widget_hook = print_realtek_coef;
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -11893,6 +12024,31 @@ static struct snd_kcontrol_new alc269_quanta_fl1_mixer[] = {
 	{ }
 	{ }
 };
 };
 
 
+static struct snd_kcontrol_new alc269_lifebook_mixer[] = {
+	/* output mixer control */
+	HDA_BIND_VOL("Master Playback Volume", &alc268_acer_bind_master_vol),
+	{
+		.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 = alc268_acer_master_sw_put,
+		.private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
+	},
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+	HDA_CODEC_VOLUME("Internal Mic Boost", 0x19, 0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x0b, 0x03, HDA_INPUT),
+	HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x0b, 0x03, HDA_INPUT),
+	HDA_CODEC_VOLUME("Dock Mic Boost", 0x1b, 0, HDA_INPUT),
+	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	{ }
+};
+
 /* bind volumes of both NID 0x0c and 0x0d */
 /* bind volumes of both NID 0x0c and 0x0d */
 static struct hda_bind_ctls alc269_epc_bind_vol = {
 static struct hda_bind_ctls alc269_epc_bind_vol = {
 	.ops = &snd_hda_bind_vol,
 	.ops = &snd_hda_bind_vol,
@@ -11911,28 +12067,18 @@ static struct snd_kcontrol_new alc269_eeepc_mixer[] = {
 };
 };
 
 
 /* capture mixer elements */
 /* capture mixer elements */
-static struct snd_kcontrol_new alc269_capture_mixer[] = {
+static struct snd_kcontrol_new alc269_epc_capture_mixer[] = {
 	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-	{
-		.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,
-	},
+	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
-/* capture mixer elements */
-static struct snd_kcontrol_new alc269_epc_capture_mixer[] = {
-	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+/* FSC amilo */
+static struct snd_kcontrol_new alc269_fujitsu_mixer[] = {
+	HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+	HDA_BIND_VOL("PCM Playback Volume", &alc269_epc_bind_vol),
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
@@ -11953,6 +12099,20 @@ static struct hda_verb alc269_quanta_fl1_verbs[] = {
 	{ }
 	{ }
 };
 };
 
 
+static struct hda_verb alc269_lifebook_verbs[] = {
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
+	{0x1a, AC_VERB_SET_CONNECT_SEL, 0x01},
+	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+	{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{ }
+};
+
 /* toggle speaker-output according to the hp-jack state */
 /* toggle speaker-output according to the hp-jack state */
 static void alc269_quanta_fl1_speaker_automute(struct hda_codec *codec)
 static void alc269_quanta_fl1_speaker_automute(struct hda_codec *codec)
 {
 {
@@ -11978,6 +12138,37 @@ static void alc269_quanta_fl1_speaker_automute(struct hda_codec *codec)
 			AC_VERB_SET_PROC_COEF, 0x480);
 			AC_VERB_SET_PROC_COEF, 0x480);
 }
 }
 
 
+/* toggle speaker-output according to the hp-jacks state */
+static void alc269_lifebook_speaker_automute(struct hda_codec *codec)
+{
+	unsigned int present;
+	unsigned char bits;
+
+	/* Check laptop headphone socket */
+	present = snd_hda_codec_read(codec, 0x15, 0,
+			AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+
+	/* Check port replicator headphone socket */
+	present |= snd_hda_codec_read(codec, 0x1a, 0,
+			AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+
+	bits = present ? AMP_IN_MUTE(0) : 0;
+	snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 0,
+			AMP_IN_MUTE(0), bits);
+	snd_hda_codec_amp_stereo(codec, 0x0c, HDA_INPUT, 1,
+			AMP_IN_MUTE(0), bits);
+
+	snd_hda_codec_write(codec, 0x20, 0,
+			AC_VERB_SET_COEF_INDEX, 0x0c);
+	snd_hda_codec_write(codec, 0x20, 0,
+			AC_VERB_SET_PROC_COEF, 0x680);
+
+	snd_hda_codec_write(codec, 0x20, 0,
+			AC_VERB_SET_COEF_INDEX, 0x0c);
+	snd_hda_codec_write(codec, 0x20, 0,
+			AC_VERB_SET_PROC_COEF, 0x480);
+}
+
 static void alc269_quanta_fl1_mic_automute(struct hda_codec *codec)
 static void alc269_quanta_fl1_mic_automute(struct hda_codec *codec)
 {
 {
 	unsigned int present;
 	unsigned int present;
@@ -11988,6 +12179,29 @@ static void alc269_quanta_fl1_mic_automute(struct hda_codec *codec)
 			    AC_VERB_SET_CONNECT_SEL, present ? 0x0 : 0x1);
 			    AC_VERB_SET_CONNECT_SEL, present ? 0x0 : 0x1);
 }
 }
 
 
+static void alc269_lifebook_mic_autoswitch(struct hda_codec *codec)
+{
+	unsigned int present_laptop;
+	unsigned int present_dock;
+
+	present_laptop = snd_hda_codec_read(codec, 0x18, 0,
+				AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+
+	present_dock = snd_hda_codec_read(codec, 0x1b, 0,
+				AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+
+	/* Laptop mic port overrides dock mic port, design decision */
+	if (present_dock)
+		snd_hda_codec_write(codec, 0x23, 0,
+				AC_VERB_SET_CONNECT_SEL, 0x3);
+	if (present_laptop)
+		snd_hda_codec_write(codec, 0x23, 0,
+				AC_VERB_SET_CONNECT_SEL, 0x0);
+	if (!present_dock && !present_laptop)
+		snd_hda_codec_write(codec, 0x23, 0,
+				AC_VERB_SET_CONNECT_SEL, 0x1);
+}
+
 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)
 {
 {
@@ -11997,12 +12211,27 @@ static void alc269_quanta_fl1_unsol_event(struct hda_codec *codec,
 		alc269_quanta_fl1_mic_automute(codec);
 		alc269_quanta_fl1_mic_automute(codec);
 }
 }
 
 
+static void alc269_lifebook_unsol_event(struct hda_codec *codec,
+					unsigned int res)
+{
+	if ((res >> 26) == ALC880_HP_EVENT)
+		alc269_lifebook_speaker_automute(codec);
+	if ((res >> 26) == ALC880_MIC_EVENT)
+		alc269_lifebook_mic_autoswitch(codec);
+}
+
 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);
 	alc269_quanta_fl1_mic_automute(codec);
 }
 }
 
 
+static void alc269_lifebook_init_hook(struct hda_codec *codec)
+{
+	alc269_lifebook_speaker_automute(codec);
+	alc269_lifebook_mic_autoswitch(codec);
+}
+
 static struct hda_verb alc269_eeepc_dmic_init_verbs[] = {
 static struct hda_verb alc269_eeepc_dmic_init_verbs[] = {
 	{0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
 	{0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
 	{0x23, AC_VERB_SET_CONNECT_SEL, 0x05},
 	{0x23, AC_VERB_SET_CONNECT_SEL, 0x05},
@@ -12303,17 +12532,17 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
 	if (spec->autocfg.dig_out_pin)
 	if (spec->autocfg.dig_out_pin)
 		spec->multiout.dig_out_nid = ALC269_DIGOUT_NID;
 		spec->multiout.dig_out_nid = ALC269_DIGOUT_NID;
 
 
-	if (spec->kctl_alloc)
-		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+	if (spec->kctls.list)
+		add_mixer(spec, spec->kctls.list);
 
 
 	/* create a beep mixer control if the pin 0x1d isn't assigned */
 	/* create a beep mixer control if the pin 0x1d isn't assigned */
 	for (i = 0; i < ARRAY_SIZE(spec->autocfg.input_pins); i++)
 	for (i = 0; i < ARRAY_SIZE(spec->autocfg.input_pins); i++)
 		if (spec->autocfg.input_pins[i] == 0x1d)
 		if (spec->autocfg.input_pins[i] == 0x1d)
 			break;
 			break;
 	if (i >= ARRAY_SIZE(spec->autocfg.input_pins))
 	if (i >= ARRAY_SIZE(spec->autocfg.input_pins))
-		spec->mixers[spec->num_mixers++] = alc269_beep_mixer;
+		add_mixer(spec, alc269_beep_mixer);
 
 
-	spec->init_verbs[spec->num_init_verbs++] = alc269_init_verbs;
+	add_verb(spec, alc269_init_verbs);
 	spec->num_mux_defs = 1;
 	spec->num_mux_defs = 1;
 	spec->input_mux = &spec->private_imux;
 	spec->input_mux = &spec->private_imux;
 	/* set default input source */
 	/* set default input source */
@@ -12325,8 +12554,8 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
 
 
-	spec->mixers[spec->num_mixers] = alc269_capture_mixer;
-	spec->num_mixers++;
+	if (!spec->cap_mixer)
+		set_capture_mixer(spec);
 
 
 	store_pin_configs(codec);
 	store_pin_configs(codec);
 	return 1;
 	return 1;
@@ -12355,7 +12584,9 @@ static const char *alc269_models[ALC269_MODEL_LAST] = {
 	[ALC269_BASIC]			= "basic",
 	[ALC269_BASIC]			= "basic",
 	[ALC269_QUANTA_FL1]		= "quanta",
 	[ALC269_QUANTA_FL1]		= "quanta",
 	[ALC269_ASUS_EEEPC_P703]	= "eeepc-p703",
 	[ALC269_ASUS_EEEPC_P703]	= "eeepc-p703",
-	[ALC269_ASUS_EEEPC_P901]	= "eeepc-p901"
+	[ALC269_ASUS_EEEPC_P901]	= "eeepc-p901",
+	[ALC269_FUJITSU]		= "fujitsu",
+	[ALC269_LIFEBOOK]		= "lifebook"
 };
 };
 
 
 static struct snd_pci_quirk alc269_cfg_tbl[] = {
 static struct snd_pci_quirk alc269_cfg_tbl[] = {
@@ -12366,12 +12597,14 @@ static struct snd_pci_quirk alc269_cfg_tbl[] = {
 		      ALC269_ASUS_EEEPC_P901),
 		      ALC269_ASUS_EEEPC_P901),
 	SND_PCI_QUIRK(0x1043, 0x834a, "ASUS Eeepc S101",
 	SND_PCI_QUIRK(0x1043, 0x834a, "ASUS Eeepc S101",
 		      ALC269_ASUS_EEEPC_P901),
 		      ALC269_ASUS_EEEPC_P901),
+	SND_PCI_QUIRK(0x1734, 0x115d, "FSC Amilo", ALC269_FUJITSU),
+	SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook ICH9M-based", ALC269_LIFEBOOK),
 	{}
 	{}
 };
 };
 
 
 static struct alc_config_preset alc269_presets[] = {
 static struct alc_config_preset alc269_presets[] = {
 	[ALC269_BASIC] = {
 	[ALC269_BASIC] = {
-		.mixers = { alc269_base_mixer, alc269_capture_mixer },
+		.mixers = { alc269_base_mixer },
 		.init_verbs = { alc269_init_verbs },
 		.init_verbs = { alc269_init_verbs },
 		.num_dacs = ARRAY_SIZE(alc269_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc269_dac_nids),
 		.dac_nids = alc269_dac_nids,
 		.dac_nids = alc269_dac_nids,
@@ -12393,7 +12626,8 @@ static struct alc_config_preset alc269_presets[] = {
 		.init_hook = alc269_quanta_fl1_init_hook,
 		.init_hook = alc269_quanta_fl1_init_hook,
 	},
 	},
 	[ALC269_ASUS_EEEPC_P703] = {
 	[ALC269_ASUS_EEEPC_P703] = {
-		.mixers = { alc269_eeepc_mixer, alc269_epc_capture_mixer },
+		.mixers = { alc269_eeepc_mixer },
+		.cap_mixer = alc269_epc_capture_mixer,
 		.init_verbs = { alc269_init_verbs,
 		.init_verbs = { alc269_init_verbs,
 				alc269_eeepc_amic_init_verbs },
 				alc269_eeepc_amic_init_verbs },
 		.num_dacs = ARRAY_SIZE(alc269_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc269_dac_nids),
@@ -12406,7 +12640,22 @@ static struct alc_config_preset alc269_presets[] = {
 		.init_hook = alc269_eeepc_amic_inithook,
 		.init_hook = alc269_eeepc_amic_inithook,
 	},
 	},
 	[ALC269_ASUS_EEEPC_P901] = {
 	[ALC269_ASUS_EEEPC_P901] = {
-		.mixers = { alc269_eeepc_mixer, alc269_epc_capture_mixer},
+		.mixers = { alc269_eeepc_mixer },
+		.cap_mixer = alc269_epc_capture_mixer,
+		.init_verbs = { alc269_init_verbs,
+				alc269_eeepc_dmic_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc269_dac_nids),
+		.dac_nids = alc269_dac_nids,
+		.hp_nid = 0x03,
+		.num_channel_mode = ARRAY_SIZE(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,
+	},
+	[ALC269_FUJITSU] = {
+		.mixers = { alc269_fujitsu_mixer, alc269_beep_mixer },
+		.cap_mixer = alc269_epc_capture_mixer,
 		.init_verbs = { alc269_init_verbs,
 		.init_verbs = { alc269_init_verbs,
 				alc269_eeepc_dmic_init_verbs },
 				alc269_eeepc_dmic_init_verbs },
 		.num_dacs = ARRAY_SIZE(alc269_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc269_dac_nids),
@@ -12418,6 +12667,18 @@ static struct alc_config_preset alc269_presets[] = {
 		.unsol_event = alc269_eeepc_dmic_unsol_event,
 		.unsol_event = alc269_eeepc_dmic_unsol_event,
 		.init_hook = alc269_eeepc_dmic_inithook,
 		.init_hook = alc269_eeepc_dmic_inithook,
 	},
 	},
+	[ALC269_LIFEBOOK] = {
+		.mixers = { alc269_lifebook_mixer },
+		.init_verbs = { alc269_init_verbs, alc269_lifebook_verbs },
+		.num_dacs = ARRAY_SIZE(alc269_dac_nids),
+		.dac_nids = alc269_dac_nids,
+		.hp_nid = 0x03,
+		.num_channel_mode = ARRAY_SIZE(alc269_modes),
+		.channel_mode = alc269_modes,
+		.input_mux = &alc269_capture_source,
+		.unsol_event = alc269_lifebook_unsol_event,
+		.init_hook = alc269_lifebook_init_hook,
+	},
 };
 };
 
 
 static int patch_alc269(struct hda_codec *codec)
 static int patch_alc269(struct hda_codec *codec)
@@ -12472,6 +12733,8 @@ static int patch_alc269(struct hda_codec *codec)
 	spec->adc_nids = alc269_adc_nids;
 	spec->adc_nids = alc269_adc_nids;
 	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)
+		set_capture_mixer(spec);
 
 
 	codec->patch_ops = alc_patch_ops;
 	codec->patch_ops = alc_patch_ops;
 	if (board_config == ALC269_AUTO)
 	if (board_config == ALC269_AUTO)
@@ -12480,6 +12743,7 @@ static int patch_alc269(struct hda_codec *codec)
 	if (!spec->loopback.amplist)
 	if (!spec->loopback.amplist)
 		spec->loopback.amplist = alc269_loopbacks;
 		spec->loopback.amplist = alc269_loopbacks;
 #endif
 #endif
+	codec->proc_widget_hook = print_realtek_coef;
 
 
 	return 0;
 	return 0;
 }
 }
@@ -12612,17 +12876,6 @@ static struct snd_kcontrol_new alc861_base_mixer[] = {
 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
 	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
 
 
-        /* Capture mixer control */
-	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Capture Source",
-		.count = 1,
-		.info = alc_mux_enum_info,
-		.get = alc_mux_enum_get,
-		.put = alc_mux_enum_put,
-	},
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
@@ -12646,17 +12899,6 @@ static struct snd_kcontrol_new alc861_3ST_mixer[] = {
 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
 	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
 
 
-	/* Capture mixer control */
-	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Capture Source",
-		.count = 1,
-		.info = alc_mux_enum_info,
-		.get = alc_mux_enum_get,
-		.put = alc_mux_enum_put,
-	},
 	{
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.name = "Channel Mode",
 		.name = "Channel Mode",
@@ -12674,18 +12916,6 @@ static struct snd_kcontrol_new alc861_toshiba_mixer[] = {
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x15, 0x01, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x15, 0x01, HDA_INPUT),
 
 
-        /*Capture mixer control */
-	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Capture Source",
-		.count = 1,
-		.info = alc_mux_enum_info,
-		.get = alc_mux_enum_get,
-		.put = alc_mux_enum_put,
-	},
-
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
@@ -12709,17 +12939,6 @@ static struct snd_kcontrol_new alc861_uniwill_m31_mixer[] = {
 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
 	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_INPUT),
 
 
-	/* Capture mixer control */
-	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Capture Source",
-		.count = 1,
-		.info = alc_mux_enum_info,
-		.get = alc_mux_enum_get,
-		.put = alc_mux_enum_put,
-	},
 	{
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.name = "Channel Mode",
 		.name = "Channel Mode",
@@ -12751,17 +12970,6 @@ static struct snd_kcontrol_new alc861_asus_mixer[] = {
 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x10, 0x01, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x03, HDA_OUTPUT),
 
 
-	/* Capture mixer control */
-	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Capture Source",
-		.count = 1,
-		.info = alc_mux_enum_info,
-		.get = alc_mux_enum_get,
-		.put = alc_mux_enum_put,
-	},
 	{
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.name = "Channel Mode",
 		.name = "Channel Mode",
@@ -13293,25 +13501,6 @@ static int alc861_auto_create_analog_input_ctls(struct alc_spec *spec,
 	return 0;
 	return 0;
 }
 }
 
 
-static struct snd_kcontrol_new alc861_capture_mixer[] = {
-	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
-
-	{
-		.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,
-	},
-	{ } /* end */
-};
-
 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, int dac_idx)
@@ -13402,18 +13591,17 @@ static int alc861_parse_auto_config(struct hda_codec *codec)
 	if (spec->autocfg.dig_out_pin)
 	if (spec->autocfg.dig_out_pin)
 		spec->multiout.dig_out_nid = ALC861_DIGOUT_NID;
 		spec->multiout.dig_out_nid = ALC861_DIGOUT_NID;
 
 
-	if (spec->kctl_alloc)
-		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+	if (spec->kctls.list)
+		add_mixer(spec, spec->kctls.list);
 
 
-	spec->init_verbs[spec->num_init_verbs++] = alc861_auto_init_verbs;
+	add_verb(spec, alc861_auto_init_verbs);
 
 
 	spec->num_mux_defs = 1;
 	spec->num_mux_defs = 1;
 	spec->input_mux = &spec->private_imux;
 	spec->input_mux = &spec->private_imux;
 
 
 	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);
-	spec->mixers[spec->num_mixers] = alc861_capture_mixer;
-	spec->num_mixers++;
+	set_capture_mixer(spec);
 
 
 	store_pin_configs(codec);
 	store_pin_configs(codec);
 	return 1;
 	return 1;
@@ -13644,6 +13832,7 @@ static int patch_alc861(struct hda_codec *codec)
 	if (!spec->loopback.amplist)
 	if (!spec->loopback.amplist)
 		spec->loopback.amplist = alc861_loopbacks;
 		spec->loopback.amplist = alc861_loopbacks;
 #endif
 #endif
+	codec->proc_widget_hook = print_realtek_coef;
 
 
 	return 0;
 	return 0;
 }
 }
@@ -13709,11 +13898,6 @@ static struct hda_input_mux alc861vd_hp_capture_source = {
 	},
 	},
 };
 };
 
 
-#define alc861vd_mux_enum_info alc_mux_enum_info
-#define alc861vd_mux_enum_get alc_mux_enum_get
-/* ALC861VD has the ALC882-type input selection (but has only one ADC) */
-#define alc861vd_mux_enum_put alc882_mux_enum_put
-
 /*
 /*
  * 2ch mode
  * 2ch mode
  */
  */
@@ -13759,25 +13943,6 @@ static struct snd_kcontrol_new alc861vd_chmode_mixer[] = {
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
-static struct snd_kcontrol_new alc861vd_capture_mixer[] = {
-	HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
-
-	{
-		.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 = alc861vd_mux_enum_info,
-		.get = alc861vd_mux_enum_get,
-		.put = alc861vd_mux_enum_put,
-	},
-	{ } /* end */
-};
-
 /* 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
  */
  */
@@ -14169,6 +14334,7 @@ static void alc861vd_dallas_unsol_event(struct hda_codec *codec, unsigned int re
 static const char *alc861vd_models[ALC861VD_MODEL_LAST] = {
 static const char *alc861vd_models[ALC861VD_MODEL_LAST] = {
 	[ALC660VD_3ST]		= "3stack-660",
 	[ALC660VD_3ST]		= "3stack-660",
 	[ALC660VD_3ST_DIG]	= "3stack-660-digout",
 	[ALC660VD_3ST_DIG]	= "3stack-660-digout",
+	[ALC660VD_ASUS_V1S]	= "asus-v1s",
 	[ALC861VD_3ST]		= "3stack",
 	[ALC861VD_3ST]		= "3stack",
 	[ALC861VD_3ST_DIG]	= "3stack-digout",
 	[ALC861VD_3ST_DIG]	= "3stack-digout",
 	[ALC861VD_6ST_DIG]	= "6stack-digout",
 	[ALC861VD_6ST_DIG]	= "6stack-digout",
@@ -14183,7 +14349,7 @@ static struct snd_pci_quirk alc861vd_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_HP),
 	SND_PCI_QUIRK(0x103c, 0x30bf, "HP TX1000", ALC861VD_HP),
 	SND_PCI_QUIRK(0x1043, 0x12e2, "Asus z35m", ALC660VD_3ST),
 	SND_PCI_QUIRK(0x1043, 0x12e2, "Asus z35m", ALC660VD_3ST),
 	SND_PCI_QUIRK(0x1043, 0x1339, "Asus G1", ALC660VD_3ST),
 	SND_PCI_QUIRK(0x1043, 0x1339, "Asus G1", ALC660VD_3ST),
-	SND_PCI_QUIRK(0x1043, 0x1633, "Asus V1Sn", ALC861VD_LENOVO),
+	SND_PCI_QUIRK(0x1043, 0x1633, "Asus V1Sn", ALC660VD_ASUS_V1S),
 	SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660VD_3ST_DIG),
 	SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660VD_3ST_DIG),
 	SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST),
 	SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST),
 	SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO),
 	SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO),
@@ -14290,6 +14456,21 @@ static struct alc_config_preset alc861vd_presets[] = {
 		.unsol_event = alc861vd_dallas_unsol_event,
 		.unsol_event = alc861vd_dallas_unsol_event,
 		.init_hook = alc861vd_dallas_automute,
 		.init_hook = alc861vd_dallas_automute,
 	},
 	},
+	[ALC660VD_ASUS_V1S] = {
+		.mixers = { alc861vd_lenovo_mixer },
+		.init_verbs = { alc861vd_volume_init_verbs,
+				alc861vd_3stack_init_verbs,
+				alc861vd_eapd_verbs,
+				alc861vd_lenovo_unsol_verbs },
+		.num_dacs = ARRAY_SIZE(alc660vd_dac_nids),
+		.dac_nids = alc660vd_dac_nids,
+		.dig_out_nid = ALC861VD_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
+		.channel_mode = alc861vd_3stack_2ch_modes,
+		.input_mux = &alc861vd_capture_source,
+		.unsol_event = alc861vd_lenovo_unsol_event,
+		.init_hook = alc861vd_lenovo_automute,
+	},
 };
 };
 
 
 /*
 /*
@@ -14514,11 +14695,10 @@ static int alc861vd_parse_auto_config(struct hda_codec *codec)
 	if (spec->autocfg.dig_out_pin)
 	if (spec->autocfg.dig_out_pin)
 		spec->multiout.dig_out_nid = ALC861VD_DIGOUT_NID;
 		spec->multiout.dig_out_nid = ALC861VD_DIGOUT_NID;
 
 
-	if (spec->kctl_alloc)
-		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+	if (spec->kctls.list)
+		add_mixer(spec, spec->kctls.list);
 
 
-	spec->init_verbs[spec->num_init_verbs++]
-		= alc861vd_volume_init_verbs;
+	add_verb(spec, alc861vd_volume_init_verbs);
 
 
 	spec->num_mux_defs = 1;
 	spec->num_mux_defs = 1;
 	spec->input_mux = &spec->private_imux;
 	spec->input_mux = &spec->private_imux;
@@ -14585,7 +14765,7 @@ static int patch_alc861vd(struct hda_codec *codec)
 		spec->stream_name_analog = "ALC660-VD Analog";
 		spec->stream_name_analog = "ALC660-VD Analog";
 		spec->stream_name_digital = "ALC660-VD Digital";
 		spec->stream_name_digital = "ALC660-VD Digital";
 		/* always turn on EAPD */
 		/* always turn on EAPD */
-		spec->init_verbs[spec->num_init_verbs++] = alc660vd_eapd_verbs;
+		add_verb(spec, alc660vd_eapd_verbs);
 	} else {
 	} else {
 		spec->stream_name_analog = "ALC861VD Analog";
 		spec->stream_name_analog = "ALC861VD Analog";
 		spec->stream_name_digital = "ALC861VD Digital";
 		spec->stream_name_digital = "ALC861VD Digital";
@@ -14600,9 +14780,9 @@ static int patch_alc861vd(struct hda_codec *codec)
 	spec->adc_nids = alc861vd_adc_nids;
 	spec->adc_nids = alc861vd_adc_nids;
 	spec->num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids);
 	spec->num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids);
 	spec->capsrc_nids = alc861vd_capsrc_nids;
 	spec->capsrc_nids = alc861vd_capsrc_nids;
+	spec->is_mix_capture = 1;
 
 
-	spec->mixers[spec->num_mixers] = alc861vd_capture_mixer;
-	spec->num_mixers++;
+	set_capture_mixer(spec);
 
 
 	spec->vmaster_nid = 0x02;
 	spec->vmaster_nid = 0x02;
 
 
@@ -14614,6 +14794,7 @@ static int patch_alc861vd(struct hda_codec *codec)
 	if (!spec->loopback.amplist)
 	if (!spec->loopback.amplist)
 		spec->loopback.amplist = alc861vd_loopbacks;
 		spec->loopback.amplist = alc861vd_loopbacks;
 #endif
 #endif
+	codec->proc_widget_hook = print_realtek_coef;
 
 
 	return 0;
 	return 0;
 }
 }
@@ -14689,10 +14870,6 @@ static struct hda_input_mux alc663_m51va_capture_source = {
 	},
 	},
 };
 };
 
 
-#define alc662_mux_enum_info alc_mux_enum_info
-#define alc662_mux_enum_get alc_mux_enum_get
-#define alc662_mux_enum_put alc882_mux_enum_put
-
 /*
 /*
  * 2ch mode
  * 2ch mode
  */
  */
@@ -15278,25 +15455,6 @@ static struct hda_verb alc662_ecs_init_verbs[] = {
 	{}
 	{}
 };
 };
 
 
-/* capture mixer elements */
-static struct snd_kcontrol_new alc662_capture_mixer[] = {
-	HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
-	{
-		.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 = alc662_mux_enum_info,
-		.get = alc662_mux_enum_get,
-		.put = alc662_mux_enum_put,
-	},
-	{ } /* end */
-};
-
 static struct snd_kcontrol_new alc662_auto_capture_mixer[] = {
 static struct snd_kcontrol_new alc662_auto_capture_mixer[] = {
 	HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
@@ -15868,7 +16026,7 @@ static struct snd_pci_quirk alc662_cfg_tbl[] = {
 
 
 static struct alc_config_preset alc662_presets[] = {
 static struct alc_config_preset alc662_presets[] = {
 	[ALC662_3ST_2ch_DIG] = {
 	[ALC662_3ST_2ch_DIG] = {
-		.mixers = { alc662_3ST_2ch_mixer, alc662_capture_mixer },
+		.mixers = { alc662_3ST_2ch_mixer },
 		.init_verbs = { alc662_init_verbs },
 		.init_verbs = { alc662_init_verbs },
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
 		.dac_nids = alc662_dac_nids,
 		.dac_nids = alc662_dac_nids,
@@ -15879,8 +16037,7 @@ static struct alc_config_preset alc662_presets[] = {
 		.input_mux = &alc662_capture_source,
 		.input_mux = &alc662_capture_source,
 	},
 	},
 	[ALC662_3ST_6ch_DIG] = {
 	[ALC662_3ST_6ch_DIG] = {
-		.mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer,
-			    alc662_capture_mixer },
+		.mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer },
 		.init_verbs = { alc662_init_verbs },
 		.init_verbs = { alc662_init_verbs },
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
 		.dac_nids = alc662_dac_nids,
 		.dac_nids = alc662_dac_nids,
@@ -15892,8 +16049,7 @@ static struct alc_config_preset alc662_presets[] = {
 		.input_mux = &alc662_capture_source,
 		.input_mux = &alc662_capture_source,
 	},
 	},
 	[ALC662_3ST_6ch] = {
 	[ALC662_3ST_6ch] = {
-		.mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer,
-			    alc662_capture_mixer },
+		.mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer },
 		.init_verbs = { alc662_init_verbs },
 		.init_verbs = { alc662_init_verbs },
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
 		.dac_nids = alc662_dac_nids,
 		.dac_nids = alc662_dac_nids,
@@ -15903,8 +16059,7 @@ static struct alc_config_preset alc662_presets[] = {
 		.input_mux = &alc662_capture_source,
 		.input_mux = &alc662_capture_source,
 	},
 	},
 	[ALC662_5ST_DIG] = {
 	[ALC662_5ST_DIG] = {
-		.mixers = { alc662_base_mixer, alc662_chmode_mixer,
-			    alc662_capture_mixer },
+		.mixers = { alc662_base_mixer, alc662_chmode_mixer },
 		.init_verbs = { alc662_init_verbs },
 		.init_verbs = { alc662_init_verbs },
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
 		.dac_nids = alc662_dac_nids,
 		.dac_nids = alc662_dac_nids,
@@ -15915,7 +16070,7 @@ static struct alc_config_preset alc662_presets[] = {
 		.input_mux = &alc662_capture_source,
 		.input_mux = &alc662_capture_source,
 	},
 	},
 	[ALC662_LENOVO_101E] = {
 	[ALC662_LENOVO_101E] = {
-		.mixers = { alc662_lenovo_101e_mixer, alc662_capture_mixer },
+		.mixers = { alc662_lenovo_101e_mixer },
 		.init_verbs = { alc662_init_verbs, alc662_sue_init_verbs },
 		.init_verbs = { alc662_init_verbs, alc662_sue_init_verbs },
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
 		.dac_nids = alc662_dac_nids,
 		.dac_nids = alc662_dac_nids,
@@ -15926,7 +16081,7 @@ static struct alc_config_preset alc662_presets[] = {
 		.init_hook = alc662_lenovo_101e_all_automute,
 		.init_hook = alc662_lenovo_101e_all_automute,
 	},
 	},
 	[ALC662_ASUS_EEEPC_P701] = {
 	[ALC662_ASUS_EEEPC_P701] = {
-		.mixers = { alc662_eeepc_p701_mixer, alc662_capture_mixer },
+		.mixers = { alc662_eeepc_p701_mixer },
 		.init_verbs = { alc662_init_verbs,
 		.init_verbs = { alc662_init_verbs,
 				alc662_eeepc_sue_init_verbs },
 				alc662_eeepc_sue_init_verbs },
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -15938,7 +16093,7 @@ static struct alc_config_preset alc662_presets[] = {
 		.init_hook = alc662_eeepc_inithook,
 		.init_hook = alc662_eeepc_inithook,
 	},
 	},
 	[ALC662_ASUS_EEEPC_EP20] = {
 	[ALC662_ASUS_EEEPC_EP20] = {
-		.mixers = { alc662_eeepc_ep20_mixer, alc662_capture_mixer,
+		.mixers = { alc662_eeepc_ep20_mixer,
 			    alc662_chmode_mixer },
 			    alc662_chmode_mixer },
 		.init_verbs = { alc662_init_verbs,
 		.init_verbs = { alc662_init_verbs,
 				alc662_eeepc_ep20_sue_init_verbs },
 				alc662_eeepc_ep20_sue_init_verbs },
@@ -15951,7 +16106,7 @@ static struct alc_config_preset alc662_presets[] = {
 		.init_hook = alc662_eeepc_ep20_inithook,
 		.init_hook = alc662_eeepc_ep20_inithook,
 	},
 	},
 	[ALC662_ECS] = {
 	[ALC662_ECS] = {
-		.mixers = { alc662_ecs_mixer, alc662_capture_mixer },
+		.mixers = { alc662_ecs_mixer },
 		.init_verbs = { alc662_init_verbs,
 		.init_verbs = { alc662_init_verbs,
 				alc662_ecs_init_verbs },
 				alc662_ecs_init_verbs },
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -15963,7 +16118,7 @@ static struct alc_config_preset alc662_presets[] = {
 		.init_hook = alc662_eeepc_inithook,
 		.init_hook = alc662_eeepc_inithook,
 	},
 	},
 	[ALC663_ASUS_M51VA] = {
 	[ALC663_ASUS_M51VA] = {
-		.mixers = { alc663_m51va_mixer, alc662_capture_mixer},
+		.mixers = { alc663_m51va_mixer },
 		.init_verbs = { alc662_init_verbs, alc663_m51va_init_verbs },
 		.init_verbs = { alc662_init_verbs, alc663_m51va_init_verbs },
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
 		.dac_nids = alc662_dac_nids,
 		.dac_nids = alc662_dac_nids,
@@ -15975,7 +16130,7 @@ static struct alc_config_preset alc662_presets[] = {
 		.init_hook = alc663_m51va_inithook,
 		.init_hook = alc663_m51va_inithook,
 	},
 	},
 	[ALC663_ASUS_G71V] = {
 	[ALC663_ASUS_G71V] = {
-		.mixers = { alc663_g71v_mixer, alc662_capture_mixer},
+		.mixers = { alc663_g71v_mixer },
 		.init_verbs = { alc662_init_verbs, alc663_g71v_init_verbs },
 		.init_verbs = { alc662_init_verbs, alc663_g71v_init_verbs },
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
 		.dac_nids = alc662_dac_nids,
 		.dac_nids = alc662_dac_nids,
@@ -15987,7 +16142,7 @@ static struct alc_config_preset alc662_presets[] = {
 		.init_hook = alc663_g71v_inithook,
 		.init_hook = alc663_g71v_inithook,
 	},
 	},
 	[ALC663_ASUS_H13] = {
 	[ALC663_ASUS_H13] = {
-		.mixers = { alc663_m51va_mixer, alc662_capture_mixer},
+		.mixers = { alc663_m51va_mixer },
 		.init_verbs = { alc662_init_verbs, alc663_m51va_init_verbs },
 		.init_verbs = { alc662_init_verbs, alc663_m51va_init_verbs },
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
 		.dac_nids = alc662_dac_nids,
 		.dac_nids = alc662_dac_nids,
@@ -15998,7 +16153,7 @@ static struct alc_config_preset alc662_presets[] = {
 		.init_hook = alc663_m51va_inithook,
 		.init_hook = alc663_m51va_inithook,
 	},
 	},
 	[ALC663_ASUS_G50V] = {
 	[ALC663_ASUS_G50V] = {
-		.mixers = { alc663_g50v_mixer, alc662_capture_mixer},
+		.mixers = { alc663_g50v_mixer },
 		.init_verbs = { alc662_init_verbs, alc663_g50v_init_verbs },
 		.init_verbs = { alc662_init_verbs, alc663_g50v_init_verbs },
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
 		.dac_nids = alc662_dac_nids,
 		.dac_nids = alc662_dac_nids,
@@ -16010,7 +16165,8 @@ static struct alc_config_preset alc662_presets[] = {
 		.init_hook = alc663_g50v_inithook,
 		.init_hook = alc663_g50v_inithook,
 	},
 	},
 	[ALC663_ASUS_MODE1] = {
 	[ALC663_ASUS_MODE1] = {
-		.mixers = { alc663_m51va_mixer, alc662_auto_capture_mixer },
+		.mixers = { alc663_m51va_mixer },
+		.cap_mixer = alc662_auto_capture_mixer,
 		.init_verbs = { alc662_init_verbs,
 		.init_verbs = { alc662_init_verbs,
 				alc663_21jd_amic_init_verbs },
 				alc663_21jd_amic_init_verbs },
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -16024,7 +16180,8 @@ static struct alc_config_preset alc662_presets[] = {
 		.init_hook = alc663_mode1_inithook,
 		.init_hook = alc663_mode1_inithook,
 	},
 	},
 	[ALC662_ASUS_MODE2] = {
 	[ALC662_ASUS_MODE2] = {
-		.mixers = { alc662_1bjd_mixer, alc662_auto_capture_mixer },
+		.mixers = { alc662_1bjd_mixer },
+		.cap_mixer = alc662_auto_capture_mixer,
 		.init_verbs = { alc662_init_verbs,
 		.init_verbs = { alc662_init_verbs,
 				alc662_1bjd_amic_init_verbs },
 				alc662_1bjd_amic_init_verbs },
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -16037,7 +16194,8 @@ static struct alc_config_preset alc662_presets[] = {
 		.init_hook = alc662_mode2_inithook,
 		.init_hook = alc662_mode2_inithook,
 	},
 	},
 	[ALC663_ASUS_MODE3] = {
 	[ALC663_ASUS_MODE3] = {
-		.mixers = { alc663_two_hp_m1_mixer, alc662_auto_capture_mixer },
+		.mixers = { alc663_two_hp_m1_mixer },
+		.cap_mixer = alc662_auto_capture_mixer,
 		.init_verbs = { alc662_init_verbs,
 		.init_verbs = { alc662_init_verbs,
 				alc663_two_hp_amic_m1_init_verbs },
 				alc663_two_hp_amic_m1_init_verbs },
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -16051,8 +16209,8 @@ static struct alc_config_preset alc662_presets[] = {
 		.init_hook = alc663_mode3_inithook,
 		.init_hook = alc663_mode3_inithook,
 	},
 	},
 	[ALC663_ASUS_MODE4] = {
 	[ALC663_ASUS_MODE4] = {
-		.mixers = { alc663_asus_21jd_clfe_mixer,
-				alc662_auto_capture_mixer},
+		.mixers = { alc663_asus_21jd_clfe_mixer },
+		.cap_mixer = alc662_auto_capture_mixer,
 		.init_verbs = { alc662_init_verbs,
 		.init_verbs = { alc662_init_verbs,
 				alc663_21jd_amic_init_verbs},
 				alc663_21jd_amic_init_verbs},
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -16066,8 +16224,8 @@ static struct alc_config_preset alc662_presets[] = {
 		.init_hook = alc663_mode4_inithook,
 		.init_hook = alc663_mode4_inithook,
 	},
 	},
 	[ALC663_ASUS_MODE5] = {
 	[ALC663_ASUS_MODE5] = {
-		.mixers = { alc663_asus_15jd_clfe_mixer,
-				alc662_auto_capture_mixer },
+		.mixers = { alc663_asus_15jd_clfe_mixer },
+		.cap_mixer = alc662_auto_capture_mixer,
 		.init_verbs = { alc662_init_verbs,
 		.init_verbs = { alc662_init_verbs,
 				alc663_15jd_amic_init_verbs },
 				alc663_15jd_amic_init_verbs },
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -16081,7 +16239,8 @@ static struct alc_config_preset alc662_presets[] = {
 		.init_hook = alc663_mode5_inithook,
 		.init_hook = alc663_mode5_inithook,
 	},
 	},
 	[ALC663_ASUS_MODE6] = {
 	[ALC663_ASUS_MODE6] = {
-		.mixers = { alc663_two_hp_m2_mixer, alc662_auto_capture_mixer },
+		.mixers = { alc663_two_hp_m2_mixer },
+		.cap_mixer = alc662_auto_capture_mixer,
 		.init_verbs = { alc662_init_verbs,
 		.init_verbs = { alc662_init_verbs,
 				alc663_two_hp_amic_m2_init_verbs },
 				alc663_two_hp_amic_m2_init_verbs },
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
 		.num_dacs = ARRAY_SIZE(alc662_dac_nids),
@@ -16342,24 +16501,20 @@ static int alc662_parse_auto_config(struct hda_codec *codec)
 	if (spec->autocfg.dig_out_pin)
 	if (spec->autocfg.dig_out_pin)
 		spec->multiout.dig_out_nid = ALC880_DIGOUT_NID;
 		spec->multiout.dig_out_nid = ALC880_DIGOUT_NID;
 
 
-	if (spec->kctl_alloc)
-		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+	if (spec->kctls.list)
+		add_mixer(spec, spec->kctls.list);
 
 
 	spec->num_mux_defs = 1;
 	spec->num_mux_defs = 1;
 	spec->input_mux = &spec->private_imux;
 	spec->input_mux = &spec->private_imux;
 
 
-	spec->init_verbs[spec->num_init_verbs++] = alc662_auto_init_verbs;
+	add_verb(spec, alc662_auto_init_verbs);
 	if (codec->vendor_id == 0x10ec0663)
 	if (codec->vendor_id == 0x10ec0663)
-		spec->init_verbs[spec->num_init_verbs++] =
-			alc663_auto_init_verbs;
+		add_verb(spec, alc663_auto_init_verbs);
 
 
 	err = alc_auto_add_mic_boost(codec);
 	err = alc_auto_add_mic_boost(codec);
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
 
 
-	spec->mixers[spec->num_mixers] = alc662_capture_mixer;
-	spec->num_mixers++;
-
 	store_pin_configs(codec);
 	store_pin_configs(codec);
 	return 1;
 	return 1;
 }
 }
@@ -16435,6 +16590,10 @@ static int patch_alc662(struct hda_codec *codec)
 	spec->adc_nids = alc662_adc_nids;
 	spec->adc_nids = alc662_adc_nids;
 	spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids);
 	spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids);
 	spec->capsrc_nids = alc662_capsrc_nids;
 	spec->capsrc_nids = alc662_capsrc_nids;
+	spec->is_mix_capture = 1;
+
+	if (!spec->cap_mixer)
+		set_capture_mixer(spec);
 
 
 	spec->vmaster_nid = 0x02;
 	spec->vmaster_nid = 0x02;
 
 
@@ -16445,6 +16604,7 @@ static int patch_alc662(struct hda_codec *codec)
 	if (!spec->loopback.amplist)
 	if (!spec->loopback.amplist)
 		spec->loopback.amplist = alc662_loopbacks;
 		spec->loopback.amplist = alc662_loopbacks;
 #endif
 #endif
+	codec->proc_widget_hook = print_realtek_coef;
 
 
 	return 0;
 	return 0;
 }
 }
@@ -16452,7 +16612,7 @@ static int patch_alc662(struct hda_codec *codec)
 /*
 /*
  * patch entries
  * patch entries
  */
  */
-struct hda_codec_preset snd_hda_preset_realtek[] = {
+static struct hda_codec_preset snd_hda_preset_realtek[] = {
 	{ .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 },
 	{ .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 },
 	{ .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 },
 	{ .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 },
 	{ .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 },
 	{ .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 },
@@ -16484,3 +16644,26 @@ struct hda_codec_preset snd_hda_preset_realtek[] = {
 	{ .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc883 },
 	{ .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc883 },
 	{} /* terminator */
 	{} /* terminator */
 };
 };
+
+MODULE_ALIAS("snd-hda-codec-id:10ec*");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Realtek HD-audio codec");
+
+static struct hda_codec_preset_list realtek_list = {
+	.preset = snd_hda_preset_realtek,
+	.owner = THIS_MODULE,
+};
+
+static int __init patch_realtek_init(void)
+{
+	return snd_hda_add_codec_preset(&realtek_list);
+}
+
+static void __exit patch_realtek_exit(void)
+{
+	snd_hda_delete_codec_preset(&realtek_list);
+}
+
+module_init(patch_realtek_init)
+module_exit(patch_realtek_exit)

+ 33 - 2
sound/pci/hda/patch_si3054.c

@@ -28,7 +28,6 @@
 #include <sound/core.h>
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_codec.h"
 #include "hda_local.h"
 #include "hda_local.h"
-#include "hda_patch.h"
 
 
 /* si3054 verbs */
 /* si3054 verbs */
 #define SI3054_VERB_READ_NODE  0x900
 #define SI3054_VERB_READ_NODE  0x900
@@ -283,7 +282,7 @@ static int patch_si3054(struct hda_codec *codec)
 /*
 /*
  * patch entries
  * patch entries
  */
  */
-struct hda_codec_preset snd_hda_preset_si3054[] = {
+static struct hda_codec_preset snd_hda_preset_si3054[] = {
  	{ .id = 0x163c3055, .name = "Si3054", .patch = patch_si3054 },
  	{ .id = 0x163c3055, .name = "Si3054", .patch = patch_si3054 },
  	{ .id = 0x163c3155, .name = "Si3054", .patch = patch_si3054 },
  	{ .id = 0x163c3155, .name = "Si3054", .patch = patch_si3054 },
  	{ .id = 0x11c13026, .name = "Si3054", .patch = patch_si3054 },
  	{ .id = 0x11c13026, .name = "Si3054", .patch = patch_si3054 },
@@ -301,3 +300,35 @@ struct hda_codec_preset snd_hda_preset_si3054[] = {
 	{}
 	{}
 };
 };
 
 
+MODULE_ALIAS("snd-hda-codec-id:163c3055");
+MODULE_ALIAS("snd-hda-codec-id:163c3155");
+MODULE_ALIAS("snd-hda-codec-id:11c13026");
+MODULE_ALIAS("snd-hda-codec-id:11c13055");
+MODULE_ALIAS("snd-hda-codec-id:11c13155");
+MODULE_ALIAS("snd-hda-codec-id:10573055");
+MODULE_ALIAS("snd-hda-codec-id:10573057");
+MODULE_ALIAS("snd-hda-codec-id:10573155");
+MODULE_ALIAS("snd-hda-codec-id:11063288");
+MODULE_ALIAS("snd-hda-codec-id:15433155");
+MODULE_ALIAS("snd-hda-codec-id:18540018");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Si3054 HD-audio modem codec");
+
+static struct hda_codec_preset_list si3054_list = {
+	.preset = snd_hda_preset_si3054,
+	.owner = THIS_MODULE,
+};
+
+static int __init patch_si3054_init(void)
+{
+	return snd_hda_add_codec_preset(&si3054_list);
+}
+
+static void __exit patch_si3054_exit(void)
+{
+	snd_hda_delete_codec_preset(&si3054_list);
+}
+
+module_init(patch_si3054_init)
+module_exit(patch_si3054_exit)

+ 772 - 546
sound/pci/hda/patch_sigmatel.c

@@ -30,17 +30,17 @@
 #include <linux/pci.h>
 #include <linux/pci.h>
 #include <sound/core.h>
 #include <sound/core.h>
 #include <sound/asoundef.h>
 #include <sound/asoundef.h>
+#include <sound/jack.h>
 #include "hda_codec.h"
 #include "hda_codec.h"
 #include "hda_local.h"
 #include "hda_local.h"
-#include "hda_patch.h"
 #include "hda_beep.h"
 #include "hda_beep.h"
 
 
-#define NUM_CONTROL_ALLOC	32
-
-#define STAC_VREF_EVENT		0x00
-#define STAC_INSERT_EVENT	0x10
-#define STAC_PWR_EVENT		0x20
-#define STAC_HP_EVENT		0x30
+enum {
+	STAC_VREF_EVENT	= 1,
+	STAC_INSERT_EVENT,
+	STAC_PWR_EVENT,
+	STAC_HP_EVENT,
+};
 
 
 enum {
 enum {
 	STAC_REF,
 	STAC_REF,
@@ -137,6 +137,19 @@ enum {
 	STAC_927X_MODELS
 	STAC_927X_MODELS
 };
 };
 
 
+struct sigmatel_event {
+	hda_nid_t nid;
+	unsigned char type;
+	unsigned char tag;
+	int data;
+};
+
+struct sigmatel_jack {
+	hda_nid_t nid;
+	int type;
+	struct snd_jack *jack;
+};
+
 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;
@@ -144,8 +157,6 @@ struct sigmatel_spec {
 	int board_config;
 	int board_config;
 	unsigned int eapd_switch: 1;
 	unsigned int eapd_switch: 1;
 	unsigned int surr_switch: 1;
 	unsigned int surr_switch: 1;
-	unsigned int line_switch: 1;
-	unsigned int mic_switch: 1;
 	unsigned int alt_switch: 1;
 	unsigned int alt_switch: 1;
 	unsigned int hp_detect: 1;
 	unsigned int hp_detect: 1;
 	unsigned int spdif_mute: 1;
 	unsigned int spdif_mute: 1;
@@ -170,12 +181,20 @@ struct sigmatel_spec {
 	hda_nid_t *pwr_nids;
 	hda_nid_t *pwr_nids;
 	hda_nid_t *dac_list;
 	hda_nid_t *dac_list;
 
 
+	/* jack detection */
+	struct snd_array jacks;
+
+	/* events */
+	struct snd_array events;
+
 	/* playback */
 	/* playback */
 	struct hda_input_mux *mono_mux;
 	struct hda_input_mux *mono_mux;
 	struct hda_input_mux *amp_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];
+	hda_nid_t hp_dacs[5];
+	hda_nid_t speaker_dacs[5];
 
 
 	/* capture */
 	/* capture */
 	hda_nid_t *adc_nids;
 	hda_nid_t *adc_nids;
@@ -199,7 +218,6 @@ struct sigmatel_spec {
 	hda_nid_t *pin_nids;
 	hda_nid_t *pin_nids;
 	unsigned int num_pins;
 	unsigned int num_pins;
 	unsigned int *pin_configs;
 	unsigned int *pin_configs;
-	unsigned int *bios_pin_configs;
 
 
 	/* codec specific stuff */
 	/* codec specific stuff */
 	struct hda_verb *init;
 	struct hda_verb *init;
@@ -220,15 +238,16 @@ struct sigmatel_spec {
 	/* i/o switches */
 	/* i/o switches */
 	unsigned int io_switch[2];
 	unsigned int io_switch[2];
 	unsigned int clfe_swap;
 	unsigned int clfe_swap;
-	unsigned int hp_switch; /* NID of HP as line-out */
+	hda_nid_t line_switch;	/* shared line-in for input and output */
+	hda_nid_t mic_switch;	/* shared mic-in for input and output */
+	hda_nid_t hp_switch; /* NID of HP as line-out */
 	unsigned int aloopback;
 	unsigned int aloopback;
 
 
 	struct hda_pcm pcm_rec[2];	/* PCM information */
 	struct hda_pcm pcm_rec[2];	/* PCM information */
 
 
 	/* dynamic controls and input_mux */
 	/* dynamic controls and input_mux */
 	struct auto_pin_cfg autocfg;
 	struct auto_pin_cfg autocfg;
-	unsigned int num_kctl_alloc, num_kctl_used;
-	struct snd_kcontrol_new *kctl_alloc;
+	struct snd_array kctls;
 	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;
@@ -272,9 +291,6 @@ 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_dac_nids[STAC92HD73_DAC_COUNT] = {
-	0x15, 0x16, 0x17, 0x18, 0x19,
-};
 
 
 static hda_nid_t stac92hd73xx_mux_nids[4] = {
 static hda_nid_t stac92hd73xx_mux_nids[4] = {
 	0x28, 0x29, 0x2a, 0x2b,
 	0x28, 0x29, 0x2a, 0x2b,
@@ -293,11 +309,7 @@ static hda_nid_t stac92hd83xxx_dmic_nids[STAC92HD83XXX_NUM_DMICS + 1] = {
 	0x11, 0x12, 0
 	0x11, 0x12, 0
 };
 };
 
 
-#define STAC92HD81_DAC_COUNT 2
 #define STAC92HD83_DAC_COUNT 3
 #define STAC92HD83_DAC_COUNT 3
-static hda_nid_t stac92hd83xxx_dac_nids[STAC92HD73_DAC_COUNT] = {
-	0x13, 0x14, 0x22,
-};
 
 
 static hda_nid_t stac92hd83xxx_dmux_nids[2] = {
 static hda_nid_t stac92hd83xxx_dmux_nids[2] = {
 	0x17, 0x18,
 	0x17, 0x18,
@@ -339,10 +351,6 @@ static hda_nid_t stac92hd71bxx_smux_nids[2] = {
 	0x24, 0x25,
 	0x24, 0x25,
 };
 };
 
 
-static hda_nid_t stac92hd71bxx_dac_nids[1] = {
-	0x10, /*0x11, */
-};
-
 #define STAC92HD71BXX_NUM_DMICS	2
 #define STAC92HD71BXX_NUM_DMICS	2
 static hda_nid_t stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS + 1] = {
 static hda_nid_t stac92hd71bxx_dmic_nids[STAC92HD71BXX_NUM_DMICS + 1] = {
 	0x18, 0x19, 0
 	0x18, 0x19, 0
@@ -574,12 +582,12 @@ static int stac92xx_smux_enum_put(struct snd_kcontrol *kcontrol,
 		else
 		else
 			nid = codec->slave_dig_outs[smux_idx - 1];
 			nid = codec->slave_dig_outs[smux_idx - 1];
 		if (spec->cur_smux[smux_idx] == smux->num_items - 1)
 		if (spec->cur_smux[smux_idx] == smux->num_items - 1)
-			val = AMP_OUT_MUTE;
+			val = HDA_AMP_MUTE;
 		else
 		else
-			val = AMP_OUT_UNMUTE;
+			val = 0;
 		/* un/mute SPDIF out */
 		/* un/mute SPDIF out */
-		snd_hda_codec_write_cache(codec, nid, 0,
-			AC_VERB_SET_AMP_GAIN_MUTE, val);
+		snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
+					 HDA_AMP_MUTE, val);
 	}
 	}
 	return 0;
 	return 0;
 }
 }
@@ -744,10 +752,6 @@ static struct hda_verb stac9200_eapd_init[] = {
 static struct hda_verb stac92hd73xx_6ch_core_init[] = {
 static struct hda_verb stac92hd73xx_6ch_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},
-	/* setup audio connections */
-	{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00},
-	{ 0x10, AC_VERB_SET_CONNECT_SEL, 0x01},
-	{ 0x11, AC_VERB_SET_CONNECT_SEL, 0x02},
 	/* setup adcs to point to mixer */
 	/* setup adcs to point to mixer */
 	{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
 	{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
 	{ 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
 	{ 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
@@ -766,10 +770,6 @@ 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 audio connections */
-	{ 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
-	{ 0x0a, AC_VERB_SET_CONNECT_SEL, 0x02},
-	{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x01},
 	/* setup adcs to point to mixer */
 	/* setup adcs to point to mixer */
 	{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
 	{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
 	{ 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
 	{ 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
@@ -783,10 +783,6 @@ static struct hda_verb dell_eq_core_init[] = {
 
 
 static struct hda_verb dell_m6_core_init[] = {
 static struct hda_verb dell_m6_core_init[] = {
 	{ 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
 	{ 0x1f, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
-	/* setup audio connections */
-	{ 0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
-	{ 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
-	{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x02},
 	/* setup adcs to point to mixer */
 	/* setup adcs to point to mixer */
 	{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
 	{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
 	{ 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
 	{ 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
@@ -801,13 +797,6 @@ static struct hda_verb dell_m6_core_init[] = {
 static struct hda_verb stac92hd73xx_8ch_core_init[] = {
 static struct hda_verb stac92hd73xx_8ch_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},
-	/* setup audio connections */
-	{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00},
-	{ 0x10, AC_VERB_SET_CONNECT_SEL, 0x01},
-	{ 0x11, AC_VERB_SET_CONNECT_SEL, 0x02},
-	/* connect hp ports to dac3 */
-	{ 0x0a, AC_VERB_SET_CONNECT_SEL, 0x03},
-	{ 0x0d, AC_VERB_SET_CONNECT_SEL, 0x03},
 	/* setup adcs to point to mixer */
 	/* setup adcs to point to mixer */
 	{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
 	{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
 	{ 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
 	{ 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
@@ -825,15 +814,8 @@ static struct hda_verb stac92hd73xx_8ch_core_init[] = {
 static struct hda_verb stac92hd73xx_10ch_core_init[] = {
 static struct hda_verb stac92hd73xx_10ch_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},
-	/* setup audio connections */
-	{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
-	{ 0x10, AC_VERB_SET_CONNECT_SEL, 0x01 },
-	{ 0x11, AC_VERB_SET_CONNECT_SEL, 0x02 },
 	/* dac3 is connected to import3 mux */
 	/* dac3 is connected to import3 mux */
 	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb07f},
 	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb07f},
-	/* connect hp ports to dac4 */
-	{ 0x0a, AC_VERB_SET_CONNECT_SEL, 0x04},
-	{ 0x0d, AC_VERB_SET_CONNECT_SEL, 0x04},
 	/* setup adcs to point to mixer */
 	/* setup adcs to point to mixer */
 	{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
 	{ 0x20, AC_VERB_SET_CONNECT_SEL, 0x0b},
 	{ 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
 	{ 0x21, AC_VERB_SET_CONNECT_SEL, 0x0b},
@@ -865,8 +847,6 @@ static struct hda_verb stac92hd83xxx_core_init[] = {
 static struct hda_verb stac92hd71bxx_core_init[] = {
 static struct hda_verb stac92hd71bxx_core_init[] = {
 	/* set master volume and direct control */
 	/* set master volume and direct control */
 	{ 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
 	{ 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
-	/* connect headphone jack to dac1 */
-	{ 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
 	/* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */
 	/* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */
 	{ 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{ 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{ 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{ 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
@@ -886,8 +866,6 @@ static struct hda_verb stac92hd71bxx_analog_core_init[] = {
 
 
 	/* set master volume and direct control */
 	/* set master volume and direct control */
 	{ 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
 	{ 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff},
-	/* connect headphone jack to dac1 */
-	{ 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01},
 	/* unmute right and left channels for nodes 0x0a, 0xd */
 	/* unmute right and left channels for nodes 0x0a, 0xd */
 	{ 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{ 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{ 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{ 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
@@ -1087,21 +1065,21 @@ static struct snd_kcontrol_new stac92hd83xxx_mixer[] = {
 	HDA_CODEC_VOLUME_IDX("Capture Volume", 0x1, 0x18, 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_MUTE_IDX("Capture Switch", 0x1, 0x18, 0x0, HDA_OUTPUT),
 
 
-	HDA_CODEC_VOLUME("DAC0 Capture Volume", 0x1b, 0, HDA_INPUT),
-	HDA_CODEC_MUTE("DAC0 Capture Switch", 0x1b, 0, HDA_INPUT),
+	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, 0x1, HDA_INPUT),
-	HDA_CODEC_MUTE("DAC1 Capture Switch", 0x1b, 0x1, 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, 0x2, HDA_INPUT),
-	HDA_CODEC_MUTE("Front Mic Capture Switch", 0x1b, 0x2, 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, 0x3, HDA_INPUT),
-	HDA_CODEC_MUTE("Line In Capture Switch", 0x1b, 0x3, 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, 0x4, HDA_INPUT),
-	HDA_CODEC_MUTE("Mic Capture Switch", 0x1b 0x4, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Capture Volume", 0x1b, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Capture Switch", 0x1b 0x1, HDA_INPUT),
 	*/
 	*/
 	{ } /* end */
 	{ } /* end */
 };
 };
@@ -1240,9 +1218,14 @@ static const char *slave_sws[] = {
 	NULL
 	NULL
 };
 };
 
 
+static void stac92xx_free_kctls(struct hda_codec *codec);
+static int stac92xx_add_jack(struct hda_codec *codec, hda_nid_t nid, int type);
+
 static int stac92xx_build_controls(struct hda_codec *codec)
 static int stac92xx_build_controls(struct hda_codec *codec)
 {
 {
 	struct sigmatel_spec *spec = codec->spec;
 	struct sigmatel_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	hda_nid_t nid;
 	int err;
 	int err;
 	int i;
 	int i;
 
 
@@ -1257,7 +1240,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
 	}
 	}
 	if (spec->num_dmuxes > 0) {
 	if (spec->num_dmuxes > 0) {
 		stac_dmux_mixer.count = spec->num_dmuxes;
 		stac_dmux_mixer.count = spec->num_dmuxes;
-		err = snd_ctl_add(codec->bus->card,
+		err = snd_hda_ctl_add(codec,
 				  snd_ctl_new1(&stac_dmux_mixer, codec));
 				  snd_ctl_new1(&stac_dmux_mixer, codec));
 		if (err < 0)
 		if (err < 0)
 			return err;
 			return err;
@@ -1273,7 +1256,7 @@ static int stac92xx_build_controls(struct hda_codec *codec)
 			spec->spdif_mute = 1;
 			spec->spdif_mute = 1;
 		}
 		}
 		stac_smux_mixer.count = spec->num_smuxes;
 		stac_smux_mixer.count = spec->num_smuxes;
-		err = snd_ctl_add(codec->bus->card,
+		err = snd_hda_ctl_add(codec,
 				  snd_ctl_new1(&stac_smux_mixer, codec));
 				  snd_ctl_new1(&stac_smux_mixer, codec));
 		if (err < 0)
 		if (err < 0)
 			return err;
 			return err;
@@ -1312,6 +1295,37 @@ static int stac92xx_build_controls(struct hda_codec *codec)
 			return err;
 			return err;
 	}
 	}
 
 
+	stac92xx_free_kctls(codec); /* no longer needed */
+
+	/* create jack input elements */
+	if (spec->hp_detect) {
+		for (i = 0; i < cfg->hp_outs; i++) {
+			int type = SND_JACK_HEADPHONE;
+			nid = cfg->hp_pins[i];
+			/* jack detection */
+			if (cfg->hp_outs == i)
+				type |= SND_JACK_LINEOUT;
+			err = stac92xx_add_jack(codec, nid, type);
+			if (err < 0)
+				return err;
+		}
+	}
+	for (i = 0; i < cfg->line_outs; i++) {
+		err = stac92xx_add_jack(codec, cfg->line_out_pins[i],
+					SND_JACK_LINEOUT);
+		if (err < 0)
+			return err;
+	}
+	for (i = 0; i < AUTO_PIN_LAST; i++) {
+		nid = cfg->input_pins[i];
+		if (nid) {
+			err = stac92xx_add_jack(codec, nid,
+						SND_JACK_MICROPHONE);
+			if (err < 0)
+				return err;
+		}
+	}
+
 	return 0;	
 	return 0;	
 }
 }
 
 
@@ -1720,6 +1734,8 @@ static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
 		      "HP dv5", STAC_HP_M4),
 		      "HP dv5", STAC_HP_M4),
 	SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30f4,
 	SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30f4,
 		      "HP dv7", STAC_HP_M4),
 		      "HP dv7", STAC_HP_M4),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30fc,
+		      "HP dv7", STAC_HP_M4),
 	SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361a,
 	SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x361a,
 				"unknown HP", STAC_HP_M4),
 				"unknown HP", STAC_HP_M4),
 	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233,
 	SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0233,
@@ -2203,12 +2219,11 @@ static int stac92xx_save_bios_config_regs(struct hda_codec *codec)
 	int i;
 	int i;
 	struct sigmatel_spec *spec = codec->spec;
 	struct sigmatel_spec *spec = codec->spec;
 	
 	
-	if (! spec->bios_pin_configs) {
-		spec->bios_pin_configs = kcalloc(spec->num_pins,
-		                                 sizeof(*spec->bios_pin_configs), GFP_KERNEL);
-		if (! spec->bios_pin_configs)
-			return -ENOMEM;
-	}
+	kfree(spec->pin_configs);
+	spec->pin_configs = kcalloc(spec->num_pins, sizeof(*spec->pin_configs),
+				    GFP_KERNEL);
+	if (!spec->pin_configs)
+		return -ENOMEM;
 	
 	
 	for (i = 0; i < spec->num_pins; i++) {
 	for (i = 0; i < spec->num_pins; i++) {
 		hda_nid_t nid = spec->pin_nids[i];
 		hda_nid_t nid = spec->pin_nids[i];
@@ -2218,7 +2233,7 @@ static int stac92xx_save_bios_config_regs(struct hda_codec *codec)
 			AC_VERB_GET_CONFIG_DEFAULT, 0x00);	
 			AC_VERB_GET_CONFIG_DEFAULT, 0x00);	
 		snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x bios pin config %8.8x\n",
 		snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x bios pin config %8.8x\n",
 					nid, pin_cfg);
 					nid, pin_cfg);
-		spec->bios_pin_configs[i] = pin_cfg;
+		spec->pin_configs[i] = pin_cfg;
 	}
 	}
 	
 	
 	return 0;
 	return 0;
@@ -2260,6 +2275,39 @@ static void stac92xx_set_config_regs(struct hda_codec *codec)
 					spec->pin_configs[i]);
 					spec->pin_configs[i]);
 }
 }
 
 
+static int stac_save_pin_cfgs(struct hda_codec *codec, unsigned int *pins)
+{
+	struct sigmatel_spec *spec = codec->spec;
+
+	if (!pins)
+		return stac92xx_save_bios_config_regs(codec);
+
+	kfree(spec->pin_configs);
+	spec->pin_configs = kmemdup(pins,
+				    spec->num_pins * sizeof(*pins),
+				    GFP_KERNEL);
+	if (!spec->pin_configs)
+		return -ENOMEM;
+
+	stac92xx_set_config_regs(codec);
+	return 0;
+}
+
+static void stac_change_pin_config(struct hda_codec *codec, hda_nid_t nid,
+				   unsigned int cfg)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	int i;
+
+	for (i = 0; i < spec->num_pins; i++) {
+		if (spec->pin_nids[i] == nid) {
+			spec->pin_configs[i] = cfg;
+			stac92xx_set_config_reg(codec, nid, cfg);
+			break;
+		}
+	}
+}
+
 /*
 /*
  * Analog playback callbacks
  * Analog playback callbacks
  */
  */
@@ -2337,7 +2385,7 @@ static int stac92xx_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
 
 
 	if (spec->powerdown_adcs) {
 	if (spec->powerdown_adcs) {
 		msleep(40);
 		msleep(40);
-		snd_hda_codec_write_cache(codec, nid, 0,
+		snd_hda_codec_write(codec, nid, 0,
 			AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
 			AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
 	}
 	}
 	snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
 	snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
@@ -2353,7 +2401,7 @@ static int stac92xx_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
 
 
 	snd_hda_codec_cleanup_stream(codec, nid);
 	snd_hda_codec_cleanup_stream(codec, nid);
 	if (spec->powerdown_adcs)
 	if (spec->powerdown_adcs)
-		snd_hda_codec_write_cache(codec, nid, 0,
+		snd_hda_codec_write(codec, nid, 0,
 			AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
 			AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
 	return 0;
 	return 0;
 }
 }
@@ -2485,6 +2533,9 @@ 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 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)
 {
 {
@@ -2497,7 +2548,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
 	 */
 	 */
-	codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
+	stac_issue_unsol_event(codec, nid, STAC_HP_EVENT);
 
 
 	return 1;
 	return 1;
 }
 }
@@ -2537,7 +2588,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)
-		codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
+		stac_issue_unsol_event(codec, nid, STAC_HP_EVENT);
 
 
         return 1;
         return 1;
 }
 }
@@ -2632,28 +2683,16 @@ static int stac92xx_add_control_temp(struct sigmatel_spec *spec,
 {
 {
 	struct snd_kcontrol_new *knew;
 	struct snd_kcontrol_new *knew;
 
 
-	if (spec->num_kctl_used >= spec->num_kctl_alloc) {
-		int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
-
-		knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */
-		if (! knew)
-			return -ENOMEM;
-		if (spec->kctl_alloc) {
-			memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc);
-			kfree(spec->kctl_alloc);
-		}
-		spec->kctl_alloc = knew;
-		spec->num_kctl_alloc = num;
-	}
-
-	knew = &spec->kctl_alloc[spec->num_kctl_used];
+	snd_array_init(&spec->kctls, sizeof(*knew), 32);
+	knew = snd_array_new(&spec->kctls);
+	if (!knew)
+		return -ENOMEM;
 	*knew = *ktemp;
 	*knew = *ktemp;
 	knew->index = idx;
 	knew->index = idx;
 	knew->name = kstrdup(name, GFP_KERNEL);
 	knew->name = kstrdup(name, GFP_KERNEL);
 	if (!knew->name)
 	if (!knew->name)
 		return -ENOMEM;
 		return -ENOMEM;
 	knew->private_value = val;
 	knew->private_value = val;
-	spec->num_kctl_used++;
 	return 0;
 	return 0;
 }
 }
 
 
@@ -2674,70 +2713,53 @@ static inline int stac92xx_add_control(struct sigmatel_spec *spec, int type,
 	return stac92xx_add_control_idx(spec, type, 0, name, val);
 	return stac92xx_add_control_idx(spec, type, 0, name, val);
 }
 }
 
 
-/* flag inputs as additional dynamic lineouts */
-static int stac92xx_add_dyn_out_pins(struct hda_codec *codec, struct auto_pin_cfg *cfg)
+/* check whether the line-input can be used as line-out */
+static hda_nid_t check_line_out_switch(struct hda_codec *codec)
 {
 {
 	struct sigmatel_spec *spec = codec->spec;
 	struct sigmatel_spec *spec = codec->spec;
-	unsigned int wcaps, wtype;
-	int i, num_dacs = 0;
-	
-	/* use the wcaps cache to count all DACs available for line-outs */
-	for (i = 0; i < codec->num_nodes; i++) {
-		wcaps = codec->wcaps[i];
-		wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	hda_nid_t nid;
+	unsigned int pincap;
 
 
-		if (wtype == AC_WID_AUD_OUT && !(wcaps & AC_WCAP_DIGITAL))
-			num_dacs++;
-	}
+	if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
+		return 0;
+	nid = cfg->input_pins[AUTO_PIN_LINE];
+	pincap = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+	if (pincap & AC_PINCAP_OUT)
+		return nid;
+	return 0;
+}
 
 
-	snd_printdd("%s: total dac count=%d\n", __func__, num_dacs);
-	
-	switch (cfg->line_outs) {
-	case 3:
-		/* add line-in as side */
-		if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 3) {
-			cfg->line_out_pins[cfg->line_outs] =
-				cfg->input_pins[AUTO_PIN_LINE];
-			spec->line_switch = 1;
-			cfg->line_outs++;
-		}
-		break;
-	case 2:
-		/* add line-in as clfe and mic as side */
-		if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 2) {
-			cfg->line_out_pins[cfg->line_outs] =
-				cfg->input_pins[AUTO_PIN_LINE];
-			spec->line_switch = 1;
-			cfg->line_outs++;
-		}
-		if (cfg->input_pins[AUTO_PIN_MIC] && num_dacs > 3) {
-			cfg->line_out_pins[cfg->line_outs] =
-				cfg->input_pins[AUTO_PIN_MIC];
-			spec->mic_switch = 1;
-			cfg->line_outs++;
-		}
-		break;
-	case 1:
-		/* add line-in as surr and mic as clfe */
-		if (cfg->input_pins[AUTO_PIN_LINE] && num_dacs > 1) {
-			cfg->line_out_pins[cfg->line_outs] =
-				cfg->input_pins[AUTO_PIN_LINE];
-			spec->line_switch = 1;
-			cfg->line_outs++;
-		}
-		if (cfg->input_pins[AUTO_PIN_MIC] && num_dacs > 2) {
-			cfg->line_out_pins[cfg->line_outs] =
-				cfg->input_pins[AUTO_PIN_MIC];
-			spec->mic_switch = 1;
-			cfg->line_outs++;
+/* check whether the mic-input can be used as line-out */
+static hda_nid_t check_mic_out_switch(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	unsigned int def_conf, pincap;
+	unsigned int mic_pin;
+
+	if (cfg->line_out_type != AUTO_PIN_LINE_OUT)
+		return 0;
+	mic_pin = AUTO_PIN_MIC;
+	for (;;) {
+		hda_nid_t nid = cfg->input_pins[mic_pin];
+		def_conf = snd_hda_codec_read(codec, nid, 0,
+					      AC_VERB_GET_CONFIG_DEFAULT, 0);
+		/* some laptops have an internal analog microphone
+		 * which can't be used as a output */
+		if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) {
+			pincap = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+			if (pincap & AC_PINCAP_OUT)
+				return nid;
 		}
 		}
-		break;
+		if (mic_pin == AUTO_PIN_MIC)
+			mic_pin = AUTO_PIN_FRONT_MIC;
+		else
+			break;
 	}
 	}
-
 	return 0;
 	return 0;
 }
 }
 
 
-
 static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
 static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
 {
 {
 	int i;
 	int i;
@@ -2750,6 +2772,52 @@ static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
 	return 0;
 	return 0;
 }
 }
 
 
+static int check_all_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
+{
+	int i;
+	if (is_in_dac_nids(spec, nid))
+		return 1;
+	for (i = 0; i < spec->autocfg.hp_outs; i++)
+		if (spec->hp_dacs[i] == nid)
+			return 1;
+	for (i = 0; i < spec->autocfg.speaker_outs; i++)
+		if (spec->speaker_dacs[i] == nid)
+			return 1;
+	return 0;
+}
+
+static hda_nid_t get_unassigned_dac(struct hda_codec *codec, hda_nid_t nid)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	int j, conn_len;
+	hda_nid_t conn[HDA_MAX_CONNECTIONS];
+	unsigned int wcaps, wtype;
+
+	conn_len = snd_hda_get_connections(codec, nid, conn,
+					   HDA_MAX_CONNECTIONS);
+	for (j = 0; j < conn_len; j++) {
+		wcaps = snd_hda_param_read(codec, conn[j],
+					   AC_PAR_AUDIO_WIDGET_CAP);
+		wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+		/* we check only analog outputs */
+		if (wtype != AC_WID_AUD_OUT || (wcaps & AC_WCAP_DIGITAL))
+			continue;
+		/* if this route has a free DAC, assign it */
+		if (!check_all_dac_nids(spec, conn[j])) {
+			if (conn_len > 1) {
+				/* select this DAC in the pin's input mux */
+				snd_hda_codec_write_cache(codec, nid, 0,
+						  AC_VERB_SET_CONNECT_SEL, j);
+			}
+			return conn[j];
+		}
+	}
+	return 0;
+}
+
+static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid);
+static int add_spec_extra_dacs(struct sigmatel_spec *spec, hda_nid_t nid);
+
 /*
 /*
  * Fill in the dac_nids table from the parsed pin configuration
  * Fill in the dac_nids table from the parsed pin configuration
  * This function only works when every pin in line_out_pins[]
  * This function only works when every pin in line_out_pins[]
@@ -2757,31 +2825,17 @@ static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
  * codecs are not connected directly to a DAC, such as the 9200
  * codecs are not connected directly to a DAC, such as the 9200
  * and 9202/925x. For those, dac_nids[] must be hard-coded.
  * and 9202/925x. For those, dac_nids[] must be hard-coded.
  */
  */
-static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec,
-				       struct auto_pin_cfg *cfg)
+static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec)
 {
 {
 	struct sigmatel_spec *spec = codec->spec;
 	struct sigmatel_spec *spec = codec->spec;
-	int i, j, conn_len = 0; 
-	hda_nid_t nid, conn[HDA_MAX_CONNECTIONS];
-	unsigned int wcaps, wtype;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	int i;
+	hda_nid_t nid, dac;
 	
 	
 	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];
-		conn_len = snd_hda_get_connections(codec, nid, conn,
-						   HDA_MAX_CONNECTIONS);
-		for (j = 0; j < conn_len; j++) {
-			wcaps = snd_hda_param_read(codec, conn[j],
-						   AC_PAR_AUDIO_WIDGET_CAP);
-			wtype = (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
-			if (wtype != AC_WID_AUD_OUT ||
-			    (wcaps & AC_WCAP_DIGITAL))
-				continue;
-			/* conn[j] is a DAC routed to this line-out */
-			if (!is_in_dac_nids(spec, conn[j]))
-				break;
-		}
-
-		if (j == conn_len) {
+		dac = get_unassigned_dac(codec, nid);
+		if (!dac) {
 			if (spec->multiout.num_dacs > 0) {
 			if (spec->multiout.num_dacs > 0) {
 				/* we have already working output pins,
 				/* we have already working output pins,
 				 * so let's drop the broken ones again
 				 * so let's drop the broken ones again
@@ -2795,24 +2849,64 @@ static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec,
 				   __func__, nid);
 				   __func__, nid);
 			return -ENODEV;
 			return -ENODEV;
 		}
 		}
+		add_spec_dacs(spec, dac);
+	}
 
 
-		spec->multiout.dac_nids[i] = conn[j];
-		spec->multiout.num_dacs++;
-		if (conn_len > 1) {
-			/* select this DAC in the pin's input mux */
-			snd_hda_codec_write_cache(codec, nid, 0,
-						  AC_VERB_SET_CONNECT_SEL, j);
+	/* add line-in as output */
+	nid = check_line_out_switch(codec);
+	if (nid) {
+		dac = get_unassigned_dac(codec, nid);
+		if (dac) {
+			snd_printdd("STAC: Add line-in 0x%x as output %d\n",
+				    nid, cfg->line_outs);
+			cfg->line_out_pins[cfg->line_outs] = nid;
+			cfg->line_outs++;
+			spec->line_switch = nid;
+			add_spec_dacs(spec, dac);
+		}
+	}
+	/* add mic as output */
+	nid = check_mic_out_switch(codec);
+	if (nid) {
+		dac = get_unassigned_dac(codec, nid);
+		if (dac) {
+			snd_printdd("STAC: Add mic-in 0x%x as output %d\n",
+				    nid, cfg->line_outs);
+			cfg->line_out_pins[cfg->line_outs] = nid;
+			cfg->line_outs++;
+			spec->mic_switch = nid;
+			add_spec_dacs(spec, dac);
+		}
+	}
 
 
+	for (i = 0; i < cfg->hp_outs; i++) {
+		nid = cfg->hp_pins[i];
+		dac = get_unassigned_dac(codec, nid);
+		if (dac) {
+			if (!spec->multiout.hp_nid)
+				spec->multiout.hp_nid = dac;
+			else
+				add_spec_extra_dacs(spec, dac);
 		}
 		}
+		spec->hp_dacs[i] = dac;
+	}
+
+	for (i = 0; i < cfg->speaker_outs; i++) {
+		nid = cfg->speaker_pins[i];
+		dac = get_unassigned_dac(codec, nid);
+		if (dac)
+			add_spec_extra_dacs(spec, dac);
+		spec->speaker_dacs[i] = dac;
 	}
 	}
 
 
-	snd_printd("dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
+	snd_printd("stac92xx: dac_nids=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
 		   spec->multiout.num_dacs,
 		   spec->multiout.num_dacs,
 		   spec->multiout.dac_nids[0],
 		   spec->multiout.dac_nids[0],
 		   spec->multiout.dac_nids[1],
 		   spec->multiout.dac_nids[1],
 		   spec->multiout.dac_nids[2],
 		   spec->multiout.dac_nids[2],
 		   spec->multiout.dac_nids[3],
 		   spec->multiout.dac_nids[3],
 		   spec->multiout.dac_nids[4]);
 		   spec->multiout.dac_nids[4]);
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -2837,9 +2931,7 @@ static int create_controls(struct sigmatel_spec *spec, const char *pfx, hda_nid_
 
 
 static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
 static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
 {
 {
-	if (!spec->multiout.hp_nid)
-		spec->multiout.hp_nid = nid;
-	else if (spec->multiout.num_dacs > 4) {
+	if (spec->multiout.num_dacs > 4) {
 		printk(KERN_WARNING "stac92xx: No space for DAC 0x%x\n", nid);
 		printk(KERN_WARNING "stac92xx: No space for DAC 0x%x\n", nid);
 		return 1;
 		return 1;
 	} else {
 	} else {
@@ -2849,35 +2941,47 @@ static int add_spec_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
 	return 0;
 	return 0;
 }
 }
 
 
-static int check_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
+static int add_spec_extra_dacs(struct sigmatel_spec *spec, hda_nid_t nid)
 {
 {
-	if (is_in_dac_nids(spec, nid))
-		return 1;
+	int i;
+	for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++) {
+		if (!spec->multiout.extra_out_nid[i]) {
+			spec->multiout.extra_out_nid[i] = nid;
+			return 0;
+		}
+	}
+	printk(KERN_WARNING "stac92xx: No space for extra DAC 0x%x\n", nid);
+	return 1;
+}
+
+static int is_unique_dac(struct sigmatel_spec *spec, hda_nid_t nid)
+{
+	int i;
+
+	if (spec->autocfg.line_outs != 1)
+		return 0;
 	if (spec->multiout.hp_nid == nid)
 	if (spec->multiout.hp_nid == nid)
-		return 1;
-	return 0;
+		return 0;
+	for (i = 0; i < ARRAY_SIZE(spec->multiout.extra_out_nid); i++)
+		if (spec->multiout.extra_out_nid[i] == nid)
+			return 0;
+	return 1;
 }
 }
 
 
 /* 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)
 {
 {
+	struct sigmatel_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 = 0;
 	hda_nid_t nid = 0;
 	int i, err;
 	int i, err;
+	unsigned int wid_caps;
 
 
-	struct sigmatel_spec *spec = codec->spec;
-	unsigned int wid_caps, pincap;
-
-
-	for (i = 0; i < cfg->line_outs && i < spec->multiout.num_dacs; i++) {
-		if (!spec->multiout.dac_nids[i])
-			continue;
-
+	for (i = 0; i < cfg->line_outs && spec->multiout.dac_nids[i]; i++) {
 		nid = spec->multiout.dac_nids[i];
 		nid = spec->multiout.dac_nids[i];
-
 		if (i == 2) {
 		if (i == 2) {
 			/* Center/LFE */
 			/* Center/LFE */
 			err = create_controls(spec, "Center", nid, 1);
 			err = create_controls(spec, "Center", nid, 1);
@@ -2899,16 +3003,24 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
 			}
 			}
 
 
 		} else {
 		} else {
-			err = create_controls(spec, chname[i], nid, 3);
+			const char *name = chname[i];
+			/* if it's a single DAC, assign a better name */
+			if (!i && is_unique_dac(spec, nid)) {
+				switch (cfg->line_out_type) {
+				case AUTO_PIN_HP_OUT:
+					name = "Headphone";
+					break;
+				case AUTO_PIN_SPEAKER_OUT:
+					name = "Speaker";
+					break;
+				}
+			}
+			err = create_controls(spec, name, nid, 3);
 			if (err < 0)
 			if (err < 0)
 				return err;
 				return err;
 		}
 		}
 	}
 	}
 
 
-	if ((spec->multiout.num_dacs - cfg->line_outs) > 0 &&
-	    cfg->hp_outs == 1 && !spec->multiout.hp_nid)
-		spec->multiout.hp_nid = nid;
-
 	if (cfg->hp_outs > 1 && cfg->line_out_type == AUTO_PIN_LINE_OUT) {
 	if (cfg->hp_outs > 1 && cfg->line_out_type == AUTO_PIN_LINE_OUT) {
 		err = stac92xx_add_control(spec,
 		err = stac92xx_add_control(spec,
 			STAC_CTL_WIDGET_HP_SWITCH,
 			STAC_CTL_WIDGET_HP_SWITCH,
@@ -2919,45 +3031,19 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec,
 	}
 	}
 
 
 	if (spec->line_switch) {
 	if (spec->line_switch) {
-		nid = cfg->input_pins[AUTO_PIN_LINE];
-		pincap = snd_hda_param_read(codec, nid,
-						AC_PAR_PIN_CAP);
-		if (pincap & AC_PINCAP_OUT) {
-			err = stac92xx_add_control(spec,
-				STAC_CTL_WIDGET_IO_SWITCH,
-				"Line In as Output Switch", nid << 8);
-			if (err < 0)
-				return err;
-		}
+		err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH,
+					   "Line In as Output Switch",
+					   spec->line_switch << 8);
+		if (err < 0)
+			return err;
 	}
 	}
 
 
 	if (spec->mic_switch) {
 	if (spec->mic_switch) {
-		unsigned int def_conf;
-		unsigned int mic_pin = AUTO_PIN_MIC;
-again:
-		nid = cfg->input_pins[mic_pin];
-		def_conf = snd_hda_codec_read(codec, nid, 0,
-						AC_VERB_GET_CONFIG_DEFAULT, 0);
-		/* some laptops have an internal analog microphone
-		 * which can't be used as a output */
-		if (get_defcfg_connect(def_conf) != AC_JACK_PORT_FIXED) {
-			pincap = snd_hda_param_read(codec, nid,
-							AC_PAR_PIN_CAP);
-			if (pincap & AC_PINCAP_OUT) {
-				err = stac92xx_add_control(spec,
-					STAC_CTL_WIDGET_IO_SWITCH,
-					"Mic as Output Switch", (nid << 8) | 1);
-				nid = snd_hda_codec_read(codec, nid, 0,
-					 AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
-				if (!check_in_dac_nids(spec, nid))
-					add_spec_dacs(spec, nid);
-				if (err < 0)
-					return err;
-			}
-		} else if (mic_pin == AUTO_PIN_MIC) {
-			mic_pin = AUTO_PIN_FRONT_MIC;
-			goto again;
-		}
+		err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH,
+					   "Mic as Output Switch",
+					   (spec->mic_switch << 8) | 1);
+		if (err < 0)
+			return err;
 	}
 	}
 
 
 	return 0;
 	return 0;
@@ -2969,55 +3055,39 @@ static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec,
 {
 {
 	struct sigmatel_spec *spec = codec->spec;
 	struct sigmatel_spec *spec = codec->spec;
 	hda_nid_t nid;
 	hda_nid_t nid;
-	int i, old_num_dacs, err;
+	int i, err, nums;
 
 
-	old_num_dacs = spec->multiout.num_dacs;
+	nums = 0;
 	for (i = 0; i < cfg->hp_outs; i++) {
 	for (i = 0; i < cfg->hp_outs; i++) {
+		static const char *pfxs[] = {
+			"Headphone", "Headphone2", "Headphone3",
+		};
 		unsigned int wid_caps = get_wcaps(codec, cfg->hp_pins[i]);
 		unsigned int wid_caps = get_wcaps(codec, cfg->hp_pins[i]);
 		if (wid_caps & AC_WCAP_UNSOL_CAP)
 		if (wid_caps & AC_WCAP_UNSOL_CAP)
 			spec->hp_detect = 1;
 			spec->hp_detect = 1;
-		nid = snd_hda_codec_read(codec, cfg->hp_pins[i], 0,
-					 AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
-		if (check_in_dac_nids(spec, nid))
-			nid = 0;
-		if (! nid)
+		if (nums >= ARRAY_SIZE(pfxs))
 			continue;
 			continue;
-		add_spec_dacs(spec, nid);
-	}
-	for (i = 0; i < cfg->speaker_outs; i++) {
-		nid = snd_hda_codec_read(codec, cfg->speaker_pins[i], 0,
-					 AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
-		if (check_in_dac_nids(spec, nid))
-			nid = 0;
-		if (! nid)
+		nid = spec->hp_dacs[i];
+		if (!nid)
 			continue;
 			continue;
-		add_spec_dacs(spec, nid);
-	}
-	for (i = 0; i < cfg->line_outs; i++) {
-		nid = snd_hda_codec_read(codec, cfg->line_out_pins[i], 0,
-					AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
-		if (check_in_dac_nids(spec, nid))
-			nid = 0;
-		if (! nid)
-			continue;
-		add_spec_dacs(spec, nid);
+		err = create_controls(spec, pfxs[nums++], nid, 3);
+		if (err < 0)
+			return err;
 	}
 	}
-	for (i = old_num_dacs; i < spec->multiout.num_dacs; i++) {
+	nums = 0;
+	for (i = 0; i < cfg->speaker_outs; i++) {
 		static const char *pfxs[] = {
 		static const char *pfxs[] = {
 			"Speaker", "External Speaker", "Speaker2",
 			"Speaker", "External Speaker", "Speaker2",
 		};
 		};
-		err = create_controls(spec, pfxs[i - old_num_dacs],
-				      spec->multiout.dac_nids[i], 3);
-		if (err < 0)
-			return err;
-	}
-	if (spec->multiout.hp_nid) {
-		err = create_controls(spec, "Headphone",
-				      spec->multiout.hp_nid, 3);
+		if (nums >= ARRAY_SIZE(pfxs))
+			continue;
+		nid = spec->speaker_dacs[i];
+		if (!nid)
+			continue;
+		err = create_controls(spec, pfxs[nums++], nid, 3);
 		if (err < 0)
 		if (err < 0)
 			return err;
 			return err;
 	}
 	}
-
 	return 0;
 	return 0;
 }
 }
 
 
@@ -3355,7 +3425,6 @@ 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 err;
 	int err;
-	int hp_speaker_swap = 0;
 
 
 	if ((err = snd_hda_parse_pin_def_config(codec,
 	if ((err = snd_hda_parse_pin_def_config(codec,
 						&spec->autocfg,
 						&spec->autocfg,
@@ -3373,13 +3442,15 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
 		 * speaker_outs so that the following routines can handle
 		 * speaker_outs so that the following routines can handle
 		 * HP pins as primary outputs.
 		 * HP pins as primary outputs.
 		 */
 		 */
+		snd_printdd("stac92xx: Enabling multi-HPs workaround\n");
 		memcpy(spec->autocfg.speaker_pins, spec->autocfg.line_out_pins,
 		memcpy(spec->autocfg.speaker_pins, spec->autocfg.line_out_pins,
 		       sizeof(spec->autocfg.line_out_pins));
 		       sizeof(spec->autocfg.line_out_pins));
 		spec->autocfg.speaker_outs = spec->autocfg.line_outs;
 		spec->autocfg.speaker_outs = spec->autocfg.line_outs;
 		memcpy(spec->autocfg.line_out_pins, spec->autocfg.hp_pins,
 		memcpy(spec->autocfg.line_out_pins, spec->autocfg.hp_pins,
 		       sizeof(spec->autocfg.hp_pins));
 		       sizeof(spec->autocfg.hp_pins));
 		spec->autocfg.line_outs = spec->autocfg.hp_outs;
 		spec->autocfg.line_outs = spec->autocfg.hp_outs;
-		hp_speaker_swap = 1;
+		spec->autocfg.line_out_type = AUTO_PIN_HP_OUT;
+		spec->autocfg.hp_outs = 0;
 	}
 	}
 	if (spec->autocfg.mono_out_pin) {
 	if (spec->autocfg.mono_out_pin) {
 		int dir = get_wcaps(codec, spec->autocfg.mono_out_pin) &
 		int dir = get_wcaps(codec, spec->autocfg.mono_out_pin) &
@@ -3431,11 +3502,11 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
 					 AC_PINCTL_OUT_EN);
 					 AC_PINCTL_OUT_EN);
 	}
 	}
 
 
-	if ((err = stac92xx_add_dyn_out_pins(codec, &spec->autocfg)) < 0)
-		return err;
-	if (spec->multiout.num_dacs == 0)
-		if ((err = stac92xx_auto_fill_dac_nids(codec, &spec->autocfg)) < 0)
+	if (!spec->multiout.num_dacs) {
+		err = stac92xx_auto_fill_dac_nids(codec);
+		if (err < 0)
 			return err;
 			return err;
+	}
 
 
 	err = stac92xx_auto_create_multi_out_ctls(codec, &spec->autocfg);
 	err = stac92xx_auto_create_multi_out_ctls(codec, &spec->autocfg);
 
 
@@ -3473,19 +3544,6 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
 	}
 	}
 #endif
 #endif
 
 
-	if (hp_speaker_swap == 1) {
-		/* Restore the hp_outs and line_outs */
-		memcpy(spec->autocfg.hp_pins, spec->autocfg.line_out_pins,
-		       sizeof(spec->autocfg.line_out_pins));
-		spec->autocfg.hp_outs = spec->autocfg.line_outs;
-		memcpy(spec->autocfg.line_out_pins, spec->autocfg.speaker_pins,
-		       sizeof(spec->autocfg.speaker_pins));
-		spec->autocfg.line_outs = spec->autocfg.speaker_outs;
-		memset(spec->autocfg.speaker_pins, 0,
-		       sizeof(spec->autocfg.speaker_pins));
-		spec->autocfg.speaker_outs = 0;
-	}
-
 	err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg);
 	err = stac92xx_auto_create_hp_ctls(codec, &spec->autocfg);
 
 
 	if (err < 0)
 	if (err < 0)
@@ -3530,11 +3588,12 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out
 	if (dig_in && spec->autocfg.dig_in_pin)
 	if (dig_in && spec->autocfg.dig_in_pin)
 		spec->dig_in_nid = dig_in;
 		spec->dig_in_nid = dig_in;
 
 
-	if (spec->kctl_alloc)
-		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+	if (spec->kctls.list)
+		spec->mixers[spec->num_mixers++] = spec->kctls.list;
 
 
 	spec->input_mux = &spec->private_imux;
 	spec->input_mux = &spec->private_imux;
-	spec->dinput_mux = &spec->private_dimux;
+	if (!spec->dinput_mux)
+		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;
 	spec->amp_mux = &spec->private_amp_mux;
@@ -3638,8 +3697,8 @@ static int stac9200_parse_auto_config(struct hda_codec *codec)
 	if (spec->autocfg.dig_in_pin)
 	if (spec->autocfg.dig_in_pin)
 		spec->dig_in_nid = 0x04;
 		spec->dig_in_nid = 0x04;
 
 
-	if (spec->kctl_alloc)
-		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+	if (spec->kctls.list)
+		spec->mixers[spec->num_mixers++] = spec->kctls.list;
 
 
 	spec->input_mux = &spec->private_imux;
 	spec->input_mux = &spec->private_imux;
 	spec->dinput_mux = &spec->private_dimux;
 	spec->dinput_mux = &spec->private_dimux;
@@ -3683,13 +3742,101 @@ static void stac_gpio_set(struct hda_codec *codec, unsigned int mask,
 			   AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */
 			   AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */
 }
 }
 
 
+static int stac92xx_add_jack(struct hda_codec *codec,
+		hda_nid_t nid, int type)
+{
+#ifdef CONFIG_SND_JACK
+	struct sigmatel_spec *spec = codec->spec;
+	struct sigmatel_jack *jack;
+	int def_conf = snd_hda_codec_read(codec, nid,
+			0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+	int connectivity = get_defcfg_connect(def_conf);
+	char name[32];
+
+	if (connectivity && connectivity != AC_JACK_PORT_FIXED)
+		return 0;
+
+	snd_array_init(&spec->jacks, sizeof(*jack), 32);
+	jack = snd_array_new(&spec->jacks);
+	if (!jack)
+		return -ENOMEM;
+	jack->nid = nid;
+	jack->type = type;
+
+	sprintf(name, "%s at %s %s Jack",
+		snd_hda_get_jack_type(def_conf),
+		snd_hda_get_jack_connectivity(def_conf),
+		snd_hda_get_jack_location(def_conf));
+
+	return snd_jack_new(codec->bus->card, name, type, &jack->jack);
+#else
+	return 0;
+#endif
+}
+
+static int stac_add_event(struct sigmatel_spec *spec, hda_nid_t nid,
+			  unsigned char type, int data)
+{
+	struct sigmatel_event *event;
+
+	snd_array_init(&spec->events, sizeof(*event), 32);
+	event = snd_array_new(&spec->events);
+	if (!event)
+		return -ENOMEM;
+	event->nid = nid;
+	event->type = type;
+	event->tag = spec->events.used;
+	event->data = data;
+
+	return event->tag;
+}
+
+static struct sigmatel_event *stac_get_event(struct hda_codec *codec,
+					     hda_nid_t nid, unsigned char type)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	struct sigmatel_event *event = spec->events.list;
+	int i;
+
+	for (i = 0; i < spec->events.used; i++, event++) {
+		if (event->nid == nid && event->type == type)
+			return event;
+	}
+	return NULL;
+}
+
+static struct sigmatel_event *stac_get_event_from_tag(struct hda_codec *codec,
+						      unsigned char tag)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	struct sigmatel_event *event = spec->events.list;
+	int i;
+
+	for (i = 0; i < spec->events.used; i++, event++) {
+		if (event->tag == tag)
+			return event;
+	}
+	return NULL;
+}
+
 static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid,
 static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid,
-			      unsigned int event)
+			      unsigned int type)
 {
 {
-	if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP)
-		snd_hda_codec_write_cache(codec, nid, 0,
-					  AC_VERB_SET_UNSOLICITED_ENABLE,
-					  (AC_USRSP_EN | event));
+	struct sigmatel_event *event;
+	int tag;
+
+	if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP))
+		return;
+	event = stac_get_event(codec, nid, type);
+	if (event)
+		tag = event->tag;
+	else
+		tag = stac_add_event(codec->spec, nid, type, 0);
+	if (tag < 0)
+		return;
+	snd_hda_codec_write_cache(codec, nid, 0,
+				  AC_VERB_SET_UNSOLICITED_ENABLE,
+				  AC_USRSP_EN | tag);
 }
 }
 
 
 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)
@@ -3709,9 +3856,8 @@ static void stac92xx_power_down(struct hda_codec *codec)
 	/* power down inactive DACs */
 	/* power down inactive DACs */
 	hda_nid_t *dac;
 	hda_nid_t *dac;
 	for (dac = spec->dac_list; *dac; dac++)
 	for (dac = spec->dac_list; *dac; dac++)
-		if (!is_in_dac_nids(spec, *dac) &&
-			spec->multiout.hp_nid != *dac)
-			snd_hda_codec_write_cache(codec, *dac, 0,
+		if (!check_all_dac_nids(spec, *dac))
+			snd_hda_codec_write(codec, *dac, 0,
 					AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
 					AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
 }
 }
 
 
@@ -3730,7 +3876,7 @@ static int stac92xx_init(struct hda_codec *codec)
 	/* power down adcs initially */
 	/* power down adcs initially */
 	if (spec->powerdown_adcs)
 	if (spec->powerdown_adcs)
 		for (i = 0; i < spec->num_adcs; i++)
 		for (i = 0; i < spec->num_adcs; i++)
-			snd_hda_codec_write_cache(codec,
+			snd_hda_codec_write(codec,
 				spec->adc_nids[i], 0,
 				spec->adc_nids[i], 0,
 				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
 				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
 
 
@@ -3746,37 +3892,51 @@ static int stac92xx_init(struct hda_codec *codec)
 	/* set up pins */
 	/* set up pins */
 	if (spec->hp_detect) {
 	if (spec->hp_detect) {
 		/* Enable unsolicited responses on the HP widget */
 		/* Enable unsolicited responses on the HP widget */
-		for (i = 0; i < cfg->hp_outs; i++)
-			enable_pin_detect(codec, cfg->hp_pins[i],
-					  STAC_HP_EVENT);
+		for (i = 0; i < cfg->hp_outs; i++) {
+			hda_nid_t nid = cfg->hp_pins[i];
+			enable_pin_detect(codec, nid, STAC_HP_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);
-		stac92xx_auto_init_hp_out(codec);
+				AC_PINCTL_OUT_EN);
 		/* fake event to set up pins */
 		/* fake event to set up pins */
-		codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
+		stac_issue_unsol_event(codec, spec->autocfg.hp_pins[0],
+				       STAC_HP_EVENT);
 	} 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++)
+			stac_toggle_power_map(codec, cfg->hp_pins[i], 1);
 	}
 	}
 	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) {
-			unsigned int pinctl;
+			unsigned int pinctl, conf;
 			if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC) {
 			if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC) {
 				/* for mic pins, force to initialize */
 				/* for mic pins, force to initialize */
 				pinctl = stac92xx_get_vref(codec, nid);
 				pinctl = stac92xx_get_vref(codec, nid);
+				pinctl |= AC_PINCTL_IN_EN;
+				stac92xx_auto_set_pinctl(codec, nid, pinctl);
 			} else {
 			} else {
 				pinctl = snd_hda_codec_read(codec, nid, 0,
 				pinctl = snd_hda_codec_read(codec, nid, 0,
 					AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
 					AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
 				/* if PINCTL already set then skip */
 				/* if PINCTL already set then skip */
-				if (pinctl & AC_PINCTL_IN_EN)
-					continue;
+				if (!(pinctl & AC_PINCTL_IN_EN)) {
+					pinctl |= AC_PINCTL_IN_EN;
+					stac92xx_auto_set_pinctl(codec, nid,
+								 pinctl);
+				}
+			}
+			conf = snd_hda_codec_read(codec, nid, 0,
+					      AC_VERB_GET_CONFIG_DEFAULT, 0);
+			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);
 			}
 			}
-			pinctl |= AC_PINCTL_IN_EN;
-			stac92xx_auto_set_pinctl(codec, nid, pinctl);
 		}
 		}
 	}
 	}
 	for (i = 0; i < spec->num_dmics; i++)
 	for (i = 0; i < spec->num_dmics; i++)
@@ -3791,9 +3951,14 @@ static int stac92xx_init(struct hda_codec *codec)
 	for (i = 0; i < spec->num_pwrs; i++)  {
 	for (i = 0; i < spec->num_pwrs; i++)  {
 		hda_nid_t nid = spec->pwr_nids[i];
 		hda_nid_t nid = spec->pwr_nids[i];
 		int pinctl, def_conf;
 		int pinctl, def_conf;
-		int event = STAC_PWR_EVENT;
 
 
-		if (is_nid_hp_pin(cfg, nid) && spec->hp_detect)
+		/* power on when no jack detection is available */
+		if (!spec->hp_detect) {
+			stac_toggle_power_map(codec, nid, 1);
+			continue;
+		}
+
+		if (is_nid_hp_pin(cfg, nid))
 			continue; /* already has an unsol event */
 			continue; /* already has an unsol event */
 
 
 		pinctl = snd_hda_codec_read(codec, nid, 0,
 		pinctl = snd_hda_codec_read(codec, nid, 0,
@@ -3802,8 +3967,10 @@ static int stac92xx_init(struct hda_codec *codec)
 		 * any attempts on powering down a input port cause the
 		 * any attempts on powering down a input port cause the
 		 * referenced VREF to act quirky.
 		 * referenced VREF to act quirky.
 		 */
 		 */
-		if (pinctl & AC_PINCTL_IN_EN)
+		if (pinctl & AC_PINCTL_IN_EN) {
+			stac_toggle_power_map(codec, nid, 1);
 			continue;
 			continue;
+		}
 		def_conf = snd_hda_codec_read(codec, nid, 0,
 		def_conf = snd_hda_codec_read(codec, nid, 0,
 					      AC_VERB_GET_CONFIG_DEFAULT, 0);
 					      AC_VERB_GET_CONFIG_DEFAULT, 0);
 		def_conf = get_defcfg_connect(def_conf);
 		def_conf = get_defcfg_connect(def_conf);
@@ -3814,30 +3981,54 @@ static int stac92xx_init(struct hda_codec *codec)
 				stac_toggle_power_map(codec, nid, 1);
 				stac_toggle_power_map(codec, nid, 1);
 			continue;
 			continue;
 		}
 		}
-		enable_pin_detect(codec, spec->pwr_nids[i], event | i);
-		codec->patch_ops.unsol_event(codec, (event | i) << 26);
+		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 (spec->dac_list)
 	if (spec->dac_list)
 		stac92xx_power_down(codec);
 		stac92xx_power_down(codec);
 	return 0;
 	return 0;
 }
 }
 
 
+static void stac92xx_free_jacks(struct hda_codec *codec)
+{
+#ifdef CONFIG_SND_JACK
+	/* free jack instances manually when clearing/reconfiguring */
+	struct sigmatel_spec *spec = codec->spec;
+	if (!codec->bus->shutdown && spec->jacks.list) {
+		struct sigmatel_jack *jacks = spec->jacks.list;
+		int i;
+		for (i = 0; i < spec->jacks.used; i++)
+			snd_device_free(codec->bus->card, &jacks[i].jack);
+	}
+	snd_array_free(&spec->jacks);
+#endif
+}
+
+static void stac92xx_free_kctls(struct hda_codec *codec)
+{
+	struct sigmatel_spec *spec = codec->spec;
+
+	if (spec->kctls.list) {
+		struct snd_kcontrol_new *kctl = spec->kctls.list;
+		int i;
+		for (i = 0; i < spec->kctls.used; i++)
+			kfree(kctl[i].name);
+	}
+	snd_array_free(&spec->kctls);
+}
+
 static void stac92xx_free(struct hda_codec *codec)
 static void stac92xx_free(struct hda_codec *codec)
 {
 {
 	struct sigmatel_spec *spec = codec->spec;
 	struct sigmatel_spec *spec = codec->spec;
-	int i;
 
 
 	if (! spec)
 	if (! spec)
 		return;
 		return;
 
 
-	if (spec->kctl_alloc) {
-		for (i = 0; i < spec->num_kctl_used; i++)
-			kfree(spec->kctl_alloc[i].name);
-		kfree(spec->kctl_alloc);
-	}
-
-	if (spec->bios_pin_configs)
-		kfree(spec->bios_pin_configs);
+	kfree(spec->pin_configs);
+	stac92xx_free_jacks(codec);
+	snd_array_free(&spec->events);
 
 
 	kfree(spec);
 	kfree(spec);
 	snd_hda_detach_beep_device(codec);
 	snd_hda_detach_beep_device(codec);
@@ -3856,11 +4047,7 @@ static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid,
 		 * "xxx as Output" mixer switch
 		 * "xxx as Output" mixer switch
 		 */
 		 */
 		struct sigmatel_spec *spec = codec->spec;
 		struct sigmatel_spec *spec = codec->spec;
-		struct auto_pin_cfg *cfg = &spec->autocfg;
-		if ((nid == cfg->input_pins[AUTO_PIN_LINE] &&
-		     spec->line_switch) ||
-		    (nid == cfg->input_pins[AUTO_PIN_MIC] &&
-		     spec->mic_switch))
+		if (nid == spec->line_switch || nid == spec->mic_switch)
 			return;
 			return;
 	}
 	}
 
 
@@ -3884,20 +4071,13 @@ static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
 			pin_ctl & ~flag);
 			pin_ctl & ~flag);
 }
 }
 
 
-static int get_hp_pin_presence(struct hda_codec *codec, hda_nid_t nid)
+static int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
 {
 {
 	if (!nid)
 	if (!nid)
 		return 0;
 		return 0;
 	if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0x00)
 	if (snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0x00)
-	    & (1 << 31)) {
-		unsigned int pinctl;
-		pinctl = snd_hda_codec_read(codec, nid, 0,
-					    AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
-		if (pinctl & AC_PINCTL_IN_EN)
-			return 0; /* mic- or line-input */
-		else
-			return 1; /* HP-output */
-	}
+	    & (1 << 31))
+		return 1;
 	return 0;
 	return 0;
 }
 }
 
 
@@ -3909,11 +4089,9 @@ static int no_hp_sensing(struct sigmatel_spec *spec, int i)
 	struct auto_pin_cfg *cfg = &spec->autocfg;
 	struct auto_pin_cfg *cfg = &spec->autocfg;
 
 
 	/* ignore sensing of shared line and mic jacks */
 	/* ignore sensing of shared line and mic jacks */
-	if (spec->line_switch &&
-	    cfg->hp_pins[i] == cfg->input_pins[AUTO_PIN_LINE])
+	if (cfg->hp_pins[i] == spec->line_switch)
 		return 1;
 		return 1;
-	if (spec->mic_switch &&
-	    cfg->hp_pins[i] == cfg->input_pins[AUTO_PIN_MIC])
+	if (cfg->hp_pins[i] == spec->mic_switch)
 		return 1;
 		return 1;
 	/* ignore if the pin is set as line-out */
 	/* ignore if the pin is set as line-out */
 	if (cfg->hp_pins[i] == spec->hp_switch)
 	if (cfg->hp_pins[i] == spec->hp_switch)
@@ -3921,7 +4099,7 @@ static int no_hp_sensing(struct sigmatel_spec *spec, int i)
 	return 0;
 	return 0;
 }
 }
 
 
-static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
+static void stac92xx_hp_detect(struct hda_codec *codec)
 {
 {
 	struct sigmatel_spec *spec = codec->spec;
 	struct sigmatel_spec *spec = codec->spec;
 	struct auto_pin_cfg *cfg = &spec->autocfg;
 	struct auto_pin_cfg *cfg = &spec->autocfg;
@@ -3937,7 +4115,14 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
 			break;
 			break;
 		if (no_hp_sensing(spec, i))
 		if (no_hp_sensing(spec, i))
 			continue;
 			continue;
-		presence = get_hp_pin_presence(codec, cfg->hp_pins[i]);
+		presence = get_pin_presence(codec, cfg->hp_pins[i]);
+		if (presence) {
+			unsigned int pinctl;
+			pinctl = snd_hda_codec_read(codec, cfg->hp_pins[i], 0,
+					    AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+			if (pinctl & AC_PINCTL_IN_EN)
+				presence = 0; /* mic- or line-input */
+		}
 	}
 	}
 
 
 	if (presence) {
 	if (presence) {
@@ -4014,50 +4199,145 @@ static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid,
 
 
 static void stac92xx_pin_sense(struct hda_codec *codec, hda_nid_t nid)
 static void stac92xx_pin_sense(struct hda_codec *codec, hda_nid_t nid)
 {
 {
-	stac_toggle_power_map(codec, nid, get_hp_pin_presence(codec, nid));
+	stac_toggle_power_map(codec, nid, get_pin_presence(codec, nid));
+}
+
+static void stac92xx_report_jack(struct hda_codec *codec, hda_nid_t nid)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	struct sigmatel_jack *jacks = spec->jacks.list;
+
+	if (jacks) {
+		int i;
+		for (i = 0; i < spec->jacks.used; i++) {
+			if (jacks->nid == nid) {
+				unsigned int pin_ctl =
+					snd_hda_codec_read(codec, nid,
+					0, AC_VERB_GET_PIN_WIDGET_CONTROL,
+					 0x00);
+				int type = jacks->type;
+				if (type == (SND_JACK_LINEOUT
+						| SND_JACK_HEADPHONE))
+					type = (pin_ctl & AC_PINCTL_HP_EN)
+					? SND_JACK_HEADPHONE : SND_JACK_LINEOUT;
+				snd_jack_report(jacks->jack,
+					get_pin_presence(codec, nid)
+					? type : 0);
+			}
+			jacks++;
+		}
+	}
+}
+
+static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid,
+				   unsigned char type)
+{
+	struct sigmatel_event *event = stac_get_event(codec, nid, type);
+	if (!event)
+		return;
+	codec->patch_ops.unsol_event(codec, (unsigned)event->tag << 26);
 }
 }
 
 
 static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
 static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
 {
 {
 	struct sigmatel_spec *spec = codec->spec;
 	struct sigmatel_spec *spec = codec->spec;
-	int idx = res >> 26 & 0x0f;
+	struct sigmatel_event *event;
+	int tag, data;
 
 
-	switch ((res >> 26) & 0x70) {
+	tag = (res >> 26) & 0x7f;
+	event = stac_get_event_from_tag(codec, tag);
+	if (!event)
+		return;
+
+	switch (event->type) {
 	case STAC_HP_EVENT:
 	case STAC_HP_EVENT:
-		stac92xx_hp_detect(codec, res);
+		stac92xx_hp_detect(codec);
 		/* fallthru */
 		/* fallthru */
+	case STAC_INSERT_EVENT:
 	case STAC_PWR_EVENT:
 	case STAC_PWR_EVENT:
 		if (spec->num_pwrs > 0)
 		if (spec->num_pwrs > 0)
-			stac92xx_pin_sense(codec, idx);
+			stac92xx_pin_sense(codec, event->nid);
+		stac92xx_report_jack(codec, event->nid);
 		break;
 		break;
-	case STAC_VREF_EVENT: {
-		int data = snd_hda_codec_read(codec, codec->afg, 0,
-			AC_VERB_GET_GPIO_DATA, 0);
+	case STAC_VREF_EVENT:
+		data = snd_hda_codec_read(codec, codec->afg, 0,
+					  AC_VERB_GET_GPIO_DATA, 0);
 		/* toggle VREF state based on GPIOx status */
 		/* toggle VREF state based on GPIOx status */
 		snd_hda_codec_write(codec, codec->afg, 0, 0x7e0,
 		snd_hda_codec_write(codec, codec->afg, 0, 0x7e0,
-			!!(data & (1 << idx)));
+				    !!(data & (1 << event->data)));
 		break;
 		break;
-		}
 	}
 	}
 }
 }
 
 
+#ifdef CONFIG_PROC_FS
+static void stac92hd_proc_hook(struct snd_info_buffer *buffer,
+			       struct hda_codec *codec, hda_nid_t nid)
+{
+	if (nid == codec->afg)
+		snd_iprintf(buffer, "Power-Map: 0x%02x\n", 
+			    snd_hda_codec_read(codec, nid, 0, 0x0fec, 0x0));
+}
+
+static void analog_loop_proc_hook(struct snd_info_buffer *buffer,
+				  struct hda_codec *codec,
+				  unsigned int verb)
+{
+	snd_iprintf(buffer, "Analog Loopback: 0x%02x\n",
+		    snd_hda_codec_read(codec, codec->afg, 0, verb, 0));
+}
+
+/* stac92hd71bxx, stac92hd73xx */
+static void stac92hd7x_proc_hook(struct snd_info_buffer *buffer,
+				 struct hda_codec *codec, hda_nid_t nid)
+{
+	stac92hd_proc_hook(buffer, codec, nid);
+	if (nid == codec->afg)
+		analog_loop_proc_hook(buffer, codec, 0xfa0);
+}
+
+static void stac9205_proc_hook(struct snd_info_buffer *buffer,
+			       struct hda_codec *codec, hda_nid_t nid)
+{
+	if (nid == codec->afg)
+		analog_loop_proc_hook(buffer, codec, 0xfe0);
+}
+
+static void stac927x_proc_hook(struct snd_info_buffer *buffer,
+			       struct hda_codec *codec, hda_nid_t nid)
+{
+	if (nid == codec->afg)
+		analog_loop_proc_hook(buffer, codec, 0xfeb);
+}
+#else
+#define stac92hd_proc_hook	NULL
+#define stac92hd7x_proc_hook	NULL
+#define stac9205_proc_hook	NULL
+#define stac927x_proc_hook	NULL
+#endif
+
 #ifdef SND_HDA_NEEDS_RESUME
 #ifdef SND_HDA_NEEDS_RESUME
 static int stac92xx_resume(struct hda_codec *codec)
 static int stac92xx_resume(struct hda_codec *codec)
 {
 {
 	struct sigmatel_spec *spec = codec->spec;
 	struct sigmatel_spec *spec = codec->spec;
 
 
 	stac92xx_set_config_regs(codec);
 	stac92xx_set_config_regs(codec);
-	snd_hda_sequence_write(codec, spec->init);
-	stac_gpio_set(codec, spec->gpio_mask,
-		spec->gpio_dir, spec->gpio_data);
+	stac92xx_init(codec);
 	snd_hda_codec_resume_amp(codec);
 	snd_hda_codec_resume_amp(codec);
 	snd_hda_codec_resume_cache(codec);
 	snd_hda_codec_resume_cache(codec);
-	/* power down inactive DACs */
-	if (spec->dac_list)
-		stac92xx_power_down(codec);
-	/* invoke unsolicited event to reset the HP state */
+	/* fake event to set up pins again to override cached values */
 	if (spec->hp_detect)
 	if (spec->hp_detect)
-		codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
+		stac_issue_unsol_event(codec, spec->autocfg.hp_pins[0],
+				       STAC_HP_EVENT);
+	return 0;
+}
+
+static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
+{
+	struct sigmatel_spec *spec = codec->spec;
+	if (spec->eapd_mask)
+		stac_gpio_set(codec, spec->gpio_mask,
+				spec->gpio_dir, spec->gpio_data &
+				~spec->eapd_mask);
 	return 0;
 	return 0;
 }
 }
 #endif
 #endif
@@ -4069,6 +4349,7 @@ static struct hda_codec_ops stac92xx_patch_ops = {
 	.free = stac92xx_free,
 	.free = stac92xx_free,
 	.unsol_event = stac92xx_unsol_event,
 	.unsol_event = stac92xx_unsol_event,
 #ifdef SND_HDA_NEEDS_RESUME
 #ifdef SND_HDA_NEEDS_RESUME
+	.suspend = stac92xx_suspend,
 	.resume = stac92xx_resume,
 	.resume = stac92xx_resume,
 #endif
 #endif
 };
 };
@@ -4091,14 +4372,12 @@ static int patch_stac9200(struct hda_codec *codec)
 	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: Unknown model for STAC9200, using BIOS defaults\n");
 		err = stac92xx_save_bios_config_regs(codec);
 		err = stac92xx_save_bios_config_regs(codec);
-		if (err < 0) {
-			stac92xx_free(codec);
-			return err;
-		}
-		spec->pin_configs = spec->bios_pin_configs;
-	} else {
-		spec->pin_configs = stac9200_brd_tbl[spec->board_config];
-		stac92xx_set_config_regs(codec);
+	} else
+		err = stac_save_pin_cfgs(codec,
+					 stac9200_brd_tbl[spec->board_config]);
+	if (err < 0) {
+		stac92xx_free(codec);
+		return err;
 	}
 	}
 
 
 	spec->multiout.max_channels = 2;
 	spec->multiout.max_channels = 2;
@@ -4154,14 +4433,12 @@ static int patch_stac925x(struct hda_codec *codec)
 		snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC925x," 
 		snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC925x," 
 				      "using BIOS defaults\n");
 				      "using BIOS defaults\n");
 		err = stac92xx_save_bios_config_regs(codec);
 		err = stac92xx_save_bios_config_regs(codec);
-		if (err < 0) {
-			stac92xx_free(codec);
-			return err;
-		}
-		spec->pin_configs = spec->bios_pin_configs;
-	} else if (stac925x_brd_tbl[spec->board_config] != NULL){
-		spec->pin_configs = stac925x_brd_tbl[spec->board_config];
-		stac92xx_set_config_regs(codec);
+	} else
+		err = stac_save_pin_cfgs(codec,
+					 stac925x_brd_tbl[spec->board_config]);
+	if (err < 0) {
+		stac92xx_free(codec);
+		return err;
 	}
 	}
 
 
 	spec->multiout.max_channels = 2;
 	spec->multiout.max_channels = 2;
@@ -4225,6 +4502,7 @@ static int patch_stac92hd73xx(struct hda_codec *codec)
 	struct sigmatel_spec *spec;
 	struct sigmatel_spec *spec;
 	hda_nid_t conn[STAC92HD73_DAC_COUNT + 2];
 	hda_nid_t conn[STAC92HD73_DAC_COUNT + 2];
 	int err = 0;
 	int err = 0;
+	int num_dacs;
 
 
 	spec  = kzalloc(sizeof(*spec), GFP_KERNEL);
 	spec  = kzalloc(sizeof(*spec), GFP_KERNEL);
 	if (spec == NULL)
 	if (spec == NULL)
@@ -4243,43 +4521,37 @@ again:
 		snd_printdd(KERN_INFO "hda_codec: Unknown model for"
 		snd_printdd(KERN_INFO "hda_codec: Unknown model for"
 			" STAC92HD73XX, using BIOS defaults\n");
 			" STAC92HD73XX, using BIOS defaults\n");
 		err = stac92xx_save_bios_config_regs(codec);
 		err = stac92xx_save_bios_config_regs(codec);
-		if (err < 0) {
-			stac92xx_free(codec);
-			return err;
-		}
-		spec->pin_configs = spec->bios_pin_configs;
-	} else {
-		spec->pin_configs = stac92hd73xx_brd_tbl[spec->board_config];
-		stac92xx_set_config_regs(codec);
+	} else
+		err = stac_save_pin_cfgs(codec,
+				stac92hd73xx_brd_tbl[spec->board_config]);
+	if (err < 0) {
+		stac92xx_free(codec);
+		return err;
 	}
 	}
 
 
-	spec->multiout.num_dacs = snd_hda_get_connections(codec, 0x0a,
+	num_dacs = snd_hda_get_connections(codec, 0x0a,
 			conn, STAC92HD73_DAC_COUNT + 2) - 1;
 			conn, STAC92HD73_DAC_COUNT + 2) - 1;
 
 
-	if (spec->multiout.num_dacs < 0) {
+	if (num_dacs < 3 || num_dacs > 5) {
 		printk(KERN_WARNING "hda_codec: Could not determine "
 		printk(KERN_WARNING "hda_codec: Could not determine "
 		       "number of channels defaulting to DAC count\n");
 		       "number of channels defaulting to DAC count\n");
-		spec->multiout.num_dacs = STAC92HD73_DAC_COUNT;
+		num_dacs = STAC92HD73_DAC_COUNT;
 	}
 	}
-
-	switch (spec->multiout.num_dacs) {
+	switch (num_dacs) {
 	case 0x3: /* 6 Channel */
 	case 0x3: /* 6 Channel */
-		spec->multiout.hp_nid = 0x17;
 		spec->mixer = stac92hd73xx_6ch_mixer;
 		spec->mixer = stac92hd73xx_6ch_mixer;
 		spec->init = stac92hd73xx_6ch_core_init;
 		spec->init = stac92hd73xx_6ch_core_init;
 		break;
 		break;
 	case 0x4: /* 8 Channel */
 	case 0x4: /* 8 Channel */
-		spec->multiout.hp_nid = 0x18;
 		spec->mixer = stac92hd73xx_8ch_mixer;
 		spec->mixer = stac92hd73xx_8ch_mixer;
 		spec->init = stac92hd73xx_8ch_core_init;
 		spec->init = stac92hd73xx_8ch_core_init;
 		break;
 		break;
 	case 0x5: /* 10 Channel */
 	case 0x5: /* 10 Channel */
-		spec->multiout.hp_nid = 0x19;
 		spec->mixer = stac92hd73xx_10ch_mixer;
 		spec->mixer = stac92hd73xx_10ch_mixer;
 		spec->init = stac92hd73xx_10ch_core_init;
 		spec->init = stac92hd73xx_10ch_core_init;
-	};
+	}
+	spec->multiout.dac_nids = spec->dac_nids;
 
 
-	spec->multiout.dac_nids = stac92hd73xx_dac_nids;
 	spec->aloopback_mask = 0x01;
 	spec->aloopback_mask = 0x01;
 	spec->aloopback_shift = 8;
 	spec->aloopback_shift = 8;
 
 
@@ -4310,9 +4582,8 @@ again:
 		spec->amp_nids = &stac92hd73xx_amp_nids[DELL_M6_AMP];
 		spec->amp_nids = &stac92hd73xx_amp_nids[DELL_M6_AMP];
 		spec->eapd_switch = 0;
 		spec->eapd_switch = 0;
 		spec->num_amps = 1;
 		spec->num_amps = 1;
-		spec->multiout.hp_nid = 0; /* dual HPs */
 
 
-		if (!spec->init)
+		if (spec->board_config != STAC_DELL_EQ)
 			spec->init = dell_m6_core_init;
 			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 */
@@ -4370,6 +4641,8 @@ again:
 
 
 	codec->patch_ops = stac92xx_patch_ops;
 	codec->patch_ops = stac92xx_patch_ops;
 
 
+	codec->proc_widget_hook = stac92hd7x_proc_hook;
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -4401,17 +4674,15 @@ static int patch_stac92hd83xxx(struct hda_codec *codec)
 	spec->pwr_nids = stac92hd83xxx_pwr_nids;
 	spec->pwr_nids = stac92hd83xxx_pwr_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 = stac92hd83xxx_dac_nids;
+	spec->multiout.dac_nids = spec->dac_nids;
 
 
 	spec->init = stac92hd83xxx_core_init;
 	spec->init = stac92hd83xxx_core_init;
 	switch (codec->vendor_id) {
 	switch (codec->vendor_id) {
 	case 0x111d7605:
 	case 0x111d7605:
-		spec->multiout.num_dacs = STAC92HD81_DAC_COUNT;
 		break;
 		break;
 	default:
 	default:
 		spec->num_pwrs--;
 		spec->num_pwrs--;
 		spec->init++; /* switch to config #2 */
 		spec->init++; /* switch to config #2 */
-		spec->multiout.num_dacs = STAC92HD83_DAC_COUNT;
 	}
 	}
 
 
 	spec->mixer = stac92hd83xxx_mixer;
 	spec->mixer = stac92hd83xxx_mixer;
@@ -4430,14 +4701,12 @@ again:
 		snd_printdd(KERN_INFO "hda_codec: Unknown model for"
 		snd_printdd(KERN_INFO "hda_codec: Unknown model for"
 			" STAC92HD83XXX, using BIOS defaults\n");
 			" STAC92HD83XXX, using BIOS defaults\n");
 		err = stac92xx_save_bios_config_regs(codec);
 		err = stac92xx_save_bios_config_regs(codec);
-		if (err < 0) {
-			stac92xx_free(codec);
-			return err;
-		}
-		spec->pin_configs = spec->bios_pin_configs;
-	} else {
-		spec->pin_configs = stac92hd83xxx_brd_tbl[spec->board_config];
-		stac92xx_set_config_regs(codec);
+	} else
+		err = stac_save_pin_cfgs(codec,
+				stac92hd83xxx_brd_tbl[spec->board_config]);
+	if (err < 0) {
+		stac92xx_free(codec);
+		return err;
 	}
 	}
 
 
 	err = stac92xx_parse_auto_config(codec, 0x1d, 0);
 	err = stac92xx_parse_auto_config(codec, 0x1d, 0);
@@ -4458,56 +4727,10 @@ again:
 
 
 	codec->patch_ops = stac92xx_patch_ops;
 	codec->patch_ops = stac92xx_patch_ops;
 
 
-	return 0;
-}
+	codec->proc_widget_hook = stac92hd_proc_hook;
 
 
-#ifdef SND_HDA_NEEDS_RESUME
-static void stac92hd71xx_set_power_state(struct hda_codec *codec, int pwr)
-{
-	struct sigmatel_spec *spec = codec->spec;
-	int i;
-	snd_hda_codec_write_cache(codec, codec->afg, 0,
-		AC_VERB_SET_POWER_STATE, pwr);
-
-	msleep(1);
-	for (i = 0; i < spec->num_adcs; i++) {
-		snd_hda_codec_write_cache(codec,
-			spec->adc_nids[i], 0,
-			AC_VERB_SET_POWER_STATE, pwr);
-	}
-};
-
-static int stac92hd71xx_resume(struct hda_codec *codec)
-{
-	stac92hd71xx_set_power_state(codec, AC_PWRST_D0);
-	return stac92xx_resume(codec);
-}
-
-static int stac92hd71xx_suspend(struct hda_codec *codec, pm_message_t state)
-{
-	struct sigmatel_spec *spec = codec->spec;
-
-	stac92hd71xx_set_power_state(codec, AC_PWRST_D3);
-	if (spec->eapd_mask)
-		stac_gpio_set(codec, spec->gpio_mask,
-				spec->gpio_dir, spec->gpio_data &
-				~spec->eapd_mask);
 	return 0;
 	return 0;
-};
-
-#endif
-
-static struct hda_codec_ops stac92hd71bxx_patch_ops = {
-	.build_controls = stac92xx_build_controls,
-	.build_pcms = stac92xx_build_pcms,
-	.init = stac92xx_init,
-	.free = stac92xx_free,
-	.unsol_event = stac92xx_unsol_event,
-#ifdef SND_HDA_NEEDS_RESUME
-	.resume = stac92hd71xx_resume,
-	.suspend = stac92hd71xx_suspend,
-#endif
-};
+}
 
 
 static struct hda_input_mux stac92hd71bxx_dmux = {
 static struct hda_input_mux stac92hd71bxx_dmux = {
 	.num_items = 4,
 	.num_items = 4,
@@ -4544,14 +4767,12 @@ again:
 		snd_printdd(KERN_INFO "hda_codec: Unknown model for"
 		snd_printdd(KERN_INFO "hda_codec: Unknown model for"
 			" STAC92HD71BXX, using BIOS defaults\n");
 			" STAC92HD71BXX, using BIOS defaults\n");
 		err = stac92xx_save_bios_config_regs(codec);
 		err = stac92xx_save_bios_config_regs(codec);
-		if (err < 0) {
-			stac92xx_free(codec);
-			return err;
-		}
-		spec->pin_configs = spec->bios_pin_configs;
-	} else {
-		spec->pin_configs = stac92hd71bxx_brd_tbl[spec->board_config];
-		stac92xx_set_config_regs(codec);
+	} else
+		err = stac_save_pin_cfgs(codec,
+				stac92hd71bxx_brd_tbl[spec->board_config]);
+	if (err < 0) {
+		stac92xx_free(codec);
+		return err;
 	}
 	}
 
 
 	if (spec->board_config > STAC_92HD71BXX_REF) {
 	if (spec->board_config > STAC_92HD71BXX_REF) {
@@ -4574,21 +4795,21 @@ again:
 		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 */
+			err = stac_add_event(spec, codec->afg,
+					     STAC_VREF_EVENT, 0x02);
+			if (err < 0)
+				return err;
 			snd_hda_codec_write_cache(codec, codec->afg, 0,
 			snd_hda_codec_write_cache(codec, codec->afg, 0,
 				AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02);
 				AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02);
 			snd_hda_codec_write_cache(codec, codec->afg, 0,
 			snd_hda_codec_write_cache(codec, codec->afg, 0,
-					AC_VERB_SET_UNSOLICITED_ENABLE,
-					(AC_USRSP_EN | STAC_VREF_EVENT | 0x01));
+				AC_VERB_SET_UNSOLICITED_ENABLE,
+				AC_USRSP_EN | err);
 			spec->gpio_mask |= 0x02;
 			spec->gpio_mask |= 0x02;
 			break;
 			break;
 		}
 		}
 		if ((codec->revision_id & 0xf) == 0 ||
 		if ((codec->revision_id & 0xf) == 0 ||
-				(codec->revision_id & 0xf) == 1) {
-#ifdef SND_HDA_NEEDS_RESUME
-			codec->patch_ops = stac92hd71bxx_patch_ops;
-#endif
+		    (codec->revision_id & 0xf) == 1)
 			spec->stream_delay = 40; /* 40 milliseconds */
 			spec->stream_delay = 40; /* 40 milliseconds */
-		}
 
 
 		/* no output amps */
 		/* no output amps */
 		spec->num_pwrs = 0;
 		spec->num_pwrs = 0;
@@ -4597,15 +4818,11 @@ again:
 
 
 		/* disable VSW */
 		/* disable VSW */
 		spec->init = &stac92hd71bxx_analog_core_init[HD_DISABLE_PORTF];
 		spec->init = &stac92hd71bxx_analog_core_init[HD_DISABLE_PORTF];
-		stac92xx_set_config_reg(codec, 0xf, 0x40f000f0);
+		stac_change_pin_config(codec, 0xf, 0x40f000f0);
 		break;
 		break;
 	case 0x111d7603: /* 6 Port with Analog Mixer */
 	case 0x111d7603: /* 6 Port with Analog Mixer */
-		if ((codec->revision_id & 0xf) == 1) {
-#ifdef SND_HDA_NEEDS_RESUME
-			codec->patch_ops = stac92hd71bxx_patch_ops;
-#endif
+		if ((codec->revision_id & 0xf) == 1)
 			spec->stream_delay = 40; /* 40 milliseconds */
 			spec->stream_delay = 40; /* 40 milliseconds */
-		}
 
 
 		/* no output amps */
 		/* no output amps */
 		spec->num_pwrs = 0;
 		spec->num_pwrs = 0;
@@ -4635,7 +4852,7 @@ again:
 	switch (spec->board_config) {
 	switch (spec->board_config) {
 	case STAC_HP_M4:
 	case STAC_HP_M4:
 		/* enable internal microphone */
 		/* enable internal microphone */
-		stac92xx_set_config_reg(codec, 0x0e, 0x01813040);
+		stac_change_pin_config(codec, 0x0e, 0x01813040);
 		stac92xx_auto_set_pinctl(codec, 0x0e,
 		stac92xx_auto_set_pinctl(codec, 0x0e,
 			AC_PINCTL_IN_EN | AC_PINCTL_VREF_80);
 			AC_PINCTL_IN_EN | AC_PINCTL_VREF_80);
 		/* fallthru */
 		/* fallthru */
@@ -4656,9 +4873,7 @@ again:
 		spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
 		spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
 	};
 	};
 
 
-	spec->multiout.num_dacs = 1;
-	spec->multiout.hp_nid = 0x11;
-	spec->multiout.dac_nids = stac92hd71bxx_dac_nids;
+	spec->multiout.dac_nids = spec->dac_nids;
 	if (spec->dinput_mux)
 	if (spec->dinput_mux)
 		spec->private_dimux.num_items +=
 		spec->private_dimux.num_items +=
 			spec->num_dmics -
 			spec->num_dmics -
@@ -4680,6 +4895,8 @@ again:
 		return err;
 		return err;
 	}
 	}
 
 
+	codec->proc_widget_hook = stac92hd7x_proc_hook;
+
 	return 0;
 	return 0;
 };
 };
 
 
@@ -4741,14 +4958,12 @@ static int patch_stac922x(struct hda_codec *codec)
 		snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, "
 		snd_printdd(KERN_INFO "hda_codec: Unknown model for STAC922x, "
 			"using BIOS defaults\n");
 			"using BIOS defaults\n");
 		err = stac92xx_save_bios_config_regs(codec);
 		err = stac92xx_save_bios_config_regs(codec);
-		if (err < 0) {
-			stac92xx_free(codec);
-			return err;
-		}
-		spec->pin_configs = spec->bios_pin_configs;
-	} else if (stac922x_brd_tbl[spec->board_config] != NULL) {
-		spec->pin_configs = stac922x_brd_tbl[spec->board_config];
-		stac92xx_set_config_regs(codec);
+	} else
+		err = stac_save_pin_cfgs(codec,
+				stac922x_brd_tbl[spec->board_config]);
+	if (err < 0) {
+		stac92xx_free(codec);
+		return err;
 	}
 	}
 
 
 	spec->adc_nids = stac922x_adc_nids;
 	spec->adc_nids = stac922x_adc_nids;
@@ -4811,14 +5026,12 @@ static int patch_stac927x(struct hda_codec *codec)
 			snd_printdd(KERN_INFO "hda_codec: Unknown model for"
 			snd_printdd(KERN_INFO "hda_codec: Unknown model for"
 				    "STAC927x, using BIOS defaults\n");
 				    "STAC927x, using BIOS defaults\n");
 		err = stac92xx_save_bios_config_regs(codec);
 		err = stac92xx_save_bios_config_regs(codec);
-		if (err < 0) {
-			stac92xx_free(codec);
-			return err;
-		}
-		spec->pin_configs = spec->bios_pin_configs;
-	} else {
-		spec->pin_configs = stac927x_brd_tbl[spec->board_config];
-		stac92xx_set_config_regs(codec);
+	} else
+		err = stac_save_pin_cfgs(codec,
+				stac927x_brd_tbl[spec->board_config]);
+	if (err < 0) {
+		stac92xx_free(codec);
+		return err;
 	}
 	}
 
 
 	spec->digbeep_nid = 0x23;
 	spec->digbeep_nid = 0x23;
@@ -4848,15 +5061,15 @@ static int patch_stac927x(struct hda_codec *codec)
 		case 0x10280209:
 		case 0x10280209:
 		case 0x1028022e:
 		case 0x1028022e:
 			/* correct the device field to SPDIF out */
 			/* correct the device field to SPDIF out */
-			stac92xx_set_config_reg(codec, 0x21, 0x01442070);
+			stac_change_pin_config(codec, 0x21, 0x01442070);
 			break;
 			break;
 		};
 		};
 		/* configure the analog microphone on some laptops */
 		/* configure the analog microphone on some laptops */
-		stac92xx_set_config_reg(codec, 0x0c, 0x90a79130);
+		stac_change_pin_config(codec, 0x0c, 0x90a79130);
 		/* correct the front output jack as a hp out */
 		/* correct the front output jack as a hp out */
-		stac92xx_set_config_reg(codec, 0x0f, 0x0227011f);
+		stac_change_pin_config(codec, 0x0f, 0x0227011f);
 		/* correct the front input jack as a mic */
 		/* correct the front input jack as a mic */
-		stac92xx_set_config_reg(codec, 0x0e, 0x02a79130);
+		stac_change_pin_config(codec, 0x0e, 0x02a79130);
 		/* fallthru */
 		/* fallthru */
 	case STAC_DELL_3ST:
 	case STAC_DELL_3ST:
 		/* GPIO2 High = Enable EAPD */
 		/* GPIO2 High = Enable EAPD */
@@ -4904,6 +5117,8 @@ static int patch_stac927x(struct hda_codec *codec)
 
 
 	codec->patch_ops = stac92xx_patch_ops;
 	codec->patch_ops = stac92xx_patch_ops;
 
 
+	codec->proc_widget_hook = stac927x_proc_hook;
+
 	/*
 	/*
 	 * !!FIXME!!
 	 * !!FIXME!!
 	 * The STAC927x seem to require fairly long delays for certain
 	 * The STAC927x seem to require fairly long delays for certain
@@ -4942,14 +5157,12 @@ static int patch_stac9205(struct hda_codec *codec)
 	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: Unknown model for STAC9205, using BIOS defaults\n");
 		err = stac92xx_save_bios_config_regs(codec);
 		err = stac92xx_save_bios_config_regs(codec);
-		if (err < 0) {
-			stac92xx_free(codec);
-			return err;
-		}
-		spec->pin_configs = spec->bios_pin_configs;
-	} else {
-		spec->pin_configs = stac9205_brd_tbl[spec->board_config];
-		stac92xx_set_config_regs(codec);
+	} else
+		err = stac_save_pin_cfgs(codec,
+					 stac9205_brd_tbl[spec->board_config]);
+	if (err < 0) {
+		stac92xx_free(codec);
+		return err;
 	}
 	}
 
 
 	spec->digbeep_nid = 0x23;
 	spec->digbeep_nid = 0x23;
@@ -4976,15 +5189,18 @@ static int patch_stac9205(struct hda_codec *codec)
 	switch (spec->board_config){
 	switch (spec->board_config){
 	case STAC_9205_DELL_M43:
 	case STAC_9205_DELL_M43:
 		/* Enable SPDIF in/out */
 		/* Enable SPDIF in/out */
-		stac92xx_set_config_reg(codec, 0x1f, 0x01441030);
-		stac92xx_set_config_reg(codec, 0x20, 0x1c410030);
+		stac_change_pin_config(codec, 0x1f, 0x01441030);
+		stac_change_pin_config(codec, 0x20, 0x1c410030);
 
 
 		/* Enable unsol response for GPIO4/Dock HP connection */
 		/* Enable unsol response for GPIO4/Dock HP connection */
+		err = stac_add_event(spec, codec->afg, STAC_VREF_EVENT, 0x01);
+		if (err < 0)
+			return err;
 		snd_hda_codec_write_cache(codec, codec->afg, 0,
 		snd_hda_codec_write_cache(codec, codec->afg, 0,
 			AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10);
 			AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10);
 		snd_hda_codec_write_cache(codec, codec->afg, 0,
 		snd_hda_codec_write_cache(codec, codec->afg, 0,
 					  AC_VERB_SET_UNSOLICITED_ENABLE,
 					  AC_VERB_SET_UNSOLICITED_ENABLE,
-					  (AC_USRSP_EN | STAC_HP_EVENT));
+					  AC_USRSP_EN | err);
 
 
 		spec->gpio_dir = 0x0b;
 		spec->gpio_dir = 0x0b;
 		spec->eapd_mask = 0x01;
 		spec->eapd_mask = 0x01;
@@ -5022,6 +5238,8 @@ static int patch_stac9205(struct hda_codec *codec)
 
 
 	codec->patch_ops = stac92xx_patch_ops;
 	codec->patch_ops = stac92xx_patch_ops;
 
 
+	codec->proc_widget_hook = stac9205_proc_hook;
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -5078,29 +5296,11 @@ static struct hda_verb vaio_ar_init[] = {
 	{}
 	{}
 };
 };
 
 
-/* bind volumes of both NID 0x02 and 0x05 */
-static struct hda_bind_ctls vaio_bind_master_vol = {
-	.ops = &snd_hda_bind_vol,
-	.values = {
-		HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
-		HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
-		0
-	},
-};
-
-/* bind volumes of both NID 0x02 and 0x05 */
-static struct hda_bind_ctls vaio_bind_master_sw = {
-	.ops = &snd_hda_bind_sw,
-	.values = {
-		HDA_COMPOSE_AMP_VAL(0x02, 3, 0, HDA_OUTPUT),
-		HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
-		0,
-	},
-};
-
 static struct snd_kcontrol_new vaio_mixer[] = {
 static struct snd_kcontrol_new vaio_mixer[] = {
-	HDA_BIND_VOL("Master Playback Volume", &vaio_bind_master_vol),
-	HDA_BIND_SW("Master Playback Switch", &vaio_bind_master_sw),
+	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x02, 0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x02, 0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x05, 0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Speaker Playback Switch", 0x05, 0, HDA_OUTPUT),
 	/* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */
 	/* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */
 	HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT),
@@ -5116,8 +5316,10 @@ static struct snd_kcontrol_new vaio_mixer[] = {
 };
 };
 
 
 static struct snd_kcontrol_new vaio_ar_mixer[] = {
 static struct snd_kcontrol_new vaio_ar_mixer[] = {
-	HDA_BIND_VOL("Master Playback Volume", &vaio_bind_master_vol),
-	HDA_BIND_SW("Master Playback Switch", &vaio_bind_master_sw),
+	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x02, 0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x02, 0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x05, 0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Speaker Playback Switch", 0x05, 0, HDA_OUTPUT),
 	/* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */
 	/* HDA_CODEC_VOLUME("CD Capture Volume", 0x07, 0, HDA_INPUT), */
 	HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Capture Volume", 0x09, 0, HDA_INPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x09, 0, HDA_INPUT),
@@ -5158,7 +5360,7 @@ static int stac9872_vaio_init(struct hda_codec *codec)
 
 
 static void stac9872_vaio_hp_detect(struct hda_codec *codec, unsigned int res)
 static void stac9872_vaio_hp_detect(struct hda_codec *codec, unsigned int res)
 {
 {
-	if (get_hp_pin_presence(codec, 0x0a)) {
+	if (get_pin_presence(codec, 0x0a)) {
 		stac92xx_reset_pinctl(codec, 0x0f, AC_PINCTL_OUT_EN);
 		stac92xx_reset_pinctl(codec, 0x0f, AC_PINCTL_OUT_EN);
 		stac92xx_set_pinctl(codec, 0x0a, AC_PINCTL_OUT_EN);
 		stac92xx_set_pinctl(codec, 0x0a, AC_PINCTL_OUT_EN);
 	} else {
 	} else {
@@ -5269,7 +5471,7 @@ static int patch_stac9872(struct hda_codec *codec)
 /*
 /*
  * patch entries
  * patch entries
  */
  */
-struct hda_codec_preset snd_hda_preset_sigmatel[] = {
+static struct hda_codec_preset snd_hda_preset_sigmatel[] = {
  	{ .id = 0x83847690, .name = "STAC9200", .patch = patch_stac9200 },
  	{ .id = 0x83847690, .name = "STAC9200", .patch = patch_stac9200 },
  	{ .id = 0x83847882, .name = "STAC9220 A1", .patch = patch_stac922x },
  	{ .id = 0x83847882, .name = "STAC9220 A1", .patch = patch_stac922x },
  	{ .id = 0x83847680, .name = "STAC9221 A1", .patch = patch_stac922x },
  	{ .id = 0x83847680, .name = "STAC9221 A1", .patch = patch_stac922x },
@@ -5333,3 +5535,27 @@ struct hda_codec_preset snd_hda_preset_sigmatel[] = {
 	{ .id = 0x111d76b7, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
 	{ .id = 0x111d76b7, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
 	{} /* terminator */
 	{} /* terminator */
 };
 };
+
+MODULE_ALIAS("snd-hda-codec-id:8384*");
+MODULE_ALIAS("snd-hda-codec-id:111d*");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("IDT/Sigmatel HD-audio codec");
+
+static struct hda_codec_preset_list sigmatel_list = {
+	.preset = snd_hda_preset_sigmatel,
+	.owner = THIS_MODULE,
+};
+
+static int __init patch_sigmatel_init(void)
+{
+	return snd_hda_add_codec_preset(&sigmatel_list);
+}
+
+static void __exit patch_sigmatel_exit(void)
+{
+	snd_hda_delete_codec_preset(&sigmatel_list);
+}
+
+module_init(patch_sigmatel_init)
+module_exit(patch_sigmatel_exit)

+ 90 - 81
sound/pci/hda/patch_via.c

@@ -47,15 +47,11 @@
 #include <sound/asoundef.h>
 #include <sound/asoundef.h>
 #include "hda_codec.h"
 #include "hda_codec.h"
 #include "hda_local.h"
 #include "hda_local.h"
-#include "hda_patch.h"
 
 
 /* amp values */
 /* amp values */
 #define AMP_VAL_IDX_SHIFT	19
 #define AMP_VAL_IDX_SHIFT	19
 #define AMP_VAL_IDX_MASK	(0x0f<<19)
 #define AMP_VAL_IDX_MASK	(0x0f<<19)
 
 
-#define NUM_CONTROL_ALLOC	32
-#define NUM_VERB_ALLOC		32
-
 /* Pin Widget NID */
 /* Pin Widget NID */
 #define VT1708_HP_NID		0x13
 #define VT1708_HP_NID		0x13
 #define VT1708_DIGOUT_NID	0x14
 #define VT1708_DIGOUT_NID	0x14
@@ -145,8 +141,6 @@ enum {
 	AUTO_SEQ_SIDE
 	AUTO_SEQ_SIDE
 };
 };
 
 
-#define get_amp_nid(kc)	((kc)->private_value & 0xffff)
-
 /* Some VT1708S based boards gets the micboost setting wrong, so we have
 /* Some VT1708S based boards gets the micboost setting wrong, so we have
  * to apply some brute-force and re-write the TLV's by software. */
  * to apply some brute-force and re-write the TLV's by software. */
 static int mic_boost_tlv(struct snd_kcontrol *kcontrol, int op_flag,
 static int mic_boost_tlv(struct snd_kcontrol *kcontrol, int op_flag,
@@ -227,8 +221,7 @@ struct via_spec {
 
 
 	/* dynamic controls, init_verbs and input_mux */
 	/* dynamic controls, init_verbs and input_mux */
 	struct auto_pin_cfg autocfg;
 	struct auto_pin_cfg autocfg;
-	unsigned int num_kctl_alloc, num_kctl_used;
-	struct snd_kcontrol_new *kctl_alloc;
+	struct snd_array kctls;
 	struct hda_input_mux private_imux[2];
 	struct hda_input_mux private_imux[2];
 	hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
 	hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
 
 
@@ -272,33 +265,31 @@ static int via_add_control(struct via_spec *spec, int type, const char *name,
 {
 {
 	struct snd_kcontrol_new *knew;
 	struct snd_kcontrol_new *knew;
 
 
-	if (spec->num_kctl_used >= spec->num_kctl_alloc) {
-		int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
-
-		/* array + terminator */
-		knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL);
-		if (!knew)
-			return -ENOMEM;
-		if (spec->kctl_alloc) {
-			memcpy(knew, spec->kctl_alloc,
-			       sizeof(*knew) * spec->num_kctl_alloc);
-			kfree(spec->kctl_alloc);
-		}
-		spec->kctl_alloc = knew;
-		spec->num_kctl_alloc = num;
-	}
-
-	knew = &spec->kctl_alloc[spec->num_kctl_used];
+	snd_array_init(&spec->kctls, sizeof(*knew), 32);
+	knew = snd_array_new(&spec->kctls);
+	if (!knew)
+		return -ENOMEM;
 	*knew = vt1708_control_templates[type];
 	*knew = vt1708_control_templates[type];
 	knew->name = kstrdup(name, GFP_KERNEL);
 	knew->name = kstrdup(name, GFP_KERNEL);
-
 	if (!knew->name)
 	if (!knew->name)
 		return -ENOMEM;
 		return -ENOMEM;
 	knew->private_value = val;
 	knew->private_value = val;
-	spec->num_kctl_used++;
 	return 0;
 	return 0;
 }
 }
 
 
+static void via_free_kctls(struct hda_codec *codec)
+{
+	struct via_spec *spec = codec->spec;
+
+	if (spec->kctls.list) {
+		struct snd_kcontrol_new *kctl = spec->kctls.list;
+		int i;
+		for (i = 0; i < spec->kctls.used; i++)
+			kfree(kctl[i].name);
+	}
+	snd_array_free(&spec->kctls);
+}
+
 /* create input playback/capture controls for the given pin */
 /* create input playback/capture controls for the given pin */
 static int via_new_analog_input(struct via_spec *spec, hda_nid_t pin,
 static int via_new_analog_input(struct via_spec *spec, hda_nid_t pin,
 				const char *ctlname, int idx, int mix_nid)
 				const char *ctlname, int idx, int mix_nid)
@@ -896,6 +887,7 @@ static int via_build_controls(struct hda_codec *codec)
 		if (err < 0)
 		if (err < 0)
 			return err;
 			return err;
 	}
 	}
+	via_free_kctls(codec); /* no longer needed */
 	return 0;
 	return 0;
 }
 }
 
 
@@ -941,17 +933,11 @@ static int via_build_pcms(struct hda_codec *codec)
 static void via_free(struct hda_codec *codec)
 static void via_free(struct hda_codec *codec)
 {
 {
 	struct via_spec *spec = codec->spec;
 	struct via_spec *spec = codec->spec;
-	unsigned int i;
 
 
 	if (!spec)
 	if (!spec)
 		return;
 		return;
 
 
-	if (spec->kctl_alloc) {
-		for (i = 0; i < spec->num_kctl_used; i++)
-			kfree(spec->kctl_alloc[i].name);
-		kfree(spec->kctl_alloc);
-	}
-
+	via_free_kctls(codec);
 	kfree(codec->spec);
 	kfree(codec->spec);
 }
 }
 
 
@@ -1373,8 +1359,8 @@ static int vt1708_parse_auto_config(struct hda_codec *codec)
 	if (spec->autocfg.dig_in_pin)
 	if (spec->autocfg.dig_in_pin)
 		spec->dig_in_nid = VT1708_DIGIN_NID;
 		spec->dig_in_nid = VT1708_DIGIN_NID;
 
 
-	if (spec->kctl_alloc)
-		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+	if (spec->kctls.list)
+		spec->mixers[spec->num_mixers++] = spec->kctls.list;
 
 
 	spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs;
 	spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs;
 
 
@@ -1846,8 +1832,8 @@ static int vt1709_parse_auto_config(struct hda_codec *codec)
 	if (spec->autocfg.dig_in_pin)
 	if (spec->autocfg.dig_in_pin)
 		spec->dig_in_nid = VT1709_DIGIN_NID;
 		spec->dig_in_nid = VT1709_DIGIN_NID;
 
 
-	if (spec->kctl_alloc)
-		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+	if (spec->kctls.list)
+		spec->mixers[spec->num_mixers++] = spec->kctls.list;
 
 
 	spec->input_mux = &spec->private_imux[0];
 	spec->input_mux = &spec->private_imux[0];
 
 
@@ -2390,8 +2376,8 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec)
 	if (spec->autocfg.dig_in_pin)
 	if (spec->autocfg.dig_in_pin)
 		spec->dig_in_nid = VT1708B_DIGIN_NID;
 		spec->dig_in_nid = VT1708B_DIGIN_NID;
 
 
-	if (spec->kctl_alloc)
-		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+	if (spec->kctls.list)
+		spec->mixers[spec->num_mixers++] = spec->kctls.list;
 
 
 	spec->input_mux = &spec->private_imux[0];
 	spec->input_mux = &spec->private_imux[0];
 
 
@@ -2855,8 +2841,8 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec)
 
 
 	spec->extra_dig_out_nid = 0x15;
 	spec->extra_dig_out_nid = 0x15;
 
 
-	if (spec->kctl_alloc)
-		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+	if (spec->kctls.list)
+		spec->mixers[spec->num_mixers++] = spec->kctls.list;
 
 
 	spec->input_mux = &spec->private_imux[0];
 	spec->input_mux = &spec->private_imux[0];
 
 
@@ -3174,8 +3160,8 @@ static int vt1702_parse_auto_config(struct hda_codec *codec)
 
 
 	spec->extra_dig_out_nid = 0x1B;
 	spec->extra_dig_out_nid = 0x1B;
 
 
-	if (spec->kctl_alloc)
-		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+	if (spec->kctls.list)
+		spec->mixers[spec->num_mixers++] = spec->kctls.list;
 
 
 	spec->input_mux = &spec->private_imux[0];
 	spec->input_mux = &spec->private_imux[0];
 
 
@@ -3262,74 +3248,97 @@ static int patch_vt1702(struct hda_codec *codec)
 /*
 /*
  * patch entries
  * patch entries
  */
  */
-struct hda_codec_preset snd_hda_preset_via[] = {
-	{ .id = 0x11061708, .name = "VIA VT1708", .patch = patch_vt1708},
-	{ .id = 0x11061709, .name = "VIA VT1708", .patch = patch_vt1708},
-	{ .id = 0x1106170A, .name = "VIA VT1708", .patch = patch_vt1708},
-	{ .id = 0x1106170B, .name = "VIA VT1708", .patch = patch_vt1708},
-	{ .id = 0x1106E710, .name = "VIA VT1709 10-Ch",
+static struct hda_codec_preset snd_hda_preset_via[] = {
+	{ .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
+	{ .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
+	{ .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
+	{ .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
+	{ .id = 0x1106e710, .name = "VT1709 10-Ch",
 	  .patch = patch_vt1709_10ch},
 	  .patch = patch_vt1709_10ch},
-	{ .id = 0x1106E711, .name = "VIA VT1709 10-Ch",
+	{ .id = 0x1106e711, .name = "VT1709 10-Ch",
 	  .patch = patch_vt1709_10ch},
 	  .patch = patch_vt1709_10ch},
-	{ .id = 0x1106E712, .name = "VIA VT1709 10-Ch",
+	{ .id = 0x1106e712, .name = "VT1709 10-Ch",
 	  .patch = patch_vt1709_10ch},
 	  .patch = patch_vt1709_10ch},
-	{ .id = 0x1106E713, .name = "VIA VT1709 10-Ch",
+	{ .id = 0x1106e713, .name = "VT1709 10-Ch",
 	  .patch = patch_vt1709_10ch},
 	  .patch = patch_vt1709_10ch},
-	{ .id = 0x1106E714, .name = "VIA VT1709 6-Ch",
+	{ .id = 0x1106e714, .name = "VT1709 6-Ch",
 	  .patch = patch_vt1709_6ch},
 	  .patch = patch_vt1709_6ch},
-	{ .id = 0x1106E715, .name = "VIA VT1709 6-Ch",
+	{ .id = 0x1106e715, .name = "VT1709 6-Ch",
 	  .patch = patch_vt1709_6ch},
 	  .patch = patch_vt1709_6ch},
-	{ .id = 0x1106E716, .name = "VIA VT1709 6-Ch",
+	{ .id = 0x1106e716, .name = "VT1709 6-Ch",
 	  .patch = patch_vt1709_6ch},
 	  .patch = patch_vt1709_6ch},
-	{ .id = 0x1106E717, .name = "VIA VT1709 6-Ch",
+	{ .id = 0x1106e717, .name = "VT1709 6-Ch",
 	  .patch = patch_vt1709_6ch},
 	  .patch = patch_vt1709_6ch},
-	{ .id = 0x1106E720, .name = "VIA VT1708B 8-Ch",
+	{ .id = 0x1106e720, .name = "VT1708B 8-Ch",
 	  .patch = patch_vt1708B_8ch},
 	  .patch = patch_vt1708B_8ch},
-	{ .id = 0x1106E721, .name = "VIA VT1708B 8-Ch",
+	{ .id = 0x1106e721, .name = "VT1708B 8-Ch",
 	  .patch = patch_vt1708B_8ch},
 	  .patch = patch_vt1708B_8ch},
-	{ .id = 0x1106E722, .name = "VIA VT1708B 8-Ch",
+	{ .id = 0x1106e722, .name = "VT1708B 8-Ch",
 	  .patch = patch_vt1708B_8ch},
 	  .patch = patch_vt1708B_8ch},
-	{ .id = 0x1106E723, .name = "VIA VT1708B 8-Ch",
+	{ .id = 0x1106e723, .name = "VT1708B 8-Ch",
 	  .patch = patch_vt1708B_8ch},
 	  .patch = patch_vt1708B_8ch},
-	{ .id = 0x1106E724, .name = "VIA VT1708B 4-Ch",
+	{ .id = 0x1106e724, .name = "VT1708B 4-Ch",
 	  .patch = patch_vt1708B_4ch},
 	  .patch = patch_vt1708B_4ch},
-	{ .id = 0x1106E725, .name = "VIA VT1708B 4-Ch",
+	{ .id = 0x1106e725, .name = "VT1708B 4-Ch",
 	  .patch = patch_vt1708B_4ch},
 	  .patch = patch_vt1708B_4ch},
-	{ .id = 0x1106E726, .name = "VIA VT1708B 4-Ch",
+	{ .id = 0x1106e726, .name = "VT1708B 4-Ch",
 	  .patch = patch_vt1708B_4ch},
 	  .patch = patch_vt1708B_4ch},
-	{ .id = 0x1106E727, .name = "VIA VT1708B 4-Ch",
+	{ .id = 0x1106e727, .name = "VT1708B 4-Ch",
 	  .patch = patch_vt1708B_4ch},
 	  .patch = patch_vt1708B_4ch},
-	{ .id = 0x11060397, .name = "VIA VT1708S",
+	{ .id = 0x11060397, .name = "VT1708S",
 	  .patch = patch_vt1708S},
 	  .patch = patch_vt1708S},
-	{ .id = 0x11061397, .name = "VIA VT1708S",
+	{ .id = 0x11061397, .name = "VT1708S",
 	  .patch = patch_vt1708S},
 	  .patch = patch_vt1708S},
-	{ .id = 0x11062397, .name = "VIA VT1708S",
+	{ .id = 0x11062397, .name = "VT1708S",
 	  .patch = patch_vt1708S},
 	  .patch = patch_vt1708S},
-	{ .id = 0x11063397, .name = "VIA VT1708S",
+	{ .id = 0x11063397, .name = "VT1708S",
 	  .patch = patch_vt1708S},
 	  .patch = patch_vt1708S},
-	{ .id = 0x11064397, .name = "VIA VT1708S",
+	{ .id = 0x11064397, .name = "VT1708S",
 	  .patch = patch_vt1708S},
 	  .patch = patch_vt1708S},
-	{ .id = 0x11065397, .name = "VIA VT1708S",
+	{ .id = 0x11065397, .name = "VT1708S",
 	  .patch = patch_vt1708S},
 	  .patch = patch_vt1708S},
-	{ .id = 0x11066397, .name = "VIA VT1708S",
+	{ .id = 0x11066397, .name = "VT1708S",
 	  .patch = patch_vt1708S},
 	  .patch = patch_vt1708S},
-	{ .id = 0x11067397, .name = "VIA VT1708S",
+	{ .id = 0x11067397, .name = "VT1708S",
 	  .patch = patch_vt1708S},
 	  .patch = patch_vt1708S},
-	{ .id = 0x11060398, .name = "VIA VT1702",
+	{ .id = 0x11060398, .name = "VT1702",
 	  .patch = patch_vt1702},
 	  .patch = patch_vt1702},
-	{ .id = 0x11061398, .name = "VIA VT1702",
+	{ .id = 0x11061398, .name = "VT1702",
 	  .patch = patch_vt1702},
 	  .patch = patch_vt1702},
-	{ .id = 0x11062398, .name = "VIA VT1702",
+	{ .id = 0x11062398, .name = "VT1702",
 	  .patch = patch_vt1702},
 	  .patch = patch_vt1702},
-	{ .id = 0x11063398, .name = "VIA VT1702",
+	{ .id = 0x11063398, .name = "VT1702",
 	  .patch = patch_vt1702},
 	  .patch = patch_vt1702},
-	{ .id = 0x11064398, .name = "VIA VT1702",
+	{ .id = 0x11064398, .name = "VT1702",
 	  .patch = patch_vt1702},
 	  .patch = patch_vt1702},
-	{ .id = 0x11065398, .name = "VIA VT1702",
+	{ .id = 0x11065398, .name = "VT1702",
 	  .patch = patch_vt1702},
 	  .patch = patch_vt1702},
-	{ .id = 0x11066398, .name = "VIA VT1702",
+	{ .id = 0x11066398, .name = "VT1702",
 	  .patch = patch_vt1702},
 	  .patch = patch_vt1702},
-	{ .id = 0x11067398, .name = "VIA VT1702",
+	{ .id = 0x11067398, .name = "VT1702",
 	  .patch = patch_vt1702},
 	  .patch = patch_vt1702},
 	{} /* terminator */
 	{} /* terminator */
 };
 };
+
+MODULE_ALIAS("snd-hda-codec-id:1106*");
+
+static struct hda_codec_preset_list via_list = {
+	.preset = snd_hda_preset_via,
+	.owner = THIS_MODULE,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("VIA HD-audio codec");
+
+static int __init patch_via_init(void)
+{
+	return snd_hda_add_codec_preset(&via_list);
+}
+
+static void __exit patch_via_exit(void)
+{
+	snd_hda_delete_codec_preset(&via_list);
+}
+
+module_init(patch_via_init)
+module_exit(patch_via_exit)