浏览代码

Merge branch 'linus' of master.kernel.org:/pub/scm/linux/kernel/git/perex/alsa

* 'linus' of master.kernel.org:/pub/scm/linux/kernel/git/perex/alsa: (102 commits)
  [ALSA] version 1.0.14
  [ALSA] remove duplicate Logitech Quickcam USB ID in usbquirks.h
  [ALSA] hda-codec - Fix input with STAC92xx
  [ALSA] hda-intel: support for iMac 24'' released on 09/2006
  [ALSA] hda-codec - Add quirk for Asus P5LD2
  [ALSA] snd-ca0106: Add support for X-Fi Extreme Audio.
  [ALSA] snd-emu10k1:Enable E-Mu 1616m notebook firmware loading.
  [ALSA] snd-emu10k1: Initial support for E-Mu 1616 and 1616m.
  [ALSA] cs46xx - Fix PM resume
  [ALSA] hda: Enable SPDIF in/out on some stac9205 boards
  [ALSA] timer: check for incorrect device state in non-debug compiles, too
  [ALSA] snd-aoa-codec-onyx: fix typo
  [ALSA] hda-codec - Add quirks for HP dx2200/dx2250
  [ALSA] hda-codec - Rename HP model-specific quirks
  [ALSA] hda-codec - Add quirk for HP Samba
  [ALSA] hda-codec - Add LG LW20 line-in capture source
  [ALSA] usb-audio - Fix AC3 with M-Audio Audiophile USB
  [ALSA] hda: stac9202 mixer fix
  [ALSA] Make s3c24xx_i2s_set_clkdiv() change the correct bits
  [ALSA] hda-codec - Add LG LW20 si3054 modem id
  ...
Linus Torvalds 18 年之前
父节点
当前提交
7f46e6ca01
共有 89 个文件被更改,包括 8447 次插入420 次删除
  1. 5 5
      CREDITS
  2. 69 6
      Documentation/sound/alsa/ALSA-Configuration.txt
  3. 162 80
      Documentation/sound/alsa/Audiophile-Usb.txt
  4. 15 0
      Documentation/sound/alsa/OSS-Emulation.txt
  5. 4 3
      include/linux/i2c-id.h
  6. 1 0
      include/sound/ak4xxx-adda.h
  7. 4 0
      include/sound/cs46xx.h
  8. 2 0
      include/sound/cs46xx_dsp_spos.h
  9. 16 0
      include/sound/emu10k1.h
  10. 1 0
      include/sound/sb.h
  11. 1 1
      include/sound/version.h
  12. 0 9
      include/sound/wavefront_fx.h
  13. 2 0
      sound/Kconfig
  14. 1 1
      sound/Makefile
  15. 2 2
      sound/aoa/codecs/snd-aoa-codec-onyx.c
  16. 1 1
      sound/core/pcm_native.c
  17. 3 3
      sound/core/seq/seq_instr.c
  18. 18 9
      sound/core/timer.c
  19. 1 1
      sound/drivers/dummy.c
  20. 1 1
      sound/drivers/mpu401/mpu401.c
  21. 1 1
      sound/drivers/portman2x4.c
  22. 1 1
      sound/drivers/serial-u16550.c
  23. 1 1
      sound/drivers/virmidi.c
  24. 22 2
      sound/i2c/other/ak4xxx-adda.c
  25. 23 9
      sound/isa/Kconfig
  26. 2 2
      sound/isa/ad1848/ad1848_lib.c
  27. 2 0
      sound/isa/opl3sa2.c
  28. 3 0
      sound/isa/opti9xx/opti92x-ad1848.c
  29. 7 8
      sound/isa/sb/Makefile
  30. 10 0
      sound/isa/sb/sb16_main.c
  31. 4 1
      sound/isa/sb/sb_common.c
  32. 3 0
      sound/isa/sb/sb_mixer.c
  33. 2 2
      sound/isa/sscape.c
  34. 1 1
      sound/isa/wavefront/wavefront_synth.c
  35. 11 0
      sound/pci/Kconfig
  36. 2 0
      sound/pci/Makefile
  37. 3 4
      sound/pci/ali5451/ali5451.c
  38. 4 3
      sound/pci/als300.c
  39. 19 0
      sound/pci/ca0106/ca0106_main.c
  40. 62 15
      sound/pci/cs46xx/cs46xx_lib.c
  41. 3 0
      sound/pci/cs46xx/cs46xx_lib.h
  42. 126 44
      sound/pci/cs46xx/dsp_spos.c
  43. 306 0
      sound/pci/cs5530.c
  44. 108 17
      sound/pci/emu10k1/emu10k1_main.c
  45. 76 2
      sound/pci/emu10k1/emufx.c
  46. 16 0
      sound/pci/emu10k1/emumixer.c
  47. 28 11
      sound/pci/emu10k1/emupcm.c
  48. 2 2
      sound/pci/ens1370.c
  49. 28 25
      sound/pci/hda/hda_intel.c
  50. 6 0
      sound/pci/hda/hda_proc.c
  51. 627 3
      sound/pci/hda/patch_analog.c
  52. 1 0
      sound/pci/hda/patch_atihdmi.c
  53. 2 0
      sound/pci/hda/patch_conexant.c
  54. 917 44
      sound/pci/hda/patch_realtek.c
  55. 4 0
      sound/pci/hda/patch_si3054.c
  56. 184 82
      sound/pci/hda/patch_sigmatel.c
  57. 6 1
      sound/pci/ice1712/revo.c
  58. 2 1
      sound/pci/nm256/nm256.c
  59. 1 1
      sound/pci/rme9652/rme9652.c
  60. 2 2
      sound/pci/via82xx.c
  61. 2 2
      sound/pci/via82xx_modem.c
  62. 20 0
      sound/ppc/Kconfig
  63. 2 1
      sound/ppc/Makefile
  64. 1125 0
      sound/ppc/snd_ps3.c
  65. 135 0
      sound/ppc/snd_ps3.h
  66. 891 0
      sound/ppc/snd_ps3_reg.h
  67. 14 0
      sound/sh/Kconfig
  68. 8 0
      sound/sh/Makefile
  69. 665 0
      sound/sh/aica.c
  70. 81 0
      sound/sh/aica.h
  71. 1 0
      sound/soc/Kconfig
  72. 1 1
      sound/soc/Makefile
  73. 27 0
      sound/soc/s3c24xx/Kconfig
  74. 9 0
      sound/soc/s3c24xx/Makefile
  75. 32 0
      sound/soc/s3c24xx/lm4857.h
  76. 670 0
      sound/soc/s3c24xx/neo1973_wm8753.c
  77. 401 0
      sound/soc/s3c24xx/s3c2443-ac97.c
  78. 25 0
      sound/soc/s3c24xx/s3c24xx-ac97.h
  79. 2 2
      sound/soc/s3c24xx/s3c24xx-i2s.c
  80. 85 0
      sound/soc/s3c24xx/smdk2443_wm9710.c
  81. 38 0
      sound/soc/sh/Kconfig
  82. 14 0
      sound/soc/sh/Makefile
  83. 354 0
      sound/soc/sh/dma-sh7760.c
  84. 322 0
      sound/soc/sh/hac.c
  85. 92 0
      sound/soc/sh/sh7760-ac97.c
  86. 400 0
      sound/soc/sh/ssi.c
  87. 20 2
      sound/usb/usbaudio.c
  88. 71 1
      sound/usb/usbquirks.h
  89. 3 4
      sound/usb/usx2y/usbusx2yaudio.c

+ 5 - 5
CREDITS

@@ -2212,13 +2212,13 @@ S: 2300 Copenhagen S
 S: Denmark
 S: Denmark
 
 
 N: Claudio S. Matsuoka
 N: Claudio S. Matsuoka
-E: claudio@conectiva.com
-E: claudio@helllabs.org
+E: cmatsuoka@gmail.com
+E: claudio@mandriva.com
 W: http://helllabs.org/~claudio
 W: http://helllabs.org/~claudio
-D: V4L, OV511 driver hacks
+D: V4L, OV511 and HDA-codec hacks
 S: Conectiva S.A.
 S: Conectiva S.A.
-S: R. Tocantins 89
-S: 80050-430  Curitiba PR
+S: Souza Naves 1250
+S: 80050-040  Curitiba PR
 S: Brazil
 S: Brazil
 
 
 N: Heinz Mauelshagen
 N: Heinz Mauelshagen

+ 69 - 6
Documentation/sound/alsa/ALSA-Configuration.txt

@@ -467,7 +467,12 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     above explicitly.
     above explicitly.
 
 
     The power-management is supported.
     The power-management is supported.
-    
+
+  Module snd-cs5530
+  _________________
+
+    Module for Cyrix/NatSemi Geode 5530 chip. 
+  
   Module snd-cs5535audio
   Module snd-cs5535audio
   ----------------------
   ----------------------
 
 
@@ -759,6 +764,7 @@ 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 = none, 2 = POSBUF, 3 = FIFO size)
     position_fix - Fix DMA pointer (0 = auto, 1 = none, 2 = POSBUF, 3 = FIFO size)
+    probe_mask  - Bitmask to probe codecs (default = -1, meaning all slots)
     single_cmd  - Use single immediate commands to communicate with
     single_cmd  - Use single immediate commands to communicate with
 		codecs (for debugging only)
 		codecs (for debugging only)
     enable_msi	- Enable Message Signaled Interrupt (MSI) (default = off)
     enable_msi	- Enable Message Signaled Interrupt (MSI) (default = off)
@@ -803,6 +809,8 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 	  hp-3013	HP machines (3013-variant)
 	  hp-3013	HP machines (3013-variant)
 	  fujitsu	Fujitsu S7020
 	  fujitsu	Fujitsu S7020
 	  acer		Acer TravelMate
 	  acer		Acer TravelMate
+	  will		Will laptops (PB V7900)
+	  replacer	Replacer 672V
 	  basic		fixed pin assignment (old default model)
 	  basic		fixed pin assignment (old default model)
 	  auto		auto-config reading BIOS (default)
 	  auto		auto-config reading BIOS (default)
 
 
@@ -811,16 +819,31 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 	  hp-bpc	HP xw4400/6400/8400/9400 laptops
 	  hp-bpc	HP xw4400/6400/8400/9400 laptops
 	  hp-bpc-d7000	HP BPC D7000
 	  hp-bpc-d7000	HP BPC D7000
 	  benq		Benq ED8
 	  benq		Benq ED8
+	  benq-t31	Benq T31
 	  hippo		Hippo (ATI) with jack detection, Sony UX-90s
 	  hippo		Hippo (ATI) with jack detection, Sony UX-90s
 	  hippo_1	Hippo (Benq) with jack detection
 	  hippo_1	Hippo (Benq) with jack detection
+	  sony-assamd	Sony ASSAMD
 	  basic		fixed pin assignment w/o SPDIF
 	  basic		fixed pin assignment w/o SPDIF
 	  auto		auto-config reading BIOS (default)
 	  auto		auto-config reading BIOS (default)
 
 
+	ALC268
+	  3stack	3-stack model
+	  auto		auto-config reading BIOS (default)
+
+	ALC662
+	  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
+	  auto		auto-config reading BIOS (default)
+
 	ALC882/885
 	ALC882/885
 	  3stack-dig	3-jack with SPDIF I/O
 	  3stack-dig	3-jack with SPDIF I/O
 	  6stack-dig	6-jack digital with SPDIF I/O
 	  6stack-dig	6-jack digital with SPDIF I/O
 	  arima		Arima W820Di1
 	  arima		Arima W820Di1
 	  macpro	MacPro support
 	  macpro	MacPro support
+	  imac24	iMac 24'' with jack detection
 	  w2jc		ASUS W2JC
 	  w2jc		ASUS W2JC
 	  auto		auto-config reading BIOS (default)
 	  auto		auto-config reading BIOS (default)
 
 
@@ -832,9 +855,15 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 	  6stack-dig-demo  6-jack digital for Intel demo board
 	  6stack-dig-demo  6-jack digital for Intel demo board
 	  acer		Acer laptops (Travelmate 3012WTMi, Aspire 5600, etc)
 	  acer		Acer laptops (Travelmate 3012WTMi, Aspire 5600, etc)
 	  medion	Medion Laptops
 	  medion	Medion Laptops
+	  medion-md2	Medion MD2
 	  targa-dig	Targa/MSI
 	  targa-dig	Targa/MSI
 	  targa-2ch-dig	Targs/MSI with 2-channel
 	  targa-2ch-dig	Targs/MSI with 2-channel
 	  laptop-eapd   3-jack with SPDIF I/O and EAPD (Clevo M540JE, M550JE)
 	  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
+	  6stack-hp	HP machines with 6stack (Nettle boards)
+	  3stack-hp	HP machines with 3stack (Lucknow, Samba boards)
 	  auto		auto-config reading BIOS (default)
 	  auto		auto-config reading BIOS (default)
 
 
 	ALC861/660
 	ALC861/660
@@ -853,7 +882,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 	  3stack-dig	3-jack with SPDIF OUT
 	  3stack-dig	3-jack with SPDIF OUT
 	  6stack-dig	6-jack with SPDIF OUT
 	  6stack-dig	6-jack with SPDIF OUT
 	  3stack-660	3-jack (for ALC660VD)
 	  3stack-660	3-jack (for ALC660VD)
+	  3stack-660-digout 3-jack with SPDIF OUT (for ALC660VD)
 	  lenovo	Lenovo 3000 C200
 	  lenovo	Lenovo 3000 C200
+	  dallas	Dallas laptops
 	  auto		auto-config reading BIOS (default)
 	  auto		auto-config reading BIOS (default)
 
 
 	CMI9880
 	CMI9880
@@ -864,12 +895,26 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 	  allout	5-jack in back, 2-jack in front, SPDIF out
 	  allout	5-jack in back, 2-jack in front, SPDIF out
 	  auto		auto-config reading BIOS (default)
 	  auto		auto-config reading BIOS (default)
 
 
+	AD1882
+	  3stack	3-stack mode (default)
+	  6stack	6-stack mode
+
+	AD1884
+	  N/A
+
 	AD1981
 	AD1981
 	  basic		3-jack (default)
 	  basic		3-jack (default)
 	  hp		HP nx6320
 	  hp		HP nx6320
 	  thinkpad	Lenovo Thinkpad T60/X60/Z60
 	  thinkpad	Lenovo Thinkpad T60/X60/Z60
 	  toshiba	Toshiba U205
 	  toshiba	Toshiba U205
 
 
+	AD1983
+	  N/A
+
+	AD1984
+	  basic		default configuration
+	  thinkpad	Lenovo Thinkpad T61/X61
+
 	AD1986A
 	AD1986A
 	  6stack	6-jack, separate surrounds (default)
 	  6stack	6-jack, separate surrounds (default)
 	  3stack	3-stack, shared surrounds
 	  3stack	3-stack, shared surrounds
@@ -907,11 +952,18 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 	  ref		Reference board
 	  ref		Reference board
 	  3stack	D945 3stack
 	  3stack	D945 3stack
 	  5stack	D945 5stack + SPDIF
 	  5stack	D945 5stack + SPDIF
-	  macmini	Intel Mac Mini
-	  macbook	Intel Mac Book
-	  macbook-pro-v1 Intel Mac Book Pro 1st generation
-	  macbook-pro	Intel Mac Book Pro 2nd generation
-	  imac-intel	Intel iMac
+	  dell		Dell XPS M1210
+	  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
+	  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)
 
 
 	STAC9202/9250/9251
 	STAC9202/9250/9251
 	  ref		Reference board, base config
 	  ref		Reference board, base config
@@ -956,6 +1008,17 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     from the irq.  Remember this is a last resort, and should be
     from the irq.  Remember this is a last resort, and should be
     avoided as much as possible...
     avoided as much as possible...
     
     
+    MORE NOTES ON "azx_get_response timeout" PROBLEMS:
+    On some hardwares, you may need to add a proper probe_mask option
+    to avoid the "azx_get_response timeout" problem above, instead.
+    This occurs when the access to non-existing or non-working codec slot
+    (likely a modem one) causes a stall of the communication via HD-audio
+    bus.  You can see which codec slots are probed by enabling
+    CONFIG_SND_DEBUG_DETECT, or simply from the file name of the codec
+    proc files.  Then limit the slots to probe by probe_mask option.
+    For example, probe_mask=1 means to probe only the first slot, and
+    probe_mask=4 means only the third slot.
+
     The power-management is supported.
     The power-management is supported.
 
 
   Module snd-hdsp
   Module snd-hdsp

+ 162 - 80
Documentation/sound/alsa/Audiophile-Usb.txt

@@ -1,4 +1,4 @@
-	Guide to using M-Audio Audiophile USB with ALSA and Jack	v1.3
+	Guide to using M-Audio Audiophile USB with ALSA and Jack	v1.5
 	========================================================
 	========================================================
 
 
 	    Thibault Le Meur <Thibault.LeMeur@supelec.fr>
 	    Thibault Le Meur <Thibault.LeMeur@supelec.fr>
@@ -6,8 +6,19 @@
 This document is a guide to using the M-Audio Audiophile USB (tm) device with 
 This document is a guide to using the M-Audio Audiophile USB (tm) device with 
 ALSA and JACK.
 ALSA and JACK.
 
 
+History
+=======
+* v1.4 - Thibault Le Meur (2007-07-11)
+ - Added Low Endianness nature of 16bits-modes
+   found by Hakan Lennestal <Hakan.Lennestal@brfsodrahamn.se>
+ - Modifying document structure
+* v1.5 - Thibault Le Meur (2007-07-12)
+ - Added AC3/DTS passthru info
+
+
 1 - Audiophile USB Specs and correct usage
 1 - Audiophile USB Specs and correct usage
 ==========================================
 ==========================================
+
 This part is a reminder of important facts about the functions and limitations 
 This part is a reminder of important facts about the functions and limitations 
 of the device.
 of the device.
 
 
@@ -25,18 +36,18 @@ The device has 4 audio interfaces, and 2 MIDI ports:
 The internal DAC/ADC has the following characteristics:
 The internal DAC/ADC has the following characteristics:
 * sample depth of 16 or 24 bits
 * sample depth of 16 or 24 bits
 * sample rate from 8kHz to 96kHz
 * sample rate from 8kHz to 96kHz
-* Two ports can't use different sample depths at the same time. Moreover, the 
-Audiophile USB documentation gives the following Warning: "Please exit any 
-audio application running before switching between bit depths"
+* Two interfaces can't use different sample depths at the same time.
+Moreover, the Audiophile USB documentation gives the following Warning:
+"Please exit any audio application running before switching between bit depths"
 
 
 Due to the USB 1.1 bandwidth limitation, a limited number of interfaces can be 
 Due to the USB 1.1 bandwidth limitation, a limited number of interfaces can be 
 activated at the same time depending on the audio mode selected:
 activated at the same time depending on the audio mode selected:
- * 16-bit/48kHz ==> 4 channels in/4 channels out
+ * 16-bit/48kHz ==> 4 channels in + 4 channels out
    - Ai+Ao+Di+Do
    - Ai+Ao+Di+Do
- * 24-bit/48kHz ==> 4 channels in/2 channels out, 
-                    or 2 channels in/4 channels out
+ * 24-bit/48kHz ==> 4 channels in + 2 channels out, 
+                    or 2 channels in + 4 channels out
    - Ai+Ao+Do or Ai+Di+Ao or Ai+Di+Do or Di+Ao+Do
    - Ai+Ao+Do or Ai+Di+Ao or Ai+Di+Do or Di+Ao+Do
- * 24-bit/96kHz ==> 2 channels in, or 2 channels out (half duplex only)
+ * 24-bit/96kHz ==> 2 channels in _or_ 2 channels out (half duplex only)
    - Ai or Ao or Di or Do
    - Ai or Ao or Di or Do
 
 
 Important facts about the Digital interface:
 Important facts about the Digital interface:
@@ -52,44 +63,56 @@ source is connected
 synchronization error (for instance sound played at an odd sample rate)
 synchronization error (for instance sound played at an odd sample rate)
 
 
 
 
-2 - Audiophile USB support in ALSA
-==================================
+2 - Audiophile USB MIDI support in ALSA
+=======================================
 
 
-2.1 - MIDI ports
-----------------
-The Audiophile USB MIDI ports will be automatically supported once the 
+The Audiophile USB MIDI ports will be automatically supported once the
 following modules have been loaded:
 following modules have been loaded:
  * snd-usb-audio
  * snd-usb-audio
  * snd-seq-midi
  * snd-seq-midi
 
 
 No additional setting is required.
 No additional setting is required.
 
 
-2.2 - Audio ports
------------------
+
+3 - Audiophile USB Audio support in ALSA
+========================================
 
 
 Audio functions of the Audiophile USB device are handled by the snd-usb-audio 
 Audio functions of the Audiophile USB device are handled by the snd-usb-audio 
 module. This module can work in a default mode (without any device-specific 
 module. This module can work in a default mode (without any device-specific 
 parameter), or in an "advanced" mode with the device-specific parameter called 
 parameter), or in an "advanced" mode with the device-specific parameter called 
 "device_setup".
 "device_setup".
 
 
-2.2.1 - Default Alsa driver mode
-
-The default behavior of the snd-usb-audio driver is to parse the device 
-capabilities at startup and enable all functions inside the device (including 
-all ports at any supported sample rates and sample depths). This approach 
-has the advantage to let the driver easily switch from sample rates/depths 
-automatically according to the need of the application claiming the device.
-
-In this case the Audiophile ports are mapped to alsa pcm devices in the 
-following way (I suppose the device's index is 1):
+3.1 - Default Alsa driver mode
+------------------------------
+
+The default behavior of the snd-usb-audio driver is to list the device 
+capabilities at startup and activate the required mode when required 
+by the applications: for instance if the user is recording in a 
+24bit-depth-mode and immediately after wants to switch to a 16bit-depth mode,
+the snd-usb-audio module will reconfigure the device on the fly.
+
+This approach has the advantage to let the driver automatically switch from sample 
+rates/depths automatically according to the user's needs. However, those who 
+are using the device under windows know that this is not how the device is meant to
+work: under windows applications must be closed before using the m-audio control
+panel to switch the device working mode. Thus as we'll see in next section, this 
+Default Alsa driver mode can lead to device misconfigurations.
+
+Let's get back to the Default Alsa driver mode for now.  In this case the 
+Audiophile interfaces are mapped to alsa pcm devices in the following 
+way (I suppose the device's index is 1):
  * hw:1,0 is Ao in playback and Di in capture
  * hw:1,0 is Ao in playback and Di in capture
  * hw:1,1 is Do in playback and Ai in capture
  * hw:1,1 is Do in playback and Ai in capture
  * hw:1,2 is Do in AC3/DTS passthrough mode
  * hw:1,2 is Do in AC3/DTS passthrough mode
 
 
-You must note as well that the device uses Big Endian byte encoding so that 
-supported audio format are S16_BE  for 16-bit depth modes and S24_3BE for 
-24-bits depth mode. One exception is the hw:1,2 port which is Little Endian 
-compliant and thus uses S16_LE.
+In this mode, the device uses Big Endian byte-encoding so that 
+supported audio format are S16_BE for 16-bit depth modes and S24_3BE for 
+24-bits depth mode.
+
+One exception is the hw:1,2 port which was reported to be Little Endian 
+compliant (supposedly supporting S16_LE) but processes in fact only S16_BE streams.
+This has been fixed in kernel 2.6.23 and above and now the hw:1,2 interface 
+is reported to be big endian in this default driver mode.
 
 
 Examples:
 Examples:
  * playing a S24_3BE encoded raw file to the Ao port
  * playing a S24_3BE encoded raw file to the Ao port
@@ -98,22 +121,26 @@ Examples:
    % arecord -D hw:1,1 -c2  -t raw -r48000 -fS24_3BE test.raw
    % arecord -D hw:1,1 -c2  -t raw -r48000 -fS24_3BE test.raw
  * playing a S16_BE encoded raw file to the Do port
  * playing a S16_BE encoded raw file to the Do port
    % aplay -D hw:1,1 -c2 -t raw -r48000 -fS16_BE test.raw
    % aplay -D hw:1,1 -c2 -t raw -r48000 -fS16_BE test.raw
+ * playing an ac3 sample file to the Do port
+   % aplay -D hw:1,2 --channels=6 ac3_S16_BE_encoded_file.raw
 
 
-If you're happy with the default Alsa driver setup and don't experience any 
+If you're happy with the default Alsa driver mode and don't experience any 
 issue with this mode, then you can skip the following chapter.
 issue with this mode, then you can skip the following chapter.
 
 
-2.2.2 - Advanced module setup
+3.2 - Advanced module setup
+---------------------------
 
 
 Due to the hardware constraints described above, the device initialization made 
 Due to the hardware constraints described above, the device initialization made 
 by the Alsa driver in default mode may result in a corrupted state of the 
 by the Alsa driver in default mode may result in a corrupted state of the 
 device. For instance, a particularly annoying issue is that the sound captured 
 device. For instance, a particularly annoying issue is that the sound captured 
-from the Ai port sounds distorted (as if boosted with an excessive high volume 
-gain).
+from the Ai interface sounds distorted (as if boosted with an excessive high
+volume gain).
 
 
 For people having this problem, the snd-usb-audio module has a new module 
 For people having this problem, the snd-usb-audio module has a new module 
-parameter called "device_setup".
+parameter called "device_setup" (this parameter was introduced in kernel
+release 2.6.17)
 
 
-2.2.2.1 - Initializing the working mode of the Audiophile USB
+3.2.1 - Initializing the working mode of the Audiophile USB
 
 
 As far as the Audiophile USB device is concerned, this value let the user 
 As far as the Audiophile USB device is concerned, this value let the user 
 specify:
 specify:
@@ -121,33 +148,57 @@ specify:
  * the sample rate
  * the sample rate
  * whether the Di port is used or not 
  * whether the Di port is used or not 
 
 
-Here is a list of supported device_setup values for this device:
- * device_setup=0x00 (or omitted)
-   - Alsa driver default mode
-   - maintains backward compatibility with setups that do not use this 
-     parameter by not introducing any change
-   - results sometimes in corrupted sound as described earlier
+When initialized with "device_setup=0x00", the snd-usb-audio module has
+the same behaviour as when the parameter is omitted (see paragraph "Default 
+Alsa driver mode" above)
+
+Others modes are described in the following subsections.
+
+3.2.1.1 - 16-bit modes
+
+The two supported modes are:
+
  * device_setup=0x01
  * device_setup=0x01
    - 16bits 48kHz mode with Di disabled
    - 16bits 48kHz mode with Di disabled
    - Ai,Ao,Do can be used at the same time
    - Ai,Ao,Do can be used at the same time
    - hw:1,0 is not available in capture mode
    - hw:1,0 is not available in capture mode
    - hw:1,2 is not available
    - hw:1,2 is not available
+
  * device_setup=0x11
  * device_setup=0x11
    - 16bits 48kHz mode with Di enabled
    - 16bits 48kHz mode with Di enabled
    - Ai,Ao,Di,Do can be used at the same time
    - Ai,Ao,Di,Do can be used at the same time
    - hw:1,0 is available in capture mode
    - hw:1,0 is available in capture mode
    - hw:1,2 is not available
    - hw:1,2 is not available
+
+In this modes the device operates only at 16bits-modes. Before kernel 2.6.23,
+the devices where reported to be Big-Endian when in fact they were Little-Endian
+so that playing a file was a matter of using:
+   % aplay -D hw:1,1 -c2 -t raw -r48000 -fS16_BE test_S16_LE.raw
+where "test_S16_LE.raw" was in fact a little-endian sample file.
+
+Thanks to Hakan Lennestal (who discovered the Little-Endiannes of the device in
+these modes) a fix has been committed (expected in kernel 2.6.23) and
+Alsa now reports Little-Endian interfaces. Thus playing a file now is as simple as
+using:
+   % aplay -D hw:1,1 -c2 -t raw -r48000 -fS16_LE test_S16_LE.raw
+
+3.2.1.2 - 24-bit modes
+
+The three supported modes are:
+
  * device_setup=0x09
  * device_setup=0x09
    - 24bits 48kHz mode with Di disabled
    - 24bits 48kHz mode with Di disabled
    - Ai,Ao,Do can be used at the same time
    - Ai,Ao,Do can be used at the same time
    - hw:1,0 is not available in capture mode
    - hw:1,0 is not available in capture mode
    - hw:1,2 is not available
    - hw:1,2 is not available
+
  * device_setup=0x19
  * device_setup=0x19
    - 24bits 48kHz mode with Di enabled
    - 24bits 48kHz mode with Di enabled
    - 3 ports from {Ai,Ao,Di,Do} can be used at the same time
    - 3 ports from {Ai,Ao,Di,Do} can be used at the same time
    - hw:1,0 is available in capture mode and an active digital source must be 
    - hw:1,0 is available in capture mode and an active digital source must be 
      connected to Di
      connected to Di
    - hw:1,2 is not available
    - hw:1,2 is not available
+
  * device_setup=0x0D or 0x10
  * device_setup=0x0D or 0x10
    - 24bits 96kHz mode
    - 24bits 96kHz mode
    - Di is enabled by default for this mode but does not need to be connected 
    - Di is enabled by default for this mode but does not need to be connected 
@@ -155,34 +206,64 @@ Here is a list of supported device_setup values for this device:
    - Only 1 port from {Ai,Ao,Di,Do} can be used at the same time
    - Only 1 port from {Ai,Ao,Di,Do} can be used at the same time
    - hw:1,0 is available in captured mode
    - hw:1,0 is available in captured mode
    - hw:1,2 is not available
    - hw:1,2 is not available
+
+In these modes the device is only Big-Endian compliant (see "Default Alsa driver 
+mode" above for an aplay command example)
+
+3.2.1.3 - AC3 w/ DTS passthru mode
+
+Thanks to Hakan Lennestal, I now have a report saying that this mode works.
+
  * device_setup=0x03
  * device_setup=0x03
    - 16bits 48kHz mode with only the Do port enabled 
    - 16bits 48kHz mode with only the Do port enabled 
-   - AC3 with DTS passthru (not tested)
+   - AC3 with DTS passthru
    - Caution with this setup the Do port is mapped to the pcm device hw:1,0
    - Caution with this setup the Do port is mapped to the pcm device hw:1,0
 
 
-2.2.2.2 - Setting and switching configurations with the device_setup parameter
+The command line used to playback the AC3/DTS encoded .wav-files in this mode:
+   % aplay -D hw:1,0 --channels=6 ac3_S16_LE_encoded_file.raw
+
+3.2.2 - How to use the device_setup parameter
+----------------------------------------------
 
 
 The parameter can be given:
 The parameter can be given:
+
  * By manually probing the device (as root):
  * By manually probing the device (as root):
    # modprobe -r snd-usb-audio
    # modprobe -r snd-usb-audio
    # modprobe snd-usb-audio index=1 device_setup=0x09
    # modprobe snd-usb-audio index=1 device_setup=0x09
+
  * Or while configuring the modules options in your modules configuration file
  * Or while configuring the modules options in your modules configuration file
    - For Fedora distributions, edit the /etc/modprobe.conf file:
    - For Fedora distributions, edit the /etc/modprobe.conf file:
        alias snd-card-1 snd-usb-audio
        alias snd-card-1 snd-usb-audio
        options snd-usb-audio index=1 device_setup=0x09
        options snd-usb-audio index=1 device_setup=0x09
 
 
-IMPORTANT NOTE WHEN SWITCHING CONFIGURATION:
--------------------------------------------
- * You may need to _first_ initialize the module with the correct device_setup 
-   parameter and _only_after_ turn on the Audiophile USB device
- * This is especially true when switching the sample depth:
+CAUTION when initializaing the device
+-------------------------------------
+
+ * Correct initialization on the device requires that device_setup is given to
+   the module BEFORE the device is turned on. So, if you use the "manual probing"
+   method described above, take care to power-on the device AFTER this initialization.
+
+ * Failing to respect this will lead in a misconfiguration of the device. In this case
+   turn off the device, unproble the snd-usb-audio module, then probe it again with 
+   correct device_setup parameter and then (and only then) turn on the device again.
+
+ * If you've correctly initialized the device in a valid mode and then want to switch
+   to  another mode (possibly with another sample-depth), please use also the following 
+   procedure:
    - first turn off the device
    - first turn off the device
    - de-register the snd-usb-audio module (modprobe -r)
    - de-register the snd-usb-audio module (modprobe -r)
    - change the device_setup parameter by changing the device_setup
    - change the device_setup parameter by changing the device_setup
      option in /etc/modprobe.conf 
      option in /etc/modprobe.conf 
    - turn on the device
    - turn on the device
+ * A workaround for this last issue has been applied to kernel 2.6.23, but it may not
+   be enough to ensure the 'stability' of the device initialization.
 
 
-2.2.2.3 - Audiophile USB's device_setup structure
+3.2.3 - Technical details for hackers
+-------------------------------------
+This section is for hackers, wanting to understand details about the device
+internals and how Alsa supports it.
+
+3.2.3.1 - Audiophile USB's device_setup structure
 
 
 If you want to understand the device_setup magic numbers for the Audiophile 
 If you want to understand the device_setup magic numbers for the Audiophile 
 USB, you need some very basic understanding of binary computation. However, 
 USB, you need some very basic understanding of binary computation. However, 
@@ -228,12 +309,12 @@ Caution:
    - choosing b2 will prepare all interfaces for 24bits/96kHz but you'll
    - choosing b2 will prepare all interfaces for 24bits/96kHz but you'll
      only be able to use one at the same time
      only be able to use one at the same time
 
 
-2.2.3 -  USB implementation details for this device
+3.2.3.2 -  USB implementation details for this device
 
 
 You may safely skip this section if you're not interested in driver 
 You may safely skip this section if you're not interested in driver 
-development.
+hacking.
 
 
-This section describes some internal aspects of the device and summarize the 
+This section describes some internal aspects of the device and summarizes the 
 data I got by usb-snooping the windows and Linux drivers.
 data I got by usb-snooping the windows and Linux drivers.
 
 
 The M-Audio Audiophile USB has 7 USB Interfaces:
 The M-Audio Audiophile USB has 7 USB Interfaces:
@@ -293,43 +374,45 @@ parse_audio_endpoints function uses a quirk called
 "audiophile_skip_setting_quirk" in order to prevent AltSettings not 
 "audiophile_skip_setting_quirk" in order to prevent AltSettings not 
 corresponding to device_setup from being registered in the driver.
 corresponding to device_setup from being registered in the driver.
 
 
-3 - Audiophile USB and Jack support
+4 - Audiophile USB and Jack support
 ===================================
 ===================================
 
 
 This section deals with support of the Audiophile USB device in Jack.
 This section deals with support of the Audiophile USB device in Jack.
-The main issue regarding this support is that the device is Big Endian 
-compliant.
 
 
-3.1 - Using the plug alsa plugin
---------------------------------
+There are 2 main potential issues when using Jackd with the device:
+* support for Big-Endian devices in 24-bit modes
+* support for 4-in / 4-out channels
+
+4.1 - Direct support in Jackd
+-----------------------------
 
 
-Jack doesn't directly support big endian devices. Thus, one way to have support 
-for this device with Alsa is to use the Alsa "plug" converter.
+Jack supports big endian devices only in recent versions (thanks to
+Andreas Steinmetz for his first big-endian patch). I can't remember 
+extacly when this support was released into jackd, let's just say that 
+with jackd version 0.103.0 it's almost ok (just a small bug is affecting 
+16bits Big-Endian devices, but since you've read  carefully the above 
+paragraphs, you're now using kernel >= 2.6.23 and your 16bits devices 
+are now Little Endians ;-) ).
+
+You can run jackd with the following command for playback with Ao and
+record with Ai:
+  % jackd -R -dalsa -Phw:1,0 -r48000 -p128 -n2 -D -Chw:1,1
+
+4.2 - Using Alsa plughw
+-----------------------
+If you don't have a recent Jackd installed, you can downgrade to using
+the Alsa "plug" converter.
 
 
 For instance here is one way to run Jack with 2 playback channels on Ao and 2 
 For instance here is one way to run Jack with 2 playback channels on Ao and 2 
 capture channels from Ai:
 capture channels from Ai:
   % jackd -R -dalsa -dplughw:1 -r48000 -p256 -n2 -D -Cplughw:1,1
   % jackd -R -dalsa -dplughw:1 -r48000 -p256 -n2 -D -Cplughw:1,1
 
 
-
 However you may see the following warning message:
 However you may see the following warning message:
 "You appear to be using the ALSA software "plug" layer, probably a result of 
 "You appear to be using the ALSA software "plug" layer, probably a result of 
 using the "default" ALSA device. This is less efficient than it could be. 
 using the "default" ALSA device. This is less efficient than it could be. 
 Consider using a hardware device instead rather than using the plug layer."
 Consider using a hardware device instead rather than using the plug layer."
 
 
-3.2 - Patching alsa to use direct pcm device
---------------------------------------------
-A patch for Jack by Andreas Steinmetz adds support for Big Endian devices. 
-However it has not been included in the CVS tree.
-
-You can find it at the following URL:
-http://sourceforge.net/tracker/index.php?func=detail&aid=1289682&group_id=39687&
-atid=425939
-
-After having applied the patch you can run jackd with the following command 
-line:
-  % jackd -R -dalsa -Phw:1,0 -r48000 -p128 -n2 -D -Chw:1,1
-
-3.2 - Getting 2 input and/or output interfaces in Jack
+4.3 - Getting 2 input and/or output interfaces in Jack
 ------------------------------------------------------
 ------------------------------------------------------
 
 
 As you can see, starting the Jack server this way will only enable 1 stereo
 As you can see, starting the Jack server this way will only enable 1 stereo
@@ -339,6 +422,7 @@ This is due to the following restrictions:
 * Jack can only open one capture device and one playback device at a time
 * Jack can only open one capture device and one playback device at a time
 * The Audiophile USB is seen as 2 (or three) Alsa devices: hw:1,0, hw:1,1
 * The Audiophile USB is seen as 2 (or three) Alsa devices: hw:1,0, hw:1,1
   (and optionally hw:1,2)
   (and optionally hw:1,2)
+
 If you want to get Ai+Di and/or Ao+Do support with Jack, you would need to
 If you want to get Ai+Di and/or Ao+Do support with Jack, you would need to
 combine the Alsa devices into one logical "complex" device.
 combine the Alsa devices into one logical "complex" device.
 
 
@@ -348,13 +432,11 @@ It is related to another device (ice1712) but can be adapted to suit
 the Audiophile USB.
 the Audiophile USB.
 
 
 Enabling multiple Audiophile USB interfaces for Jackd will certainly require:
 Enabling multiple Audiophile USB interfaces for Jackd will certainly require:
-* patching Jack with the previously mentioned "Big Endian" patch
-* patching Jackd with the MMAP_COMPLEX patch (see the ice1712 page)
-* patching the alsa-lib/src/pcm/pcm_multi.c file (see the ice1712 page)
+* Making sure your Jackd version has the MMAP_COMPLEX patch (see the ice1712 page)
+* (maybe) patching the alsa-lib/src/pcm/pcm_multi.c file (see the ice1712 page)
 * define a multi device (combination of hw:1,0 and hw:1,1) in your .asoundrc
 * define a multi device (combination of hw:1,0 and hw:1,1) in your .asoundrc
   file 
   file 
 * start jackd with this device
 * start jackd with this device
 
 
-I had no success in testing this for now, but this may be due to my OS
-configuration. If you have any success with this kind of setup, please
-drop me an email.
+I had no success in testing this for now, if you have any success with this kind 
+of setup, please drop me an email.

+ 15 - 0
Documentation/sound/alsa/OSS-Emulation.txt

@@ -278,6 +278,21 @@ current mixer configuration by reading and writing the whole file
 image.
 image.
 
 
 
 
+Duplex Streams
+==============
+
+Note that when attempting to use a single device file for playback and
+capture, the OSS API provides no way to set the format, sample rate or
+number of channels different in each direction.  Thus
+	io_handle = open("device", O_RDWR)
+will only function correctly if the values are the same in each direction.
+
+To use different values in the two directions, use both
+	input_handle = open("device", O_RDONLY)
+	output_handle = open("device", O_WRONLY)
+and set the values for the corresponding handle.
+
+
 Unsupported Features
 Unsupported Features
 ====================
 ====================
 
 

+ 4 - 3
include/linux/i2c-id.h

@@ -115,9 +115,10 @@
 #define I2C_DRIVERID_KS0127	86	/* Samsung ks0127 video decoder */
 #define I2C_DRIVERID_KS0127	86	/* Samsung ks0127 video decoder */
 #define I2C_DRIVERID_TLV320AIC23B 87	/* TI TLV320AIC23B audio codec  */
 #define I2C_DRIVERID_TLV320AIC23B 87	/* TI TLV320AIC23B audio codec  */
 #define I2C_DRIVERID_ISL1208	88	/* Intersil ISL1208 RTC		*/
 #define I2C_DRIVERID_ISL1208	88	/* Intersil ISL1208 RTC		*/
-#define I2C_DRIVERID_WM8731		89	/* Wolfson WM8731 audio codec */
-#define I2C_DRIVERID_WM8750		90	/* Wolfson WM8750 audio codec */
-#define I2C_DRIVERID_WM8753		91	/* Wolfson WM8753 audio codec */
+#define I2C_DRIVERID_WM8731	89	/* Wolfson WM8731 audio codec */
+#define I2C_DRIVERID_WM8750	90	/* Wolfson WM8750 audio codec */
+#define I2C_DRIVERID_WM8753	91	/* Wolfson WM8753 audio codec */
+#define I2C_DRIVERID_LM4857 	92 	/* LM4857 Audio Amplifier */
 
 
 #define I2C_DRIVERID_I2CDEV	900
 #define I2C_DRIVERID_I2CDEV	900
 #define I2C_DRIVERID_ARP        902    /* SMBus ARP Client              */
 #define I2C_DRIVERID_ARP        902    /* SMBus ARP Client              */

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

@@ -43,6 +43,7 @@ struct snd_ak4xxx_ops {
 struct snd_akm4xxx_dac_channel {
 struct snd_akm4xxx_dac_channel {
 	char *name;		/* mixer volume name */
 	char *name;		/* mixer volume name */
 	unsigned int num_channels;
 	unsigned int num_channels;
+	char *switch_name;		/* mixer switch*/
 };
 };
 
 
 /* ADC labels and channels */
 /* ADC labels and channels */

+ 4 - 0
include/sound/cs46xx.h

@@ -1723,6 +1723,10 @@ struct snd_cs46xx {
 	struct snd_cs46xx_pcm *playback_pcm;
 	struct snd_cs46xx_pcm *playback_pcm;
 	unsigned int play_ctl;
 	unsigned int play_ctl;
 #endif
 #endif
+
+#ifdef CONFIG_PM
+	u32 *saved_regs;
+#endif
 };
 };
 
 
 int snd_cs46xx_create(struct snd_card *card,
 int snd_cs46xx_create(struct snd_card *card,

+ 2 - 0
include/sound/cs46xx_dsp_spos.h

@@ -107,6 +107,7 @@ struct dsp_scb_descriptor {
 	char scb_name[DSP_MAX_SCB_NAME];
 	char scb_name[DSP_MAX_SCB_NAME];
 	u32 address;
 	u32 address;
 	int index;
 	int index;
+	u32 *data;
 
 
 	struct dsp_scb_descriptor * sub_list_ptr;
 	struct dsp_scb_descriptor * sub_list_ptr;
 	struct dsp_scb_descriptor * next_scb_ptr;
 	struct dsp_scb_descriptor * next_scb_ptr;
@@ -127,6 +128,7 @@ struct dsp_task_descriptor {
 	int size;
 	int size;
 	u32 address;
 	u32 address;
 	int index;
 	int index;
+	u32 *data;
 };
 };
 
 
 struct dsp_pcm_channel_descriptor {
 struct dsp_pcm_channel_descriptor {

+ 16 - 0
include/sound/emu10k1.h

@@ -1120,6 +1120,16 @@
 /************************************************************************************************/
 /************************************************************************************************/
 /* EMU1010m HANA Destinations									*/
 /* EMU1010m HANA Destinations									*/
 /************************************************************************************************/
 /************************************************************************************************/
+/* 32-bit destinations of signal in the Hana FPGA. Destinations are either
+ * physical outputs of Hana, or outputs going to Alice2 (audigy) for capture
+ * - 16 x EMU_DST_ALICE2_EMU32_X.
+ */
+/* EMU32 = 32-bit serial channel between Alice2 (audigy) and Hana (FPGA) */
+/* EMU_DST_ALICE2_EMU32_X - data channels from Hana to Alice2 used for capture.
+ * Which data is fed into a EMU_DST_ALICE2_EMU32_X channel in Hana depends on
+ * setup of mixer control for each destination - see emumixer.c -
+ * snd_emu1010_output_enum_ctls[], snd_emu1010_input_enum_ctls[]
+ */
 #define EMU_DST_ALICE2_EMU32_0	0x000f	/* 16 EMU32 channels to Alice2 +0 to +0xf */
 #define EMU_DST_ALICE2_EMU32_0	0x000f	/* 16 EMU32 channels to Alice2 +0 to +0xf */
 #define EMU_DST_ALICE2_EMU32_1	0x0000	/* 16 EMU32 channels to Alice2 +0 to +0xf */
 #define EMU_DST_ALICE2_EMU32_1	0x0000	/* 16 EMU32 channels to Alice2 +0 to +0xf */
 #define EMU_DST_ALICE2_EMU32_2	0x0001	/* 16 EMU32 channels to Alice2 +0 to +0xf */
 #define EMU_DST_ALICE2_EMU32_2	0x0001	/* 16 EMU32 channels to Alice2 +0 to +0xf */
@@ -1199,6 +1209,12 @@
 /************************************************************************************************/
 /************************************************************************************************/
 /* EMU1010m HANA Sources									*/
 /* EMU1010m HANA Sources									*/
 /************************************************************************************************/
 /************************************************************************************************/
+/* 32-bit sources of signal in the Hana FPGA. The sources are routed to
+ * destinations using mixer control for each destination - see emumixer.c
+ * Sources are either physical inputs of FPGA,
+ * or outputs from Alice (audigy) - 16 x EMU_SRC_ALICE_EMU32A +
+ * 16 x EMU_SRC_ALICE_EMU32B
+ */
 #define EMU_SRC_SILENCE		0x0000	/* Silence */
 #define EMU_SRC_SILENCE		0x0000	/* Silence */
 #define EMU_SRC_DOCK_MIC_A1	0x0100	/* Audio Dock Mic A, 1st or 48kHz only */
 #define EMU_SRC_DOCK_MIC_A1	0x0100	/* Audio Dock Mic A, 1st or 48kHz only */
 #define EMU_SRC_DOCK_MIC_A2	0x0101	/* Audio Dock Mic A, 2nd or 96kHz */
 #define EMU_SRC_DOCK_MIC_A2	0x0101	/* Audio Dock Mic A, 2nd or 96kHz */

+ 1 - 0
include/sound/sb.h

@@ -38,6 +38,7 @@ enum sb_hw_type {
 	SB_HW_ALS100,		/* Avance Logic ALS100 chip */
 	SB_HW_ALS100,		/* Avance Logic ALS100 chip */
 	SB_HW_ALS4000,		/* Avance Logic ALS4000 chip */
 	SB_HW_ALS4000,		/* Avance Logic ALS4000 chip */
 	SB_HW_DT019X,		/* Diamond Tech. DT-019X / Avance Logic ALS-007 */
 	SB_HW_DT019X,		/* Diamond Tech. DT-019X / Avance Logic ALS-007 */
+	SB_HW_CS5530,		/* Cyrix/NatSemi 5530 VSA1 */
 };
 };
 
 
 #define SB_OPEN_PCM			0x01
 #define SB_OPEN_PCM			0x01

+ 1 - 1
include/sound/version.h

@@ -1,3 +1,3 @@
 /* include/version.h.  Generated by alsa/ksync script.  */
 /* include/version.h.  Generated by alsa/ksync script.  */
 #define CONFIG_SND_VERSION "1.0.14"
 #define CONFIG_SND_VERSION "1.0.14"
-#define CONFIG_SND_DATE " (Thu May 31 09:03:25 2007 UTC)"
+#define CONFIG_SND_DATE " (Fri Jul 20 09:12:58 2007 UTC)"

+ 0 - 9
include/sound/wavefront_fx.h

@@ -1,9 +0,0 @@
-#ifndef __SOUND_WAVEFRONT_FX_H
-#define __SOUND_WAVEFRONT_FX_H
-
-extern int  snd_wavefront_fx_detect (snd_wavefront_t *);
-extern void snd_wavefront_fx_ioctl  (snd_synth_t *sdev, 
-				     unsigned int cmd, 
-				     unsigned long arg);
-
-#endif  __SOUND_WAVEFRONT_FX_H

+ 2 - 0
sound/Kconfig

@@ -65,6 +65,8 @@ source "sound/arm/Kconfig"
 
 
 source "sound/mips/Kconfig"
 source "sound/mips/Kconfig"
 
 
+source "sound/sh/Kconfig"
+
 # the following will depend on the order of config.
 # the following will depend on the order of config.
 # here assuming USB is defined before ALSA
 # here assuming USB is defined before ALSA
 source "sound/usb/Kconfig"
 source "sound/usb/Kconfig"

+ 1 - 1
sound/Makefile

@@ -5,7 +5,7 @@ obj-$(CONFIG_SOUND) += soundcore.o
 obj-$(CONFIG_SOUND_PRIME) += sound_firmware.o
 obj-$(CONFIG_SOUND_PRIME) += sound_firmware.o
 obj-$(CONFIG_SOUND_PRIME) += oss/
 obj-$(CONFIG_SOUND_PRIME) += oss/
 obj-$(CONFIG_DMASOUND) += oss/
 obj-$(CONFIG_DMASOUND) += oss/
-obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/ soc/
+obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/ soc/
 obj-$(CONFIG_SND_AOA) += aoa/
 obj-$(CONFIG_SND_AOA) += aoa/
 
 
 # This one must be compilable even if sound is configured out
 # This one must be compilable even if sound is configured out

+ 2 - 2
sound/aoa/codecs/snd-aoa-codec-onyx.c

@@ -661,7 +661,7 @@ static struct transfer_info onyx_transfers[] = {
 		.tag = 2,
 		.tag = 2,
 	},
 	},
 #ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE
 #ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE
-Once alsa gets supports for this kind of thing we can add it...
+	/* Once alsa gets supports for this kind of thing we can add it... */
 	{
 	{
 		/* digital compressed output */
 		/* digital compressed output */
 		.formats =  SNDRV_PCM_FMTBIT_COMPRESSED_16BE,
 		.formats =  SNDRV_PCM_FMTBIT_COMPRESSED_16BE,
@@ -713,7 +713,7 @@ static int onyx_prepare(struct codec_info_item *cii,
 	if (substream->runtime->format == SNDRV_PCM_FMTBIT_COMPRESSED_16BE) {
 	if (substream->runtime->format == SNDRV_PCM_FMTBIT_COMPRESSED_16BE) {
 		/* mute and lock analog output */
 		/* mute and lock analog output */
 		onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v);
 		onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v);
-		if (onyx_write_register(onyx
+		if (onyx_write_register(onyx,
 					ONYX_REG_DAC_CONTROL,
 					ONYX_REG_DAC_CONTROL,
 					v | ONYX_MUTE_RIGHT | ONYX_MUTE_LEFT))
 					v | ONYX_MUTE_RIGHT | ONYX_MUTE_LEFT))
 			goto out_unlock;
 			goto out_unlock;

+ 1 - 1
sound/core/pcm_native.c

@@ -1487,7 +1487,7 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream)
 
 
 	snd_pcm_stream_lock_irq(substream);
 	snd_pcm_stream_lock_irq(substream);
 	/* resume pause */
 	/* resume pause */
-	if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
+	if (substream->runtime->status->state == SNDRV_PCM_STATE_PAUSED)
 		snd_pcm_pause(substream, 0);
 		snd_pcm_pause(substream, 0);
 
 
 	/* pre-start/stop - all running streams are changed to DRAINING state */
 	/* pre-start/stop - all running streams are changed to DRAINING state */

+ 3 - 3
sound/core/seq/seq_instr.c

@@ -109,7 +109,7 @@ void snd_seq_instr_list_free(struct snd_seq_kinstr_list **list_ptr)
 			spin_lock_irqsave(&list->lock, flags);
 			spin_lock_irqsave(&list->lock, flags);
 			while (instr->use) {
 			while (instr->use) {
 				spin_unlock_irqrestore(&list->lock, flags);
 				spin_unlock_irqrestore(&list->lock, flags);
-				schedule_timeout_interruptible(1);
+				schedule_timeout(1);
 				spin_lock_irqsave(&list->lock, flags);
 				spin_lock_irqsave(&list->lock, flags);
 			}				
 			}				
 			spin_unlock_irqrestore(&list->lock, flags);
 			spin_unlock_irqrestore(&list->lock, flags);
@@ -199,7 +199,7 @@ int snd_seq_instr_list_free_cond(struct snd_seq_kinstr_list *list,
 			instr = flist;
 			instr = flist;
 			flist = instr->next;
 			flist = instr->next;
 			while (instr->use)
 			while (instr->use)
-				schedule_timeout_interruptible(1);
+				schedule_timeout(1);
 			if (snd_seq_instr_free(instr, atomic)<0)
 			if (snd_seq_instr_free(instr, atomic)<0)
 				snd_printk(KERN_WARNING "instrument free problem\n");
 				snd_printk(KERN_WARNING "instrument free problem\n");
 			instr = next;
 			instr = next;
@@ -555,7 +555,7 @@ static int instr_free(struct snd_seq_kinstr_ops *ops,
 					   SNDRV_SEQ_INSTR_NOTIFY_REMOVE);
 					   SNDRV_SEQ_INSTR_NOTIFY_REMOVE);
 		while (instr->use) {
 		while (instr->use) {
 			spin_unlock_irqrestore(&list->lock, flags);
 			spin_unlock_irqrestore(&list->lock, flags);
-			schedule_timeout_interruptible(1);
+			schedule_timeout(1);
 			spin_lock_irqsave(&list->lock, flags);
 			spin_lock_irqsave(&list->lock, flags);
 		}				
 		}				
 		spin_unlock_irqrestore(&list->lock, flags);
 		spin_unlock_irqrestore(&list->lock, flags);

+ 18 - 9
sound/core/timer.c

@@ -1549,9 +1549,11 @@ static int snd_timer_user_info(struct file *file,
 	int err = 0;
 	int err = 0;
 
 
 	tu = file->private_data;
 	tu = file->private_data;
-	snd_assert(tu->timeri != NULL, return -ENXIO);
+	if (!tu->timeri)
+		return -EBADFD;
 	t = tu->timeri->timer;
 	t = tu->timeri->timer;
-	snd_assert(t != NULL, return -ENXIO);
+	if (!t)
+		return -EBADFD;
 
 
 	info = kzalloc(sizeof(*info), GFP_KERNEL);
 	info = kzalloc(sizeof(*info), GFP_KERNEL);
 	if (! info)
 	if (! info)
@@ -1579,9 +1581,11 @@ static int snd_timer_user_params(struct file *file,
 	int err;
 	int err;
 
 
 	tu = file->private_data;
 	tu = file->private_data;
-	snd_assert(tu->timeri != NULL, return -ENXIO);
+	if (!tu->timeri)
+		return -EBADFD;
 	t = tu->timeri->timer;
 	t = tu->timeri->timer;
-	snd_assert(t != NULL, return -ENXIO);
+	if (!t)
+		return -EBADFD;
 	if (copy_from_user(&params, _params, sizeof(params)))
 	if (copy_from_user(&params, _params, sizeof(params)))
 		return -EFAULT;
 		return -EFAULT;
 	if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE) && params.ticks < 1) {
 	if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE) && params.ticks < 1) {
@@ -1675,7 +1679,8 @@ static int snd_timer_user_status(struct file *file,
 	struct snd_timer_status status;
 	struct snd_timer_status status;
 
 
 	tu = file->private_data;
 	tu = file->private_data;
-	snd_assert(tu->timeri != NULL, return -ENXIO);
+	if (!tu->timeri)
+		return -EBADFD;
 	memset(&status, 0, sizeof(status));
 	memset(&status, 0, sizeof(status));
 	status.tstamp = tu->tstamp;
 	status.tstamp = tu->tstamp;
 	status.resolution = snd_timer_resolution(tu->timeri);
 	status.resolution = snd_timer_resolution(tu->timeri);
@@ -1695,7 +1700,8 @@ static int snd_timer_user_start(struct file *file)
 	struct snd_timer_user *tu;
 	struct snd_timer_user *tu;
 
 
 	tu = file->private_data;
 	tu = file->private_data;
-	snd_assert(tu->timeri != NULL, return -ENXIO);
+	if (!tu->timeri)
+		return -EBADFD;
 	snd_timer_stop(tu->timeri);
 	snd_timer_stop(tu->timeri);
 	tu->timeri->lost = 0;
 	tu->timeri->lost = 0;
 	tu->last_resolution = 0;
 	tu->last_resolution = 0;
@@ -1708,7 +1714,8 @@ static int snd_timer_user_stop(struct file *file)
 	struct snd_timer_user *tu;
 	struct snd_timer_user *tu;
 
 
 	tu = file->private_data;
 	tu = file->private_data;
-	snd_assert(tu->timeri != NULL, return -ENXIO);
+	if (!tu->timeri)
+		return -EBADFD;
 	return (err = snd_timer_stop(tu->timeri)) < 0 ? err : 0;
 	return (err = snd_timer_stop(tu->timeri)) < 0 ? err : 0;
 }
 }
 
 
@@ -1718,7 +1725,8 @@ static int snd_timer_user_continue(struct file *file)
 	struct snd_timer_user *tu;
 	struct snd_timer_user *tu;
 
 
 	tu = file->private_data;
 	tu = file->private_data;
-	snd_assert(tu->timeri != NULL, return -ENXIO);
+	if (!tu->timeri)
+		return -EBADFD;
 	tu->timeri->lost = 0;
 	tu->timeri->lost = 0;
 	return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0;
 	return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0;
 }
 }
@@ -1729,7 +1737,8 @@ static int snd_timer_user_pause(struct file *file)
 	struct snd_timer_user *tu;
 	struct snd_timer_user *tu;
 
 
 	tu = file->private_data;
 	tu = file->private_data;
-	snd_assert(tu->timeri != NULL, return -ENXIO);
+	if (!tu->timeri)
+		return -EBADFD;
 	return (err = snd_timer_pause(tu->timeri)) < 0 ? err : 0;
 	return (err = snd_timer_pause(tu->timeri)) < 0 ? err : 0;
 }
 }
 
 

+ 1 - 1
sound/drivers/dummy.c

@@ -659,7 +659,7 @@ static struct platform_driver snd_dummy_driver = {
 	},
 	},
 };
 };
 
 
-static void __init_or_module snd_dummy_unregister_all(void)
+static void snd_dummy_unregister_all(void)
 {
 {
 	int i;
 	int i;
 
 

+ 1 - 1
sound/drivers/mpu401/mpu401.c

@@ -228,7 +228,7 @@ static struct pnp_driver snd_mpu401_pnp_driver = {
 static struct pnp_driver snd_mpu401_pnp_driver;
 static struct pnp_driver snd_mpu401_pnp_driver;
 #endif
 #endif
 
 
-static void __init_or_module snd_mpu401_unregister_all(void)
+static void snd_mpu401_unregister_all(void)
 {
 {
 	int i;
 	int i;
 
 

+ 1 - 1
sound/drivers/portman2x4.c

@@ -833,7 +833,7 @@ static struct platform_driver snd_portman_driver = {
 /*********************************************************************
 /*********************************************************************
  * module init stuff
  * module init stuff
  *********************************************************************/
  *********************************************************************/
-static void __init_or_module snd_portman_unregister_all(void)
+static void snd_portman_unregister_all(void)
 {
 {
 	int i;
 	int i;
 
 

+ 1 - 1
sound/drivers/serial-u16550.c

@@ -998,7 +998,7 @@ static struct platform_driver snd_serial_driver = {
 	},
 	},
 };
 };
 
 
-static void __init_or_module snd_serial_unregister_all(void)
+static void snd_serial_unregister_all(void)
 {
 {
 	int i;
 	int i;
 
 

+ 1 - 1
sound/drivers/virmidi.c

@@ -145,7 +145,7 @@ static struct platform_driver snd_virmidi_driver = {
 	},
 	},
 };
 };
 
 
-static void __init_or_module snd_virmidi_unregister_all(void)
+static void snd_virmidi_unregister_all(void)
 {
 {
 	int i;
 	int i;
 
 

+ 22 - 2
sound/i2c/other/ak4xxx-adda.c

@@ -481,8 +481,8 @@ static int ak4xxx_switch_get(struct snd_kcontrol *kcontrol,
 	int addr = AK_GET_ADDR(kcontrol->private_value);
 	int addr = AK_GET_ADDR(kcontrol->private_value);
 	int shift = AK_GET_SHIFT(kcontrol->private_value);
 	int shift = AK_GET_SHIFT(kcontrol->private_value);
 	int invert = AK_GET_INVERT(kcontrol->private_value);
 	int invert = AK_GET_INVERT(kcontrol->private_value);
-	unsigned char val = snd_akm4xxx_get(ak, chip, addr);
-
+	/* we observe the (1<<shift) bit only */
+	unsigned char val = snd_akm4xxx_get(ak, chip, addr) & (1<<shift);
 	if (invert)
 	if (invert)
 		val = ! val;
 		val = ! val;
 	ucontrol->value.integer.value[0] = (val & (1<<shift)) != 0;
 	ucontrol->value.integer.value[0] = (val & (1<<shift)) != 0;
@@ -585,6 +585,26 @@ static int build_dac_controls(struct snd_akm4xxx *ak)
 
 
 	mixer_ch = 0;
 	mixer_ch = 0;
 	for (idx = 0; idx < ak->num_dacs; ) {
 	for (idx = 0; idx < ak->num_dacs; ) {
+		/* mute control for Revolution 7.1 - AK4381 */
+		if (ak->type == SND_AK4381 
+				&&  ak->dac_info[mixer_ch].switch_name) {
+			memset(&knew, 0, sizeof(knew));
+			knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+			knew.count = 1;
+			knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+			knew.name = ak->dac_info[mixer_ch].switch_name;
+			knew.info = ak4xxx_switch_info;
+			knew.get = ak4xxx_switch_get;
+			knew.put = ak4xxx_switch_put;
+			knew.access = 0;
+			/* register 1, bit 0 (SMUTE): 0 = normal operation,
+			   1 = mute */
+			knew.private_value =
+				AK_COMPOSE(idx/2, 1, 0, 0) | AK_INVERT;
+			err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
+			if (err < 0)
+				return err;
+		}
 		memset(&knew, 0, sizeof(knew));
 		memset(&knew, 0, sizeof(knew));
 		if (! ak->dac_info || ! ak->dac_info[mixer_ch].name) {
 		if (! ak->dac_info || ! ak->dac_info[mixer_ch].name) {
 			knew.name = "DAC Volume";
 			knew.name = "DAC Volume";

+ 23 - 9
sound/isa/Kconfig

@@ -1,8 +1,5 @@
 # ALSA ISA drivers
 # ALSA ISA drivers
 
 
-menu "ISA devices"
-	depends on SND!=n && ISA && ISA_DMA_API
-
 config SND_AD1848_LIB
 config SND_AD1848_LIB
         tristate
         tristate
         select SND_PCM
         select SND_PCM
@@ -11,6 +8,22 @@ config SND_CS4231_LIB
         tristate
         tristate
         select SND_PCM
         select SND_PCM
 
 
+config SND_SB_COMMON
+        tristate
+
+config SND_SB8_DSP
+        tristate
+        select SND_PCM
+        select SND_SB_COMMON
+
+config SND_SB16_DSP
+        tristate
+        select SND_PCM
+        select SND_SB_COMMON
+
+menu "ISA devices"
+	depends on SND!=n && ISA && ISA_DMA_API
+
 config SND_ADLIB
 config SND_ADLIB
 	tristate "AdLib FM card"
 	tristate "AdLib FM card"
 	depends on SND
 	depends on SND
@@ -55,7 +68,7 @@ config SND_ALS100
 	select ISAPNP
 	select ISAPNP
 	select SND_OPL3_LIB
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_MPU401_UART
-	select SND_PCM
+	select SND_SB16_DSP
 	help
 	help
 	  Say Y here to include support for soundcards based on Avance
 	  Say Y here to include support for soundcards based on Avance
 	  Logic ALS100, ALS110, ALS120 and ALS200 chips.
 	  Logic ALS100, ALS110, ALS120 and ALS200 chips.
@@ -81,6 +94,7 @@ config SND_CMI8330
 	tristate "C-Media CMI8330"
 	tristate "C-Media CMI8330"
 	depends on SND
 	depends on SND
 	select SND_AD1848_LIB
 	select SND_AD1848_LIB
+	select SND_SB16_DSP
 	help
 	help
 	  Say Y here to include support for soundcards based on the
 	  Say Y here to include support for soundcards based on the
 	  C-Media CMI8330 chip.
 	  C-Media CMI8330 chip.
@@ -132,7 +146,7 @@ config SND_DT019X
 	select ISAPNP
 	select ISAPNP
 	select SND_OPL3_LIB
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_MPU401_UART
-	select SND_PCM
+	select SND_SB16_DSP
 	help
 	help
 	  Say Y here to include support for soundcards based on the
 	  Say Y here to include support for soundcards based on the
 	  Diamond Technologies DT-019X or Avance Logic ALS-007 chips.
 	  Diamond Technologies DT-019X or Avance Logic ALS-007 chips.
@@ -145,7 +159,7 @@ config SND_ES968
 	depends on SND && PNP && ISA
 	depends on SND && PNP && ISA
 	select ISAPNP
 	select ISAPNP
 	select SND_MPU401_UART
 	select SND_MPU401_UART
-	select SND_PCM
+	select SND_SB8_DSP
 	help
 	help
 	  Say Y here to include support for ESS AudioDrive ES968 chips.
 	  Say Y here to include support for ESS AudioDrive ES968 chips.
 
 
@@ -321,7 +335,7 @@ config SND_SB8
 	depends on SND
 	depends on SND
 	select SND_OPL3_LIB
 	select SND_OPL3_LIB
 	select SND_RAWMIDI
 	select SND_RAWMIDI
-	select SND_PCM
+	select SND_SB8_DSP
 	help
 	help
 	  Say Y here to include support for Creative Sound Blaster 1.0/
 	  Say Y here to include support for Creative Sound Blaster 1.0/
 	  2.0/Pro (8-bit) or 100% compatible soundcards.
 	  2.0/Pro (8-bit) or 100% compatible soundcards.
@@ -334,7 +348,7 @@ config SND_SB16
 	depends on SND
 	depends on SND
 	select SND_OPL3_LIB
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_MPU401_UART
-	select SND_PCM
+	select SND_SB16_DSP
 	help
 	help
 	  Say Y here to include support for Sound Blaster 16 soundcards
 	  Say Y here to include support for Sound Blaster 16 soundcards
 	  (including the Plug and Play version).
 	  (including the Plug and Play version).
@@ -347,7 +361,7 @@ config SND_SBAWE
 	depends on SND
 	depends on SND
 	select SND_OPL3_LIB
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_MPU401_UART
-	select SND_PCM
+	select SND_SB16_DSP
 	help
 	help
 	  Say Y here to include support for Sound Blaster AWE soundcards
 	  Say Y here to include support for Sound Blaster AWE soundcards
 	  (including the Plug and Play version).
 	  (including the Plug and Play version).

+ 2 - 2
sound/isa/ad1848/ad1848_lib.c

@@ -245,7 +245,7 @@ static void snd_ad1848_mce_down(struct snd_ad1848 *chip)
 			snd_printk(KERN_ERR "mce_down - auto calibration time out (2)\n");
 			snd_printk(KERN_ERR "mce_down - auto calibration time out (2)\n");
 			return;
 			return;
 		}
 		}
-		time = schedule_timeout_interruptible(time);
+		time = schedule_timeout(time);
 		spin_lock_irqsave(&chip->reg_lock, flags);
 		spin_lock_irqsave(&chip->reg_lock, flags);
 	}
 	}
 #if 0
 #if 0
@@ -258,7 +258,7 @@ static void snd_ad1848_mce_down(struct snd_ad1848 *chip)
 			snd_printk(KERN_ERR "mce_down - auto calibration time out (3)\n");
 			snd_printk(KERN_ERR "mce_down - auto calibration time out (3)\n");
 			return;
 			return;
 		}
 		}
-		time = schedule_timeout_interruptible(time);
+		time = schedule_timeout(time);
 		spin_lock_irqsave(&chip->reg_lock, flags);
 		spin_lock_irqsave(&chip->reg_lock, flags);
 	}
 	}
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
 	spin_unlock_irqrestore(&chip->reg_lock, flags);

+ 2 - 0
sound/isa/opl3sa2.c

@@ -164,6 +164,8 @@ static struct pnp_card_device_id snd_opl3sa2_pnpids[] = {
 	{ .id = "YMH0801", .devs = { { "YMH0021" } } },
 	{ .id = "YMH0801", .devs = { { "YMH0021" } } },
 	/* NeoMagic MagicWave 3DX */
 	/* NeoMagic MagicWave 3DX */
 	{ .id = "NMX2200", .devs = { { "YMH2210" } } },
 	{ .id = "NMX2200", .devs = { { "YMH2210" } } },
+	/* NeoMagic MagicWave 3D */
+	{ .id = "NMX2200", .devs = { { "NMX2210" } } },
 	/* --- */
 	/* --- */
 	{ .id = "" }	/* end */
 	{ .id = "" }	/* end */
 };
 };

+ 3 - 0
sound/isa/opti9xx/opti92x-ad1848.c

@@ -1927,10 +1927,12 @@ static struct snd_card *snd_opti9xx_card_new(void)
 static int __devinit snd_opti9xx_isa_match(struct device *devptr,
 static int __devinit snd_opti9xx_isa_match(struct device *devptr,
 					   unsigned int dev)
 					   unsigned int dev)
 {
 {
+#ifdef CONFIG_PNP
 	if (snd_opti9xx_pnp_is_probed)
 	if (snd_opti9xx_pnp_is_probed)
 		return 0;
 		return 0;
 	if (isapnp)
 	if (isapnp)
 		return 0;
 		return 0;
+#endif
 	return 1;
 	return 1;
 }
 }
 
 
@@ -2096,6 +2098,7 @@ static int __init alsa_card_opti9xx_init(void)
 	pnp_register_card_driver(&opti9xx_pnpc_driver);
 	pnp_register_card_driver(&opti9xx_pnpc_driver);
 	if (snd_opti9xx_pnp_is_probed)
 	if (snd_opti9xx_pnp_is_probed)
 		return 0;
 		return 0;
+	pnp_unregister_card_driver(&opti9xx_pnpc_driver);
 #endif
 #endif
 	return isa_register_driver(&snd_opti9xx_driver, 1);
 	return isa_register_driver(&snd_opti9xx_driver, 1);
 }
 }

+ 7 - 8
sound/isa/sb/Makefile

@@ -22,14 +22,13 @@ snd-es968-objs := es968.o
 sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
 sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
 
 
 # Toplevel Module Dependency
 # Toplevel Module Dependency
-obj-$(CONFIG_SND_ALS100) += snd-sb16-dsp.o snd-sb-common.o
-obj-$(CONFIG_SND_CMI8330) += snd-sb16-dsp.o snd-sb-common.o
-obj-$(CONFIG_SND_DT019X) += snd-sb16-dsp.o snd-sb-common.o
-obj-$(CONFIG_SND_SB8) += snd-sb8.o snd-sb8-dsp.o snd-sb-common.o
-obj-$(CONFIG_SND_SB16) += snd-sb16.o snd-sb16-dsp.o snd-sb-common.o
-obj-$(CONFIG_SND_SBAWE) += snd-sbawe.o snd-sb16-dsp.o snd-sb-common.o
-obj-$(CONFIG_SND_ES968) += snd-es968.o snd-sb8-dsp.o snd-sb-common.o
-obj-$(CONFIG_SND_ALS4000) += snd-sb-common.o
+obj-$(CONFIG_SND_SB_COMMON) += snd-sb-common.o
+obj-$(CONFIG_SND_SB16_DSP) += snd-sb16-dsp.o
+obj-$(CONFIG_SND_SB8_DSP) += snd-sb8-dsp.o
+obj-$(CONFIG_SND_SB8) += snd-sb8.o
+obj-$(CONFIG_SND_SB16) += snd-sb16.o
+obj-$(CONFIG_SND_SBAWE) += snd-sbawe.o
+obj-$(CONFIG_SND_ES968) += snd-es968.o
 ifeq ($(CONFIG_SND_SB16_CSP),y)
 ifeq ($(CONFIG_SND_SB16_CSP),y)
   obj-$(CONFIG_SND_SB16) += snd-sb16-csp.o
   obj-$(CONFIG_SND_SB16) += snd-sb16-csp.o
   obj-$(CONFIG_SND_SBAWE) += snd-sb16-csp.o
   obj-$(CONFIG_SND_SBAWE) += snd-sb16-csp.o

+ 10 - 0
sound/isa/sb/sb16_main.c

@@ -563,6 +563,11 @@ static int snd_sb16_playback_open(struct snd_pcm_substream *substream)
       __open_ok:
       __open_ok:
 	if (chip->hardware == SB_HW_ALS100)
 	if (chip->hardware == SB_HW_ALS100)
 		runtime->hw.rate_max = 48000;
 		runtime->hw.rate_max = 48000;
+	if (chip->hardware == SB_HW_CS5530) {
+		runtime->hw.buffer_bytes_max = 32 * 1024;
+		runtime->hw.periods_min = 2;
+		runtime->hw.rate_min = 44100;
+	}
 	if (chip->mode & SB_RATE_LOCK)
 	if (chip->mode & SB_RATE_LOCK)
 		runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate;
 		runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate;
 	chip->playback_substream = substream;
 	chip->playback_substream = substream;
@@ -633,6 +638,11 @@ static int snd_sb16_capture_open(struct snd_pcm_substream *substream)
       __open_ok:
       __open_ok:
 	if (chip->hardware == SB_HW_ALS100)
 	if (chip->hardware == SB_HW_ALS100)
 		runtime->hw.rate_max = 48000;
 		runtime->hw.rate_max = 48000;
+	if (chip->hardware == SB_HW_CS5530) {
+		runtime->hw.buffer_bytes_max = 32 * 1024;
+		runtime->hw.periods_min = 2;
+		runtime->hw.rate_min = 44100;
+	}
 	if (chip->mode & SB_RATE_LOCK)
 	if (chip->mode & SB_RATE_LOCK)
 		runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate;
 		runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate;
 	chip->capture_substream = substream;
 	chip->capture_substream = substream;

+ 4 - 1
sound/isa/sb/sb_common.c

@@ -128,7 +128,7 @@ static int snd_sbdsp_probe(struct snd_sb * chip)
 	minor = version & 0xff;
 	minor = version & 0xff;
 	snd_printdd("SB [0x%lx]: DSP chip found, version = %i.%i\n",
 	snd_printdd("SB [0x%lx]: DSP chip found, version = %i.%i\n",
 		    chip->port, major, minor);
 		    chip->port, major, minor);
-	
+
 	switch (chip->hardware) {
 	switch (chip->hardware) {
 	case SB_HW_AUTO:
 	case SB_HW_AUTO:
 		switch (major) {
 		switch (major) {
@@ -168,6 +168,9 @@ static int snd_sbdsp_probe(struct snd_sb * chip)
 	case SB_HW_DT019X:
 	case SB_HW_DT019X:
 		str = "(DT019X/ALS007)";
 		str = "(DT019X/ALS007)";
 		break;
 		break;
+	case SB_HW_CS5530:
+		str = "16 (CS5530)";
+		break;
 	default:
 	default:
 		return -ENODEV;
 		return -ENODEV;
 	}
 	}

+ 3 - 0
sound/isa/sb/sb_mixer.c

@@ -821,6 +821,7 @@ int snd_sbmixer_new(struct snd_sb *chip)
 		break;
 		break;
 	case SB_HW_16:
 	case SB_HW_16:
 	case SB_HW_ALS100:
 	case SB_HW_ALS100:
+	case SB_HW_CS5530:
 		if ((err = snd_sbmixer_init(chip,
 		if ((err = snd_sbmixer_init(chip,
 					    snd_sb16_controls,
 					    snd_sb16_controls,
 					    ARRAY_SIZE(snd_sb16_controls),
 					    ARRAY_SIZE(snd_sb16_controls),
@@ -950,6 +951,7 @@ void snd_sbmixer_suspend(struct snd_sb *chip)
 		break;
 		break;
 	case SB_HW_16:
 	case SB_HW_16:
 	case SB_HW_ALS100:
 	case SB_HW_ALS100:
+	case SB_HW_CS5530:
 		save_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs));
 		save_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs));
 		break;
 		break;
 	case SB_HW_ALS4000:
 	case SB_HW_ALS4000:
@@ -975,6 +977,7 @@ void snd_sbmixer_resume(struct snd_sb *chip)
 		break;
 		break;
 	case SB_HW_16:
 	case SB_HW_16:
 	case SB_HW_ALS100:
 	case SB_HW_ALS100:
+	case SB_HW_CS5530:
 		restore_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs));
 		restore_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs));
 		break;
 		break;
 	case SB_HW_ALS4000:
 	case SB_HW_ALS4000:

+ 2 - 2
sound/isa/sscape.c

@@ -382,7 +382,7 @@ static int obp_startup_ack(struct soundscape *s, unsigned timeout)
 		unsigned long flags;
 		unsigned long flags;
 		unsigned char x;
 		unsigned char x;
 
 
-		schedule_timeout_interruptible(1);
+		schedule_timeout(1);
 
 
 		spin_lock_irqsave(&s->lock, flags);
 		spin_lock_irqsave(&s->lock, flags);
 		x = inb(HOST_DATA_IO(s->io_base));
 		x = inb(HOST_DATA_IO(s->io_base));
@@ -409,7 +409,7 @@ static int host_startup_ack(struct soundscape *s, unsigned timeout)
 		unsigned long flags;
 		unsigned long flags;
 		unsigned char x;
 		unsigned char x;
 
 
-		schedule_timeout_interruptible(1);
+		schedule_timeout(1);
 
 
 		spin_lock_irqsave(&s->lock, flags);
 		spin_lock_irqsave(&s->lock, flags);
 		x = inb(HOST_DATA_IO(s->io_base));
 		x = inb(HOST_DATA_IO(s->io_base));

+ 1 - 1
sound/isa/wavefront/wavefront_synth.c

@@ -1780,7 +1780,7 @@ wavefront_should_cause_interrupt (snd_wavefront_t *dev,
 	outb (val,port);
 	outb (val,port);
 	spin_unlock_irq(&dev->irq_lock);
 	spin_unlock_irq(&dev->irq_lock);
 	while (1) {
 	while (1) {
-		if ((timeout = schedule_timeout_interruptible(timeout)) == 0)
+		if ((timeout = schedule_timeout(timeout)) == 0)
 			return;
 			return;
 		if (dev->irq_ok)
 		if (dev->irq_ok)
 			return;
 			return;

+ 11 - 0
sound/pci/Kconfig

@@ -33,6 +33,7 @@ config SND_ALS4000
 	select SND_OPL3_LIB
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	select SND_MPU401_UART
 	select SND_PCM
 	select SND_PCM
+	select SND_SB_COMMON
 	help
 	help
 	  Say Y here to include support for soundcards based on Avance Logic
 	  Say Y here to include support for soundcards based on Avance Logic
 	  ALS4000 chips.
 	  ALS4000 chips.
@@ -215,6 +216,16 @@ config SND_CS46XX_NEW_DSP
 
 
 	  This works better than the old code, so say Y.
 	  This works better than the old code, so say Y.
 
 
+config SND_CS5530
+	tristate "CS5530 Audio"
+	depends on SND && ISA_DMA_API
+	select SND_SB16_DSP
+	help
+	  Say Y here to include support for audio on Cyrix/NatSemi CS5530 chips.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd-cs5530.
+
 config SND_CS5535AUDIO
 config SND_CS5535AUDIO
 	tristate "CS5535/CS5536 Audio"
 	tristate "CS5535/CS5536 Audio"
 	depends on SND && X86 && !X86_64
 	depends on SND && X86 && !X86_64

+ 2 - 0
sound/pci/Makefile

@@ -12,6 +12,7 @@ snd-azt3328-objs := azt3328.o
 snd-bt87x-objs := bt87x.o
 snd-bt87x-objs := bt87x.o
 snd-cmipci-objs := cmipci.o
 snd-cmipci-objs := cmipci.o
 snd-cs4281-objs := cs4281.o
 snd-cs4281-objs := cs4281.o
+snd-cs5530-objs := cs5530.o
 snd-ens1370-objs := ens1370.o
 snd-ens1370-objs := ens1370.o
 snd-ens1371-objs := ens1371.o
 snd-ens1371-objs := ens1371.o
 snd-es1938-objs := es1938.o
 snd-es1938-objs := es1938.o
@@ -36,6 +37,7 @@ obj-$(CONFIG_SND_AZT3328) += snd-azt3328.o
 obj-$(CONFIG_SND_BT87X) += snd-bt87x.o
 obj-$(CONFIG_SND_BT87X) += snd-bt87x.o
 obj-$(CONFIG_SND_CMIPCI) += snd-cmipci.o
 obj-$(CONFIG_SND_CMIPCI) += snd-cmipci.o
 obj-$(CONFIG_SND_CS4281) += snd-cs4281.o
 obj-$(CONFIG_SND_CS4281) += snd-cs4281.o
+obj-$(CONFIG_SND_CS5530) += snd-cs5530.o
 obj-$(CONFIG_SND_ENS1370) += snd-ens1370.o
 obj-$(CONFIG_SND_ENS1370) += snd-ens1370.o
 obj-$(CONFIG_SND_ENS1371) += snd-ens1371.o
 obj-$(CONFIG_SND_ENS1371) += snd-ens1371.o
 obj-$(CONFIG_SND_ES1938) += snd-es1938.o
 obj-$(CONFIG_SND_ES1938) += snd-es1938.o

+ 3 - 4
sound/pci/ali5451/ali5451.c

@@ -239,7 +239,7 @@ struct snd_ali_image {
 
 
 
 
 struct snd_ali {
 struct snd_ali {
-	unsigned long	irq;
+	int		irq;
 	unsigned long	port;
 	unsigned long	port;
 	unsigned char	revision;
 	unsigned char	revision;
 
 
@@ -731,8 +731,7 @@ static void snd_ali_detect_spdif_rate(struct snd_ali *codec)
 		return;
 		return;
 	}
 	}
 
 
-	count = 0;
-	while (count++ <= 50000) {
+	for (count = 0; count <= 50000; count++) {
 		snd_ali_delay(codec, 6);
 		snd_ali_delay(codec, 6);
 		bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1));
 		bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1));
 		R2 = bval & 0x1F;
 		R2 = bval & 0x1F;
@@ -2343,7 +2342,7 @@ static int __devinit snd_ali_probe(struct pci_dev *pci,
 	strcpy(card->driver, "ALI5451");
 	strcpy(card->driver, "ALI5451");
 	strcpy(card->shortname, "ALI 5451");
 	strcpy(card->shortname, "ALI 5451");
 	
 	
-	sprintf(card->longname, "%s at 0x%lx, irq %li",
+	sprintf(card->longname, "%s at 0x%lx, irq %i",
 		card->shortname, codec->port, codec->irq);
 		card->shortname, codec->port, codec->irq);
 
 
 	snd_ali_printk("register card.\n");
 	snd_ali_printk("register card.\n");

+ 4 - 3
sound/pci/als300.c

@@ -88,8 +88,8 @@
 #define PLAYBACK_BLOCK_COUNTER	0x9A
 #define PLAYBACK_BLOCK_COUNTER	0x9A
 #define RECORD_BLOCK_COUNTER	0x9B
 #define RECORD_BLOCK_COUNTER	0x9B
 
 
-#define DEBUG_CALLS	1
-#define DEBUG_PLAY_REC	1
+#define DEBUG_CALLS	0
+#define DEBUG_PLAY_REC	0
 
 
 #if DEBUG_CALLS
 #if DEBUG_CALLS
 #define snd_als300_dbgcalls(format, args...) printk(format, ##args)
 #define snd_als300_dbgcalls(format, args...) printk(format, ##args)
@@ -733,7 +733,8 @@ static int __devinit snd_als300_create(struct snd_card *card,
 
 
 	snd_als300_init(chip);
 	snd_als300_init(chip);
 
 
-	if (snd_als300_ac97(chip) < 0) {
+	err = snd_als300_ac97(chip);
+	if (err < 0) {
 		snd_printk(KERN_WARNING "Could not create ac97\n");
 		snd_printk(KERN_WARNING "Could not create ac97\n");
 		snd_als300_free(chip);
 		snd_als300_free(chip);
 		return err;
 		return err;

+ 19 - 0
sound/pci/ca0106/ca0106_main.c

@@ -168,6 +168,25 @@ MODULE_PARM_DESC(subsystem, "Force card subsystem model.");
 #include "ca0106.h"
 #include "ca0106.h"
 
 
 static struct snd_ca0106_details ca0106_chip_details[] = {
 static struct snd_ca0106_details ca0106_chip_details[] = {
+	 /* Sound Blaster X-Fi Extreme Audio. This does not have an AC97. 53SB079000000 */
+	 /* It is really just a normal SB Live 24bit. */
+	 /*
+ 	  * CTRL:CA0111-WTLF
+	  * ADC: WM8775SEDS
+	  * DAC: CS4382-KQZ
+	  */
+	 /* Tested:
+	  * Playback on front, rear, center/lfe speakers
+	  * Capture from Mic in.
+	  * Not-Tested:
+	  * Capture from Line in.
+	  * Playback to digital out.
+	  */
+	 { .serial = 0x10121102,
+	   .name   = "X-Fi Extreme Audio [SB0790]",
+	   .gpio_type = 1,
+	   .i2c_adc = 1 } ,
+	 /* New Dell Sound Blaster Live! 7.1 24bit. This does not have an AC97.  */
 	 /* AudigyLS[SB0310] */
 	 /* AudigyLS[SB0310] */
 	 { .serial = 0x10021102,
 	 { .serial = 0x10021102,
 	   .name   = "AudigyLS [SB0310]",
 	   .name   = "AudigyLS [SB0310]",

+ 62 - 15
sound/pci/cs46xx/cs46xx_lib.c

@@ -2897,6 +2897,10 @@ static int snd_cs46xx_free(struct snd_cs46xx *chip)
 	}
 	}
 #endif
 #endif
 	
 	
+#ifdef CONFIG_PM
+	kfree(chip->saved_regs);
+#endif
+
 	pci_disable_device(chip->pci);
 	pci_disable_device(chip->pci);
 	kfree(chip);
 	kfree(chip);
 	return 0;
 	return 0;
@@ -3140,6 +3144,23 @@ static int snd_cs46xx_chip_init(struct snd_cs46xx *chip)
 /*
 /*
  *  start and load DSP 
  *  start and load DSP 
  */
  */
+
+static void cs46xx_enable_stream_irqs(struct snd_cs46xx *chip)
+{
+	unsigned int tmp;
+
+	snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_IEV | HICR_CHGM);
+        
+	tmp = snd_cs46xx_peek(chip, BA1_PFIE);
+	tmp &= ~0x0000f03f;
+	snd_cs46xx_poke(chip, BA1_PFIE, tmp);	/* playback interrupt enable */
+
+	tmp = snd_cs46xx_peek(chip, BA1_CIE);
+	tmp &= ~0x0000003f;
+	tmp |=  0x00000001;
+	snd_cs46xx_poke(chip, BA1_CIE, tmp);	/* capture interrupt enable */
+}
+
 int __devinit snd_cs46xx_start_dsp(struct snd_cs46xx *chip)
 int __devinit snd_cs46xx_start_dsp(struct snd_cs46xx *chip)
 {	
 {	
 	unsigned int tmp;
 	unsigned int tmp;
@@ -3214,19 +3235,7 @@ int __devinit snd_cs46xx_start_dsp(struct snd_cs46xx *chip)
 
 
 	snd_cs46xx_proc_start(chip);
 	snd_cs46xx_proc_start(chip);
 
 
-	/*
-	 *  Enable interrupts on the part.
-	 */
-	snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_IEV | HICR_CHGM);
-        
-	tmp = snd_cs46xx_peek(chip, BA1_PFIE);
-	tmp &= ~0x0000f03f;
-	snd_cs46xx_poke(chip, BA1_PFIE, tmp);	/* playback interrupt enable */
-
-	tmp = snd_cs46xx_peek(chip, BA1_CIE);
-	tmp &= ~0x0000003f;
-	tmp |=  0x00000001;
-	snd_cs46xx_poke(chip, BA1_CIE, tmp);	/* capture interrupt enable */
+	cs46xx_enable_stream_irqs(chip);
 	
 	
 #ifndef CONFIG_SND_CS46XX_NEW_DSP
 #ifndef CONFIG_SND_CS46XX_NEW_DSP
 	/* set the attenuation to 0dB */ 
 	/* set the attenuation to 0dB */ 
@@ -3665,11 +3674,19 @@ static struct cs_card_type __devinitdata cards[] = {
  * APM support
  * APM support
  */
  */
 #ifdef CONFIG_PM
 #ifdef CONFIG_PM
+static unsigned int saved_regs[] = {
+	BA0_ACOSV,
+	BA0_ASER_FADDR,
+	BA0_ASER_MASTER,
+	BA1_PVOL,
+	BA1_CVOL,
+};
+
 int snd_cs46xx_suspend(struct pci_dev *pci, pm_message_t state)
 int snd_cs46xx_suspend(struct pci_dev *pci, pm_message_t state)
 {
 {
 	struct snd_card *card = pci_get_drvdata(pci);
 	struct snd_card *card = pci_get_drvdata(pci);
 	struct snd_cs46xx *chip = card->private_data;
 	struct snd_cs46xx *chip = card->private_data;
-	int amp_saved;
+	int i, amp_saved;
 
 
 	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
 	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
 	chip->in_suspend = 1;
 	chip->in_suspend = 1;
@@ -3680,6 +3697,10 @@ int snd_cs46xx_suspend(struct pci_dev *pci, pm_message_t state)
 	snd_ac97_suspend(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]);
 	snd_ac97_suspend(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]);
 	snd_ac97_suspend(chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]);
 	snd_ac97_suspend(chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]);
 
 
+	/* save some registers */
+	for (i = 0; i < ARRAY_SIZE(saved_regs); i++)
+		chip->saved_regs[i] = snd_cs46xx_peekBA0(chip, saved_regs[i]);
+
 	amp_saved = chip->amplifier;
 	amp_saved = chip->amplifier;
 	/* turn off amp */
 	/* turn off amp */
 	chip->amplifier_ctrl(chip, -chip->amplifier);
 	chip->amplifier_ctrl(chip, -chip->amplifier);
@@ -3698,7 +3719,7 @@ int snd_cs46xx_resume(struct pci_dev *pci)
 {
 {
 	struct snd_card *card = pci_get_drvdata(pci);
 	struct snd_card *card = pci_get_drvdata(pci);
 	struct snd_cs46xx *chip = card->private_data;
 	struct snd_cs46xx *chip = card->private_data;
-	int amp_saved;
+	int i, amp_saved;
 
 
 	pci_set_power_state(pci, PCI_D0);
 	pci_set_power_state(pci, PCI_D0);
 	pci_restore_state(pci);
 	pci_restore_state(pci);
@@ -3716,6 +3737,16 @@ int snd_cs46xx_resume(struct pci_dev *pci)
 
 
 	snd_cs46xx_chip_init(chip);
 	snd_cs46xx_chip_init(chip);
 
 
+	snd_cs46xx_reset(chip);
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	cs46xx_dsp_resume(chip);
+	/* restore some registers */
+	for (i = 0; i < ARRAY_SIZE(saved_regs); i++)
+		snd_cs46xx_pokeBA0(chip, saved_regs[i], chip->saved_regs[i]);
+#else
+	snd_cs46xx_download_image(chip);
+#endif
+
 #if 0
 #if 0
 	snd_cs46xx_codec_write(chip, BA0_AC97_GENERAL_PURPOSE, 
 	snd_cs46xx_codec_write(chip, BA0_AC97_GENERAL_PURPOSE, 
 			       chip->ac97_general_purpose);
 			       chip->ac97_general_purpose);
@@ -3730,6 +3761,13 @@ int snd_cs46xx_resume(struct pci_dev *pci)
 	snd_ac97_resume(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]);
 	snd_ac97_resume(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]);
 	snd_ac97_resume(chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]);
 	snd_ac97_resume(chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]);
 
 
+	/* reset playback/capture */
+	snd_cs46xx_set_play_sample_rate(chip, 8000);
+	snd_cs46xx_set_capture_sample_rate(chip, 8000);
+	snd_cs46xx_proc_start(chip);
+
+	cs46xx_enable_stream_irqs(chip);
+
 	if (amp_saved)
 	if (amp_saved)
 		chip->amplifier_ctrl(chip, 1); /* turn amp on */
 		chip->amplifier_ctrl(chip, 1); /* turn amp on */
 	else
 	else
@@ -3896,6 +3934,15 @@ int __devinit snd_cs46xx_create(struct snd_card *card,
 	
 	
 	snd_cs46xx_proc_init(card, chip);
 	snd_cs46xx_proc_init(card, chip);
 
 
+#ifdef CONFIG_PM
+	chip->saved_regs = kmalloc(sizeof(*chip->saved_regs) *
+				   ARRAY_SIZE(saved_regs), GFP_KERNEL);
+	if (!chip->saved_regs) {
+		snd_cs46xx_free(chip);
+		return -ENOMEM;
+	}
+#endif
+
 	chip->active_ctrl(chip, -1); /* disable CLKRUN */
 	chip->active_ctrl(chip, -1); /* disable CLKRUN */
 
 
 	snd_card_set_dev(card, &pci->dev);
 	snd_card_set_dev(card, &pci->dev);

+ 3 - 0
sound/pci/cs46xx/cs46xx_lib.h

@@ -86,6 +86,9 @@ static inline unsigned int snd_cs46xx_peekBA0(struct snd_cs46xx *chip, unsigned
 struct dsp_spos_instance *cs46xx_dsp_spos_create (struct snd_cs46xx * chip);
 struct dsp_spos_instance *cs46xx_dsp_spos_create (struct snd_cs46xx * chip);
 void cs46xx_dsp_spos_destroy (struct snd_cs46xx * chip);
 void cs46xx_dsp_spos_destroy (struct snd_cs46xx * chip);
 int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * module);
 int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * module);
+#ifdef CONFIG_PM
+int cs46xx_dsp_resume(struct snd_cs46xx * chip);
+#endif
 struct dsp_symbol_entry *cs46xx_dsp_lookup_symbol (struct snd_cs46xx * chip, char * symbol_name,
 struct dsp_symbol_entry *cs46xx_dsp_lookup_symbol (struct snd_cs46xx * chip, char * symbol_name,
 						   int symbol_type);
 						   int symbol_type);
 #ifdef CONFIG_PROC_FS
 #ifdef CONFIG_PROC_FS

+ 126 - 44
sound/pci/cs46xx/dsp_spos.c

@@ -306,13 +306,59 @@ void  cs46xx_dsp_spos_destroy (struct snd_cs46xx * chip)
 	mutex_unlock(&chip->spos_mutex);
 	mutex_unlock(&chip->spos_mutex);
 }
 }
 
 
+static int dsp_load_parameter(struct snd_cs46xx *chip,
+			      struct dsp_segment_desc *parameter)
+{
+	u32 doffset, dsize;
+
+	if (!parameter) {
+		snd_printdd("dsp_spos: module got no parameter segment\n");
+		return 0;
+	}
+
+	doffset = (parameter->offset * 4 + DSP_PARAMETER_BYTE_OFFSET);
+	dsize   = parameter->size * 4;
+
+	snd_printdd("dsp_spos: "
+		    "downloading parameter data to chip (%08x-%08x)\n",
+		    doffset,doffset + dsize);
+	if (snd_cs46xx_download (chip, parameter->data, doffset, dsize)) {
+		snd_printk(KERN_ERR "dsp_spos: "
+			   "failed to download parameter data to DSP\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int dsp_load_sample(struct snd_cs46xx *chip,
+			   struct dsp_segment_desc *sample)
+{
+	u32 doffset, dsize;
+
+	if (!sample) {
+		snd_printdd("dsp_spos: module got no sample segment\n");
+		return 0;
+	}
+
+	doffset = (sample->offset * 4  + DSP_SAMPLE_BYTE_OFFSET);
+	dsize   =  sample->size * 4;
+
+	snd_printdd("dsp_spos: downloading sample data to chip (%08x-%08x)\n",
+		    doffset,doffset + dsize);
+
+	if (snd_cs46xx_download (chip,sample->data,doffset,dsize)) {
+		snd_printk(KERN_ERR "dsp_spos: failed to sample data to DSP\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
 int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * module)
 int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * module)
 {
 {
 	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
 	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
 	struct dsp_segment_desc * code = get_segment_desc (module,SEGTYPE_SP_PROGRAM);
 	struct dsp_segment_desc * code = get_segment_desc (module,SEGTYPE_SP_PROGRAM);
-	struct dsp_segment_desc * parameter = get_segment_desc (module,SEGTYPE_SP_PARAMETER);
-	struct dsp_segment_desc * sample = get_segment_desc (module,SEGTYPE_SP_SAMPLE);
 	u32 doffset, dsize;
 	u32 doffset, dsize;
+	int err;
 
 
 	if (ins->nmodules == DSP_MAX_MODULES - 1) {
 	if (ins->nmodules == DSP_MAX_MODULES - 1) {
 		snd_printk(KERN_ERR "dsp_spos: to many modules loaded into DSP\n");
 		snd_printk(KERN_ERR "dsp_spos: to many modules loaded into DSP\n");
@@ -326,49 +372,20 @@ int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * m
 		snd_cs46xx_clear_BA1(chip, DSP_PARAMETER_BYTE_OFFSET, DSP_PARAMETER_BYTE_SIZE);
 		snd_cs46xx_clear_BA1(chip, DSP_PARAMETER_BYTE_OFFSET, DSP_PARAMETER_BYTE_SIZE);
 	}
 	}
   
   
-	if (parameter == NULL) {
-		snd_printdd("dsp_spos: module got no parameter segment\n");
-	} else {
-		if (ins->nmodules > 0) {
-			snd_printk(KERN_WARNING "dsp_spos: WARNING current parameter data may be overwriten!\n");
-		}
-
-		doffset = (parameter->offset * 4 + DSP_PARAMETER_BYTE_OFFSET);
-		dsize   = parameter->size * 4;
-
-		snd_printdd("dsp_spos: downloading parameter data to chip (%08x-%08x)\n",
-			    doffset,doffset + dsize);
-
-		if (snd_cs46xx_download (chip, parameter->data, doffset, dsize)) {
-			snd_printk(KERN_ERR "dsp_spos: failed to download parameter data to DSP\n");
-			return -EINVAL;
-		}
-	}
+	err = dsp_load_parameter(chip, get_segment_desc(module,
+							SEGTYPE_SP_PARAMETER));
+	if (err < 0)
+		return err;
 
 
 	if (ins->nmodules == 0) {
 	if (ins->nmodules == 0) {
 		snd_printdd("dsp_spos: clearing sample area\n");
 		snd_printdd("dsp_spos: clearing sample area\n");
 		snd_cs46xx_clear_BA1(chip, DSP_SAMPLE_BYTE_OFFSET, DSP_SAMPLE_BYTE_SIZE);
 		snd_cs46xx_clear_BA1(chip, DSP_SAMPLE_BYTE_OFFSET, DSP_SAMPLE_BYTE_SIZE);
 	}
 	}
 
 
-	if (sample == NULL) {
-		snd_printdd("dsp_spos: module got no sample segment\n");
-	} else {
-		if (ins->nmodules > 0) {
-			snd_printk(KERN_WARNING "dsp_spos: WARNING current sample data may be overwriten\n");
-		}
-
-		doffset = (sample->offset * 4  + DSP_SAMPLE_BYTE_OFFSET);
-		dsize   =  sample->size * 4;
-
-		snd_printdd("dsp_spos: downloading sample data to chip (%08x-%08x)\n",
-			    doffset,doffset + dsize);
-
-		if (snd_cs46xx_download (chip,sample->data,doffset,dsize)) {
-			snd_printk(KERN_ERR "dsp_spos: failed to sample data to DSP\n");
-			return -EINVAL;
-		}
-	}
-
+	err = dsp_load_sample(chip, get_segment_desc(module,
+						     SEGTYPE_SP_SAMPLE));
+	if (err < 0)
+		return err;
 
 
 	if (ins->nmodules == 0) {
 	if (ins->nmodules == 0) {
 		snd_printdd("dsp_spos: clearing code area\n");
 		snd_printdd("dsp_spos: clearing code area\n");
@@ -986,7 +1003,10 @@ _map_task_tree (struct snd_cs46xx *chip, char * name, u32 dest, u32 size)
 		return NULL;
 		return NULL;
 	}
 	}
 
 
-	strcpy(ins->tasks[ins->ntask].task_name,name);
+	if (name)
+		strcpy(ins->tasks[ins->ntask].task_name, name);
+	else
+		strcpy(ins->tasks[ins->ntask].task_name, "(NULL)");
 	ins->tasks[ins->ntask].address = dest;
 	ins->tasks[ins->ntask].address = dest;
 	ins->tasks[ins->ntask].size = size;
 	ins->tasks[ins->ntask].size = size;
 
 
@@ -995,7 +1015,8 @@ _map_task_tree (struct snd_cs46xx *chip, char * name, u32 dest, u32 size)
 	desc = (ins->tasks + ins->ntask);
 	desc = (ins->tasks + ins->ntask);
 	ins->ntask++;
 	ins->ntask++;
 
 
-	add_symbol (chip,name,dest,SYMBOL_PARAMETER);
+	if (name)
+		add_symbol (chip,name,dest,SYMBOL_PARAMETER);
 	return desc;
 	return desc;
 }
 }
 
 
@@ -1006,6 +1027,7 @@ cs46xx_dsp_create_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u32
 
 
 	desc = _map_scb (chip,name,dest);
 	desc = _map_scb (chip,name,dest);
 	if (desc) {
 	if (desc) {
+		desc->data = scb_data;
 		_dsp_create_scb(chip,scb_data,dest);
 		_dsp_create_scb(chip,scb_data,dest);
 	} else {
 	} else {
 		snd_printk(KERN_ERR "dsp_spos: failed to map SCB\n");
 		snd_printk(KERN_ERR "dsp_spos: failed to map SCB\n");
@@ -1023,6 +1045,7 @@ cs46xx_dsp_create_task_tree (struct snd_cs46xx *chip, char * name, u32 * task_da
 
 
 	desc = _map_task_tree (chip,name,dest,size);
 	desc = _map_task_tree (chip,name,dest,size);
 	if (desc) {
 	if (desc) {
+		desc->data = task_data;
 		_dsp_create_task_tree(chip,task_data,dest,size);
 		_dsp_create_task_tree(chip,task_data,dest,size);
 	} else {
 	} else {
 		snd_printk(KERN_ERR "dsp_spos: failed to map TASK\n");
 		snd_printk(KERN_ERR "dsp_spos: failed to map TASK\n");
@@ -1320,8 +1343,10 @@ int cs46xx_dsp_scb_and_task_init (struct snd_cs46xx *chip)
 			0x0000ffff
 			0x0000ffff
 		};
 		};
     
     
-		/* dirty hack ... */
-		_dsp_create_task_tree (chip,(u32 *)&mix2_ostream_spb,WRITE_BACK_SPB,2);
+		if (!cs46xx_dsp_create_task_tree(chip, NULL,
+						 (u32 *)&mix2_ostream_spb,
+						 WRITE_BACK_SPB, 2))
+			goto _fail_end;
 	}
 	}
 
 
 	/* input sample converter */
 	/* input sample converter */
@@ -1622,7 +1647,6 @@ static int cs46xx_dsp_async_init (struct snd_cs46xx *chip,
 	return 0;
 	return 0;
 }
 }
 
 
-
 static void cs46xx_dsp_disable_spdif_hw (struct snd_cs46xx *chip)
 static void cs46xx_dsp_disable_spdif_hw (struct snd_cs46xx *chip)
 {
 {
 	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
 	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
@@ -1894,3 +1918,61 @@ int cs46xx_dsp_set_iec958_volume (struct snd_cs46xx * chip, u16 left, u16 right)
 
 
 	return 0;
 	return 0;
 }
 }
+
+#ifdef CONFIG_PM
+int cs46xx_dsp_resume(struct snd_cs46xx * chip)
+{
+	struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+	int i, err;
+
+	/* clear parameter, sample and code areas */
+	snd_cs46xx_clear_BA1(chip, DSP_PARAMETER_BYTE_OFFSET,
+			     DSP_PARAMETER_BYTE_SIZE);
+	snd_cs46xx_clear_BA1(chip, DSP_SAMPLE_BYTE_OFFSET,
+			     DSP_SAMPLE_BYTE_SIZE);
+	snd_cs46xx_clear_BA1(chip, DSP_CODE_BYTE_OFFSET, DSP_CODE_BYTE_SIZE);
+
+	for (i = 0; i < ins->nmodules; i++) {
+		struct dsp_module_desc *module = &ins->modules[i];
+		struct dsp_segment_desc *seg;
+		u32 doffset, dsize;
+
+		seg = get_segment_desc(module, SEGTYPE_SP_PARAMETER);
+		err = dsp_load_parameter(chip, seg);
+		if (err < 0)
+			return err;
+
+		seg = get_segment_desc(module, SEGTYPE_SP_SAMPLE);
+		err = dsp_load_sample(chip, seg);
+		if (err < 0)
+			return err;
+
+		seg = get_segment_desc(module, SEGTYPE_SP_PROGRAM);
+		if (!seg)
+			continue;
+
+		doffset = seg->offset * 4 + module->load_address * 4
+			+ DSP_CODE_BYTE_OFFSET;
+		dsize   = seg->size * 4;
+		err = snd_cs46xx_download(chip,
+					  ins->code.data + module->load_address,
+					  doffset, dsize);
+		if (err < 0)
+			return err;
+	}
+
+	for (i = 0; i < ins->ntask; i++) {
+		struct dsp_task_descriptor *t = &ins->tasks[i];
+		_dsp_create_task_tree(chip, t->data, t->address, t->size);
+	}
+
+	for (i = 0; i < ins->nscb; i++) {
+		struct dsp_scb_descriptor *s = &ins->scbs[i];
+		if (s->deleted)
+			continue;
+		_dsp_create_scb(chip, s->data, s->address);
+	}
+
+	return 0;
+}
+#endif

+ 306 - 0
sound/pci/cs5530.c

@@ -0,0 +1,306 @@
+/*
+ * cs5530.c - Initialisation code for Cyrix/NatSemi VSA1 softaudio
+ *
+ * 	(C) Copyright 2007 Ash Willis <ashwillis@programmer.net>
+ *	(C) Copyright 2003 Red Hat Inc <alan@redhat.com>
+ *
+ * This driver was ported (shamelessly ripped ;) from oss/kahlua.c but I did
+ * mess with it a bit. The chip seems to have to have trouble with full duplex
+ * mode. If we're recording in 8bit 8000kHz, say, and we then attempt to
+ * simultaneously play back audio at 16bit 44100kHz, the device actually plays
+ * back in the same format in which it is capturing. By forcing the chip to
+ * always play/capture in 16/44100, we can let alsa-lib convert the samples and
+ * that way we can hack up some full duplex audio. 
+ * 
+ * XpressAudio(tm) is used on the Cyrix MediaGX (now NatSemi Geode) systems.
+ * The older version (VSA1) provides fairly good soundblaster emulation
+ * although there are a couple of bugs: large DMA buffers break record,
+ * and the MPU event handling seems suspect. VSA2 allows the native driver
+ * to control the AC97 audio engine directly and requires a different driver.
+ *
+ * Thanks to National Semiconductor for providing the needed information
+ * on the XpressAudio(tm) internals.
+ *
+ * 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, 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.
+ *
+ * TO DO:
+ *	Investigate whether we can portably support Cognac (5520) in the
+ *	same manner.
+ */
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include <sound/sb.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Ash Willis");
+MODULE_DESCRIPTION("CS5530 Audio");
+MODULE_LICENSE("GPL");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+struct snd_cs5530 {
+	struct snd_card *card;
+	struct pci_dev *pci;
+	struct snd_sb *sb;
+	unsigned long pci_base;
+};
+
+static struct pci_device_id snd_cs5530_ids[] = {
+	{PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_AUDIO, PCI_ANY_ID,
+							PCI_ANY_ID, 0, 0},
+	{0,}
+};
+
+MODULE_DEVICE_TABLE(pci, snd_cs5530_ids);
+
+static int snd_cs5530_free(struct snd_cs5530 *chip)
+{
+	pci_release_regions(chip->pci);
+	pci_disable_device(chip->pci);
+	kfree(chip);
+	return 0;
+}
+
+static int snd_cs5530_dev_free(struct snd_device *device)
+{
+	struct snd_cs5530 *chip = device->device_data;
+	return snd_cs5530_free(chip);
+}
+
+static void __devexit snd_cs5530_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+static u8 __devinit snd_cs5530_mixer_read(unsigned long io, u8 reg)
+{
+	outb(reg, io + 4);
+	udelay(20);
+	reg = inb(io + 5);
+	udelay(20);
+	return reg;
+}
+
+static int __devinit snd_cs5530_create(struct snd_card *card,
+				       struct pci_dev *pci,
+				       struct snd_cs5530 **rchip)
+{
+	struct snd_cs5530 *chip;
+	unsigned long sb_base;
+	u8 irq, dma8, dma16 = 0;
+	u16 map;
+	void __iomem *mem;
+	int err;
+
+	static struct snd_device_ops ops = {
+		.dev_free = snd_cs5530_dev_free,
+	};
+	*rchip = NULL;
+
+	err = pci_enable_device(pci);
+ 	if (err < 0)
+		return err;
+
+	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL) {
+		pci_disable_device(pci);
+		return -ENOMEM;
+	}
+
+	chip->card = card;
+	chip->pci = pci;
+
+	err = pci_request_regions(pci, "CS5530");
+	if (err < 0) {
+		kfree(chip); 
+		pci_disable_device(pci);
+		return err;
+	}
+	chip->pci_base = pci_resource_start(pci, 0);
+
+	mem = ioremap_nocache(chip->pci_base, pci_resource_len(pci, 0));
+	if (mem == NULL) {
+		kfree(chip);
+		pci_disable_device(pci);
+		return -EBUSY;
+	}
+
+	map = readw(mem + 0x18);
+	iounmap(mem);
+
+	/* Map bits
+		0:1	* 0x20 + 0x200 = sb base
+		2	sb enable
+		3	adlib enable
+		5	MPU enable 0x330
+		6	MPU enable 0x300
+
+	   The other bits may be used internally so must be masked */
+
+	sb_base = 0x220 + 0x20 * (map & 3);
+
+	if (map & (1<<2))
+		printk(KERN_INFO "CS5530: XpressAudio at 0x%lx\n", sb_base);
+	else {
+		printk(KERN_ERR "Could not find XpressAudio!\n");
+		snd_cs5530_free(chip);
+		return -ENODEV;
+	}
+
+	if (map & (1<<5))
+		printk(KERN_INFO "CS5530: MPU at 0x300\n");
+	else if (map & (1<<6))
+		printk(KERN_INFO "CS5530: MPU at 0x330\n");
+
+	irq = snd_cs5530_mixer_read(sb_base, 0x80) & 0x0F;
+	dma8 = snd_cs5530_mixer_read(sb_base, 0x81);
+
+	if (dma8 & 0x20)
+		dma16 = 5;
+	else if (dma8 & 0x40)
+		dma16 = 6;
+	else if (dma8 & 0x80)
+		dma16 = 7;
+	else {
+		printk(KERN_ERR "CS5530: No 16bit DMA enabled\n");
+		snd_cs5530_free(chip);
+		return -ENODEV;
+	}
+
+	if (dma8 & 0x01)
+		dma8 = 0;
+	else if (dma8 & 02)
+		dma8 = 1;
+	else if (dma8 & 0x08)
+		dma8 = 3;
+	else {
+		printk(KERN_ERR "CS5530: No 8bit DMA enabled\n");
+		snd_cs5530_free(chip);
+		return -ENODEV;
+	}
+
+	if (irq & 1)
+		irq = 9;
+	else if (irq & 2)
+		irq = 5;
+	else if (irq & 4)
+		irq = 7;
+	else if (irq & 8)
+		irq = 10;
+	else {
+		printk(KERN_ERR "CS5530: SoundBlaster IRQ not set\n");
+		snd_cs5530_free(chip);
+		return -ENODEV;
+	}
+
+	printk(KERN_INFO "CS5530: IRQ: %d DMA8: %d DMA16: %d\n", irq, dma8, 
+									dma16);
+
+	err = snd_sbdsp_create(card, sb_base, irq, snd_sb16dsp_interrupt, dma8,
+						dma16, SB_HW_CS5530, &chip->sb);
+	if (err < 0) {
+		printk(KERN_ERR "CS5530: Could not create SoundBlaster\n");
+		snd_cs5530_free(chip);
+		return err;
+	}
+
+	err = snd_sb16dsp_pcm(chip->sb, 0, &chip->sb->pcm);
+	if (err < 0) {
+		printk(KERN_ERR "CS5530: Could not create PCM\n");
+		snd_cs5530_free(chip);
+		return err;
+	}
+
+	err = snd_sbmixer_new(chip->sb);
+	if (err < 0) {
+		printk(KERN_ERR "CS5530: Could not create Mixer\n");
+		snd_cs5530_free(chip);
+		return err;
+	}
+
+	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+	if (err < 0) {
+		snd_cs5530_free(chip);
+		return err;
+	}
+
+	snd_card_set_dev(card, &pci->dev);
+	*rchip = chip;
+	return 0;
+}
+
+static int __devinit snd_cs5530_probe(struct pci_dev *pci,
+					const struct pci_device_id *pci_id)
+{
+	static int dev;
+	struct snd_card *card;
+	struct snd_cs5530 *chip = NULL;
+	int err;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+
+	if (card == NULL)
+		return -ENOMEM;
+
+	err = snd_cs5530_create(card, pci, &chip);
+	if (err < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	strcpy(card->driver, "CS5530");
+	strcpy(card->shortname, "CS5530 Audio");
+	sprintf(card->longname, "%s at 0x%lx", card->shortname, chip->pci_base);
+
+	err = snd_card_register(card);
+	if (err < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	pci_set_drvdata(pci, card);
+	dev++;
+	return 0;
+}
+
+static struct pci_driver driver = {
+	.name = "CS5530_Audio",
+	.id_table = snd_cs5530_ids,
+	.probe = snd_cs5530_probe,
+	.remove = __devexit_p(snd_cs5530_remove),
+};
+
+static int __init alsa_card_cs5530_init(void)
+{
+	return pci_register_driver(&driver);
+}
+
+static void __exit alsa_card_cs5530_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_cs5530_init)
+module_exit(alsa_card_cs5530_exit)
+

+ 108 - 17
sound/pci/emu10k1/emu10k1_main.c

@@ -51,9 +51,15 @@
 
 
 #define HANA_FILENAME "emu/hana.fw"
 #define HANA_FILENAME "emu/hana.fw"
 #define DOCK_FILENAME "emu/audio_dock.fw"
 #define DOCK_FILENAME "emu/audio_dock.fw"
+#define EMU1010B_FILENAME "emu/emu1010b.fw"
+#define MICRO_DOCK_FILENAME "emu/micro_dock.fw"
+#define EMU1010_NOTEBOOK_FILENAME "emu/emu1010_notebook.fw"
 
 
 MODULE_FIRMWARE(HANA_FILENAME);
 MODULE_FIRMWARE(HANA_FILENAME);
 MODULE_FIRMWARE(DOCK_FILENAME);
 MODULE_FIRMWARE(DOCK_FILENAME);
+MODULE_FIRMWARE(EMU1010B_FILENAME);
+MODULE_FIRMWARE(MICRO_DOCK_FILENAME);
+MODULE_FIRMWARE(EMU1010_NOTEBOOK_FILENAME);
 
 
 
 
 /*************************************************************************
 /*************************************************************************
@@ -660,10 +666,12 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file
 		return err;
 		return err;
 	}
 	}
 	snd_printk(KERN_INFO "firmware size=0x%zx\n", fw_entry->size);
 	snd_printk(KERN_INFO "firmware size=0x%zx\n", fw_entry->size);
+#if 0
 	if (fw_entry->size != 0x133a4) {
 	if (fw_entry->size != 0x133a4) {
 		snd_printk(KERN_ERR "firmware: %s wrong size.\n",filename);
 		snd_printk(KERN_ERR "firmware: %s wrong size.\n",filename);
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
+#endif
 
 
 	/* The FPGA is a Xilinx Spartan IIE XC2S50E */
 	/* The FPGA is a Xilinx Spartan IIE XC2S50E */
 	/* GPIO7 -> FPGA PGMN
 	/* GPIO7 -> FPGA PGMN
@@ -694,6 +702,37 @@ static int snd_emu1010_load_firmware(struct snd_emu10k1 * emu, const char * file
 	return 0;
 	return 0;
 }
 }
 
 
+/*
+ * EMU-1010 - details found out from this driver, official MS Win drivers,
+ * testing the card:
+ *
+ * Audigy2 (aka Alice2):
+ * ---------------------
+ * 	* communication over PCI
+ * 	* conversion of 32-bit data coming over EMU32 links from HANA FPGA
+ *	  to 2 x 16-bit, using internal DSP instructions
+ * 	* slave mode, clock supplied by HANA
+ * 	* linked to HANA using:
+ * 		32 x 32-bit serial EMU32 output channels
+ * 		16 x EMU32 input channels
+ * 		(?) x I2S I/O channels (?)
+ *
+ * FPGA (aka HANA):
+ * ---------------
+ * 	* provides all (?) physical inputs and outputs of the card
+ * 		(ADC, DAC, SPDIF I/O, ADAT I/O, etc.)
+ * 	* provides clock signal for the card and Alice2
+ * 	* two crystals - for 44.1kHz and 48kHz multiples
+ * 	* provides internal routing of signal sources to signal destinations
+ * 	* inputs/outputs to Alice2 - see above
+ *
+ * Current status of the driver:
+ * ----------------------------
+ * 	* only 44.1/48kHz supported (the MS Win driver supports up to 192 kHz)
+ * 	* PCM device nb. 2:
+ *		16 x 16-bit playback - snd_emu10k1_fx8010_playback_ops
+ * 		16 x 32-bit capture - snd_emu10k1_capture_efx_ops
+ */
 static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
 static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
 {
 {
 	unsigned int i;
 	unsigned int i;
@@ -727,7 +766,7 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
 	/* ID, should read & 0x7f = 0x55. (Bit 7 is the IRQ bit) */
 	/* ID, should read & 0x7f = 0x55. (Bit 7 is the IRQ bit) */
 	snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
 	snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
 	snd_printdd("reg1=0x%x\n",reg);
 	snd_printdd("reg1=0x%x\n",reg);
-	if (reg == 0x55) {
+	if ((reg & 0x3f) == 0x15) {
 		/* FPGA netlist already present so clear it */
 		/* FPGA netlist already present so clear it */
 		/* Return to programming mode */
 		/* Return to programming mode */
 
 
@@ -735,19 +774,32 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
 	}
 	}
 	snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
 	snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
 	snd_printdd("reg2=0x%x\n",reg);
 	snd_printdd("reg2=0x%x\n",reg);
-	if (reg == 0x55) {
+	if ((reg & 0x3f) == 0x15) {
 		/* FPGA failed to return to programming mode */
 		/* FPGA failed to return to programming mode */
+		snd_printk(KERN_INFO "emu1010: FPGA failed to return to programming mode\n");
 		return -ENODEV;
 		return -ENODEV;
 	}
 	}
 	snd_printk(KERN_INFO "emu1010: EMU_HANA_ID=0x%x\n",reg);
 	snd_printk(KERN_INFO "emu1010: EMU_HANA_ID=0x%x\n",reg);
-	if ((err = snd_emu1010_load_firmware(emu, HANA_FILENAME)) != 0) {
-		snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file %s failed\n", HANA_FILENAME);
-		return err;
+	if (emu->card_capabilities->emu1010 == 1) {
+		if ((err = snd_emu1010_load_firmware(emu, HANA_FILENAME)) != 0) {
+			snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file %s failed\n", HANA_FILENAME);
+			return err;
+		}
+	} else if (emu->card_capabilities->emu1010 == 2) {
+		if ((err = snd_emu1010_load_firmware(emu, EMU1010B_FILENAME)) != 0) {
+			snd_printk(KERN_INFO "emu1010: Loading Firmware file %s failed\n", EMU1010B_FILENAME);
+			return err;
+		}
+	} else if (emu->card_capabilities->emu1010 == 3) {
+		if ((err = snd_emu1010_load_firmware(emu, EMU1010_NOTEBOOK_FILENAME)) != 0) {
+			snd_printk(KERN_INFO "emu1010: Loading Firmware file %s failed\n", EMU1010_NOTEBOOK_FILENAME);
+			return err;
+		}
 	}
 	}
 
 
 	/* ID, should read & 0x7f = 0x55 when FPGA programmed. */
 	/* ID, should read & 0x7f = 0x55 when FPGA programmed. */
 	snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
 	snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
-	if (reg != 0x55) {
+	if ((reg & 0x3f) != 0x15) {
 		/* FPGA failed to be programmed */
 		/* FPGA failed to be programmed */
 		snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file failed, reg=0x%x\n", reg);
 		snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file failed, reg=0x%x\n", reg);
 		return -ENODEV;
 		return -ENODEV;
@@ -850,6 +902,27 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
 		EMU_DST_ALICE2_EMU32_6, EMU_SRC_DOCK_ADC2_LEFT1);
 		EMU_DST_ALICE2_EMU32_6, EMU_SRC_DOCK_ADC2_LEFT1);
 	snd_emu1010_fpga_link_dst_src_write(emu,
 	snd_emu1010_fpga_link_dst_src_write(emu,
 		EMU_DST_ALICE2_EMU32_7, EMU_SRC_DOCK_ADC2_RIGHT1);
 		EMU_DST_ALICE2_EMU32_7, EMU_SRC_DOCK_ADC2_RIGHT1);
+	/* Pavel Hofman - setting defaults for 8 more capture channels
+	 * Defaults only, users will set their own values anyways, let's
+	 * just copy/paste.
+	 */
+	
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_8, EMU_SRC_DOCK_MIC_A1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_9, EMU_SRC_DOCK_MIC_B1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_A, EMU_SRC_HAMOA_ADC_LEFT2);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_B, EMU_SRC_HAMOA_ADC_LEFT2);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_C, EMU_SRC_DOCK_ADC1_LEFT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_D, EMU_SRC_DOCK_ADC1_RIGHT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_E, EMU_SRC_DOCK_ADC2_LEFT1);
+	snd_emu1010_fpga_link_dst_src_write(emu,
+		EMU_DST_ALICE2_EMU32_F, EMU_SRC_DOCK_ADC2_RIGHT1);
 #endif
 #endif
 #if 0
 #if 0
 	/* Original */
 	/* Original */
@@ -943,16 +1016,27 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
 		/* Return to Audio Dock programming mode */
 		/* Return to Audio Dock programming mode */
 		snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware\n");
 		snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware\n");
 		snd_emu1010_fpga_write(emu,  EMU_HANA_FPGA_CONFIG, EMU_HANA_FPGA_CONFIG_AUDIODOCK );
 		snd_emu1010_fpga_write(emu,  EMU_HANA_FPGA_CONFIG, EMU_HANA_FPGA_CONFIG_AUDIODOCK );
-		if ((err = snd_emu1010_load_firmware(emu, DOCK_FILENAME)) != 0) {
-			return err;
+		if (emu->card_capabilities->emu1010 == 1) {
+			if ((err = snd_emu1010_load_firmware(emu, DOCK_FILENAME)) != 0) {
+				return err;
+			}
+		} else if (emu->card_capabilities->emu1010 == 2) {
+			if ((err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME)) != 0) {
+				return err;
+			}
+		} else if (emu->card_capabilities->emu1010 == 3) {
+			if ((err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME)) != 0) {
+				return err;
+			}
 		}
 		}
+
 		snd_emu1010_fpga_write(emu,  EMU_HANA_FPGA_CONFIG, 0 );
 		snd_emu1010_fpga_write(emu,  EMU_HANA_FPGA_CONFIG, 0 );
 		snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &reg );
 		snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, &reg );
 		snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_IRQ_STATUS=0x%x\n",reg);
 		snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_IRQ_STATUS=0x%x\n",reg);
 		/* ID, should read & 0x7f = 0x55 when FPGA programmed. */
 		/* ID, should read & 0x7f = 0x55 when FPGA programmed. */
 		snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
 		snd_emu1010_fpga_read(emu, EMU_HANA_ID, &reg );
 		snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_ID=0x%x\n",reg);
 		snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_ID=0x%x\n",reg);
-		if (reg != 0x55) {
+		if ((reg & 0x3f) != 0x15) {
 			/* FPGA failed to be programmed */
 			/* FPGA failed to be programmed */
 			snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware file failed, reg=0x%x\n", reg);
 			snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware file failed, reg=0x%x\n", reg);
 			return 0;
 			return 0;
@@ -1227,9 +1311,15 @@ static struct snd_emu_chip_details emu_chip_details[] = {
 	 .emu10k2_chip = 1,
 	 .emu10k2_chip = 1,
 	 .ca0108_chip = 1,
 	 .ca0108_chip = 1,
 	 .ca_cardbus_chip = 1,
 	 .ca_cardbus_chip = 1,
-	 .spi_dac = 1,
-	 .i2c_adc = 1,
-	 .spk71 = 1} ,
+	 .spk71 = 1 ,
+	 .emu1010 = 3} ,
+	{.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40041102,
+	 .driver = "Audigy2", .name = "E-mu 1010b PCI [MAEM????]", 
+	 .id = "EMU1010",
+	 .emu10k2_chip = 1,
+	 .ca0108_chip = 1,
+	 .spk71 = 1 ,
+	 .emu1010 = 2} ,
 	{.vendor = 0x1102, .device = 0x0008, 
 	{.vendor = 0x1102, .device = 0x0008, 
 	 .driver = "Audigy2", .name = "Audigy 2 Value [Unknown]", 
 	 .driver = "Audigy2", .name = "Audigy 2 Value [Unknown]", 
 	 .id = "Audigy2",
 	 .id = "Audigy2",
@@ -1663,12 +1753,13 @@ int __devinit snd_emu10k1_create(struct snd_card *card,
 	emu->fx8010.extout_mask = extout_mask;
 	emu->fx8010.extout_mask = extout_mask;
 	emu->enable_ir = enable_ir;
 	emu->enable_ir = enable_ir;
 
 
+	if (emu->card_capabilities->ca_cardbus_chip) {
+		if ((err = snd_emu10k1_cardbus_init(emu)) < 0)
+			goto error;
+	}
 	if (emu->card_capabilities->ecard) {
 	if (emu->card_capabilities->ecard) {
 		if ((err = snd_emu10k1_ecard_init(emu)) < 0)
 		if ((err = snd_emu10k1_ecard_init(emu)) < 0)
 			goto error;
 			goto error;
-	} else if (emu->card_capabilities->ca_cardbus_chip) {
-		if ((err = snd_emu10k1_cardbus_init(emu)) < 0)
-			goto error;
  	} else if (emu->card_capabilities->emu1010) {
  	} else if (emu->card_capabilities->emu1010) {
  		if ((err = snd_emu10k1_emu1010_init(emu)) < 0) {
  		if ((err = snd_emu10k1_emu1010_init(emu)) < 0) {
  			snd_emu10k1_free(emu);
  			snd_emu10k1_free(emu);
@@ -1814,10 +1905,10 @@ void snd_emu10k1_suspend_regs(struct snd_emu10k1 *emu)
 
 
 void snd_emu10k1_resume_init(struct snd_emu10k1 *emu)
 void snd_emu10k1_resume_init(struct snd_emu10k1 *emu)
 {
 {
+	if (emu->card_capabilities->ca_cardbus_chip)
+		snd_emu10k1_cardbus_init(emu);
 	if (emu->card_capabilities->ecard)
 	if (emu->card_capabilities->ecard)
 		snd_emu10k1_ecard_init(emu);
 		snd_emu10k1_ecard_init(emu);
-	else if (emu->card_capabilities->ca_cardbus_chip)
-		snd_emu10k1_cardbus_init(emu);
 	else if (emu->card_capabilities->emu1010)
 	else if (emu->card_capabilities->emu1010)
  		snd_emu10k1_emu1010_init(emu);
  		snd_emu10k1_emu1010_init(emu);
 	else
 	else

+ 76 - 2
sound/pci/emu10k1/emufx.c

@@ -1123,6 +1123,11 @@ snd_emu10k1_init_stereo_onoff_control(struct snd_emu10k1_fx8010_control_gpr *ctl
 	ctl->translation = EMU10K1_GPR_TRANSLATION_ONOFF;
 	ctl->translation = EMU10K1_GPR_TRANSLATION_ONOFF;
 }
 }
 
 
+/*
+ * Used for emu1010 - conversion from 32-bit capture inputs from HANA
+ * to 2 x 16-bit registers in audigy - their values are read via DMA.
+ * Conversion is performed by Audigy DSP instructions of FX8010.
+ */
 static int snd_emu10k1_audigy_dsp_convert_32_to_2x16(
 static int snd_emu10k1_audigy_dsp_convert_32_to_2x16(
 				struct snd_emu10k1_fx8010_code *icode,
 				struct snd_emu10k1_fx8010_code *icode,
 				u32 *ptr, int tmp, int bit_shifter16,
 				u32 *ptr, int tmp, int bit_shifter16,
@@ -1193,7 +1198,11 @@ static int __devinit _snd_emu10k1_audigy_init_efx(struct snd_emu10k1 *emu)
 	snd_emu10k1_ptr_write(emu, A_DBG, 0, (emu->fx8010.dbg = 0) | A_DBG_SINGLE_STEP);
 	snd_emu10k1_ptr_write(emu, A_DBG, 0, (emu->fx8010.dbg = 0) | A_DBG_SINGLE_STEP);
 
 
 #if 1
 #if 1
-	/* PCM front Playback Volume (independent from stereo mix) */
+	/* PCM front Playback Volume (independent from stereo mix)
+	 * playback = 0 + ( gpr * FXBUS_PCM_LEFT_FRONT >> 31)
+	 * where gpr contains attenuation from corresponding mixer control
+	 * (snd_emu10k1_init_stereo_control)
+	 */
 	A_OP(icode, &ptr, iMAC0, A_GPR(playback), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_FRONT));
 	A_OP(icode, &ptr, iMAC0, A_GPR(playback), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_FRONT));
 	A_OP(icode, &ptr, iMAC0, A_GPR(playback+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_FRONT));
 	A_OP(icode, &ptr, iMAC0, A_GPR(playback+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_FRONT));
 	snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Front Playback Volume", gpr, 100);
 	snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Front Playback Volume", gpr, 100);
@@ -1549,7 +1558,7 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
 
 
 	if (emu->card_capabilities->emu1010) {
 	if (emu->card_capabilities->emu1010) {
 		snd_printk("EMU inputs on\n");
 		snd_printk("EMU inputs on\n");
-		/* Capture 8 channels of S32_LE sound */
+		/* Capture 16 (originally 8) channels of S32_LE sound */
 		
 		
 		/* printk("emufx.c: gpr=0x%x, tmp=0x%x\n",gpr, tmp); */
 		/* printk("emufx.c: gpr=0x%x, tmp=0x%x\n",gpr, tmp); */
 		/* For the EMU1010: How to get 32bit values from the DSP. High 16bits into L, low 16bits into R. */
 		/* For the EMU1010: How to get 32bit values from the DSP. High 16bits into L, low 16bits into R. */
@@ -1560,6 +1569,11 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
 		snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_P16VIN(0x0), A_FXBUS2(0) );
 		snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_P16VIN(0x0), A_FXBUS2(0) );
 		/* Right ADC in 1 of 2 */
 		/* Right ADC in 1 of 2 */
 		gpr_map[gpr++] = 0x00000000;
 		gpr_map[gpr++] = 0x00000000;
+		/* Delaying by one sample: instead of copying the input
+		 * value A_P16VIN to output A_FXBUS2 as in the first channel,
+		 * we use an auxiliary register, delaying the value by one
+		 * sample
+		 */
 		snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(2) );
 		snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(2) );
 		A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x1), A_C_00000000, A_C_00000000);
 		A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x1), A_C_00000000, A_C_00000000);
 		gpr_map[gpr++] = 0x00000000;
 		gpr_map[gpr++] = 0x00000000;
@@ -1583,6 +1597,66 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
 		gpr_map[gpr++] = 0x00000000;
 		gpr_map[gpr++] = 0x00000000;
 		snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xe) );
 		snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xe) );
 		A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x7), A_C_00000000, A_C_00000000);
 		A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x7), A_C_00000000, A_C_00000000);
+		/* Pavel Hofman - we still have voices, A_FXBUS2s, and
+		 * A_P16VINs available -
+		 * let's add 8 more capture channels - total of 16
+		 */
+		gpr_map[gpr++] = 0x00000000;
+		snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
+							  bit_shifter16,
+							  A_GPR(gpr - 1),
+							  A_FXBUS2(0x10));
+		A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x8),
+		     A_C_00000000, A_C_00000000);
+		gpr_map[gpr++] = 0x00000000;
+		snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
+							  bit_shifter16,
+							  A_GPR(gpr - 1),
+							  A_FXBUS2(0x12));
+		A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x9),
+		     A_C_00000000, A_C_00000000);
+		gpr_map[gpr++] = 0x00000000;
+		snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
+							  bit_shifter16,
+							  A_GPR(gpr - 1),
+							  A_FXBUS2(0x14));
+		A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xa),
+		     A_C_00000000, A_C_00000000);
+		gpr_map[gpr++] = 0x00000000;
+		snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
+							  bit_shifter16,
+							  A_GPR(gpr - 1),
+							  A_FXBUS2(0x16));
+		A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xb),
+		     A_C_00000000, A_C_00000000);
+		gpr_map[gpr++] = 0x00000000;
+		snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
+							  bit_shifter16,
+							  A_GPR(gpr - 1),
+							  A_FXBUS2(0x18));
+		A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xc),
+		     A_C_00000000, A_C_00000000);
+		gpr_map[gpr++] = 0x00000000;
+		snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
+							  bit_shifter16,
+							  A_GPR(gpr - 1),
+							  A_FXBUS2(0x1a));
+		A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xd),
+		     A_C_00000000, A_C_00000000);
+		gpr_map[gpr++] = 0x00000000;
+		snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
+							  bit_shifter16,
+							  A_GPR(gpr - 1),
+							  A_FXBUS2(0x1c));
+		A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xe),
+		     A_C_00000000, A_C_00000000);
+		gpr_map[gpr++] = 0x00000000;
+		snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
+							  bit_shifter16,
+							  A_GPR(gpr - 1),
+							  A_FXBUS2(0x1e));
+		A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xf),
+		     A_C_00000000, A_C_00000000);
 
 
 #if 0
 #if 0
 		for (z = 4; z < 8; z++) {
 		for (z = 4; z < 8; z++) {

+ 16 - 0
sound/pci/emu10k1/emumixer.c

@@ -77,6 +77,10 @@ static int snd_emu10k1_spdif_get_mask(struct snd_kcontrol *kcontrol,
 	return 0;
 	return 0;
 }
 }
 
 
+/*
+ * Items labels in enum mixer controls assigning source data to
+ * each destination
+ */
 static char *emu1010_src_texts[] = { 
 static char *emu1010_src_texts[] = { 
 	"Silence",
 	"Silence",
 	"Dock Mic A",
 	"Dock Mic A",
@@ -133,6 +137,9 @@ static char *emu1010_src_texts[] = {
 	"DSP 31",
 	"DSP 31",
 };
 };
 
 
+/*
+ * List of data sources available for each destination
+ */
 static unsigned int emu1010_src_regs[] = {
 static unsigned int emu1010_src_regs[] = {
 	EMU_SRC_SILENCE,/* 0 */
 	EMU_SRC_SILENCE,/* 0 */
 	EMU_SRC_DOCK_MIC_A1, /* 1 */
 	EMU_SRC_DOCK_MIC_A1, /* 1 */
@@ -189,6 +196,10 @@ static unsigned int emu1010_src_regs[] = {
 	EMU_SRC_ALICE_EMU32B+0xf, /* 52 */
 	EMU_SRC_ALICE_EMU32B+0xf, /* 52 */
 };
 };
 
 
+/*
+ * Data destinations - physical EMU outputs.
+ * Each destination has an enum mixer control to choose a data source
+ */
 static unsigned int emu1010_output_dst[] = {
 static unsigned int emu1010_output_dst[] = {
 	EMU_DST_DOCK_DAC1_LEFT1, /* 0 */
 	EMU_DST_DOCK_DAC1_LEFT1, /* 0 */
 	EMU_DST_DOCK_DAC1_RIGHT1, /* 1 */
 	EMU_DST_DOCK_DAC1_RIGHT1, /* 1 */
@@ -216,6 +227,11 @@ static unsigned int emu1010_output_dst[] = {
 	EMU_DST_HANA_ADAT+7, /* 23 */
 	EMU_DST_HANA_ADAT+7, /* 23 */
 };
 };
 
 
+/*
+ * Data destinations - HANA outputs going to Alice2 (audigy) for
+ *   capture (EMU32 + I2S links)
+ * Each destination has an enum mixer control to choose a data source
+ */
 static unsigned int emu1010_input_dst[] = {
 static unsigned int emu1010_input_dst[] = {
 	EMU_DST_ALICE2_EMU32_0,
 	EMU_DST_ALICE2_EMU32_0,
 	EMU_DST_ALICE2_EMU32_1,
 	EMU_DST_ALICE2_EMU32_1,

+ 28 - 11
sound/pci/emu10k1/emupcm.c

@@ -1233,24 +1233,26 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream)
 	runtime->hw.rate_min = runtime->hw.rate_max = 48000;
 	runtime->hw.rate_min = runtime->hw.rate_max = 48000;
 	spin_lock_irq(&emu->reg_lock);
 	spin_lock_irq(&emu->reg_lock);
 	if (emu->card_capabilities->emu1010) {
 	if (emu->card_capabilities->emu1010) {
-		/* TODO 
+		/*  Nb. of channels has been increased to 16 */
+		/* TODO
 		 * SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE
 		 * SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE
 		 * SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
 		 * SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
 		 * SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
 		 * SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
 		 * SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000
 		 * SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000
 		 * rate_min = 44100,
 		 * rate_min = 44100,
 		 * rate_max = 192000,
 		 * rate_max = 192000,
-		 * channels_min = 8,
-		 * channels_max = 8,
+		 * channels_min = 16,
+		 * channels_max = 16,
 		 * Need to add mixer control to fix sample rate
 		 * Need to add mixer control to fix sample rate
 		 *                 
 		 *                 
-		 * There are 16 mono channels of 16bits each.
+		 * There are 32 mono channels of 16bits each.
 		 * 24bit Audio uses 2x channels over 16bit
 		 * 24bit Audio uses 2x channels over 16bit
 		 * 96kHz uses 2x channels over 48kHz
 		 * 96kHz uses 2x channels over 48kHz
 		 * 192kHz uses 4x channels over 48kHz
 		 * 192kHz uses 4x channels over 48kHz
-		 * So, for 48kHz 24bit, one has 8 channels
-		 * for 96kHz 24bit, one has 4 channels
-		 * for 192kHz 24bit, one has 2 channels
+		 * So, for 48kHz 24bit, one has 16 channels
+		 * for 96kHz 24bit, one has 8 channels
+		 * for 192kHz 24bit, one has 4 channels
+		 *
 		 */
 		 */
 #if 1
 #if 1
 		switch (emu->emu1010.internal_clock) {
 		switch (emu->emu1010.internal_clock) {
@@ -1258,13 +1260,15 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream)
 			/* For 44.1kHz */
 			/* For 44.1kHz */
 			runtime->hw.rates = SNDRV_PCM_RATE_44100;
 			runtime->hw.rates = SNDRV_PCM_RATE_44100;
 			runtime->hw.rate_min = runtime->hw.rate_max = 44100;
 			runtime->hw.rate_min = runtime->hw.rate_max = 44100;
-			runtime->hw.channels_min = runtime->hw.channels_max = 8;
+			runtime->hw.channels_min =
+				runtime->hw.channels_max = 16;
 			break;
 			break;
 		case 1:
 		case 1:
 			/* For 48kHz */
 			/* For 48kHz */
 			runtime->hw.rates = SNDRV_PCM_RATE_48000;
 			runtime->hw.rates = SNDRV_PCM_RATE_48000;
 			runtime->hw.rate_min = runtime->hw.rate_max = 48000;
 			runtime->hw.rate_min = runtime->hw.rate_max = 48000;
-			runtime->hw.channels_min = runtime->hw.channels_max = 8;
+			runtime->hw.channels_min =
+				runtime->hw.channels_max = 16;
 			break;
 			break;
 		};
 		};
 #endif
 #endif
@@ -1282,7 +1286,7 @@ static int snd_emu10k1_capture_efx_open(struct snd_pcm_substream *substream)
 #endif
 #endif
 		runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
 		runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
 		/* efx_voices_mask[0] is expected to be zero
 		/* efx_voices_mask[0] is expected to be zero
- 		 * efx_voices_mask[1] is expected to have 16bits set
+ 		 * efx_voices_mask[1] is expected to have 32bits set
 		 */
 		 */
 	} else {
 	} else {
 		runtime->hw.channels_min = runtime->hw.channels_max = 0;
 		runtime->hw.channels_min = runtime->hw.channels_max = 0;
@@ -1787,11 +1791,24 @@ int __devinit snd_emu10k1_pcm_efx(struct snd_emu10k1 * emu, int device, struct s
 	/* emu->efx_voices_mask[0] = FXWC_DEFAULTROUTE_C | FXWC_DEFAULTROUTE_A; */
 	/* emu->efx_voices_mask[0] = FXWC_DEFAULTROUTE_C | FXWC_DEFAULTROUTE_A; */
 	if (emu->audigy) {
 	if (emu->audigy) {
 		emu->efx_voices_mask[0] = 0;
 		emu->efx_voices_mask[0] = 0;
-		emu->efx_voices_mask[1] = 0xffff;
+		if (emu->card_capabilities->emu1010)
+			/* Pavel Hofman - 32 voices will be used for
+			 * capture (write mode) -
+			 * each bit = corresponding voice
+			 */
+			emu->efx_voices_mask[1] = 0xffffffff;
+		else
+			emu->efx_voices_mask[1] = 0xffff;
 	} else {
 	} else {
 		emu->efx_voices_mask[0] = 0xffff0000;
 		emu->efx_voices_mask[0] = 0xffff0000;
 		emu->efx_voices_mask[1] = 0;
 		emu->efx_voices_mask[1] = 0;
 	}
 	}
+	/* For emu1010, the control has to set 32 upper bits (voices)
+	 * out of the 64 bits (voices) to true for the 16-channels capture
+	 * to work correctly. Correct A_FXWC2 initial value (0xffffffff)
+	 * is already defined but the snd_emu10k1_pcm_efx_voices_mask
+	 * control can override this register's value.
+	 */
 	kctl = snd_ctl_new1(&snd_emu10k1_pcm_efx_voices_mask, emu);
 	kctl = snd_ctl_new1(&snd_emu10k1_pcm_efx_voices_mask, emu);
 	if (!kctl)
 	if (!kctl)
 		return -ENOMEM;
 		return -ENOMEM;

+ 2 - 2
sound/pci/ens1370.c

@@ -1607,8 +1607,8 @@ struct es1371_quirk {
 	unsigned char rev;		/* revision */
 	unsigned char rev;		/* revision */
 };
 };
 
 
-static int __devinit es1371_quirk_lookup(struct ensoniq *ensoniq,
-					 struct es1371_quirk *list)
+static int es1371_quirk_lookup(struct ensoniq *ensoniq,
+				struct es1371_quirk *list)
 {
 {
 	while (list->vid != (unsigned short)PCI_ANY_ID) {
 	while (list->vid != (unsigned short)PCI_ANY_ID) {
 		if (ensoniq->pci->vendor == list->vid &&
 		if (ensoniq->pci->vendor == list->vid &&

+ 28 - 25
sound/pci/hda/hda_intel.c

@@ -341,6 +341,9 @@ struct azx {
 	unsigned int single_cmd :1;
 	unsigned int single_cmd :1;
 	unsigned int polling_mode :1;
 	unsigned int polling_mode :1;
 	unsigned int msi :1;
 	unsigned int msi :1;
+
+	/* for debugging */
+	unsigned int last_cmd;	/* last issued command (to sync) */
 };
 };
 
 
 /* driver types */
 /* driver types */
@@ -466,18 +469,10 @@ 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, hda_nid_t nid, int direct,
-			     unsigned int verb, unsigned int para)
+static int azx_corb_send_cmd(struct hda_codec *codec, u32 val)
 {
 {
 	struct azx *chip = codec->bus->private_data;
 	struct azx *chip = codec->bus->private_data;
 	unsigned int wp;
 	unsigned int wp;
-	u32 val;
-
-	val = (u32)(codec->addr & 0x0f) << 28;
-	val |= (u32)direct << 27;
-	val |= (u32)nid << 20;
-	val |= verb << 8;
-	val |= para;
 
 
 	/* add command to corb */
 	/* add command to corb */
 	wp = azx_readb(chip, CORBWP);
 	wp = azx_readb(chip, CORBWP);
@@ -538,12 +533,12 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
 		}
 		}
 		if (! chip->rirb.cmds)
 		if (! chip->rirb.cmds)
 			return chip->rirb.res; /* the last value */
 			return chip->rirb.res; /* the last value */
-		schedule_timeout_interruptible(1);
+		schedule_timeout(1);
 	} while (time_after_eq(timeout, jiffies));
 	} while (time_after_eq(timeout, jiffies));
 
 
 	if (chip->msi) {
 	if (chip->msi) {
 		snd_printk(KERN_WARNING "hda_intel: No response from codec, "
 		snd_printk(KERN_WARNING "hda_intel: No response from codec, "
-			   "disabling MSI...\n");
+			   "disabling MSI: last cmd=0x%08x\n", chip->last_cmd);
 		free_irq(chip->irq, chip);
 		free_irq(chip->irq, chip);
 		chip->irq = -1;
 		chip->irq = -1;
 		pci_disable_msi(chip->pci);
 		pci_disable_msi(chip->pci);
@@ -555,13 +550,15 @@ static unsigned int azx_rirb_get_response(struct hda_codec *codec)
 
 
 	if (!chip->polling_mode) {
 	if (!chip->polling_mode) {
 		snd_printk(KERN_WARNING "hda_intel: azx_get_response timeout, "
 		snd_printk(KERN_WARNING "hda_intel: azx_get_response timeout, "
-			   "switching to polling mode...\n");
+			   "switching to polling mode: last cmd=0x%08x\n",
+			   chip->last_cmd);
 		chip->polling_mode = 1;
 		chip->polling_mode = 1;
 		goto again;
 		goto again;
 	}
 	}
 
 
 	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...\n");
+		   "switching to single_cmd mode: last cmd=0x%08x\n",
+		   chip->last_cmd);
 	chip->rirb.rp = azx_readb(chip, RIRBWP);
 	chip->rirb.rp = azx_readb(chip, RIRBWP);
 	chip->rirb.cmds = 0;
 	chip->rirb.cmds = 0;
 	/* switch to single_cmd mode */
 	/* switch to single_cmd mode */
@@ -581,20 +578,11 @@ 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, hda_nid_t nid,
-			       int direct, unsigned int verb,
-			       unsigned int para)
+static int azx_single_send_cmd(struct hda_codec *codec, u32 val)
 {
 {
 	struct azx *chip = codec->bus->private_data;
 	struct azx *chip = codec->bus->private_data;
-	u32 val;
 	int timeout = 50;
 	int timeout = 50;
 
 
-	val = (u32)(codec->addr & 0x0f) << 28;
-	val |= (u32)direct << 27;
-	val |= (u32)nid << 20;
-	val |= verb << 8;
-	val |= para;
-
 	while (timeout--) {
 	while (timeout--) {
 		/* check ICB busy bit */
 		/* check ICB busy bit */
 		if (! (azx_readw(chip, IRS) & ICH6_IRS_BUSY)) {
 		if (! (azx_readw(chip, IRS) & ICH6_IRS_BUSY)) {
@@ -639,10 +627,19 @@ static int azx_send_cmd(struct hda_codec *codec, hda_nid_t nid,
 			unsigned int para)
 			unsigned int para)
 {
 {
 	struct azx *chip = codec->bus->private_data;
 	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;
+
 	if (chip->single_cmd)
 	if (chip->single_cmd)
-		return azx_single_send_cmd(codec, nid, direct, verb, para);
+		return azx_single_send_cmd(codec, val);
 	else
 	else
-		return azx_corb_send_cmd(codec, nid, direct, verb, para);
+		return azx_corb_send_cmd(codec, val);
 }
 }
 
 
 /* get a response */
 /* get a response */
@@ -1788,6 +1785,12 @@ static struct pci_device_id azx_ids[] = {
 	{ 0x10de, 0x044b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP65 */
 	{ 0x10de, 0x044b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP65 */
 	{ 0x10de, 0x055c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP67 */
 	{ 0x10de, 0x055c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP67 */
 	{ 0x10de, 0x055d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP67 */
 	{ 0x10de, 0x055d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP67 */
+	{ 0x10de, 0x07fc, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP73 */
+	{ 0x10de, 0x07fd, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP73 */
+	{ 0x10de, 0x0774, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */
+	{ 0x10de, 0x0775, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */
+	{ 0x10de, 0x0776, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */
+	{ 0x10de, 0x0777, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */
 	{ 0, }
 	{ 0, }
 };
 };
 MODULE_DEVICE_TABLE(pci, azx_ids);
 MODULE_DEVICE_TABLE(pci, azx_ids);

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

@@ -250,6 +250,12 @@ static void print_codec_info(struct snd_info_entry *entry, struct snd_info_buffe
 	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);
 	snd_iprintf(buffer, "Revision Id: 0x%x\n", codec->revision_id);
 	snd_iprintf(buffer, "Revision Id: 0x%x\n", codec->revision_id);
+
+	if (codec->mfg)
+		snd_iprintf(buffer, "Modem Function Group: 0x%x\n", codec->mfg);
+	else
+		snd_iprintf(buffer, "No Modem Function Group found\n");
+
 	if (! codec->afg)
 	if (! codec->afg)
 		return;
 		return;
 	snd_iprintf(buffer, "Default PCM:\n");
 	snd_iprintf(buffer, "Default PCM:\n");

+ 627 - 3
sound/pci/hda/patch_analog.c

@@ -1,7 +1,8 @@
 /*
 /*
- * HD audio interface patch for AD1981HD, AD1983, AD1986A, AD1988
+ * HD audio interface patch for AD1882, AD1884, AD1981HD, AD1983, AD1984,
+ *   AD1986A, AD1988
  *
  *
- * Copyright (c) 2005 Takashi Iwai <tiwai@suse.de>
+ * Copyright (c) 2005-2007 Takashi Iwai <tiwai@suse.de>
  *
  *
  *  This driver is free software; you can redistribute it and/or modify
  *  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
  *  it under the terms of the GNU General Public License as published by
@@ -61,7 +62,7 @@ struct ad198x_spec {
 	int num_channel_mode;
 	int num_channel_mode;
 
 
 	/* PCM information */
 	/* PCM information */
-	struct hda_pcm pcm_rec[2];	/* used in alc_build_pcms() */
+	struct hda_pcm pcm_rec[3];	/* used in alc_build_pcms() */
 
 
 	struct mutex amp_mutex;	/* PCM volume/mute control mutex */
 	struct mutex amp_mutex;	/* PCM volume/mute control mutex */
 	unsigned int spdif_route;
 	unsigned int spdif_route;
@@ -2774,12 +2775,635 @@ static int patch_ad1988(struct hda_codec *codec)
 }
 }
 
 
 
 
+/*
+ * AD1884 / AD1984
+ *
+ * port-B - front line/mic-in
+ * port-E - aux in/out
+ * port-F - aux in/out
+ * port-C - rear line/mic-in
+ * port-D - rear line/hp-out
+ * port-A - front line/hp-out
+ *
+ * AD1984 = AD1884 + two digital mic-ins
+ *
+ * FIXME:
+ * For simplicity, we share the single DAC for both HP and line-outs
+ * right now.  The inidividual playbacks could be easily implemented,
+ * but no build-up framework is given, so far.
+ */
+
+static hda_nid_t ad1884_dac_nids[1] = {
+	0x04,
+};
+
+static hda_nid_t ad1884_adc_nids[2] = {
+	0x08, 0x09,
+};
+
+static hda_nid_t ad1884_capsrc_nids[2] = {
+	0x0c, 0x0d,
+};
+
+#define AD1884_SPDIF_OUT	0x02
+
+static struct hda_input_mux ad1884_capture_source = {
+	.num_items = 4,
+	.items = {
+		{ "Front Mic", 0x0 },
+		{ "Mic", 0x1 },
+		{ "CD", 0x2 },
+		{ "Mix", 0x3 },
+	},
+};
+
+static struct snd_kcontrol_new ad1884_base_mixers[] = {
+	HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
+	/* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
+	/*
+	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT),
+	HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT),
+	HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Digital Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
+	*/
+	HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* The multiple "Capture Source" controls confuse alsamixer
+		 * So call somewhat different..
+		 * FIXME: the controls appear in the "playback" view!
+		 */
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 2,
+		.info = ad198x_mux_enum_info,
+		.get = ad198x_mux_enum_get,
+		.put = ad198x_mux_enum_put,
+	},
+	/* SPDIF controls */
+	HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
+		/* identical with ad1983 */
+		.info = ad1983_spdif_route_info,
+		.get = ad1983_spdif_route_get,
+		.put = ad1983_spdif_route_put,
+	},
+	{ } /* end */
+};
+
+static struct snd_kcontrol_new ad1984_dmic_mixers[] = {
+	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
+			     HDA_INPUT),
+	HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
+			   HDA_INPUT),
+	{ } /* end */
+};
+
+/*
+ * initialization verbs
+ */
+static struct hda_verb ad1884_init_verbs[] = {
+	/* DACs; mute as default */
+	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	/* Port-A (HP) mixer */
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	/* Port-A pin */
+	{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* HP selector - select DAC2 */
+	{0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
+	/* Port-D (Line-out) mixer */
+	{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	/* Port-D pin */
+	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Mono-out mixer */
+	{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	/* Mono-out pin */
+	{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Mono selector */
+	{0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
+	/* Port-B (front mic) pin */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Port-C (rear mic) pin */
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Analog mixer; mute as default */
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+	/* Analog Mix output amp */
+	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
+	/* SPDIF output selector */
+	{0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
+	{ } /* end */
+};
+
+static int patch_ad1884(struct hda_codec *codec)
+{
+	struct ad198x_spec *spec;
+
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	mutex_init(&spec->amp_mutex);
+	codec->spec = spec;
+
+	spec->multiout.max_channels = 2;
+	spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
+	spec->multiout.dac_nids = ad1884_dac_nids;
+	spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
+	spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
+	spec->adc_nids = ad1884_adc_nids;
+	spec->capsrc_nids = ad1884_capsrc_nids;
+	spec->input_mux = &ad1884_capture_source;
+	spec->num_mixers = 1;
+	spec->mixers[0] = ad1884_base_mixers;
+	spec->num_init_verbs = 1;
+	spec->init_verbs[0] = ad1884_init_verbs;
+	spec->spdif_route = 0;
+
+	codec->patch_ops = ad198x_patch_ops;
+
+	return 0;
+}
+
+/*
+ * Lenovo Thinkpad T61/X61
+ */
+static struct hda_input_mux ad1984_thinkpad_capture_source = {
+	.num_items = 3,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "Internal Mic", 0x1 },
+		{ "Mix", 0x3 },
+	},
+};
+
+static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
+	HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
+	/* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
+	HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
+	HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
+	HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
+	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* The multiple "Capture Source" controls confuse alsamixer
+		 * So call somewhat different..
+		 * FIXME: the controls appear in the "playback" view!
+		 */
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 2,
+		.info = ad198x_mux_enum_info,
+		.get = ad198x_mux_enum_get,
+		.put = ad198x_mux_enum_put,
+	},
+	{ } /* end */
+};
+
+/* additional verbs */
+static struct hda_verb ad1984_thinkpad_init_verbs[] = {
+	/* Port-E (docking station mic) pin */
+	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* docking mic boost */
+	{0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Analog mixer - docking mic; mute as default */
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+	/* enable EAPD bit */
+	{0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
+	{ } /* end */
+};
+
+/* Digial MIC ADC NID 0x05 + 0x06 */
+static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
+				   struct hda_codec *codec,
+				   unsigned int stream_tag,
+				   unsigned int format,
+				   struct snd_pcm_substream *substream)
+{
+	snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
+				   stream_tag, 0, format);
+	return 0;
+}
+
+static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
+				   struct hda_codec *codec,
+				   struct snd_pcm_substream *substream)
+{
+	snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
+				   0, 0, 0);
+	return 0;
+}
+
+static struct hda_pcm_stream ad1984_pcm_dmic_capture = {
+	.substreams = 2,
+	.channels_min = 2,
+	.channels_max = 2,
+	.nid = 0x05,
+	.ops = {
+		.prepare = ad1984_pcm_dmic_prepare,
+		.cleanup = ad1984_pcm_dmic_cleanup
+	},
+};
+
+static int ad1984_build_pcms(struct hda_codec *codec)
+{
+	struct ad198x_spec *spec = codec->spec;
+	struct hda_pcm *info;
+	int err;
+
+	err = ad198x_build_pcms(codec);
+	if (err < 0)
+		return err;
+
+	info = spec->pcm_rec + codec->num_pcms;
+	codec->num_pcms++;
+	info->name = "AD1984 Digital Mic";
+	info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
+	return 0;
+}
+
+/* models */
+enum {
+	AD1984_BASIC,
+	AD1984_THINKPAD,
+	AD1984_MODELS
+};
+
+static const char *ad1984_models[AD1984_MODELS] = {
+	[AD1984_BASIC]		= "basic",
+	[AD1984_THINKPAD]	= "thinkpad",
+};
+
+static struct snd_pci_quirk ad1984_cfg_tbl[] = {
+	/* Lenovo Thinkpad T61/X61 */
+	SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1984_THINKPAD),
+	{}
+};
+
+static int patch_ad1984(struct hda_codec *codec)
+{
+	struct ad198x_spec *spec;
+	int board_config, err;
+
+	err = patch_ad1884(codec);
+	if (err < 0)
+		return err;
+	spec = codec->spec;
+	board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
+						  ad1984_models, ad1984_cfg_tbl);
+	switch (board_config) {
+	case AD1984_BASIC:
+		/* additional digital mics */
+		spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
+		codec->patch_ops.build_pcms = ad1984_build_pcms;
+		break;
+	case AD1984_THINKPAD:
+		spec->multiout.dig_out_nid = 0;
+		spec->input_mux = &ad1984_thinkpad_capture_source;
+		spec->mixers[0] = ad1984_thinkpad_mixers;
+		spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
+		break;
+	}
+	return 0;
+}
+
+
+/*
+ * AD1882
+ *
+ * port-A - front hp-out
+ * port-B - front mic-in
+ * port-C - rear line-in, shared surr-out (3stack)
+ * port-D - rear line-out
+ * port-E - rear mic-in, shared clfe-out (3stack)
+ * port-F - rear surr-out (6stack)
+ * port-G - rear clfe-out (6stack)
+ */
+
+static hda_nid_t ad1882_dac_nids[3] = {
+	0x04, 0x03, 0x05
+};
+
+static hda_nid_t ad1882_adc_nids[2] = {
+	0x08, 0x09,
+};
+
+static hda_nid_t ad1882_capsrc_nids[2] = {
+	0x0c, 0x0d,
+};
+
+#define AD1882_SPDIF_OUT	0x02
+
+/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
+static struct hda_input_mux ad1882_capture_source = {
+	.num_items = 5,
+	.items = {
+		{ "Front Mic", 0x1 },
+		{ "Mic", 0x4 },
+		{ "Line", 0x2 },
+		{ "CD", 0x3 },
+		{ "Mix", 0x7 },
+	},
+};
+
+static struct snd_kcontrol_new ad1882_base_mixers[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
+	HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x07, HDA_INPUT),
+	HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x07, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Line-In Boost", 0x3a, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* The multiple "Capture Source" controls confuse alsamixer
+		 * So call somewhat different..
+		 * FIXME: the controls appear in the "playback" view!
+		 */
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 2,
+		.info = ad198x_mux_enum_info,
+		.get = ad198x_mux_enum_get,
+		.put = ad198x_mux_enum_put,
+	},
+	/* SPDIF controls */
+	HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
+		/* identical with ad1983 */
+		.info = ad1983_spdif_route_info,
+		.get = ad1983_spdif_route_get,
+		.put = ad1983_spdif_route_put,
+	},
+	{ } /* end */
+};
+
+static struct snd_kcontrol_new ad1882_3stack_mixers[] = {
+	HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Channel Mode",
+		.info = ad198x_ch_mode_info,
+		.get = ad198x_ch_mode_get,
+		.put = ad198x_ch_mode_put,
+	},
+	{ } /* end */
+};
+
+static struct snd_kcontrol_new ad1882_6stack_mixers[] = {
+	HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
+	{ } /* end */
+};
+
+static struct hda_verb ad1882_ch2_init[] = {
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{ } /* end */
+};
+
+static struct hda_verb ad1882_ch4_init[] = {
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{ } /* end */
+};
+
+static struct hda_verb ad1882_ch6_init[] = {
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{ } /* end */
+};
+
+static struct hda_channel_mode ad1882_modes[3] = {
+	{ 2, ad1882_ch2_init },
+	{ 4, ad1882_ch4_init },
+	{ 6, ad1882_ch6_init },
+};
+
+/*
+ * initialization verbs
+ */
+static struct hda_verb ad1882_init_verbs[] = {
+	/* DACs; mute as default */
+	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	/* Port-A (HP) mixer */
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	/* Port-A pin */
+	{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* HP selector - select DAC2 */
+	{0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
+	/* Port-D (Line-out) mixer */
+	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	/* Port-D pin */
+	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Mono-out mixer */
+	{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	/* Mono-out pin */
+	{0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Port-B (front mic) pin */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
+	/* Port-C (line-in) pin */
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
+	/* Port-C mixer - mute as input */
+	{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	/* Port-E (mic-in) pin */
+	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
+	/* Port-E mixer - mute as input */
+	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	/* Port-F (surround) */
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Port-G (CLFE) */
+	{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Analog mixer; mute as default */
+	/* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
+	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
+	/* Analog Mix output amp */
+	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
+	/* SPDIF output selector */
+	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
+	{0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
+	{ } /* end */
+};
+
+/* models */
+enum {
+	AD1882_3STACK,
+	AD1882_6STACK,
+	AD1882_MODELS
+};
+
+static const char *ad1882_models[AD1986A_MODELS] = {
+	[AD1882_3STACK]		= "3stack",
+	[AD1882_6STACK]		= "6stack",
+};
+
+
+static int patch_ad1882(struct hda_codec *codec)
+{
+	struct ad198x_spec *spec;
+	int board_config;
+
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	mutex_init(&spec->amp_mutex);
+	codec->spec = spec;
+
+	spec->multiout.max_channels = 6;
+	spec->multiout.num_dacs = 3;
+	spec->multiout.dac_nids = ad1882_dac_nids;
+	spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
+	spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
+	spec->adc_nids = ad1882_adc_nids;
+	spec->capsrc_nids = ad1882_capsrc_nids;
+	spec->input_mux = &ad1882_capture_source;
+	spec->num_mixers = 1;
+	spec->mixers[0] = ad1882_base_mixers;
+	spec->num_init_verbs = 1;
+	spec->init_verbs[0] = ad1882_init_verbs;
+	spec->spdif_route = 0;
+
+	codec->patch_ops = ad198x_patch_ops;
+
+	/* override some parameters */
+	board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
+						  ad1882_models, NULL);
+	switch (board_config) {
+	default:
+	case AD1882_3STACK:
+		spec->num_mixers = 2;
+		spec->mixers[1] = ad1882_3stack_mixers;
+		spec->channel_mode = ad1882_modes;
+		spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
+		spec->need_dac_fix = 1;
+		spec->multiout.max_channels = 2;
+		spec->multiout.num_dacs = 1;
+		break;
+	case AD1882_6STACK:
+		spec->num_mixers = 2;
+		spec->mixers[1] = ad1882_6stack_mixers;
+		break;
+	}
+	return 0;
+}
+
+
 /*
 /*
  * patch entries
  * patch entries
  */
  */
 struct hda_codec_preset snd_hda_preset_analog[] = {
 struct hda_codec_preset snd_hda_preset_analog[] = {
+	{ .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
+	{ .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
 	{ .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
 	{ .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
 	{ .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
 	{ .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
+	{ .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
 	{ .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
 	{ .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
 	{ .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
 	{ .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
 	{ .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
 	{ .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },

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

@@ -172,6 +172,7 @@ static int patch_atihdmi(struct hda_codec *codec)
  */
  */
 struct hda_codec_preset snd_hda_preset_atihdmi[] = {
 struct hda_codec_preset snd_hda_preset_atihdmi[] = {
 	{ .id = 0x1002793c, .name = "ATI RS600 HDMI", .patch = patch_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 = 0x1002791a, .name = "ATI RS690/780 HDMI", .patch = patch_atihdmi },
 	{ .id = 0x1002aa01, .name = "ATI R600 HDMI", .patch = patch_atihdmi },
 	{ .id = 0x1002aa01, .name = "ATI R600 HDMI", .patch = patch_atihdmi },
 	{} /* terminator */
 	{} /* terminator */

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

@@ -801,7 +801,9 @@ static struct snd_pci_quirk cxt5045_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV Series", CXT5045_LAPTOP),
 	SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV Series", CXT5045_LAPTOP),
 	SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2120", CXT5045_LAPTOP),
 	SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2120", CXT5045_LAPTOP),
 	SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP),
 	SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP),
+	SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP),
 	SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_FUJITSU),
 	SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_FUJITSU),
+	SND_PCI_QUIRK(0x1734, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP),
 	SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP),
 	SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP),
 	{}
 	{}
 };
 };

+ 917 - 44
sound/pci/hda/patch_realtek.c

@@ -94,10 +94,18 @@ enum {
 	ALC262_HP_BPC_D7000_WF,
 	ALC262_HP_BPC_D7000_WF,
 	ALC262_BENQ_ED8,
 	ALC262_BENQ_ED8,
 	ALC262_SONY_ASSAMD,
 	ALC262_SONY_ASSAMD,
+	ALC262_BENQ_T31,
 	ALC262_AUTO,
 	ALC262_AUTO,
 	ALC262_MODEL_LAST /* last tag */
 	ALC262_MODEL_LAST /* last tag */
 };
 };
 
 
+/* ALC268 models */
+enum {
+	ALC268_3ST,
+	ALC268_AUTO,
+	ALC268_MODEL_LAST /* last tag */
+};
+
 /* ALC861 models */
 /* ALC861 models */
 enum {
 enum {
 	ALC861_3ST,
 	ALC861_3ST,
@@ -115,6 +123,7 @@ enum {
 /* ALC861-VD models */
 /* ALC861-VD models */
 enum {
 enum {
 	ALC660VD_3ST,
 	ALC660VD_3ST,
+	ALC660VD_3ST_DIG,
 	ALC861VD_3ST,
 	ALC861VD_3ST,
 	ALC861VD_3ST_DIG,
 	ALC861VD_3ST_DIG,
 	ALC861VD_6ST_DIG,
 	ALC861VD_6ST_DIG,
@@ -144,6 +153,7 @@ enum {
 	ALC882_TARGA,
 	ALC882_TARGA,
 	ALC882_ASUS_A7J,
 	ALC882_ASUS_A7J,
 	ALC885_MACPRO,
 	ALC885_MACPRO,
+	ALC885_IMAC24,
 	ALC882_AUTO,
 	ALC882_AUTO,
 	ALC882_MODEL_LAST,
 	ALC882_MODEL_LAST,
 };
 };
@@ -163,6 +173,8 @@ enum {
 	ALC883_LENOVO_101E_2ch,
 	ALC883_LENOVO_101E_2ch,
 	ALC883_LENOVO_NB0763,
 	ALC883_LENOVO_NB0763,
 	ALC888_LENOVO_MS7195_DIG,		
 	ALC888_LENOVO_MS7195_DIG,		
+	ALC888_6ST_HP,
+	ALC888_3ST_HP,
 	ALC883_AUTO,
 	ALC883_AUTO,
 	ALC883_MODEL_LAST,
 	ALC883_MODEL_LAST,
 };
 };
@@ -712,6 +724,38 @@ static void alc_subsystem_id(struct hda_codec *codec,
 	}
 	}
 }
 }
 
 
+/*
+ * Fix-up pin default configurations
+ */
+
+struct alc_pincfg {
+	hda_nid_t nid;
+	u32 val;
+};
+
+static void alc_fix_pincfg(struct hda_codec *codec,
+			   const struct snd_pci_quirk *quirk,
+			   const struct alc_pincfg **pinfix)
+{
+	const struct alc_pincfg *cfg;
+
+	quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk);
+	if (!quirk)
+		return;
+
+	cfg = pinfix[quirk->value];
+	for (; cfg->nid; cfg++) {
+		int i;
+		u32 val = cfg->val;
+		for (i = 0; i < 4; i++) {
+			snd_hda_codec_write(codec, cfg->nid, 0,
+				    AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 + i,
+				    val & 0xff);
+			val >>= 8;
+		}
+	}
+}
+
 /*
 /*
  * ALC880 3-stack model
  * ALC880 3-stack model
  *
  *
@@ -1878,31 +1922,53 @@ static void alc880_lg_unsol_event(struct hda_codec *codec, unsigned int res)
  * Pin assignment:
  * Pin assignment:
  *   Speaker-out: 0x14
  *   Speaker-out: 0x14
  *   Mic-In: 0x18
  *   Mic-In: 0x18
- *   Built-in Mic-In: 0x19 (?)
- *   HP-Out: 0x1b
+ *   Built-in Mic-In: 0x19
+ *   Line-In: 0x1b
+ *   HP-Out: 0x1a
  *   SPDIF-Out: 0x1e
  *   SPDIF-Out: 0x1e
  */
  */
 
 
-/* seems analog CD is not working */
 static struct hda_input_mux alc880_lg_lw_capture_source = {
 static struct hda_input_mux alc880_lg_lw_capture_source = {
-	.num_items = 2,
+	.num_items = 3,
 	.items = {
 	.items = {
 		{ "Mic", 0x0 },
 		{ "Mic", 0x0 },
 		{ "Internal Mic", 0x1 },
 		{ "Internal Mic", 0x1 },
+		{ "Line In", 0x2 },
 	},
 	},
 };
 };
 
 
+#define alc880_lg_lw_modes alc880_threestack_modes
+
 static struct snd_kcontrol_new alc880_lg_lw_mixer[] = {
 static struct snd_kcontrol_new alc880_lg_lw_mixer[] = {
-	HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
-	HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT),
+	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", 0x0f, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Surround Playback Switch", 0x0f, 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("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 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("Internal Mic Playback Volume", 0x0b, 0x01, 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_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Channel Mode",
+		.info = alc_ch_mode_info,
+		.get = alc_ch_mode_get,
+		.put = alc_ch_mode_put,
+	},
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
 static struct hda_verb alc880_lg_lw_init_verbs[] = {
 static struct hda_verb alc880_lg_lw_init_verbs[] = {
+	{0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
+	{0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */
+	{0x12, AC_VERB_SET_CONNECT_SEL, 0x03}, /* line/surround */
+
 	/* set capture source to mic-in */
 	/* set capture source to mic-in */
 	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
@@ -1912,7 +1978,6 @@ static struct hda_verb alc880_lg_lw_init_verbs[] = {
 	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
 	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 	/* HP-out */
 	/* HP-out */
-	{0x13, AC_VERB_SET_CONNECT_SEL, 0x00},
 	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
 	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 	/* mic-in to input */
 	/* mic-in to input */
@@ -2856,11 +2921,11 @@ static struct alc_config_preset alc880_presets[] = {
 		.mixers = { alc880_lg_lw_mixer },
 		.mixers = { alc880_lg_lw_mixer },
 		.init_verbs = { alc880_volume_init_verbs,
 		.init_verbs = { alc880_volume_init_verbs,
 				alc880_lg_lw_init_verbs },
 				alc880_lg_lw_init_verbs },
-		.num_dacs = 1,
+		.num_dacs = ARRAY_SIZE(alc880_dac_nids),
 		.dac_nids = alc880_dac_nids,
 		.dac_nids = alc880_dac_nids,
 		.dig_out_nid = ALC880_DIGOUT_NID,
 		.dig_out_nid = ALC880_DIGOUT_NID,
-		.num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
-		.channel_mode = alc880_2_jack_modes,
+		.num_channel_mode = ARRAY_SIZE(alc880_lg_lw_modes),
+		.channel_mode = alc880_lg_lw_modes,
 		.input_mux = &alc880_lg_lw_capture_source,
 		.input_mux = &alc880_lg_lw_capture_source,
 		.unsol_event = alc880_lg_lw_unsol_event,
 		.unsol_event = alc880_lg_lw_unsol_event,
 		.init_hook = alc880_lg_lw_automute,
 		.init_hook = alc880_lg_lw_automute,
@@ -5054,6 +5119,60 @@ static struct hda_verb alc882_macpro_init_verbs[] = {
 	{ }
 	{ }
 };
 };
 
 
+/* iMac 24 mixer. */
+static struct snd_kcontrol_new alc885_imac24_mixer[] = {
+	HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Master Playback Switch", 0x0c, 0x00, HDA_INPUT),
+	{ } /* end */
+};
+
+/* iMac 24 init verbs. */
+static struct hda_verb alc885_imac24_init_verbs[] = {
+	/* Internal speakers: output 0 (0x0c) */
+	{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, 0x00},
+	/* Internal speakers: output 0 (0x0c) */
+	{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, 0x00},
+	/* Headphone: output 0 (0x0c) */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+	/* Front Mic: input vref at 80% */
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{ }
+};
+
+/* Toggle speaker-output according to the hp-jack state */
+static void alc885_imac24_automute(struct hda_codec *codec)
+{
+ 	unsigned int present;
+
+ 	present = snd_hda_codec_read(codec, 0x14, 0,
+				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+	snd_hda_codec_amp_update(codec, 0x18, 0, HDA_OUTPUT, 0,
+				 0x80, present ? 0x80 : 0);
+	snd_hda_codec_amp_update(codec, 0x18, 1, HDA_OUTPUT, 0,
+				 0x80, present ? 0x80 : 0);
+	snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
+				 0x80, present ? 0x80 : 0);
+	snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
+				 0x80, present ? 0x80 : 0);
+}
+
+/* Processes unsolicited events. */
+static void alc885_imac24_unsol_event(struct hda_codec *codec,
+				      unsigned int res)
+{
+	/* Headphone insertion or removal. */
+	if ((res >> 26) == ALC880_HP_EVENT)
+		alc885_imac24_automute(codec);
+}
+
 static struct hda_verb alc882_targa_verbs[] = {
 static struct hda_verb alc882_targa_verbs[] = {
 	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
 	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
@@ -5274,6 +5393,7 @@ static const char *alc882_models[ALC882_MODEL_LAST] = {
 	[ALC882_ARIMA]		= "arima",
 	[ALC882_ARIMA]		= "arima",
 	[ALC882_W2JC]		= "w2jc",
 	[ALC882_W2JC]		= "w2jc",
 	[ALC885_MACPRO]		= "macpro",
 	[ALC885_MACPRO]		= "macpro",
+	[ALC885_IMAC24]		= "imac24",
 	[ALC882_AUTO]		= "auto",
 	[ALC882_AUTO]		= "auto",
 };
 };
 
 
@@ -5284,6 +5404,7 @@ static struct snd_pci_quirk alc882_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8  */
 	SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8  */
 	SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA),
 	SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA),
 	SND_PCI_QUIRK(0x1043, 0x060d, "Asus A7J", ALC882_ASUS_A7J),
 	SND_PCI_QUIRK(0x1043, 0x060d, "Asus A7J", ALC882_ASUS_A7J),
+	SND_PCI_QUIRK(0x1043, 0x817f, "Asus P5LD2", ALC882_6ST_DIG),
 	SND_PCI_QUIRK(0x1043, 0x81d8, "Asus P5WD", ALC882_6ST_DIG),
 	SND_PCI_QUIRK(0x1043, 0x81d8, "Asus P5WD", ALC882_6ST_DIG),
 	SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_W2JC),
 	SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_W2JC),
 	{}
 	{}
@@ -5345,6 +5466,19 @@ static struct alc_config_preset alc882_presets[] = {
 		.channel_mode = alc882_ch_modes,
 		.channel_mode = alc882_ch_modes,
 		.input_mux = &alc882_capture_source,
 		.input_mux = &alc882_capture_source,
 	},
 	},
+	[ALC885_IMAC24] = {
+		.mixers = { alc885_imac24_mixer },
+		.init_verbs = { alc885_imac24_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
+		.dac_nids = alc882_dac_nids,
+		.dig_out_nid = ALC882_DIGOUT_NID,
+		.dig_in_nid = ALC882_DIGIN_NID,
+		.num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
+		.channel_mode = alc882_ch_modes,
+		.input_mux = &alc882_capture_source,
+		.unsol_event = alc885_imac24_unsol_event,
+		.init_hook = alc885_imac24_automute,
+	},
 	[ALC882_TARGA] = {
 	[ALC882_TARGA] = {
 		.mixers = { alc882_targa_mixer, alc882_chmode_mixer,
 		.mixers = { alc882_targa_mixer, alc882_chmode_mixer,
 			    alc882_capture_mixer },
 			    alc882_capture_mixer },
@@ -5378,6 +5512,29 @@ static struct alc_config_preset alc882_presets[] = {
 };
 };
 
 
 
 
+/*
+ * Pin config fixes
+ */
+enum { 
+	PINFIX_ABIT_AW9D_MAX
+};
+
+static struct alc_pincfg alc882_abit_aw9d_pinfix[] = {
+	{ 0x15, 0x01080104 }, /* side */
+	{ 0x16, 0x01011012 }, /* rear */
+	{ 0x17, 0x01016011 }, /* clfe */
+	{ }
+};
+
+static const struct alc_pincfg *alc882_pin_fixes[] = {
+	[PINFIX_ABIT_AW9D_MAX] = alc882_abit_aw9d_pinfix,
+};
+
+static struct snd_pci_quirk alc882_pinfix_tbl[] = {
+	SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX),
+	{}
+};
+
 /*
 /*
  * BIOS auto configuration
  * BIOS auto configuration
  */
  */
@@ -5494,6 +5651,9 @@ static int patch_alc882(struct hda_codec *codec)
 		case 0x106b0c00: /* Mac Pro */
 		case 0x106b0c00: /* Mac Pro */
 			board_config = ALC885_MACPRO;
 			board_config = ALC885_MACPRO;
 			break;
 			break;
+		case 0x106b1000: /* iMac 24 */
+			board_config = ALC885_IMAC24;
+			break;
 		default:
 		default:
 			printk(KERN_INFO "hda_codec: Unknown model for ALC882, "
 			printk(KERN_INFO "hda_codec: Unknown model for ALC882, "
 		       			 "trying auto-probe from BIOS...\n");
 		       			 "trying auto-probe from BIOS...\n");
@@ -5501,6 +5661,8 @@ static int patch_alc882(struct hda_codec *codec)
 		}
 		}
 	}
 	}
 
 
+	alc_fix_pincfg(codec, alc882_pinfix_tbl, alc882_pin_fixes);
+
 	if (board_config == ALC882_AUTO) {
 	if (board_config == ALC882_AUTO) {
 		/* automatic parse from the BIOS config */
 		/* automatic parse from the BIOS config */
 		err = alc882_parse_auto_config(codec);
 		err = alc882_parse_auto_config(codec);
@@ -5518,7 +5680,7 @@ static int patch_alc882(struct hda_codec *codec)
 	if (board_config != ALC882_AUTO)
 	if (board_config != ALC882_AUTO)
 		setup_preset(spec, &alc882_presets[board_config]);
 		setup_preset(spec, &alc882_presets[board_config]);
 
 
-	if (board_config == ALC885_MACPRO) {
+	if (board_config == ALC885_MACPRO || board_config == ALC885_IMAC24) {
 		alc882_gpio_mute(codec, 0, 0);
 		alc882_gpio_mute(codec, 0, 0);
 		alc882_gpio_mute(codec, 1, 0);
 		alc882_gpio_mute(codec, 1, 0);
 	}
 	}
@@ -5995,6 +6157,84 @@ static struct snd_kcontrol_new alc883_medion_md2_mixer[] = {
 	{ } /* end */
 	{ } /* end */
 };	
 };	
 
 
+static struct snd_kcontrol_new alc888_6st_hp_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", 0x0e, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT),
+	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 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 */
+};
+
+static struct snd_kcontrol_new alc888_3st_hp_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", 0x0e, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT),
+	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("PC Speaker Playback Volume", 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 */
+};
+
 static struct snd_kcontrol_new alc883_chmode_mixer[] = {
 static struct snd_kcontrol_new alc883_chmode_mixer[] = {
 	{
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -6126,6 +6366,42 @@ static struct hda_verb alc888_lenovo_ms7195_verbs[] = {
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
+static struct hda_verb alc888_6st_hp_verbs[] = {
+	{0x14, AC_VERB_SET_CONNECT_SEL, 0x00},	/* Front: output 0 (0x0c) */
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x02},	/* Rear : output 2 (0x0e) */
+	{0x16, AC_VERB_SET_CONNECT_SEL, 0x01},	/* CLFE : output 1 (0x0d) */
+	{0x17, AC_VERB_SET_CONNECT_SEL, 0x03},	/* Side : output 3 (0x0f) */
+	{ }
+};
+
+static struct hda_verb alc888_3st_hp_verbs[] = {
+	{0x14, AC_VERB_SET_CONNECT_SEL, 0x00},	/* Front: output 0 (0x0c) */
+	{0x18, AC_VERB_SET_CONNECT_SEL, 0x01},	/* Rear : output 1 (0x0d) */
+	{0x16, AC_VERB_SET_CONNECT_SEL, 0x02},	/* CLFE : output 2 (0x0e) */
+	{ }
+};
+
+static struct hda_verb alc888_3st_hp_2ch_init[] = {
+	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{ 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+	{ 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{ }
+};
+
+static struct hda_verb alc888_3st_hp_6ch_init[] = {
+	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ }
+};
+
+static struct hda_channel_mode alc888_3st_hp_modes[2] = {
+	{ 2, alc888_3st_hp_2ch_init },
+	{ 6, alc888_3st_hp_6ch_init },
+};
+
 /* toggle front-jack and RCA according to the hp-jack state */
 /* toggle front-jack and RCA according to the hp-jack state */
 static void alc888_lenovo_ms7195_front_automute(struct hda_codec *codec)
 static void alc888_lenovo_ms7195_front_automute(struct hda_codec *codec)
 {
 {
@@ -6368,11 +6644,14 @@ static const char *alc883_models[ALC883_MODEL_LAST] = {
 	[ALC883_LENOVO_101E_2ch] = "lenovo-101e",
 	[ALC883_LENOVO_101E_2ch] = "lenovo-101e",
 	[ALC883_LENOVO_NB0763]	= "lenovo-nb0763",
 	[ALC883_LENOVO_NB0763]	= "lenovo-nb0763",
 	[ALC888_LENOVO_MS7195_DIG] = "lenovo-ms7195-dig",
 	[ALC888_LENOVO_MS7195_DIG] = "lenovo-ms7195-dig",
+	[ALC888_6ST_HP]		= "6stack-hp",
+	[ALC888_3ST_HP]		= "3stack-hp",
 	[ALC883_AUTO]		= "auto",
 	[ALC883_AUTO]		= "auto",
 };
 };
 
 
 static struct snd_pci_quirk alc883_cfg_tbl[] = {
 static struct snd_pci_quirk alc883_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC883_3ST_6ch_DIG),
 	SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC883_3ST_6ch_DIG),
+	SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavillion", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch),
 	SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch),
 	SND_PCI_QUIRK(0x1558, 0, "Clevo laptop", ALC883_LAPTOP_EAPD),
 	SND_PCI_QUIRK(0x1558, 0, "Clevo laptop", ALC883_LAPTOP_EAPD),
 	SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG),
@@ -6381,6 +6660,8 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
 	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, 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, 0x0349, "MSI", ALC883_TARGA_2ch_DIG),
 	SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG),
 	SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG),
 	SND_PCI_QUIRK(0x1462, 0x3729, "MSI S420", ALC883_TARGA_DIG),
 	SND_PCI_QUIRK(0x1462, 0x3729, "MSI S420", ALC883_TARGA_DIG),
 	SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG),
 	SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG),
@@ -6400,6 +6681,9 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = {
 	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, 0x3bfd, "Lenovo NB0763", ALC883_LENOVO_NB0763),
 	SND_PCI_QUIRK(0x17aa, 0x3bfd, "Lenovo NB0763", ALC883_LENOVO_NB0763),
 	SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763),
 	SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763),
+	SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC888_6ST_HP),
+	SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP),
+	SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP),
 	SND_PCI_QUIRK(0x17c0, 0x4071, "MEDION MD2", ALC883_MEDION_MD2),
 	SND_PCI_QUIRK(0x17c0, 0x4071, "MEDION MD2", ALC883_MEDION_MD2),
 	{}
 	{}
 };
 };
@@ -6584,6 +6868,31 @@ static struct alc_config_preset alc883_presets[] = {
 		.unsol_event = alc883_lenovo_ms7195_unsol_event,
 		.unsol_event = alc883_lenovo_ms7195_unsol_event,
 		.init_hook = alc888_lenovo_ms7195_front_automute,
 		.init_hook = alc888_lenovo_ms7195_front_automute,
 	},	
 	},	
+	[ALC888_6ST_HP] = {
+		.mixers = { alc888_6st_hp_mixer, alc883_chmode_mixer },
+		.init_verbs = { alc883_init_verbs, alc888_6st_hp_verbs },
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.dig_out_nid = ALC883_DIGOUT_NID,
+		.num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+		.adc_nids = alc883_adc_nids,
+		.dig_in_nid = ALC883_DIGIN_NID,
+		.num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
+		.channel_mode = alc883_sixstack_modes,
+		.input_mux = &alc883_capture_source,
+	},
+	[ALC888_3ST_HP] = {
+		.mixers = { alc888_3st_hp_mixer, alc883_chmode_mixer },
+		.init_verbs = { alc883_init_verbs, alc888_3st_hp_verbs },
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+		.adc_nids = alc883_adc_nids,
+		.num_channel_mode = ARRAY_SIZE(alc888_3st_hp_modes),
+		.channel_mode = alc888_3st_hp_modes,
+		.need_dac_fix = 1,
+		.input_mux = &alc883_capture_source,
+	},
 };
 };
 
 
 
 
@@ -6857,7 +7166,16 @@ static struct snd_kcontrol_new alc262_sony_mixer[] = {
 	{ } /* end */
 	{ } /* end */
 };
 };
 
 
-
+static struct snd_kcontrol_new alc262_benq_t31_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Front Playback Switch", 0x14, 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_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+	{ } /* end */
+};
 
 
 #define alc262_capture_mixer		alc882_capture_mixer
 #define alc262_capture_mixer		alc882_capture_mixer
 #define alc262_capture_alt_mixer	alc882_capture_alt_mixer
 #define alc262_capture_alt_mixer	alc882_capture_alt_mixer
@@ -7189,6 +7507,15 @@ static struct hda_verb alc262_EAPD_verbs[] = {
 	{}
 	{}
 };
 };
 
 
+static struct hda_verb alc262_benq_t31_EAPD_verbs[] = {
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+
+	{0x20, AC_VERB_SET_COEF_INDEX, 0x07},
+	{0x20, AC_VERB_SET_PROC_COEF,  0x3050},
+	{}
+};
+
 /* add playback controls from the parsed DAC table */
 /* add playback controls from the parsed DAC table */
 static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec,
 static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec,
 					     const struct auto_pin_cfg *cfg)
 					     const struct auto_pin_cfg *cfg)
@@ -7584,7 +7911,8 @@ static const char *alc262_models[ALC262_MODEL_LAST] = {
 	[ALC262_HP_BPC]		= "hp-bpc",
 	[ALC262_HP_BPC]		= "hp-bpc",
 	[ALC262_HP_BPC_D7000_WL]= "hp-bpc-d7000",
 	[ALC262_HP_BPC_D7000_WL]= "hp-bpc-d7000",
 	[ALC262_BENQ_ED8]	= "benq",
 	[ALC262_BENQ_ED8]	= "benq",
-	[ALC262_BENQ_ED8]	= "sony-assamd",
+	[ALC262_BENQ_T31]	= "benq-t31",
+	[ALC262_SONY_ASSAMD]	= "sony-assamd",
 	[ALC262_AUTO]		= "auto",
 	[ALC262_AUTO]		= "auto",
 };
 };
 
 
@@ -7592,8 +7920,12 @@ static struct snd_pci_quirk alc262_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x1002, 0x437b, "Hippo", ALC262_HIPPO),
 	SND_PCI_QUIRK(0x1002, 0x437b, "Hippo", ALC262_HIPPO),
 	SND_PCI_QUIRK(0x103c, 0x12fe, "HP xw9400", ALC262_HP_BPC),
 	SND_PCI_QUIRK(0x103c, 0x12fe, "HP xw9400", ALC262_HP_BPC),
 	SND_PCI_QUIRK(0x103c, 0x280c, "HP xw4400", ALC262_HP_BPC),
 	SND_PCI_QUIRK(0x103c, 0x280c, "HP xw4400", ALC262_HP_BPC),
+	SND_PCI_QUIRK(0x103c, 0x12ff, "HP xw4550", ALC262_HP_BPC),
+	SND_PCI_QUIRK(0x103c, 0x1308, "HP xw4600", ALC262_HP_BPC),
 	SND_PCI_QUIRK(0x103c, 0x3014, "HP xw6400", ALC262_HP_BPC),
 	SND_PCI_QUIRK(0x103c, 0x3014, "HP xw6400", ALC262_HP_BPC),
+	SND_PCI_QUIRK(0x103c, 0x1307, "HP xw6600", ALC262_HP_BPC),
 	SND_PCI_QUIRK(0x103c, 0x3015, "HP xw8400", ALC262_HP_BPC),
 	SND_PCI_QUIRK(0x103c, 0x3015, "HP xw8400", ALC262_HP_BPC),
+	SND_PCI_QUIRK(0x103c, 0x1306, "HP xw8600", ALC262_HP_BPC),
 	SND_PCI_QUIRK(0x103c, 0x2800, "HP D7000", ALC262_HP_BPC_D7000_WL),
 	SND_PCI_QUIRK(0x103c, 0x2800, "HP D7000", ALC262_HP_BPC_D7000_WL),
 	SND_PCI_QUIRK(0x103c, 0x2802, "HP D7000", ALC262_HP_BPC_D7000_WL),
 	SND_PCI_QUIRK(0x103c, 0x2802, "HP D7000", ALC262_HP_BPC_D7000_WL),
 	SND_PCI_QUIRK(0x103c, 0x2804, "HP D7000", ALC262_HP_BPC_D7000_WL),
 	SND_PCI_QUIRK(0x103c, 0x2804, "HP D7000", ALC262_HP_BPC_D7000_WL),
@@ -7606,6 +7938,7 @@ static struct snd_pci_quirk alc262_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU),
 	SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU),
 	SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1),
 	SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1),
 	SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_BENQ_ED8),
 	SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_BENQ_ED8),
+	SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_BENQ_T31),
 	SND_PCI_QUIRK(0x104d, 0x9015, "Sony 0x9015", ALC262_SONY_ASSAMD),
 	SND_PCI_QUIRK(0x104d, 0x9015, "Sony 0x9015", 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, 0x1f00, "Sony ASSAMD", ALC262_SONY_ASSAMD),
 	SND_PCI_QUIRK(0x104d, 0x1f00, "Sony ASSAMD", ALC262_SONY_ASSAMD),
@@ -7710,6 +8043,17 @@ static struct alc_config_preset alc262_presets[] = {
 		.channel_mode = alc262_modes,
 		.channel_mode = alc262_modes,
 		.input_mux = &alc262_capture_source,
 		.input_mux = &alc262_capture_source,
 		.unsol_event = alc262_hippo_unsol_event,
 		.unsol_event = alc262_hippo_unsol_event,
+	},
+	[ALC262_BENQ_T31] = {
+		.mixers = { alc262_benq_t31_mixer },
+		.init_verbs = { alc262_init_verbs, alc262_benq_t31_EAPD_verbs, alc262_hippo_unsol_verbs },
+		.num_dacs = ARRAY_SIZE(alc262_dac_nids),
+		.dac_nids = alc262_dac_nids,
+		.hp_nid = 0x03,
+		.num_channel_mode = ARRAY_SIZE(alc262_modes),
+		.channel_mode = alc262_modes,
+		.input_mux = &alc262_capture_source,
+		.unsol_event = alc262_hippo_unsol_event,
 	},	
 	},	
 };
 };
 
 
@@ -7800,31 +8144,540 @@ static int patch_alc262(struct hda_codec *codec)
 }
 }
 
 
 /*
 /*
- *  ALC861 channel source setting (2/6 channel selection for 3-stack)
+ *  ALC268 channel source setting (2 channel)
  */
  */
+#define ALC268_DIGOUT_NID	ALC880_DIGOUT_NID
+#define alc268_modes		alc260_modes
+	
+static hda_nid_t alc268_dac_nids[2] = {
+	/* front, hp */
+	0x02, 0x03
+};
 
 
-/*
- * set the path ways for 2 channel output
- * need to set the codec line out and mic 1 pin widgets to inputs
- */
-static struct hda_verb alc861_threestack_ch2_init[] = {
-	/* set pin widget 1Ah (line in) for input */
-	{ 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
-	/* set pin widget 18h (mic1/2) for input, for mic also enable
-	 * the vref
-	 */
-	{ 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+static hda_nid_t alc268_adc_nids[2] = {
+	/* ADC0-1 */
+	0x08, 0x07
+};
 
 
-	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c },
-#if 0
-	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/
-	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, /*line-in*/
-#endif
-	{ } /* end */
+static hda_nid_t alc268_adc_nids_alt[1] = {
+	/* ADC0 */
+	0x08
 };
 };
-/*
- * 6ch mode
- * need to set the codec line out and mic 1 pin widgets to outputs
+
+static struct snd_kcontrol_new alc268_base_mixer[] = {
+	/* output mixer control */
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+	{ }
+};
+
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb alc268_base_init_verbs[] = {
+	/* Unmute DAC0-1 and set vol = 0 */
+	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+	/*
+	 * Set up output mixers (0x0c - 0x0e)
+	 */
+	/* set vol=0 to output mixers */
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+        {0x0e, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+	{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+
+	/* FIXME: use matrix-type input source selection */
+	/* Mixer elements: 0x18, 19, 1a, 1c, 14, 15, 0b */
+	/* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
+	/* Input mixer2 */
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
+
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
+	{ }
+};
+
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb alc268_volume_init_verbs[] = {
+	/* set output DAC */
+	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+	{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+
+	/* set PCBEEP vol = 0 */
+	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, (0xb000 | (0x00 << 8))},
+
+	{ }
+};
+
+#define alc268_mux_enum_info alc_mux_enum_info
+#define alc268_mux_enum_get alc_mux_enum_get
+
+static int alc268_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);
+	static hda_nid_t capture_mixers[3] = { 0x23, 0x24 };
+	hda_nid_t nid = capture_mixers[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 && !codec->in_resume)
+		return 0;
+	for (i = 0; i < imux->num_items; i++) {
+		unsigned int v = (i == idx) ? 0x7000 : 0x7080;
+		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+				    v | (imux->items[i].index << 8));
+                snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL,
+				    idx );
+	}
+	*cur_val = idx;
+	return 1;
+}
+
+static struct snd_kcontrol_new alc268_capture_alt_mixer[] = {
+	HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* The multiple "Capture Source" controls confuse alsamixer
+		 * So call somewhat different..
+		 * FIXME: the controls appear in the "playback" view!
+		 */
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 1,
+		.info = alc268_mux_enum_info,
+		.get = alc268_mux_enum_get,
+		.put = alc268_mux_enum_put,
+	},
+	{ } /* end */
+};
+
+static struct snd_kcontrol_new alc268_capture_mixer[] = {
+	HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x24, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x24, 0x0, HDA_OUTPUT),
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		/* The multiple "Capture Source" controls confuse alsamixer
+		 * So call somewhat different..
+		 * FIXME: the controls appear in the "playback" view!
+		 */
+		/* .name = "Capture Source", */
+		.name = "Input Source",
+		.count = 2,
+		.info = alc268_mux_enum_info,
+		.get = alc268_mux_enum_get,
+		.put = alc268_mux_enum_put,
+	},
+	{ } /* end */
+};
+
+static struct hda_input_mux alc268_capture_source = {
+	.num_items = 4,
+	.items = {
+		{ "Mic", 0x0 },
+		{ "Front Mic", 0x1 },
+		{ "Line", 0x2 },
+		{ "CD", 0x3 },
+	},
+};
+
+/* create input playback/capture controls for the given pin */
+static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
+				    const char *ctlname, int idx)
+{
+	char name[32];
+	int err;
+
+	sprintf(name, "%s Playback Volume", ctlname);
+	if (nid == 0x14) {
+		err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+				  HDA_COMPOSE_AMP_VAL(0x02, 3, idx,
+						      HDA_OUTPUT));
+		if (err < 0)
+			return err;
+	} else if (nid == 0x15) {
+		err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+				  HDA_COMPOSE_AMP_VAL(0x03, 3, idx,
+						      HDA_OUTPUT));
+		if (err < 0)
+			return err;
+	} else
+		return -1;
+	sprintf(name, "%s Playback Switch", ctlname);
+	err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+			  HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT));
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec,
+					     const struct auto_pin_cfg *cfg)
+{
+	hda_nid_t nid;
+	int err;
+
+	spec->multiout.num_dacs = 2;	/* only use one dac */
+	spec->multiout.dac_nids = spec->private_dac_nids;
+	spec->multiout.dac_nids[0] = 2;
+	spec->multiout.dac_nids[1] = 3;
+
+	nid = cfg->line_out_pins[0];
+	if (nid)
+		alc268_new_analog_output(spec, nid, "Front", 0);	
+
+	nid = cfg->speaker_pins[0];
+	if (nid == 0x1d) {
+		err = add_control(spec, ALC_CTL_WIDGET_VOL,
+				  "Speaker Playback Volume",
+				  HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
+		if (err < 0)
+			return err;
+	}
+	nid = cfg->hp_pins[0];
+	if (nid)
+		alc268_new_analog_output(spec, nid, "Headphone", 0);
+
+	nid = cfg->line_out_pins[1] | cfg->line_out_pins[2];
+	if (nid == 0x16) {
+		err = add_control(spec, ALC_CTL_WIDGET_MUTE,
+				  "Mono Playback Switch",
+				  HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_INPUT));
+		if (err < 0)
+			return err;
+	}
+	return 0;	
+}
+
+/* create playback/capture controls for input pins */
+static int alc268_auto_create_analog_input_ctls(struct alc_spec *spec,
+						const struct auto_pin_cfg *cfg)
+{
+	struct hda_input_mux *imux = &spec->private_imux;
+	int i, idx1;
+
+	for (i = 0; i < AUTO_PIN_LAST; i++) {
+		switch(cfg->input_pins[i]) {
+		case 0x18:
+			idx1 = 0;	/* Mic 1 */
+			break;
+		case 0x19:
+			idx1 = 1;	/* Mic 2 */
+			break;
+		case 0x1a:
+			idx1 = 2;	/* Line In */
+			break;
+		case 0x1c:	
+			idx1 = 3;	/* CD */
+			break;
+		default:
+			continue;
+		}
+		imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
+		imux->items[imux->num_items].index = idx1;
+		imux->num_items++;	
+	}
+	return 0;
+}
+
+static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	hda_nid_t speaker_nid = spec->autocfg.speaker_pins[0];
+	hda_nid_t hp_nid = spec->autocfg.hp_pins[0];
+	hda_nid_t line_nid = spec->autocfg.line_out_pins[0];
+	unsigned int	dac_vol1, dac_vol2;
+
+	if (speaker_nid) {
+		snd_hda_codec_write(codec, speaker_nid, 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+		snd_hda_codec_write(codec, 0x0f, 0,
+				    AC_VERB_SET_AMP_GAIN_MUTE,
+				    AMP_IN_UNMUTE(1));
+		snd_hda_codec_write(codec, 0x10, 0,
+				    AC_VERB_SET_AMP_GAIN_MUTE,
+				    AMP_IN_UNMUTE(1));
+	} else {
+		snd_hda_codec_write(codec, 0x0f, 0,
+				    AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1));
+		snd_hda_codec_write(codec, 0x10, 0,
+				    AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1));
+	}
+
+	dac_vol1 = dac_vol2 = 0xb000 | 0x40;	/* set max volume  */
+	if (line_nid == 0x14)	
+		dac_vol2 = AMP_OUT_ZERO;
+	else if (line_nid == 0x15)
+		dac_vol1 = AMP_OUT_ZERO;
+	if (hp_nid == 0x14)	
+		dac_vol2 = AMP_OUT_ZERO;
+	else if (hp_nid == 0x15)
+		dac_vol1 = AMP_OUT_ZERO;
+	if (line_nid != 0x16 || hp_nid != 0x16 ||
+	    spec->autocfg.line_out_pins[1] != 0x16 ||
+	    spec->autocfg.line_out_pins[2] != 0x16)
+		dac_vol1 = dac_vol2 = AMP_OUT_ZERO;
+
+	snd_hda_codec_write(codec, 0x02, 0,
+			    AC_VERB_SET_AMP_GAIN_MUTE, dac_vol1);
+	snd_hda_codec_write(codec, 0x03, 0,
+			    AC_VERB_SET_AMP_GAIN_MUTE, dac_vol2);
+}
+
+/* pcm configuration: identiacal with ALC880 */
+#define alc268_pcm_analog_playback	alc880_pcm_analog_playback
+#define alc268_pcm_analog_capture	alc880_pcm_analog_capture
+#define alc268_pcm_digital_playback	alc880_pcm_digital_playback
+
+/*
+ * BIOS auto configuration
+ */
+static int alc268_parse_auto_config(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	int err;
+	static hda_nid_t alc268_ignore[] = { 0 };
+
+	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
+					   alc268_ignore);
+	if (err < 0)
+		return err;
+	if (!spec->autocfg.line_outs)
+		return 0; /* can't find valid BIOS pin config */
+
+	err = alc268_auto_create_multi_out_ctls(spec, &spec->autocfg);
+	if (err < 0)
+		return err;
+	err = alc268_auto_create_analog_input_ctls(spec, &spec->autocfg);
+	if (err < 0)
+		return err;
+
+	spec->multiout.max_channels = 2;
+
+	/* digital only support output */
+	if (spec->autocfg.dig_out_pin)
+		spec->multiout.dig_out_nid = ALC268_DIGOUT_NID;
+
+	if (spec->kctl_alloc)
+		spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
+
+	spec->init_verbs[spec->num_init_verbs++] = alc268_volume_init_verbs;
+	spec->num_mux_defs = 1;
+	spec->input_mux = &spec->private_imux;
+
+	return 1;
+}
+
+#define alc268_auto_init_multi_out	alc882_auto_init_multi_out
+#define alc268_auto_init_hp_out		alc882_auto_init_hp_out
+#define alc268_auto_init_analog_input	alc882_auto_init_analog_input
+
+/* init callback for auto-configuration model -- overriding the default init */
+static void alc268_auto_init(struct hda_codec *codec)
+{
+	alc268_auto_init_multi_out(codec);
+	alc268_auto_init_hp_out(codec);
+	alc268_auto_init_mono_speaker_out(codec);
+	alc268_auto_init_analog_input(codec);
+}
+
+/*
+ * configuration and preset
+ */
+static const char *alc268_models[ALC268_MODEL_LAST] = {
+	[ALC268_3ST]		= "3stack",
+	[ALC268_AUTO]		= "auto",
+};
+
+static struct snd_pci_quirk alc268_cfg_tbl[] = {
+	SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST),
+	{}
+};
+
+static struct alc_config_preset alc268_presets[] = {
+	[ALC268_3ST] = {
+		.mixers = { alc268_base_mixer, alc268_capture_alt_mixer },
+		.init_verbs = { alc268_base_init_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,
+		.hp_nid = 0x03,
+		.dig_out_nid = ALC268_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc268_modes),
+		.channel_mode = alc268_modes,
+		.input_mux = &alc268_capture_source,
+	},
+};
+
+static int patch_alc268(struct hda_codec *codec)
+{
+	struct alc_spec *spec;
+	int board_config;
+	int err;
+
+	spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	codec->spec = spec;
+
+	board_config = snd_hda_check_board_config(codec, ALC268_MODEL_LAST,
+						  alc268_models,
+						  alc268_cfg_tbl);
+
+	if (board_config < 0 || board_config >= ALC268_MODEL_LAST) {
+		printk(KERN_INFO "hda_codec: Unknown model for ALC268, "
+		       "trying auto-probe from BIOS...\n");
+		board_config = ALC268_AUTO;
+	}
+
+	if (board_config == ALC268_AUTO) {
+		/* automatic parse from the BIOS config */
+		err = alc268_parse_auto_config(codec);
+		if (err < 0) {
+			alc_free(codec);
+			return err;
+		} else if (!err) {
+			printk(KERN_INFO
+			       "hda_codec: Cannot set up configuration "
+			       "from BIOS.  Using base mode...\n");
+			board_config = ALC268_3ST;
+		}
+	}
+
+	if (board_config != ALC268_AUTO)
+		setup_preset(spec, &alc268_presets[board_config]);
+
+	spec->stream_name_analog = "ALC268 Analog";
+	spec->stream_analog_playback = &alc268_pcm_analog_playback;
+	spec->stream_analog_capture = &alc268_pcm_analog_capture;
+
+	spec->stream_name_digital = "ALC268 Digital";
+	spec->stream_digital_playback = &alc268_pcm_digital_playback;
+
+	if (board_config == ALC268_AUTO) {
+		if (!spec->adc_nids && spec->input_mux) {
+			/* check whether NID 0x07 is valid */
+			unsigned int wcap = get_wcaps(codec, 0x07);
+
+			/* get type */
+			wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+			if (wcap != AC_WID_AUD_IN) {
+				spec->adc_nids = 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++;
+			} else {
+				spec->adc_nids = alc268_adc_nids;
+				spec->num_adc_nids =
+					ARRAY_SIZE(alc268_adc_nids);
+				spec->mixers[spec->num_mixers] =
+					alc268_capture_mixer;
+				spec->num_mixers++;
+			}
+		}
+	}
+	codec->patch_ops = alc_patch_ops;
+	if (board_config == ALC268_AUTO)
+		spec->init_hook = alc268_auto_init;
+		
+	return 0;
+}
+
+/*
+ *  ALC861 channel source setting (2/6 channel selection for 3-stack)
+ */
+
+/*
+ * set the path ways for 2 channel output
+ * need to set the codec line out and mic 1 pin widgets to inputs
+ */
+static struct hda_verb alc861_threestack_ch2_init[] = {
+	/* set pin widget 1Ah (line in) for input */
+	{ 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+	/* set pin widget 18h (mic1/2) for input, for mic also enable
+	 * the vref
+	 */
+	{ 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+
+	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c },
+#if 0
+	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/
+	{ 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, /*line-in*/
+#endif
+	{ } /* end */
+};
+/*
+ * 6ch mode
+ * need to set the codec line out and mic 1 pin widgets to outputs
  */
  */
 static struct hda_verb alc861_threestack_ch6_init[] = {
 static struct hda_verb alc861_threestack_ch6_init[] = {
 	/* set pin widget 1Ah (line in) for output (Back Surround)*/
 	/* set pin widget 1Ah (line in) for output (Back Surround)*/
@@ -8767,13 +9620,21 @@ static struct snd_pci_quirk alc861_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x1043, 0x1335, "ASUS F2/3", ALC861_ASUS_LAPTOP),
 	SND_PCI_QUIRK(0x1043, 0x1335, "ASUS F2/3", ALC861_ASUS_LAPTOP),
 	SND_PCI_QUIRK(0x1043, 0x1338, "ASUS F2/3", ALC861_ASUS_LAPTOP),
 	SND_PCI_QUIRK(0x1043, 0x1338, "ASUS F2/3", ALC861_ASUS_LAPTOP),
 	SND_PCI_QUIRK(0x1043, 0x13d7, "ASUS A9rp", ALC861_ASUS_LAPTOP),
 	SND_PCI_QUIRK(0x1043, 0x13d7, "ASUS A9rp", ALC861_ASUS_LAPTOP),
+	SND_PCI_QUIRK(0x1584, 0x9075, "Airis Praxis N1212", ALC861_ASUS_LAPTOP),
 	SND_PCI_QUIRK(0x1043, 0x1393, "ASUS", ALC861_ASUS),
 	SND_PCI_QUIRK(0x1043, 0x1393, "ASUS", ALC861_ASUS),
+	SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS P1-AH2", ALC861_3ST_DIG),
 	SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba", ALC861_TOSHIBA),
 	SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba", ALC861_TOSHIBA),
-	SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba", ALC861_TOSHIBA),
+	/* FIXME: the entry below breaks Toshiba A100 (model=auto works!)
+	 *        Any other models that need this preset?
+	 */
+	/* SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba", ALC861_TOSHIBA), */
 	SND_PCI_QUIRK(0x1584, 0x9072, "Uniwill m31", ALC861_UNIWILL_M31),
 	SND_PCI_QUIRK(0x1584, 0x9072, "Uniwill m31", ALC861_UNIWILL_M31),
+	SND_PCI_QUIRK(0x1584, 0x9075, "Uniwill", ALC861_UNIWILL_M31),
 	SND_PCI_QUIRK(0x1584, 0x2b01, "Uniwill X40AIx", ALC861_UNIWILL_M31),
 	SND_PCI_QUIRK(0x1584, 0x2b01, "Uniwill X40AIx", ALC861_UNIWILL_M31),
 	SND_PCI_QUIRK(0x1849, 0x0660, "Asrock 939SLI32", ALC660_3ST),
 	SND_PCI_QUIRK(0x1849, 0x0660, "Asrock 939SLI32", ALC660_3ST),
 	SND_PCI_QUIRK(0x8086, 0xd600, "Intel", ALC861_3ST),
 	SND_PCI_QUIRK(0x8086, 0xd600, "Intel", ALC861_3ST),
+	SND_PCI_QUIRK(0x1462, 0x7254, "HP dx2200 (MSI MS-7254)", ALC861_3ST),
+	SND_PCI_QUIRK(0x1462, 0x7297, "HP dx2250 (MSI MS-7297)", ALC861_3ST),
 	{}
 	{}
 };
 };
 
 
@@ -9464,6 +10325,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",
 	[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",
@@ -9475,7 +10337,7 @@ static const char *alc861vd_models[ALC861VD_MODEL_LAST] = {
 static struct snd_pci_quirk alc861vd_cfg_tbl[] = {
 static struct snd_pci_quirk alc861vd_cfg_tbl[] = {
 	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, 0x81e7, "ASUS", ALC660VD_3ST),
+	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(0x1019, 0xa88d, "Realtek ALC660 demo", ALC660VD_3ST),
 	SND_PCI_QUIRK(0x1019, 0xa88d, "Realtek ALC660 demo", ALC660VD_3ST),
 
 
@@ -9483,6 +10345,7 @@ static struct snd_pci_quirk alc861vd_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x1179, 0xff01, "DALLAS", ALC861VD_DALLAS),
 	SND_PCI_QUIRK(0x1179, 0xff01, "DALLAS", ALC861VD_DALLAS),
 	SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo 3000 C200", ALC861VD_LENOVO),
 	SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo 3000 C200", ALC861VD_LENOVO),
 	SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo", ALC861VD_LENOVO),
 	SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo", ALC861VD_LENOVO),
+	SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO),
 	{}
 	{}
 };
 };
 
 
@@ -9499,6 +10362,19 @@ static struct alc_config_preset alc861vd_presets[] = {
 		.channel_mode = alc861vd_3stack_2ch_modes,
 		.channel_mode = alc861vd_3stack_2ch_modes,
 		.input_mux = &alc861vd_capture_source,
 		.input_mux = &alc861vd_capture_source,
 	},
 	},
+	[ALC660VD_3ST_DIG] = {
+		.mixers = { alc861vd_3st_mixer },
+		.init_verbs = { alc861vd_volume_init_verbs,
+				 alc861vd_3stack_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc660vd_dac_nids),
+		.dac_nids = alc660vd_dac_nids,
+		.dig_out_nid = ALC861VD_DIGOUT_NID,
+		.num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids),
+		.adc_nids = alc861vd_adc_nids,
+		.num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
+		.channel_mode = alc861vd_3stack_2ch_modes,
+		.input_mux = &alc861vd_capture_source,
+	},
 	[ALC861VD_3ST] = {
 	[ALC861VD_3ST] = {
 		.mixers = { alc861vd_3st_mixer },
 		.mixers = { alc861vd_3st_mixer },
 		.init_verbs = { alc861vd_volume_init_verbs,
 		.init_verbs = { alc861vd_volume_init_verbs,
@@ -10420,7 +11296,7 @@ static int alc662_auto_create_multi_out_ctls(struct alc_spec *spec,
 	for (i = 0; i < cfg->line_outs; i++) {
 	for (i = 0; i < cfg->line_outs; i++) {
 		if (!spec->multiout.dac_nids[i])
 		if (!spec->multiout.dac_nids[i])
 			continue;
 			continue;
-		nid = alc880_idx_to_dac(i);
+		nid = alc880_idx_to_mixer(i);
 		if (i == 2) {
 		if (i == 2) {
 			/* Center/LFE */
 			/* Center/LFE */
 			err = add_control(spec, ALC_CTL_WIDGET_VOL,
 			err = add_control(spec, ALC_CTL_WIDGET_VOL,
@@ -10643,14 +11519,10 @@ static int alc662_parse_auto_config(struct hda_codec *codec)
 	spec->num_mux_defs = 1;
 	spec->num_mux_defs = 1;
 	spec->input_mux = &spec->private_imux;
 	spec->input_mux = &spec->private_imux;
 	
 	
-	if (err < 0)
-		return err;
-	else if (err > 0)
-		/* hack - override the init verbs */
-		spec->init_verbs[0] = alc662_auto_init_verbs;
+	spec->init_verbs[spec->num_init_verbs++] = alc662_auto_init_verbs;
 	spec->mixers[spec->num_mixers] = alc662_capture_mixer;
 	spec->mixers[spec->num_mixers] = alc662_capture_mixer;
 	spec->num_mixers++;
 	spec->num_mixers++;
-	return err;
+	return 1;
 }
 }
 
 
 /* additional initialization for auto-configuration model */
 /* additional initialization for auto-configuration model */
@@ -10687,7 +11559,7 @@ static int patch_alc662(struct hda_codec *codec)
 		if (err < 0) {
 		if (err < 0) {
 			alc_free(codec);
 			alc_free(codec);
 			return err;
 			return err;
-		} else if (err) {
+		} else if (!err) {
 			printk(KERN_INFO
 			printk(KERN_INFO
 			       "hda_codec: Cannot set up configuration "
 			       "hda_codec: Cannot set up configuration "
 			       "from BIOS.  Using base mode...\n");
 			       "from BIOS.  Using base mode...\n");
@@ -10724,6 +11596,7 @@ static int patch_alc662(struct hda_codec *codec)
 struct hda_codec_preset snd_hda_preset_realtek[] = {
 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 = 0x10ec0268, .name = "ALC268", .patch = patch_alc268 },
 	{ .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
 	{ .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
 	  .patch = patch_alc861 },
 	  .patch = patch_alc861 },
 	{ .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd },
 	{ .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd },

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

@@ -304,8 +304,12 @@ struct hda_codec_preset snd_hda_preset_si3054[] = {
  	{ .id = 0x10573055, .name = "Si3054", .patch = patch_si3054 },
  	{ .id = 0x10573055, .name = "Si3054", .patch = patch_si3054 },
  	{ .id = 0x10573057, .name = "Si3054", .patch = patch_si3054 },
  	{ .id = 0x10573057, .name = "Si3054", .patch = patch_si3054 },
  	{ .id = 0x10573155, .name = "Si3054", .patch = patch_si3054 },
  	{ .id = 0x10573155, .name = "Si3054", .patch = patch_si3054 },
+	/* VIA HDA on Clevo m540 */
+	{ .id = 0x11063288, .name = "Si3054", .patch = patch_si3054 },
 	/* Asus A8J Modem (SM56) */
 	/* Asus A8J Modem (SM56) */
 	{ .id = 0x15433155, .name = "Si3054", .patch = patch_si3054 },
 	{ .id = 0x15433155, .name = "Si3054", .patch = patch_si3054 },
+	/* LG LW20 modem */
+	{ .id = 0x18540018, .name = "Si3054", .patch = patch_si3054 },
 	{}
 	{}
 };
 };
 
 

+ 184 - 82
sound/pci/hda/patch_sigmatel.c

@@ -44,6 +44,7 @@ enum {
 
 
 enum {
 enum {
 	STAC_9205_REF,
 	STAC_9205_REF,
+	STAC_M43xx,
 	STAC_9205_MODELS
 	STAC_9205_MODELS
 };
 };
 
 
@@ -59,11 +60,19 @@ enum {
 	STAC_D945_REF,
 	STAC_D945_REF,
 	STAC_D945GTP3,
 	STAC_D945GTP3,
 	STAC_D945GTP5,
 	STAC_D945GTP5,
+	STAC_922X_DELL,
+	STAC_INTEL_MAC_V1,
+	STAC_INTEL_MAC_V2,
+	STAC_INTEL_MAC_V3,
+	STAC_INTEL_MAC_V4,
+	STAC_INTEL_MAC_V5,
+	/* for backward compitability */
 	STAC_MACMINI,
 	STAC_MACMINI,
 	STAC_MACBOOK,
 	STAC_MACBOOK,
 	STAC_MACBOOK_PRO_V1,
 	STAC_MACBOOK_PRO_V1,
 	STAC_MACBOOK_PRO_V2,
 	STAC_MACBOOK_PRO_V2,
 	STAC_IMAC_INTEL,
 	STAC_IMAC_INTEL,
+	STAC_IMAC_INTEL_20,
 	STAC_922X_MODELS
 	STAC_922X_MODELS
 };
 };
 
 
@@ -210,7 +219,6 @@ static hda_nid_t stac9205_pin_nids[12] = {
 	0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
 	0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
 	0x0f, 0x14, 0x16, 0x17, 0x18,
 	0x0f, 0x14, 0x16, 0x17, 0x18,
 	0x21, 0x22,
 	0x21, 0x22,
-	
 };
 };
 
 
 static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol,
 static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol,
@@ -326,8 +334,6 @@ static struct snd_kcontrol_new stac9200_mixer[] = {
 };
 };
 
 
 static struct snd_kcontrol_new stac925x_mixer[] = {
 static struct snd_kcontrol_new stac925x_mixer[] = {
-	HDA_CODEC_VOLUME("Master Playback Volume", 0xe, 0, HDA_OUTPUT),
-	HDA_CODEC_MUTE("Master Playback Switch", 0xe, 0, HDA_OUTPUT),
 	{
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 		.name = "Input Source",
 		.name = "Input Source",
@@ -549,44 +555,78 @@ static unsigned int d945gtp5_pin_configs[10] = {
 	0x02a19320, 0x40000100,
 	0x02a19320, 0x40000100,
 };
 };
 
 
-static unsigned int macbook_pro_v1_pin_configs[10] = {
-	0x0321e230, 0x03a1e020, 0x9017e110, 0x01014010,
-	0x01a19021, 0x0381e021, 0x1345e240, 0x13c5e22e,
-	0x02a19320, 0x400000fb
+static unsigned int intel_mac_v1_pin_configs[10] = {
+	0x0121e21f, 0x400000ff, 0x9017e110, 0x400000fd,
+	0x400000fe, 0x0181e020, 0x1145e030, 0x11c5e240,
+	0x400000fc, 0x400000fb,
+};
+
+static unsigned int intel_mac_v2_pin_configs[10] = {
+	0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd,
+	0x400000fe, 0x0181e020, 0x1145e230, 0x500000fa,
+	0x400000fc, 0x400000fb,
+};
+
+static unsigned int intel_mac_v3_pin_configs[10] = {
+	0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd,
+	0x400000fe, 0x0181e020, 0x1145e230, 0x11c5e240,
+	0x400000fc, 0x400000fb,
 };
 };
 
 
-static unsigned int macbook_pro_v2_pin_configs[10] = {
-	0x0221401f, 0x90a70120, 0x01813024, 0x01014010,
-	0x400000fd, 0x01016011, 0x1345e240, 0x13c5e22e,
+static unsigned int intel_mac_v4_pin_configs[10] = {
+	0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f,
+	0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240,
 	0x400000fc, 0x400000fb,
 	0x400000fc, 0x400000fb,
 };
 };
 
 
-static unsigned int imac_intel_pin_configs[10] = {
-	0x0121e230, 0x90a70120, 0x9017e110, 0x400000fe,
-	0x400000fd, 0x0181e021, 0x1145e040, 0x400000fa,
+static unsigned int intel_mac_v5_pin_configs[10] = {
+	0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f,
+	0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240,
 	0x400000fc, 0x400000fb,
 	0x400000fc, 0x400000fb,
 };
 };
 
 
+static unsigned int stac922x_dell_pin_configs[10] = {
+	0x0221121e, 0x408103ff, 0x02a1123e, 0x90100310,
+	0x408003f1, 0x0221122f, 0x03451340, 0x40c003f2,
+	0x50a003f3, 0x405003f4
+};
+
 static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = {
 static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = {
 	[STAC_D945_REF] = ref922x_pin_configs,
 	[STAC_D945_REF] = ref922x_pin_configs,
 	[STAC_D945GTP3] = d945gtp3_pin_configs,
 	[STAC_D945GTP3] = d945gtp3_pin_configs,
 	[STAC_D945GTP5] = d945gtp5_pin_configs,
 	[STAC_D945GTP5] = d945gtp5_pin_configs,
-	[STAC_MACMINI] = macbook_pro_v1_pin_configs,
-	[STAC_MACBOOK] = macbook_pro_v1_pin_configs,
-	[STAC_MACBOOK_PRO_V1] = macbook_pro_v1_pin_configs,
-	[STAC_MACBOOK_PRO_V2] = macbook_pro_v2_pin_configs,
-	[STAC_IMAC_INTEL] = imac_intel_pin_configs,
+	[STAC_922X_DELL] = stac922x_dell_pin_configs,
+	[STAC_INTEL_MAC_V1] = intel_mac_v1_pin_configs,
+	[STAC_INTEL_MAC_V2] = intel_mac_v2_pin_configs,
+	[STAC_INTEL_MAC_V3] = intel_mac_v3_pin_configs,
+	[STAC_INTEL_MAC_V4] = intel_mac_v4_pin_configs,
+	[STAC_INTEL_MAC_V5] = intel_mac_v5_pin_configs,
+	/* for backward compitability */
+	[STAC_MACMINI] = intel_mac_v3_pin_configs,
+	[STAC_MACBOOK] = intel_mac_v5_pin_configs,
+	[STAC_MACBOOK_PRO_V1] = intel_mac_v3_pin_configs,
+	[STAC_MACBOOK_PRO_V2] = intel_mac_v3_pin_configs,
+	[STAC_IMAC_INTEL] = intel_mac_v2_pin_configs,
+	[STAC_IMAC_INTEL_20] = intel_mac_v3_pin_configs,
 };
 };
 
 
 static const char *stac922x_models[STAC_922X_MODELS] = {
 static const char *stac922x_models[STAC_922X_MODELS] = {
 	[STAC_D945_REF]	= "ref",
 	[STAC_D945_REF]	= "ref",
 	[STAC_D945GTP5]	= "5stack",
 	[STAC_D945GTP5]	= "5stack",
 	[STAC_D945GTP3]	= "3stack",
 	[STAC_D945GTP3]	= "3stack",
+	[STAC_922X_DELL] = "dell",
+	[STAC_INTEL_MAC_V1] = "intel-mac-v1",
+	[STAC_INTEL_MAC_V2] = "intel-mac-v2",
+	[STAC_INTEL_MAC_V3] = "intel-mac-v3",
+	[STAC_INTEL_MAC_V4] = "intel-mac-v4",
+	[STAC_INTEL_MAC_V5] = "intel-mac-v5",
+	/* for backward compitability */
 	[STAC_MACMINI]	= "macmini",
 	[STAC_MACMINI]	= "macmini",
 	[STAC_MACBOOK]	= "macbook",
 	[STAC_MACBOOK]	= "macbook",
 	[STAC_MACBOOK_PRO_V1]	= "macbook-pro-v1",
 	[STAC_MACBOOK_PRO_V1]	= "macbook-pro-v1",
 	[STAC_MACBOOK_PRO_V2]	= "macbook-pro",
 	[STAC_MACBOOK_PRO_V2]	= "macbook-pro",
 	[STAC_IMAC_INTEL] = "imac-intel",
 	[STAC_IMAC_INTEL] = "imac-intel",
+	[STAC_IMAC_INTEL_20] = "imac-intel-20",
 };
 };
 
 
 static struct snd_pci_quirk stac922x_cfg_tbl[] = {
 static struct snd_pci_quirk stac922x_cfg_tbl[] = {
@@ -649,7 +689,10 @@ static struct snd_pci_quirk stac922x_cfg_tbl[] = {
 	/* other systems  */
 	/* other systems  */
 	/* Apple Mac Mini (early 2006) */
 	/* Apple Mac Mini (early 2006) */
 	SND_PCI_QUIRK(0x8384, 0x7680,
 	SND_PCI_QUIRK(0x8384, 0x7680,
-		      "Mac Mini", STAC_MACMINI),
+		      "Mac Mini", STAC_INTEL_MAC_V3),
+	/* Dell */
+	SND_PCI_QUIRK(0x1028, 0x01d7, "Dell XPS M1210", STAC_922X_DELL),
+
 	{} /* terminator */
 	{} /* terminator */
 };
 };
 
 
@@ -730,7 +773,8 @@ static unsigned int ref9205_pin_configs[12] = {
 };
 };
 
 
 static unsigned int *stac9205_brd_tbl[STAC_9205_MODELS] = {
 static unsigned int *stac9205_brd_tbl[STAC_9205_MODELS] = {
-	ref9205_pin_configs,
+	[STAC_REF] = ref9205_pin_configs,
+	[STAC_M43xx] = NULL,
 };
 };
 
 
 static const char *stac9205_models[STAC_9205_MODELS] = {
 static const char *stac9205_models[STAC_9205_MODELS] = {
@@ -741,6 +785,10 @@ static struct snd_pci_quirk stac9205_cfg_tbl[] = {
 	/* SigmaTel reference board */
 	/* SigmaTel reference board */
 	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
 	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
 		      "DFI LanParty", STAC_9205_REF),
 		      "DFI LanParty", STAC_9205_REF),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x01f8,
+		      "Dell Precision", STAC_M43xx),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x01ff,
+		      "Dell Precision", STAC_M43xx),
 	{} /* terminator */
 	{} /* terminator */
 };
 };
 
 
@@ -770,33 +818,56 @@ static int stac92xx_save_bios_config_regs(struct hda_codec *codec)
 	return 0;
 	return 0;
 }
 }
 
 
+static void stac92xx_set_config_reg(struct hda_codec *codec,
+				    hda_nid_t pin_nid, unsigned int pin_config)
+{
+	int i;
+	snd_hda_codec_write(codec, pin_nid, 0,
+			    AC_VERB_SET_CONFIG_DEFAULT_BYTES_0,
+			    pin_config & 0x000000ff);
+	snd_hda_codec_write(codec, pin_nid, 0,
+			    AC_VERB_SET_CONFIG_DEFAULT_BYTES_1,
+			    (pin_config & 0x0000ff00) >> 8);
+	snd_hda_codec_write(codec, pin_nid, 0,
+			    AC_VERB_SET_CONFIG_DEFAULT_BYTES_2,
+			    (pin_config & 0x00ff0000) >> 16);
+	snd_hda_codec_write(codec, pin_nid, 0,
+			    AC_VERB_SET_CONFIG_DEFAULT_BYTES_3,
+			    pin_config >> 24);
+	i = snd_hda_codec_read(codec, pin_nid, 0,
+			       AC_VERB_GET_CONFIG_DEFAULT,
+			       0x00);	
+	snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x pin config %8.8x\n",
+		    pin_nid, i);
+}
+
 static void stac92xx_set_config_regs(struct hda_codec *codec)
 static void stac92xx_set_config_regs(struct hda_codec *codec)
 {
 {
 	int i;
 	int i;
 	struct sigmatel_spec *spec = codec->spec;
 	struct sigmatel_spec *spec = codec->spec;
-	unsigned int pin_cfg;
 
 
-	if (! spec->pin_nids || ! spec->pin_configs)
-		return;
+ 	if (!spec->pin_configs)
+ 		return;
 
 
-	for (i = 0; i < spec->num_pins; i++) {
-		snd_hda_codec_write(codec, spec->pin_nids[i], 0,
-				    AC_VERB_SET_CONFIG_DEFAULT_BYTES_0,
-				    spec->pin_configs[i] & 0x000000ff);
-		snd_hda_codec_write(codec, spec->pin_nids[i], 0,
-				    AC_VERB_SET_CONFIG_DEFAULT_BYTES_1,
-				    (spec->pin_configs[i] & 0x0000ff00) >> 8);
-		snd_hda_codec_write(codec, spec->pin_nids[i], 0,
-				    AC_VERB_SET_CONFIG_DEFAULT_BYTES_2,
-				    (spec->pin_configs[i] & 0x00ff0000) >> 16);
-		snd_hda_codec_write(codec, spec->pin_nids[i], 0,
-				    AC_VERB_SET_CONFIG_DEFAULT_BYTES_3,
-				    spec->pin_configs[i] >> 24);
-		pin_cfg = snd_hda_codec_read(codec, spec->pin_nids[i], 0,
-					     AC_VERB_GET_CONFIG_DEFAULT,
-					     0x00);	
-		snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x pin config %8.8x\n", spec->pin_nids[i], pin_cfg);
-	}
+	for (i = 0; i < spec->num_pins; i++)
+		stac92xx_set_config_reg(codec, spec->pin_nids[i],
+					spec->pin_configs[i]);
+}
+
+static void stac92xx_enable_gpio_mask(struct hda_codec *codec,
+				      int gpio_mask, int gpio_data)
+{
+	/* Configure GPIOx as output */
+	snd_hda_codec_write(codec, codec->afg, 0,
+			    AC_VERB_SET_GPIO_DIRECTION, gpio_mask);
+	/* Configure GPIOx as CMOS */
+	snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0x00000000);
+	/* Assert GPIOx */
+	snd_hda_codec_write(codec, codec->afg, 0,
+			    AC_VERB_SET_GPIO_DATA, gpio_data);
+	/* Enable GPIOx */
+	snd_hda_codec_write(codec, codec->afg, 0,
+			    AC_VERB_SET_GPIO_MASK, gpio_mask);
 }
 }
 
 
 /*
 /*
@@ -1168,7 +1239,7 @@ static int is_in_dac_nids(struct sigmatel_spec *spec, hda_nid_t nid)
  * 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,
 static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec,
-				       const struct auto_pin_cfg *cfg)
+				       struct auto_pin_cfg *cfg)
 {
 {
 	struct sigmatel_spec *spec = codec->spec;
 	struct sigmatel_spec *spec = codec->spec;
 	int i, j, conn_len = 0; 
 	int i, j, conn_len = 0; 
@@ -1193,6 +1264,13 @@ static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec,
 		}
 		}
 
 
 		if (j == conn_len) {
 		if (j == conn_len) {
+			if (spec->multiout.num_dacs > 0) {
+				/* we have already working output pins,
+				 * so let's drop the broken ones again
+				 */
+				cfg->line_outs = spec->multiout.num_dacs;
+				break;
+			}
 			/* error out, no available DAC found */
 			/* error out, no available DAC found */
 			snd_printk(KERN_ERR
 			snd_printk(KERN_ERR
 				   "%s: No available DAC for pin 0x%x\n",
 				   "%s: No available DAC for pin 0x%x\n",
@@ -1334,7 +1412,15 @@ static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec,
 			continue;
 			continue;
 		add_spec_dacs(spec, nid);
 		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);
+	}
 	for (i = old_num_dacs; i < spec->multiout.num_dacs; i++) {
 	for (i = old_num_dacs; i < spec->multiout.num_dacs; i++) {
 		static const char *pfxs[] = {
 		static const char *pfxs[] = {
 			"Speaker", "External Speaker", "Speaker2",
 			"Speaker", "External Speaker", "Speaker2",
@@ -1891,7 +1977,7 @@ static int patch_stac9200(struct hda_codec *codec)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	codec->spec = spec;
 	codec->spec = spec;
-	spec->num_pins = 8;
+	spec->num_pins = ARRAY_SIZE(stac9200_pin_nids);
 	spec->pin_nids = stac9200_pin_nids;
 	spec->pin_nids = stac9200_pin_nids;
 	spec->board_config = snd_hda_check_board_config(codec, STAC_9200_MODELS,
 	spec->board_config = snd_hda_check_board_config(codec, STAC_9200_MODELS,
 							stac9200_models,
 							stac9200_models,
@@ -1941,7 +2027,7 @@ static int patch_stac925x(struct hda_codec *codec)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	codec->spec = spec;
 	codec->spec = spec;
-	spec->num_pins = 8;
+	spec->num_pins = ARRAY_SIZE(stac925x_pin_nids);
 	spec->pin_nids = stac925x_pin_nids;
 	spec->pin_nids = stac925x_pin_nids;
 	spec->board_config = snd_hda_check_board_config(codec, STAC_925x_MODELS,
 	spec->board_config = snd_hda_check_board_config(codec, STAC_925x_MODELS,
 							stac925x_models,
 							stac925x_models,
@@ -2013,29 +2099,41 @@ static int patch_stac922x(struct hda_codec *codec)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	codec->spec = spec;
 	codec->spec = spec;
-	spec->num_pins = 10;
+	spec->num_pins = ARRAY_SIZE(stac922x_pin_nids);
 	spec->pin_nids = stac922x_pin_nids;
 	spec->pin_nids = stac922x_pin_nids;
 	spec->board_config = snd_hda_check_board_config(codec, STAC_922X_MODELS,
 	spec->board_config = snd_hda_check_board_config(codec, STAC_922X_MODELS,
 							stac922x_models,
 							stac922x_models,
 							stac922x_cfg_tbl);
 							stac922x_cfg_tbl);
-	if (spec->board_config == STAC_MACMINI) {
+	if (spec->board_config == STAC_INTEL_MAC_V3) {
 		spec->gpio_mute = 1;
 		spec->gpio_mute = 1;
 		/* Intel Macs have all same PCI SSID, so we need to check
 		/* Intel Macs have all same PCI SSID, so we need to check
 		 * codec SSID to distinguish the exact models
 		 * codec SSID to distinguish the exact models
 		 */
 		 */
 		printk(KERN_INFO "hda_codec: STAC922x, Apple subsys_id=%x\n", codec->subsystem_id);
 		printk(KERN_INFO "hda_codec: STAC922x, Apple subsys_id=%x\n", codec->subsystem_id);
 		switch (codec->subsystem_id) {
 		switch (codec->subsystem_id) {
-		case 0x106b0a00: /* MacBook First generatoin */
-			spec->board_config = STAC_MACBOOK;
+
+		case 0x106b0800:
+			spec->board_config = STAC_INTEL_MAC_V1;
+			break;
+		case 0x106b0600:
+		case 0x106b0700:
+			spec->board_config = STAC_INTEL_MAC_V2;
 			break;
 			break;
-		case 0x106b0200: /* MacBook Pro first generation */
-			spec->board_config = STAC_MACBOOK_PRO_V1;
+		case 0x106b0e00:
+		case 0x106b0f00:
+		case 0x106b1600:
+		case 0x106b1700:
+		case 0x106b0200:
+		case 0x106b1e00:
+			spec->board_config = STAC_INTEL_MAC_V3;
 			break;
 			break;
-		case 0x106b1e00: /* MacBook Pro second generation */
-			spec->board_config = STAC_MACBOOK_PRO_V2;
+		case 0x106b1a00:
+		case 0x00000100:
+			spec->board_config = STAC_INTEL_MAC_V4;
 			break;
 			break;
-		case 0x106b0700: /* Intel-based iMac */
-			spec->board_config = STAC_IMAC_INTEL;
+		case 0x106b0a00:
+		case 0x106b2200:
+			spec->board_config = STAC_INTEL_MAC_V5;
 			break;
 			break;
 		}
 		}
 	}
 	}
@@ -2082,6 +2180,13 @@ static int patch_stac922x(struct hda_codec *codec)
 
 
 	codec->patch_ops = stac92xx_patch_ops;
 	codec->patch_ops = stac92xx_patch_ops;
 
 
+	/* Fix Mux capture level; max to 2 */
+	snd_hda_override_amp_caps(codec, 0x12, HDA_OUTPUT,
+				  (0 << AC_AMPCAP_OFFSET_SHIFT) |
+				  (2 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+				  (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+				  (0 << AC_AMPCAP_MUTE_SHIFT));
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -2095,7 +2200,7 @@ static int patch_stac927x(struct hda_codec *codec)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	codec->spec = spec;
 	codec->spec = spec;
-	spec->num_pins = 14;
+	spec->num_pins = ARRAY_SIZE(stac927x_pin_nids);
 	spec->pin_nids = stac927x_pin_nids;
 	spec->pin_nids = stac927x_pin_nids;
 	spec->board_config = snd_hda_check_board_config(codec, STAC_927X_MODELS,
 	spec->board_config = snd_hda_check_board_config(codec, STAC_927X_MODELS,
 							stac927x_models,
 							stac927x_models,
@@ -2141,7 +2246,9 @@ static int patch_stac927x(struct hda_codec *codec)
 	}
 	}
 
 
 	spec->multiout.dac_nids = spec->dac_nids;
 	spec->multiout.dac_nids = spec->dac_nids;
-
+	/* GPIO0 High = Enable EAPD */
+	stac92xx_enable_gpio_mask(codec, 0x00000001, 0x00000001);
+	
 	err = stac92xx_parse_auto_config(codec, 0x1e, 0x20);
 	err = stac92xx_parse_auto_config(codec, 0x1e, 0x20);
 	if (!err) {
 	if (!err) {
 		if (spec->board_config < 0) {
 		if (spec->board_config < 0) {
@@ -2159,27 +2266,20 @@ static int patch_stac927x(struct hda_codec *codec)
 
 
 	codec->patch_ops = stac92xx_patch_ops;
 	codec->patch_ops = stac92xx_patch_ops;
 
 
-	/* Fix Mux capture level; max to 2 */
-	snd_hda_override_amp_caps(codec, 0x12, HDA_OUTPUT,
-				  (0 << AC_AMPCAP_OFFSET_SHIFT) |
-				  (2 << AC_AMPCAP_NUM_STEPS_SHIFT) |
-				  (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) |
-				  (0 << AC_AMPCAP_MUTE_SHIFT));
-
 	return 0;
 	return 0;
 }
 }
 
 
 static int patch_stac9205(struct hda_codec *codec)
 static int patch_stac9205(struct hda_codec *codec)
 {
 {
 	struct sigmatel_spec *spec;
 	struct sigmatel_spec *spec;
-	int err;
+	int err, gpio_mask, gpio_data;
 
 
 	spec  = kzalloc(sizeof(*spec), GFP_KERNEL);
 	spec  = kzalloc(sizeof(*spec), GFP_KERNEL);
 	if (spec == NULL)
 	if (spec == NULL)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	codec->spec = spec;
 	codec->spec = spec;
-	spec->num_pins = 14;
+	spec->num_pins = ARRAY_SIZE(stac9205_pin_nids);
 	spec->pin_nids = stac9205_pin_nids;
 	spec->pin_nids = stac9205_pin_nids;
 	spec->board_config = snd_hda_check_board_config(codec, STAC_9205_MODELS,
 	spec->board_config = snd_hda_check_board_config(codec, STAC_9205_MODELS,
 							stac9205_models,
 							stac9205_models,
@@ -2209,19 +2309,21 @@ static int patch_stac9205(struct hda_codec *codec)
 	spec->mixer = stac9205_mixer;
 	spec->mixer = stac9205_mixer;
 
 
 	spec->multiout.dac_nids = spec->dac_nids;
 	spec->multiout.dac_nids = spec->dac_nids;
+	
+	if (spec->board_config == STAC_M43xx) {
+		/* Enable SPDIF in/out */
+		stac92xx_set_config_reg(codec, 0x1f, 0x01441030);
+		stac92xx_set_config_reg(codec, 0x20, 0x1c410030);
+
+		gpio_mask = 0x00000007; /* GPIO0-2 */
+		/* GPIO0 High = EAPD, GPIO1 Low = DRM,
+		 * GPIO2 High = Headphone Mute
+		 */
+		gpio_data = 0x00000005;
+	} else
+		gpio_mask = gpio_data = 0x00000001; /* GPIO0 High = EAPD */
 
 
-	/* Configure GPIO0 as EAPD output */
-	snd_hda_codec_write(codec, codec->afg, 0,
-			    AC_VERB_SET_GPIO_DIRECTION, 0x00000001);
-	/* Configure GPIO0 as CMOS */
-	snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0x00000000);
-	/* Assert GPIO0 high */
-	snd_hda_codec_write(codec, codec->afg, 0,
-			    AC_VERB_SET_GPIO_DATA, 0x00000001);
-	/* Enable GPIO0 */
-	snd_hda_codec_write(codec, codec->afg, 0,
-			    AC_VERB_SET_GPIO_MASK, 0x00000001);
-
+	stac92xx_enable_gpio_mask(codec, gpio_mask, gpio_data);
 	err = stac92xx_parse_auto_config(codec, 0x1f, 0x20);
 	err = stac92xx_parse_auto_config(codec, 0x1f, 0x20);
 	if (!err) {
 	if (!err) {
 		if (spec->board_config < 0) {
 		if (spec->board_config < 0) {
@@ -2256,8 +2358,8 @@ static struct hda_input_mux vaio_mux = {
 	.num_items = 2,
 	.num_items = 2,
 	.items = {
 	.items = {
 		/* { "HP", 0x0 }, */
 		/* { "HP", 0x0 }, */
-		{ "Line", 0x1 },
-		{ "Mic", 0x2 },
+		{ "Mic Jack", 0x1 },
+		{ "Internal Mic", 0x2 },
 		{ "PCM", 0x3 },
 		{ "PCM", 0x3 },
 	}
 	}
 };
 };
@@ -2268,7 +2370,7 @@ static struct hda_verb vaio_init[] = {
 	{0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? (<- 0x2) */
 	{0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? (<- 0x2) */
 	{0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */
 	{0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */
 	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */
 	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */
-	{0x15, AC_VERB_SET_CONNECT_SEL, 0x2}, /* mic-sel: 0a,0d,14,02 */
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mic-sel: 0a,0d,14,02 */
 	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */
 	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */
 	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */
 	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */
 	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* capture sw/vol -> 0x8 */
 	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* capture sw/vol -> 0x8 */
@@ -2284,7 +2386,7 @@ static struct hda_verb vaio_ar_init[] = {
 	{0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */
 	{0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */
 /*	{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },*/ /* Optical Out */
 /*	{0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },*/ /* Optical Out */
 	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */
 	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */
-	{0x15, AC_VERB_SET_CONNECT_SEL, 0x2}, /* mic-sel: 0a,0d,14,02 */
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mic-sel: 0a,0d,14,02 */
 	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */
 	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */
 	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */
 	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */
 /*	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},*/ /* Optical Out */
 /*	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},*/ /* Optical Out */

+ 6 - 1
sound/pci/ice1712/revo.c

@@ -186,7 +186,12 @@ static int revo51_i2c_init(struct snd_ice1712 *ice,
 #define AK_DAC(xname,xch) { .name = xname, .num_channels = xch }
 #define AK_DAC(xname,xch) { .name = xname, .num_channels = xch }
 
 
 static const struct snd_akm4xxx_dac_channel revo71_front[] = {
 static const struct snd_akm4xxx_dac_channel revo71_front[] = {
-	AK_DAC("PCM Playback Volume", 2)
+	{
+		.name = "PCM Playback Volume",
+		.num_channels = 2,
+		/* front channels DAC supports muting */
+		.switch_name = "PCM Playback Switch",
+	},
 };
 };
 
 
 static const struct snd_akm4xxx_dac_channel revo71_surround[] = {
 static const struct snd_akm4xxx_dac_channel revo71_surround[] = {

+ 2 - 1
sound/pci/nm256/nm256.c

@@ -1533,7 +1533,8 @@ snd_nm256_create(struct snd_card *card, struct pci_dev *pci,
 				printk(KERN_ERR "  force the driver to load by "
 				printk(KERN_ERR "  force the driver to load by "
 				       "passing in the module parameter\n");
 				       "passing in the module parameter\n");
 				printk(KERN_ERR "    force_ac97=1\n");
 				printk(KERN_ERR "    force_ac97=1\n");
-				printk(KERN_ERR "  or try sb16 or cs423x drivers instead.\n");
+				printk(KERN_ERR "  or try sb16, opl3sa2, or "
+				       "cs423x drivers instead.\n");
 				err = -ENXIO;
 				err = -ENXIO;
 				goto __error;
 				goto __error;
 			}
 			}

+ 1 - 1
sound/pci/rme9652/rme9652.c

@@ -406,7 +406,7 @@ static snd_pcm_uframes_t rme9652_hw_pointer(struct snd_rme9652 *rme9652)
 		} else if (!frag)
 		} else if (!frag)
 			return 0;
 			return 0;
 		offset -= rme9652->max_jitter;
 		offset -= rme9652->max_jitter;
-		if (offset < 0)
+		if ((int)offset < 0)
 			offset += period_size * 2;
 			offset += period_size * 2;
 	} else {
 	} else {
 		if (offset > period_size + rme9652->max_jitter) {
 		if (offset > period_size + rme9652->max_jitter) {

+ 2 - 2
sound/pci/via82xx.c

@@ -2098,7 +2098,7 @@ static int snd_via82xx_chip_init(struct via82xx *chip)
 		pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval);
 		pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval);
 		if (pval & VIA_ACLINK_C00_READY) /* primary codec ready */
 		if (pval & VIA_ACLINK_C00_READY) /* primary codec ready */
 			break;
 			break;
-		schedule_timeout_uninterruptible(1);
+		schedule_timeout(1);
 	} while (time_before(jiffies, end_time));
 	} while (time_before(jiffies, end_time));
 
 
 	if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY)
 	if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY)
@@ -2117,7 +2117,7 @@ static int snd_via82xx_chip_init(struct via82xx *chip)
 			chip->ac97_secondary = 1;
 			chip->ac97_secondary = 1;
 			goto __ac97_ok2;
 			goto __ac97_ok2;
 		}
 		}
-		schedule_timeout_interruptible(1);
+		schedule_timeout(1);
 	} while (time_before(jiffies, end_time));
 	} while (time_before(jiffies, end_time));
 	/* This is ok, the most of motherboards have only one codec */
 	/* This is ok, the most of motherboards have only one codec */
 
 

+ 2 - 2
sound/pci/via82xx_modem.c

@@ -983,7 +983,7 @@ static int snd_via82xx_chip_init(struct via82xx_modem *chip)
 		pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval);
 		pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval);
 		if (pval & VIA_ACLINK_C00_READY) /* primary codec ready */
 		if (pval & VIA_ACLINK_C00_READY) /* primary codec ready */
 			break;
 			break;
-		schedule_timeout_uninterruptible(1);
+		schedule_timeout(1);
 	} while (time_before(jiffies, end_time));
 	} while (time_before(jiffies, end_time));
 
 
 	if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY)
 	if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY)
@@ -1001,7 +1001,7 @@ static int snd_via82xx_chip_init(struct via82xx_modem *chip)
 			chip->ac97_secondary = 1;
 			chip->ac97_secondary = 1;
 			goto __ac97_ok2;
 			goto __ac97_ok2;
 		}
 		}
-		schedule_timeout_interruptible(1);
+		schedule_timeout(1);
 	} while (time_before(jiffies, end_time));
 	} while (time_before(jiffies, end_time));
 	/* This is ok, the most of motherboards have only one codec */
 	/* This is ok, the most of motherboards have only one codec */
 
 

+ 20 - 0
sound/ppc/Kconfig

@@ -33,3 +33,23 @@ config SND_POWERMAC_AUTO_DRC
 	  option.
 	  option.
 
 
 endmenu
 endmenu
+
+menu "ALSA PowerPC devices"
+	depends on SND!=n && ( PPC64 || PPC32 )
+
+config SND_PS3
+	tristate "PS3 Audio support"
+	depends on SND && PS3_PS3AV
+	select SND_PCM
+	default m
+	help
+	  Say Y here to include support for audio on the PS3
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd_ps3.
+
+config SND_PS3_DEFAULT_START_DELAY
+	int "Startup delay time in ms"
+	depends on SND_PS3
+	default "2000"
+endmenu

+ 2 - 1
sound/ppc/Makefile

@@ -6,4 +6,5 @@
 snd-powermac-objs := powermac.o pmac.o awacs.o burgundy.o daca.o tumbler.o keywest.o beep.o
 snd-powermac-objs := powermac.o pmac.o awacs.o burgundy.o daca.o tumbler.o keywest.o beep.o
 
 
 # Toplevel Module Dependency
 # Toplevel Module Dependency
-obj-$(CONFIG_SND_POWERMAC) += snd-powermac.o
+obj-$(CONFIG_SND_POWERMAC)	+= snd-powermac.o
+obj-$(CONFIG_SND_PS3)		+= snd_ps3.o

+ 1125 - 0
sound/ppc/snd_ps3.c

@@ -0,0 +1,1125 @@
+/*
+ * Audio support for PS3
+ * Copyright (C) 2007 Sony Computer Entertainment Inc.
+ * All rights reserved.
+ * Copyright 2006, 2007 Sony Corporation
+ *
+ * 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; version 2 of the Licence.
+ *
+ * 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/slab.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/asound.h>
+#include <sound/memalloc.h>
+#include <sound/pcm_params.h>
+#include <sound/control.h>
+#include <linux/dmapool.h>
+#include <linux/dma-mapping.h>
+#include <asm/firmware.h>
+#include <linux/io.h>
+#include <asm/dma.h>
+#include <asm/lv1call.h>
+#include <asm/ps3.h>
+#include <asm/ps3av.h>
+
+#include "snd_ps3_reg.h"
+#include "snd_ps3.h"
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PS3 sound driver");
+MODULE_AUTHOR("Sony Computer Entertainment Inc.");
+
+/* module  entries */
+static int __init snd_ps3_init(void);
+static void __exit snd_ps3_exit(void);
+
+/* ALSA snd driver ops */
+static int snd_ps3_pcm_open(struct snd_pcm_substream *substream);
+static int snd_ps3_pcm_close(struct snd_pcm_substream *substream);
+static int snd_ps3_pcm_prepare(struct snd_pcm_substream *substream);
+static int snd_ps3_pcm_trigger(struct snd_pcm_substream *substream,
+				 int cmd);
+static snd_pcm_uframes_t snd_ps3_pcm_pointer(struct snd_pcm_substream
+					     *substream);
+static int snd_ps3_pcm_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *hw_params);
+static int snd_ps3_pcm_hw_free(struct snd_pcm_substream *substream);
+
+
+/* ps3_system_bus_driver entries */
+static int __init snd_ps3_driver_probe(struct ps3_system_bus_device *dev);
+static int snd_ps3_driver_remove(struct ps3_system_bus_device *dev);
+
+/* address setup */
+static int snd_ps3_map_mmio(void);
+static void snd_ps3_unmap_mmio(void);
+static int snd_ps3_allocate_irq(void);
+static void snd_ps3_free_irq(void);
+static void snd_ps3_audio_set_base_addr(uint64_t ioaddr_start);
+
+/* interrupt handler */
+static irqreturn_t snd_ps3_interrupt(int irq, void *dev_id);
+
+
+/* set sampling rate/format */
+static int snd_ps3_set_avsetting(struct snd_pcm_substream *substream);
+/* take effect parameter change */
+static int snd_ps3_change_avsetting(struct snd_ps3_card_info *card);
+/* initialize avsetting and take it effect */
+static int snd_ps3_init_avsetting(struct snd_ps3_card_info *card);
+/* setup dma */
+static int snd_ps3_program_dma(struct snd_ps3_card_info *card,
+			       enum snd_ps3_dma_filltype filltype);
+static void snd_ps3_wait_for_dma_stop(struct snd_ps3_card_info *card);
+
+static dma_addr_t v_to_bus(struct snd_ps3_card_info *, void  *vaddr, int ch);
+
+
+module_init(snd_ps3_init);
+module_exit(snd_ps3_exit);
+
+/*
+ * global
+ */
+static struct snd_ps3_card_info the_card;
+
+static int snd_ps3_start_delay = CONFIG_SND_PS3_DEFAULT_START_DELAY;
+
+module_param_named(start_delay, snd_ps3_start_delay, uint, 0644);
+MODULE_PARM_DESC(start_delay, "time to insert silent data in milisec");
+
+static int index = SNDRV_DEFAULT_IDX1;
+static char *id = SNDRV_DEFAULT_STR1;
+
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for PS3 soundchip.");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for PS3 soundchip.");
+
+
+/*
+ * PS3 audio register access
+ */
+static inline u32 read_reg(unsigned int reg)
+{
+	return in_be32(the_card.mapped_mmio_vaddr + reg);
+}
+static inline void write_reg(unsigned int reg, u32 val)
+{
+	out_be32(the_card.mapped_mmio_vaddr + reg, val);
+}
+static inline void update_reg(unsigned int reg, u32 or_val)
+{
+	u32 newval = read_reg(reg) | or_val;
+	write_reg(reg, newval);
+}
+static inline void update_mask_reg(unsigned int reg, u32 mask, u32 or_val)
+{
+	u32 newval = (read_reg(reg) & mask) | or_val;
+	write_reg(reg, newval);
+}
+
+/*
+ * ALSA defs
+ */
+const static struct snd_pcm_hardware snd_ps3_pcm_hw = {
+	.info = (SNDRV_PCM_INFO_MMAP |
+		 SNDRV_PCM_INFO_NONINTERLEAVED |
+		 SNDRV_PCM_INFO_MMAP_VALID),
+	.formats = (SNDRV_PCM_FMTBIT_S16_BE |
+		    SNDRV_PCM_FMTBIT_S24_BE),
+	.rates = (SNDRV_PCM_RATE_44100 |
+		  SNDRV_PCM_RATE_48000 |
+		  SNDRV_PCM_RATE_88200 |
+		  SNDRV_PCM_RATE_96000),
+	.rate_min = 44100,
+	.rate_max = 96000,
+
+	.channels_min = 2, /* stereo only */
+	.channels_max = 2,
+
+	.buffer_bytes_max = PS3_AUDIO_FIFO_SIZE * 64,
+
+	/* interrupt by four stages */
+	.period_bytes_min = PS3_AUDIO_FIFO_STAGE_SIZE * 4,
+	.period_bytes_max = PS3_AUDIO_FIFO_STAGE_SIZE * 4,
+
+	.periods_min = 16,
+	.periods_max = 32, /* buffer_size_max/ period_bytes_max */
+
+	.fifo_size = PS3_AUDIO_FIFO_SIZE
+};
+
+static struct snd_pcm_ops snd_ps3_pcm_spdif_ops =
+{
+	.open = snd_ps3_pcm_open,
+	.close = snd_ps3_pcm_close,
+	.prepare = snd_ps3_pcm_prepare,
+	.ioctl = snd_pcm_lib_ioctl,
+	.trigger = snd_ps3_pcm_trigger,
+	.pointer = snd_ps3_pcm_pointer,
+	.hw_params = snd_ps3_pcm_hw_params,
+	.hw_free = snd_ps3_pcm_hw_free
+};
+
+static int snd_ps3_verify_dma_stop(struct snd_ps3_card_info *card,
+				   int count, int force_stop)
+{
+	int dma_ch, done, retries, stop_forced = 0;
+	uint32_t status;
+
+	for (dma_ch = 0; dma_ch < 8; dma_ch ++) {
+		retries = count;
+		do {
+			status = read_reg(PS3_AUDIO_KICK(dma_ch)) &
+				PS3_AUDIO_KICK_STATUS_MASK;
+			switch (status) {
+			case PS3_AUDIO_KICK_STATUS_DONE:
+			case PS3_AUDIO_KICK_STATUS_NOTIFY:
+			case PS3_AUDIO_KICK_STATUS_CLEAR:
+			case PS3_AUDIO_KICK_STATUS_ERROR:
+				done = 1;
+				break;
+			default:
+				done = 0;
+				udelay(10);
+			}
+		} while (!done && --retries);
+		if (!retries && force_stop) {
+			pr_info("%s: DMA ch %d is not stopped.",
+				__func__, dma_ch);
+			/* last resort. force to stop dma.
+			 *  NOTE: this cause DMA done interrupts
+			 */
+			update_reg(PS3_AUDIO_CONFIG, PS3_AUDIO_CONFIG_CLEAR);
+			stop_forced = 1;
+		}
+	}
+	return stop_forced;
+}
+
+/*
+ * wait for all dma is done.
+ * NOTE: caller should reset card->running before call.
+ *       If not, the interrupt handler will re-start DMA,
+ *       then DMA is never stopped.
+ */
+static void snd_ps3_wait_for_dma_stop(struct snd_ps3_card_info *card)
+{
+	int stop_forced;
+	/*
+	 * wait for the last dma is done
+	 */
+
+	/*
+	 * expected maximum DMA done time is 5.7ms + something (DMA itself).
+	 * 5.7ms is from 16bit/sample 2ch 44.1Khz; the time next
+	 * DMA kick event would occur.
+	 */
+	stop_forced = snd_ps3_verify_dma_stop(card, 700, 1);
+
+	/*
+	 * clear outstanding interrupts.
+	 */
+	update_reg(PS3_AUDIO_INTR_0, 0);
+	update_reg(PS3_AUDIO_AX_IS, 0);
+
+	/*
+	 *revert CLEAR bit since it will not reset automatically after DMA stop
+	 */
+	if (stop_forced)
+		update_mask_reg(PS3_AUDIO_CONFIG, ~PS3_AUDIO_CONFIG_CLEAR, 0);
+	/* ensure the hardware sees changes */
+	wmb();
+}
+
+static void snd_ps3_kick_dma(struct snd_ps3_card_info *card)
+{
+
+	update_reg(PS3_AUDIO_KICK(0), PS3_AUDIO_KICK_REQUEST);
+	/* ensure the hardware sees the change */
+	wmb();
+}
+
+/*
+ * convert virtual addr to ioif bus addr.
+ */
+static dma_addr_t v_to_bus(struct snd_ps3_card_info *card,
+			   void * paddr,
+			   int ch)
+{
+	return card->dma_start_bus_addr[ch] +
+		(paddr - card->dma_start_vaddr[ch]);
+};
+
+
+/*
+ * increment ring buffer pointer.
+ * NOTE: caller must hold write spinlock
+ */
+static void snd_ps3_bump_buffer(struct snd_ps3_card_info *card,
+				enum snd_ps3_ch ch, size_t byte_count,
+				int stage)
+{
+	if (!stage)
+		card->dma_last_transfer_vaddr[ch] =
+			card->dma_next_transfer_vaddr[ch];
+	card->dma_next_transfer_vaddr[ch] += byte_count;
+	if ((card->dma_start_vaddr[ch] + (card->dma_buffer_size / 2)) <=
+	    card->dma_next_transfer_vaddr[ch]) {
+		card->dma_next_transfer_vaddr[ch] = card->dma_start_vaddr[ch];
+	}
+}
+/*
+ * setup dmac to send data to audio and attenuate samples on the ring buffer
+ */
+static int snd_ps3_program_dma(struct snd_ps3_card_info *card,
+			       enum snd_ps3_dma_filltype filltype)
+{
+	/* this dmac does not support over 4G */
+	uint32_t dma_addr;
+	int fill_stages, dma_ch, stage;
+	enum snd_ps3_ch ch;
+	uint32_t ch0_kick_event = 0; /* initialize to mute gcc */
+	void *start_vaddr;
+	unsigned long irqsave;
+	int silent = 0;
+
+	switch (filltype) {
+	case SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL:
+		silent = 1;
+		/* intentionally fall thru */
+	case SND_PS3_DMA_FILLTYPE_FIRSTFILL:
+		ch0_kick_event = PS3_AUDIO_KICK_EVENT_ALWAYS;
+		break;
+
+	case SND_PS3_DMA_FILLTYPE_SILENT_RUNNING:
+		silent = 1;
+		/* intentionally fall thru */
+	case SND_PS3_DMA_FILLTYPE_RUNNING:
+		ch0_kick_event = PS3_AUDIO_KICK_EVENT_SERIALOUT0_EMPTY;
+		break;
+	}
+
+	snd_ps3_verify_dma_stop(card, 700, 0);
+	fill_stages = 4;
+	spin_lock_irqsave(&card->dma_lock, irqsave);
+	for (ch = 0; ch < 2; ch++) {
+		start_vaddr = card->dma_next_transfer_vaddr[0];
+		for (stage = 0; stage < fill_stages; stage ++) {
+			dma_ch = stage * 2 + ch;
+			if (silent)
+				dma_addr = card->null_buffer_start_dma_addr;
+			else
+				dma_addr =
+				v_to_bus(card,
+					 card->dma_next_transfer_vaddr[ch],
+					 ch);
+
+			write_reg(PS3_AUDIO_SOURCE(dma_ch),
+				  (PS3_AUDIO_SOURCE_TARGET_SYSTEM_MEMORY |
+				   dma_addr));
+
+			/* dst: fixed to 3wire#0 */
+			if (ch == 0)
+				write_reg(PS3_AUDIO_DEST(dma_ch),
+					  (PS3_AUDIO_DEST_TARGET_AUDIOFIFO |
+					   PS3_AUDIO_AO_3W_LDATA(0)));
+			else
+				write_reg(PS3_AUDIO_DEST(dma_ch),
+					  (PS3_AUDIO_DEST_TARGET_AUDIOFIFO |
+					   PS3_AUDIO_AO_3W_RDATA(0)));
+
+			/* count always 1 DMA block (1/2 stage = 128 bytes) */
+			write_reg(PS3_AUDIO_DMASIZE(dma_ch), 0);
+			/* bump pointer if needed */
+			if (!silent)
+				snd_ps3_bump_buffer(card, ch,
+						    PS3_AUDIO_DMAC_BLOCK_SIZE,
+						    stage);
+
+			/* kick event  */
+			if (dma_ch == 0)
+				write_reg(PS3_AUDIO_KICK(dma_ch),
+					  ch0_kick_event);
+			else
+				write_reg(PS3_AUDIO_KICK(dma_ch),
+					  PS3_AUDIO_KICK_EVENT_AUDIO_DMA(dma_ch
+									 - 1) |
+					  PS3_AUDIO_KICK_REQUEST);
+		}
+	}
+	/* ensure the hardware sees the change */
+	wmb();
+	spin_unlock_irqrestore(&card->dma_lock, irqsave);
+
+	return 0;
+}
+
+/*
+ * audio mute on/off
+ * mute_on : 0 output enabled
+ *           1 mute
+ */
+static int snd_ps3_mute(int mute_on)
+{
+	return ps3av_audio_mute(mute_on);
+}
+
+/*
+ * PCM operators
+ */
+static int snd_ps3_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream);
+	int pcm_index;
+
+	pcm_index = substream->pcm->device;
+	/* to retrieve substream/runtime in interrupt handler */
+	card->substream = substream;
+
+	runtime->hw = snd_ps3_pcm_hw;
+
+	card->start_delay = snd_ps3_start_delay;
+
+	/* mute off */
+	snd_ps3_mute(0); /* this function sleep */
+
+	snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+				   PS3_AUDIO_FIFO_STAGE_SIZE * 4 * 2);
+	return 0;
+};
+
+static int snd_ps3_pcm_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *hw_params)
+{
+	size_t size;
+
+	/* alloc transport buffer */
+	size = params_buffer_bytes(hw_params);
+	snd_pcm_lib_malloc_pages(substream, size);
+	return 0;
+};
+
+static int snd_ps3_delay_to_bytes(struct snd_pcm_substream *substream,
+				  unsigned int delay_ms)
+{
+	int ret;
+	int rate ;
+
+	rate = substream->runtime->rate;
+	ret = snd_pcm_format_size(substream->runtime->format,
+				  rate * delay_ms / 1000)
+		* substream->runtime->channels;
+
+	pr_debug(KERN_ERR "%s: time=%d rate=%d bytes=%ld, frames=%d, ret=%d\n",
+		 __func__,
+		 delay_ms,
+		 rate,
+		 snd_pcm_format_size(substream->runtime->format, rate),
+		 rate * delay_ms / 1000,
+		 ret);
+
+	return ret;
+};
+
+static int snd_ps3_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream);
+	unsigned long irqsave;
+
+	if (!snd_ps3_set_avsetting(substream)) {
+		/* some parameter changed */
+		write_reg(PS3_AUDIO_AX_IE,
+			  PS3_AUDIO_AX_IE_ASOBEIE(0) |
+			  PS3_AUDIO_AX_IE_ASOBUIE(0));
+		/*
+		 * let SPDIF device re-lock with SPDIF signal,
+		 * start with some silence
+		 */
+		card->silent = snd_ps3_delay_to_bytes(substream,
+						      card->start_delay) /
+			(PS3_AUDIO_FIFO_STAGE_SIZE * 4); /* every 4 times */
+	}
+
+	/* restart ring buffer pointer */
+	spin_lock_irqsave(&card->dma_lock, irqsave);
+	{
+		card->dma_buffer_size = runtime->dma_bytes;
+
+		card->dma_last_transfer_vaddr[SND_PS3_CH_L] =
+			card->dma_next_transfer_vaddr[SND_PS3_CH_L] =
+			card->dma_start_vaddr[SND_PS3_CH_L] =
+			runtime->dma_area;
+		card->dma_start_bus_addr[SND_PS3_CH_L] = runtime->dma_addr;
+
+		card->dma_last_transfer_vaddr[SND_PS3_CH_R] =
+			card->dma_next_transfer_vaddr[SND_PS3_CH_R] =
+			card->dma_start_vaddr[SND_PS3_CH_R] =
+			runtime->dma_area + (runtime->dma_bytes / 2);
+		card->dma_start_bus_addr[SND_PS3_CH_R] =
+			runtime->dma_addr + (runtime->dma_bytes / 2);
+
+		pr_debug("%s: vaddr=%p bus=%#lx\n", __func__,
+			 card->dma_start_vaddr[SND_PS3_CH_L],
+			 card->dma_start_bus_addr[SND_PS3_CH_L]);
+
+	}
+	spin_unlock_irqrestore(&card->dma_lock, irqsave);
+
+	/* ensure the hardware sees the change */
+	mb();
+
+	return 0;
+};
+
+static int snd_ps3_pcm_trigger(struct snd_pcm_substream *substream,
+			       int cmd)
+{
+	struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream);
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		/* clear outstanding interrupts  */
+		update_reg(PS3_AUDIO_AX_IS, 0);
+
+		spin_lock(&card->dma_lock);
+		{
+			card->running = 1;
+		}
+		spin_unlock(&card->dma_lock);
+
+		snd_ps3_program_dma(card,
+				    SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
+		snd_ps3_kick_dma(card);
+		while (read_reg(PS3_AUDIO_KICK(7)) &
+		       PS3_AUDIO_KICK_STATUS_MASK) {
+			udelay(1);
+		}
+		snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_RUNNING);
+		snd_ps3_kick_dma(card);
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		spin_lock(&card->dma_lock);
+		{
+			card->running = 0;
+		}
+		spin_unlock(&card->dma_lock);
+		snd_ps3_wait_for_dma_stop(card);
+		break;
+	default:
+		break;
+
+	}
+
+	return ret;
+};
+
+/*
+ * report current pointer
+ */
+static snd_pcm_uframes_t snd_ps3_pcm_pointer(
+	struct snd_pcm_substream *substream)
+{
+	struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream);
+	size_t bytes;
+	snd_pcm_uframes_t ret;
+
+	spin_lock(&card->dma_lock);
+	{
+		bytes = (size_t)(card->dma_last_transfer_vaddr[SND_PS3_CH_L] -
+				 card->dma_start_vaddr[SND_PS3_CH_L]);
+	}
+	spin_unlock(&card->dma_lock);
+
+	ret = bytes_to_frames(substream->runtime, bytes * 2);
+
+	return ret;
+};
+
+static int snd_ps3_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	int ret;
+	ret = snd_pcm_lib_free_pages(substream);
+	return ret;
+};
+
+static int snd_ps3_pcm_close(struct snd_pcm_substream *substream)
+{
+	/* mute on */
+	snd_ps3_mute(1);
+	return 0;
+};
+
+static void snd_ps3_audio_fixup(struct snd_ps3_card_info *card)
+{
+	/*
+	 * avsetting driver seems to never change the followings
+	 * so, init them here once
+	 */
+
+	/* no dma interrupt needed */
+	write_reg(PS3_AUDIO_INTR_EN_0, 0);
+
+	/* use every 4 buffer empty interrupt */
+	update_mask_reg(PS3_AUDIO_AX_IC,
+			PS3_AUDIO_AX_IC_AASOIMD_MASK,
+			PS3_AUDIO_AX_IC_AASOIMD_EVERY4);
+
+	/* enable 3wire clocks */
+	update_mask_reg(PS3_AUDIO_AO_3WMCTRL,
+			~(PS3_AUDIO_AO_3WMCTRL_ASOBCLKD_DISABLED |
+			  PS3_AUDIO_AO_3WMCTRL_ASOLRCKD_DISABLED),
+			0);
+	update_reg(PS3_AUDIO_AO_3WMCTRL,
+		   PS3_AUDIO_AO_3WMCTRL_ASOPLRCK_DEFAULT);
+}
+
+/*
+ * av setting
+ * NOTE: calling this function may generate audio interrupt.
+ */
+static int snd_ps3_change_avsetting(struct snd_ps3_card_info *card)
+{
+	int ret, retries, i;
+	pr_debug("%s: start\n", __func__);
+
+	ret = ps3av_set_audio_mode(card->avs.avs_audio_ch,
+				  card->avs.avs_audio_rate,
+				  card->avs.avs_audio_width,
+				  card->avs.avs_audio_format,
+				  card->avs.avs_audio_source);
+	/*
+	 * Reset the following unwanted settings:
+	 */
+
+	/* disable all 3wire buffers */
+	update_mask_reg(PS3_AUDIO_AO_3WMCTRL,
+			~(PS3_AUDIO_AO_3WMCTRL_ASOEN(0) |
+			  PS3_AUDIO_AO_3WMCTRL_ASOEN(1) |
+			  PS3_AUDIO_AO_3WMCTRL_ASOEN(2) |
+			  PS3_AUDIO_AO_3WMCTRL_ASOEN(3)),
+			0);
+	wmb(); 	/* ensure the hardware sees the change */
+	/* wait for actually stopped */
+	retries = 1000;
+	while ((read_reg(PS3_AUDIO_AO_3WMCTRL) &
+		(PS3_AUDIO_AO_3WMCTRL_ASORUN(0) |
+		 PS3_AUDIO_AO_3WMCTRL_ASORUN(1) |
+		 PS3_AUDIO_AO_3WMCTRL_ASORUN(2) |
+		 PS3_AUDIO_AO_3WMCTRL_ASORUN(3))) &&
+	       --retries) {
+		udelay(1);
+	}
+
+	/* reset buffer pointer */
+	for (i = 0; i < 4; i++) {
+		update_reg(PS3_AUDIO_AO_3WCTRL(i),
+			   PS3_AUDIO_AO_3WCTRL_ASOBRST_RESET);
+		udelay(10);
+	}
+	wmb(); /* ensure the hardware actually start resetting */
+
+	/* enable 3wire#0 buffer */
+	update_reg(PS3_AUDIO_AO_3WMCTRL, PS3_AUDIO_AO_3WMCTRL_ASOEN(0));
+
+
+	/* In 24bit mode,ALSA inserts a zero byte at first byte of per sample */
+	update_mask_reg(PS3_AUDIO_AO_3WCTRL(0),
+			~PS3_AUDIO_AO_3WCTRL_ASODF,
+			PS3_AUDIO_AO_3WCTRL_ASODF_LSB);
+	update_mask_reg(PS3_AUDIO_AO_SPDCTRL(0),
+			~PS3_AUDIO_AO_SPDCTRL_SPODF,
+			PS3_AUDIO_AO_SPDCTRL_SPODF_LSB);
+	/* ensure all the setting above is written back to register */
+	wmb();
+	/* avsetting driver altered AX_IE, caller must reset it if you want */
+	pr_debug("%s: end\n", __func__);
+	return ret;
+}
+
+static int snd_ps3_init_avsetting(struct snd_ps3_card_info *card)
+{
+	int ret;
+	pr_debug("%s: start\n", __func__);
+	card->avs.avs_audio_ch = PS3AV_CMD_AUDIO_NUM_OF_CH_2;
+	card->avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_48K;
+	card->avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16;
+	card->avs.avs_audio_format = PS3AV_CMD_AUDIO_FORMAT_PCM;
+	card->avs.avs_audio_source = PS3AV_CMD_AUDIO_SOURCE_SERIAL;
+
+	ret = snd_ps3_change_avsetting(card);
+
+	snd_ps3_audio_fixup(card);
+
+	/* to start to generate SPDIF signal, fill data */
+	snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
+	snd_ps3_kick_dma(card);
+	pr_debug("%s: end\n", __func__);
+	return ret;
+}
+
+/*
+ *  set sampling rate according to the substream
+ */
+static int snd_ps3_set_avsetting(struct snd_pcm_substream *substream)
+{
+	struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream);
+	struct snd_ps3_avsetting_info avs;
+
+	avs = card->avs;
+
+	pr_debug("%s: called freq=%d width=%d\n", __func__,
+		 substream->runtime->rate,
+		 snd_pcm_format_width(substream->runtime->format));
+
+	pr_debug("%s: before freq=%d width=%d\n", __func__,
+		 card->avs.avs_audio_rate, card->avs.avs_audio_width);
+
+	/* sample rate */
+	switch (substream->runtime->rate) {
+	case 44100:
+		avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_44K;
+		break;
+	case 48000:
+		avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_48K;
+		break;
+	case 88200:
+		avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_88K;
+		break;
+	case 96000:
+		avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_96K;
+		break;
+	default:
+		pr_info("%s: invalid rate %d\n", __func__,
+			substream->runtime->rate);
+		return 1;
+	}
+
+	/* width */
+	switch (snd_pcm_format_width(substream->runtime->format)) {
+	case 16:
+		avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16;
+		break;
+	case 24:
+		avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_24;
+		break;
+	default:
+		pr_info("%s: invalid width %d\n", __func__,
+			snd_pcm_format_width(substream->runtime->format));
+		return 1;
+	}
+
+	if ((card->avs.avs_audio_width != avs.avs_audio_width) ||
+	    (card->avs.avs_audio_rate != avs.avs_audio_rate)) {
+		card->avs = avs;
+		snd_ps3_change_avsetting(card);
+
+		pr_debug("%s: after freq=%d width=%d\n", __func__,
+			 card->avs.avs_audio_rate, card->avs.avs_audio_width);
+
+		return 0;
+	} else
+		return 1;
+}
+
+
+
+static int snd_ps3_map_mmio(void)
+{
+	the_card.mapped_mmio_vaddr =
+		ioremap(the_card.ps3_dev->m_region->bus_addr,
+			the_card.ps3_dev->m_region->len);
+
+	if (!the_card.mapped_mmio_vaddr) {
+		pr_info("%s: ioremap 0 failed p=%#lx l=%#lx \n",
+		       __func__, the_card.ps3_dev->m_region->lpar_addr,
+		       the_card.ps3_dev->m_region->len);
+		return -ENXIO;
+	}
+
+	return 0;
+};
+
+static void snd_ps3_unmap_mmio(void)
+{
+	iounmap(the_card.mapped_mmio_vaddr);
+	the_card.mapped_mmio_vaddr = NULL;
+}
+
+static int snd_ps3_allocate_irq(void)
+{
+	int ret;
+	u64 lpar_addr, lpar_size;
+	u64 __iomem *mapped;
+
+	/* FIXME: move this to device_init (H/W probe) */
+
+	/* get irq outlet */
+	ret = lv1_gpu_device_map(1, &lpar_addr, &lpar_size);
+	if (ret) {
+		pr_info("%s: device map 1 failed %d\n", __func__,
+			ret);
+		return -ENXIO;
+	}
+
+	mapped = ioremap(lpar_addr, lpar_size);
+	if (!mapped) {
+		pr_info("%s: ioremap 1 failed \n", __func__);
+		return -ENXIO;
+	}
+
+	the_card.audio_irq_outlet = in_be64(mapped);
+
+	iounmap(mapped);
+	ret = lv1_gpu_device_unmap(1);
+	if (ret)
+		pr_info("%s: unmap 1 failed\n", __func__);
+
+	/* irq */
+	ret = ps3_irq_plug_setup(PS3_BINDING_CPU_ANY,
+				 the_card.audio_irq_outlet,
+				 &the_card.irq_no);
+	if (ret) {
+		pr_info("%s:ps3_alloc_irq failed (%d)\n", __func__, ret);
+		return ret;
+	}
+
+	ret = request_irq(the_card.irq_no, snd_ps3_interrupt, IRQF_DISABLED,
+			  SND_PS3_DRIVER_NAME, &the_card);
+	if (ret) {
+		pr_info("%s: request_irq failed (%d)\n", __func__, ret);
+		goto cleanup_irq;
+	}
+
+	return 0;
+
+ cleanup_irq:
+	ps3_irq_plug_destroy(the_card.irq_no);
+	return ret;
+};
+
+static void snd_ps3_free_irq(void)
+{
+	free_irq(the_card.irq_no, &the_card);
+	ps3_irq_plug_destroy(the_card.irq_no);
+}
+
+static void snd_ps3_audio_set_base_addr(uint64_t ioaddr_start)
+{
+	uint64_t val;
+	int ret;
+
+	val = (ioaddr_start & (0x0fUL << 32)) >> (32 - 20) |
+		(0x03UL << 24) |
+		(0x0fUL << 12) |
+		(PS3_AUDIO_IOID);
+
+	ret = lv1_gpu_attribute(0x100, 0x007, val, 0, 0);
+	if (ret)
+		pr_info("%s: gpu_attribute failed %d\n", __func__,
+			ret);
+}
+
+static int __init snd_ps3_driver_probe(struct ps3_system_bus_device *dev)
+{
+	int ret;
+	u64 lpar_addr, lpar_size;
+
+	BUG_ON(!firmware_has_feature(FW_FEATURE_PS3_LV1));
+	BUG_ON(dev->match_id != PS3_MATCH_ID_SOUND);
+
+	the_card.ps3_dev = dev;
+
+	ret = ps3_open_hv_device(dev);
+
+	if (ret)
+		return -ENXIO;
+
+	/* setup MMIO */
+	ret = lv1_gpu_device_map(2, &lpar_addr, &lpar_size);
+	if (ret) {
+		pr_info("%s: device map 2 failed %d\n", __func__, ret);
+		goto clean_open;
+	}
+	ps3_mmio_region_init(dev, dev->m_region, lpar_addr, lpar_size,
+		PAGE_SHIFT);
+
+	ret = snd_ps3_map_mmio();
+	if (ret)
+		goto clean_dev_map;
+
+	/* setup DMA area */
+	ps3_dma_region_init(dev, dev->d_region,
+			    PAGE_SHIFT, /* use system page size */
+			    0, /* dma type; not used */
+			    NULL,
+			    _ALIGN_UP(SND_PS3_DMA_REGION_SIZE, PAGE_SIZE));
+	dev->d_region->ioid = PS3_AUDIO_IOID;
+
+	ret = ps3_dma_region_create(dev->d_region);
+	if (ret) {
+		pr_info("%s: region_create\n", __func__);
+		goto clean_mmio;
+	}
+
+	snd_ps3_audio_set_base_addr(dev->d_region->bus_addr);
+
+	/* CONFIG_SND_PS3_DEFAULT_START_DELAY */
+	the_card.start_delay = snd_ps3_start_delay;
+
+	/* irq */
+	if (snd_ps3_allocate_irq()) {
+		ret = -ENXIO;
+		goto clean_dma_region;
+	}
+
+	/* create card instance */
+	the_card.card = snd_card_new(index, id, THIS_MODULE, 0);
+	if (!the_card.card) {
+		ret = -ENXIO;
+		goto clean_irq;
+	}
+
+	strcpy(the_card.card->driver, "PS3");
+	strcpy(the_card.card->shortname, "PS3");
+	strcpy(the_card.card->longname, "PS3 sound");
+	/* create PCM devices instance */
+	/* NOTE:this driver works assuming pcm:substream = 1:1 */
+	ret = snd_pcm_new(the_card.card,
+			  "SPDIF",
+			  0, /* instance index, will be stored pcm.device*/
+			  1, /* output substream */
+			  0, /* input substream */
+			  &(the_card.pcm));
+	if (ret)
+		goto clean_card;
+
+	the_card.pcm->private_data = &the_card;
+	strcpy(the_card.pcm->name, "SPDIF");
+
+	/* set pcm ops */
+	snd_pcm_set_ops(the_card.pcm, SNDRV_PCM_STREAM_PLAYBACK,
+			&snd_ps3_pcm_spdif_ops);
+
+	the_card.pcm->info_flags = SNDRV_PCM_INFO_NONINTERLEAVED;
+	/* pre-alloc PCM DMA buffer*/
+	ret = snd_pcm_lib_preallocate_pages_for_all(the_card.pcm,
+					SNDRV_DMA_TYPE_DEV,
+					&dev->core,
+					SND_PS3_PCM_PREALLOC_SIZE,
+					SND_PS3_PCM_PREALLOC_SIZE);
+	if (ret < 0) {
+		pr_info("%s: prealloc failed\n", __func__);
+		goto clean_card;
+	}
+
+	/*
+	 * allocate null buffer
+	 * its size should be lager than PS3_AUDIO_FIFO_STAGE_SIZE * 2
+	 * PAGE_SIZE is enogh
+	 */
+	if (!(the_card.null_buffer_start_vaddr =
+	      dma_alloc_coherent(&the_card.ps3_dev->core,
+				 PAGE_SIZE,
+				 &the_card.null_buffer_start_dma_addr,
+				 GFP_KERNEL))) {
+		pr_info("%s: nullbuffer alloc failed\n", __func__);
+		goto clean_preallocate;
+	}
+	pr_debug("%s: null vaddr=%p dma=%#lx\n", __func__,
+		 the_card.null_buffer_start_vaddr,
+		 the_card.null_buffer_start_dma_addr);
+	/* set default sample rate/word width */
+	snd_ps3_init_avsetting(&the_card);
+
+	/* register the card */
+	ret = snd_card_register(the_card.card);
+	if (ret < 0)
+		goto clean_dma_map;
+
+	pr_info("%s started. start_delay=%dms\n",
+		the_card.card->longname, the_card.start_delay);
+	return 0;
+
+clean_dma_map:
+	dma_free_coherent(&the_card.ps3_dev->core,
+			  PAGE_SIZE,
+			  the_card.null_buffer_start_vaddr,
+			  the_card.null_buffer_start_dma_addr);
+clean_preallocate:
+	snd_pcm_lib_preallocate_free_for_all(the_card.pcm);
+clean_card:
+	snd_card_free(the_card.card);
+clean_irq:
+	snd_ps3_free_irq();
+clean_dma_region:
+	ps3_dma_region_free(dev->d_region);
+clean_mmio:
+	snd_ps3_unmap_mmio();
+clean_dev_map:
+	lv1_gpu_device_unmap(2);
+clean_open:
+	ps3_close_hv_device(dev);
+	/*
+	 * there is no destructor function to pcm.
+	 * midlayer automatically releases if the card removed
+	 */
+	return ret;
+}; /* snd_ps3_probe */
+
+/* called when module removal */
+static int snd_ps3_driver_remove(struct ps3_system_bus_device *dev)
+{
+	int ret;
+	pr_info("%s:start id=%d\n", __func__,  dev->match_id);
+	if (dev->match_id != PS3_MATCH_ID_SOUND)
+		return -ENXIO;
+
+	/*
+	 * ctl and preallocate buffer will be freed in
+	 * snd_card_free
+	 */
+	ret = snd_card_free(the_card.card);
+	if (ret)
+		pr_info("%s: ctl freecard=%d\n", __func__, ret);
+
+	dma_free_coherent(&dev->core,
+			  PAGE_SIZE,
+			  the_card.null_buffer_start_vaddr,
+			  the_card.null_buffer_start_dma_addr);
+
+	ps3_dma_region_free(dev->d_region);
+
+	snd_ps3_free_irq();
+	snd_ps3_unmap_mmio();
+
+	lv1_gpu_device_unmap(2);
+	ps3_close_hv_device(dev);
+	pr_info("%s:end id=%d\n", __func__, dev->match_id);
+	return 0;
+} /* snd_ps3_remove */
+
+static struct ps3_system_bus_driver snd_ps3_bus_driver_info = {
+	.match_id = PS3_MATCH_ID_SOUND,
+	.probe = snd_ps3_driver_probe,
+	.remove = snd_ps3_driver_remove,
+	.shutdown = snd_ps3_driver_remove,
+	.core = {
+		.name = SND_PS3_DRIVER_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+
+/*
+ * Interrupt handler
+ */
+static irqreturn_t snd_ps3_interrupt(int irq, void *dev_id)
+{
+
+	uint32_t port_intr;
+	int underflow_occured = 0;
+	struct snd_ps3_card_info *card = dev_id;
+
+	if (!card->running) {
+		update_reg(PS3_AUDIO_AX_IS, 0);
+		update_reg(PS3_AUDIO_INTR_0, 0);
+		return IRQ_HANDLED;
+	}
+
+	port_intr = read_reg(PS3_AUDIO_AX_IS);
+	/*
+	 *serial buffer empty detected (every 4 times),
+	 *program next dma and kick it
+	 */
+	if (port_intr & PS3_AUDIO_AX_IE_ASOBEIE(0)) {
+		write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBEIE(0));
+		if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) {
+			write_reg(PS3_AUDIO_AX_IS, port_intr);
+			underflow_occured = 1;
+		}
+		if (card->silent) {
+			/* we are still in silent time */
+			snd_ps3_program_dma(card,
+				(underflow_occured) ?
+				SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL :
+				SND_PS3_DMA_FILLTYPE_SILENT_RUNNING);
+			snd_ps3_kick_dma(card);
+			card->silent --;
+		} else {
+			snd_ps3_program_dma(card,
+				(underflow_occured) ?
+				SND_PS3_DMA_FILLTYPE_FIRSTFILL :
+				SND_PS3_DMA_FILLTYPE_RUNNING);
+			snd_ps3_kick_dma(card);
+			snd_pcm_period_elapsed(card->substream);
+		}
+	} else if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) {
+		write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBUIE(0));
+		/*
+		 * serial out underflow, but buffer empty not detected.
+		 * in this case, fill fifo with 0 to recover.  After
+		 * filling dummy data, serial automatically start to
+		 * consume them and then will generate normal buffer
+		 * empty interrupts.
+		 * If both buffer underflow and buffer empty are occured,
+		 * it is better to do nomal data transfer than empty one
+		 */
+		snd_ps3_program_dma(card,
+				    SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
+		snd_ps3_kick_dma(card);
+		snd_ps3_program_dma(card,
+				    SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
+		snd_ps3_kick_dma(card);
+	}
+	/* clear interrupt cause */
+	return IRQ_HANDLED;
+};
+
+/*
+ * module/subsystem initialize/terminate
+ */
+static int __init snd_ps3_init(void)
+{
+	int ret;
+
+	if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
+		return -ENXIO;
+
+	memset(&the_card, 0, sizeof(the_card));
+	spin_lock_init(&the_card.dma_lock);
+
+	/* register systembus DRIVER, this calls our probe() func */
+	ret = ps3_system_bus_driver_register(&snd_ps3_bus_driver_info);
+
+	return ret;
+}
+
+static void __exit snd_ps3_exit(void)
+{
+	ps3_system_bus_driver_unregister(&snd_ps3_bus_driver_info);
+}
+
+MODULE_ALIAS(PS3_MODULE_ALIAS_SOUND);

+ 135 - 0
sound/ppc/snd_ps3.h

@@ -0,0 +1,135 @@
+/*
+ * Audio support for PS3
+ * Copyright (C) 2007 Sony Computer Entertainment Inc.
+ * All rights reserved.
+ * Copyright 2006, 2007 Sony Corporation
+ *
+ * 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; version 2 of the Licence.
+ *
+ * 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
+ */
+
+#if !defined(_SND_PS3_H_)
+#define _SND_PS3_H_
+
+#include <linux/irqreturn.h>
+
+#define SND_PS3_DRIVER_NAME "snd_ps3"
+
+enum snd_ps3_out_channel {
+	SND_PS3_OUT_SPDIF_0,
+	SND_PS3_OUT_SPDIF_1,
+	SND_PS3_OUT_SERIAL_0,
+	SND_PS3_OUT_DEVS
+};
+
+enum snd_ps3_dma_filltype {
+	SND_PS3_DMA_FILLTYPE_FIRSTFILL,
+	SND_PS3_DMA_FILLTYPE_RUNNING,
+	SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL,
+	SND_PS3_DMA_FILLTYPE_SILENT_RUNNING
+};
+
+enum snd_ps3_ch {
+	SND_PS3_CH_L = 0,
+	SND_PS3_CH_R = 1,
+	SND_PS3_CH_MAX = 2
+};
+
+struct snd_ps3_avsetting_info {
+	uint32_t avs_audio_ch;     /* fixed */
+	uint32_t avs_audio_rate;
+	uint32_t avs_audio_width;
+	uint32_t avs_audio_format; /* fixed */
+	uint32_t avs_audio_source; /* fixed */
+};
+/*
+ * PS3 audio 'card' instance
+ * there should be only ONE hardware.
+ */
+struct snd_ps3_card_info {
+	struct ps3_system_bus_device *ps3_dev;
+	struct snd_card *card;
+
+	struct snd_pcm *pcm;
+	struct snd_pcm_substream *substream;
+
+	/* hvc info */
+	u64 audio_lpar_addr;
+	u64 audio_lpar_size;
+
+	/* registers */
+	void __iomem *mapped_mmio_vaddr;
+
+	/* irq */
+	u64 audio_irq_outlet;
+	unsigned int irq_no;
+
+	/* remember avsetting */
+	struct snd_ps3_avsetting_info avs;
+
+	/* dma buffer management */
+	spinlock_t dma_lock;
+		/* dma_lock start */
+		void * dma_start_vaddr[2]; /* 0 for L, 1 for R */
+		dma_addr_t dma_start_bus_addr[2];
+		size_t dma_buffer_size;
+		void * dma_last_transfer_vaddr[2];
+		void * dma_next_transfer_vaddr[2];
+		int    silent;
+		/* dma_lock end */
+
+	int running;
+
+	/* null buffer */
+	void *null_buffer_start_vaddr;
+	dma_addr_t null_buffer_start_dma_addr;
+
+	/* start delay */
+	unsigned int start_delay;
+
+};
+
+
+/* PS3 audio DMAC block size in bytes */
+#define PS3_AUDIO_DMAC_BLOCK_SIZE (128)
+/* one stage (stereo)  of audio FIFO in bytes */
+#define PS3_AUDIO_FIFO_STAGE_SIZE (256)
+/* how many stages the fifo have */
+#define PS3_AUDIO_FIFO_STAGE_COUNT (8)
+/* fifo size 128 bytes * 8 stages * stereo (2ch) */
+#define PS3_AUDIO_FIFO_SIZE \
+	(PS3_AUDIO_FIFO_STAGE_SIZE * PS3_AUDIO_FIFO_STAGE_COUNT)
+
+/* PS3 audio DMAC max block count in one dma shot = 128 (0x80) blocks*/
+#define PS3_AUDIO_DMAC_MAX_BLOCKS  (PS3_AUDIO_DMASIZE_BLOCKS_MASK + 1)
+
+#define PS3_AUDIO_NORMAL_DMA_START_CH (0)
+#define PS3_AUDIO_NORMAL_DMA_COUNT    (8)
+#define PS3_AUDIO_NULL_DMA_START_CH \
+	(PS3_AUDIO_NORMAL_DMA_START_CH + PS3_AUDIO_NORMAL_DMA_COUNT)
+#define PS3_AUDIO_NULL_DMA_COUNT      (2)
+
+#define SND_PS3_MAX_VOL (0x0F)
+#define SND_PS3_MIN_VOL (0x00)
+#define SND_PS3_MIN_ATT SND_PS3_MIN_VOL
+#define SND_PS3_MAX_ATT SND_PS3_MAX_VOL
+
+#define SND_PS3_PCM_PREALLOC_SIZE \
+	(PS3_AUDIO_DMAC_BLOCK_SIZE * PS3_AUDIO_DMAC_MAX_BLOCKS * 4)
+
+#define SND_PS3_DMA_REGION_SIZE \
+	(SND_PS3_PCM_PREALLOC_SIZE + PAGE_SIZE)
+
+#define PS3_AUDIO_IOID       (1UL)
+
+#endif /* _SND_PS3_H_ */

+ 891 - 0
sound/ppc/snd_ps3_reg.h

@@ -0,0 +1,891 @@
+/*
+ * Audio support for PS3
+ * Copyright (C) 2007 Sony Computer Entertainment Inc.
+ * Copyright 2006, 2007 Sony Corporation
+ * All rights reserved.
+ *
+ * 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; version 2 of the License.
+ *
+ * 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
+ */
+
+/*
+ * interrupt / configure registers
+ */
+
+#define PS3_AUDIO_INTR_0                 (0x00000100)
+#define PS3_AUDIO_INTR_EN_0              (0x00000140)
+#define PS3_AUDIO_CONFIG                 (0x00000200)
+
+/*
+ * DMAC registers
+ * n:0..9
+ */
+#define PS3_AUDIO_DMAC_REGBASE(x)         (0x0000210 + 0x20 * (x))
+
+#define PS3_AUDIO_KICK(n)                 (PS3_AUDIO_DMAC_REGBASE(n) + 0x00)
+#define PS3_AUDIO_SOURCE(n)               (PS3_AUDIO_DMAC_REGBASE(n) + 0x04)
+#define PS3_AUDIO_DEST(n)                 (PS3_AUDIO_DMAC_REGBASE(n) + 0x08)
+#define PS3_AUDIO_DMASIZE(n)              (PS3_AUDIO_DMAC_REGBASE(n) + 0x0C)
+
+/*
+ * mute control
+ */
+#define PS3_AUDIO_AX_MCTRL                (0x00004000)
+#define PS3_AUDIO_AX_ISBP                 (0x00004004)
+#define PS3_AUDIO_AX_AOBP                 (0x00004008)
+#define PS3_AUDIO_AX_IC                   (0x00004010)
+#define PS3_AUDIO_AX_IE                   (0x00004014)
+#define PS3_AUDIO_AX_IS                   (0x00004018)
+
+/*
+ * three wire serial
+ * n:0..3
+ */
+#define PS3_AUDIO_AO_MCTRL                (0x00006000)
+#define PS3_AUDIO_AO_3WMCTRL              (0x00006004)
+
+#define PS3_AUDIO_AO_3WCTRL(n)            (0x00006200 + 0x200 * (n))
+
+/*
+ * S/PDIF
+ * n:0..1
+ * x:0..11
+ * y:0..5
+ */
+#define PS3_AUDIO_AO_SPD_REGBASE(n)       (0x00007200 + 0x200 * (n))
+
+#define PS3_AUDIO_AO_SPDCTRL(n) \
+	(PS3_AUDIO_AO_SPD_REGBASE(n) + 0x00)
+#define PS3_AUDIO_AO_SPDUB(n, x) \
+	(PS3_AUDIO_AO_SPD_REGBASE(n) + 0x04 + 0x04 * (x))
+#define PS3_AUDIO_AO_SPDCS(n, y) \
+	(PS3_AUDIO_AO_SPD_REGBASE(n) + 0x34 + 0x04 * (y))
+
+
+/*
+  PS3_AUDIO_INTR_0 register tells an interrupt handler which audio
+  DMA channel triggered the interrupt.  The interrupt status for a channel
+  can be cleared by writing a '1' to the corresponding bit.  A new interrupt
+  cannot be generated until the previous interrupt has been cleared.
+
+  Note that the status reported by PS3_AUDIO_INTR_0 is independent of the
+  value of PS3_AUDIO_INTR_EN_0.
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |0 0 0 0 0 0 0 0 0 0 0 0 0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C| INTR_0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+*/
+#define PS3_AUDIO_INTR_0_CHAN(n)	(1 << ((n) * 2))
+#define PS3_AUDIO_INTR_0_CHAN9     PS3_AUDIO_INTR_0_CHAN(9)
+#define PS3_AUDIO_INTR_0_CHAN8     PS3_AUDIO_INTR_0_CHAN(8)
+#define PS3_AUDIO_INTR_0_CHAN7     PS3_AUDIO_INTR_0_CHAN(7)
+#define PS3_AUDIO_INTR_0_CHAN6     PS3_AUDIO_INTR_0_CHAN(6)
+#define PS3_AUDIO_INTR_0_CHAN5     PS3_AUDIO_INTR_0_CHAN(5)
+#define PS3_AUDIO_INTR_0_CHAN4     PS3_AUDIO_INTR_0_CHAN(4)
+#define PS3_AUDIO_INTR_0_CHAN3     PS3_AUDIO_INTR_0_CHAN(3)
+#define PS3_AUDIO_INTR_0_CHAN2     PS3_AUDIO_INTR_0_CHAN(2)
+#define PS3_AUDIO_INTR_0_CHAN1     PS3_AUDIO_INTR_0_CHAN(1)
+#define PS3_AUDIO_INTR_0_CHAN0     PS3_AUDIO_INTR_0_CHAN(0)
+
+/*
+  The PS3_AUDIO_INTR_EN_0 register specifies which DMA channels can generate
+  an interrupt to the PU.  Each bit of PS3_AUDIO_INTR_EN_0 is ANDed with the
+  corresponding bit in PS3_AUDIO_INTR_0.  The resulting bits are OR'd together
+  to generate the Audio interrupt.
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |0 0 0 0 0 0 0 0 0 0 0 0 0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C| INTR_EN_0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+
+  Bit assignments are same as PS3_AUDIO_INTR_0
+*/
+
+/*
+  PS3_AUDIO_CONFIG
+  31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 C|0 0 0 0 0 0 0 0| CONFIG
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+
+*/
+
+/* The CLEAR field cancels all pending transfers, and stops any running DMA
+   transfers.  Any interrupts associated with the canceled transfers
+   will occur as if the transfer had finished.
+   Since this bit is designed to recover from DMA related issues
+   which are caused by unpredictable situations, it is prefered to wait
+   for normal DMA transfer end without using this bit.
+*/
+#define PS3_AUDIO_CONFIG_CLEAR          (1 << 8)  /* RWIVF */
+
+/*
+  PS3_AUDIO_AX_MCTRL: Audio Port Mute Control Register
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|A|A|A|0 0 0 0 0 0 0|S|S|A|A|A|A| AX_MCTRL
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+*/
+
+/* 3 Wire Audio Serial Output Channel Mutes (0..3)  */
+#define PS3_AUDIO_AX_MCTRL_ASOMT(n)     (1 << (3 - (n)))  /* RWIVF */
+#define PS3_AUDIO_AX_MCTRL_ASO3MT       (1 << 0)          /* RWIVF */
+#define PS3_AUDIO_AX_MCTRL_ASO2MT       (1 << 1)          /* RWIVF */
+#define PS3_AUDIO_AX_MCTRL_ASO1MT       (1 << 2)          /* RWIVF */
+#define PS3_AUDIO_AX_MCTRL_ASO0MT       (1 << 3)          /* RWIVF */
+
+/* S/PDIF mutes (0,1)*/
+#define PS3_AUDIO_AX_MCTRL_SPOMT(n)     (1 << (5 - (n)))  /* RWIVF */
+#define PS3_AUDIO_AX_MCTRL_SPO1MT       (1 << 4)          /* RWIVF */
+#define PS3_AUDIO_AX_MCTRL_SPO0MT       (1 << 5)          /* RWIVF */
+
+/* All 3 Wire Serial Outputs Mute */
+#define PS3_AUDIO_AX_MCTRL_AASOMT       (1 << 13)         /* RWIVF */
+
+/* All S/PDIF Mute */
+#define PS3_AUDIO_AX_MCTRL_ASPOMT       (1 << 14)         /* RWIVF */
+
+/* All Audio Outputs Mute */
+#define PS3_AUDIO_AX_MCTRL_AAOMT        (1 << 15)         /* RWIVF */
+
+/*
+  S/PDIF Outputs Buffer Read/Write Pointer Register
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |0 0 0 0 0 0 0 0|0|SPO0B|0|SPO1B|0 0 0 0 0 0 0 0|0|SPO0B|0|SPO1B| AX_ISBP
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+
+*/
+/*
+ S/PDIF Output Channel Read Buffer Numbers
+ Buffer number is  value of field.
+ Indicates current read access buffer ID from Audio Data
+ Transfer controller of S/PDIF Output
+*/
+
+#define PS3_AUDIO_AX_ISBP_SPOBRN_MASK(n) (0x7 << 4 * (1 - (n))) /* R-IUF */
+#define PS3_AUDIO_AX_ISBP_SPO1BRN_MASK		(0x7 << 0) /* R-IUF */
+#define PS3_AUDIO_AX_ISBP_SPO0BRN_MASK		(0x7 << 4) /* R-IUF */
+
+/*
+S/PDIF Output Channel Buffer Write Numbers
+Indicates current write access buffer ID from bus master.
+*/
+#define PS3_AUDIO_AX_ISBP_SPOBWN_MASK(n) (0x7 <<  4 * (5 - (n))) /* R-IUF */
+#define PS3_AUDIO_AX_ISBP_SPO1BWN_MASK		(0x7 << 16) /* R-IUF */
+#define PS3_AUDIO_AX_ISBP_SPO0BWN_MASK		(0x7 << 20) /* R-IUF */
+
+/*
+  3 Wire Audio Serial Outputs Buffer Read/Write
+  Pointer Register
+  Buffer number is  value of field
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |0|ASO0B|0|ASO1B|0|ASO2B|0|ASO3B|0|ASO0B|0|ASO1B|0|ASO2B|0|ASO3B| AX_AOBP
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+*/
+
+/*
+3 Wire Audio Serial Output Channel Buffer Read Numbers
+Indicates current read access buffer Id from Audio Data Transfer
+Controller of 3 Wire Audio Serial Output Channels
+*/
+#define PS3_AUDIO_AX_AOBP_ASOBRN_MASK(n) (0x7 << 4 * (3 - (n))) /* R-IUF */
+
+#define PS3_AUDIO_AX_AOBP_ASO3BRN_MASK	(0x7 << 0) /* R-IUF */
+#define PS3_AUDIO_AX_AOBP_ASO2BRN_MASK	(0x7 << 4) /* R-IUF */
+#define PS3_AUDIO_AX_AOBP_ASO1BRN_MASK	(0x7 << 8) /* R-IUF */
+#define PS3_AUDIO_AX_AOBP_ASO0BRN_MASK	(0x7 << 12) /* R-IUF */
+
+/*
+3 Wire Audio Serial Output Channel Buffer Write Numbers
+Indicates current write access buffer ID from bus master.
+*/
+#define PS3_AUDIO_AX_AOBP_ASOBWN_MASK(n) (0x7 << 4 * (7 - (n))) /* R-IUF */
+
+#define PS3_AUDIO_AX_AOBP_ASO3BWN_MASK        (0x7 << 16) /* R-IUF */
+#define PS3_AUDIO_AX_AOBP_ASO2BWN_MASK        (0x7 << 20) /* R-IUF */
+#define PS3_AUDIO_AX_AOBP_ASO1BWN_MASK        (0x7 << 24) /* R-IUF */
+#define PS3_AUDIO_AX_AOBP_ASO0BWN_MASK        (0x7 << 28) /* R-IUF */
+
+
+
+/*
+Audio Port Interrupt Condition Register
+For the fields in this register, the following values apply:
+0 = Interrupt is generated every interrupt event.
+1 = Interrupt is generated every 2 interrupt events.
+2 = Interrupt is generated every 4 interrupt events.
+3 = Reserved
+
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |0 0 0 0 0 0 0 0|0 0|SPO|0 0|SPO|0 0|AAS|0 0 0 0 0 0 0 0 0 0 0 0| AX_IC
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+*/
+/*
+All 3-Wire Audio Serial Outputs Interrupt Mode
+Configures the Interrupt and Signal Notification
+condition of all 3-wire Audio Serial Outputs.
+*/
+#define PS3_AUDIO_AX_IC_AASOIMD_MASK          (0x3 << 12) /* RWIVF */
+#define PS3_AUDIO_AX_IC_AASOIMD_EVERY1        (0x0 << 12) /* RWI-V */
+#define PS3_AUDIO_AX_IC_AASOIMD_EVERY2        (0x1 << 12) /* RW--V */
+#define PS3_AUDIO_AX_IC_AASOIMD_EVERY4        (0x2 << 12) /* RW--V */
+
+/*
+S/PDIF Output Channel Interrupt Modes
+Configures the Interrupt and signal Notification
+conditions of S/PDIF output channels.
+*/
+#define PS3_AUDIO_AX_IC_SPO1IMD_MASK          (0x3 << 16) /* RWIVF */
+#define PS3_AUDIO_AX_IC_SPO1IMD_EVERY1        (0x0 << 16) /* RWI-V */
+#define PS3_AUDIO_AX_IC_SPO1IMD_EVERY2        (0x1 << 16) /* RW--V */
+#define PS3_AUDIO_AX_IC_SPO1IMD_EVERY4        (0x2 << 16) /* RW--V */
+
+#define PS3_AUDIO_AX_IC_SPO0IMD_MASK          (0x3 << 20) /* RWIVF */
+#define PS3_AUDIO_AX_IC_SPO0IMD_EVERY1        (0x0 << 20) /* RWI-V */
+#define PS3_AUDIO_AX_IC_SPO0IMD_EVERY2        (0x1 << 20) /* RW--V */
+#define PS3_AUDIO_AX_IC_SPO0IMD_EVERY4        (0x2 << 20) /* RW--V */
+
+/*
+Audio Port interrupt Enable Register
+Configures whether to enable or disable each Interrupt Generation.
+
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |0 0 0 0 0 0 0 0|S|S|0 0|A|A|A|A|0 0 0 0|S|S|0 0|S|S|0 0|A|A|A|A| AX_IE
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+
+*/
+
+/*
+3 Wire Audio Serial Output Channel Buffer Underflow
+Interrupt Enables
+Select enable/disable of Buffer Underflow Interrupts for
+3-Wire Audio Serial Output Channels
+DISABLED=Interrupt generation disabled.
+*/
+#define PS3_AUDIO_AX_IE_ASOBUIE(n)      (1 << (3 - (n))) /* RWIVF */
+#define PS3_AUDIO_AX_IE_ASO3BUIE        (1 << 0) /* RWIVF */
+#define PS3_AUDIO_AX_IE_ASO2BUIE        (1 << 1) /* RWIVF */
+#define PS3_AUDIO_AX_IE_ASO1BUIE        (1 << 2) /* RWIVF */
+#define PS3_AUDIO_AX_IE_ASO0BUIE        (1 << 3) /* RWIVF */
+
+/* S/PDIF Output Channel Buffer Underflow Interrupt Enables */
+
+#define PS3_AUDIO_AX_IE_SPOBUIE(n)      (1 << (7 - (n))) /* RWIVF */
+#define PS3_AUDIO_AX_IE_SPO1BUIE        (1 << 6) /* RWIVF */
+#define PS3_AUDIO_AX_IE_SPO0BUIE        (1 << 7) /* RWIVF */
+
+/* S/PDIF Output Channel One Block Transfer Completion Interrupt Enables */
+
+#define PS3_AUDIO_AX_IE_SPOBTCIE(n)     (1 << (11 - (n))) /* RWIVF */
+#define PS3_AUDIO_AX_IE_SPO1BTCIE       (1 << 10) /* RWIVF */
+#define PS3_AUDIO_AX_IE_SPO0BTCIE       (1 << 11) /* RWIVF */
+
+/* 3-Wire Audio Serial Output Channel Buffer Empty Interrupt Enables */
+
+#define PS3_AUDIO_AX_IE_ASOBEIE(n)      (1 << (19 - (n))) /* RWIVF */
+#define PS3_AUDIO_AX_IE_ASO3BEIE        (1 << 16) /* RWIVF */
+#define PS3_AUDIO_AX_IE_ASO2BEIE        (1 << 17) /* RWIVF */
+#define PS3_AUDIO_AX_IE_ASO1BEIE        (1 << 18) /* RWIVF */
+#define PS3_AUDIO_AX_IE_ASO0BEIE        (1 << 19) /* RWIVF */
+
+/* S/PDIF Output Channel Buffer Empty Interrupt Enables */
+
+#define PS3_AUDIO_AX_IE_SPOBEIE(n)      (1 << (23 - (n))) /* RWIVF */
+#define PS3_AUDIO_AX_IE_SPO1BEIE        (1 << 22) /* RWIVF */
+#define PS3_AUDIO_AX_IE_SPO0BEIE        (1 << 23) /* RWIVF */
+
+/*
+Audio Port Interrupt Status Register
+Indicates Interrupt status, which interrupt has occured, and can clear
+each interrupt in this register.
+Writing 1b to a field containing 1b clears field and de-asserts interrupt.
+Writing 0b to a field has no effect.
+Field vaules are the following:
+0 - Interrupt hasn't occured.
+1 - Interrupt has occured.
+
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |0 0 0 0 0 0 0 0|S|S|0 0|A|A|A|A|0 0 0 0|S|S|0 0|S|S|0 0|A|A|A|A| AX_IS
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+
+ Bit assignment are same as AX_IE
+*/
+
+/*
+Audio Output Master Control Register
+Configures Master Clock and other master Audio Output Settings
+
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |0|SCKSE|0|SCKSE|  MR0  |  MR1  |MCL|MCL|0 0 0 0|0 0 0 0 0 0 0 0| AO_MCTRL
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+*/
+
+/*
+MCLK Output Control
+Controls mclko[1] output.
+0 - Disable output (fixed at High)
+1 - Output clock produced by clock selected
+with scksel1 by mr1
+2 - Reserved
+3 - Reserved
+*/
+
+#define PS3_AUDIO_AO_MCTRL_MCLKC1_MASK		(0x3 << 12) /* RWIVF */
+#define PS3_AUDIO_AO_MCTRL_MCLKC1_DISABLED	(0x0 << 12) /* RWI-V */
+#define PS3_AUDIO_AO_MCTRL_MCLKC1_ENABLED	(0x1 << 12) /* RW--V */
+#define PS3_AUDIO_AO_MCTRL_MCLKC1_RESVD2	(0x2 << 12) /* RW--V */
+#define PS3_AUDIO_AO_MCTRL_MCLKC1_RESVD3	(0x3 << 12) /* RW--V */
+
+/*
+MCLK Output Control
+Controls mclko[0] output.
+0 - Disable output (fixed at High)
+1 - Output clock produced by clock selected
+with SCKSEL0 by MR0
+2 - Reserved
+3 - Reserved
+*/
+#define PS3_AUDIO_AO_MCTRL_MCLKC0_MASK		(0x3 << 14) /* RWIVF */
+#define PS3_AUDIO_AO_MCTRL_MCLKC0_DISABLED	(0x0 << 14) /* RWI-V */
+#define PS3_AUDIO_AO_MCTRL_MCLKC0_ENABLED	(0x1 << 14) /* RW--V */
+#define PS3_AUDIO_AO_MCTRL_MCLKC0_RESVD2	(0x2 << 14) /* RW--V */
+#define PS3_AUDIO_AO_MCTRL_MCLKC0_RESVD3	(0x3 << 14) /* RW--V */
+/*
+Master Clock Rate 1
+Sets the divide ration of Master Clock1 (clock output from
+mclko[1] for the input clock selected by scksel1.
+*/
+#define PS3_AUDIO_AO_MCTRL_MR1_MASK	(0xf << 16)
+#define PS3_AUDIO_AO_MCTRL_MR1_DEFAULT	(0x0 << 16) /* RWI-V */
+/*
+Master Clock Rate 0
+Sets the divide ratio of Master Clock0 (clock output from
+mclko[0] for the input clock selected by scksel0).
+*/
+#define PS3_AUDIO_AO_MCTRL_MR0_MASK	(0xf << 20) /* RWIVF */
+#define PS3_AUDIO_AO_MCTRL_MR0_DEFAULT	(0x0 << 20) /* RWI-V */
+/*
+System Clock Select 0/1
+Selects the system clock to be used as Master Clock 0/1
+Input the system clock that is appropriate for the sampling
+rate.
+*/
+#define PS3_AUDIO_AO_MCTRL_SCKSEL1_MASK		(0x7 << 24) /* RWIVF */
+#define PS3_AUDIO_AO_MCTRL_SCKSEL1_DEFAULT	(0x2 << 24) /* RWI-V */
+
+#define PS3_AUDIO_AO_MCTRL_SCKSEL0_MASK		(0x7 << 28) /* RWIVF */
+#define PS3_AUDIO_AO_MCTRL_SCKSEL0_DEFAULT	(0x2 << 28) /* RWI-V */
+
+
+/*
+3-Wire Audio Output Master Control Register
+Configures clock, 3-Wire Audio Serial Output Enable, and
+other 3-Wire Audio Serial Output Master Settings
+
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |A|A|A|A|0 0 0|A| ASOSR |0 0 0 0|A|A|A|A|A|A|0|1|0 0 0 0 0 0 0 0| AO_3WMCTRL
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+*/
+
+
+/*
+LRCKO Polarity
+0 - Reserved
+1 - default
+*/
+#define PS3_AUDIO_AO_3WMCTRL_ASOPLRCK 		(1 << 8) /* RWIVF */
+#define PS3_AUDIO_AO_3WMCTRL_ASOPLRCK_DEFAULT	(1 << 8) /* RW--V */
+
+/* LRCK Output Disable */
+
+#define PS3_AUDIO_AO_3WMCTRL_ASOLRCKD		(1 << 10) /* RWIVF */
+#define PS3_AUDIO_AO_3WMCTRL_ASOLRCKD_ENABLED	(0 << 10) /* RW--V */
+#define PS3_AUDIO_AO_3WMCTRL_ASOLRCKD_DISABLED	(1 << 10) /* RWI-V */
+
+/* Bit Clock Output Disable */
+
+#define PS3_AUDIO_AO_3WMCTRL_ASOBCLKD		(1 << 11) /* RWIVF */
+#define PS3_AUDIO_AO_3WMCTRL_ASOBCLKD_ENABLED	(0 << 11) /* RW--V */
+#define PS3_AUDIO_AO_3WMCTRL_ASOBCLKD_DISABLED	(1 << 11) /* RWI-V */
+
+/*
+3-Wire Audio Serial Output Channel 0-3 Operational
+Status.  Each bit becomes 1 after each 3-Wire Audio
+Serial Output Channel N is in action by setting 1 to
+asoen.
+Each bit becomes 0 after each 3-Wire Audio Serial Output
+Channel N is out of action by setting 0 to asoen.
+*/
+#define PS3_AUDIO_AO_3WMCTRL_ASORUN(n)		(1 << (15 - (n))) /* R-IVF */
+#define PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(n)	(0 << (15 - (n))) /* R-I-V */
+#define PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(n)	(1 << (15 - (n))) /* R---V */
+#define PS3_AUDIO_AO_3WMCTRL_ASORUN0		\
+	PS3_AUDIO_AO_3WMCTRL_ASORUN(0)
+#define PS3_AUDIO_AO_3WMCTRL_ASORUN0_STOPPED	\
+	PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(0)
+#define PS3_AUDIO_AO_3WMCTRL_ASORUN0_RUNNING	\
+	PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(0)
+#define PS3_AUDIO_AO_3WMCTRL_ASORUN1		\
+	PS3_AUDIO_AO_3WMCTRL_ASORUN(1)
+#define PS3_AUDIO_AO_3WMCTRL_ASORUN1_STOPPED	\
+	PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(1)
+#define PS3_AUDIO_AO_3WMCTRL_ASORUN1_RUNNING	\
+	PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(1)
+#define PS3_AUDIO_AO_3WMCTRL_ASORUN2		\
+	PS3_AUDIO_AO_3WMCTRL_ASORUN(2)
+#define PS3_AUDIO_AO_3WMCTRL_ASORUN2_STOPPED	\
+	PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(2)
+#define PS3_AUDIO_AO_3WMCTRL_ASORUN2_RUNNING	\
+	PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(2)
+#define PS3_AUDIO_AO_3WMCTRL_ASORUN3		\
+	PS3_AUDIO_AO_3WMCTRL_ASORUN(3)
+#define PS3_AUDIO_AO_3WMCTRL_ASORUN3_STOPPED	\
+	PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(3)
+#define PS3_AUDIO_AO_3WMCTRL_ASORUN3_RUNNING	\
+	PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(3)
+
+/*
+Sampling Rate
+Specifies the divide ratio of the bit clock (clock output
+from bclko) used by the 3-wire Audio Output Clock, whcih
+is applied to the master clock selected by mcksel.
+Data output is synchronized with this clock.
+*/
+#define PS3_AUDIO_AO_3WMCTRL_ASOSR_MASK		(0xf << 20) /* RWIVF */
+#define PS3_AUDIO_AO_3WMCTRL_ASOSR_DIV2		(0x1 << 20) /* RWI-V */
+#define PS3_AUDIO_AO_3WMCTRL_ASOSR_DIV4		(0x2 << 20) /* RW--V */
+#define PS3_AUDIO_AO_3WMCTRL_ASOSR_DIV8		(0x4 << 20) /* RW--V */
+#define PS3_AUDIO_AO_3WMCTRL_ASOSR_DIV12	(0x6 << 20) /* RW--V */
+
+/*
+Master Clock Select
+0 - Master Clock 0
+1 - Master Clock 1
+*/
+#define PS3_AUDIO_AO_3WMCTRL_ASOMCKSEL		(1 << 24) /* RWIVF */
+#define PS3_AUDIO_AO_3WMCTRL_ASOMCKSEL_CLK0	(0 << 24) /* RWI-V */
+#define PS3_AUDIO_AO_3WMCTRL_ASOMCKSEL_CLK1	(1 << 24) /* RW--V */
+
+/*
+Enables and disables 4ch 3-Wire Audio Serial Output
+operation.  Each Bit from 0 to 3 corresponds to an
+output channel, which means that each output channel
+can be enabled or disabled individually.  When
+multiple channels are enabled at the same time, output
+operations are performed in synchronization.
+Bit 0 - Output Channel 0 (SDOUT[0])
+Bit 1 - Output Channel 1 (SDOUT[1])
+Bit 2 - Output Channel 2 (SDOUT[2])
+Bit 3 - Output Channel 3 (SDOUT[3])
+*/
+#define PS3_AUDIO_AO_3WMCTRL_ASOEN(n)		(1 << (31 - (n))) /* RWIVF */
+#define PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(n)	(0 << (31 - (n))) /* RWI-V */
+#define PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(n)	(1 << (31 - (n))) /* RW--V */
+
+#define PS3_AUDIO_AO_3WMCTRL_ASOEN0 \
+	PS3_AUDIO_AO_3WMCTRL_ASOEN(0) /* RWIVF */
+#define PS3_AUDIO_AO_3WMCTRL_ASOEN0_DISABLED \
+	PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(0) /* RWI-V */
+#define PS3_AUDIO_AO_3WMCTRL_ASOEN0_ENABLED \
+	PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(0) /* RW--V */
+#define PS3_AUDIO_A1_3WMCTRL_ASOEN0 \
+	PS3_AUDIO_AO_3WMCTRL_ASOEN(1) /* RWIVF */
+#define PS3_AUDIO_A1_3WMCTRL_ASOEN0_DISABLED \
+	PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(1) /* RWI-V */
+#define PS3_AUDIO_A1_3WMCTRL_ASOEN0_ENABLED \
+	PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(1) /* RW--V */
+#define PS3_AUDIO_A2_3WMCTRL_ASOEN0 \
+	PS3_AUDIO_AO_3WMCTRL_ASOEN(2) /* RWIVF */
+#define PS3_AUDIO_A2_3WMCTRL_ASOEN0_DISABLED \
+	PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(2) /* RWI-V */
+#define PS3_AUDIO_A2_3WMCTRL_ASOEN0_ENABLED \
+	PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(2) /* RW--V */
+#define PS3_AUDIO_A3_3WMCTRL_ASOEN0 \
+	PS3_AUDIO_AO_3WMCTRL_ASOEN(3) /* RWIVF */
+#define PS3_AUDIO_A3_3WMCTRL_ASOEN0_DISABLED \
+	PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(3) /* RWI-V */
+#define PS3_AUDIO_A3_3WMCTRL_ASOEN0_ENABLED \
+	PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(3) /* RW--V */
+
+/*
+3-Wire Audio Serial output Channel 0-3 Control Register
+Configures settings for 3-Wire Serial Audio Output Channel 0-3
+
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|A|0 0 0 0|A|0|ASO|0 0 0|0|0|0|0|0| AO_3WCTRL
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+
+*/
+/*
+Data Bit Mode
+Specifies the number of data bits
+0 - 16 bits
+1 - reserved
+2 - 20 bits
+3 - 24 bits
+*/
+#define PS3_AUDIO_AO_3WCTRL_ASODB_MASK	(0x3 << 8) /* RWIVF */
+#define PS3_AUDIO_AO_3WCTRL_ASODB_16BIT	(0x0 << 8) /* RWI-V */
+#define PS3_AUDIO_AO_3WCTRL_ASODB_RESVD	(0x1 << 8) /* RWI-V */
+#define PS3_AUDIO_AO_3WCTRL_ASODB_20BIT	(0x2 << 8) /* RW--V */
+#define PS3_AUDIO_AO_3WCTRL_ASODB_24BIT	(0x3 << 8) /* RW--V */
+/*
+Data Format Mode
+Specifies the data format where (LSB side or MSB) the data(in 20 bit
+or 24 bit resolution mode) is put in a 32 bit field.
+0 - Data put on LSB side
+1 - Data put on MSB side
+*/
+#define PS3_AUDIO_AO_3WCTRL_ASODF 	(1 << 11) /* RWIVF */
+#define PS3_AUDIO_AO_3WCTRL_ASODF_LSB	(0 << 11) /* RWI-V */
+#define PS3_AUDIO_AO_3WCTRL_ASODF_MSB	(1 << 11) /* RW--V */
+/*
+Buffer Reset
+Performs buffer reset.  Writing 1 to this bit initializes the
+corresponding 3-Wire Audio Output buffers(both L and R).
+*/
+#define PS3_AUDIO_AO_3WCTRL_ASOBRST 		(1 << 16) /* CWIVF */
+#define PS3_AUDIO_AO_3WCTRL_ASOBRST_IDLE	(0 << 16) /* -WI-V */
+#define PS3_AUDIO_AO_3WCTRL_ASOBRST_RESET	(1 << 16) /* -W--T */
+
+/*
+S/PDIF Audio Output Channel 0/1 Control Register
+Configures settings for S/PDIF Audio Output Channel 0/1.
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |S|0 0 0|S|0 0|S| SPOSR |0 0|SPO|0 0 0 0|S|0|SPO|0 0 0 0 0 0 0|S| AO_SPDCTRL
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+*/
+/*
+Buffer reset.  Writing 1 to this bit initializes the
+corresponding S/PDIF output buffer pointer.
+*/
+#define PS3_AUDIO_AO_SPDCTRL_SPOBRST		(1 << 0) /* CWIVF */
+#define PS3_AUDIO_AO_SPDCTRL_SPOBRST_IDLE	(0 << 0) /* -WI-V */
+#define PS3_AUDIO_AO_SPDCTRL_SPOBRST_RESET	(1 << 0) /* -W--T */
+
+/*
+Data Bit Mode
+Specifies number of data bits
+0 - 16 bits
+1 - Reserved
+2 - 20 bits
+3 - 24 bits
+*/
+#define PS3_AUDIO_AO_SPDCTRL_SPODB_MASK		(0x3 << 8) /* RWIVF */
+#define PS3_AUDIO_AO_SPDCTRL_SPODB_16BIT	(0x0 << 8) /* RWI-V */
+#define PS3_AUDIO_AO_SPDCTRL_SPODB_RESVD	(0x1 << 8) /* RW--V */
+#define PS3_AUDIO_AO_SPDCTRL_SPODB_20BIT	(0x2 << 8) /* RW--V */
+#define PS3_AUDIO_AO_SPDCTRL_SPODB_24BIT	(0x3 << 8) /* RW--V */
+/*
+Data format Mode
+Specifies the data format, where (LSB side or MSB)
+the data(in 20 or 24 bit resolution) is put in the
+32 bit field.
+0 - LSB Side
+1 - MSB Side
+*/
+#define PS3_AUDIO_AO_SPDCTRL_SPODF	(1 << 11) /* RWIVF */
+#define PS3_AUDIO_AO_SPDCTRL_SPODF_LSB	(0 << 11) /* RWI-V */
+#define PS3_AUDIO_AO_SPDCTRL_SPODF_MSB	(1 << 11) /* RW--V */
+/*
+Source Select
+Specifies the source of the S/PDIF output.  When 0, output
+operation is controlled by 3wen[0] of AO_3WMCTRL register.
+The SR must have the same setting as the a0_3wmctrl reg.
+0 - 3-Wire Audio OUT Ch0 Buffer
+1 - S/PDIF buffer
+*/
+#define PS3_AUDIO_AO_SPDCTRL_SPOSS_MASK		(0x3 << 16) /* RWIVF */
+#define PS3_AUDIO_AO_SPDCTRL_SPOSS_3WEN		(0x0 << 16) /* RWI-V */
+#define PS3_AUDIO_AO_SPDCTRL_SPOSS_SPDIF	(0x1 << 16) /* RW--V */
+/*
+Sampling Rate
+Specifies the divide ratio of the bit clock (clock output
+from bclko) used by the S/PDIF Output Clock, which
+is applied to the master clock selected by mcksel.
+*/
+#define PS3_AUDIO_AO_SPDCTRL_SPOSR		(0xf << 20) /* RWIVF */
+#define PS3_AUDIO_AO_SPDCTRL_SPOSR_DIV2		(0x1 << 20) /* RWI-V */
+#define PS3_AUDIO_AO_SPDCTRL_SPOSR_DIV4		(0x2 << 20) /* RW--V */
+#define PS3_AUDIO_AO_SPDCTRL_SPOSR_DIV8		(0x4 << 20) /* RW--V */
+#define PS3_AUDIO_AO_SPDCTRL_SPOSR_DIV12	(0x6 << 20) /* RW--V */
+/*
+Master Clock Select
+0 - Master Clock 0
+1 - Master Clock 1
+*/
+#define PS3_AUDIO_AO_SPDCTRL_SPOMCKSEL		(1 << 24) /* RWIVF */
+#define PS3_AUDIO_AO_SPDCTRL_SPOMCKSEL_CLK0	(0 << 24) /* RWI-V */
+#define PS3_AUDIO_AO_SPDCTRL_SPOMCKSEL_CLK1	(1 << 24) /* RW--V */
+
+/*
+S/PDIF Output Channel Operational Status
+This bit becomes 1 after S/PDIF Output Channel is in
+action by setting 1 to spoen.  This bit becomes 0
+after S/PDIF Output Channel is out of action by setting
+0 to spoen.
+*/
+#define PS3_AUDIO_AO_SPDCTRL_SPORUN		(1 << 27) /* R-IVF */
+#define PS3_AUDIO_AO_SPDCTRL_SPORUN_STOPPED	(0 << 27) /* R-I-V */
+#define PS3_AUDIO_AO_SPDCTRL_SPORUN_RUNNING	(1 << 27) /* R---V */
+
+/*
+S/PDIF Audio Output Channel Output Enable
+Enables and disables output operation.  This bit is used
+only when sposs = 1
+*/
+#define PS3_AUDIO_AO_SPDCTRL_SPOEN		(1 << 31) /* RWIVF */
+#define PS3_AUDIO_AO_SPDCTRL_SPOEN_DISABLED	(0 << 31) /* RWI-V */
+#define PS3_AUDIO_AO_SPDCTRL_SPOEN_ENABLED	(1 << 31) /* RW--V */
+
+/*
+S/PDIF Audio Output Channel Channel Status
+Setting Registers.
+Configures channel status bit settings for each block
+(192 bits).
+Output is performed from the MSB(AO_SPDCS0 register bit 31).
+The same value is added for subframes within the same frame.
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |                             SPOCS                             | AO_SPDCS
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+
+S/PDIF Audio Output Channel User Bit Setting
+Configures user bit settings for each block (384 bits).
+Output is performed from the MSB(ao_spdub0 register bit 31).
+
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |                             SPOUB                             | AO_SPDUB
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+*/
+/*****************************************************************************
+ *
+ * DMAC register
+ *
+ *****************************************************************************/
+/*
+The PS3_AUDIO_KICK register is used to initiate a DMA transfer and monitor
+its status
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |0 0 0 0 0|STATU|0 0 0|  EVENT  |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|R| KICK
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+*/
+/*
+The REQUEST field is written to ACTIVE to initiate a DMA request when EVENT
+occurs.
+It will return to the DONE state when the request is completed.
+The registers for a DMA channel should only be written if REQUEST is IDLE.
+*/
+
+#define PS3_AUDIO_KICK_REQUEST                (1 << 0) /* RWIVF */
+#define PS3_AUDIO_KICK_REQUEST_IDLE           (0 << 0) /* RWI-V */
+#define PS3_AUDIO_KICK_REQUEST_ACTIVE         (1 << 0) /* -W--T */
+
+/*
+ *The EVENT field is used to set the event in which
+ *the DMA request becomes active.
+ */
+#define PS3_AUDIO_KICK_EVENT_MASK             (0x1f << 16) /* RWIVF */
+#define PS3_AUDIO_KICK_EVENT_ALWAYS           (0x00 << 16) /* RWI-V */
+#define PS3_AUDIO_KICK_EVENT_SERIALOUT0_EMPTY (0x01 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_SERIALOUT0_UNDERFLOW	(0x02 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_SERIALOUT1_EMPTY		(0x03 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_SERIALOUT1_UNDERFLOW	(0x04 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_SERIALOUT2_EMPTY		(0x05 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_SERIALOUT2_UNDERFLOW	(0x06 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_SERIALOUT3_EMPTY		(0x07 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_SERIALOUT3_UNDERFLOW	(0x08 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_SPDIF0_BLOCKTRANSFERCOMPLETE \
+	(0x09 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_SPDIF0_UNDERFLOW		(0x0A << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_SPDIF0_EMPTY		(0x0B << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_SPDIF1_BLOCKTRANSFERCOMPLETE \
+	(0x0C << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_SPDIF1_UNDERFLOW		(0x0D << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_SPDIF1_EMPTY		(0x0E << 16) /* RW--V */
+
+#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA(n) \
+	((0x13 + (n)) << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA0         (0x13 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA1         (0x14 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA2         (0x15 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA3         (0x16 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA4         (0x17 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA5         (0x18 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA6         (0x19 << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA7         (0x1A << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA8         (0x1B << 16) /* RW--V */
+#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA9         (0x1C << 16) /* RW--V */
+
+/*
+The STATUS field can be used to monitor the progress of a DMA request.
+DONE indicates the previous request has completed.
+EVENT indicates that the DMA engine is waiting for the EVENT to occur.
+PENDING indicates that the DMA engine has not started processing this
+request, but the EVENT has occured.
+DMA indicates that the data transfer is in progress.
+NOTIFY indicates that the notifier signalling end of transfer is being written.
+CLEAR indicated that the previous transfer was cleared.
+ERROR indicates the previous transfer requested an unsupported
+source/destination combination.
+*/
+
+#define PS3_AUDIO_KICK_STATUS_MASK	(0x7 << 24) /* R-IVF */
+#define PS3_AUDIO_KICK_STATUS_DONE	(0x0 << 24) /* R-I-V */
+#define PS3_AUDIO_KICK_STATUS_EVENT	(0x1 << 24) /* R---V */
+#define PS3_AUDIO_KICK_STATUS_PENDING	(0x2 << 24) /* R---V */
+#define PS3_AUDIO_KICK_STATUS_DMA	(0x3 << 24) /* R---V */
+#define PS3_AUDIO_KICK_STATUS_NOTIFY	(0x4 << 24) /* R---V */
+#define PS3_AUDIO_KICK_STATUS_CLEAR	(0x5 << 24) /* R---V */
+#define PS3_AUDIO_KICK_STATUS_ERROR	(0x6 << 24) /* R---V */
+
+/*
+The PS3_AUDIO_SOURCE register specifies the source address for transfers.
+
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |                      START                      |0 0 0 0 0|TAR| SOURCE
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+*/
+
+/*
+The Audio DMA engine uses 128-byte transfers, thus the address must be aligned
+to a 128 byte boundary.  The low seven bits are assumed to be 0.
+*/
+
+#define PS3_AUDIO_SOURCE_START_MASK	(0x01FFFFFF << 7) /* RWIUF */
+
+/*
+The TARGET field specifies the memory space containing the source address.
+*/
+
+#define PS3_AUDIO_SOURCE_TARGET_MASK 		(3 << 0) /* RWIVF */
+#define PS3_AUDIO_SOURCE_TARGET_SYSTEM_MEMORY	(2 << 0) /* RW--V */
+
+/*
+The PS3_AUDIO_DEST register specifies the destination address for transfers.
+
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |                      START                      |0 0 0 0 0|TAR| DEST
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+*/
+
+/*
+The Audio DMA engine uses 128-byte transfers, thus the address must be aligned
+to a 128 byte boundary.  The low seven bits are assumed to be 0.
+*/
+
+#define PS3_AUDIO_DEST_START_MASK	(0x01FFFFFF << 7) /* RWIUF */
+
+/*
+The TARGET field specifies the memory space containing the destination address
+AUDIOFIFO = Audio WriteData FIFO,
+*/
+
+#define PS3_AUDIO_DEST_TARGET_MASK		(3 << 0) /* RWIVF */
+#define PS3_AUDIO_DEST_TARGET_AUDIOFIFO		(1 << 0) /* RW--V */
+
+/*
+PS3_AUDIO_DMASIZE specifies the number of 128-byte blocks + 1 to transfer.
+So a value of 0 means 128-bytes will get transfered.
+
+
+ 31            24 23           16 15            8 7             0
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|   BLOCKS    | DMASIZE
+ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+*/
+
+
+#define PS3_AUDIO_DMASIZE_BLOCKS_MASK 	(0x7f << 0) /* RWIUF */
+
+/*
+ * source/destination address for internal fifos
+ */
+#define PS3_AUDIO_AO_3W_LDATA(n)	(0x1000 + (0x100 * (n)))
+#define PS3_AUDIO_AO_3W_RDATA(n)	(0x1080 + (0x100 * (n)))
+
+#define PS3_AUDIO_AO_SPD_DATA(n)	(0x2000 + (0x400 * (n)))
+
+
+/*
+ * field attiribute
+ *
+ *	Read
+ *	  ' ' = Other Information
+ *	  '-' = Field is part of a write-only register
+ *	  'C' = Value read is always the same, constant value line follows (C)
+ *	  'R' = Value is read
+ *
+ *	Write
+ *	  ' ' = Other Information
+ *	  '-' = Must not be written (D), value ignored when written (R,A,F)
+ *	  'W' = Can be written
+ *
+ *	Internal State
+ *	  ' ' = Other Information
+ *	  '-' = No internal state
+ *	  'X' = Internal state, initial value is unknown
+ *	  'I' = Internal state, initial value is known and follows (I)
+ *
+ *	Declaration/Size
+ *	  ' ' = Other Information
+ *	  '-' = Does Not Apply
+ *	  'V' = Type is void
+ *	  'U' = Type is unsigned integer
+ *	  'S' = Type is signed integer
+ *	  'F' = Type is IEEE floating point
+ *	  '1' = Byte size (008)
+ *	  '2' = Short size (016)
+ *	  '3' = Three byte size (024)
+ *	  '4' = Word size (032)
+ *	  '8' = Double size (064)
+ *
+ *	Define Indicator
+ *	  ' ' = Other Information
+ *	  'D' = Device
+ *	  'M' = Memory
+ *	  'R' = Register
+ *	  'A' = Array of Registers
+ *	  'F' = Field
+ *	  'V' = Value
+ *	  'T' = Task
+ */
+

+ 14 - 0
sound/sh/Kconfig

@@ -0,0 +1,14 @@
+# ALSA SH drivers
+
+menu "SUPERH devices"
+	depends on SND!=n && SUPERH
+
+config SND_AICA
+	tristate "Dreamcast Yamaha AICA sound"
+	depends on SH_DREAMCAST && SND
+	select SND_PCM
+	help
+	  ALSA Sound driver for the SEGA Dreamcast console.
+
+endmenu
+

+ 8 - 0
sound/sh/Makefile

@@ -0,0 +1,8 @@
+#
+# Makefile for ALSA
+#
+
+snd-aica-objs := aica.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_AICA) += snd-aica.o

+ 665 - 0
sound/sh/aica.c

@@ -0,0 +1,665 @@
+/*
+* This code is licenced under 
+* the General Public Licence
+* version 2
+*
+* Copyright Adrian McMenamin 2005, 2006, 2007
+* <adrian@mcmen.demon.co.uk>
+* Requires firmware (BSD licenced) available from:
+* http://linuxdc.cvs.sourceforge.net/linuxdc/linux-sh-dc/sound/oss/aica/firmware/
+* or the maintainer
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of version 2 of the GNU General Public License as published by
+* the Free Software Foundation.
+*
+* 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/jiffies.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/info.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/dreamcast/sysasic.h>
+#include "aica.h"
+
+MODULE_AUTHOR("Adrian McMenamin <adrian@mcmen.demon.co.uk>");
+MODULE_DESCRIPTION("Dreamcast AICA sound (pcm) driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{Yamaha/SEGA, AICA}}");
+
+/* module parameters */
+#define CARD_NAME "AICA"
+static int index = -1;
+static char *id;
+static int enable = 1;
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
+module_param(enable, bool, 0644);
+MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
+
+/* Use workqueue */
+static struct workqueue_struct *aica_queue;
+
+/* Simple platform device */
+static struct platform_device *pd;
+static struct resource aica_memory_space[2] = {
+	{
+	 .name = "AICA ARM CONTROL",
+	 .start = ARM_RESET_REGISTER,
+	 .flags = IORESOURCE_MEM,
+	 .end = ARM_RESET_REGISTER + 3,
+	 },
+	{
+	 .name = "AICA Sound RAM",
+	 .start = SPU_MEMORY_BASE,
+	 .flags = IORESOURCE_MEM,
+	 .end = SPU_MEMORY_BASE + 0x200000 - 1,
+	 },
+};
+
+/* SPU specific functions */
+/* spu_write_wait - wait for G2-SH FIFO to clear */
+static void spu_write_wait(void)
+{
+	int time_count;
+	time_count = 0;
+	while (1) {
+		if (!(readl(G2_FIFO) & 0x11))
+			break;
+		/* To ensure hardware failure doesn't wedge kernel */
+		time_count++;
+		if (time_count > 0x10000) {
+			snd_printk
+			    ("WARNING: G2 FIFO appears to be blocked.\n");
+			break;
+		}
+	}
+}
+
+/* spu_memset - write to memory in SPU address space */
+static void spu_memset(u32 toi, u32 what, int length)
+{
+	int i;
+	snd_assert(length % 4 == 0, return);
+	for (i = 0; i < length; i++) {
+		if (!(i % 8))
+			spu_write_wait();
+		writel(what, toi + SPU_MEMORY_BASE);
+		toi++;
+	}
+}
+
+/* spu_memload - write to SPU address space */
+static void spu_memload(u32 toi, void *from, int length)
+{
+	u32 *froml = from;
+	u32 __iomem *to = (u32 __iomem *) (SPU_MEMORY_BASE + toi);
+	int i;
+	u32 val;
+	length = DIV_ROUND_UP(length, 4);
+	spu_write_wait();
+	for (i = 0; i < length; i++) {
+		if (!(i % 8))
+			spu_write_wait();
+		val = *froml;
+		writel(val, to);
+		froml++;
+		to++;
+	}
+}
+
+/* spu_disable - set spu registers to stop sound output */
+static void spu_disable(void)
+{
+	int i;
+	u32 regval;
+	spu_write_wait();
+	regval = readl(ARM_RESET_REGISTER);
+	regval |= 1;
+	spu_write_wait();
+	writel(regval, ARM_RESET_REGISTER);
+	for (i = 0; i < 64; i++) {
+		spu_write_wait();
+		regval = readl(SPU_REGISTER_BASE + (i * 0x80));
+		regval = (regval & ~0x4000) | 0x8000;
+		spu_write_wait();
+		writel(regval, SPU_REGISTER_BASE + (i * 0x80));
+	}
+}
+
+/* spu_enable - set spu registers to enable sound output */
+static void spu_enable(void)
+{
+	u32 regval = readl(ARM_RESET_REGISTER);
+	regval &= ~1;
+	spu_write_wait();
+	writel(regval, ARM_RESET_REGISTER);
+}
+
+/* 
+ * Halt the sound processor, clear the memory,
+ * load some default ARM7 code, and then restart ARM7
+*/
+static void spu_reset(void)
+{
+	spu_disable();
+	spu_memset(0, 0, 0x200000 / 4);
+	/* Put ARM7 in endless loop */
+	ctrl_outl(0xea000002, SPU_MEMORY_BASE);
+	spu_enable();
+}
+
+/* aica_chn_start - write to spu to start playback */
+static void aica_chn_start(void)
+{
+	spu_write_wait();
+	writel(AICA_CMD_KICK | AICA_CMD_START, (u32 *) AICA_CONTROL_POINT);
+}
+
+/* aica_chn_halt - write to spu to halt playback */
+static void aica_chn_halt(void)
+{
+	spu_write_wait();
+	writel(AICA_CMD_KICK | AICA_CMD_STOP, (u32 *) AICA_CONTROL_POINT);
+}
+
+/* ALSA code below */
+static struct snd_pcm_hardware snd_pcm_aica_playback_hw = {
+	.info = (SNDRV_PCM_INFO_NONINTERLEAVED),
+	.formats =
+	    (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |
+	     SNDRV_PCM_FMTBIT_IMA_ADPCM),
+	.rates = SNDRV_PCM_RATE_8000_48000,
+	.rate_min = 8000,
+	.rate_max = 48000,
+	.channels_min = 1,
+	.channels_max = 2,
+	.buffer_bytes_max = AICA_BUFFER_SIZE,
+	.period_bytes_min = AICA_PERIOD_SIZE,
+	.period_bytes_max = AICA_PERIOD_SIZE,
+	.periods_min = AICA_PERIOD_NUMBER,
+	.periods_max = AICA_PERIOD_NUMBER,
+};
+
+static int aica_dma_transfer(int channels, int buffer_size,
+			     struct snd_pcm_substream *substream)
+{
+	int q, err, period_offset;
+	struct snd_card_aica *dreamcastcard;
+	struct snd_pcm_runtime *runtime;
+	err = 0;
+	dreamcastcard = substream->pcm->private_data;
+	period_offset = dreamcastcard->clicks;
+	period_offset %= (AICA_PERIOD_NUMBER / channels);
+	runtime = substream->runtime;
+	for (q = 0; q < channels; q++) {
+		err = dma_xfer(AICA_DMA_CHANNEL,
+			       (unsigned long) (runtime->dma_area +
+						(AICA_BUFFER_SIZE * q) /
+						channels +
+						AICA_PERIOD_SIZE *
+						period_offset),
+			       AICA_CHANNEL0_OFFSET + q * CHANNEL_OFFSET +
+			       AICA_PERIOD_SIZE * period_offset,
+			       buffer_size / channels, AICA_DMA_MODE);
+		if (unlikely(err < 0))
+			break;
+		dma_wait_for_completion(AICA_DMA_CHANNEL);
+	}
+	return err;
+}
+
+static void startup_aica(struct snd_card_aica *dreamcastcard)
+{
+	spu_memload(AICA_CHANNEL0_CONTROL_OFFSET,
+		    dreamcastcard->channel, sizeof(struct aica_channel));
+	aica_chn_start();
+}
+
+static void run_spu_dma(struct work_struct *work)
+{
+	int buffer_size;
+	struct snd_pcm_runtime *runtime;
+	struct snd_card_aica *dreamcastcard;
+	dreamcastcard =
+	    container_of(work, struct snd_card_aica, spu_dma_work);
+	runtime = dreamcastcard->substream->runtime;
+	if (unlikely(dreamcastcard->dma_check == 0)) {
+		buffer_size =
+		    frames_to_bytes(runtime, runtime->buffer_size);
+		if (runtime->channels > 1)
+			dreamcastcard->channel->flags |= 0x01;
+		aica_dma_transfer(runtime->channels, buffer_size,
+				  dreamcastcard->substream);
+		startup_aica(dreamcastcard);
+		dreamcastcard->clicks =
+		    buffer_size / (AICA_PERIOD_SIZE * runtime->channels);
+		return;
+	} else {
+		aica_dma_transfer(runtime->channels,
+				  AICA_PERIOD_SIZE * runtime->channels,
+				  dreamcastcard->substream);
+		snd_pcm_period_elapsed(dreamcastcard->substream);
+		dreamcastcard->clicks++;
+		if (unlikely(dreamcastcard->clicks >= AICA_PERIOD_NUMBER))
+			dreamcastcard->clicks %= AICA_PERIOD_NUMBER;
+		mod_timer(&dreamcastcard->timer, jiffies + 1);
+	}
+}
+
+static void aica_period_elapsed(unsigned long timer_var)
+{
+	/*timer function - so cannot sleep */
+	int play_period;
+	struct snd_pcm_runtime *runtime;
+	struct snd_pcm_substream *substream;
+	struct snd_card_aica *dreamcastcard;
+	substream = (struct snd_pcm_substream *) timer_var;
+	runtime = substream->runtime;
+	dreamcastcard = substream->pcm->private_data;
+	/* Have we played out an additional period? */
+	play_period =
+	    frames_to_bytes(runtime,
+			    readl
+			    (AICA_CONTROL_CHANNEL_SAMPLE_NUMBER)) /
+	    AICA_PERIOD_SIZE;
+	if (play_period == dreamcastcard->current_period) {
+		/* reschedule the timer */
+		mod_timer(&(dreamcastcard->timer), jiffies + 1);
+		return;
+	}
+	if (runtime->channels > 1)
+		dreamcastcard->current_period = play_period;
+	if (unlikely(dreamcastcard->dma_check == 0))
+		dreamcastcard->dma_check = 1;
+	queue_work(aica_queue, &(dreamcastcard->spu_dma_work));
+}
+
+static void spu_begin_dma(struct snd_pcm_substream *substream)
+{
+	struct snd_card_aica *dreamcastcard;
+	struct snd_pcm_runtime *runtime;
+	runtime = substream->runtime;
+	dreamcastcard = substream->pcm->private_data;
+	/*get the queue to do the work */
+	queue_work(aica_queue, &(dreamcastcard->spu_dma_work));
+	/* Timer may already be running */
+	if (unlikely(dreamcastcard->timer.data)) {
+		mod_timer(&dreamcastcard->timer, jiffies + 4);
+		return;
+	}
+	init_timer(&(dreamcastcard->timer));
+	dreamcastcard->timer.data = (unsigned long) substream;
+	dreamcastcard->timer.function = aica_period_elapsed;
+	dreamcastcard->timer.expires = jiffies + 4;
+	add_timer(&(dreamcastcard->timer));
+}
+
+static int snd_aicapcm_pcm_open(struct snd_pcm_substream
+				*substream)
+{
+	struct snd_pcm_runtime *runtime;
+	struct aica_channel *channel;
+	struct snd_card_aica *dreamcastcard;
+	if (!enable)
+		return -ENOENT;
+	dreamcastcard = substream->pcm->private_data;
+	channel = kmalloc(sizeof(struct aica_channel), GFP_KERNEL);
+	if (!channel)
+		return -ENOMEM;
+	/* set defaults for channel */
+	channel->sfmt = SM_8BIT;
+	channel->cmd = AICA_CMD_START;
+	channel->vol = dreamcastcard->master_volume;
+	channel->pan = 0x80;
+	channel->pos = 0;
+	channel->flags = 0;	/* default to mono */
+	dreamcastcard->channel = channel;
+	runtime = substream->runtime;
+	runtime->hw = snd_pcm_aica_playback_hw;
+	spu_enable();
+	dreamcastcard->clicks = 0;
+	dreamcastcard->current_period = 0;
+	dreamcastcard->dma_check = 0;
+	return 0;
+}
+
+static int snd_aicapcm_pcm_close(struct snd_pcm_substream
+				 *substream)
+{
+	struct snd_card_aica *dreamcastcard = substream->pcm->private_data;
+	flush_workqueue(aica_queue);
+	if (dreamcastcard->timer.data)
+		del_timer(&dreamcastcard->timer);
+	kfree(dreamcastcard->channel);
+	spu_disable();
+	return 0;
+}
+
+static int snd_aicapcm_pcm_hw_free(struct snd_pcm_substream
+				   *substream)
+{
+	/* Free the DMA buffer */
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_aicapcm_pcm_hw_params(struct snd_pcm_substream
+				     *substream, struct snd_pcm_hw_params
+				     *hw_params)
+{
+	/* Allocate a DMA buffer using ALSA built-ins */
+	return
+	    snd_pcm_lib_malloc_pages(substream,
+				     params_buffer_bytes(hw_params));
+}
+
+static int snd_aicapcm_pcm_prepare(struct snd_pcm_substream
+				   *substream)
+{
+	struct snd_card_aica *dreamcastcard = substream->pcm->private_data;
+	if ((substream->runtime)->format == SNDRV_PCM_FORMAT_S16_LE)
+		dreamcastcard->channel->sfmt = SM_16BIT;
+	dreamcastcard->channel->freq = substream->runtime->rate;
+	dreamcastcard->substream = substream;
+	return 0;
+}
+
+static int snd_aicapcm_pcm_trigger(struct snd_pcm_substream
+				   *substream, int cmd)
+{
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		spu_begin_dma(substream);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		aica_chn_halt();
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static unsigned long snd_aicapcm_pcm_pointer(struct snd_pcm_substream
+					     *substream)
+{
+	return readl(AICA_CONTROL_CHANNEL_SAMPLE_NUMBER);
+}
+
+static struct snd_pcm_ops snd_aicapcm_playback_ops = {
+	.open = snd_aicapcm_pcm_open,
+	.close = snd_aicapcm_pcm_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = snd_aicapcm_pcm_hw_params,
+	.hw_free = snd_aicapcm_pcm_hw_free,
+	.prepare = snd_aicapcm_pcm_prepare,
+	.trigger = snd_aicapcm_pcm_trigger,
+	.pointer = snd_aicapcm_pcm_pointer,
+};
+
+/* TO DO: set up to handle more than one pcm instance */
+static int __init snd_aicapcmchip(struct snd_card_aica
+				  *dreamcastcard, int pcm_index)
+{
+	struct snd_pcm *pcm;
+	int err;
+	/* AICA has no capture ability */
+	err =
+	    snd_pcm_new(dreamcastcard->card, "AICA PCM", pcm_index, 1, 0,
+			&pcm);
+	if (unlikely(err < 0))
+		return err;
+	pcm->private_data = dreamcastcard;
+	strcpy(pcm->name, "AICA PCM");
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+			&snd_aicapcm_playback_ops);
+	/* Allocate the DMA buffers */
+	err =
+	    snd_pcm_lib_preallocate_pages_for_all(pcm,
+						  SNDRV_DMA_TYPE_CONTINUOUS,
+						  snd_dma_continuous_data
+						  (GFP_KERNEL),
+						  AICA_BUFFER_SIZE,
+						  AICA_BUFFER_SIZE);
+	return err;
+}
+
+/* Mixer controls */
+static int aica_pcmswitch_info(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int aica_pcmswitch_get(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = 1;	/* TO DO: Fix me */
+	return 0;
+}
+
+static int aica_pcmswitch_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	if (ucontrol->value.integer.value[0] == 1)
+		return 0;	/* TO DO: Fix me */
+	else
+		aica_chn_halt();
+	return 0;
+}
+
+static int aica_pcmvolume_info(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 0xFF;
+	return 0;
+}
+
+static int aica_pcmvolume_get(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_card_aica *dreamcastcard;
+	dreamcastcard = kcontrol->private_data;
+	if (unlikely(!dreamcastcard->channel))
+		return -ETXTBSY;	/* we've not yet been set up */
+	ucontrol->value.integer.value[0] = dreamcastcard->channel->vol;
+	return 0;
+}
+
+static int aica_pcmvolume_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_card_aica *dreamcastcard;
+	dreamcastcard = kcontrol->private_data;
+	if (unlikely(!dreamcastcard->channel))
+		return -ETXTBSY;
+	if (unlikely(dreamcastcard->channel->vol ==
+		     ucontrol->value.integer.value[0]))
+		return 0;
+	dreamcastcard->channel->vol = ucontrol->value.integer.value[0];
+	dreamcastcard->master_volume = ucontrol->value.integer.value[0];
+	spu_memload(AICA_CHANNEL0_CONTROL_OFFSET,
+		    dreamcastcard->channel, sizeof(struct aica_channel));
+	return 1;
+}
+
+static struct snd_kcontrol_new snd_aica_pcmswitch_control __devinitdata = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "PCM Playback Switch",
+	.index = 0,
+	.info = aica_pcmswitch_info,
+	.get = aica_pcmswitch_get,
+	.put = aica_pcmswitch_put
+};
+
+static struct snd_kcontrol_new snd_aica_pcmvolume_control __devinitdata = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "PCM Playback Volume",
+	.index = 0,
+	.info = aica_pcmvolume_info,
+	.get = aica_pcmvolume_get,
+	.put = aica_pcmvolume_put
+};
+
+static int load_aica_firmware(void)
+{
+	int err;
+	const struct firmware *fw_entry;
+	spu_reset();
+	err = request_firmware(&fw_entry, "aica_firmware.bin", &pd->dev);
+	if (unlikely(err))
+		return err;
+	/* write firware into memory */
+	spu_disable();
+	spu_memload(0, fw_entry->data, fw_entry->size);
+	spu_enable();
+	release_firmware(fw_entry);
+	return err;
+}
+
+static int __devinit add_aicamixer_controls(struct snd_card_aica
+					    *dreamcastcard)
+{
+	int err;
+	err = snd_ctl_add
+	    (dreamcastcard->card,
+	     snd_ctl_new1(&snd_aica_pcmvolume_control, dreamcastcard));
+	if (unlikely(err < 0))
+		return err;
+	err = snd_ctl_add
+	    (dreamcastcard->card,
+	     snd_ctl_new1(&snd_aica_pcmswitch_control, dreamcastcard));
+	if (unlikely(err < 0))
+		return err;
+	return 0;
+}
+
+static int snd_aica_remove(struct platform_device *devptr)
+{
+	struct snd_card_aica *dreamcastcard;
+	dreamcastcard = platform_get_drvdata(devptr);
+	if (unlikely(!dreamcastcard))
+		return -ENODEV;
+	snd_card_free(dreamcastcard->card);
+	kfree(dreamcastcard);
+	platform_set_drvdata(devptr, NULL);
+	return 0;
+}
+
+static int __init snd_aica_probe(struct platform_device *devptr)
+{
+	int err;
+	struct snd_card_aica *dreamcastcard;
+	dreamcastcard = kmalloc(sizeof(struct snd_card_aica), GFP_KERNEL);
+	if (unlikely(!dreamcastcard))
+		return -ENOMEM;
+	dreamcastcard->card =
+	    snd_card_new(index, SND_AICA_DRIVER, THIS_MODULE, 0);
+	if (unlikely(!dreamcastcard->card)) {
+		kfree(dreamcastcard);
+		return -ENODEV;
+	}
+	strcpy(dreamcastcard->card->driver, "snd_aica");
+	strcpy(dreamcastcard->card->shortname, SND_AICA_DRIVER);
+	strcpy(dreamcastcard->card->longname,
+	       "Yamaha AICA Super Intelligent Sound Processor for SEGA Dreamcast");
+	/* Prepare to use the queue */
+	INIT_WORK(&(dreamcastcard->spu_dma_work), run_spu_dma);
+	/* Load the PCM 'chip' */
+	err = snd_aicapcmchip(dreamcastcard, 0);
+	if (unlikely(err < 0))
+		goto freedreamcast;
+	snd_card_set_dev(dreamcastcard->card, &devptr->dev);
+	dreamcastcard->timer.data = 0;
+	dreamcastcard->channel = NULL;
+	/* Add basic controls */
+	err = add_aicamixer_controls(dreamcastcard);
+	if (unlikely(err < 0))
+		goto freedreamcast;
+	/* Register the card with ALSA subsystem */
+	err = snd_card_register(dreamcastcard->card);
+	if (unlikely(err < 0))
+		goto freedreamcast;
+	platform_set_drvdata(devptr, dreamcastcard);
+	aica_queue = create_workqueue(CARD_NAME);
+	if (unlikely(!aica_queue))
+		goto freedreamcast;
+	snd_printk
+	    ("ALSA Driver for Yamaha AICA Super Intelligent Sound Processor\n");
+	return 0;
+      freedreamcast:
+	snd_card_free(dreamcastcard->card);
+	kfree(dreamcastcard);
+	return err;
+}
+
+static struct platform_driver snd_aica_driver = {
+	.probe = snd_aica_probe,
+	.remove = snd_aica_remove,
+	.driver = {
+		   .name = SND_AICA_DRIVER},
+};
+
+static int __init aica_init(void)
+{
+	int err;
+	err = platform_driver_register(&snd_aica_driver);
+	if (unlikely(err < 0))
+		return err;
+	pd = platform_device_register_simple(SND_AICA_DRIVER, -1,
+					     aica_memory_space, 2);
+	if (unlikely(IS_ERR(pd))) {
+		platform_driver_unregister(&snd_aica_driver);
+		return PTR_ERR(pd);
+	}
+	/* Load the firmware */
+	return load_aica_firmware();
+}
+
+static void __exit aica_exit(void)
+{
+	/* Destroy the aica kernel thread            *
+	 * being extra cautious to check if it exists*/
+	if (likely(aica_queue))
+		destroy_workqueue(aica_queue);
+	platform_device_unregister(pd);
+	platform_driver_unregister(&snd_aica_driver);
+	/* Kill any sound still playing and reset ARM7 to safe state */
+	spu_reset();
+}
+
+module_init(aica_init);
+module_exit(aica_exit);

+ 81 - 0
sound/sh/aica.h

@@ -0,0 +1,81 @@
+/* aica.h
+ * Header file for ALSA driver for
+ * Sega Dreamcast Yamaha AICA sound
+ * Copyright Adrian McMenamin
+ * <adrian@mcmen.demon.co.uk>
+ * 2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * 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
+ *
+ */
+
+/* SPU memory and register constants etc */
+#define G2_FIFO 0xa05f688c
+#define SPU_MEMORY_BASE 0xA0800000
+#define ARM_RESET_REGISTER 0xA0702C00
+#define SPU_REGISTER_BASE 0xA0700000
+
+/* AICA channels stuff */
+#define AICA_CONTROL_POINT 0xA0810000
+#define AICA_CONTROL_CHANNEL_SAMPLE_NUMBER 0xA0810008
+#define AICA_CHANNEL0_CONTROL_OFFSET 0x10004
+
+/* Command values */
+#define AICA_CMD_KICK 0x80000000
+#define AICA_CMD_NONE 0
+#define AICA_CMD_START 1
+#define AICA_CMD_STOP 2
+#define AICA_CMD_VOL 3
+
+/* Sound modes */
+#define SM_8BIT		1
+#define SM_16BIT	0
+#define SM_ADPCM	2
+
+/* Buffer and period size */
+#define AICA_BUFFER_SIZE 0x8000
+#define AICA_PERIOD_SIZE 0x800
+#define AICA_PERIOD_NUMBER 16
+
+#define AICA_CHANNEL0_OFFSET 0x11000
+#define AICA_CHANNEL1_OFFSET 0x21000
+#define CHANNEL_OFFSET 0x10000
+
+#define AICA_DMA_CHANNEL 0
+#define AICA_DMA_MODE 5
+
+#define SND_AICA_DRIVER "AICA"
+
+struct aica_channel {
+	uint32_t cmd;		/* Command ID           */
+	uint32_t pos;		/* Sample position      */
+	uint32_t length;	/* Sample length        */
+	uint32_t freq;		/* Frequency            */
+	uint32_t vol;		/* Volume 0-255         */
+	uint32_t pan;		/* Pan 0-255            */
+	uint32_t sfmt;		/* Sound format         */
+	uint32_t flags;		/* Bit flags            */
+};
+
+struct snd_card_aica {
+	struct work_struct spu_dma_work;
+	struct snd_card *card;
+	struct aica_channel *channel;
+	struct snd_pcm_substream *substream;
+	int clicks;
+	int current_period;
+	struct timer_list timer;
+	int master_volume;
+	int dma_check;
+};

+ 1 - 0
sound/soc/Kconfig

@@ -27,6 +27,7 @@ config SND_SOC
 source "sound/soc/at91/Kconfig"
 source "sound/soc/at91/Kconfig"
 source "sound/soc/pxa/Kconfig"
 source "sound/soc/pxa/Kconfig"
 source "sound/soc/s3c24xx/Kconfig"
 source "sound/soc/s3c24xx/Kconfig"
+source "sound/soc/sh/Kconfig"
 
 
 # Supported codecs
 # Supported codecs
 source "sound/soc/codecs/Kconfig"
 source "sound/soc/codecs/Kconfig"

+ 1 - 1
sound/soc/Makefile

@@ -1,4 +1,4 @@
 snd-soc-core-objs := soc-core.o soc-dapm.o
 snd-soc-core-objs := soc-core.o soc-dapm.o
 
 
 obj-$(CONFIG_SND_SOC)	+= snd-soc-core.o
 obj-$(CONFIG_SND_SOC)	+= snd-soc-core.o
-obj-$(CONFIG_SND_SOC)	+= codecs/ at91/ pxa/ s3c24xx/
+obj-$(CONFIG_SND_SOC)	+= codecs/ at91/ pxa/ s3c24xx/ sh/

+ 27 - 0
sound/soc/s3c24xx/Kconfig

@@ -1,6 +1,7 @@
 config SND_S3C24XX_SOC
 config SND_S3C24XX_SOC
 	tristate "SoC Audio for the Samsung S3C24XX chips"
 	tristate "SoC Audio for the Samsung S3C24XX chips"
 	depends on ARCH_S3C2410 && SND_SOC
 	depends on ARCH_S3C2410 && SND_SOC
+	select SND_PCM
 	help
 	help
 	  Say Y or M if you want to add support for codecs attached to
 	  Say Y or M if you want to add support for codecs attached to
 	  the S3C24XX AC97, I2S or SSP interface. You will also need
 	  the S3C24XX AC97, I2S or SSP interface. You will also need
@@ -8,3 +9,29 @@ config SND_S3C24XX_SOC
 
 
 config SND_S3C24XX_SOC_I2S
 config SND_S3C24XX_SOC_I2S
 	tristate
 	tristate
+
+config SND_S3C2443_SOC_AC97
+	tristate
+	select AC97_BUS
+	select SND_AC97_CODEC
+	select SND_SOC_AC97_BUS
+	
+config SND_S3C24XX_SOC_NEO1973_WM8753
+	tristate "SoC I2S Audio support for NEO1973 - WM8753"
+	depends on SND_S3C24XX_SOC && MACH_GTA01
+	select SND_S3C24XX_SOC_I2S
+	select SND_SOC_WM8753
+	help
+	  Say Y if you want to add support for SoC audio on smdk2440
+	  with the WM8753.
+
+config SND_S3C24XX_SOC_SMDK2443_WM9710
+	tristate "SoC AC97 Audio support for SMDK2443 - WM9710"
+	depends on SND_S3C24XX_SOC && MACH_SMDK2443
+	select SND_S3C2443_SOC_AC97
+	select SND_SOC_AC97_CODEC
+	help
+	  Say Y if you want to add support for SoC audio on smdk2443
+	  with the WM9710.
+
+

+ 9 - 0
sound/soc/s3c24xx/Makefile

@@ -1,6 +1,15 @@
 # S3c24XX Platform Support
 # S3c24XX Platform Support
 snd-soc-s3c24xx-objs := s3c24xx-pcm.o
 snd-soc-s3c24xx-objs := s3c24xx-pcm.o
 snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o
 snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o
+snd-soc-s3c2443-ac97-objs := s3c2443-ac97.o
 
 
 obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o
 obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o
 obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o
 obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o
+obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.o
+
+# S3C24XX Machine Support
+snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o
+snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o
+
+obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
+obj-$(CONFIG_SND_S3C24XX_SOC_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o

+ 32 - 0
sound/soc/s3c24xx/lm4857.h

@@ -0,0 +1,32 @@
+/*
+ * lm4857.h  --  ALSA Soc Audio Layer
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ *         graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.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.
+ *
+ *  Revision history
+ *    18th Jun 2007   Initial version.
+ */
+
+#ifndef LM4857_H_
+#define LM4857_H_
+
+/* The register offsets in the cache array */
+#define LM4857_MVOL 0
+#define LM4857_LVOL 1
+#define LM4857_RVOL 2
+#define LM4857_CTRL 3
+
+/* the shifts required to set these bits */
+#define LM4857_3D 5
+#define LM4857_WAKEUP 5
+#define LM4857_EPGAIN 4
+
+#endif /*LM4857_H_*/
+

+ 670 - 0
sound/soc/s3c24xx/neo1973_wm8753.c

@@ -0,0 +1,670 @@
+/*
+ * neo1973_wm8753.c  --  SoC audio for Neo1973
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ *         graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.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.
+ *
+ *  Revision history
+ *    20th Jan 2007   Initial version.
+ *    05th Feb 2007   Rename all to Neo1973
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <asm/hardware/scoop.h>
+#include <asm/arch/regs-iis.h>
+#include <asm/arch/regs-clock.h>
+#include <asm/arch/regs-gpio.h>
+#include <asm/hardware.h>
+#include <asm/arch/audio.h>
+#include <asm/io.h>
+#include <asm/arch/spi-gpio.h>
+#include "../codecs/wm8753.h"
+#include "lm4857.h"
+#include "s3c24xx-pcm.h"
+#include "s3c24xx-i2s.h"
+
+/* define the scenarios */
+#define NEO_AUDIO_OFF			0
+#define NEO_GSM_CALL_AUDIO_HANDSET	1
+#define NEO_GSM_CALL_AUDIO_HEADSET	2
+#define NEO_GSM_CALL_AUDIO_BLUETOOTH	3
+#define NEO_STEREO_TO_SPEAKERS		4
+#define NEO_STEREO_TO_HEADPHONES	5
+#define NEO_CAPTURE_HANDSET		6
+#define NEO_CAPTURE_HEADSET		7
+#define NEO_CAPTURE_BLUETOOTH		8
+
+static struct snd_soc_machine neo1973;
+static struct i2c_client *i2c;
+
+static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+	unsigned int pll_out = 0, bclk = 0;
+	int ret = 0;
+	unsigned long iis_clkrate;
+
+	iis_clkrate = s3c24xx_i2s_get_clockrate();
+
+	switch (params_rate(params)) {
+	case 8000:
+	case 16000:
+		pll_out = 12288000;
+		break;
+	case 48000:
+		bclk = WM8753_BCLK_DIV_4;
+		pll_out = 12288000;
+		break;
+	case 96000:
+		bclk = WM8753_BCLK_DIV_2;
+		pll_out = 12288000;
+		break;
+	case 11025:
+		bclk = WM8753_BCLK_DIV_16;
+		pll_out = 11289600;
+		break;
+	case 22050:
+		bclk = WM8753_BCLK_DIV_8;
+		pll_out = 11289600;
+		break;
+	case 44100:
+		bclk = WM8753_BCLK_DIV_4;
+		pll_out = 11289600;
+		break;
+	case 88200:
+		bclk = WM8753_BCLK_DIV_2;
+		pll_out = 11289600;
+		break;
+	}
+
+	/* set codec DAI configuration */
+	ret = codec_dai->dai_ops.set_fmt(codec_dai,
+		SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+		SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0)
+		return ret;
+
+	/* set cpu DAI configuration */
+	ret = cpu_dai->dai_ops.set_fmt(cpu_dai,
+		SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+		SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0)
+		return ret;
+
+	/* set the codec system clock for DAC and ADC */
+	ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_MCLK, pll_out,
+		SND_SOC_CLOCK_IN);
+	if (ret < 0)
+		return ret;
+
+	/* set MCLK division for sample rate */
+	ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
+		S3C2410_IISMOD_32FS );
+	if (ret < 0)
+		return ret;
+
+	/* set codec BCLK division for sample rate */
+	ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_BCLKDIV, bclk);
+	if (ret < 0)
+		return ret;
+
+	/* set prescaler division for sample rate */
+	ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
+		S3C24XX_PRESCALE(4,4));
+	if (ret < 0)
+		return ret;
+
+	/* codec PLL input is PCLK/4 */
+	ret = codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1,
+		iis_clkrate / 4, pll_out);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
+
+	/* disable the PLL */
+	return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1, 0, 0);
+}
+
+/*
+ * Neo1973 WM8753 HiFi DAI opserations.
+ */
+static struct snd_soc_ops neo1973_hifi_ops = {
+	.hw_params = neo1973_hifi_hw_params,
+	.hw_free = neo1973_hifi_hw_free,
+};
+
+static int neo1973_voice_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
+	unsigned int pcmdiv = 0;
+	int ret = 0;
+	unsigned long iis_clkrate;
+
+	iis_clkrate = s3c24xx_i2s_get_clockrate();
+
+	if (params_rate(params) != 8000)
+		return -EINVAL;
+	if (params_channels(params) != 1)
+		return -EINVAL;
+
+	pcmdiv = WM8753_PCM_DIV_6; /* 2.048 MHz */
+
+	/* todo: gg check mode (DSP_B) against CSR datasheet */
+	/* set codec DAI configuration */
+	ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B |
+		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0)
+		return ret;
+
+	/* set the codec system clock for DAC and ADC */
+	ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_PCMCLK, 12288000,
+		SND_SOC_CLOCK_IN);
+	if (ret < 0)
+		return ret;
+
+	/* set codec PCM division for sample rate */
+	ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv);
+	if (ret < 0)
+		return ret;
+
+	/* configue and enable PLL for 12.288MHz output */
+	ret = codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL2,
+		iis_clkrate / 4, 12288000);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int neo1973_voice_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
+
+	/* disable the PLL */
+	return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL2, 0, 0);
+}
+
+static struct snd_soc_ops neo1973_voice_ops = {
+	.hw_params = neo1973_voice_hw_params,
+	.hw_free = neo1973_voice_hw_free,
+};
+
+static int neo1973_scenario = 0;
+
+static int neo1973_get_scenario(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = neo1973_scenario;
+	return 0;
+}
+
+static int set_scenario_endpoints(struct snd_soc_codec *codec, int scenario)
+{
+	switch(neo1973_scenario) {
+	case NEO_AUDIO_OFF:
+		snd_soc_dapm_set_endpoint(codec, "Audio Out",    0);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  0);
+		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
+		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+		break;
+	case NEO_GSM_CALL_AUDIO_HANDSET:
+		snd_soc_dapm_set_endpoint(codec, "Audio Out",    1);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  1);
+		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
+		snd_soc_dapm_set_endpoint(codec, "Call Mic",     1);
+		break;
+	case NEO_GSM_CALL_AUDIO_HEADSET:
+		snd_soc_dapm_set_endpoint(codec, "Audio Out",    1);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  1);
+		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  1);
+		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+		break;
+	case NEO_GSM_CALL_AUDIO_BLUETOOTH:
+		snd_soc_dapm_set_endpoint(codec, "Audio Out",    0);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  1);
+		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
+		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+		break;
+	case NEO_STEREO_TO_SPEAKERS:
+		snd_soc_dapm_set_endpoint(codec, "Audio Out",    1);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  0);
+		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
+		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+		break;
+	case NEO_STEREO_TO_HEADPHONES:
+		snd_soc_dapm_set_endpoint(codec, "Audio Out",    1);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  0);
+		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
+		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+		break;
+	case NEO_CAPTURE_HANDSET:
+		snd_soc_dapm_set_endpoint(codec, "Audio Out",    0);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  0);
+		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
+		snd_soc_dapm_set_endpoint(codec, "Call Mic",     1);
+		break;
+	case NEO_CAPTURE_HEADSET:
+		snd_soc_dapm_set_endpoint(codec, "Audio Out",    0);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  0);
+		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  1);
+		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+		break;
+	case NEO_CAPTURE_BLUETOOTH:
+		snd_soc_dapm_set_endpoint(codec, "Audio Out",    0);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  0);
+		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
+		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+		break;
+	default:
+		snd_soc_dapm_set_endpoint(codec, "Audio Out",    0);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
+		snd_soc_dapm_set_endpoint(codec, "GSM Line In",  0);
+		snd_soc_dapm_set_endpoint(codec, "Headset Mic",  0);
+		snd_soc_dapm_set_endpoint(codec, "Call Mic",     0);
+	}
+
+	snd_soc_dapm_sync_endpoints(codec);
+
+	return 0;
+}
+
+static int neo1973_set_scenario(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+	if (neo1973_scenario == ucontrol->value.integer.value[0])
+		return 0;
+
+	neo1973_scenario = ucontrol->value.integer.value[0];
+	set_scenario_endpoints(codec, neo1973_scenario);
+	return 1;
+}
+
+static u8 lm4857_regs[4] = {0x00, 0x40, 0x80, 0xC0};
+
+static void lm4857_write_regs(void)
+{
+	if (i2c_master_send(i2c, lm4857_regs, 4) != 4)
+		printk(KERN_ERR "lm4857: i2c write failed\n");
+}
+
+static int lm4857_get_reg(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	int reg=kcontrol->private_value & 0xFF;
+	int shift = (kcontrol->private_value >> 8) & 0x0F;
+	int mask = (kcontrol->private_value >> 16) & 0xFF;
+
+	ucontrol->value.integer.value[0] = (lm4857_regs[reg] >> shift) & mask;
+	return 0;
+}
+
+static int lm4857_set_reg(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	int reg = kcontrol->private_value & 0xFF;
+	int shift = (kcontrol->private_value >> 8) & 0x0F;
+	int mask = (kcontrol->private_value >> 16) & 0xFF;
+
+	if (((lm4857_regs[reg] >> shift ) & mask) ==
+		ucontrol->value.integer.value[0])
+		return 0;
+
+	lm4857_regs[reg] &= ~ (mask << shift);
+	lm4857_regs[reg] |= ucontrol->value.integer.value[0] << shift;
+	lm4857_write_regs();
+	return 1;
+}
+
+static int lm4857_get_mode(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	u8 value = lm4857_regs[LM4857_CTRL] & 0x0F;
+
+	if (value)
+		value -= 5;
+
+	ucontrol->value.integer.value[0] = value;
+	return 0;
+}
+
+static int lm4857_set_mode(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	u8 value = ucontrol->value.integer.value[0];
+
+	if (value)
+		value += 5;
+
+	if ((lm4857_regs[LM4857_CTRL] & 0x0F) == value)
+		return 0;
+
+	lm4857_regs[LM4857_CTRL] &= 0xF0;
+	lm4857_regs[LM4857_CTRL] |= value;
+	lm4857_write_regs();
+	return 1;
+}
+
+static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = {
+	SND_SOC_DAPM_LINE("Audio Out", NULL),
+	SND_SOC_DAPM_LINE("GSM Line Out", NULL),
+	SND_SOC_DAPM_LINE("GSM Line In", NULL),
+	SND_SOC_DAPM_MIC("Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("Call Mic", NULL),
+};
+
+
+/* example machine audio_mapnections */
+static const char* audio_map[][3] = {
+
+	/* Connections to the lm4857 amp */
+	{"Audio Out", NULL, "LOUT1"},
+	{"Audio Out", NULL, "ROUT1"},
+
+	/* Connections to the GSM Module */
+	{"GSM Line Out", NULL, "MONO1"},
+	{"GSM Line Out", NULL, "MONO2"},
+	{"RXP", NULL, "GSM Line In"},
+	{"RXN", NULL, "GSM Line In"},
+
+	/* Connections to Headset */
+	{"MIC1", NULL, "Mic Bias"},
+	{"Mic Bias", NULL, "Headset Mic"},
+
+	/* Call Mic */
+	{"MIC2", NULL, "Mic Bias"},
+	{"MIC2N", NULL, "Mic Bias"},
+	{"Mic Bias", NULL, "Call Mic"},
+
+	/* Connect the ALC pins */
+	{"ACIN", NULL, "ACOP"},
+
+	{NULL, NULL, NULL},
+};
+
+static const char *lm4857_mode[] = {
+	"Off",
+	"Call Speaker",
+	"Stereo Speakers",
+	"Stereo Speakers + Headphones",
+	"Headphones"
+};
+
+static const struct soc_enum lm4857_mode_enum[] = {
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lm4857_mode), lm4857_mode),
+};
+
+static const char *neo_scenarios[] = {
+	"Off",
+	"GSM Handset",
+	"GSM Headset",
+	"GSM Bluetooth",
+	"Speakers",
+	"Headphones",
+	"Capture Handset",
+	"Capture Headset",
+	"Capture Bluetooth"
+};
+
+static const struct soc_enum neo_scenario_enum[] = {
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(neo_scenarios),neo_scenarios),
+};
+
+static const struct snd_kcontrol_new wm8753_neo1973_controls[] = {
+	SOC_SINGLE_EXT("Amp Left Playback Volume", LM4857_LVOL, 0, 31, 0,
+		lm4857_get_reg, lm4857_set_reg),
+	SOC_SINGLE_EXT("Amp Right Playback Volume", LM4857_RVOL, 0, 31, 0,
+		lm4857_get_reg, lm4857_set_reg),
+	SOC_SINGLE_EXT("Amp Mono Playback Volume", LM4857_MVOL, 0, 31, 0,
+		lm4857_get_reg, lm4857_set_reg),
+	SOC_ENUM_EXT("Amp Mode", lm4857_mode_enum[0],
+		lm4857_get_mode, lm4857_set_mode),
+	SOC_ENUM_EXT("Neo Mode", neo_scenario_enum[0],
+		neo1973_get_scenario, neo1973_set_scenario),
+	SOC_SINGLE_EXT("Amp Spk 3D Playback Switch", LM4857_LVOL, 5, 1, 0,
+		lm4857_get_reg, lm4857_set_reg),
+	SOC_SINGLE_EXT("Amp HP 3d Playback Switch", LM4857_RVOL, 5, 1, 0,
+		lm4857_get_reg, lm4857_set_reg),
+	SOC_SINGLE_EXT("Amp Fast Wakeup Playback Switch", LM4857_CTRL, 5, 1, 0,
+		lm4857_get_reg, lm4857_set_reg),
+	SOC_SINGLE_EXT("Amp Earpiece 6dB Playback Switch", LM4857_CTRL, 4, 1, 0,
+		lm4857_get_reg, lm4857_set_reg),
+};
+
+/*
+ * This is an example machine initialisation for a wm8753 connected to a
+ * neo1973 II. It is missing logic to detect hp/mic insertions and logic
+ * to re-route the audio in such an event.
+ */
+static int neo1973_wm8753_init(struct snd_soc_codec *codec)
+{
+	int i, err;
+
+	/* set up NC codec pins */
+	snd_soc_dapm_set_endpoint(codec, "LOUT2", 0);
+	snd_soc_dapm_set_endpoint(codec, "ROUT2", 0);
+	snd_soc_dapm_set_endpoint(codec, "OUT3",  0);
+	snd_soc_dapm_set_endpoint(codec, "OUT4",  0);
+	snd_soc_dapm_set_endpoint(codec, "LINE1", 0);
+	snd_soc_dapm_set_endpoint(codec, "LINE2", 0);
+
+
+	/* set endpoints to default mode */
+	set_scenario_endpoints(codec, NEO_AUDIO_OFF);
+
+	/* Add neo1973 specific widgets */
+	for (i = 0; i < ARRAY_SIZE(wm8753_dapm_widgets); i++)
+		snd_soc_dapm_new_control(codec, &wm8753_dapm_widgets[i]);
+
+	/* add neo1973 specific controls */
+	for (i = 0; i < ARRAY_SIZE(wm8753_neo1973_controls); i++) {
+		err = snd_ctl_add(codec->card,
+				snd_soc_cnew(&wm8753_neo1973_controls[i],
+				codec, NULL));
+		if (err < 0)
+			return err;
+	}
+
+	/* set up neo1973 specific audio path audio_mapnects */
+	for (i = 0; audio_map[i][0] != NULL; i++) {
+		snd_soc_dapm_connect_input(codec, audio_map[i][0],
+			audio_map[i][1], audio_map[i][2]);
+	}
+
+	snd_soc_dapm_sync_endpoints(codec);
+	return 0;
+}
+
+/*
+ * BT Codec DAI
+ */
+static struct snd_soc_cpu_dai bt_dai =
+{	.name = "Bluetooth",
+	.id = 0,
+	.type = SND_SOC_DAI_PCM,
+	.playback = {
+		.channels_min = 1,
+		.channels_max = 1,
+		.rates = SNDRV_PCM_RATE_8000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+	.capture = {
+		.channels_min = 1,
+		.channels_max = 1,
+		.rates = SNDRV_PCM_RATE_8000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+};
+
+static struct snd_soc_dai_link neo1973_dai[] = {
+{ /* Hifi Playback - for similatious use with voice below */
+	.name = "WM8753",
+	.stream_name = "WM8753 HiFi",
+	.cpu_dai = &s3c24xx_i2s_dai,
+	.codec_dai = &wm8753_dai[WM8753_DAI_HIFI],
+	.init = neo1973_wm8753_init,
+	.ops = &neo1973_hifi_ops,
+},
+{ /* Voice via BT */
+	.name = "Bluetooth",
+	.stream_name = "Voice",
+	.cpu_dai = &bt_dai,
+	.codec_dai = &wm8753_dai[WM8753_DAI_VOICE],
+	.ops = &neo1973_voice_ops,
+},
+};
+
+static struct snd_soc_machine neo1973 = {
+	.name = "neo1973",
+	.dai_link = neo1973_dai,
+	.num_links = ARRAY_SIZE(neo1973_dai),
+};
+
+static struct wm8753_setup_data neo1973_wm8753_setup = {
+	.i2c_address = 0x1a,
+};
+
+static struct snd_soc_device neo1973_snd_devdata = {
+	.machine = &neo1973,
+	.platform = &s3c24xx_soc_platform,
+	.codec_dev = &soc_codec_dev_wm8753,
+	.codec_data = &neo1973_wm8753_setup,
+};
+
+static struct i2c_client client_template;
+
+static unsigned short normal_i2c[] = { 0x7C, I2C_CLIENT_END };
+
+/* Magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static int lm4857_amp_probe(struct i2c_adapter *adap, int addr, int kind)
+{
+	int ret;
+
+	client_template.adapter = adap;
+	client_template.addr = addr;
+
+	i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
+	if (i2c == NULL)
+		return -ENOMEM;
+
+	ret = i2c_attach_client(i2c);
+	if (ret < 0) {
+		printk(KERN_ERR "LM4857 failed to attach at addr %x\n", addr);
+		goto exit_err;
+	}
+
+	lm4857_write_regs();
+	return ret;
+
+exit_err:
+	kfree(i2c);
+	return ret;
+}
+
+static int lm4857_i2c_detach(struct i2c_client *client)
+{
+	i2c_detach_client(client);
+	kfree(client);
+	return 0;
+}
+
+static int lm4857_i2c_attach(struct i2c_adapter *adap)
+{
+	return i2c_probe(adap, &addr_data, lm4857_amp_probe);
+}
+
+/* corgi i2c codec control layer */
+static struct i2c_driver lm4857_i2c_driver = {
+	.driver = {
+		.name = "LM4857 I2C Amp",
+		.owner = THIS_MODULE,
+	},
+	.id =             I2C_DRIVERID_LM4857,
+	.attach_adapter = lm4857_i2c_attach,
+	.detach_client =  lm4857_i2c_detach,
+	.command =        NULL,
+};
+
+static struct i2c_client client_template = {
+	.name =   "LM4857",
+	.driver = &lm4857_i2c_driver,
+};
+
+static struct platform_device *neo1973_snd_device;
+
+static int __init neo1973_init(void)
+{
+	int ret;
+
+	neo1973_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!neo1973_snd_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(neo1973_snd_device, &neo1973_snd_devdata);
+	neo1973_snd_devdata.dev = &neo1973_snd_device->dev;
+	ret = platform_device_add(neo1973_snd_device);
+
+	if (ret)
+		platform_device_put(neo1973_snd_device);
+
+	ret = i2c_add_driver(&lm4857_i2c_driver);
+	if (ret != 0)
+		printk(KERN_ERR "can't add i2c driver");
+
+	return ret;
+}
+
+static void __exit neo1973_exit(void)
+{
+	platform_device_unregister(neo1973_snd_device);
+}
+
+module_init(neo1973_init);
+module_exit(neo1973_exit);
+
+/* Module information */
+MODULE_AUTHOR("Graeme Gregory, graeme.gregory@wolfsonmicro.com, www.wolfsonmicro.com");
+MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973");
+MODULE_LICENSE("GPL");

+ 401 - 0
sound/soc/s3c24xx/s3c2443-ac97.c

@@ -0,0 +1,401 @@
+/*
+ * s3c2443-ac97.c  --  ALSA Soc Audio Layer
+ *
+ * (c) 2007 Wolfson Microelectronics PLC.
+ * Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ *  Copyright (C) 2005, Sean Choi <sh428.choi@samsung.com>
+ *  All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  Revision history
+ *	21st Mar 2007   Initial Version
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/arch/regs-ac97.h>
+#include <asm/arch/regs-gpio.h>
+#include <asm/arch/regs-clock.h>
+#include <asm/arch/audio.h>
+#include <asm/dma.h>
+#include <asm/arch/dma.h>
+
+#include "s3c24xx-pcm.h"
+#include "s3c24xx-ac97.h"
+
+struct s3c24xx_ac97_info {
+	void __iomem	*regs;
+	struct clk	*ac97_clk;
+};
+static struct s3c24xx_ac97_info s3c24xx_ac97;
+
+DECLARE_COMPLETION(ac97_completion);
+static u32 codec_ready;
+static DECLARE_MUTEX(ac97_mutex);
+
+static unsigned short s3c2443_ac97_read(struct snd_ac97 *ac97,
+	unsigned short reg)
+{
+	u32 ac_glbctrl;
+	u32 ac_codec_cmd;
+	u32 stat, addr, data;
+
+	down(&ac97_mutex);
+
+	codec_ready = S3C_AC97_GLBSTAT_CODECREADY;
+	ac_codec_cmd = readl(s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
+	ac_codec_cmd = S3C_AC97_CODEC_CMD_READ | AC_CMD_ADDR(reg);
+	writel(ac_codec_cmd, s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
+
+	udelay(50);
+
+	ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
+	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+
+	wait_for_completion(&ac97_completion);
+
+	stat = readl(s3c24xx_ac97.regs + S3C_AC97_STAT);
+	addr = (stat >> 16) & 0x7f;
+	data = (stat & 0xffff);
+
+	if (addr != reg)
+		printk(KERN_ERR "s3c24xx-ac97: req addr = %02x,"
+				" rep addr = %02x\n", reg, addr);
+
+	up(&ac97_mutex);
+
+	return (unsigned short)data;
+}
+
+static void s3c2443_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+	unsigned short val)
+{
+	u32 ac_glbctrl;
+	u32 ac_codec_cmd;
+
+	down(&ac97_mutex);
+
+	codec_ready = S3C_AC97_GLBSTAT_CODECREADY;
+	ac_codec_cmd = readl(s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
+	ac_codec_cmd = AC_CMD_ADDR(reg) | AC_CMD_DATA(val);
+	writel(ac_codec_cmd, s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
+
+	udelay(50);
+
+	ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
+	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+
+	wait_for_completion(&ac97_completion);
+
+	ac_codec_cmd = readl(s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
+	ac_codec_cmd |= S3C_AC97_CODEC_CMD_READ;
+	writel(ac_codec_cmd, s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
+
+	up(&ac97_mutex);
+
+}
+
+static void s3c2443_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+	u32 ac_glbctrl;
+
+	ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	ac_glbctrl = S3C_AC97_GLBCTRL_WARMRESET;
+	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+
+	ac_glbctrl = 0;
+	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+}
+
+static void s3c2443_ac97_cold_reset(struct snd_ac97 *ac97)
+{
+	u32 ac_glbctrl;
+
+	ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	ac_glbctrl = S3C_AC97_GLBCTRL_COLDRESET;
+	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+
+	ac_glbctrl = 0;
+	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+
+	ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
+	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+
+	ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE;
+	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+
+	ac_glbctrl |= S3C_AC97_GLBCTRL_PCMOUTTM_DMA |
+		S3C_AC97_GLBCTRL_PCMINTM_DMA | S3C_AC97_GLBCTRL_MICINTM_DMA;
+	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+}
+
+static irqreturn_t s3c2443_ac97_irq(int irq, void *dev_id)
+{
+	int status;
+	u32 ac_glbctrl;
+
+	status = readl(s3c24xx_ac97.regs + S3C_AC97_GLBSTAT) & codec_ready;
+
+	if (status) {
+		ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+		ac_glbctrl &= ~S3C_AC97_GLBCTRL_CODECREADYIE;
+		writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+		complete(&ac97_completion);
+	}
+	return IRQ_HANDLED;
+}
+
+struct snd_ac97_bus_ops soc_ac97_ops = {
+	.read	= s3c2443_ac97_read,
+	.write	= s3c2443_ac97_write,
+	.warm_reset	= s3c2443_ac97_warm_reset,
+	.reset	= s3c2443_ac97_cold_reset,
+};
+
+static struct s3c2410_dma_client s3c2443_dma_client_out = {
+	.name = "AC97 PCM Stereo out"
+};
+
+static struct s3c2410_dma_client s3c2443_dma_client_in = {
+	.name = "AC97 PCM Stereo in"
+};
+
+static struct s3c2410_dma_client s3c2443_dma_client_micin = {
+	.name = "AC97 Mic Mono in"
+};
+
+static struct s3c24xx_pcm_dma_params s3c2443_ac97_pcm_stereo_out = {
+	.client		= &s3c2443_dma_client_out,
+	.channel	= DMACH_PCM_OUT,
+	.dma_addr	= S3C2440_PA_AC97 + S3C_AC97_PCM_DATA,
+	.dma_size	= 4,
+};
+
+static struct s3c24xx_pcm_dma_params s3c2443_ac97_pcm_stereo_in = {
+	.client		= &s3c2443_dma_client_in,
+	.channel	= DMACH_PCM_IN,
+	.dma_addr	= S3C2440_PA_AC97 + S3C_AC97_PCM_DATA,
+	.dma_size	= 4,
+};
+
+static struct s3c24xx_pcm_dma_params s3c2443_ac97_mic_mono_in = {
+	.client		= &s3c2443_dma_client_micin,
+	.channel	= DMACH_MIC_IN,
+	.dma_addr	= S3C2440_PA_AC97 + S3C_AC97_MIC_DATA,
+	.dma_size	= 4,
+};
+
+static int s3c2443_ac97_probe(struct platform_device *pdev)
+{
+	int ret;
+	u32 ac_glbctrl;
+
+	s3c24xx_ac97.regs = ioremap(S3C2440_PA_AC97, 0x100);
+	if (s3c24xx_ac97.regs == NULL)
+		return -ENXIO;
+
+	s3c24xx_ac97.ac97_clk = clk_get(&pdev->dev, "ac97");
+	if (s3c24xx_ac97.ac97_clk == NULL) {
+		printk(KERN_ERR "s3c2443-ac97 failed to get ac97_clock\n");
+		iounmap(s3c24xx_ac97.regs);
+		return -ENODEV;
+	}
+	clk_enable(s3c24xx_ac97.ac97_clk);
+
+	s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2443_GPE0_AC_nRESET);
+	s3c2410_gpio_cfgpin(S3C2410_GPE1, S3C2443_GPE1_AC_SYNC);
+	s3c2410_gpio_cfgpin(S3C2410_GPE2, S3C2443_GPE2_AC_BITCLK);
+	s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2443_GPE3_AC_SDI);
+	s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2443_GPE4_AC_SDO);
+
+	ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	ac_glbctrl = S3C_AC97_GLBCTRL_COLDRESET;
+	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+
+	ac_glbctrl = 0;
+	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+
+	ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
+	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+
+	ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE;
+	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+
+	ret = request_irq(IRQ_S3C2443_AC97, s3c2443_ac97_irq,
+		IRQF_DISABLED, "AC97", NULL);
+	if (ret < 0) {
+		printk(KERN_ERR "s3c24xx-ac97: interrupt request failed.\n");
+		clk_disable(s3c24xx_ac97.ac97_clk);
+		clk_put(s3c24xx_ac97.ac97_clk);
+		iounmap(s3c24xx_ac97.regs);
+	}
+	return ret;
+}
+
+static void s3c2443_ac97_remove(struct platform_device *pdev)
+{
+	free_irq(IRQ_S3C2443_AC97, NULL);
+	clk_disable(s3c24xx_ac97.ac97_clk);
+	clk_put(s3c24xx_ac97.ac97_clk);
+	iounmap(s3c24xx_ac97.regs);
+}
+
+static int s3c2443_ac97_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		cpu_dai->dma_data = &s3c2443_ac97_pcm_stereo_out;
+	else
+		cpu_dai->dma_data = &s3c2443_ac97_pcm_stereo_in;
+
+	return 0;
+}
+
+static int s3c2443_ac97_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	u32 ac_glbctrl;
+
+	ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	switch(cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+			ac_glbctrl |= S3C_AC97_GLBCTRL_PCMINTM_DMA;
+		else
+			ac_glbctrl |= S3C_AC97_GLBCTRL_PCMOUTTM_DMA;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+			ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMINTM_MASK;
+		else
+			ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMOUTTM_MASK;
+		break;
+	}
+	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+
+	return 0;
+}
+
+static int s3c2443_ac97_hw_mic_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		return -ENODEV;
+	else
+		cpu_dai->dma_data = &s3c2443_ac97_mic_mono_in;
+
+	return 0;
+}
+
+static int s3c2443_ac97_mic_trigger(struct snd_pcm_substream *substream,
+	int cmd)
+{
+	u32 ac_glbctrl;
+
+	ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+	switch(cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		ac_glbctrl |= S3C_AC97_GLBCTRL_PCMINTM_DMA;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMINTM_MASK;
+	}
+	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
+
+	return 0;
+}
+
+#define s3c2443_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+		SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
+		SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+struct snd_soc_cpu_dai s3c2443_ac97_dai[] = {
+{
+	.name = "s3c2443-ac97",
+	.id = 0,
+	.type = SND_SOC_DAI_AC97,
+	.probe = s3c2443_ac97_probe,
+	.remove = s3c2443_ac97_remove,
+	.playback = {
+		.stream_name = "AC97 Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = s3c2443_AC97_RATES,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+	.capture = {
+		.stream_name = "AC97 Capture",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = s3c2443_AC97_RATES,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+	.ops = {
+		.hw_params = s3c2443_ac97_hw_params,
+		.trigger = s3c2443_ac97_trigger},
+},
+{
+	.name = "pxa2xx-ac97-mic",
+	.id = 1,
+	.type = SND_SOC_DAI_AC97,
+	.capture = {
+		.stream_name = "AC97 Mic Capture",
+		.channels_min = 1,
+		.channels_max = 1,
+		.rates = s3c2443_AC97_RATES,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+	.ops = {
+		.hw_params = s3c2443_ac97_hw_mic_params,
+		.trigger = s3c2443_ac97_mic_trigger,},
+},
+};
+
+EXPORT_SYMBOL_GPL(s3c2443_ac97_dai);
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+MODULE_AUTHOR("Graeme Gregory");
+MODULE_DESCRIPTION("AC97 driver for the Samsung s3c2443 chip");
+MODULE_LICENSE("GPL");

+ 25 - 0
sound/soc/s3c24xx/s3c24xx-ac97.h

@@ -0,0 +1,25 @@
+/*
+ * s3c24xx-ac97.c  --  ALSA Soc Audio Layer
+ *
+ * (c) 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ *         graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.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.
+ *
+ *  Revision history
+ *    10th Nov 2006   Initial version.
+ */
+
+#ifndef S3C24XXAC97_H_
+#define S3C24XXAC97_H_
+
+#define AC_CMD_ADDR(x) (x << 16)
+#define AC_CMD_DATA(x) (x & 0xffff)
+
+extern struct snd_soc_cpu_dai s3c2443_ac97_dai[];
+
+#endif /*S3C24XXAC97_H_*/

+ 2 - 2
sound/soc/s3c24xx/s3c24xx-i2s.c

@@ -344,11 +344,11 @@ static int s3c24xx_i2s_set_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
 	DBG("Entered %s\n", __FUNCTION__);
 	DBG("Entered %s\n", __FUNCTION__);
 
 
 	switch (div_id) {
 	switch (div_id) {
-	case S3C24XX_DIV_MCLK:
+	case S3C24XX_DIV_BCLK:
 		reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~S3C2410_IISMOD_FS_MASK;
 		reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~S3C2410_IISMOD_FS_MASK;
 		writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
 		writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
 		break;
 		break;
-	case S3C24XX_DIV_BCLK:
+	case S3C24XX_DIV_MCLK:
 		reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~(S3C2410_IISMOD_384FS);
 		reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~(S3C2410_IISMOD_384FS);
 		writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
 		writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
 		break;
 		break;

+ 85 - 0
sound/soc/s3c24xx/smdk2443_wm9710.c

@@ -0,0 +1,85 @@
+/*
+ * smdk2443_wm9710.c  --  SoC audio for smdk2443
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ *         graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.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.
+ *
+ *  Revision history
+ *    8th Mar 2007   Initial version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "../codecs/ac97.h"
+#include "s3c24xx-pcm.h"
+#include "s3c24xx-ac97.h"
+
+static struct snd_soc_machine smdk2443;
+
+static struct snd_soc_dai_link smdk2443_dai[] = {
+{
+	.name = "AC97",
+	.stream_name = "AC97 HiFi",
+	.cpu_dai = &s3c2443_ac97_dai[0],
+	.codec_dai = &ac97_dai,
+},
+};
+
+static struct snd_soc_machine smdk2443 = {
+	.name = "SMDK2443",
+	.dai_link = smdk2443_dai,
+	.num_links = ARRAY_SIZE(smdk2443_dai),
+};
+
+static struct snd_soc_device smdk2443_snd_ac97_devdata = {
+	.machine = &smdk2443,
+	.platform = &s3c24xx_soc_platform,
+	.codec_dev = &soc_codec_dev_ac97,
+};
+
+static struct platform_device *smdk2443_snd_ac97_device;
+
+static int __init smdk2443_init(void)
+{
+	int ret;
+
+	smdk2443_snd_ac97_device = platform_device_alloc("soc-audio", -1);
+	if (!smdk2443_snd_ac97_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(smdk2443_snd_ac97_device,
+				&smdk2443_snd_ac97_devdata);
+	smdk2443_snd_ac97_devdata.dev = &smdk2443_snd_ac97_device->dev;
+	ret = platform_device_add(smdk2443_snd_ac97_device);
+
+	if (ret)
+		platform_device_put(smdk2443_snd_ac97_device);
+
+	return ret;
+}
+
+static void __exit smdk2443_exit(void)
+{
+	platform_device_unregister(smdk2443_snd_ac97_device);
+}
+
+module_init(smdk2443_init);
+module_exit(smdk2443_exit);
+
+/* Module information */
+MODULE_AUTHOR("Graeme Gregory, graeme.gregory@wolfsonmicro.com, www.wolfsonmicro.com");
+MODULE_DESCRIPTION("ALSA SoC WM9710 SMDK2443");
+MODULE_LICENSE("GPL");

+ 38 - 0
sound/soc/sh/Kconfig

@@ -0,0 +1,38 @@
+menu "SoC Audio support for SuperH"
+
+config SND_SOC_PCM_SH7760
+	tristate "SoC Audio support for Renesas SH7760"
+	depends on CPU_SUBTYPE_SH7760 && SND_SOC && SH_DMABRG
+	help
+	  Enable this option for SH7760 AC97/I2S audio support.
+
+
+##
+## Audio unit modules
+##
+
+config SND_SOC_SH4_HAC
+	select AC97_BUS
+	select SND_SOC_AC97_BUS
+	select SND_AC97_CODEC
+	tristate
+
+config SND_SOC_SH4_SSI
+	tristate
+
+
+
+##
+## Boards
+##
+
+config SND_SH7760_AC97
+	tristate "SH7760 AC97 sound support"
+	depends on CPU_SUBTYPE_SH7760 && SND_SOC_PCM_SH7760
+	select SND_SOC_SH4_HAC
+	select SND_SOC_AC97_CODEC
+	help
+	  This option enables generic sound support for the first
+	  AC97 unit of the SH7760.
+
+endmenu

+ 14 - 0
sound/soc/sh/Makefile

@@ -0,0 +1,14 @@
+## DMA engines
+snd-soc-dma-sh7760-objs	:= dma-sh7760.o
+obj-$(CONFIG_SND_SOC_PCM_SH7760)	+= snd-soc-dma-sh7760.o
+
+## audio units found on some SH-4
+snd-soc-hac-objs	:= hac.o
+snd-soc-ssi-objs	:= ssi.o
+obj-$(CONFIG_SND_SOC_SH4_HAC)	+= snd-soc-hac.o
+obj-$(CONFIG_SND_SOC_SH4_SSI)	+= snd-soc-ssi.o
+
+## boards
+snd-soc-sh7760-ac97-objs	:= sh7760-ac97.o
+
+obj-$(CONFIG_SND_SH7760_AC97)	+= snd-soc-sh7760-ac97.o

+ 354 - 0
sound/soc/sh/dma-sh7760.c

@@ -0,0 +1,354 @@
+/*
+ * SH7760 ("camelot") DMABRG audio DMA unit support
+ *
+ * Copyright (C) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
+ *  licensed under the terms outlined in the file COPYING at the root
+ *  of the linux kernel sources.
+ *
+ * The SH7760 DMABRG provides 4 dma channels (2x rec, 2x play), which
+ * trigger an interrupt when one half of the programmed transfer size
+ * has been xmitted.
+ *
+ * FIXME: little-endian only for now
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <asm/dmabrg.h>
+
+
+/* registers and bits */
+#define BRGATXSAR	0x00
+#define BRGARXDAR	0x04
+#define BRGATXTCR	0x08
+#define BRGARXTCR	0x0C
+#define BRGACR		0x10
+#define BRGATXTCNT	0x14
+#define BRGARXTCNT	0x18
+
+#define ACR_RAR		(1 << 18)
+#define ACR_RDS		(1 << 17)
+#define ACR_RDE		(1 << 16)
+#define ACR_TAR		(1 << 2)
+#define ACR_TDS		(1 << 1)
+#define ACR_TDE		(1 << 0)
+
+/* receiver/transmitter data alignment */
+#define ACR_RAM_NONE	(0 << 24)
+#define ACR_RAM_4BYTE	(1 << 24)
+#define ACR_RAM_2WORD	(2 << 24)
+#define ACR_TAM_NONE	(0 << 8)
+#define ACR_TAM_4BYTE	(1 << 8)
+#define ACR_TAM_2WORD	(2 << 8)
+
+
+struct camelot_pcm {
+	unsigned long mmio;  /* DMABRG audio channel control reg MMIO */
+	unsigned int txid;    /* ID of first DMABRG IRQ for this unit */
+
+	struct snd_pcm_substream *tx_ss;
+	unsigned long tx_period_size;
+	unsigned int  tx_period;
+
+	struct snd_pcm_substream *rx_ss;
+	unsigned long rx_period_size;
+	unsigned int  rx_period;
+
+} cam_pcm_data[2] = {
+	{
+		.mmio	=	0xFE3C0040,
+		.txid	=	DMABRGIRQ_A0TXF,
+	},
+	{
+		.mmio	=	0xFE3C0060,
+		.txid	=	DMABRGIRQ_A1TXF,
+	},
+};
+
+#define BRGREG(x)	(*(unsigned long *)(cam->mmio + (x)))
+
+/*
+ * set a minimum of 16kb per period, to avoid interrupt-"storm" and
+ * resulting skipping. In general, the bigger the minimum size, the
+ * better for overall system performance. (The SH7760 is a puny CPU
+ * with a slow SDRAM interface and poor internal bus bandwidth,
+ * *especially* when the LCDC is active).  The minimum for the DMAC
+ * is 8 bytes; 16kbytes are enough to get skip-free playback of a
+ * 44kHz/16bit/stereo MP3 on a lightly loaded system, and maintain
+ * reasonable responsiveness in MPlayer.
+ */
+#define DMABRG_PERIOD_MIN		16 * 1024
+#define DMABRG_PERIOD_MAX		0x03fffffc
+#define DMABRG_PREALLOC_BUFFER		32 * 1024
+#define DMABRG_PREALLOC_BUFFER_MAX	32 * 1024
+
+/* support everything the SSI supports */
+#define DMABRG_RATES	\
+	SNDRV_PCM_RATE_8000_192000
+
+#define DMABRG_FMTS	\
+	(SNDRV_PCM_FMTBIT_S8      | SNDRV_PCM_FMTBIT_U8      |	\
+	 SNDRV_PCM_FMTBIT_S16_LE  | SNDRV_PCM_FMTBIT_U16_LE  |	\
+	 SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE |	\
+	 SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3LE |	\
+	 SNDRV_PCM_FMTBIT_S32_LE  | SNDRV_PCM_FMTBIT_U32_LE)
+
+static struct snd_pcm_hardware camelot_pcm_hardware = {
+	.info = (SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =	DMABRG_FMTS,
+	.rates =	DMABRG_RATES,
+	.rate_min =		8000,
+	.rate_max =		192000,
+	.channels_min =		2,
+	.channels_max =		8,		/* max of the SSI */
+	.buffer_bytes_max =	DMABRG_PERIOD_MAX,
+	.period_bytes_min =	DMABRG_PERIOD_MIN,
+	.period_bytes_max =	DMABRG_PERIOD_MAX / 2,
+	.periods_min =		2,
+	.periods_max =		2,
+	.fifo_size =		128,
+};
+
+static void camelot_txdma(void *data)
+{
+	struct camelot_pcm *cam = data;
+	cam->tx_period ^= 1;
+	snd_pcm_period_elapsed(cam->tx_ss);
+}
+
+static void camelot_rxdma(void *data)
+{
+	struct camelot_pcm *cam = data;
+	cam->rx_period ^= 1;
+	snd_pcm_period_elapsed(cam->rx_ss);
+}
+
+static int camelot_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id];
+	int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
+	int ret, dmairq;
+
+	snd_soc_set_runtime_hwparams(substream, &camelot_pcm_hardware);
+
+	/* DMABRG buffer half/full events */
+	dmairq = (recv) ? cam->txid + 2 : cam->txid;
+	if (recv) {
+		cam->rx_ss = substream;
+		ret = dmabrg_request_irq(dmairq, camelot_rxdma, cam);
+		if (unlikely(ret)) {
+			pr_debug("audio unit %d irqs already taken!\n",
+			     rtd->dai->cpu_dai->id);
+			return -EBUSY;
+		}
+		(void)dmabrg_request_irq(dmairq + 1,camelot_rxdma, cam);
+	} else {
+		cam->tx_ss = substream;
+		ret = dmabrg_request_irq(dmairq, camelot_txdma, cam);
+		if (unlikely(ret)) {
+			pr_debug("audio unit %d irqs already taken!\n",
+			     rtd->dai->cpu_dai->id);
+			return -EBUSY;
+		}
+		(void)dmabrg_request_irq(dmairq + 1, camelot_txdma, cam);
+	}
+	return 0;
+}
+
+static int camelot_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id];
+	int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
+	int dmairq;
+
+	dmairq = (recv) ? cam->txid + 2 : cam->txid;
+
+	if (recv)
+		cam->rx_ss = NULL;
+	else
+		cam->tx_ss = NULL;
+
+	dmabrg_free_irq(dmairq + 1);
+	dmabrg_free_irq(dmairq);
+
+	return 0;
+}
+
+static int camelot_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id];
+	int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
+	int ret;
+
+	ret = snd_pcm_lib_malloc_pages(substream,
+				       params_buffer_bytes(hw_params));
+	if (ret < 0)
+		return ret;
+
+	if (recv) {
+		cam->rx_period_size = params_period_bytes(hw_params);
+		cam->rx_period = 0;
+	} else {
+		cam->tx_period_size = params_period_bytes(hw_params);
+		cam->tx_period = 0;
+	}
+	return 0;
+}
+
+static int camelot_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int camelot_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id];
+
+	pr_debug("PCM data: addr 0x%08ulx len %d\n",
+		 (u32)runtime->dma_addr, runtime->dma_bytes);
+ 
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		BRGREG(BRGATXSAR) = (unsigned long)runtime->dma_area;
+		BRGREG(BRGATXTCR) = runtime->dma_bytes;
+	} else {
+		BRGREG(BRGARXDAR) = (unsigned long)runtime->dma_area;
+		BRGREG(BRGARXTCR) = runtime->dma_bytes;
+	}
+
+	return 0;
+}
+
+static inline void dmabrg_play_dma_start(struct camelot_pcm *cam)
+{
+	unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
+	/* start DMABRG engine: XFER start, auto-addr-reload */
+	BRGREG(BRGACR) = acr | ACR_TDE | ACR_TAR | ACR_TAM_2WORD;
+}
+
+static inline void dmabrg_play_dma_stop(struct camelot_pcm *cam)
+{
+	unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
+	/* forcibly terminate data transmission */
+	BRGREG(BRGACR) = acr | ACR_TDS;
+}
+
+static inline void dmabrg_rec_dma_start(struct camelot_pcm *cam)
+{
+	unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
+	/* start DMABRG engine: recv start, auto-reload */
+	BRGREG(BRGACR) = acr | ACR_RDE | ACR_RAR | ACR_RAM_2WORD;
+}
+
+static inline void dmabrg_rec_dma_stop(struct camelot_pcm *cam)
+{
+	unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
+	/* forcibly terminate data receiver */
+	BRGREG(BRGACR) = acr | ACR_RDS;
+}
+
+static int camelot_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id];
+	int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		if (recv)
+			dmabrg_rec_dma_start(cam);
+		else
+			dmabrg_play_dma_start(cam);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		if (recv)
+			dmabrg_rec_dma_stop(cam);
+		else
+			dmabrg_play_dma_stop(cam);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static snd_pcm_uframes_t camelot_pos(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id];
+	int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
+	unsigned long pos;
+
+	/* cannot use the DMABRG pointer register: under load, by the
+	 * time ALSA comes around to read the register, it is already
+	 * far ahead (or worse, already done with the fragment) of the
+	 * position at the time the IRQ was triggered, which results in
+	 * fast-playback sound in my test application (ScummVM)
+	 */
+	if (recv)
+		pos = cam->rx_period ? cam->rx_period_size : 0;
+	else
+		pos = cam->tx_period ? cam->tx_period_size : 0;
+
+	return bytes_to_frames(runtime, pos);
+}
+
+static struct snd_pcm_ops camelot_pcm_ops = {
+	.open		= camelot_pcm_open,
+	.close		= camelot_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= camelot_hw_params,
+	.hw_free	= camelot_hw_free,
+	.prepare	= camelot_prepare,
+	.trigger	= camelot_trigger,
+	.pointer	= camelot_pos,
+};
+
+static void camelot_pcm_free(struct snd_pcm *pcm)
+{
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int camelot_pcm_new(struct snd_card *card,
+			   struct snd_soc_codec_dai *dai,
+			   struct snd_pcm *pcm)
+{
+	/* dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel
+	 * in MMAP mode (i.e. aplay -M)
+	 */
+	snd_pcm_lib_preallocate_pages_for_all(pcm,
+		SNDRV_DMA_TYPE_CONTINUOUS,
+		snd_dma_continuous_data(GFP_KERNEL),
+		DMABRG_PREALLOC_BUFFER,	DMABRG_PREALLOC_BUFFER_MAX);
+
+	return 0;
+}
+
+struct snd_soc_platform sh7760_soc_platform = {
+	.name		= "sh7760-pcm",
+	.pcm_ops 	= &camelot_pcm_ops,
+	.pcm_new	= camelot_pcm_new,
+	.pcm_free	= camelot_pcm_free,
+};
+EXPORT_SYMBOL_GPL(sh7760_soc_platform);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SH7760 Audio DMA (DMABRG) driver");
+MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");

+ 322 - 0
sound/soc/sh/hac.c

@@ -0,0 +1,322 @@
+/*
+ * Hitachi Audio Controller (AC97) support for SH7760/SH7780
+ *
+ * Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
+ *  licensed under the terms outlined in the file COPYING at the root
+ *  of the linux kernel sources.
+ *
+ * dont forget to set IPSEL/OMSEL register bits (in your board code) to
+ * enable HAC output pins!
+ */
+
+/* BIG FAT FIXME: although the SH7760 has 2 independent AC97 units, only
+ * the FIRST can be used since ASoC does not pass any information to the
+ * ac97_read/write() functions regarding WHICH unit to use.  You'll have
+ * to edit the code a bit to use the other AC97 unit.		--mlau
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+/* regs and bits */
+#define HACCR		0x08
+#define HACCSAR		0x20
+#define HACCSDR		0x24
+#define HACPCML		0x28
+#define HACPCMR		0x2C
+#define HACTIER		0x50
+#define	HACTSR		0x54
+#define HACRIER		0x58
+#define HACRSR		0x5C
+#define HACACR		0x60
+
+#define CR_CR		(1 << 15)	/* "codec-ready" indicator */
+#define CR_CDRT		(1 << 11)	/* cold reset */
+#define CR_WMRT		(1 << 10)	/* warm reset */
+#define CR_B9		(1 << 9)	/* the mysterious "bit 9" */
+#define CR_ST		(1 << 5)	/* AC97 link start bit */
+
+#define CSAR_RD		(1 << 19)	/* AC97 data read bit */
+#define CSAR_WR		(0)
+
+#define TSR_CMDAMT	(1 << 31)
+#define TSR_CMDDMT	(1 << 30)
+
+#define RSR_STARY	(1 << 22)
+#define RSR_STDRY	(1 << 21)
+
+#define ACR_DMARX16	(1 << 30)
+#define ACR_DMATX16	(1 << 29)
+#define ACR_TX12ATOM	(1 << 26)
+#define ACR_DMARX20	((1 << 24) | (1 << 22))
+#define ACR_DMATX20	((1 << 23) | (1 << 21))
+
+#define CSDR_SHIFT	4
+#define CSDR_MASK	(0xffff << CSDR_SHIFT)
+#define CSAR_SHIFT	12
+#define CSAR_MASK	(0x7f << CSAR_SHIFT)
+
+#define AC97_WRITE_RETRY	1
+#define AC97_READ_RETRY		5
+
+/* manual-suggested AC97 codec access timeouts (us) */
+#define TMO_E1	500	/* 21 < E1 < 1000 */
+#define TMO_E2	13	/* 13 < E2 */
+#define TMO_E3	21	/* 21 < E3 */
+#define TMO_E4	500	/* 21 < E4 < 1000 */
+
+struct hac_priv {
+	unsigned long mmio;	/* HAC base address */
+} hac_cpu_data[] = {
+#if defined(CONFIG_CPU_SUBTYPE_SH7760)
+	{
+		.mmio	= 0xFE240000,
+	},
+	{
+		.mmio	= 0xFE250000,
+	},
+#elif defined(CONFIG_CPU_SUBTYPE_SH7780)
+	{
+		.mmio	= 0xFFE40000,
+	},
+#else
+#error "Unsupported SuperH SoC"
+#endif
+};
+
+#define HACREG(reg)	(*(unsigned long *)(hac->mmio + (reg)))
+
+/*
+ * AC97 read/write flow as outlined in the SH7760 manual (pages 903-906)
+ */
+static int hac_get_codec_data(struct hac_priv *hac, unsigned short r,
+			      unsigned short *v)
+{
+	unsigned int to1, to2, i;
+	unsigned short adr;
+
+	for (i = 0; i < AC97_READ_RETRY; ++i) {
+		*v = 0;
+		/* wait for HAC to receive something from the codec */
+		for (to1 = TMO_E4;
+		     to1 && !(HACREG(HACRSR) & RSR_STARY);
+		     --to1)
+			udelay(1);
+		for (to2 = TMO_E4; 
+		     to2 && !(HACREG(HACRSR) & RSR_STDRY);
+		     --to2)
+			udelay(1);
+
+		if (!to1 && !to2)
+			return 0;	/* codec comm is down */
+
+		adr = ((HACREG(HACCSAR) & CSAR_MASK) >> CSAR_SHIFT);
+		*v  = ((HACREG(HACCSDR) & CSDR_MASK) >> CSDR_SHIFT);
+
+		HACREG(HACRSR) &= ~(RSR_STDRY | RSR_STARY);
+
+		if (r == adr)
+			break;
+
+		/* manual says: wait at least 21 usec before retrying */
+		udelay(21);
+	}
+	HACREG(HACRSR) &= ~(RSR_STDRY | RSR_STARY);
+	return (i < AC97_READ_RETRY);
+}
+
+static unsigned short hac_read_codec_aux(struct hac_priv *hac,
+					 unsigned short reg)
+{
+	unsigned short val;
+	unsigned int i, to;
+
+	for (i = 0; i < AC97_READ_RETRY; i++) {
+		/* send_read_request */
+		local_irq_disable();
+		HACREG(HACTSR) &= ~(TSR_CMDAMT);
+		HACREG(HACCSAR) = (reg << CSAR_SHIFT) | CSAR_RD;
+		local_irq_enable();
+
+		for (to = TMO_E3;
+		     to && !(HACREG(HACTSR) & TSR_CMDAMT);
+		     --to)
+			udelay(1);
+
+		HACREG(HACTSR) &= ~TSR_CMDAMT;
+		val = 0;
+		if (hac_get_codec_data(hac, reg, &val) != 0)
+			break;
+	}
+
+	if (i == AC97_READ_RETRY)
+		return ~0;
+
+	return val;
+}
+
+static void hac_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+			   unsigned short val)
+{
+	int unit_id = 0 /* ac97->private_data */;
+	struct hac_priv *hac = &hac_cpu_data[unit_id];
+	unsigned int i, to;
+	/* write_codec_aux */
+	for (i = 0; i < AC97_WRITE_RETRY; i++) {
+		/* send_write_request */
+		local_irq_disable();
+		HACREG(HACTSR) &= ~(TSR_CMDDMT | TSR_CMDAMT);
+		HACREG(HACCSDR) = (val << CSDR_SHIFT);
+		HACREG(HACCSAR) = (reg << CSAR_SHIFT) & (~CSAR_RD);
+		local_irq_enable();
+
+		/* poll-wait for CMDAMT and CMDDMT */
+		for (to = TMO_E1;
+		     to && !(HACREG(HACTSR) & (TSR_CMDAMT|TSR_CMDDMT));
+		     --to)
+			udelay(1);
+
+		HACREG(HACTSR) &= ~(TSR_CMDAMT | TSR_CMDDMT);
+		if (to)
+			break;
+		/* timeout, try again */
+	}
+}
+
+static unsigned short hac_ac97_read(struct snd_ac97 *ac97,
+				    unsigned short reg)
+{
+	int unit_id = 0 /* ac97->private_data */;
+	struct hac_priv *hac = &hac_cpu_data[unit_id];
+	return hac_read_codec_aux(hac, reg);
+}
+
+static void hac_ac97_warmrst(struct snd_ac97 *ac97)
+{
+	int unit_id = 0 /* ac97->private_data */;
+	struct hac_priv *hac = &hac_cpu_data[unit_id];
+	unsigned int tmo;
+
+	HACREG(HACCR) = CR_WMRT | CR_ST | CR_B9;
+	msleep(10);
+	HACREG(HACCR) = CR_ST | CR_B9;
+	for (tmo = 1000; (tmo > 0) && !(HACREG(HACCR) & CR_CR); tmo--)
+		udelay(1);
+
+	if (!tmo)
+		printk(KERN_INFO "hac: reset: AC97 link down!\n");
+	/* settings this bit lets us have a conversation with codec */
+	HACREG(HACACR) |= ACR_TX12ATOM;
+}
+
+static void hac_ac97_coldrst(struct snd_ac97 *ac97)
+{
+	int unit_id = 0 /* ac97->private_data */;
+	struct hac_priv *hac;
+	hac = &hac_cpu_data[unit_id];
+
+	HACREG(HACCR) = 0;
+	HACREG(HACCR) = CR_CDRT | CR_ST | CR_B9;
+	msleep(10);
+	hac_ac97_warmrst(ac97);
+}
+
+struct snd_ac97_bus_ops soc_ac97_ops = {
+	.read	= hac_ac97_read,
+	.write	= hac_ac97_write,
+	.reset	= hac_ac97_coldrst,
+	.warm_reset = hac_ac97_warmrst,
+};
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+static int hac_hw_params(struct snd_pcm_substream *substream,
+			 struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct hac_priv *hac = &hac_cpu_data[rtd->dai->cpu_dai->id];
+	int d = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1;
+
+	switch (params->msbits) {
+	case 16:
+		HACREG(HACACR) |= d ?  ACR_DMARX16 :  ACR_DMATX16;
+		HACREG(HACACR) &= d ? ~ACR_DMARX20 : ~ACR_DMATX20;
+		break;
+	case 20:
+		HACREG(HACACR) &= d ? ~ACR_DMARX16 : ~ACR_DMATX16;
+		HACREG(HACACR) |= d ?  ACR_DMARX20 :  ACR_DMATX20;
+		break;
+	default:
+		pr_debug("hac: invalid depth %d bit\n", params->msbits);
+		return -EINVAL;
+		break;
+	}
+
+	return 0;
+}
+
+#define AC97_RATES	\
+	SNDRV_PCM_RATE_8000_192000
+
+#define AC97_FMTS	\
+	SNDRV_PCM_FMTBIT_S16_LE
+
+struct snd_soc_cpu_dai sh4_hac_dai[] = {
+{
+	.name			= "HAC0",
+	.id			= 0,
+	.type			= SND_SOC_DAI_AC97,
+	.playback = {
+		.rates		= AC97_RATES,
+		.formats	= AC97_FMTS,
+		.channels_min	= 2,
+		.channels_max	= 2,
+	},
+	.capture = {
+		.rates		= AC97_RATES,
+		.formats	= AC97_FMTS,
+		.channels_min	= 2,
+		.channels_max	= 2,
+	},
+	.ops = {
+		.hw_params	= hac_hw_params,
+	},
+},
+#ifdef CONFIG_CPU_SUBTYPE_SH7760
+{
+	.name			= "HAC1",
+	.id			= 1,
+	.type			= SND_SOC_DAI_AC97,
+	.playback = {
+		.rates		= AC97_RATES,
+		.formats	= AC97_FMTS,
+		.channels_min	= 2,
+		.channels_max	= 2,
+	},
+	.capture = {
+		.rates		= AC97_RATES,
+		.formats	= AC97_FMTS,
+		.channels_min	= 2,
+		.channels_max	= 2,
+	},
+	.ops = {
+		.hw_params	= hac_hw_params,
+	},
+
+},
+#endif
+};
+EXPORT_SYMBOL_GPL(sh4_hac_dai);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SuperH onchip HAC (AC97) audio driver");
+MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");

+ 92 - 0
sound/soc/sh/sh7760-ac97.c

@@ -0,0 +1,92 @@
+/*
+ * Generic AC97 sound support for SH7760
+ *
+ * (c) 2007 Manuel Lauss
+ *
+ * Licensed under the GPLv2.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <asm/io.h>
+
+#include "../codecs/ac97.h"
+
+#define IPSEL 0xFE400034
+
+/* platform specific structs can be declared here */
+extern struct snd_soc_cpu_dai sh4_hac_dai[2];
+extern struct snd_soc_platform sh7760_soc_platform;
+
+static int machine_init(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_sync_endpoints(codec);
+	return 0;
+}
+
+static struct snd_soc_dai_link sh7760_ac97_dai = {
+	.name = "AC97",
+	.stream_name = "AC97 HiFi",
+	.cpu_dai = &sh4_hac_dai[0],	/* HAC0 */
+	.codec_dai = &ac97_dai,
+	.init = machine_init,
+	.ops = NULL,
+};
+
+static struct snd_soc_machine sh7760_ac97_soc_machine  = {
+	.name = "SH7760 AC97",
+	.dai_link = &sh7760_ac97_dai,
+	.num_links = 1,
+};
+
+static struct snd_soc_device sh7760_ac97_snd_devdata = {
+	.machine = &sh7760_ac97_soc_machine,
+	.platform = &sh7760_soc_platform,
+	.codec_dev = &soc_codec_dev_ac97,
+};
+
+static struct platform_device *sh7760_ac97_snd_device;
+
+static int __init sh7760_ac97_init(void)
+{
+	int ret;
+	unsigned short ipsel;
+
+	/* enable both AC97 controllers in pinmux reg */
+	ipsel = ctrl_inw(IPSEL);
+	ctrl_outw(ipsel | (3 << 10), IPSEL);
+
+	ret = -ENOMEM;
+	sh7760_ac97_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!sh7760_ac97_snd_device)
+		goto out;
+
+	platform_set_drvdata(sh7760_ac97_snd_device,
+			     &sh7760_ac97_snd_devdata);
+	sh7760_ac97_snd_devdata.dev = &sh7760_ac97_snd_device->dev;
+	ret = platform_device_add(sh7760_ac97_snd_device);
+
+	if (ret)
+		platform_device_put(sh7760_ac97_snd_device);
+
+out:
+	return ret;
+}
+
+static void __exit sh7760_ac97_exit(void)
+{
+	platform_device_unregister(sh7760_ac97_snd_device);
+}
+
+module_init(sh7760_ac97_init);
+module_exit(sh7760_ac97_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Generic SH7760 AC97 sound machine");
+MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");

+ 400 - 0
sound/soc/sh/ssi.c

@@ -0,0 +1,400 @@
+/*
+ * Serial Sound Interface (I2S) support for SH7760/SH7780
+ *
+ * Copyright (c) 2007 Manuel Lauss <mano@roarinelk.homelinux.net>
+ *
+ *  licensed under the terms outlined in the file COPYING at the root
+ *  of the linux kernel sources.
+ *
+ * dont forget to set IPSEL/OMSEL register bits (in your board code) to
+ * enable SSI output pins!
+ */
+
+/*
+ * LIMITATIONS:
+ *	The SSI unit has only one physical data line, so full duplex is
+ *	impossible.  This can be remedied  on the  SH7760 by  using the
+ *	other SSI unit for recording; however the SH7780 has only 1 SSI
+ *	unit, and its pins are shared with the AC97 unit,  among others.
+ *
+ * FEATURES:
+ *	The SSI features "compressed mode": in this mode it continuously
+ *	streams PCM data over the I2S lines and uses LRCK as a handshake
+ *	signal.  Can be used to send compressed data (AC3/DTS) to a DSP.
+ *	The number of bits sent over the wire in a frame can be adjusted
+ *	and can be independent from the actual sample bit depth. This is
+ *	useful to support TDM mode codecs like the AD1939 which have a
+ *	fixed TDM slot size, regardless of sample resolution.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <asm/io.h>
+
+#define SSICR	0x00
+#define SSISR	0x04
+
+#define CR_DMAEN	(1 << 28)
+#define CR_CHNL_SHIFT	22
+#define CR_CHNL_MASK	(3 << CR_CHNL_SHIFT)
+#define CR_DWL_SHIFT	19
+#define CR_DWL_MASK	(7 << CR_DWL_SHIFT)
+#define CR_SWL_SHIFT	16
+#define CR_SWL_MASK	(7 << CR_SWL_SHIFT)
+#define CR_SCK_MASTER	(1 << 15)	/* bitclock master bit */
+#define CR_SWS_MASTER	(1 << 14)	/* wordselect master bit */
+#define CR_SCKP		(1 << 13)	/* I2Sclock polarity */
+#define CR_SWSP		(1 << 12)	/* LRCK polarity */
+#define CR_SPDP		(1 << 11)
+#define CR_SDTA		(1 << 10)	/* i2s alignment (msb/lsb) */
+#define CR_PDTA		(1 << 9)	/* fifo data alignment */
+#define CR_DEL		(1 << 8)	/* delay data by 1 i2sclk */
+#define CR_BREN		(1 << 7)	/* clock gating in burst mode */
+#define CR_CKDIV_SHIFT	4
+#define CR_CKDIV_MASK	(7 << CR_CKDIV_SHIFT)	/* bitclock divider */
+#define CR_MUTE		(1 << 3)	/* SSI mute */
+#define CR_CPEN		(1 << 2)	/* compressed mode */
+#define CR_TRMD		(1 << 1)	/* transmit/receive select */
+#define CR_EN		(1 << 0)	/* enable SSI */
+
+#define SSIREG(reg)	(*(unsigned long *)(ssi->mmio + (reg)))
+
+struct ssi_priv {
+	unsigned long mmio;
+	unsigned long sysclk;
+	int inuse;
+} ssi_cpu_data[] = {
+#if defined(CONFIG_CPU_SUBTYPE_SH7760)
+	{
+		.mmio	= 0xFE680000,
+	},
+	{
+		.mmio	= 0xFE690000,
+	},
+#elif defined(CONFIG_CPU_SUBTYPE_SH7780)
+	{
+		.mmio	= 0xFFE70000,
+	},
+#else
+#error "Unsupported SuperH SoC"
+#endif
+};
+
+/*
+ * track usage of the SSI; it is simplex-only so prevent attempts of
+ * concurrent playback + capture. FIXME: any locking required?
+ */
+static int ssi_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id];
+	if (ssi->inuse) {
+		pr_debug("ssi: already in use!\n");
+		return -EBUSY;
+	} else
+		ssi->inuse = 1;
+	return 0;
+}
+
+static void ssi_shutdown(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id];
+
+	ssi->inuse = 0;
+}
+
+static int ssi_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id];
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		SSIREG(SSICR) |= CR_DMAEN | CR_EN;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		SSIREG(SSICR) &= ~(CR_DMAEN | CR_EN);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ssi_hw_params(struct snd_pcm_substream *substream,
+			 struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id];
+	unsigned long ssicr = SSIREG(SSICR);
+	unsigned int bits, channels, swl, recv, i;
+
+	channels = params_channels(params);
+	bits = params->msbits;
+	recv = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 0 : 1;
+
+	pr_debug("ssi_hw_params() enter\nssicr was    %08lx\n", ssicr);
+	pr_debug("bits: %d channels: %d\n", bits, channels);
+
+	ssicr &= ~(CR_TRMD | CR_CHNL_MASK | CR_DWL_MASK | CR_PDTA |
+		   CR_SWL_MASK);
+
+	/* direction (send/receive) */
+	if (!recv)
+		ssicr |= CR_TRMD;	/* transmit */
+
+	/* channels */
+	if ((channels < 2) || (channels > 8) || (channels & 1)) {
+		pr_debug("ssi: invalid number of channels\n");
+		return -EINVAL;
+	}
+	ssicr |= ((channels >> 1) - 1) << CR_CHNL_SHIFT;
+
+	/* DATA WORD LENGTH (DWL): databits in audio sample */
+	i = 0;
+	switch (bits) {
+	case 32: ++i;
+	case 24: ++i;
+	case 22: ++i;
+	case 20: ++i;
+	case 18: ++i;
+	case 16: ++i;
+		 ssicr |= i << CR_DWL_SHIFT;
+	case 8:	 break;
+	default:
+		pr_debug("ssi: invalid sample width\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * SYSTEM WORD LENGTH: size in bits of half a frame over the I2S
+	 * wires. This is usually bits_per_sample x channels/2;  i.e. in
+	 * Stereo mode  the SWL equals DWL.  SWL can  be bigger than the
+	 * product of (channels_per_slot x samplebits), e.g.  for codecs
+	 * like the AD1939 which  only accept 32bit wide TDM slots.  For
+	 * "standard" I2S operation we set SWL = chans / 2 * DWL here.
+	 * Waiting for ASoC to get TDM support ;-)
+	 */
+	if ((bits > 16) && (bits <= 24)) {
+		bits = 24;	/* these are padded by the SSI */
+		/*ssicr |= CR_PDTA;*/ /* cpu/data endianness ? */
+	}
+	i = 0;
+	swl = (bits * channels) / 2;
+	switch (swl) {
+	case 256: ++i;
+	case 128: ++i;
+	case 64:  ++i;
+	case 48:  ++i;
+	case 32:  ++i;
+	case 16:  ++i;
+		  ssicr |= i << CR_SWL_SHIFT;
+	case 8:   break;
+	default:
+		pr_debug("ssi: invalid system word length computed\n");
+		return -EINVAL;
+	}
+
+	SSIREG(SSICR) = ssicr;
+
+	pr_debug("ssi_hw_params() leave\nssicr is now %08lx\n", ssicr);
+	return 0;
+}
+
+static int ssi_set_sysclk(struct snd_soc_cpu_dai *cpu_dai, int clk_id,
+			  unsigned int freq, int dir)
+{
+	struct ssi_priv *ssi = &ssi_cpu_data[cpu_dai->id];
+
+	ssi->sysclk = freq;
+
+	return 0;
+}
+
+/*
+ * This divider is used to generate the SSI_SCK (I2S bitclock) from the
+ * clock at the HAC_BIT_CLK ("oversampling clock") pin.
+ */
+static int ssi_set_clkdiv(struct snd_soc_cpu_dai *dai, int did, int div)
+{
+	struct ssi_priv *ssi = &ssi_cpu_data[dai->id];
+	unsigned long ssicr;
+	int i;
+
+	i = 0;
+	ssicr = SSIREG(SSICR) & ~CR_CKDIV_MASK;
+	switch (div) {
+	case 16: ++i;
+	case 8:  ++i;
+	case 4:  ++i;
+	case 2:  ++i;
+		 SSIREG(SSICR) = ssicr | (i << CR_CKDIV_SHIFT);
+	case 1:  break;
+	default:
+		pr_debug("ssi: invalid sck divider %d\n", div);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ssi_set_fmt(struct snd_soc_cpu_dai *dai, unsigned int fmt)
+{
+	struct ssi_priv *ssi = &ssi_cpu_data[dai->id];
+	unsigned long ssicr = SSIREG(SSICR);
+
+	pr_debug("ssi_set_fmt()\nssicr was    0x%08lx\n", ssicr);
+
+	ssicr &= ~(CR_DEL | CR_PDTA | CR_BREN | CR_SWSP | CR_SCKP |
+		   CR_SWS_MASTER | CR_SCK_MASTER);
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		ssicr |= CR_DEL | CR_PDTA;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		ssicr |= CR_DEL;
+		break;
+	default:
+		pr_debug("ssi: unsupported format\n");
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
+	case SND_SOC_DAIFMT_CONT:
+		break;
+	case SND_SOC_DAIFMT_GATED:
+		ssicr |= CR_BREN;
+		break;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		ssicr |= CR_SCKP;	/* sample data at low clkedge */
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		ssicr |= CR_SCKP | CR_SWSP;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		ssicr |= CR_SWSP;	/* word select starts low */
+		break;
+	default:
+		pr_debug("ssi: invalid inversion\n");
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		break;
+	case SND_SOC_DAIFMT_CBS_CFM:
+		ssicr |= CR_SCK_MASTER;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFS:
+		ssicr |= CR_SWS_MASTER;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		ssicr |= CR_SWS_MASTER | CR_SCK_MASTER;
+		break;
+	default:
+		pr_debug("ssi: invalid master/slave configuration\n");
+		return -EINVAL;
+	}
+
+	SSIREG(SSICR) = ssicr;
+	pr_debug("ssi_set_fmt() leave\nssicr is now 0x%08lx\n", ssicr);
+
+	return 0;
+}
+
+/* the SSI depends on an external clocksource (at HAC_BIT_CLK) even in
+ * Master mode,  so really this is board specific;  the SSI can do any
+ * rate with the right bitclk and divider settings.
+ */
+#define SSI_RATES	\
+	SNDRV_PCM_RATE_8000_192000
+
+/* the SSI can do 8-32 bit samples, with 8 possible channels */
+#define SSI_FMTS	\
+	(SNDRV_PCM_FMTBIT_S8      | SNDRV_PCM_FMTBIT_U8      |	\
+	 SNDRV_PCM_FMTBIT_S16_LE  | SNDRV_PCM_FMTBIT_U16_LE  |	\
+	 SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE |	\
+	 SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3LE |	\
+	 SNDRV_PCM_FMTBIT_S32_LE  | SNDRV_PCM_FMTBIT_U32_LE)
+
+struct snd_soc_cpu_dai sh4_ssi_dai[] = {
+{
+	.name			= "SSI0",
+	.id			= 0,
+	.type			= SND_SOC_DAI_I2S,
+	.playback = {
+		.rates		= SSI_RATES,
+		.formats	= SSI_FMTS,
+		.channels_min	= 2,
+		.channels_max	= 8,
+	},
+	.capture = {
+		.rates		= SSI_RATES,
+		.formats	= SSI_FMTS,
+		.channels_min	= 2,
+		.channels_max	= 8,
+	},
+	.ops = {
+		.startup	= ssi_startup,
+		.shutdown	= ssi_shutdown,
+		.trigger	= ssi_trigger,
+		.hw_params	= ssi_hw_params,
+	},
+	.dai_ops = {
+		.set_sysclk	= ssi_set_sysclk,
+		.set_clkdiv	= ssi_set_clkdiv,
+		.set_fmt	= ssi_set_fmt,
+	},
+},
+#ifdef CONFIG_CPU_SUBTYPE_SH7760
+{
+	.name			= "SSI1",
+	.id			= 1,
+	.type			= SND_SOC_DAI_I2S,
+	.playback = {
+		.rates		= SSI_RATES,
+		.formats	= SSI_FMTS,
+		.channels_min	= 2,
+		.channels_max	= 8,
+	},
+	.capture = {
+		.rates		= SSI_RATES,
+		.formats	= SSI_FMTS,
+		.channels_min	= 2,
+		.channels_max	= 8,
+	},
+	.ops = {
+		.startup	= ssi_startup,
+		.shutdown	= ssi_shutdown,
+		.trigger	= ssi_trigger,
+		.hw_params	= ssi_hw_params,
+	},
+	.dai_ops = {
+		.set_sysclk	= ssi_set_sysclk,
+		.set_clkdiv	= ssi_set_clkdiv,
+		.set_fmt	= ssi_set_fmt,
+	},
+},
+#endif
+};
+EXPORT_SYMBOL_GPL(sh4_ssi_dai);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SuperH onchip SSI (I2S) audio driver");
+MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");

+ 20 - 2
sound/usb/usbaudio.c

@@ -2350,7 +2350,9 @@ static int is_big_endian_format(struct snd_usb_audio *chip, struct audioformat *
 			return 1;
 			return 1;
 		break;
 		break;
 	case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
 	case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
-		return 1;
+		if (device_setup[chip->index] == 0x00 ||
+		    fp->altsetting==1 || fp->altsetting==2 || fp->altsetting==3)
+			return 1;
 	}
 	}
 	return 0;
 	return 0;
 }
 }
@@ -2530,7 +2532,18 @@ static int parse_audio_format_i(struct snd_usb_audio *chip, struct audioformat *
 		 *        but we give normal PCM format to get the existing
 		 *        but we give normal PCM format to get the existing
 		 *        apps working...
 		 *        apps working...
 		 */
 		 */
-		pcm_format = SNDRV_PCM_FORMAT_S16_LE;
+		switch (chip->usb_id) {
+
+		case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
+			if (device_setup[chip->index] == 0x00 && 
+			    fp->altsetting == 6)
+				pcm_format = SNDRV_PCM_FORMAT_S16_BE;
+			else
+				pcm_format = SNDRV_PCM_FORMAT_S16_LE;
+			break;
+		default:
+			pcm_format = SNDRV_PCM_FORMAT_S16_LE;
+		}
 	} else {
 	} else {
 		pcm_format = parse_audio_format_i_type(chip, fp, format, fmt);
 		pcm_format = parse_audio_format_i_type(chip, fp, format, fmt);
 		if (pcm_format < 0)
 		if (pcm_format < 0)
@@ -3251,6 +3264,11 @@ static int snd_usb_cm106_boot_quirk(struct usb_device *dev)
 static int audiophile_skip_setting_quirk(struct snd_usb_audio *chip,
 static int audiophile_skip_setting_quirk(struct snd_usb_audio *chip,
 					 int iface, int altno)
 					 int iface, int altno)
 {
 {
+	/* Reset ALL ifaces to 0 altsetting.
+	 * Call it for every possible altsetting of every interface.
+	 */
+	usb_set_interface(chip->dev, iface, 0);
+
 	if (device_setup[chip->index] & AUDIOPHILE_SET) {
 	if (device_setup[chip->index] & AUDIOPHILE_SET) {
 		if ((device_setup[chip->index] & AUDIOPHILE_SET_DTS)
 		if ((device_setup[chip->index] & AUDIOPHILE_SET_DTS)
 		    && altno != 6)
 		    && altno != 6)

+ 71 - 1
sound/usb/usbquirks.h

@@ -52,6 +52,24 @@
 	.bInterfaceClass = USB_CLASS_AUDIO,
 	.bInterfaceClass = USB_CLASS_AUDIO,
 	.bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL
 	.bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL
 },
 },
+{
+	.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+		       USB_DEVICE_ID_MATCH_INT_CLASS |
+		       USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+	.idVendor = 0x046d,
+	.idProduct = 0x08ae,
+	.bInterfaceClass = USB_CLASS_AUDIO,
+	.bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL
+},
+{
+	.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+		       USB_DEVICE_ID_MATCH_INT_CLASS |
+		       USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+	.idVendor = 0x046d,
+	.idProduct = 0x08c6,
+	.bInterfaceClass = USB_CLASS_AUDIO,
+	.bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL
+},
 {
 {
 	.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
 	.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
 		       USB_DEVICE_ID_MATCH_INT_CLASS |
 		       USB_DEVICE_ID_MATCH_INT_CLASS |
@@ -1051,7 +1069,15 @@ YAMAHA_DEVICE(0x7010, "UB99"),
 		.type = QUIRK_MIDI_STANDARD_INTERFACE
 		.type = QUIRK_MIDI_STANDARD_INTERFACE
 	}
 	}
 },
 },
-	/* TODO: add Roland EXR support */
+{
+	USB_DEVICE(0x0582, 0x0060),
+	.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+		.vendor_name = "Roland",
+		.product_name = "EXR Series",
+		.ifnum = 0,
+		.type = QUIRK_MIDI_STANDARD_INTERFACE
+	}
+},
 {
 {
 	/* has ID 0x0067 when not in "Advanced Driver" mode */
 	/* has ID 0x0067 when not in "Advanced Driver" mode */
 	USB_DEVICE(0x0582, 0x0065),
 	USB_DEVICE(0x0582, 0x0065),
@@ -1094,6 +1120,19 @@ YAMAHA_DEVICE(0x7010, "UB99"),
 		}
 		}
 	}
 	}
 },
 },
+{
+	USB_DEVICE(0x582, 0x00a6),
+	.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+		.vendor_name = "Roland",
+		.product_name = "Juno-G",
+		.ifnum = 0,
+		.type = QUIRK_MIDI_FIXED_ENDPOINT,
+		.data = & (const struct snd_usb_midi_endpoint_info) {
+			.out_cables = 0x0001,
+			.in_cables  = 0x0001
+		}
+	}
+},
 {	/*
 {	/*
 	 * This quirk is for the "Advanced" modes of the Edirol UA-25.
 	 * This quirk is for the "Advanced" modes of the Edirol UA-25.
 	 * If the switch is not in an advanced setting, the UA-25 has
 	 * If the switch is not in an advanced setting, the UA-25 has
@@ -1230,6 +1269,37 @@ YAMAHA_DEVICE(0x7010, "UB99"),
 	}
 	}
 },
 },
 	/* TODO: add Edirol MD-P1 support */
 	/* TODO: add Edirol MD-P1 support */
+{
+	/* Roland SH-201 */
+	USB_DEVICE(0x0582, 0x00ad),
+	.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+		.vendor_name = "Roland",
+		.product_name = "SH-201",
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_COMPOSITE,
+		.data = (const struct snd_usb_audio_quirk[]) {
+			{
+				.ifnum = 0,
+				.type = QUIRK_AUDIO_STANDARD_INTERFACE
+			},
+			{
+				.ifnum = 1,
+				.type = QUIRK_AUDIO_STANDARD_INTERFACE
+			},
+			{
+				.ifnum = 2,
+				.type = QUIRK_MIDI_FIXED_ENDPOINT,
+				.data = & (const struct snd_usb_midi_endpoint_info) {
+					.out_cables = 0x0001,
+					.in_cables  = 0x0001
+				}
+			},
+			{
+				.ifnum = -1
+			}
+		}
+	}
+},
 
 
 /* Guillemot devices */
 /* Guillemot devices */
 {
 {

+ 3 - 4
sound/usb/usx2y/usbusx2yaudio.c

@@ -935,10 +935,9 @@ static struct snd_pcm_ops snd_usX2Y_pcm_ops =
  */
  */
 static void usX2Y_audio_stream_free(struct snd_usX2Y_substream **usX2Y_substream)
 static void usX2Y_audio_stream_free(struct snd_usX2Y_substream **usX2Y_substream)
 {
 {
-	if (NULL != usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK]) {
-		kfree(usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK]);
-		usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK] = NULL;
-	}
+	kfree(usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK]);
+	usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK] = NULL;
+
 	kfree(usX2Y_substream[SNDRV_PCM_STREAM_CAPTURE]);
 	kfree(usX2Y_substream[SNDRV_PCM_STREAM_CAPTURE]);
 	usX2Y_substream[SNDRV_PCM_STREAM_CAPTURE] = NULL;
 	usX2Y_substream[SNDRV_PCM_STREAM_CAPTURE] = NULL;
 }
 }