]> pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/w1-2.6
authorLinus Torvalds <torvalds@ppc970.osdl.org>
Wed, 22 Jun 2005 17:41:59 +0000 (10:41 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Wed, 22 Jun 2005 17:41:59 +0000 (10:41 -0700)
142 files changed:
CREDITS
Documentation/sound/alsa/ALSA-Configuration.txt
Documentation/sound/alsa/CMIPCI.txt
Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
Documentation/sound/alsa/emu10k1-jack.txt [new file with mode: 0644]
Documentation/sound/alsa/hdspm.txt [new file with mode: 0644]
include/sound/ac97_codec.h
include/sound/asound.h
include/sound/control.h
include/sound/emu10k1.h
include/sound/gus.h
include/sound/hdspm.h [new file with mode: 0644]
include/sound/pcm.h
include/sound/seq_midi_event.h
include/sound/seq_virmidi.h
include/sound/timer.h
include/sound/version.h
net/ipv4/Kconfig
sound/Kconfig
sound/arm/Kconfig
sound/arm/Makefile
sound/arm/aaci.c [new file with mode: 0644]
sound/arm/aaci.h [new file with mode: 0644]
sound/arm/devdma.c [new file with mode: 0644]
sound/arm/devdma.h [new file with mode: 0644]
sound/core/control.c
sound/core/memalloc.c
sound/core/oss/pcm_oss.c
sound/core/oss/pcm_plugin.c
sound/core/pcm.c
sound/core/pcm_lib.c
sound/core/pcm_memory.c
sound/core/pcm_misc.c
sound/core/pcm_native.c
sound/core/seq/oss/seq_oss_synth.c
sound/core/seq/seq_dummy.c
sound/core/seq/seq_midi.c
sound/core/seq/seq_midi_event.c
sound/core/seq/seq_queue.c
sound/core/seq/seq_queue.h
sound/core/seq/seq_timer.c
sound/core/seq/seq_timer.h
sound/core/seq/seq_virmidi.c
sound/core/sound.c
sound/core/timer.c
sound/core/timer_compat.c
sound/drivers/vx/vx_pcm.c
sound/i2c/tea6330t.c
sound/isa/Kconfig
sound/isa/ad1816a/ad1816a.c
sound/isa/cs423x/cs4236.c
sound/isa/gus/gus_io.c
sound/isa/gus/gus_main.c
sound/isa/gus/gus_mem.c
sound/isa/gus/gus_pcm.c
sound/isa/gus/gus_reset.c
sound/isa/gus/gus_synth.c
sound/isa/gus/gus_tables.h
sound/isa/gus/gus_volume.c
sound/pci/Kconfig
sound/pci/ac97/ac97_codec.c
sound/pci/ac97/ac97_patch.c
sound/pci/ac97/ac97_patch.h
sound/pci/ali5451/ali5451.c
sound/pci/als4000.c
sound/pci/atiixp.c
sound/pci/atiixp_modem.c
sound/pci/au88x0/au88x0.c
sound/pci/azt3328.c
sound/pci/bt87x.c
sound/pci/ca0106/ca0106.h
sound/pci/ca0106/ca0106_main.c
sound/pci/ca0106/ca0106_mixer.c
sound/pci/ca0106/ca0106_proc.c
sound/pci/cmipci.c
sound/pci/cs4281.c
sound/pci/cs46xx/cs46xx.c
sound/pci/cs46xx/cs46xx_lib.c
sound/pci/emu10k1/emu10k1.c
sound/pci/emu10k1/emu10k1_main.c
sound/pci/emu10k1/emu10k1x.c
sound/pci/emu10k1/emufx.c
sound/pci/emu10k1/emumixer.c
sound/pci/emu10k1/emupcm.c
sound/pci/emu10k1/emuproc.c
sound/pci/emu10k1/irq.c
sound/pci/emu10k1/p16v.c
sound/pci/ens1370.c
sound/pci/es1938.c
sound/pci/es1968.c
sound/pci/fm801.c
sound/pci/hda/Makefile
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h
sound/pci/hda/hda_generic.c
sound/pci/hda/hda_intel.c
sound/pci/hda/hda_local.h
sound/pci/hda/hda_patch.h
sound/pci/hda/hda_proc.c
sound/pci/hda/patch_analog.c
sound/pci/hda/patch_cmedia.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_sigmatel.c [new file with mode: 0644]
sound/pci/ice1712/amp.c
sound/pci/ice1712/amp.h
sound/pci/ice1712/ice1712.c
sound/pci/ice1712/ice1712.h
sound/pci/ice1712/ice1724.c
sound/pci/ice1712/phase.c
sound/pci/ice1712/phase.h
sound/pci/ice1712/vt1720_mobo.c
sound/pci/ice1712/vt1720_mobo.h
sound/pci/intel8x0.c
sound/pci/intel8x0m.c
sound/pci/korg1212/korg1212.c
sound/pci/maestro3.c
sound/pci/mixart/mixart.c
sound/pci/nm256/nm256.c
sound/pci/rme32.c
sound/pci/rme96.c
sound/pci/rme9652/Makefile
sound/pci/rme9652/hdsp.c
sound/pci/rme9652/hdspm.c [new file with mode: 0644]
sound/pci/rme9652/rme9652.c
sound/pci/sonicvibes.c
sound/pci/trident/trident.c
sound/pci/via82xx.c
sound/pci/via82xx_modem.c
sound/pci/vx222/vx222.c
sound/pci/ymfpci/ymfpci.c
sound/pci/ymfpci/ymfpci_main.c
sound/pcmcia/vx/vx_entry.c
sound/synth/emux/emux_effect.c
sound/usb/Kconfig
sound/usb/usbaudio.c
sound/usb/usbaudio.h
sound/usb/usbmidi.c
sound/usb/usbmixer.c
sound/usb/usbmixer_maps.c
sound/usb/usbquirks.h
sound/usb/usx2y/usbusx2y.c
sound/usb/usx2y/usbusx2yaudio.c

diff --git a/CREDITS b/CREDITS
index 2993348ce6ce425cd8c9a48d266d4a8b80977a3b..3b7a1548aaf9c945d6c9c46f2ef310b4f2de1807 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -2380,9 +2380,10 @@ E: tmolina@cablespeed.com
 D: bug fixes, documentation, minor hackery
 
 N: James Morris
-E: jmorris@intercode.com.au
+E: jmorris@redhat.com
 W: http://www.intercode.com.au/jmorris/
-D: Netfilter, Linux Security Modules (LSM).
+D: Netfilter, Linux Security Modules (LSM), SELinux, IPSec,
+D: Crypto API, general networking, miscellaneous.
 S: PO Box 707
 S: Spit Junction NSW 2088
 S: Australia
index 71ef0498d5e03e3e334c3d9bdefdd1c502611291..104a994b8289fc056ac6685f8bd7720ce35289c5 100644 (file)
@@ -615,9 +615,11 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
   Module snd-hda-intel
   --------------------
 
-    Module for Intel HD Audio (ICH6, ICH6M, ICH7)
+    Module for Intel HD Audio (ICH6, ICH6M, ICH7), ATI SB450,
+              VIA VT8251/VT8237A
 
     model      - force the model name
+    position_fix - Fix DMA pointer (0 = FIFO size, 1 = none, 2 = POSBUF)
 
     Module supports up to 8 cards.
 
@@ -635,6 +637,10 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
          5stack        5-jack in back, 2-jack in front
          5stack-digout 5-jack in back, 2-jack in front, a SPDIF out
          w810          3-jack
+         z71v          3-jack (HP shared SPDIF)
+         asus          3-jack
+         uniwill       3-jack
+         F1734         2-jack
 
        CMI9880
          minimal       3-jack in back
@@ -642,6 +648,15 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
          full          6-jack in back, 2-jack in front
          full_dig      6-jack in back, 2-jack in front, SPDIF I/O
          allout        5-jack in back, 2-jack in front, SPDIF out
+         auto          auto-config reading BIOS (default)
+
+    Note 2: If you get click noises on output, try the module option
+           position_fix=1 or 2.  position_fix=1 will use the SD_LPIB
+           register value without FIFO size correction as the current
+           DMA pointer.  position_fix=2 will make the driver to use
+           the position buffer instead of reading SD_LPIB register.
+           (Usually SD_LPLIB register is more accurate than the
+           position buffer.)
 
   Module snd-hdsp
   ---------------
@@ -660,7 +675,19 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
           module did formerly.  It will allocate the buffers in advance
           when any HDSP cards are found.  To make the buffer
           allocation sure, load snd-page-alloc module in the early
-          stage of boot sequence.
+          stage of boot sequence.  See "Early Buffer Allocation"
+         section.
+
+  Module snd-hdspm
+  ----------------
+
+    Module for RME HDSP MADI board.
+
+    precise_ptr                - Enable precise pointer, or disable.
+    line_outs_monitor  - Send playback streams to analog outs by default.
+    enable_monitor     - Enable Analog Out on Channel 63/64 by default.
+
+    See hdspm.txt for details.
 
   Module snd-ice1712
   ------------------
@@ -677,15 +704,19 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
                         * TerraTec EWS 88D
                         * TerraTec EWX 24/96
                         * TerraTec DMX 6Fire
+                       * TerraTec Phase 88
                         * Hoontech SoundTrack DSP 24
                         * Hoontech SoundTrack DSP 24 Value
                         * Hoontech SoundTrack DSP 24 Media 7.1
+                       * Event Electronics, EZ8
                         * Digigram VX442
+                       * Lionstracs, Mediastaton
 
     model       - Use the given board model, one of the following:
                  delta1010, dio2496, delta66, delta44, audiophile, delta410,
                  delta1010lt, vx442, ewx2496, ews88mt, ews88mt_new, ews88d,
-                 dmx6fire, dsp24, dsp24_value, dsp24_71, ez8
+                 dmx6fire, dsp24, dsp24_value, dsp24_71, ez8,
+                 phase88, mediastation
     omni       - Omni I/O support for MidiMan M-Audio Delta44/66
     cs8427_timeout - reset timeout for the CS8427 chip (S/PDIF transciever)
                      in msec resolution, default value is 500 (0.5 sec)
@@ -694,20 +725,46 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     is not used with all Envy24 based cards (for example in the MidiMan Delta
     serie).
 
+    Note: The supported board is detected by reading EEPROM or PCI
+         SSID (if EEPROM isn't available).  You can override the
+         model by passing "model" module option in case that the
+         driver isn't configured properly or you want to try another
+         type for testing.
+
   Module snd-ice1724
   ------------------
 
-    Module for Envy24HT (VT/ICE1724) based PCI sound cards.
+    Module for Envy24HT (VT/ICE1724), Envy24PT (VT1720) based PCI sound cards.
                        * MidiMan M Audio Revolution 7.1
                        * AMP Ltd AUDIO2000
-                       * TerraTec Aureon Sky-5.1, Space-7.1
+                       * TerraTec Aureon 5.1 Sky
+                       * TerraTec Aureon 7.1 Space
+                       * TerraTec Aureon 7.1 Universe
+                       * TerraTec Phase 22
+                       * TerraTec Phase 28
+                       * AudioTrak Prodigy 7.1
+                       * AudioTrak Prodigy 192
+                       * Pontis MS300
+                       * Albatron K8X800 Pro II 
+                       * Chaintech ZNF3-150
+                       * Chaintech ZNF3-250
+                       * Chaintech 9CJS
+                       * Chaintech AV-710
+                       * Shuttle SN25P
 
     model       - Use the given board model, one of the following:
-                 revo71, amp2000, prodigy71, aureon51, aureon71,
-                 k8x800
+                 revo71, amp2000, prodigy71, prodigy192, aureon51,
+                 aureon71, universe, k8x800, phase22, phase28, ms300,
+                 av710
 
     Module supports up to 8 cards and autoprobe.
 
+    Note: The supported board is detected by reading EEPROM or PCI
+         SSID (if EEPROM isn't available).  You can override the
+         model by passing "model" module option in case that the
+         driver isn't configured properly or you want to try another
+         type for testing.
+
   Module snd-intel8x0
   -------------------
 
@@ -1026,7 +1083,8 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
           module did formerly.  It will allocate the buffers in advance
           when any RME9652 cards are found.  To make the buffer
           allocation sure, load snd-page-alloc module in the early
-          stage of boot sequence.
+          stage of boot sequence.  See "Early Buffer Allocation"
+         section.
 
   Module snd-sa11xx-uda1341 (on arm only)
   ---------------------------------------
@@ -1211,16 +1269,18 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
   ------------------
 
     Module for AC'97 motherboards based on VIA 82C686A/686B, 8233,
-    8233A, 8233C, 8235 (south) bridge.
+    8233A, 8233C, 8235, 8237 (south) bridge.
 
     mpu_port   - 0x300,0x310,0x320,0x330, otherwise obtain BIOS setup
                  [VIA686A/686B only]
     joystick   - Enable joystick (default off) [VIA686A/686B only]
     ac97_clock - AC'97 codec clock base (default 48000Hz)
     dxs_support        - support DXS channels,
-                 0 = auto (defalut), 1 = enable, 2 = disable,
-                 3 = 48k only, 4 = no VRA
-                 [VIA8233/C,8235 only]
+                 0 = auto (default), 1 = enable, 2 = disable,
+                 3 = 48k only, 4 = no VRA, 5 = enable any sample
+                 rate and different sample rates on different
+                 channels
+                 [VIA8233/C, 8235, 8237 only]
     ac97_quirk  - AC'97 workaround for strange hardware
                   See the description of intel8x0 module for details.
 
@@ -1232,18 +1292,23 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
           default value 1.4.  Then the interrupt number will be
           assigned under 15. You might also upgrade your BIOS.
     
-    Note: VIA8233/5 (not VIA8233A) can support DXS (direct sound)
+    Note: VIA8233/5/7 (not VIA8233A) can support DXS (direct sound)
          channels as the first PCM.  On these channels, up to 4
-         streams can be played at the same time.
+         streams can be played at the same time, and the controller
+         can perform sample rate conversion with separate rates for
+         each channel.
          As default (dxs_support = 0), 48k fixed rate is chosen
          except for the known devices since the output is often
          noisy except for 48k on some mother boards due to the
          bug of BIOS.
-         Please try once dxs_support=1 and if it works on other
+         Please try once dxs_support=5 and if it works on other
          sample rates (e.g. 44.1kHz of mp3 playback), please let us
          know the PCI subsystem vendor/device id's (output of
          "lspci -nv").
-         If it doesn't work, try dxs_support=4.  If it still doesn't
+         If dxs_support=5 does not work, try dxs_support=4; if it
+         doesn't work too, try dxs_support=1.  (dxs_support=1 is
+         usually for old motherboards.  The correct implementated
+         board should work with 4 or 5.)  If it still doesn't
          work and the default setting is ok, dxs_support=3 is the
          right choice.  If the default setting doesn't work at all,
          try dxs_support=2 to disable the DXS channels.
@@ -1497,6 +1562,36 @@ Proc interfaces (/proc/asound)
           echo "rvplayer 0 0 block" > /proc/asound/card0/pcm0p/oss
 
 
+Early Buffer Allocation
+=======================
+
+Some drivers (e.g. hdsp) require the large contiguous buffers, and
+sometimes it's too late to find such spaces when the driver module is
+actually loaded due to memory fragmentation.  You can pre-allocate the
+PCM buffers by loading snd-page-alloc module and write commands to its
+proc file in prior, for example, in the early boot stage like
+/etc/init.d/*.local scripts.
+
+Reading the proc file /proc/drivers/snd-page-alloc shows the current
+usage of page allocation.  In writing, you can send the following
+commands to the snd-page-alloc driver:
+
+  - add VENDOR DEVICE MASK SIZE BUFFERS
+
+    VENDOR and DEVICE are PCI vendor and device IDs.  They take
+    integer numbers (0x prefix is needed for the hex).
+    MASK is the PCI DMA mask.  Pass 0 if not restricted.
+    SIZE is the size of each buffer to allocate.  You can pass
+    k and m suffix for KB and MB.  The max number is 16MB.
+    BUFFERS is the number of buffers to allocate.  It must be greater
+    than 0.  The max number is 4.
+
+  - erase
+
+    This will erase the all pre-allocated buffers which are not in
+    use.
+
+
 Links
 =====
 
index 4a7df771b806c7f7728d4b72292b62478a3bcd37..1872e24442a47537d7b0497ab2ea17067dc64f16 100644 (file)
@@ -89,19 +89,22 @@ and use the interleaved 4 channel data.
 
 There are some control switchs affecting to the speaker connections:
 
-"Line-In As Rear"      - As mentioned above, the line-in jack is used
-       for the rear (3th and 4th channels) output.
-"Line-In As Bass"      - The line-in jack is used for the bass (5th
-       and 6th channels) output.
-"Mic As Center/LFE"    - The mic jack is used for the bass output.
-       If this switch is on, you cannot use a microphone as a capture
-       source, of course.
-
+"Line-In Mode" - an enum control to change the behavior of line-in
+       jack.  Either "Line-In", "Rear Output" or "Bass Output" can
+       be selected.  The last item is available only with model 039
+       or newer. 
+       When "Rear Output" is chosen, the surround channels 3 and 4
+       are output to line-in jack.
+"Mic-In Mode"  - an enum control to change the behavior of mic-in
+       jack.  Either "Mic-In" or "Center/LFE Output" can be
+       selected. 
+       When "Center/LFE Output" is chosen, the center and bass
+       channels (channels 5 and 6) are output to mic-in jack. 
 
 Digital I/O
 -----------
 
-The CM8x38 provides the excellent SPDIF capability with very chip
+The CM8x38 provides the excellent SPDIF capability with very cheap
 price (yes, that's the reason I bought the card :)
 
 The SPDIF playback and capture are done via the third PCM device
@@ -122,8 +125,9 @@ respectively, so you cannot playback both analog and digital streams
 simultaneously.
 
 To enable SPDIF output, you need to turn on "IEC958 Output Switch"
-control via mixer or alsactl.  Then you'll see the red light on from
-the card so you know that's working obviously :)
+control via mixer or alsactl ("IEC958" is the official name of
+so-called S/PDIF).  Then you'll see the red light on from the card so
+you know that's working obviously :)
 The SPDIF input is always enabled, so you can hear SPDIF input data
 from line-out with "IEC958 In Monitor" switch at any time (see
 below).
@@ -205,9 +209,10 @@ In addition to the standard SB mixer, CM8x38 provides more functions.
 MIDI CONTROLLER
 ---------------
 
-The MPU401-UART interface is enabled as default only for the first
-(CMIPCI) card.  You need to set module option "midi_port" properly
-for the 2nd (CMIPCI) card.
+The MPU401-UART interface is disabled as default.  You need to set
+module option "mpu_port" with a valid I/O port address to enable the
+MIDI support.  The valid I/O ports are 0x300, 0x310, 0x320 and 0x330.
+Choose the value which doesn't conflict with other cards.
 
 There is _no_ hardware wavetable function on this chip (except for
 OPL3 synth below).
@@ -229,9 +234,11 @@ I don't know why..
 Joystick and Modem
 ------------------
 
-The joystick and modem should be available by enabling the control
-switch "Joystick" and "Modem" respectively.  But I myself have never
-tested them yet.
+The legacy joystick is supported.  To enable the joystick support, pass
+joystick_port=1 module option.  The value 1 means the auto-detection.
+If the auto-detection fails, try to pass the exact I/O address.
+
+The modem is enabled dynamically via a card control switch "Modem".
 
 
 Debugging Information
index e789475304b6e03b8b8e5c60c72fd8f445f65a5b..db0b7d2dc477cfc01aa0bc10282c850e0c10ae9f 100644 (file)
           <listitem><para>create <function>probe()</function> callback.</para></listitem>
           <listitem><para>create <function>remove()</function> callback.</para></listitem>
           <listitem><para>create pci_driver table which contains the three pointers above.</para></listitem>
-          <listitem><para>create <function>init()</function> function just calling <function>pci_module_init()</function> to register the pci_driver table defined above.</para></listitem>
+          <listitem><para>create <function>init()</function> function just calling <function>pci_register_driver()</function> to register the pci_driver table defined above.</para></listitem>
           <listitem><para>create <function>exit()</function> function to call <function>pci_unregister_driver()</function> function.</para></listitem>
         </itemizedlist>
       </para>
   /* initialization of the module */
   static int __init alsa_card_mychip_init(void)
   {
-          return pci_module_init(&driver);
+          return pci_register_driver(&driver);
   }
 
   /* clean up the module */
 <![CDATA[
   static int __init alsa_card_mychip_init(void)
   {
-          return pci_module_init(&driver);
+          return pci_register_driver(&driver);
   }
 
   static void __exit alsa_card_mychip_exit(void)
diff --git a/Documentation/sound/alsa/emu10k1-jack.txt b/Documentation/sound/alsa/emu10k1-jack.txt
new file mode 100644 (file)
index 0000000..751d450
--- /dev/null
@@ -0,0 +1,74 @@
+This document is a guide to using the emu10k1 based devices with JACK for low
+latency, multichannel recording functionality.  All of my recent work to allow
+Linux users to use the full capabilities of their hardware has been inspired 
+by the kX Project.  Without their work I never would have discovered the true
+power of this hardware.
+
+       http://www.kxproject.com
+                                               - Lee Revell, 2005.03.30
+
+Low latency, multichannel audio with JACK and the emu10k1/emu10k2
+-----------------------------------------------------------------
+
+Until recently, emu10k1 users on Linux did not have access to the same low
+latency, multichannel features offered by the "kX ASIO" feature of their
+Windows driver.  As of ALSA 1.0.9 this is no more!
+
+For those unfamiliar with kX ASIO, this consists of 16 capture and 16 playback
+channels.  With a post 2.6.9 Linux kernel, latencies down to 64 (1.33 ms) or
+even 32 (0.66ms) frames should work well.
+
+The configuration is slightly more involved than on Windows, as you have to
+select the correct device for JACK to use.  Actually, for qjackctl users it's
+fairly self explanatory - select Duplex, then for capture and playback select
+the multichannel devices, set the in and out channels to 16, and the sample
+rate to 48000Hz.  The command line looks like this:
+
+/usr/local/bin/jackd -R -dalsa -r48000 -p64 -n2 -D -Chw:0,2 -Phw:0,3 -S
+
+This will give you 16 input ports and 16 output ports.
+
+The 16 output ports map onto the 16 FX buses (or the first 16 of 64, for the
+Audigy).  The mapping from FX bus to physical output is described in
+SB-Live-mixer.txt (or Audigy-mixer.txt).
+
+The 16 input ports are connected to the 16 physical inputs.  Contrary to
+popular belief, all emu10k1 cards are multichannel cards.  Which of these
+input channels have physical inputs connected to them depends on the card
+model.  Trial and error is highly recommended; the pinout diagrams
+for the card have been reverse engineered by some enterprising kX users and are 
+available on the internet.  Meterbridge is helpful here, and the kX forums are
+packed with useful information.
+
+Each input port will either correspond to a digital (SPDIF) input, an analog
+input, or nothing.  The one exception is the SBLive! 5.1.  On these devices,
+the second and third input ports are wired to the center/LFE output.  You will
+still see 16 capture channels, but only 14 are available for recording inputs.
+
+This chart, borrowed from kxfxlib/da_asio51.cpp, describes the mapping of JACK
+ports to FXBUS2 (multitrack recording input) and EXTOUT (physical output)
+channels.
+
+/*JACK (& ASIO) mappings on 10k1 5.1 SBLive cards:
+--------------------------------------------
+JACK           Epilog          FXBUS2(nr)
+--------------------------------------------
+capture_1      asio14          FXBUS2(0xe)
+capture_2      asio15          FXBUS2(0xf)
+capture_3      asio0           FXBUS2(0x0)     
+~capture_4     Center          EXTOUT(0x11)    // mapped to by Center
+~capture_5     LFE             EXTOUT(0x12)    // mapped to by LFE
+capture_6      asio3           FXBUS2(0x3)
+capture_7      asio4           FXBUS2(0x4)
+capture_8      asio5           FXBUS2(0x5)
+capture_9      asio6           FXBUS2(0x6)
+capture_10     asio7           FXBUS2(0x7)
+capture_11     asio8           FXBUS2(0x8)
+capture_12     asio9           FXBUS2(0x9)
+capture_13     asio10          FXBUS2(0xa)
+capture_14     asio11          FXBUS2(0xb)
+capture_15     asio12          FXBUS2(0xc)
+capture_16     asio13          FXBUS2(0xd)
+*/
+
+TODO: describe use of ld10k1/qlo10k1 in conjunction with JACK
diff --git a/Documentation/sound/alsa/hdspm.txt b/Documentation/sound/alsa/hdspm.txt
new file mode 100644 (file)
index 0000000..7a67ff7
--- /dev/null
@@ -0,0 +1,362 @@
+Software Interface ALSA-DSP MADI Driver 
+
+(translated from German, so no good English ;-), 
+2004 - winfried ritsch
+
+
+
+ Full functionality has been added to the driver. Since some of
+ the Controls and startup-options  are ALSA-Standard and only the
+ special Controls are described and discussed below.
+
+
+ hardware functionality:
+
+   
+   Audio transmission:
+
+     number of channels --  depends on transmission mode
+
+               The number of channels chosen is from 1..Nmax. The reason to
+               use for a lower number of channels is only resource allocation,
+               since unused DMA channels are disabled and less memory is
+               allocated. So also the throughput of the PCI system can be
+               scaled. (Only important for low performance boards).
+
+       Single Speed -- 1..64 channels 
+
+                (Note: Choosing the 56channel mode for transmission or as
+                receiver, only 56 are transmitted/received over the MADI, but
+                all 64 channels are available for the mixer, so channel count
+                for the driver)
+
+       Double Speed -- 1..32 channels
+
+                Note: Choosing the 56-channel mode for
+                transmission/receive-mode , only 28 are transmitted/received
+                over the MADI, but all 32 channels are available for the mixer,
+                so channel count for the driver
+
+
+       Quad Speed -- 1..16 channels 
+
+                Note: Choosing the 56-channel mode for
+                transmission/receive-mode , only 14 are transmitted/received
+                over the MADI, but all 16 channels are available for the mixer,
+                so channel count for the driver
+
+     Format -- signed 32 Bit Little Endian (SNDRV_PCM_FMTBIT_S32_LE)
+
+     Sample Rates --
+
+       Single Speed -- 32000, 44100, 48000
+
+       Double Speed -- 64000, 88200, 96000 (untested)
+
+       Quad Speed -- 128000, 176400, 192000 (untested)
+
+     access-mode -- MMAP (memory mapped), Not interleaved
+     (PCM_NON-INTERLEAVED)
+
+     buffer-sizes -- 64,128,256,512,1024,2048,8192 Samples
+
+     fragments -- 2
+
+     Hardware-pointer -- 2 Modi
+
+
+                The Card supports the readout of the actual Buffer-pointer,
+                where DMA reads/writes. Since of the bulk mode of PCI it is only
+                64 Byte accurate. SO it is not really usable for the
+                ALSA-mid-level functions (here the buffer-ID gives a better
+                result), but if MMAP is used by the application. Therefore it
+                can be configured at load-time with the parameter
+                precise-pointer.
+
+
+                (Hint: Experimenting I found that the pointer is maximum 64 to
+                large never to small. So if you subtract 64 you always have a
+                safe pointer for writing, which is used on this mode inside
+                ALSA. In theory now you can get now a latency as low as 16
+                Samples, which is a quarter of the interrupt possibilities.)
+
+       Precise Pointer -- off
+                                       interrupt used for pointer-calculation
+
+       Precise Pointer -- on
+                                       hardware pointer used.
+
+   Controller:
+
+
+         Since DSP-MADI-Mixer has 8152 Fader, it does not make sense to
+         use the standard mixer-controls, since this would break most of
+         (especially graphic) ALSA-Mixer GUIs. So Mixer control has be
+         provided by a 2-dimensional controller using the
+         hwdep-interface. 
+
+     Also all 128+256 Peak and RMS-Meter can be accessed via the
+     hwdep-interface. Since it could be a performance problem always
+     copying and converting Peak and RMS-Levels even if you just need
+     one, I decided to export the hardware structure, so that of
+     needed some driver-guru can implement a memory-mapping of mixer
+     or peak-meters over ioctl, or also to do only copying and no
+     conversion. A test-application shows the usage of the controller.
+
+    Latency Controls --- not implemented !!!
+
+
+          Note: Within the windows-driver the latency is accessible of a
+          control-panel, but buffer-sizes are controlled with ALSA from
+          hwparams-calls and should not be changed in run-state, I did not
+          implement it here.
+
+
+    System Clock -- suspended !!!!
+
+        Name -- "System Clock Mode"
+
+        Access -- Read Write
+
+        Values -- "Master" "Slave"
+
+
+                 !!!! This is a hardware-function but is in conflict with the
+                 Clock-source controller, which is a kind of ALSA-standard. I
+                 makes sense to set the card to a special mode (master at some
+                 frequency or slave), since even not using an Audio-application
+                 a studio should have working synchronisations setup. So use
+                 Clock-source-controller instead !!!!
+
+    Clock Source  
+
+       Name -- "Sample Clock Source"
+
+       Access -- Read Write
+
+       Values -- "AutoSync", "Internal 32.0 kHz", "Internal 44.1 kHz",
+       "Internal 48.0 kHz", "Internal 64.0 kHz", "Internal 88.2 kHz",
+       "Internal 96.0 kHz"
+
+                Choose between Master at a specific Frequency and so also the
+                Speed-mode or Slave (Autosync). Also see  "Preferred Sync Ref"
+
+
+       !!!! This is no pure hardware function but was implemented by
+       ALSA by some ALSA-drivers before, so I use it also. !!!
+
+
+    Preferred Sync Ref
+
+       Name -- "Preferred Sync Reference"
+
+       Access -- Read Write
+
+       Values -- "Word" "MADI"
+
+
+                Within the Auto-sync-Mode the preferred Sync Source can be
+                chosen. If it is not available another is used if possible.
+
+                Note: Since MADI has a much higher bit-rate than word-clock, the
+                card should synchronise better in MADI Mode. But since the
+                RME-PLL is very good, there are almost no problems with
+                word-clock too. I never found a difference.
+
+
+    TX 64 channel --- 
+
+       Name -- "TX 64 channels mode"
+
+       Access -- Read Write
+
+       Values -- 0 1
+
+                Using 64-channel-modus (1) or 56-channel-modus for
+                MADI-transmission (0).
+
+
+                Note: This control is for output only. Input-mode is detected
+                automatically from hardware sending MADI.
+
+
+    Clear TMS ---
+
+       Name -- "Clear Track Marker"
+
+       Access -- Read Write
+
+       Values -- 0 1
+
+
+                Don't use to lower 5 Audio-bits on AES as additional Bits.
+        
+
+    Safe Mode oder Auto Input --- 
+
+       Name -- "Safe Mode"
+
+       Access -- Read Write
+
+       Values -- 0 1
+
+       (default on)
+
+                If on (1), then if either the optical or coaxial connection
+                has a failure, there is a takeover to the working one, with no
+                sample failure. Its only useful if you use the second as a
+                backup connection.
+
+    Input --- 
+
+       Name -- "Input Select"
+
+       Access -- Read Write
+
+       Values -- optical coaxial
+
+
+                Choosing the Input, optical or coaxial. If Safe-mode is active,
+                this is the preferred Input.
+
+-------------- Mixer ----------------------
+
+    Mixer
+
+       Name -- "Mixer"
+
+       Access -- Read Write
+
+       Values - <channel-number 0-127> <Value 0-65535>
+
+
+                Here as a first value the channel-index is taken to get/set the
+                corresponding mixer channel, where 0-63 are the input to output
+                fader and 64-127 the playback to outputs fader. Value 0
+                is channel muted 0 and 32768 an amplification of  1.
+
+    Chn 1-64
+
+       fast mixer for the ALSA-mixer utils. The diagonal of the
+       mixer-matrix is implemented from playback to output.
+       
+
+    Line Out
+
+       Name  -- "Line Out"
+
+       Access -- Read Write
+
+       Values -- 0 1
+
+                Switching on and off the analog out, which has nothing to do
+                with mixing or routing. the analog outs reflects channel 63,64.
+
+
+--- information (only read access):
+    Sample Rate
+
+       Name -- "System Sample Rate"
+
+       Access -- Read-only
+
+                getting the sample rate.
+
+
+    External Rate measured
+
+       Name -- "External Rate"
+
+       Access -- Read only
+
+
+                Should be "Autosync Rate", but Name used is
+                ALSA-Scheme. External Sample frequency liked used on Autosync is
+                reported.
+
+
+    MADI Sync Status
+
+       Name -- "MADI Sync Lock Status"
+
+       Access -- Read
+
+       Values -- 0,1,2
+
+       MADI-Input is 0=Unlocked, 1=Locked, or 2=Synced.
+
+
+    Word Clock Sync Status
+
+       Name -- "Word Clock Lock Status"
+
+       Access -- Read
+
+       Values -- 0,1,2
+
+       Word Clock Input is 0=Unlocked, 1=Locked, or 2=Synced.
+
+    AutoSync
+
+       Name -- "AutoSync Reference"
+
+       Access -- Read
+
+       Values -- "WordClock", "MADI", "None"
+
+                Sync-Reference is either "WordClock", "MADI" or none.
+
+   RX 64ch --- noch nicht implementiert
+
+       MADI-Receiver is in 64 channel mode oder 56 channel mode.
+
+
+   AB_inp   --- not tested 
+
+                Used input for Auto-Input.
+
+
+   actual Buffer Position --- not implemented
+
+          !!! this is a ALSA internal function, so no control is used !!!
+
+
+
+Calling Parameter:
+
+   index int array (min = 1, max = 8), 
+     "Index value for RME HDSPM interface." card-index within ALSA
+
+     note: ALSA-standard
+
+   id string array (min = 1, max = 8), 
+     "ID string for RME HDSPM interface."
+
+     note: ALSA-standard
+
+   enable int array (min = 1, max = 8), 
+     "Enable/disable specific HDSPM sound-cards."
+
+     note: ALSA-standard
+
+   precise_ptr int array (min = 1, max = 8), 
+     "Enable precise pointer, or disable."
+
+     note: Use only when the application supports this (which is a special case).
+
+   line_outs_monitor int array (min = 1, max = 8), 
+     "Send playback streams to analog outs by default."
+
+
+         note: each playback channel is mixed to the same numbered output
+         channel (routed). This is against the ALSA-convention, where all
+         channels have to be muted on after loading the driver, but was
+         used before on other cards, so i historically use it again)
+
+
+
+   enable_monitor int array (min = 1, max = 8), 
+     "Enable Analog Out on Channel 63/64 by default."
+
+      note: here the analog output is enabled (but not routed).
\ No newline at end of file
index 2433e279e0712040dcd6c6a1b372c000cd4bbd7b..1309c12b8f71274faf024a944a8244abf17ed5e3 100644 (file)
@@ -437,6 +437,7 @@ struct snd_ac97_build_ops {
        void (*suspend) (ac97_t *ac97);
        void (*resume) (ac97_t *ac97);
 #endif
+       void (*update_jacks) (ac97_t *ac97);    /* for jack-sharing */
 };
 
 struct _snd_ac97_bus_ops {
@@ -516,6 +517,9 @@ struct _snd_ac97 {
                } ad18xx;
                unsigned int dev_flags;         /* device specific */
        } spec;
+       /* jack-sharing info */
+       unsigned char indep_surround;
+       unsigned char channel_mode;
 };
 
 /* conditions */
@@ -569,8 +573,8 @@ enum {
 };
 
 struct ac97_quirk {
-       unsigned short vendor;  /* PCI vendor id */
-       unsigned short device;  /* PCI device id */
+       unsigned short subvendor; /* PCI subsystem vendor id */
+       unsigned short subdevice; /* PCI sybsystem device id */
        unsigned short mask;    /* device id bit mask, 0 = accept all */
        unsigned int codec_id;  /* codec id (if any), 0 = accept all */
        const char *name;       /* name shown as info */
index a4d149f34541e2b455a528c35096cbfe5b337ace..9974f83cca440a88e6e9af4f30e0b7cc2bad767b 100644 (file)
@@ -113,9 +113,10 @@ enum sndrv_hwdep_iface {
        SNDRV_HWDEP_IFACE_BLUETOOTH,    /* Bluetooth audio */
        SNDRV_HWDEP_IFACE_USX2Y_PCM,    /* Tascam US122, US224 & US428 rawusb pcm */
        SNDRV_HWDEP_IFACE_PCXHR,        /* Digigram PCXHR */
+       SNDRV_HWDEP_IFACE_SB_RC,        /* SB Extigy/Audigy2NX remote control */
 
        /* Don't forget to change the following: */
-       SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_PCXHR
+       SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_SB_RC
 };
 
 struct sndrv_hwdep_info {
@@ -344,7 +345,7 @@ enum sndrv_pcm_hw_param {
        SNDRV_PCM_HW_PARAM_LAST_INTERVAL = SNDRV_PCM_HW_PARAM_TICK_TIME
 };
 
-#define SNDRV_PCM_HW_PARAMS_RUNTIME            (1<<0)
+#define SNDRV_PCM_HW_PARAMS_NORESAMPLE         (1<<0)  /* avoid rate resampling */
 
 struct sndrv_interval {
        unsigned int min, max;
@@ -559,7 +560,7 @@ enum {
  *  Timer section - /dev/snd/timer
  */
 
-#define SNDRV_TIMER_VERSION            SNDRV_PROTOCOL_VERSION(2, 0, 2)
+#define SNDRV_TIMER_VERSION            SNDRV_PROTOCOL_VERSION(2, 0, 4)
 
 enum sndrv_timer_class {
        SNDRV_TIMER_CLASS_NONE = -1,
@@ -672,10 +673,11 @@ enum {
        SNDRV_TIMER_IOCTL_INFO = _IOR('T', 0x11, struct sndrv_timer_info),
        SNDRV_TIMER_IOCTL_PARAMS = _IOW('T', 0x12, struct sndrv_timer_params),
        SNDRV_TIMER_IOCTL_STATUS = _IOR('T', 0x14, struct sndrv_timer_status),
-       SNDRV_TIMER_IOCTL_START = _IO('T', 0x20),
-       SNDRV_TIMER_IOCTL_STOP = _IO('T', 0x21),
-       SNDRV_TIMER_IOCTL_CONTINUE = _IO('T', 0x22),
-       SNDRV_TIMER_IOCTL_PAUSE = _IO('T', 0x23),
+       /* The following four ioctls are changed since 1.0.9 due to confliction */
+       SNDRV_TIMER_IOCTL_START = _IO('T', 0xa0),
+       SNDRV_TIMER_IOCTL_STOP = _IO('T', 0xa1),
+       SNDRV_TIMER_IOCTL_CONTINUE = _IO('T', 0xa2),
+       SNDRV_TIMER_IOCTL_PAUSE = _IO('T', 0xa3),
 };
 
 struct sndrv_timer_read {
index 7b9444cd02f400af4242f21c38db416ea4da02da..ef7903c7a3277b63d7860d8c6c9d18bbb9e9230d 100644 (file)
@@ -106,7 +106,7 @@ typedef int (*snd_kctl_ioctl_func_t) (snd_card_t * card,
 void snd_ctl_notify(snd_card_t * card, unsigned int mask, snd_ctl_elem_id_t * id);
 
 snd_kcontrol_t *snd_ctl_new(snd_kcontrol_t * kcontrol, unsigned int access);
-snd_kcontrol_t *snd_ctl_new1(snd_kcontrol_new_t * kcontrolnew, void * private_data);
+snd_kcontrol_t *snd_ctl_new1(const snd_kcontrol_new_t * kcontrolnew, void * private_data);
 void snd_ctl_free_one(snd_kcontrol_t * kcontrol);
 int snd_ctl_add(snd_card_t * card, snd_kcontrol_t * kcontrol);
 int snd_ctl_remove(snd_card_t * card, snd_kcontrol_t * kcontrol);
index 43b6786abae527e4a7ecbaae77def5a778fa7503..c50b91958ff985c4d2028d3edbef9bc1cec5e28e 100644 (file)
@@ -83,7 +83,8 @@
 #define IPR                    0x08            /* Global interrupt pending register            */
                                                /* Clear pending interrupts by writing a 1 to   */
                                                /* the relevant bits and zero to the other bits */
-
+#define IPR_P16V               0x80000000      /* Bit set when the CA0151 P16V chip wishes
+                                                  to interrupt */
 #define IPR_GPIOMSG            0x20000000      /* GPIO message interrupt (RE'd, still not sure 
                                                   which INTE bits enable it)                   */
 
                                                /* Assumes sample lock                          */
 
 /* These three bitfields apply to CDSRCS, GPSRCS, and (except as noted) ZVSRCS.                        */
+#define SRCS_SPDIFVALID                0x04000000      /* SPDIF stream valid                           */
 #define SRCS_SPDIFLOCKED       0x02000000      /* SPDIF stream locked                          */
 #define SRCS_RATELOCKED                0x01000000      /* Sample rate locked                           */
 #define SRCS_ESTSAMPLERATE     0x0007ffff      /* Do not modify this field.                    */
 #define A_FXWC2                        0x75            /* Selects 0x9f-0x80 for FX recording           */
 
 #define A_SPDIF_SAMPLERATE     0x76            /* Set the sample rate of SPDIF output          */
-#define A_SPDIF_RATE_MASK      0x000000c0
+#define A_SAMPLE_RATE          0x76            /* Various sample rate settings. */
+#define A_SAMPLE_RATE_NOT_USED  0x0ffc111e     /* Bits that are not used and cannot be set.    */
+#define A_SAMPLE_RATE_UNKNOWN  0xf0030001      /* Bits that can be set, but have unknown use.  */
+#define A_SPDIF_RATE_MASK      0x000000e0      /* Any other values for rates, just use 48000   */
 #define A_SPDIF_48000          0x00000000
-#define A_SPDIF_44100          0x00000080
+#define A_SPDIF_192000         0x00000020
 #define A_SPDIF_96000          0x00000040
+#define A_SPDIF_44100          0x00000080
+
+#define A_I2S_CAPTURE_RATE_MASK        0x00000e00      /* This sets the capture PCM rate, but it is    */
+#define A_I2S_CAPTURE_48000    0x00000000      /* unclear if this sets the ADC rate as well.   */
+#define A_I2S_CAPTURE_192000   0x00000200
+#define A_I2S_CAPTURE_96000    0x00000400
+#define A_I2S_CAPTURE_44100    0x00000800
+
+#define A_PCM_RATE_MASK                0x0000e000      /* This sets the playback PCM rate on the P16V  */
+#define A_PCM_48000            0x00000000
+#define A_PCM_192000           0x00002000
+#define A_PCM_96000            0x00004000
+#define A_PCM_44100            0x00008000
 
 /* 0x77,0x78,0x79 "something i2s-related" - default to 0x01080000 on my audigy 2 ZS --rlrevell */
 /* 0x7a, 0x7b - lookup tables */
@@ -1039,28 +1057,28 @@ typedef struct {
        u32 vendor;
        u32 device;
        u32 subsystem;
+       unsigned char revision;
        unsigned char emu10k1_chip; /* Original SB Live. Not SB Live 24bit. */
        unsigned char emu10k2_chip; /* Audigy 1 or Audigy 2. */
        unsigned char ca0102_chip;  /* Audigy 1 or Audigy 2. Not SB Audigy 2 Value. */
        unsigned char ca0108_chip;  /* Audigy 2 Value */
        unsigned char ca0151_chip;  /* P16V */
        unsigned char spk71;        /* Has 7.1 speakers */
+       unsigned char sblive51;     /* SBLive! 5.1 - extout 0x11 -> center, 0x12 -> lfe */
        unsigned char spdif_bug;    /* Has Spdif phasing bug */
        unsigned char ac97_chip;    /* Has an AC97 chip */
        unsigned char ecard;        /* APS EEPROM */
-       char * driver;
-       char * name;
+       const char *driver;
+       const char *name;
+       const char *id;         /* for backward compatibility - can be NULL if not needed */
 } emu_chip_details_t;
 
 struct _snd_emu10k1 {
        int irq;
 
        unsigned long port;                     /* I/O port number */
-       unsigned int APS: 1,                    /* APS flag */
-           no_ac97: 1,                         /* no AC'97 */
-           tos_link: 1,                        /* tos link detected */
-           rear_ac97: 1,                       /* rear channels are on AC'97 */
-           spk71:1;                            /* 7.1 configuration (Audigy 2 ZS) */
+       unsigned int tos_link: 1,               /* tos link detected */
+           rear_ac97: 1;                       /* rear channels are on AC'97 */
        const emu_chip_details_t *card_capabilities;    /* Contains profile of card capabilities */
        unsigned int audigy;                    /* is Audigy? */
        unsigned int revision;                  /* chip revision */
@@ -1109,7 +1127,10 @@ struct _snd_emu10k1 {
 
        emu10k1_voice_t voices[NUM_G];
        emu10k1_voice_t p16v_voices[4];
+       emu10k1_voice_t p16v_capture_voice;
        int p16v_device_offset;
+       u32 p16v_capture_source;
+       u32 p16v_capture_channel;
        emu10k1_pcm_mixer_t pcm_mixer[32];
        emu10k1_pcm_mixer_t efx_pcm_mixer[NUM_EFX_PLAYBACK];
        snd_kcontrol_t *ctl_send_routing;
@@ -1453,7 +1474,6 @@ int snd_emu10k1_fx8010_unregister_irq_handler(emu10k1_t *emu,
 #endif
 
 typedef struct {
-       unsigned int card;                      /* card type */
        unsigned int internal_tram_size;        /* in samples */
        unsigned int external_tram_size;        /* in samples */
        char fxbus_names[16][32];               /* names of FXBUSes */
index 8b6287a6fff574b8ea21ee3237c27d7936883fba..b4b461ca173d6eb72a58ce7a1079b1c1e9417aaf 100644 (file)
@@ -526,9 +526,6 @@ extern void snd_gf1_adlib_write(snd_gus_card_t * gus, unsigned char reg, unsigne
 extern void snd_gf1_dram_addr(snd_gus_card_t * gus, unsigned int addr);
 extern void snd_gf1_poke(snd_gus_card_t * gus, unsigned int addr, unsigned char data);
 extern unsigned char snd_gf1_peek(snd_gus_card_t * gus, unsigned int addr);
-extern void snd_gf1_pokew(snd_gus_card_t * gus, unsigned int addr, unsigned short data);
-extern unsigned short snd_gf1_peekw(snd_gus_card_t * gus, unsigned int addr);
-extern void snd_gf1_dram_setmem(snd_gus_card_t * gus, unsigned int addr, unsigned short value, unsigned int count);
 extern void snd_gf1_write_addr(snd_gus_card_t * gus, unsigned char reg, unsigned int addr, short w_16bit);
 extern unsigned int snd_gf1_read_addr(snd_gus_card_t * gus, unsigned char reg, short w_16bit);
 extern void snd_gf1_i_ctrl_stop(snd_gus_card_t * gus, unsigned char reg);
@@ -544,9 +541,6 @@ extern inline unsigned short snd_gf1_i_read16(snd_gus_card_t * gus, unsigned cha
 {
        return snd_gf1_i_look16(gus, reg | 0x80);
 }
-extern void snd_gf1_i_adlib_write(snd_gus_card_t * gus, unsigned char reg, unsigned char data);
-extern void snd_gf1_i_write_addr(snd_gus_card_t * gus, unsigned char reg, unsigned int addr, short w_16bit);
-extern unsigned int snd_gf1_i_read_addr(snd_gus_card_t * gus, unsigned char reg, short w_16bit);
 
 extern void snd_gf1_select_active_voices(snd_gus_card_t * gus);
 
@@ -580,10 +574,6 @@ extern void snd_gf1_lfo_command(snd_gus_card_t * gus, int voice, unsigned char *
 
 void snd_gf1_mem_lock(snd_gf1_mem_t * alloc, int xup);
 int snd_gf1_mem_xfree(snd_gf1_mem_t * alloc, snd_gf1_mem_block_t * block);
-snd_gf1_mem_block_t *snd_gf1_mem_look(snd_gf1_mem_t * alloc,
-                                     unsigned int address);
-snd_gf1_mem_block_t *snd_gf1_mem_share(snd_gf1_mem_t * alloc,
-                                      unsigned int *share_id);
 snd_gf1_mem_block_t *snd_gf1_mem_alloc(snd_gf1_mem_t * alloc, int owner,
                                       char *name, int size, int w_16,
                                       int align, unsigned int *share_id);
@@ -608,23 +598,13 @@ int snd_gf1_dma_transfer_block(snd_gus_card_t * gus,
 /* gus_volume.c */
 
 unsigned short snd_gf1_lvol_to_gvol_raw(unsigned int vol);
-unsigned int snd_gf1_gvol_to_lvol_raw(unsigned short gf1_vol);
-unsigned int snd_gf1_calc_ramp_rate(snd_gus_card_t * gus,
-                                   unsigned short start,
-                                   unsigned short end,
-                                   unsigned int us);
 unsigned short snd_gf1_translate_freq(snd_gus_card_t * gus, unsigned int freq2);
-unsigned short snd_gf1_compute_pitchbend(unsigned short pitchbend, unsigned short sens);
-unsigned short snd_gf1_compute_freq(unsigned int freq,
-                                   unsigned int rate,
-                                   unsigned short mix_rate);
 
 /* gus_reset.c */
 
 void snd_gf1_set_default_handlers(snd_gus_card_t * gus, unsigned int what);
 void snd_gf1_smart_stop_voice(snd_gus_card_t * gus, unsigned short voice);
 void snd_gf1_stop_voice(snd_gus_card_t * gus, unsigned short voice);
-void snd_gf1_clear_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max);
 void snd_gf1_stop_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max);
 snd_gus_voice_t *snd_gf1_alloc_voice(snd_gus_card_t * gus, int type, int client, int port);
 void snd_gf1_free_voice(snd_gus_card_t * gus, snd_gus_voice_t *voice);
@@ -641,9 +621,6 @@ int snd_gf1_pcm_new(snd_gus_card_t * gus, int pcm_dev, int control_index, snd_pc
 
 #ifdef CONFIG_SND_DEBUG
 extern void snd_gf1_print_voice_registers(snd_gus_card_t * gus);
-extern void snd_gf1_print_global_registers(snd_gus_card_t * gus);
-extern void snd_gf1_print_setup_registers(snd_gus_card_t * gus);
-extern void snd_gf1_peek_print_block(snd_gus_card_t * gus, unsigned int addr, int count, int w_16bit);
 #endif
 
 /* gus.c */
diff --git a/include/sound/hdspm.h b/include/sound/hdspm.h
new file mode 100644 (file)
index 0000000..c34427c
--- /dev/null
@@ -0,0 +1,131 @@
+#ifndef __SOUND_HDSPM_H                /* -*- linux-c -*- */
+#define __SOUND_HDSPM_H
+/*
+ *   Copyright (C) 2003 Winfried Ritsch (IEM)
+ *   based on hdsp.h from Thomas Charbonnel (thomas@undata.org)
+ *                      
+ *    
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Maximum channels is 64 even on 56Mode you have 64playbacks to matrix */
+#define HDSPM_MAX_CHANNELS      64
+
+/* -------------------- IOCTL Peak/RMS Meters -------------------- */
+
+typedef struct _snd_hdspm_peak_rms hdspm_peak_rms_t;
+
+/* peam rms level structure like we get from hardware 
+  
+   maybe in future we can memory map it so I just copy it
+   to user on ioctl call now an dont change anything
+   rms are made out of low and high values
+   where (long) ????_rms = (????_rms_l >> 8) + ((????_rms_h & 0xFFFFFF00)<<24)
+   (i asume so from the code)
+*/
+
+struct _snd_hdspm_peak_rms {
+
+       unsigned int level_offset[1024];
+
+       unsigned int input_peak[64];
+       unsigned int playback_peak[64];
+       unsigned int output_peak[64];
+       unsigned int xxx_peak[64];      /* not used */
+
+       unsigned int reserved[256];     /* not used */
+
+       unsigned int input_rms_l[64];
+       unsigned int playback_rms_l[64];
+       unsigned int output_rms_l[64];
+       unsigned int xxx_rms_l[64];     /* not used */
+
+       unsigned int input_rms_h[64];
+       unsigned int playback_rms_h[64];
+       unsigned int output_rms_h[64];
+       unsigned int xxx_rms_h[64];     /* not used */
+};
+
+struct sndrv_hdspm_peak_rms_ioctl {
+       hdspm_peak_rms_t *peak;
+};
+
+/* use indirect access due to the limit of ioctl bit size */
+#define SNDRV_HDSPM_IOCTL_GET_PEAK_RMS _IOR('H', 0x40, struct sndrv_hdspm_peak_rms_ioctl)
+
+/* ------------ CONFIG block IOCTL ---------------------- */
+
+typedef struct _snd_hdspm_config_info hdspm_config_info_t;
+
+struct _snd_hdspm_config_info {
+       unsigned char pref_sync_ref;
+       unsigned char wordclock_sync_check;
+       unsigned char madi_sync_check;
+       unsigned int system_sample_rate;
+       unsigned int autosync_sample_rate;
+       unsigned char system_clock_mode;
+       unsigned char clock_source;
+       unsigned char autosync_ref;
+       unsigned char line_out;
+       unsigned int passthru;
+       unsigned int analog_out;
+};
+
+#define SNDRV_HDSPM_IOCTL_GET_CONFIG_INFO _IOR('H', 0x41, hdspm_config_info_t)
+
+
+/* get Soundcard Version */
+
+typedef struct _snd_hdspm_version hdspm_version_t;
+
+struct _snd_hdspm_version {
+       unsigned short firmware_rev;
+};
+
+#define SNDRV_HDSPM_IOCTL_GET_VERSION _IOR('H', 0x43, hdspm_version_t)
+
+
+/* ------------- get Matrix Mixer IOCTL --------------- */
+
+/* MADI mixer: 64inputs+64playback in 64outputs = 8192 => *4Byte = 32768 Bytes */
+
+/* organisation is 64 channelfader in a continous memory block */
+/* equivalent to hardware definition, maybe for future feature of mmap of them */
+/* each of 64 outputs has 64 infader and 64 outfader: 
+   Ins to Outs mixer[out].in[in], Outstreams to Outs mixer[out].pb[pb] */
+
+#define HDSPM_MIXER_CHANNELS HDSPM_MAX_CHANNELS
+
+typedef struct _snd_hdspm_channelfader snd_hdspm_channelfader_t;
+
+struct _snd_hdspm_channelfader {
+       unsigned int in[HDSPM_MIXER_CHANNELS];
+       unsigned int pb[HDSPM_MIXER_CHANNELS];
+};
+
+typedef struct _snd_hdspm_mixer hdspm_mixer_t;
+
+struct _snd_hdspm_mixer {
+       snd_hdspm_channelfader_t ch[HDSPM_MIXER_CHANNELS];
+};
+
+struct sndrv_hdspm_mixer_ioctl {
+       hdspm_mixer_t *mixer;
+};
+
+/* use indirect access due to the limit of ioctl bit size */
+#define SNDRV_HDSPM_IOCTL_GET_MIXER _IOR('H', 0x44, struct sndrv_hdspm_mixer_ioctl)
+
+#endif                         /* __SOUND_HDSPM_H */
index 53fc04d75badf3be8c27075d2d84afe1965bf1f3..d935417575b542bd94cc9ba1cff1dcc9c6fd4445 100644 (file)
@@ -848,23 +848,6 @@ int snd_interval_ratnum(snd_interval_t *i,
 
 void _snd_pcm_hw_params_any(snd_pcm_hw_params_t *params);
 void _snd_pcm_hw_param_setempty(snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var);
-int snd_pcm_hw_param_min(snd_pcm_substream_t *substream, 
-                        snd_pcm_hw_params_t *params,
-                        snd_pcm_hw_param_t var,
-                        unsigned int val, int *dir);
-int snd_pcm_hw_param_max(snd_pcm_substream_t *substream, 
-                        snd_pcm_hw_params_t *params,
-                        snd_pcm_hw_param_t var,
-                        unsigned int val, int *dir);
-int snd_pcm_hw_param_setinteger(snd_pcm_substream_t *substream, 
-                               snd_pcm_hw_params_t *params,
-                               snd_pcm_hw_param_t var);
-int snd_pcm_hw_param_first(snd_pcm_substream_t *substream, 
-                          snd_pcm_hw_params_t *params,
-                          snd_pcm_hw_param_t var, int *dir);
-int snd_pcm_hw_param_last(snd_pcm_substream_t *substream, 
-                         snd_pcm_hw_params_t *params,
-                         snd_pcm_hw_param_t var, int *dir);
 int snd_pcm_hw_param_near(snd_pcm_substream_t *substream, 
                          snd_pcm_hw_params_t *params,
                          snd_pcm_hw_param_t var, 
@@ -876,7 +859,6 @@ int snd_pcm_hw_param_set(snd_pcm_substream_t *pcm,
 int snd_pcm_hw_params_choose(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *params);
 
 int snd_pcm_hw_refine(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *params);
-int snd_pcm_hw_params(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *params);
 
 int snd_pcm_hw_constraints_init(snd_pcm_substream_t *substream);
 int snd_pcm_hw_constraints_complete(snd_pcm_substream_t *substream);
@@ -922,8 +904,22 @@ int snd_pcm_format_unsigned(snd_pcm_format_t format);
 int snd_pcm_format_linear(snd_pcm_format_t format);
 int snd_pcm_format_little_endian(snd_pcm_format_t format);
 int snd_pcm_format_big_endian(snd_pcm_format_t format);
+/**
+ * snd_pcm_format_cpu_endian - Check the PCM format is CPU-endian
+ * @format: the format to check
+ *
+ * Returns 1 if the given PCM format is CPU-endian, 0 if
+ * opposite, or a negative error code if endian not specified.
+ */
+/* int snd_pcm_format_cpu_endian(snd_pcm_format_t format); */
+#ifdef SNDRV_LITTLE_ENDIAN
+#define snd_pcm_format_cpu_endian      snd_pcm_format_little_endian
+#else
+#define snd_pcm_format_cpu_endian      snd_pcm_format_big_endian
+#endif
 int snd_pcm_format_width(snd_pcm_format_t format);                     /* in bits */
 int snd_pcm_format_physical_width(snd_pcm_format_t format);            /* in bits */
+ssize_t snd_pcm_format_size(snd_pcm_format_t format, size_t samples);
 const unsigned char *snd_pcm_format_silence_64(snd_pcm_format_t format);
 int snd_pcm_format_set_silence(snd_pcm_format_t format, void *buf, unsigned int frames);
 snd_pcm_format_t snd_pcm_build_linear_format(int width, int unsignd, int big_endian);
index 4357cac075000d5a42e9f20ddb087a6d95c5d73b..8857e2bd31a5f690998a276dcee475fae78cdffa 100644 (file)
@@ -41,9 +41,7 @@ struct snd_midi_event_t {
 };
 
 int snd_midi_event_new(int bufsize, snd_midi_event_t **rdev);
-int snd_midi_event_resize_buffer(snd_midi_event_t *dev, int bufsize);
 void snd_midi_event_free(snd_midi_event_t *dev);
-void snd_midi_event_init(snd_midi_event_t *dev);
 void snd_midi_event_reset_encode(snd_midi_event_t *dev);
 void snd_midi_event_reset_decode(snd_midi_event_t *dev);
 void snd_midi_event_no_status(snd_midi_event_t *dev, int on);
index cf4e2388103f078aac5f7c95418253a04f6f13cf..1ad27e859af373136d6ae80a5b0f50d93242e25e 100644 (file)
@@ -79,6 +79,5 @@ struct _snd_virmidi_dev {
 #define SNDRV_VIRMIDI_SEQ_DISPATCH     2
 
 int snd_virmidi_new(snd_card_t *card, int device, snd_rawmidi_t **rrmidi);
-int snd_virmidi_receive(snd_rawmidi_t *rmidi, snd_seq_event_t *ev);
 
 #endif /* __SOUND_SEQ_VIRMIDI */
index 57fde990606e9b3006789f0510a15c233aa46337..1898511a0f389d53a61023d68d7ad2c2f0b8df9c 100644 (file)
@@ -152,6 +152,4 @@ extern int snd_timer_pause(snd_timer_instance_t * timeri);
 
 extern void snd_timer_interrupt(snd_timer_t * timer, unsigned long ticks_left);
 
-extern unsigned int snd_timer_system_resolution(void);
-
 #endif /* __SOUND_TIMER_H */
index 98b4230778ed456fa34893ec3aed122db9b2e7d0..46acfa8c998843fff235a10586041d710a1ec01e 100644 (file)
@@ -1,3 +1,3 @@
 /* include/version.h.  Generated by configure.  */
-#define CONFIG_SND_VERSION "1.0.9rc2"
-#define CONFIG_SND_DATE "  (Thu Mar 24 10:33:39 2005 UTC)"
+#define CONFIG_SND_VERSION "1.0.9"
+#define CONFIG_SND_DATE " (Sun May 29 07:31:02 2005 UTC)"
index 05107e0dc1450ec8f45d28f1320620c06cde17ca..567b03b1c349c3bfd4a7ba3ec5110bd3c0e474bb 100644 (file)
@@ -2,7 +2,7 @@
 # IP configuration
 #
 choice 
-       prompt "Choose IP: FIB lookup""
+       prompt "Choose IP: FIB lookup"
        depends on INET
        default IP_FIB_HASH
 
index 047d59ea0573557392878be64ecbad3bb4acc395..ee794ae06040a2cf768092f79300ab4f8f1cf4fe 100644 (file)
@@ -42,6 +42,11 @@ menu "Advanced Linux Sound Architecture"
 config SND
        tristate "Advanced Linux Sound Architecture"
        depends on SOUND
+       help
+         Say 'Y' or 'M' to enable ALSA (Advanced Linux Sound Architecture),
+         the new base sound system.
+
+         For more information, see <http://www.alsa-project.org/>
 
 source "sound/core/Kconfig"
 
index cdacf4d3a38719daf1813f318c0614c96df715f0..34c1740aa6e9a7e1a1e0f32cbecca1cd9d2783ca 100644 (file)
@@ -14,5 +14,11 @@ config SND_SA11XX_UDA1341
          To compile this driver as a module, choose M here: the module
          will be called snd-sa11xx-uda1341.
 
+config SND_ARMAACI
+       tristate "ARM PrimeCell PL041 AC Link support"
+       depends on SND && ARM_AMBA
+       select SND_PCM
+       select SND_AC97_CODEC
+
 endmenu
 
index d7e7dc0c3cdfafd8a346c36be1782ea4ce12c30e..f74ec28e1068179719fb8e39c87de50e67b10101 100644 (file)
@@ -6,3 +6,6 @@ snd-sa11xx-uda1341-objs := sa11xx-uda1341.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_SA11XX_UDA1341) += snd-sa11xx-uda1341.o 
+
+obj-$(CONFIG_SND_ARMAACI)      += snd-aaci.o
+snd-aaci-objs                  := aaci.o devdma.o
diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c
new file mode 100644 (file)
index 0000000..08cc3dd
--- /dev/null
@@ -0,0 +1,968 @@
+/*
+ *  linux/sound/arm/aaci.c - ARM PrimeCell AACI PL041 driver
+ *
+ *  Copyright (C) 2003 Deep Blue Solutions Ltd, 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.
+ *
+ *  Documentation: ARM DDI 0173B
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/hardware/amba.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/ac97_codec.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#include "aaci.h"
+#include "devdma.h"
+
+#define DRIVER_NAME    "aaci-pl041"
+
+/*
+ * PM support is not complete.  Turn it off.
+ */
+#undef CONFIG_PM
+
+static void aaci_ac97_select_codec(struct aaci *aaci, ac97_t *ac97)
+{
+       u32 v, maincr = aaci->maincr | MAINCR_SCRA(ac97->num);
+
+       /*
+        * Ensure that the slot 1/2 RX registers are empty.
+        */
+       v = readl(aaci->base + AACI_SLFR);
+       if (v & SLFR_2RXV)
+               readl(aaci->base + AACI_SL2RX);
+       if (v & SLFR_1RXV)
+               readl(aaci->base + AACI_SL1RX);
+
+       writel(maincr, aaci->base + AACI_MAINCR);
+}
+
+/*
+ * P29:
+ *  The recommended use of programming the external codec through slot 1
+ *  and slot 2 data is to use the channels during setup routines and the
+ *  slot register at any other time.  The data written into slot 1, slot 2
+ *  and slot 12 registers is transmitted only when their corresponding
+ *  SI1TxEn, SI2TxEn and SI12TxEn bits are set in the AACI_MAINCR
+ *  register.
+ */
+static void aaci_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val)
+{
+       struct aaci *aaci = ac97->private_data;
+       u32 v;
+
+       if (ac97->num >= 4)
+               return;
+
+       down(&aaci->ac97_sem);
+
+       aaci_ac97_select_codec(aaci, ac97);
+
+       /*
+        * P54: You must ensure that AACI_SL2TX is always written
+        * to, if required, before data is written to AACI_SL1TX.
+        */
+       writel(val << 4, aaci->base + AACI_SL2TX);
+       writel(reg << 12, aaci->base + AACI_SL1TX);
+
+       /*
+        * Wait for the transmission of both slots to complete.
+        */
+       do {
+               v = readl(aaci->base + AACI_SLFR);
+       } while (v & (SLFR_1TXB|SLFR_2TXB));
+
+       up(&aaci->ac97_sem);
+}
+
+/*
+ * Read an AC'97 register.
+ */
+static unsigned short aaci_ac97_read(ac97_t *ac97, unsigned short reg)
+{
+       struct aaci *aaci = ac97->private_data;
+       u32 v;
+
+       if (ac97->num >= 4)
+               return ~0;
+
+       down(&aaci->ac97_sem);
+
+       aaci_ac97_select_codec(aaci, ac97);
+
+       /*
+        * Write the register address to slot 1.
+        */
+       writel((reg << 12) | (1 << 19), aaci->base + AACI_SL1TX);
+
+       /*
+        * Wait for the transmission to complete.
+        */
+       do {
+               v = readl(aaci->base + AACI_SLFR);
+       } while (v & SLFR_1TXB);
+
+       /*
+        * Give the AC'97 codec more than enough time
+        * to respond. (42us = ~2 frames at 48kHz.)
+        */
+       udelay(42);
+
+       /*
+        * Wait for slot 2 to indicate data.
+        */
+       do {
+               cond_resched();
+               v = readl(aaci->base + AACI_SLFR) & (SLFR_1RXV|SLFR_2RXV);
+       } while (v != (SLFR_1RXV|SLFR_2RXV));
+
+       v = readl(aaci->base + AACI_SL1RX) >> 12;
+       if (v == reg) {
+               v = readl(aaci->base + AACI_SL2RX) >> 4;
+       } else {
+               dev_err(&aaci->dev->dev,
+                       "wrong ac97 register read back (%x != %x)\n",
+                       v, reg);
+               v = ~0;
+       }
+
+       up(&aaci->ac97_sem);
+       return v;
+}
+
+static inline void aaci_chan_wait_ready(struct aaci_runtime *aacirun)
+{
+       u32 val;
+       int timeout = 5000;
+
+       do {
+               val = readl(aacirun->base + AACI_SR);
+       } while (val & (SR_TXB|SR_RXB) && timeout--);
+}
+
+
+
+/*
+ * Interrupt support.
+ */
+static void aaci_fifo_irq(struct aaci *aaci, u32 mask)
+{
+       if (mask & ISR_URINTR) {
+               writel(ICLR_TXUEC1, aaci->base + AACI_INTCLR);
+       }
+
+       if (mask & ISR_TXINTR) {
+               struct aaci_runtime *aacirun = &aaci->playback;
+               void *ptr;
+
+               if (!aacirun->substream || !aacirun->start) {
+                       dev_warn(&aaci->dev->dev, "TX interrupt???");
+                       writel(0, aacirun->base + AACI_IE);
+                       return;
+               }
+
+               ptr = aacirun->ptr;
+               do {
+                       unsigned int len = aacirun->fifosz;
+                       u32 val;
+
+                       if (aacirun->bytes <= 0) {
+                               aacirun->bytes += aacirun->period;
+                               aacirun->ptr = ptr;
+                               spin_unlock(&aaci->lock);
+                               snd_pcm_period_elapsed(aacirun->substream);
+                               spin_lock(&aaci->lock);
+                       }
+                       if (!(aacirun->cr & TXCR_TXEN))
+                               break;
+
+                       val = readl(aacirun->base + AACI_SR);
+                       if (!(val & SR_TXHE))
+                               break;
+                       if (!(val & SR_TXFE))
+                               len >>= 1;
+
+                       aacirun->bytes -= len;
+
+                       /* writing 16 bytes at a time */
+                       for ( ; len > 0; len -= 16) {
+                               asm(
+                                       "ldmia  %0!, {r0, r1, r2, r3}\n\t"
+                                       "stmia  %1, {r0, r1, r2, r3}"
+                                       : "+r" (ptr)
+                                       : "r" (aacirun->fifo)
+                                       : "r0", "r1", "r2", "r3", "cc");
+
+                               if (ptr >= aacirun->end)
+                                       ptr = aacirun->start;
+                       }
+               } while (1);
+
+               aacirun->ptr = ptr;
+       }
+}
+
+static irqreturn_t aaci_irq(int irq, void *devid, struct pt_regs *regs)
+{
+       struct aaci *aaci = devid;
+       u32 mask;
+       int i;
+
+       spin_lock(&aaci->lock);
+       mask = readl(aaci->base + AACI_ALLINTS);
+       if (mask) {
+               u32 m = mask;
+               for (i = 0; i < 4; i++, m >>= 7) {
+                       if (m & 0x7f) {
+                               aaci_fifo_irq(aaci, m);
+                       }
+               }
+       }
+       spin_unlock(&aaci->lock);
+
+       return mask ? IRQ_HANDLED : IRQ_NONE;
+}
+
+
+
+/*
+ * ALSA support.
+ */
+
+struct aaci_stream {
+       unsigned char codec_idx;
+       unsigned char rate_idx;
+};
+
+static struct aaci_stream aaci_streams[] = {
+       [ACSTREAM_FRONT] = {
+               .codec_idx      = 0,
+               .rate_idx       = AC97_RATES_FRONT_DAC,
+       },
+       [ACSTREAM_SURROUND] = {
+               .codec_idx      = 0,
+               .rate_idx       = AC97_RATES_SURR_DAC,
+       },
+       [ACSTREAM_LFE] = {
+               .codec_idx      = 0,
+               .rate_idx       = AC97_RATES_LFE_DAC,
+       },
+};
+
+static inline unsigned int aaci_rate_mask(struct aaci *aaci, int streamid)
+{
+       struct aaci_stream *s = aaci_streams + streamid;
+       return aaci->ac97_bus->codec[s->codec_idx]->rates[s->rate_idx];
+}
+
+static unsigned int rate_list[] = {
+       5512, 8000, 11025, 16000, 22050, 32000, 44100,
+       48000, 64000, 88200, 96000, 176400, 192000
+};
+
+/*
+ * Double-rate rule: we can support double rate iff channels == 2
+ *  (unimplemented)
+ */
+static int
+aaci_rule_rate_by_channels(snd_pcm_hw_params_t *p, snd_pcm_hw_rule_t *rule)
+{
+       struct aaci *aaci = rule->private;
+       unsigned int rate_mask = SNDRV_PCM_RATE_8000_48000|SNDRV_PCM_RATE_5512;
+       snd_interval_t *c = hw_param_interval(p, SNDRV_PCM_HW_PARAM_CHANNELS);
+
+       switch (c->max) {
+       case 6:
+               rate_mask &= aaci_rate_mask(aaci, ACSTREAM_LFE);
+       case 4:
+               rate_mask &= aaci_rate_mask(aaci, ACSTREAM_SURROUND);
+       case 2:
+               rate_mask &= aaci_rate_mask(aaci, ACSTREAM_FRONT);
+       }
+
+       return snd_interval_list(hw_param_interval(p, rule->var),
+                                ARRAY_SIZE(rate_list), rate_list,
+                                rate_mask);
+}
+
+static snd_pcm_hardware_t aaci_hw_info = {
+       .info                   = SNDRV_PCM_INFO_MMAP |
+                                 SNDRV_PCM_INFO_MMAP_VALID |
+                                 SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                 SNDRV_PCM_INFO_RESUME,
+
+       /*
+        * ALSA doesn't support 18-bit or 20-bit packed into 32-bit
+        * words.  It also doesn't support 12-bit at all.
+        */
+       .formats                = SNDRV_PCM_FMTBIT_S16_LE,
+
+       /* should this be continuous or knot? */
+       .rates                  = SNDRV_PCM_RATE_CONTINUOUS,
+       .rate_max               = 48000,
+       .rate_min               = 4000,
+       .channels_min           = 2,
+       .channels_max           = 6,
+       .buffer_bytes_max       = 64 * 1024,
+       .period_bytes_min       = 256,
+       .period_bytes_max       = PAGE_SIZE,
+       .periods_min            = 4,
+       .periods_max            = PAGE_SIZE / 16,
+};
+
+static int aaci_pcm_open(struct aaci *aaci, snd_pcm_substream_t *substream,
+                        struct aaci_runtime *aacirun)
+{
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       int ret;
+
+       aacirun->substream = substream;
+       runtime->private_data = aacirun;
+       runtime->hw = aaci_hw_info;
+
+       /*
+        * FIXME: ALSA specifies fifo_size in bytes.  If we're in normal
+        * mode, each 32-bit word contains one sample.  If we're in
+        * compact mode, each 32-bit word contains two samples, effectively
+        * halving the FIFO size.  However, we don't know for sure which
+        * we'll be using at this point.  We set this to the lower limit.
+        */
+       runtime->hw.fifo_size = aaci->fifosize * 2;
+
+       /*
+        * Add rule describing hardware rate dependency
+        * on the number of channels.
+        */
+       ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+                                 aaci_rule_rate_by_channels, aaci,
+                                 SNDRV_PCM_HW_PARAM_CHANNELS,
+                                 SNDRV_PCM_HW_PARAM_RATE, -1);
+       if (ret)
+               goto out;
+
+       ret = request_irq(aaci->dev->irq[0], aaci_irq, SA_SHIRQ|SA_INTERRUPT,
+                         DRIVER_NAME, aaci);
+       if (ret)
+               goto out;
+
+       return 0;
+
+ out:
+       return ret;
+}
+
+
+/*
+ * Common ALSA stuff
+ */
+static int aaci_pcm_close(snd_pcm_substream_t *substream)
+{
+       struct aaci *aaci = substream->private_data;
+       struct aaci_runtime *aacirun = substream->runtime->private_data;
+
+       WARN_ON(aacirun->cr & TXCR_TXEN);
+
+       aacirun->substream = NULL;
+       free_irq(aaci->dev->irq[0], aaci);
+
+       return 0;
+}
+
+static int aaci_pcm_hw_free(snd_pcm_substream_t *substream)
+{
+       struct aaci_runtime *aacirun = substream->runtime->private_data;
+
+       /*
+        * This must not be called with the device enabled.
+        */
+       WARN_ON(aacirun->cr & TXCR_TXEN);
+
+       if (aacirun->pcm_open)
+               snd_ac97_pcm_close(aacirun->pcm);
+       aacirun->pcm_open = 0;
+
+       /*
+        * Clear out the DMA and any allocated buffers.
+        */
+       devdma_hw_free(NULL, substream);
+
+       return 0;
+}
+
+static int aaci_pcm_hw_params(snd_pcm_substream_t *substream,
+                             struct aaci_runtime *aacirun,
+                             snd_pcm_hw_params_t *params)
+{
+       int err;
+
+       aaci_pcm_hw_free(substream);
+
+       err = devdma_hw_alloc(NULL, substream,
+                             params_buffer_bytes(params));
+       if (err < 0)
+               goto out;
+
+       err = snd_ac97_pcm_open(aacirun->pcm, params_rate(params),
+                               params_channels(params),
+                               aacirun->pcm->r[0].slots);
+       if (err)
+               goto out;
+
+       aacirun->pcm_open = 1;
+
+ out:
+       return err;
+}
+
+static int aaci_pcm_prepare(snd_pcm_substream_t *substream)
+{
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       struct aaci_runtime *aacirun = runtime->private_data;
+
+       aacirun->start  = (void *)runtime->dma_area;
+       aacirun->end    = aacirun->start + runtime->dma_bytes;
+       aacirun->ptr    = aacirun->start;
+       aacirun->period =
+       aacirun->bytes  = frames_to_bytes(runtime, runtime->period_size);
+
+       return 0;
+}
+
+static snd_pcm_uframes_t aaci_pcm_pointer(snd_pcm_substream_t *substream)
+{
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       struct aaci_runtime *aacirun = runtime->private_data;
+       ssize_t bytes = aacirun->ptr - aacirun->start;
+
+       return bytes_to_frames(runtime, bytes);
+}
+
+static int aaci_pcm_mmap(snd_pcm_substream_t *substream, struct vm_area_struct *vma)
+{
+       return devdma_mmap(NULL, substream, vma);
+}
+
+
+/*
+ * Playback specific ALSA stuff
+ */
+static const u32 channels_to_txmask[] = {
+       [2] = TXCR_TX3 | TXCR_TX4,
+       [4] = TXCR_TX3 | TXCR_TX4 | TXCR_TX7 | TXCR_TX8,
+       [6] = TXCR_TX3 | TXCR_TX4 | TXCR_TX7 | TXCR_TX8 | TXCR_TX6 | TXCR_TX9,
+};
+
+/*
+ * We can support two and four channel audio.  Unfortunately
+ * six channel audio requires a non-standard channel ordering:
+ *   2 -> FL(3), FR(4)
+ *   4 -> FL(3), FR(4), SL(7), SR(8)
+ *   6 -> FL(3), FR(4), SL(7), SR(8), C(6), LFE(9) (required)
+ *        FL(3), FR(4), C(6), SL(7), SR(8), LFE(9) (actual)
+ * This requires an ALSA configuration file to correct.
+ */
+static unsigned int channel_list[] = { 2, 4, 6 };
+
+static int
+aaci_rule_channels(snd_pcm_hw_params_t *p, snd_pcm_hw_rule_t *rule)
+{
+       struct aaci *aaci = rule->private;
+       unsigned int chan_mask = 1 << 0, slots;
+
+       /*
+        * pcms[0] is the our 5.1 PCM instance.
+        */
+       slots = aaci->ac97_bus->pcms[0].r[0].slots;
+       if (slots & (1 << AC97_SLOT_PCM_SLEFT)) {
+               chan_mask |= 1 << 1;
+               if (slots & (1 << AC97_SLOT_LFE))
+                       chan_mask |= 1 << 2;
+       }
+
+       return snd_interval_list(hw_param_interval(p, rule->var),
+                                ARRAY_SIZE(channel_list), channel_list,
+                                chan_mask);
+}
+
+static int aaci_pcm_playback_open(snd_pcm_substream_t *substream)
+{
+       struct aaci *aaci = substream->private_data;
+       int ret;
+
+       /*
+        * Add rule describing channel dependency.
+        */
+       ret = snd_pcm_hw_rule_add(substream->runtime, 0,
+                                 SNDRV_PCM_HW_PARAM_CHANNELS,
+                                 aaci_rule_channels, aaci,
+                                 SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+       if (ret)
+               return ret;
+
+       return aaci_pcm_open(aaci, substream, &aaci->playback);
+}
+
+static int aaci_pcm_playback_hw_params(snd_pcm_substream_t *substream,
+                                      snd_pcm_hw_params_t *params)
+{
+       struct aaci *aaci = substream->private_data;
+       struct aaci_runtime *aacirun = substream->runtime->private_data;
+       unsigned int channels = params_channels(params);
+       int ret;
+
+       WARN_ON(channels >= ARRAY_SIZE(channels_to_txmask) ||
+               !channels_to_txmask[channels]);
+
+       ret = aaci_pcm_hw_params(substream, aacirun, params);
+
+       /*
+        * Enable FIFO, compact mode, 16 bits per sample.
+        * FIXME: double rate slots?
+        */
+       if (ret >= 0) {
+               aacirun->cr = TXCR_FEN | TXCR_COMPACT | TXCR_TSZ16;
+               aacirun->cr |= channels_to_txmask[channels];
+
+               aacirun->fifosz = aaci->fifosize * 4;
+               if (aacirun->cr & TXCR_COMPACT)
+                       aacirun->fifosz >>= 1;
+       }
+       return ret;
+}
+
+static void aaci_pcm_playback_stop(struct aaci_runtime *aacirun)
+{
+       u32 ie;
+
+       ie = readl(aacirun->base + AACI_IE);
+       ie &= ~(IE_URIE|IE_TXIE);
+       writel(ie, aacirun->base + AACI_IE);
+       aacirun->cr &= ~TXCR_TXEN;
+       aaci_chan_wait_ready(aacirun);
+       writel(aacirun->cr, aacirun->base + AACI_TXCR);
+}
+
+static void aaci_pcm_playback_start(struct aaci_runtime *aacirun)
+{
+       u32 ie;
+
+       aaci_chan_wait_ready(aacirun);
+       aacirun->cr |= TXCR_TXEN;
+
+       ie = readl(aacirun->base + AACI_IE);
+       ie |= IE_URIE | IE_TXIE;
+       writel(ie, aacirun->base + AACI_IE);
+       writel(aacirun->cr, aacirun->base + AACI_TXCR);
+}
+
+static int aaci_pcm_playback_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+       struct aaci *aaci = substream->private_data;
+       struct aaci_runtime *aacirun = substream->runtime->private_data;
+       unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(&aaci->lock, flags);
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               aaci_pcm_playback_start(aacirun);
+               break;
+
+       case SNDRV_PCM_TRIGGER_RESUME:
+               aaci_pcm_playback_start(aacirun);
+               break;
+
+       case SNDRV_PCM_TRIGGER_STOP:
+               aaci_pcm_playback_stop(aacirun);
+               break;
+
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+               aaci_pcm_playback_stop(aacirun);
+               break;
+
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               break;
+
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               break;
+
+       default:
+               ret = -EINVAL;
+       }
+       spin_unlock_irqrestore(&aaci->lock, flags);
+
+       return ret;
+}
+
+static snd_pcm_ops_t aaci_playback_ops = {
+       .open           = aaci_pcm_playback_open,
+       .close          = aaci_pcm_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = aaci_pcm_playback_hw_params,
+       .hw_free        = aaci_pcm_hw_free,
+       .prepare        = aaci_pcm_prepare,
+       .trigger        = aaci_pcm_playback_trigger,
+       .pointer        = aaci_pcm_pointer,
+       .mmap           = aaci_pcm_mmap,
+};
+
+
+
+/*
+ * Power Management.
+ */
+#ifdef CONFIG_PM
+static int aaci_do_suspend(snd_card_t *card, unsigned int state)
+{
+       struct aaci *aaci = card->private_data;
+       if (aaci->card->power_state != SNDRV_CTL_POWER_D3cold) {
+               snd_pcm_suspend_all(aaci->pcm);
+               snd_power_change_state(aaci->card, SNDRV_CTL_POWER_D3cold);
+       }
+       return 0;
+}
+
+static int aaci_do_resume(snd_card_t *card, unsigned int state)
+{
+       struct aaci *aaci = card->private_data;
+       if (aaci->card->power_state != SNDRV_CTL_POWER_D0) {
+               snd_power_change_state(aaci->card, SNDRV_CTL_POWER_D0);
+       }
+       return 0;
+}
+
+static int aaci_suspend(struct amba_device *dev, u32 state)
+{
+       snd_card_t *card = amba_get_drvdata(dev);
+       return card ? aaci_do_suspend(card) : 0;
+}
+
+static int aaci_resume(struct amba_device *dev)
+{
+       snd_card_t *card = amba_get_drvdata(dev);
+       return card ? aaci_do_resume(card) : 0;
+}
+#else
+#define aaci_do_suspend                NULL
+#define aaci_do_resume         NULL
+#define aaci_suspend           NULL
+#define aaci_resume            NULL
+#endif
+
+
+static struct ac97_pcm ac97_defs[] __devinitdata = {
+       [0] = {         /* Front PCM */
+               .exclusive = 1,
+               .r = {
+                       [0] = {
+                               .slots  = (1 << AC97_SLOT_PCM_LEFT) |
+                                         (1 << AC97_SLOT_PCM_RIGHT) |
+                                         (1 << AC97_SLOT_PCM_CENTER) |
+                                         (1 << AC97_SLOT_PCM_SLEFT) |
+                                         (1 << AC97_SLOT_PCM_SRIGHT) |
+                                         (1 << AC97_SLOT_LFE),
+                       },
+               },
+       },
+       [1] = { /* PCM in */
+               .stream = 1,
+               .exclusive = 1,
+               .r = {
+                       [0] = {
+                               .slots  = (1 << AC97_SLOT_PCM_LEFT) |
+                                         (1 << AC97_SLOT_PCM_RIGHT),
+                       },
+               },
+       },
+       [2] = { /* Mic in */
+               .stream = 1,
+               .exclusive = 1,
+               .r = {
+                       [0] = {
+                               .slots  = (1 << AC97_SLOT_MIC),
+                       },
+               },
+       }
+};
+
+static ac97_bus_ops_t aaci_bus_ops = {
+       .write  = aaci_ac97_write,
+       .read   = aaci_ac97_read,
+};
+
+static int __devinit aaci_probe_ac97(struct aaci *aaci)
+{
+       ac97_template_t ac97_template;
+       ac97_bus_t *ac97_bus;
+       ac97_t *ac97;
+       int ret;
+
+       /*
+        * Assert AACIRESET for 2us
+        */
+       writel(0, aaci->base + AACI_RESET);
+       udelay(2);
+       writel(RESET_NRST, aaci->base + AACI_RESET);
+
+       /*
+        * Give the AC'97 codec more than enough time
+        * to wake up. (42us = ~2 frames at 48kHz.)
+        */
+       udelay(42);
+
+       ret = snd_ac97_bus(aaci->card, 0, &aaci_bus_ops, aaci, &ac97_bus);
+       if (ret)
+               goto out;
+
+       ac97_bus->clock = 48000;
+       aaci->ac97_bus = ac97_bus;
+
+       memset(&ac97_template, 0, sizeof(ac97_template_t));
+       ac97_template.private_data = aaci;
+       ac97_template.num = 0;
+       ac97_template.scaps = AC97_SCAP_SKIP_MODEM;
+
+       ret = snd_ac97_mixer(ac97_bus, &ac97_template, &ac97);
+       if (ret)
+               goto out;
+
+       /*
+        * Disable AC97 PC Beep input on audio codecs.
+        */
+       if (ac97_is_audio(ac97))
+               snd_ac97_write_cache(ac97, AC97_PC_BEEP, 0x801e);
+
+       ret = snd_ac97_pcm_assign(ac97_bus, ARRAY_SIZE(ac97_defs), ac97_defs);
+       if (ret)
+               goto out;
+
+       aaci->playback.pcm = &ac97_bus->pcms[0];
+
+ out:
+       return ret;
+}
+
+static void aaci_free_card(snd_card_t *card)
+{
+       struct aaci *aaci = card->private_data;
+       if (aaci->base)
+               iounmap(aaci->base);
+}
+
+static struct aaci * __devinit aaci_init_card(struct amba_device *dev)
+{
+       struct aaci *aaci;
+       snd_card_t *card;
+
+       card = snd_card_new(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+                           THIS_MODULE, sizeof(struct aaci));
+       if (card == NULL)
+               return ERR_PTR(-ENOMEM);
+
+       card->private_free = aaci_free_card;
+       snd_card_set_pm_callback(card, aaci_do_suspend, aaci_do_resume, NULL);
+
+       strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
+       strlcpy(card->shortname, "ARM AC'97 Interface", sizeof(card->shortname));
+       snprintf(card->longname, sizeof(card->longname),
+                "%s at 0x%08lx, irq %d",
+                card->shortname, dev->res.start, dev->irq[0]);
+
+       aaci = card->private_data;
+       init_MUTEX(&aaci->ac97_sem);
+       spin_lock_init(&aaci->lock);
+       aaci->card = card;
+       aaci->dev = dev;
+
+       /* Set MAINCR to allow slot 1 and 2 data IO */
+       aaci->maincr = MAINCR_IE | MAINCR_SL1RXEN | MAINCR_SL1TXEN |
+                      MAINCR_SL2RXEN | MAINCR_SL2TXEN;
+
+       return aaci;
+}
+
+static int __devinit aaci_init_pcm(struct aaci *aaci)
+{
+       snd_pcm_t *pcm;
+       int ret;
+
+       ret = snd_pcm_new(aaci->card, "AACI AC'97", 0, 1, 0, &pcm);
+       if (ret == 0) {
+               aaci->pcm = pcm;
+               pcm->private_data = aaci;
+               pcm->info_flags = 0;
+
+               strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
+
+               snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &aaci_playback_ops);
+       }
+
+       return ret;
+}
+
+static unsigned int __devinit aaci_size_fifo(struct aaci *aaci)
+{
+       void *base = aaci->base + AACI_CSCH1;
+       int i;
+
+       writel(TXCR_FEN | TXCR_TSZ16 | TXCR_TXEN, base + AACI_TXCR);
+
+       for (i = 0; !(readl(base + AACI_SR) & SR_TXFF) && i < 4096; i++)
+               writel(0, aaci->base + AACI_DR1);
+
+       writel(0, base + AACI_TXCR);
+
+       /*
+        * Re-initialise the AACI after the FIFO depth test, to
+        * ensure that the FIFOs are empty.  Unfortunately, merely
+        * disabling the channel doesn't clear the FIFO.
+        */
+       writel(aaci->maincr & ~MAINCR_IE, aaci->base + AACI_MAINCR);
+       writel(aaci->maincr, aaci->base + AACI_MAINCR);
+
+       /*
+        * If we hit 4096, we failed.  Go back to the specified
+        * fifo depth.
+        */
+       if (i == 4096)
+               i = 8;
+
+       return i;
+}
+
+static int __devinit aaci_probe(struct amba_device *dev, void *id)
+{
+       struct aaci *aaci;
+       int ret, i;
+
+       ret = amba_request_regions(dev, NULL);
+       if (ret)
+               return ret;
+
+       aaci = aaci_init_card(dev);
+       if (IS_ERR(aaci)) {
+               ret = PTR_ERR(aaci);
+               goto out;
+       }
+
+       aaci->base = ioremap(dev->res.start, SZ_4K);
+       if (!aaci->base) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       /*
+        * Playback uses AACI channel 0
+        */
+       aaci->playback.base = aaci->base + AACI_CSCH1;
+       aaci->playback.fifo = aaci->base + AACI_DR1;
+
+       for (i = 0; i < 4; i++) {
+               void *base = aaci->base + i * 0x14;
+
+               writel(0, base + AACI_IE);
+               writel(0, base + AACI_TXCR);
+               writel(0, base + AACI_RXCR);
+       }
+
+       writel(0x1fff, aaci->base + AACI_INTCLR);
+       writel(aaci->maincr, aaci->base + AACI_MAINCR);
+
+       /*
+        * Size the FIFOs.
+        */
+       aaci->fifosize = aaci_size_fifo(aaci);
+
+       ret = aaci_probe_ac97(aaci);
+       if (ret)
+               goto out;
+
+       ret = aaci_init_pcm(aaci);
+       if (ret)
+               goto out;
+
+       ret = snd_card_register(aaci->card);
+       if (ret == 0) {
+               dev_info(&dev->dev, "%s, fifo %d\n", aaci->card->longname,
+                       aaci->fifosize);
+               amba_set_drvdata(dev, aaci->card);
+               return ret;
+       }
+
+ out:
+       if (aaci)
+               snd_card_free(aaci->card);
+       amba_release_regions(dev);
+       return ret;
+}
+
+static int __devexit aaci_remove(struct amba_device *dev)
+{
+       snd_card_t *card = amba_get_drvdata(dev);
+
+       amba_set_drvdata(dev, NULL);
+
+       if (card) {
+               struct aaci *aaci = card->private_data;
+               writel(0, aaci->base + AACI_MAINCR);
+
+               snd_card_free(card);
+               amba_release_regions(dev);
+       }
+
+       return 0;
+}
+
+static struct amba_id aaci_ids[] = {
+       {
+               .id     = 0x00041041,
+               .mask   = 0x000fffff,
+       },
+       { 0, 0 },
+};
+
+static struct amba_driver aaci_driver = {
+       .drv            = {
+               .name   = DRIVER_NAME,
+       },
+       .probe          = aaci_probe,
+       .remove         = __devexit_p(aaci_remove),
+       .suspend        = aaci_suspend,
+       .resume         = aaci_resume,
+       .id_table       = aaci_ids,
+};
+
+static int __init aaci_init(void)
+{
+       return amba_driver_register(&aaci_driver);
+}
+
+static void __exit aaci_exit(void)
+{
+       amba_driver_unregister(&aaci_driver);
+}
+
+module_init(aaci_init);
+module_exit(aaci_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ARM PrimeCell PL041 Advanced Audio CODEC Interface driver");
diff --git a/sound/arm/aaci.h b/sound/arm/aaci.h
new file mode 100644 (file)
index 0000000..d752e64
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ *  linux/sound/arm/aaci.c - ARM PrimeCell AACI PL041 driver
+ *
+ *  Copyright (C) 2003 Deep Blue Solutions, Ltd, 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.
+ */
+#ifndef AACI_H
+#define AACI_H
+
+/*
+ * Control and status register offsets
+ *  P39.
+ */
+#define AACI_CSCH1     0x000
+#define AACI_CSCH2     0x014
+#define AACI_CSCH3     0x028
+#define AACI_CSCH4     0x03c
+
+#define AACI_RXCR      0x000   /* 29 bits Control Rx FIFO */
+#define AACI_TXCR      0x004   /* 17 bits Control Tx FIFO */
+#define AACI_SR                0x008   /* 12 bits Status */
+#define AACI_ISR       0x00c   /* 7 bits  Int Status */
+#define AACI_IE        0x010   /* 7 bits  Int Enable */
+
+/*
+ * Other registers
+ */
+#define AACI_SL1RX     0x050
+#define AACI_SL1TX     0x054
+#define AACI_SL2RX     0x058
+#define AACI_SL2TX     0x05c
+#define AACI_SL12RX    0x060
+#define AACI_SL12TX    0x064
+#define AACI_SLFR      0x068   /* slot flags */
+#define AACI_SLISTAT   0x06c   /* slot interrupt status */
+#define AACI_SLIEN     0x070   /* slot interrupt enable */
+#define AACI_INTCLR    0x074   /* interrupt clear */
+#define AACI_MAINCR    0x078   /* main control */
+#define AACI_RESET     0x07c   /* reset control */
+#define AACI_SYNC      0x080   /* sync control */
+#define AACI_ALLINTS   0x084   /* all fifo interrupt status */
+#define AACI_MAINFR    0x088   /* main flag register */
+#define AACI_DR1       0x090   /* data read/written fifo 1 */
+#define AACI_DR2       0x0b0   /* data read/written fifo 2 */
+#define AACI_DR3       0x0d0   /* data read/written fifo 3 */
+#define AACI_DR4       0x0f0   /* data read/written fifo 4 */
+
+/*
+ * transmit fifo control register. P48
+ */
+#define TXCR_FEN       (1 << 16)       /* fifo enable */
+#define TXCR_COMPACT   (1 << 15)       /* compact mode */
+#define TXCR_TSZ16     (0 << 13)       /* 16 bits */
+#define TXCR_TSZ18     (1 << 13)       /* 18 bits */
+#define TXCR_TSZ20     (2 << 13)       /* 20 bits */
+#define TXCR_TSZ12     (3 << 13)       /* 12 bits */
+#define TXCR_TX12      (1 << 12)       /* transmits slot 12 */
+#define TXCR_TX11      (1 << 11)       /* transmits slot 12 */
+#define TXCR_TX10      (1 << 10)       /* transmits slot 12 */
+#define TXCR_TX9       (1 << 9)        /* transmits slot 12 */
+#define TXCR_TX8       (1 << 8)        /* transmits slot 12 */
+#define TXCR_TX7       (1 << 7)        /* transmits slot 12 */
+#define TXCR_TX6       (1 << 6)        /* transmits slot 12 */
+#define TXCR_TX5       (1 << 5)        /* transmits slot 12 */
+#define TXCR_TX4       (1 << 4)        /* transmits slot 12 */
+#define TXCR_TX3       (1 << 3)        /* transmits slot 12 */
+#define TXCR_TX2       (1 << 2)        /* transmits slot 12 */
+#define TXCR_TX1       (1 << 1)        /* transmits slot 12 */
+#define TXCR_TXEN      (1 << 0)        /* transmit enable */
+
+/*
+ * status register bits. P49
+ */
+#define SR_RXTOFE      (1 << 11)       /* rx timeout fifo empty */
+#define SR_TXTO                (1 << 10)       /* rx timeout fifo nonempty */
+#define SR_TXU         (1 << 9)        /* tx underrun */
+#define SR_RXO         (1 << 8)        /* rx overrun */
+#define SR_TXB         (1 << 7)        /* tx busy */
+#define SR_RXB         (1 << 6)        /* rx busy */
+#define SR_TXFF                (1 << 5)        /* tx fifo full */
+#define SR_RXFF                (1 << 4)        /* rx fifo full */
+#define SR_TXHE                (1 << 3)        /* tx fifo half empty */
+#define SR_RXHF                (1 << 2)        /* rx fifo half full */
+#define SR_TXFE                (1 << 1)        /* tx fifo empty */
+#define SR_RXFE                (1 << 0)        /* rx fifo empty */
+
+/*
+ * interrupt status register bits.
+ */
+#define ISR_RXTOFEINTR (1 << 6)        /* rx fifo empty */
+#define ISR_URINTR     (1 << 5)        /* tx underflow */
+#define ISR_ORINTR     (1 << 4)        /* rx overflow */
+#define ISR_RXINTR     (1 << 3)        /* rx fifo */
+#define ISR_TXINTR     (1 << 2)        /* tx fifo intr */
+#define ISR_RXTOINTR   (1 << 1)        /* tx timeout */
+#define ISR_TXCINTR    (1 << 0)        /* tx complete */
+
+/*
+ * interrupt enable register bits.
+ */
+#define IE_RXTOIE      (1 << 6)
+#define IE_URIE                (1 << 5)
+#define IE_ORIE                (1 << 4)
+#define IE_RXIE                (1 << 3)
+#define IE_TXIE                (1 << 2)
+#define IE_RXTIE       (1 << 1)
+#define IE_TXCIE       (1 << 0)
+
+/*
+ * interrupt status. P51
+ */
+#define ISR_RXTOFE     (1 << 6)        /* rx timeout fifo empty */
+#define ISR_UR         (1 << 5)        /* tx fifo underrun */
+#define ISR_OR         (1 << 4)        /* rx fifo overrun */
+#define ISR_RX         (1 << 3)        /* rx interrupt status */
+#define ISR_TX         (1 << 2)        /* tx interrupt status */
+#define ISR_RXTO       (1 << 1)        /* rx timeout */
+#define ISR_TXC                (1 << 0)        /* tx complete */
+
+/*
+ * interrupt enable. P52
+ */
+#define IE_RXTOFE      (1 << 6)        /* rx timeout fifo empty */
+#define IE_UR          (1 << 5)        /* tx fifo underrun */
+#define IE_OR          (1 << 4)        /* rx fifo overrun */
+#define IE_RX          (1 << 3)        /* rx interrupt status */
+#define IE_TX          (1 << 2)        /* tx interrupt status */
+#define IE_RXTO                (1 << 1)        /* rx timeout */
+#define IE_TXC         (1 << 0)        /* tx complete */
+
+/*
+ * slot flag register bits. P56
+ */
+#define SLFR_RWIS      (1 << 13)       /* raw wake-up interrupt status */
+#define SLFR_RGPIOINTR (1 << 12)       /* raw gpio interrupt */
+#define SLFR_12TXE     (1 << 11)       /* slot 12 tx empty */
+#define SLFR_12RXV     (1 << 10)       /* slot 12 rx valid */
+#define SLFR_2TXE      (1 << 9)        /* slot 2 tx empty */
+#define SLFR_2RXV      (1 << 8)        /* slot 2 rx valid */
+#define SLFR_1TXE      (1 << 7)        /* slot 1 tx empty */
+#define SLFR_1RXV      (1 << 6)        /* slot 1 rx valid */
+#define SLFR_12TXB     (1 << 5)        /* slot 12 tx busy */
+#define SLFR_12RXB     (1 << 4)        /* slot 12 rx busy */
+#define SLFR_2TXB      (1 << 3)        /* slot 2 tx busy */
+#define SLFR_2RXB      (1 << 2)        /* slot 2 rx busy */
+#define SLFR_1TXB      (1 << 1)        /* slot 1 tx busy */
+#define SLFR_1RXB      (1 << 0)        /* slot 1 rx busy */
+
+/*
+ * Interrupt clear register.
+ */
+#define ICLR_RXTOFEC4  (1 << 12)
+#define ICLR_RXTOFEC3  (1 << 11)
+#define ICLR_RXTOFEC2  (1 << 10)
+#define ICLR_RXTOFEC1  (1 << 9)
+#define ICLR_TXUEC4    (1 << 8)
+#define ICLR_TXUEC3    (1 << 7)
+#define ICLR_TXUEC2    (1 << 6)
+#define ICLR_TXUEC1    (1 << 5)
+#define ICLR_RXOEC4    (1 << 4)
+#define ICLR_RXOEC3    (1 << 3)
+#define ICLR_RXOEC2    (1 << 2)
+#define ICLR_RXOEC1    (1 << 1)
+#define ICLR_WISC      (1 << 0)
+
+/*
+ * Main control register bits. P62
+ */
+#define MAINCR_SCRA(x) ((x) << 10)     /* secondary codec reg access */
+#define MAINCR_DMAEN   (1 << 9)        /* dma enable */
+#define MAINCR_SL12TXEN        (1 << 8)        /* slot 12 transmit enable */
+#define MAINCR_SL12RXEN        (1 << 7)        /* slot 12 receive enable */
+#define MAINCR_SL2TXEN (1 << 6)        /* slot 2 transmit enable */
+#define MAINCR_SL2RXEN (1 << 5)        /* slot 2 receive enable */
+#define MAINCR_SL1TXEN (1 << 4)        /* slot 1 transmit enable */
+#define MAINCR_SL1RXEN (1 << 3)        /* slot 1 receive enable */
+#define MAINCR_LPM     (1 << 2)        /* low power mode */
+#define MAINCR_LOOPBK  (1 << 1)        /* loopback */
+#define MAINCR_IE      (1 << 0)        /* aaci interface enable */
+
+/*
+ * Reset register bits. P65
+ */
+#define RESET_NRST     (1 << 0)
+
+/*
+ * Sync register bits. P65
+ */
+#define SYNC_FORCE     (1 << 0)
+
+/*
+ * Main flag register bits. P66
+ */
+#define MAINFR_TXB     (1 << 1)        /* transmit busy */
+#define MAINFR_RXB     (1 << 0)        /* receive busy */
+
+
+
+struct aaci_runtime {
+       void                    *base;
+       void                    *fifo;
+
+       struct ac97_pcm         *pcm;
+       int                     pcm_open;
+
+       u32                     cr;
+       snd_pcm_substream_t     *substream;
+
+       /*
+        * PIO support
+        */
+       void                    *start;
+       void                    *end;
+       void                    *ptr;
+       int                     bytes;
+       unsigned int            period;
+       unsigned int            fifosz;
+};
+
+struct aaci {
+       struct amba_device      *dev;
+       snd_card_t              *card;
+       void                    *base;
+       unsigned int            fifosize;
+
+       /* AC'97 */
+       struct semaphore        ac97_sem;
+       ac97_bus_t              *ac97_bus;
+
+       u32                     maincr;
+       spinlock_t              lock;
+
+       struct aaci_runtime     playback;
+       struct aaci_runtime     capture;
+
+       snd_pcm_t               *pcm;
+};
+
+#define ACSTREAM_FRONT         0
+#define ACSTREAM_SURROUND      1
+#define ACSTREAM_LFE           2
+
+#endif
diff --git a/sound/arm/devdma.c b/sound/arm/devdma.c
new file mode 100644 (file)
index 0000000..60826a5
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ *  linux/sound/arm/devdma.c
+ *
+ *  Copyright (C) 2003-2004 Russell King, 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.
+ *
+ *  ARM DMA shim for ALSA.
+ */
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+
+#include "devdma.h"
+
+void devdma_hw_free(struct device *dev, snd_pcm_substream_t *substream)
+{
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       struct snd_dma_buffer *buf = runtime->dma_buffer_p;
+
+       if (runtime->dma_area == NULL)
+               return;
+
+       if (buf != &substream->dma_buffer) {
+               dma_free_coherent(buf->dev.dev, buf->bytes, buf->area, buf->addr);
+               kfree(runtime->dma_buffer_p);
+       }
+
+       snd_pcm_set_runtime_buffer(substream, NULL);
+}
+
+int devdma_hw_alloc(struct device *dev, snd_pcm_substream_t *substream, size_t size)
+{
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       struct snd_dma_buffer *buf = runtime->dma_buffer_p;
+       int ret = 0;
+
+       if (buf) {
+               if (buf->bytes >= size)
+                       goto out;
+               devdma_hw_free(dev, substream);
+       }
+
+       if (substream->dma_buffer.area != NULL && substream->dma_buffer.bytes >= size) {
+               buf = &substream->dma_buffer;
+       } else {
+               buf = kmalloc(sizeof(struct snd_dma_buffer), GFP_KERNEL);
+               if (!buf)
+                       goto nomem;
+
+               buf->dev.type = SNDRV_DMA_TYPE_DEV;
+               buf->dev.dev = dev;
+               buf->area = dma_alloc_coherent(dev, size, &buf->addr, GFP_KERNEL);
+               buf->bytes = size;
+               buf->private_data = NULL;
+
+               if (!buf->area)
+                       goto free;
+       }
+       snd_pcm_set_runtime_buffer(substream, buf);
+       ret = 1;
+ out:
+       runtime->dma_bytes = size;
+       return ret;
+
+ free:
+       kfree(buf);
+ nomem:
+       return -ENOMEM;
+}
+
+int devdma_mmap(struct device *dev, snd_pcm_substream_t *substream, struct vm_area_struct *vma)
+{
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       return dma_mmap_coherent(dev, vma, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes);
+}
diff --git a/sound/arm/devdma.h b/sound/arm/devdma.h
new file mode 100644 (file)
index 0000000..5a33b6b
--- /dev/null
@@ -0,0 +1,3 @@
+void devdma_hw_free(struct device *dev, snd_pcm_substream_t *substream);
+int devdma_hw_alloc(struct device *dev, snd_pcm_substream_t *substream, size_t size);
+int devdma_mmap(struct device *dev, snd_pcm_substream_t *substream, struct vm_area_struct *vma);
index f4ea6bff1dd3b133ddf528f1cc272c1f647c3070..227f3cf02771605d11e8ddd32c565bc902d75d7d 100644 (file)
@@ -215,7 +215,7 @@ snd_kcontrol_t *snd_ctl_new(snd_kcontrol_t * control, unsigned int access)
  *
  * Returns the pointer of the newly generated instance, or NULL on failure.
  */
-snd_kcontrol_t *snd_ctl_new1(snd_kcontrol_new_t * ncontrol, void *private_data)
+snd_kcontrol_t *snd_ctl_new1(const snd_kcontrol_new_t * ncontrol, void *private_data)
 {
        snd_kcontrol_t kctl;
        unsigned int access;
@@ -1102,7 +1102,7 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
                }
        }
        up_read(&snd_ioctl_rwsem);
-       snd_printd("unknown ioctl = 0x%x\n", cmd);
+       snd_printdd("unknown ioctl = 0x%x\n", cmd);
        return -ENOTTY;
 }
 
index 344a83fd7c2e6b884cc318cad9518dfa733def8e..dbc23e35fa065decbbeb4910f39d0153073f0c36 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/pci.h>
 #include <linux/slab.h>
 #include <linux/mm.h>
+#include <asm/uaccess.h>
 #include <linux/dma-mapping.h>
 #include <linux/moduleparam.h>
 #include <asm/semaphore.h>
@@ -46,13 +47,6 @@ MODULE_LICENSE("GPL");
 #define SNDRV_CARDS    8
 #endif
 
-/* FIXME: so far only some PCI devices have the preallocation table */
-#ifdef CONFIG_PCI
-static int enable[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 1};
-module_param_array(enable, bool, NULL, 0444);
-MODULE_PARM_DESC(enable, "Enable cards to allocate buffers.");
-#endif
-
 /*
  */
 
@@ -451,9 +445,13 @@ size_t snd_dma_get_reserved_buf(struct snd_dma_buffer *dmab, unsigned int id)
        list_for_each(p, &mem_list_head) {
                mem = list_entry(p, struct snd_mem_list, list);
                if (mem->id == id &&
-                   ! memcmp(&mem->buffer.dev, &dmab->dev, sizeof(dmab->dev))) {
+                   (mem->buffer.dev.dev == NULL || dmab->dev.dev == NULL ||
+                    ! memcmp(&mem->buffer.dev, &dmab->dev, sizeof(dmab->dev)))) {
+                       struct device *dev = dmab->dev.dev;
                        list_del(p);
                        *dmab = mem->buffer;
+                       if (dmab->dev.dev == NULL)
+                               dmab->dev.dev = dev;
                        kfree(mem);
                        up(&list_mutex);
                        return dmab->bytes;
@@ -508,91 +506,13 @@ static void free_all_reserved_pages(void)
 }
 
 
-
-/*
- * allocation of buffers for pre-defined devices
- */
-
-#ifdef CONFIG_PCI
-/* FIXME: for pci only - other bus? */
-struct prealloc_dev {
-       unsigned short vendor;
-       unsigned short device;
-       unsigned long dma_mask;
-       unsigned int size;
-       unsigned int buffers;
-};
-
-#define HAMMERFALL_BUFFER_SIZE    (16*1024*4*(26+1)+0x10000)
-
-static struct prealloc_dev prealloc_devices[] __initdata = {
-       {
-               /* hammerfall */
-               .vendor = 0x10ee,
-               .device = 0x3fc4,
-               .dma_mask = 0xffffffff,
-               .size = HAMMERFALL_BUFFER_SIZE,
-               .buffers = 2
-       },
-       {
-               /* HDSP */
-               .vendor = 0x10ee,
-               .device = 0x3fc5,
-               .dma_mask = 0xffffffff,
-               .size = HAMMERFALL_BUFFER_SIZE,
-               .buffers = 2
-       },
-       { }, /* terminator */
-};
-
-static void __init preallocate_cards(void)
-{
-       struct pci_dev *pci = NULL;
-       int card;
-
-       card = 0;
-
-       while ((pci = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pci)) != NULL) {
-               struct prealloc_dev *dev;
-               unsigned int i;
-               if (card >= SNDRV_CARDS)
-                       break;
-               for (dev = prealloc_devices; dev->vendor; dev++) {
-                       if (dev->vendor == pci->vendor && dev->device == pci->device)
-                               break;
-               }
-               if (! dev->vendor)
-                       continue;
-               if (! enable[card++]) {
-                       printk(KERN_DEBUG "snd-page-alloc: skipping card %d, device %04x:%04x\n", card, pci->vendor, pci->device);
-                       continue;
-               }
-                       
-               if (pci_set_dma_mask(pci, dev->dma_mask) < 0 ||
-                   pci_set_consistent_dma_mask(pci, dev->dma_mask) < 0) {
-                       printk(KERN_ERR "snd-page-alloc: cannot set DMA mask %lx for pci %04x:%04x\n", dev->dma_mask, dev->vendor, dev->device);
-                       continue;
-               }
-               for (i = 0; i < dev->buffers; i++) {
-                       struct snd_dma_buffer dmab;
-                       memset(&dmab, 0, sizeof(dmab));
-                       if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
-                                               dev->size, &dmab) < 0)
-                               printk(KERN_WARNING "snd-page-alloc: cannot allocate buffer pages (size = %d)\n", dev->size);
-                       else
-                               snd_dma_reserve_buf(&dmab, snd_dma_pci_buf_id(pci));
-               }
-       }
-}
-#else
-#define preallocate_cards()    /* NOP */
-#endif
-
-
 #ifdef CONFIG_PROC_FS
 /*
  * proc file interface
  */
+#define SND_MEM_PROC_FILE      "driver/snd-page-alloc"
+struct proc_dir_entry *snd_mem_proc;
+
 static int snd_mem_proc_read(char *page, char **start, off_t off,
                             int count, int *eof, void *data)
 {
@@ -621,6 +541,97 @@ static int snd_mem_proc_read(char *page, char **start, off_t off,
        up(&list_mutex);
        return len;
 }
+
+/* FIXME: for pci only - other bus? */
+#ifdef CONFIG_PCI
+#define gettoken(bufp) strsep(bufp, " \t\n")
+
+static int snd_mem_proc_write(struct file *file, const char __user *buffer,
+                             unsigned long count, void *data)
+{
+       char buf[128];
+       char *token, *p;
+
+       if (count > ARRAY_SIZE(buf) - 1)
+               count = ARRAY_SIZE(buf) - 1;
+       if (copy_from_user(buf, buffer, count))
+               return -EFAULT;
+       buf[ARRAY_SIZE(buf) - 1] = '\0';
+
+       p = buf;
+       token = gettoken(&p);
+       if (! token || *token == '#')
+               return (int)count;
+       if (strcmp(token, "add") == 0) {
+               char *endp;
+               int vendor, device, size, buffers;
+               long mask;
+               int i, alloced;
+               struct pci_dev *pci;
+
+               if ((token = gettoken(&p)) == NULL ||
+                   (vendor = simple_strtol(token, NULL, 0)) <= 0 ||
+                   (token = gettoken(&p)) == NULL ||
+                   (device = simple_strtol(token, NULL, 0)) <= 0 ||
+                   (token = gettoken(&p)) == NULL ||
+                   (mask = simple_strtol(token, NULL, 0)) < 0 ||
+                   (token = gettoken(&p)) == NULL ||
+                   (size = memparse(token, &endp)) < 64*1024 ||
+                   size > 16*1024*1024 /* too big */ ||
+                   (token = gettoken(&p)) == NULL ||
+                   (buffers = simple_strtol(token, NULL, 0)) <= 0 ||
+                   buffers > 4) {
+                       printk(KERN_ERR "snd-page-alloc: invalid proc write format\n");
+                       return (int)count;
+               }
+               vendor &= 0xffff;
+               device &= 0xffff;
+
+               alloced = 0;
+               pci = NULL;
+               while ((pci = pci_find_device(vendor, device, pci)) != NULL) {
+                       if (mask > 0 && mask < 0xffffffff) {
+                               if (pci_set_dma_mask(pci, mask) < 0 ||
+                                   pci_set_consistent_dma_mask(pci, mask) < 0) {
+                                       printk(KERN_ERR "snd-page-alloc: cannot set DMA mask %lx for pci %04x:%04x\n", mask, vendor, device);
+                                       return (int)count;
+                               }
+                       }
+                       for (i = 0; i < buffers; i++) {
+                               struct snd_dma_buffer dmab;
+                               memset(&dmab, 0, sizeof(dmab));
+                               if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+                                                       size, &dmab) < 0) {
+                                       printk(KERN_ERR "snd-page-alloc: cannot allocate buffer pages (size = %d)\n", size);
+                                       return (int)count;
+                               }
+                               snd_dma_reserve_buf(&dmab, snd_dma_pci_buf_id(pci));
+                       }
+                       alloced++;
+               }
+               if (! alloced) {
+                       for (i = 0; i < buffers; i++) {
+                               struct snd_dma_buffer dmab;
+                               memset(&dmab, 0, sizeof(dmab));
+                               /* FIXME: We can allocate only in ZONE_DMA
+                                * without a device pointer!
+                                */
+                               if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, NULL,
+                                                       size, &dmab) < 0) {
+                                       printk(KERN_ERR "snd-page-alloc: cannot allocate buffer pages (size = %d)\n", size);
+                                       break;
+                               }
+                               snd_dma_reserve_buf(&dmab, (unsigned int)((vendor << 16) | device));
+                       }
+               }
+       } else if (strcmp(token, "erase") == 0)
+               /* FIXME: need for releasing each buffer chunk? */
+               free_all_reserved_pages();
+       else
+               printk(KERN_ERR "snd-page-alloc: invalid proc cmd\n");
+       return (int)count;
+}
+#endif /* CONFIG_PCI */
 #endif /* CONFIG_PROC_FS */
 
 /*
@@ -630,15 +641,21 @@ static int snd_mem_proc_read(char *page, char **start, off_t off,
 static int __init snd_mem_init(void)
 {
 #ifdef CONFIG_PROC_FS
-       create_proc_read_entry("driver/snd-page-alloc", 0, NULL, snd_mem_proc_read, NULL);
+       snd_mem_proc = create_proc_entry(SND_MEM_PROC_FILE, 0644, NULL);
+       if (snd_mem_proc) {
+               snd_mem_proc->read_proc = snd_mem_proc_read;
+#ifdef CONFIG_PCI
+               snd_mem_proc->write_proc = snd_mem_proc_write;
+#endif
+       }
 #endif
-       preallocate_cards();
        return 0;
 }
 
 static void __exit snd_mem_exit(void)
 {
-       remove_proc_entry("driver/snd-page-alloc", NULL);
+       if (snd_mem_proc)
+               remove_proc_entry(SND_MEM_PROC_FILE, NULL);
        free_all_reserved_pages();
        if (snd_allocated_pages > 0)
                printk(KERN_ERR "snd-malloc: Memory leak?  pages not freed = %li\n", snd_allocated_pages);
index 1a805020f57a5f3f93293a925ae613a13510f8a9..cab30977e7c0887603abacd05218894857176463 100644 (file)
@@ -125,17 +125,26 @@ int snd_pcm_plugin_append(snd_pcm_plugin_t *plugin)
 static long snd_pcm_oss_bytes(snd_pcm_substream_t *substream, long frames)
 {
        snd_pcm_runtime_t *runtime = substream->runtime;
-       snd_pcm_uframes_t buffer_size = snd_pcm_lib_buffer_bytes(substream);
-       frames = frames_to_bytes(runtime, frames);
+       long buffer_size = snd_pcm_lib_buffer_bytes(substream);
+       long bytes = frames_to_bytes(runtime, frames);
        if (buffer_size == runtime->oss.buffer_bytes)
-               return frames;
-       return (runtime->oss.buffer_bytes * frames) / buffer_size;
+               return bytes;
+#if BITS_PER_LONG >= 64
+       return runtime->oss.buffer_bytes * bytes / buffer_size;
+#else
+       {
+               u64 bsize = (u64)runtime->oss.buffer_bytes * (u64)bytes;
+               u32 rem;
+               div64_32(&bsize, buffer_size, &rem);
+               return (long)bsize;
+       }
+#endif
 }
 
 static long snd_pcm_alsa_frames(snd_pcm_substream_t *substream, long bytes)
 {
        snd_pcm_runtime_t *runtime = substream->runtime;
-       snd_pcm_uframes_t buffer_size = snd_pcm_lib_buffer_bytes(substream);
+       long buffer_size = snd_pcm_lib_buffer_bytes(substream);
        if (buffer_size == runtime->oss.buffer_bytes)
                return bytes_to_frames(runtime, bytes);
        return bytes_to_frames(runtime, (buffer_size * bytes) / runtime->oss.buffer_bytes);
@@ -464,7 +473,8 @@ static int snd_pcm_oss_change_params(snd_pcm_substream_t *substream)
        sw_params->tstamp_mode = SNDRV_PCM_TSTAMP_NONE;
        sw_params->period_step = 1;
        sw_params->sleep_min = 0;
-       sw_params->avail_min = 1;
+       sw_params->avail_min = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+               1 : runtime->period_size;
        sw_params->xfer_align = 1;
        if (atomic_read(&runtime->mmap_count) ||
            (substream->oss.setup && substream->oss.setup->nosilence)) {
@@ -1527,12 +1537,15 @@ static int snd_pcm_oss_get_ptr(snd_pcm_oss_file_t *pcm_oss_file, int stream, str
                        snd_pcm_oss_simulate_fill(substream, delay);
                info.bytes = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr) & INT_MAX;
        } else {
-               delay = snd_pcm_oss_bytes(substream, delay) + fixup;
-               info.blocks = delay / runtime->oss.period_bytes;
-               if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+               delay = snd_pcm_oss_bytes(substream, delay);
+               if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       info.blocks = (runtime->oss.buffer_bytes - delay - fixup) / runtime->oss.period_bytes;
                        info.bytes = (runtime->oss.bytes - delay) & INT_MAX;
-               else
+               } else {
+                       delay += fixup;
+                       info.blocks = delay / runtime->oss.period_bytes;
                        info.bytes = (runtime->oss.bytes + delay) & INT_MAX;
+               }
        }
        if (copy_to_user(_info, &info, sizeof(info)))
                return -EFAULT;
index 6bb31009f0b4996949981c129b0cc94b29b1b7ce..6430410c6c04703af940b04f377b15fdb304572a 100644 (file)
@@ -663,10 +663,7 @@ static int snd_pcm_plug_playback_channels_mask(snd_pcm_plug_t *plug,
                bitset_t *dstmask = bs;
                int err;
                bitset_one(dstmask, schannels);
-               if (plugin == NULL) {
-                       bitset_and(client_vmask, dstmask, schannels);
-                       return 0;
-               }
+
                while (1) {
                        err = plugin->src_channels_mask(plugin, dstmask, &srcmask);
                        if (err < 0)
index 8d94325529a87258bcdade5152f55fdb8f37b53f..9f4c9209b2717a0ff23b938402e9fb5132efeaeb 100644 (file)
@@ -451,6 +451,7 @@ static int snd_pcm_stream_proc_init(snd_pcm_str_t *pstr)
                entry->c.text.read = snd_pcm_xrun_debug_read;
                entry->c.text.write_size = 64;
                entry->c.text.write = snd_pcm_xrun_debug_write;
+               entry->mode |= S_IWUSR;
                entry->private_data = pstr;
                if (snd_info_register(entry) < 0) {
                        snd_info_free_entry(entry);
@@ -1048,7 +1049,6 @@ EXPORT_SYMBOL(snd_pcm_release_substream);
 EXPORT_SYMBOL(snd_pcm_format_name);
   /* pcm_native.c */
 EXPORT_SYMBOL(snd_pcm_link_rwlock);
-EXPORT_SYMBOL(snd_pcm_start);
 #ifdef CONFIG_PM
 EXPORT_SYMBOL(snd_pcm_suspend);
 EXPORT_SYMBOL(snd_pcm_suspend_all);
@@ -1068,6 +1068,7 @@ EXPORT_SYMBOL(snd_pcm_format_little_endian);
 EXPORT_SYMBOL(snd_pcm_format_big_endian);
 EXPORT_SYMBOL(snd_pcm_format_width);
 EXPORT_SYMBOL(snd_pcm_format_physical_width);
+EXPORT_SYMBOL(snd_pcm_format_size);
 EXPORT_SYMBOL(snd_pcm_format_silence_64);
 EXPORT_SYMBOL(snd_pcm_format_set_silence);
 EXPORT_SYMBOL(snd_pcm_build_linear_format);
index 151fd99ca2c9b27b7a5a75e6892c5391bb042cd2..c5bfd0918cff9eba195d733355435580f27f8882 100644 (file)
@@ -1143,7 +1143,8 @@ int snd_pcm_hw_constraint_pow2(snd_pcm_runtime_t *runtime,
 #define INT_MIN ((int)((unsigned int)INT_MAX+1))
 #endif
 
-void _snd_pcm_hw_param_any(snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var)
+static void _snd_pcm_hw_param_any(snd_pcm_hw_params_t *params,
+                                 snd_pcm_hw_param_t var)
 {
        if (hw_is_mask(var)) {
                snd_mask_any(hw_param_mask(params, var));
@@ -1160,6 +1161,7 @@ void _snd_pcm_hw_param_any(snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var)
        snd_BUG();
 }
 
+#if 0
 /**
  * snd_pcm_hw_param_any
  */
@@ -1169,6 +1171,7 @@ int snd_pcm_hw_param_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
        _snd_pcm_hw_param_any(params, var);
        return snd_pcm_hw_refine(pcm, params);
 }
+#endif  /*  0  */
 
 void _snd_pcm_hw_params_any(snd_pcm_hw_params_t *params)
 {
@@ -1181,6 +1184,7 @@ void _snd_pcm_hw_params_any(snd_pcm_hw_params_t *params)
        params->info = ~0U;
 }
 
+#if 0
 /**
  * snd_pcm_hw_params_any
  *
@@ -1191,6 +1195,7 @@ int snd_pcm_hw_params_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
        _snd_pcm_hw_params_any(params);
        return snd_pcm_hw_refine(pcm, params);
 }
+#endif  /*  0  */
 
 /**
  * snd_pcm_hw_param_value
@@ -1198,8 +1203,8 @@ int snd_pcm_hw_params_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
  * Return the value for field PAR if it's fixed in configuration space 
  *  defined by PARAMS. Return -EINVAL otherwise
  */
-int snd_pcm_hw_param_value(const snd_pcm_hw_params_t *params,
-                          snd_pcm_hw_param_t var, int *dir)
+static int snd_pcm_hw_param_value(const snd_pcm_hw_params_t *params,
+                                 snd_pcm_hw_param_t var, int *dir)
 {
        if (hw_is_mask(var)) {
                const snd_mask_t *mask = hw_param_mask_c(params, var);
@@ -1296,6 +1301,7 @@ int _snd_pcm_hw_param_setinteger(snd_pcm_hw_params_t *params,
        return changed;
 }
        
+#if 0
 /**
  * snd_pcm_hw_param_setinteger
  *
@@ -1317,9 +1323,10 @@ int snd_pcm_hw_param_setinteger(snd_pcm_t *pcm,
        }
        return 0;
 }
+#endif  /*  0  */
 
-int _snd_pcm_hw_param_first(snd_pcm_hw_params_t *params,
-                           snd_pcm_hw_param_t var)
+static int _snd_pcm_hw_param_first(snd_pcm_hw_params_t *params,
+                                  snd_pcm_hw_param_t var)
 {
        int changed;
        if (hw_is_mask(var))
@@ -1345,9 +1352,9 @@ int _snd_pcm_hw_param_first(snd_pcm_hw_params_t *params,
  * values > minimum. Reduce configuration space accordingly.
  * Return the minimum.
  */
-int snd_pcm_hw_param_first(snd_pcm_t *pcm, 
-                          snd_pcm_hw_params_t *params, 
-                          snd_pcm_hw_param_t var, int *dir)
+static int snd_pcm_hw_param_first(snd_pcm_t *pcm, 
+                                 snd_pcm_hw_params_t *params, 
+                                 snd_pcm_hw_param_t var, int *dir)
 {
        int changed = _snd_pcm_hw_param_first(params, var);
        if (changed < 0)
@@ -1359,8 +1366,8 @@ int snd_pcm_hw_param_first(snd_pcm_t *pcm,
        return snd_pcm_hw_param_value(params, var, dir);
 }
 
-int _snd_pcm_hw_param_last(snd_pcm_hw_params_t *params,
-                          snd_pcm_hw_param_t var)
+static int _snd_pcm_hw_param_last(snd_pcm_hw_params_t *params,
+                                 snd_pcm_hw_param_t var)
 {
        int changed;
        if (hw_is_mask(var))
@@ -1386,9 +1393,9 @@ int _snd_pcm_hw_param_last(snd_pcm_hw_params_t *params,
  * values < maximum. Reduce configuration space accordingly.
  * Return the maximum.
  */
-int snd_pcm_hw_param_last(snd_pcm_t *pcm, 
-                         snd_pcm_hw_params_t *params,
-                         snd_pcm_hw_param_t var, int *dir)
+static int snd_pcm_hw_param_last(snd_pcm_t *pcm, 
+                                snd_pcm_hw_params_t *params,
+                                snd_pcm_hw_param_t var, int *dir)
 {
        int changed = _snd_pcm_hw_param_last(params, var);
        if (changed < 0)
@@ -1437,8 +1444,9 @@ int _snd_pcm_hw_param_min(snd_pcm_hw_params_t *params,
  * values < VAL. Reduce configuration space accordingly.
  * Return new minimum or -EINVAL if the configuration space is empty
  */
-int snd_pcm_hw_param_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
-                        snd_pcm_hw_param_t var, unsigned int val, int *dir)
+static int snd_pcm_hw_param_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
+                               snd_pcm_hw_param_t var, unsigned int val,
+                               int *dir)
 {
        int changed = _snd_pcm_hw_param_min(params, var, val, dir ? *dir : 0);
        if (changed < 0)
@@ -1451,8 +1459,9 @@ int snd_pcm_hw_param_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
        return snd_pcm_hw_param_value_min(params, var, dir);
 }
 
-int _snd_pcm_hw_param_max(snd_pcm_hw_params_t *params,
-                          snd_pcm_hw_param_t var, unsigned int val, int dir)
+static int _snd_pcm_hw_param_max(snd_pcm_hw_params_t *params,
+                                snd_pcm_hw_param_t var, unsigned int val,
+                                int dir)
 {
        int changed;
        int open = 0;
@@ -1490,8 +1499,9 @@ int _snd_pcm_hw_param_max(snd_pcm_hw_params_t *params,
  *  values >= VAL + 1. Reduce configuration space accordingly.
  *  Return new maximum or -EINVAL if the configuration space is empty
  */
-int snd_pcm_hw_param_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
-                         snd_pcm_hw_param_t var, unsigned int val, int *dir)
+static int snd_pcm_hw_param_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
+                               snd_pcm_hw_param_t var, unsigned int val,
+                               int *dir)
 {
        int changed = _snd_pcm_hw_param_max(params, var, val, dir ? *dir : 0);
        if (changed < 0)
@@ -2564,9 +2574,6 @@ snd_pcm_sframes_t snd_pcm_lib_readv(snd_pcm_substream_t *substream,
 EXPORT_SYMBOL(snd_interval_refine);
 EXPORT_SYMBOL(snd_interval_list);
 EXPORT_SYMBOL(snd_interval_ratnum);
-EXPORT_SYMBOL(snd_interval_muldivk);
-EXPORT_SYMBOL(snd_interval_mulkdiv);
-EXPORT_SYMBOL(snd_interval_div);
 EXPORT_SYMBOL(_snd_pcm_hw_params_any);
 EXPORT_SYMBOL(_snd_pcm_hw_param_min);
 EXPORT_SYMBOL(_snd_pcm_hw_param_set);
@@ -2580,7 +2587,6 @@ EXPORT_SYMBOL(snd_pcm_hw_param_last);
 EXPORT_SYMBOL(snd_pcm_hw_param_near);
 EXPORT_SYMBOL(snd_pcm_hw_param_set);
 EXPORT_SYMBOL(snd_pcm_hw_refine);
-EXPORT_SYMBOL(snd_pcm_hw_params);
 EXPORT_SYMBOL(snd_pcm_hw_constraints_init);
 EXPORT_SYMBOL(snd_pcm_hw_constraints_complete);
 EXPORT_SYMBOL(snd_pcm_hw_constraint_list);
index f1d5f7a6ee0c69b4e6131af5c10060a7d68e8590..9a174fb96565368abe767344de9bffca32887e09 100644 (file)
@@ -204,6 +204,7 @@ static int snd_pcm_lib_preallocate_pages1(snd_pcm_substream_t *substream,
                entry->c.text.read = snd_pcm_lib_preallocate_proc_read;
                entry->c.text.write_size = 64;
                entry->c.text.write = snd_pcm_lib_preallocate_proc_write;
+               entry->mode |= S_IWUSR;
                entry->private_data = substream;
                if (snd_info_register(entry) < 0) {
                        snd_info_free_entry(entry);
index 422b8db141547fe07b1c69b817ad7de79a651a3b..1453743e4da0ed1f1eb9169bbb7651af63aaf7e0 100644 (file)
@@ -269,22 +269,6 @@ int snd_pcm_format_big_endian(snd_pcm_format_t format)
        return !val;
 }
 
-/**
- * snd_pcm_format_cpu_endian - Check the PCM format is CPU-endian
- * @format: the format to check
- *
- * Returns 1 if the given PCM format is CPU-endian, 0 if
- * opposite, or a negative error code if endian not specified.
- */
-int snd_pcm_format_cpu_endian(snd_pcm_format_t format)
-{
-#ifdef SNDRV_LITTLE_ENDIAN
-       return snd_pcm_format_little_endian(format);
-#else
-       return snd_pcm_format_big_endian(format);
-#endif
-}
-
 /**
  * snd_pcm_format_width - return the bit-width of the format
  * @format: the format to check
index cad9bbde99868049c14a1e8c7365d331308bb32c..10c2c98326497e6f0cb5fc0c453c1701daf5f8c4 100644 (file)
@@ -337,8 +337,8 @@ out:
        return err;
 }
 
-int snd_pcm_hw_params(snd_pcm_substream_t *substream,
-                     snd_pcm_hw_params_t *params)
+static int snd_pcm_hw_params(snd_pcm_substream_t *substream,
+                            snd_pcm_hw_params_t *params)
 {
        snd_pcm_runtime_t *runtime;
        int err;
@@ -1368,43 +1368,32 @@ static int snd_pcm_drain(snd_pcm_substream_t *substream)
        if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
                return -EBADFD;
 
-       down_read(&snd_pcm_link_rwsem);
        snd_power_lock(card);
        if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
                result = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);
-               if (result < 0)
-                       goto _unlock;
+               if (result < 0) {
+                       snd_power_unlock(card);
+                       return result;
+               }
        }
 
        /* allocate temporary record for drain sync */
+       down_read(&snd_pcm_link_rwsem);
        if (snd_pcm_stream_linked(substream)) {
                drec = kmalloc(substream->group->count * sizeof(*drec), GFP_KERNEL);
                if (! drec) {
-                       result = -ENOMEM;
-                       goto _unlock;
+                       up_read(&snd_pcm_link_rwsem);
+                       snd_power_unlock(card);
+                       return -ENOMEM;
                }
        } else
                drec = &drec_tmp;
 
-       snd_pcm_stream_lock_irq(substream);
-       /* resume pause */
-       if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
-               snd_pcm_pause(substream, 0);
-
-       /* pre-start/stop - all running streams are changed to DRAINING state */
-       result = snd_pcm_action(&snd_pcm_action_drain_init, substream, 0);
-       if (result < 0)
-               goto _end;
-
-       /* check streams with PLAYBACK & DRAINING */
+       /* count only playback streams */
        num_drecs = 0;
        snd_pcm_group_for_each(pos, substream) {
                snd_pcm_substream_t *s = snd_pcm_group_substream_entry(pos);
                runtime = s->runtime;
-               if (runtime->status->state != SNDRV_PCM_STATE_DRAINING) {
-                       runtime->status->state = SNDRV_PCM_STATE_SETUP;
-                       continue;
-               }
                if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {
                        d = &drec[num_drecs++];
                        d->substream = s;
@@ -1418,9 +1407,21 @@ static int snd_pcm_drain(snd_pcm_substream_t *substream)
                                runtime->stop_threshold = runtime->buffer_size;
                }
        }
-
+       up_read(&snd_pcm_link_rwsem);
        if (! num_drecs)
-               goto _end;
+               goto _error;
+
+       snd_pcm_stream_lock_irq(substream);
+       /* resume pause */
+       if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
+               snd_pcm_pause(substream, 0);
+
+       /* pre-start/stop - all running streams are changed to DRAINING state */
+       result = snd_pcm_action(&snd_pcm_action_drain_init, substream, 0);
+       if (result < 0) {
+               snd_pcm_stream_unlock_irq(substream);
+               goto _error;
+       }
 
        for (;;) {
                long tout;
@@ -1428,6 +1429,15 @@ static int snd_pcm_drain(snd_pcm_substream_t *substream)
                        result = -ERESTARTSYS;
                        break;
                }
+               /* all finished? */
+               for (i = 0; i < num_drecs; i++) {
+                       runtime = drec[i].substream->runtime;
+                       if (runtime->status->state == SNDRV_PCM_STATE_DRAINING)
+                               break;
+               }
+               if (i == num_drecs)
+                       break; /* yes, all drained */
+
                set_current_state(TASK_INTERRUPTIBLE);
                snd_pcm_stream_unlock_irq(substream);
                snd_power_unlock(card);
@@ -1444,15 +1454,11 @@ static int snd_pcm_drain(snd_pcm_substream_t *substream)
                        }
                        break;
                }
-               /* all finished? */
-               for (i = 0; i < num_drecs; i++) {
-                       runtime = drec[i].substream->runtime;
-                       if (runtime->status->state == SNDRV_PCM_STATE_DRAINING)
-                               break;
-               }
-               if (i == num_drecs)
-                       break;
        }
+
+       snd_pcm_stream_unlock_irq(substream);
+
+ _error:
        for (i = 0; i < num_drecs; i++) {
                d = &drec[i];
                runtime = d->substream->runtime;
@@ -1460,13 +1466,9 @@ static int snd_pcm_drain(snd_pcm_substream_t *substream)
                runtime->stop_threshold = d->stop_threshold;
        }
 
- _end:
-       snd_pcm_stream_unlock_irq(substream);
        if (drec != &drec_tmp)
                kfree(drec);
- _unlock:
        snd_power_unlock(card);
-       up_read(&snd_pcm_link_rwsem);
 
        return result;
 }
index 638cc148706dcf4099fd1e3c4125c21b2f79c54c..1a7736cbf3a4717202159087fb4355071c5c7c4e 100644 (file)
@@ -325,14 +325,10 @@ snd_seq_oss_synth_cleanup(seq_oss_devinfo_t *dp)
                        }
                        snd_use_lock_free(&rec->use_lock);
                }
-               if (info->sysex) {
-                       kfree(info->sysex);
-                       info->sysex = NULL;
-               }
-               if (info->ch) {
-                       kfree(info->ch);
-                       info->ch = NULL;
-               }
+               kfree(info->sysex);
+               info->sysex = NULL;
+               kfree(info->ch);
+               info->ch = NULL;
        }
        dp->synth_opened = 0;
        dp->max_synthdev = 0;
@@ -418,14 +414,10 @@ snd_seq_oss_synth_reset(seq_oss_devinfo_t *dp, int dev)
                                          dp->file_mode) < 0) {
                        midi_synth_dev.opened--;
                        info->opened = 0;
-                       if (info->sysex) {
-                               kfree(info->sysex);
-                               info->sysex = NULL;
-                       }
-                       if (info->ch) {
-                               kfree(info->ch);
-                               info->ch = NULL;
-                       }
+                       kfree(info->sysex);
+                       info->sysex = NULL;
+                       kfree(info->ch);
+                       info->ch = NULL;
                }
                return;
        }
index e88967c5b93d1fe6c53428551535840f7043823b..ea945a5d2a0b194f4884cdbac0aeae4c00f3ced2 100644 (file)
@@ -140,10 +140,7 @@ dummy_input(snd_seq_event_t *ev, int direct, void *private_data, int atomic, int
 static void
 dummy_free(void *private_data)
 {
-       snd_seq_dummy_port_t *p;
-
-       p = private_data;
-       kfree(p);
+       kfree(private_data);
 }
 
 /*
index 18247db45db65d501040eacb4dcf74c2a6b76e6e..57be9155eb622854f97c4f85cacc1c3a259fb566 100644 (file)
@@ -414,6 +414,8 @@ snd_seq_midisynth_register_port(snd_seq_device_t *dev)
        if (newclient)
                synths[card->number] = client;
        up(&register_mutex);
+       kfree(info);
+       kfree(port);
        return 0;       /* success */
 
       __nomem:
index 21e569062bc384e55f98b2403c9b20e61348b4cc..df1e2bb397458f1cea7d5388f250f95ff760b0b2 100644 (file)
@@ -171,11 +171,13 @@ void snd_midi_event_reset_decode(snd_midi_event_t *dev)
        spin_unlock_irqrestore(&dev->lock, flags);
 }
 
+#if 0
 void snd_midi_event_init(snd_midi_event_t *dev)
 {
        snd_midi_event_reset_encode(dev);
        snd_midi_event_reset_decode(dev);
 }
+#endif  /*  0  */
 
 void snd_midi_event_no_status(snd_midi_event_t *dev, int on)
 {
@@ -185,6 +187,7 @@ void snd_midi_event_no_status(snd_midi_event_t *dev, int on)
 /*
  * resize buffer
  */
+#if 0
 int snd_midi_event_resize_buffer(snd_midi_event_t *dev, int bufsize)
 {
        unsigned char *new_buf, *old_buf;
@@ -204,6 +207,7 @@ int snd_midi_event_resize_buffer(snd_midi_event_t *dev, int bufsize)
        kfree(old_buf);
        return 0;
 }
+#endif  /*  0  */
 
 /*
  *  read bytes and encode to sequencer event if finished
@@ -517,8 +521,6 @@ static int extra_decode_xrpn(snd_midi_event_t *dev, unsigned char *buf, int coun
  
 EXPORT_SYMBOL(snd_midi_event_new);
 EXPORT_SYMBOL(snd_midi_event_free);
-EXPORT_SYMBOL(snd_midi_event_resize_buffer);
-EXPORT_SYMBOL(snd_midi_event_init);
 EXPORT_SYMBOL(snd_midi_event_reset_encode);
 EXPORT_SYMBOL(snd_midi_event_reset_decode);
 EXPORT_SYMBOL(snd_midi_event_no_status);
index 3afc7cc0c9a79d65c20de27788c2a701be5b1623..98de2e711fde90b174d6cc87ffde3b00861073d1 100644 (file)
@@ -672,7 +672,8 @@ static void queue_broadcast_event(queue_t *q, snd_seq_event_t *ev, int atomic, i
  * process a received queue-control event.
  * this function is exported for seq_sync.c.
  */
-void snd_seq_queue_process_event(queue_t *q, snd_seq_event_t *ev, int atomic, int hop)
+static void snd_seq_queue_process_event(queue_t *q, snd_seq_event_t *ev,
+                                       int atomic, int hop)
 {
        switch (ev->type) {
        case SNDRV_SEQ_EVENT_START:
index b1bf5519fb3b0ac119a06e953644e47445b2d103..ea3c54216ea83214bc64361df62b296e276cd669 100644 (file)
@@ -111,7 +111,6 @@ int snd_seq_queue_use(int queueid, int client, int use);
 int snd_seq_queue_is_used(int queueid, int client);
 
 int snd_seq_control_queue(snd_seq_event_t *ev, int atomic, int hop);
-void snd_seq_queue_process_event(queue_t *q, snd_seq_event_t *ev, int atomic, int hop);
 
 /*
  * 64bit division - for sync stuff..
index 753f1c0863ccf29cc24e67e679997f9437674885..a7f76fc9528092bc38985337409f97dd81451270 100644 (file)
@@ -36,7 +36,8 @@ extern int seq_default_timer_resolution;
 
 #define SKEW_BASE      0x10000 /* 16bit shift */
 
-void snd_seq_timer_set_tick_resolution(seq_timer_tick_t *tick, int tempo, int ppq, int nticks)
+static void snd_seq_timer_set_tick_resolution(seq_timer_tick_t *tick,
+                                             int tempo, int ppq, int nticks)
 {
        if (tempo < 1000000)
                tick->resolution = (tempo * 1000) / ppq;
index 4c0872df8931d996173c8603d3706da6abbd68ac..287ed68591dee872f11eeb7cf421a45793f02564 100644 (file)
@@ -64,8 +64,6 @@ extern seq_timer_t *snd_seq_timer_new(void);
 /* delete timer (destructor) */
 extern void snd_seq_timer_delete(seq_timer_t **tmr);
 
-void snd_seq_timer_set_tick_resolution(seq_timer_tick_t *tick, int tempo, int ppq, int nticks);
-
 /* */
 static inline void snd_seq_timer_update_tick(seq_timer_tick_t *tick, unsigned long resolution)
 {
index 6b4e630ace54108c6ce2d2bb4f012b4896845fb7..a66484b5cf0e921991895296021ae56aad2c34dc 100644 (file)
@@ -110,7 +110,7 @@ static int snd_virmidi_dev_receive_event(snd_virmidi_dev_t *rdev, snd_seq_event_
  * handler of a remote port which is attached to the virmidi via
  * SNDRV_VIRMIDI_SEQ_ATTACH.
  */
-/* exported */
+#if 0
 int snd_virmidi_receive(snd_rawmidi_t *rmidi, snd_seq_event_t *ev)
 {
        snd_virmidi_dev_t *rdev;
@@ -118,6 +118,7 @@ int snd_virmidi_receive(snd_rawmidi_t *rmidi, snd_seq_event_t *ev)
        rdev = rmidi->private_data;
        return snd_virmidi_dev_receive_event(rdev, ev);
 }
+#endif  /*  0  */
 
 /*
  * event handler of virmidi port
@@ -384,7 +385,7 @@ static int snd_virmidi_dev_attach_seq(snd_virmidi_dev_t *rdev)
        info->client = client;
        info->type = KERNEL_CLIENT;
        sprintf(info->name, "%s %d-%d", rdev->rmidi->name, rdev->card->number, rdev->device);
-       snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, &info);
+       snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, info);
 
        /* create a port */
        memset(pinfo, 0, sizeof(*pinfo));
@@ -405,7 +406,7 @@ static int snd_virmidi_dev_attach_seq(snd_virmidi_dev_t *rdev)
        pcallbacks.unuse = snd_virmidi_unuse;
        pcallbacks.event_input = snd_virmidi_event_input;
        pinfo->kernel = &pcallbacks;
-       err = snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_CREATE_PORT, &pinfo);
+       err = snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_CREATE_PORT, pinfo);
        if (err < 0) {
                snd_seq_delete_kernel_client(client);
                rdev->client = -1;
@@ -548,4 +549,3 @@ module_init(alsa_virmidi_init)
 module_exit(alsa_virmidi_exit)
 
 EXPORT_SYMBOL(snd_virmidi_new);
-EXPORT_SYMBOL(snd_virmidi_receive);
index 33eaa5e5d284d6b366a2d8f0a42b95b59b0c697d..0815fadeb3ec7915e6b0c31add6cece70073c9dd 100644 (file)
@@ -431,7 +431,6 @@ EXPORT_SYMBOL(snd_card_pci_resume);
 EXPORT_SYMBOL(snd_device_new);
 EXPORT_SYMBOL(snd_device_register);
 EXPORT_SYMBOL(snd_device_free);
-EXPORT_SYMBOL(snd_device_free_all);
   /* isadma.c */
 #ifdef CONFIG_ISA
 EXPORT_SYMBOL(snd_dma_program);
index fa762ca439be43573f9bd2bfaac8d361be4fea82..b498e5482d77d8d40ce20eb1ed08c3e856e49708 100644 (file)
@@ -69,6 +69,7 @@ typedef struct {
        struct timespec tstamp;         /* trigger tstamp */
        wait_queue_head_t qchange_sleep;
        struct fasync_struct *fasync;
+       struct semaphore tread_sem;
 } snd_timer_user_t;
 
 /* list of timers */
@@ -844,7 +845,7 @@ int snd_timer_dev_register(snd_device_t *dev)
        return 0;
 }
 
-int snd_timer_unregister(snd_timer_t *timer)
+static int snd_timer_unregister(snd_timer_t *timer)
 {
        struct list_head *p, *n;
        snd_timer_instance_t *ti;
@@ -945,11 +946,6 @@ struct snd_timer_system_private {
        unsigned long correction;
 };
 
-unsigned int snd_timer_system_resolution(void)
-{
-       return 1000000000L / HZ;
-}
-
 static void snd_timer_s_function(unsigned long data)
 {
        snd_timer_t *timer = (snd_timer_t *)data;
@@ -1208,6 +1204,7 @@ static int snd_timer_user_open(struct inode *inode, struct file *file)
                return -ENOMEM;
        spin_lock_init(&tu->qlock);
        init_waitqueue_head(&tu->qchange_sleep);
+       init_MUTEX(&tu->tread_sem);
        tu->ticks = 1;
        tu->queue_size = 128;
        tu->queue = (snd_timer_read_t *)kmalloc(tu->queue_size * sizeof(snd_timer_read_t), GFP_KERNEL);
@@ -1454,46 +1451,51 @@ static int snd_timer_user_tselect(struct file *file, snd_timer_select_t __user *
        snd_timer_user_t *tu;
        snd_timer_select_t tselect;
        char str[32];
-       int err;
+       int err = 0;
        
        tu = file->private_data;
-       if (tu->timeri)
+       down(&tu->tread_sem);
+       if (tu->timeri) {
                snd_timer_close(tu->timeri);
-       if (copy_from_user(&tselect, _tselect, sizeof(tselect)))
-               return -EFAULT;
+               tu->timeri = NULL;
+       }
+       if (copy_from_user(&tselect, _tselect, sizeof(tselect))) {
+               err = -EFAULT;
+               goto __err;
+       }
        sprintf(str, "application %i", current->pid);
        if (tselect.id.dev_class != SNDRV_TIMER_CLASS_SLAVE)
                tselect.id.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION;
        if ((err = snd_timer_open(&tu->timeri, str, &tselect.id, current->pid)) < 0)
-               return err;
+               goto __err;
 
-       if (tu->queue) {
-               kfree(tu->queue);
-               tu->queue = NULL;
-       }
-       if (tu->tqueue) {
-               kfree(tu->tqueue);
-               tu->tqueue = NULL;
-       }
+       kfree(tu->queue);
+       tu->queue = NULL;
+       kfree(tu->tqueue);
+       tu->tqueue = NULL;
        if (tu->tread) {
                tu->tqueue = (snd_timer_tread_t *)kmalloc(tu->queue_size * sizeof(snd_timer_tread_t), GFP_KERNEL);
-               if (tu->tqueue == NULL) {
-                       snd_timer_close(tu->timeri);
-                       return -ENOMEM;
-               }
+               if (tu->tqueue == NULL)
+                       err = -ENOMEM;
        } else {
                tu->queue = (snd_timer_read_t *)kmalloc(tu->queue_size * sizeof(snd_timer_read_t), GFP_KERNEL);
-               if (tu->queue == NULL) {
-                       snd_timer_close(tu->timeri);
-                       return -ENOMEM;
-               }
+               if (tu->queue == NULL)
+                       err = -ENOMEM;
        }
        
-       tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST;
-       tu->timeri->callback = tu->tread ? snd_timer_user_tinterrupt : snd_timer_user_interrupt;
-       tu->timeri->ccallback = snd_timer_user_ccallback;
-       tu->timeri->callback_data = (void *)tu;
-       return 0;
+       if (err < 0) {
+               snd_timer_close(tu->timeri);
+               tu->timeri = NULL;
+       } else {
+               tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST;
+               tu->timeri->callback = tu->tread ? snd_timer_user_tinterrupt : snd_timer_user_interrupt;
+               tu->timeri->ccallback = snd_timer_user_ccallback;
+               tu->timeri->callback_data = (void *)tu;
+       }
+
+      __err:
+       up(&tu->tread_sem);
+       return err;
 }
 
 static int snd_timer_user_info(struct file *file, snd_timer_info_t __user *_info)
@@ -1669,6 +1671,23 @@ static int snd_timer_user_continue(struct file *file)
        return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0;
 }
 
+static int snd_timer_user_pause(struct file *file)
+{
+       int err;
+       snd_timer_user_t *tu;
+               
+       tu = file->private_data;
+       snd_assert(tu->timeri != NULL, return -ENXIO);
+       return (err = snd_timer_pause(tu->timeri)) < 0 ? err : 0;
+}
+
+enum {
+       SNDRV_TIMER_IOCTL_START_OLD = _IO('T', 0x20),
+       SNDRV_TIMER_IOCTL_STOP_OLD = _IO('T', 0x21),
+       SNDRV_TIMER_IOCTL_CONTINUE_OLD = _IO('T', 0x22),
+       SNDRV_TIMER_IOCTL_PAUSE_OLD = _IO('T', 0x23),
+};
+
 static long snd_timer_user_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
        snd_timer_user_t *tu;
@@ -1685,11 +1704,17 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd, unsigned l
        {
                int xarg;
                
-               if (tu->timeri)         /* too late */
+               down(&tu->tread_sem);
+               if (tu->timeri) {       /* too late */
+                       up(&tu->tread_sem);
                        return -EBUSY;
-               if (get_user(xarg, p))
+               }
+               if (get_user(xarg, p)) {
+                       up(&tu->tread_sem);
                        return -EFAULT;
+               }
                tu->tread = xarg ? 1 : 0;
+               up(&tu->tread_sem);
                return 0;
        }
        case SNDRV_TIMER_IOCTL_GINFO:
@@ -1707,11 +1732,17 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd, unsigned l
        case SNDRV_TIMER_IOCTL_STATUS:
                return snd_timer_user_status(file, argp);
        case SNDRV_TIMER_IOCTL_START:
+       case SNDRV_TIMER_IOCTL_START_OLD:
                return snd_timer_user_start(file);
        case SNDRV_TIMER_IOCTL_STOP:
+       case SNDRV_TIMER_IOCTL_STOP_OLD:
                return snd_timer_user_stop(file);
        case SNDRV_TIMER_IOCTL_CONTINUE:
+       case SNDRV_TIMER_IOCTL_CONTINUE_OLD:
                return snd_timer_user_continue(file);
+       case SNDRV_TIMER_IOCTL_PAUSE:
+       case SNDRV_TIMER_IOCTL_PAUSE_OLD:
+               return snd_timer_user_pause(file);
        }
        return -ENOTTY;
 }
@@ -1898,4 +1929,3 @@ EXPORT_SYMBOL(snd_timer_global_free);
 EXPORT_SYMBOL(snd_timer_global_register);
 EXPORT_SYMBOL(snd_timer_global_unregister);
 EXPORT_SYMBOL(snd_timer_interrupt);
-EXPORT_SYMBOL(snd_timer_system_resolution);
index 9fbc3957a22dfbc14a7cacf7458c3fefddbb62a0..3de552dfe80f2dcb8a9644acb9824fbd9f1cba5d 100644 (file)
@@ -106,8 +106,13 @@ static long snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd, uns
        case SNDRV_TIMER_IOCTL_SELECT:
        case SNDRV_TIMER_IOCTL_PARAMS:
        case SNDRV_TIMER_IOCTL_START:
+       case SNDRV_TIMER_IOCTL_START_OLD:
        case SNDRV_TIMER_IOCTL_STOP:
+       case SNDRV_TIMER_IOCTL_STOP_OLD:
        case SNDRV_TIMER_IOCTL_CONTINUE:
+       case SNDRV_TIMER_IOCTL_CONTINUE_OLD:
+       case SNDRV_TIMER_IOCTL_PAUSE:
+       case SNDRV_TIMER_IOCTL_PAUSE_OLD:
        case SNDRV_TIMER_IOCTL_NEXT_DEVICE:
                return snd_timer_user_ioctl(file, cmd, (unsigned long)argp);
        case SNDRV_TIMER_IOCTL_INFO32:
index 98587176b32744469818e9074e9a9508e4821f45..af381b15fe5c4a813eb413ae04b0994b8ae67f38 100644 (file)
@@ -1264,14 +1264,10 @@ static void snd_vx_pcm_free(snd_pcm_t *pcm)
 {
        vx_core_t *chip = pcm->private_data;
        chip->pcm[pcm->device] = NULL;
-       if (chip->playback_pipes) {
-               kfree(chip->playback_pipes);
-               chip->playback_pipes = NULL;
-       }
-       if (chip->capture_pipes) {
-               kfree(chip->capture_pipes);
-               chip->capture_pipes = NULL;
-       }
+       kfree(chip->playback_pipes);
+       chip->playback_pipes = NULL;
+       kfree(chip->capture_pipes);
+       chip->capture_pipes = NULL;
 }
 
 /*
index bb503e70b6645b42b4e4258e46f80dc3b7a66928..2da8d7f157f4609eebc0b8b584af20bea91b1403 100644 (file)
@@ -266,8 +266,7 @@ TEA6330T_TREBLE("Tone Control - Treble", 0)
 
 static void snd_tea6330_free(snd_i2c_device_t *device)
 {
-       tea6330t_t *tea = device->private_data;
-       kfree(tea);
+       kfree(device->private_data);
 }
                                         
 int snd_tea6330t_update_mixer(snd_card_t * card,
index 3a3228b18726c9aaae432f7d3ecec06df3c4afa3..148a856a43ad7631207804137347ba6cd26184ca 100644 (file)
@@ -179,6 +179,7 @@ config SND_INTERWAVE_STB
        select SND_RAWMIDI
        select SND_CS4231_LIB
        select SND_GUS_SYNTH
+       select ISAPNP
        help
          Say Y here to include support for AMD InterWave based
          soundcards with a TEA6330T bass and treble regulator
index 9fa7a78da6c3301254919c9adbcef9a382e1dd10..563296d028941b53e211956fff89b54459640dce 100644 (file)
@@ -83,6 +83,8 @@ struct snd_card_ad1816a {
 static struct pnp_card_device_id snd_ad1816a_pnpids[] = {
        /* Analog Devices AD1815 */
        { .id = "ADS7150", .devs = { { .id = "ADS7150" }, { .id = "ADS7151" } } },
+       /* Analog Device AD1816? */
+       { .id = "ADS7180", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
        /* Analog Devices AD1816A - added by Kenneth Platz <kxp@atl.hp.com> */
        { .id = "ADS7181", .devs = { { .id = "ADS7180" }, { .id = "ADS7181" } } },
        /* Analog Devices AD1816A - Aztech/Newcom SC-16 3D */
index e745a54e00a1be7269486ce86cb5396cb492ef95..39f4eff44f5c8c5ac8fb2dc7595ef2082325fe5e 100644 (file)
@@ -349,8 +349,7 @@ static int __devinit snd_card_cs4236_pnp(int dev, struct snd_card_cs4236 *acard,
                pnp_init_resource_table(cfg);
                if (mpu_port[dev] != SNDRV_AUTO_PORT)
                        pnp_resource_change(&cfg->port_resource[0], mpu_port[dev], 2);
-               if (mpu_irq[dev] != SNDRV_AUTO_IRQ && mpu_irq[dev] >= 0 &&
-                   pnp_irq_valid(pdev, 0))
+               if (mpu_irq[dev] != SNDRV_AUTO_IRQ && mpu_irq[dev] >= 0)
                        pnp_resource_change(&cfg->irq_resource[0], mpu_irq[dev], 1);
                err = pnp_manual_config_dev(pdev, cfg, 0);
                if (err < 0)
index f0570f2bf75f89d1c0a62fc094622d88999b220c..337b0e2a8a36905a6b996587043699caf0c6c9ac 100644 (file)
@@ -244,6 +244,8 @@ unsigned short snd_gf1_i_look16(snd_gus_card_t * gus, unsigned char reg)
        return res;
 }
 
+#if 0
+
 void snd_gf1_i_adlib_write(snd_gus_card_t * gus,
                           unsigned char reg,
                           unsigned char data)
@@ -265,6 +267,8 @@ void snd_gf1_i_write_addr(snd_gus_card_t * gus, unsigned char reg,
        spin_unlock_irqrestore(&gus->reg_lock, flags);
 }
 
+#endif  /*  0  */
+
 unsigned int snd_gf1_i_read_addr(snd_gus_card_t * gus,
                                 unsigned char reg, short w_16bit)
 {
@@ -329,6 +333,8 @@ unsigned char snd_gf1_peek(snd_gus_card_t * gus, unsigned int addr)
        return res;
 }
 
+#if 0
+
 void snd_gf1_pokew(snd_gus_card_t * gus, unsigned int addr, unsigned short data)
 {
        unsigned long flags;
@@ -405,9 +411,7 @@ void snd_gf1_dram_setmem(snd_gus_card_t * gus, unsigned int addr,
        spin_unlock_irqrestore(&gus->reg_lock, flags);
 }
 
-/*
-
- */
+#endif  /*  0  */
 
 void snd_gf1_select_active_voices(snd_gus_card_t * gus)
 {
@@ -469,6 +473,8 @@ void snd_gf1_print_voice_registers(snd_gus_card_t * gus)
                printk(" -%i- GF1  pan                    = 0x%x\n", voice, snd_gf1_i_read8(gus, 0x0c));
 }
 
+#if 0
+
 void snd_gf1_print_global_registers(snd_gus_card_t * gus)
 {
        unsigned char global_mode = 0x00;
@@ -528,4 +534,6 @@ void snd_gf1_peek_print_block(snd_gus_card_t * gus, unsigned int addr, int count
        }
 }
 
+#endif  /*  0  */
+
 #endif
index 73f81c14f768ccfc4e7a5e0ba43a201489b648ca..94bbd344be5e31f97b5af4aa72f4e1e38f4eaa05 100644 (file)
@@ -459,7 +459,6 @@ EXPORT_SYMBOL(snd_gf1_write16);
 EXPORT_SYMBOL(snd_gf1_look16);
 EXPORT_SYMBOL(snd_gf1_i_write8);
 EXPORT_SYMBOL(snd_gf1_i_look8);
-EXPORT_SYMBOL(snd_gf1_i_write16);
 EXPORT_SYMBOL(snd_gf1_i_look16);
 EXPORT_SYMBOL(snd_gf1_dram_addr);
 EXPORT_SYMBOL(snd_gf1_write_addr);
@@ -470,8 +469,6 @@ EXPORT_SYMBOL(snd_gf1_alloc_voice);
 EXPORT_SYMBOL(snd_gf1_free_voice);
 EXPORT_SYMBOL(snd_gf1_ctrl_stop);
 EXPORT_SYMBOL(snd_gf1_stop_voice);
-EXPORT_SYMBOL(snd_gf1_start);
-EXPORT_SYMBOL(snd_gf1_stop);
   /* gus_mixer.c */
 EXPORT_SYMBOL(snd_gf1_new_mixer);
   /* gus_pcm.c */
index bfc2b91001d5d8db9f18d3317bf65a9560adaeb2..609838e8ef6727162c8b8b941c0c346e38667cd4 100644 (file)
@@ -39,8 +39,8 @@ void snd_gf1_mem_lock(snd_gf1_mem_t * alloc, int xup)
        }
 }
 
-snd_gf1_mem_block_t *snd_gf1_mem_xalloc(snd_gf1_mem_t * alloc,
-                                       snd_gf1_mem_block_t * block)
+static snd_gf1_mem_block_t *snd_gf1_mem_xalloc(snd_gf1_mem_t * alloc,
+                                              snd_gf1_mem_block_t * block)
 {
        snd_gf1_mem_block_t *pblock, *nblock;
 
@@ -105,8 +105,8 @@ int snd_gf1_mem_xfree(snd_gf1_mem_t * alloc, snd_gf1_mem_block_t * block)
        return 0;
 }
 
-snd_gf1_mem_block_t *snd_gf1_mem_look(snd_gf1_mem_t * alloc,
-                                     unsigned int address)
+static snd_gf1_mem_block_t *snd_gf1_mem_look(snd_gf1_mem_t * alloc,
+                                            unsigned int address)
 {
        snd_gf1_mem_block_t *block;
 
@@ -118,8 +118,8 @@ snd_gf1_mem_block_t *snd_gf1_mem_look(snd_gf1_mem_t * alloc,
        return NULL;
 }
 
-snd_gf1_mem_block_t *snd_gf1_mem_share(snd_gf1_mem_t * alloc,
-                                      unsigned int *share_id)
+static snd_gf1_mem_block_t *snd_gf1_mem_share(snd_gf1_mem_t * alloc,
+                                             unsigned int *share_id)
 {
        snd_gf1_mem_block_t *block;
 
index 8995ad9c516d0c33dfe063da63780af087838b38..b75066ab46fc33edfa1dd8262f0d1beb822a7bb9 100644 (file)
@@ -656,8 +656,7 @@ static snd_pcm_hardware_t snd_gf1_pcm_capture =
 
 static void snd_gf1_pcm_playback_free(snd_pcm_runtime_t *runtime)
 {
-       gus_pcm_private_t * pcmp = runtime->private_data;
-       kfree(pcmp);
+       kfree(runtime->private_data);
 }
 
 static int snd_gf1_pcm_playback_open(snd_pcm_substream_t *substream)
index b4e66f6a10aeee86e40b9779425a924e07321587..ef687abc707093154618df5d4710a6a058c7b328 100644 (file)
@@ -161,7 +161,8 @@ void snd_gf1_stop_voice(snd_gus_card_t * gus, unsigned short voice)
 #endif
 }
 
-void snd_gf1_clear_voices(snd_gus_card_t * gus, unsigned short v_min, unsigned short v_max)
+static void snd_gf1_clear_voices(snd_gus_card_t * gus, unsigned short v_min,
+                                unsigned short v_max)
 {
        unsigned long flags;
        unsigned int daddr;
index 66552e6013a4bc0dd5b016185b5f76ba95fc45d6..f51c386ee192dcf788e35f1f487c289a829b8f73 100644 (file)
@@ -99,7 +99,8 @@ static void snd_gus_synth_free_private_instruments(snd_gus_port_t *p, int client
        snd_seq_instr_list_free_cond(p->gus->gf1.ilist, &ifree, client, 0);
 }
  
-int snd_gus_synth_event_input(snd_seq_event_t *ev, int direct, void *private_data, int atomic, int hop)
+static int snd_gus_synth_event_input(snd_seq_event_t *ev, int direct,
+                                    void *private_data, int atomic, int hop)
 {
        snd_gus_port_t * p = (snd_gus_port_t *) private_data;
        
index ed8e9d85ad31888f273050f8f8efcb3a96f78dba..4adf098d3269d6a7d20711a258e4419db11ca197 100644 (file)
@@ -23,6 +23,8 @@
 
 #ifdef __GUS_TABLES_ALLOC__
 
+#if 0
+
 unsigned int snd_gf1_scale_table[SNDRV_GF1_SCALE_TABLE_SIZE] =
 {
       8372,      8870,      9397,      9956,     10548,     11175,
@@ -49,6 +51,8 @@ unsigned int snd_gf1_scale_table[SNDRV_GF1_SCALE_TABLE_SIZE] =
   12123977,  12844906
 };
 
+#endif  /*  0  */
+
 unsigned short snd_gf1_atten_table[SNDRV_GF1_ATTEN_TABLE_SIZE] = {
   4095 /* 0   */,1789 /* 1   */,1533 /* 2   */,1383 /* 3   */,1277 /* 4   */,
   1195 /* 5   */,1127 /* 6   */,1070 /* 7   */,1021 /* 8   */,978  /* 9   */,
index b72bcfb286174cd9d78a7ec32b25e33304550732..3d36f6c8ee6a5fad70579ad8fbb34db15717e01b 100644 (file)
@@ -55,6 +55,8 @@ unsigned short snd_gf1_lvol_to_gvol_raw(unsigned int vol)
        return (e << 8) | m;
 }
 
+#if 0
+
 unsigned int snd_gf1_gvol_to_lvol_raw(unsigned short gf1_vol)
 {
        unsigned int rvol;
@@ -108,6 +110,8 @@ unsigned int snd_gf1_calc_ramp_rate(snd_gus_card_t * gus,
        return (range << 6) | (increment & 0x3f);
 }
 
+#endif  /*  0  */
+
 unsigned short snd_gf1_translate_freq(snd_gus_card_t * gus, unsigned int freq16)
 {
        freq16 >>= 3;
@@ -120,6 +124,8 @@ unsigned short snd_gf1_translate_freq(snd_gus_card_t * gus, unsigned int freq16)
        return ((freq16 << 9) + (gus->gf1.playback_freq >> 1)) / gus->gf1.playback_freq;
 }
 
+#if 0
+
 short snd_gf1_compute_vibrato(short cents, unsigned short fc_register)
 {
        static short vibrato_table[] =
@@ -208,3 +214,5 @@ unsigned short snd_gf1_compute_freq(unsigned int freq,
        }
        return (unsigned short) fc;
 }
+
+#endif  /*  0  */
index 428efdbd70a12cd65491c60267ed8b25f6cc3732..6d7a00f34d822cc3171e4518f02a8271a1303104 100644 (file)
@@ -274,6 +274,19 @@ config SND_HDSP
          To compile this driver as a module, choose M here: the module
          will be called snd-hdsp.
 
+config SND_HDSPM
+       tristate "RME Hammerfall DSP MADI"
+       depends on SND
+       select SND_HWDEP
+       select SND_RAWMIDI
+       select SND_PCM
+       help
+         Say Y here to include support for RME Hammerfall DSP MADI
+         soundcards.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-hdspm.
+
 config SND_TRIDENT
        tristate "Trident 4D-Wave DX/NX; SiS 7018"
        depends on SND
index 0b024ec1f7096d2419c6243b57d9d1d1eff6c3c8..a4b72cd2eea065e0501c8307387897f18af64dee 100644 (file)
@@ -120,6 +120,7 @@ static const ac97_codec_id_t snd_ac97_codec_ids[] = {
 { 0x414c4770, 0xfffffff0, "ALC203",            NULL,           NULL },
 { 0x434d4941, 0xffffffff, "CMI9738",           patch_cm9738,   NULL },
 { 0x434d4961, 0xffffffff, "CMI9739",           patch_cm9739,   NULL },
+{ 0x434d4969, 0xffffffff, "CMI9780",           patch_cm9780,   NULL },
 { 0x434d4978, 0xffffffff, "CMI9761",           patch_cm9761,   NULL },
 { 0x434d4982, 0xffffffff, "CMI9761",           patch_cm9761,   NULL },
 { 0x434d4983, 0xffffffff, "CMI9761",           patch_cm9761,   NULL },
@@ -149,7 +150,7 @@ static const ac97_codec_id_t snd_ac97_codec_ids[] = {
 { 0x4e534331, 0xffffffff, "LM4549",            NULL,           NULL },
 { 0x4e534350, 0xffffffff, "LM4550",            NULL,           NULL },
 { 0x50534304, 0xffffffff, "UCB1400",           NULL,           NULL },
-{ 0x53494c20, 0xffffffe0, "Si3036,8",          NULL,           mpatch_si3036 },
+{ 0x53494c20, 0xffffffe0, "Si3036,8",          mpatch_si3036,  mpatch_si3036, AC97_MODEM_PATCH },
 { 0x54524102, 0xffffffff, "TR28022",           NULL,           NULL },
 { 0x54524106, 0xffffffff, "TR28026",           NULL,           NULL },
 { 0x54524108, 0xffffffff, "TR28028",           patch_tritech_tr28028,  NULL }, // added by xin jin [07/09/99]
@@ -462,12 +463,14 @@ int snd_ac97_get_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * u
 {
        ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
        struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value;
-       unsigned short val;
+       unsigned short val, bitmask;
        
+       for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
+               ;
        val = snd_ac97_read_cache(ac97, e->reg);
-       ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (e->mask - 1);
+       ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1);
        if (e->shift_l != e->shift_r)
-               ucontrol->value.enumerated.item[1] = (val >> e->shift_r) & (e->mask - 1);
+               ucontrol->value.enumerated.item[1] = (val >> e->shift_r) & (bitmask - 1);
 
        return 0;
 }
@@ -477,17 +480,19 @@ int snd_ac97_put_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * u
        ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
        struct ac97_enum *e = (struct ac97_enum *)kcontrol->private_value;
        unsigned short val;
-       unsigned short mask;
+       unsigned short mask, bitmask;
        
+       for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
+               ;
        if (ucontrol->value.enumerated.item[0] > e->mask - 1)
                return -EINVAL;
        val = ucontrol->value.enumerated.item[0] << e->shift_l;
-       mask = (e->mask - 1) << e->shift_l;
+       mask = (bitmask - 1) << e->shift_l;
        if (e->shift_l != e->shift_r) {
                if (ucontrol->value.enumerated.item[1] > e->mask - 1)
                        return -EINVAL;
                val |= ucontrol->value.enumerated.item[1] << e->shift_r;
-               mask |= (e->mask - 1) << e->shift_r;
+               mask |= (bitmask - 1) << e->shift_r;
        }
        return snd_ac97_update_bits(ac97, e->reg, mask, val);
 }
@@ -658,14 +663,14 @@ AC97_SINGLE("LFE Playback Switch", AC97_CENTER_LFE_MASTER, 15, 1, 1),
 AC97_SINGLE("LFE Playback Volume", AC97_CENTER_LFE_MASTER, 8, 31, 1)
 };
 
-static const snd_kcontrol_new_t snd_ac97_controls_surround[2] = {
-AC97_DOUBLE("Surround Playback Switch", AC97_SURROUND_MASTER, 15, 7, 1, 1),
-AC97_DOUBLE("Surround Playback Volume", AC97_SURROUND_MASTER, 8, 0, 31, 1),
-};
-
 static const snd_kcontrol_new_t snd_ac97_control_eapd =
 AC97_SINGLE("External Amplifier", AC97_POWERDOWN, 15, 1, 1);
 
+static const snd_kcontrol_new_t snd_ac97_controls_modem_switches[2] = {
+AC97_SINGLE("Off-hook Switch", AC97_GPIO_STATUS, 0, 1, 0),
+AC97_SINGLE("Caller ID Switch", AC97_GPIO_STATUS, 2, 1, 0)
+};
+
 /* change the existing EAPD control as inverted */
 static void set_inv_eapd(ac97_t *ac97, snd_kcontrol_t *kctl)
 {
@@ -1072,9 +1077,9 @@ static void check_volume_resolution(ac97_t *ac97, int reg, unsigned char *lo_max
                unsigned short val;
                snd_ac97_write(ac97, reg, 0x8080 | cbit[i] | (cbit[i] << 8));
                val = snd_ac97_read(ac97, reg);
-               if (! *lo_max && (val & cbit[i]))
+               if (! *lo_max && (val & 0x7f) == cbit[i])
                        *lo_max = max[i];
-               if (! *hi_max && (val & (cbit[i] << 8)))
+               if (! *hi_max && ((val >> 8) & 0x7f) == cbit[i])
                        *hi_max = max[i];
                if (*lo_max && *hi_max)
                        break;
@@ -1526,13 +1531,25 @@ static int snd_ac97_mixer_build(ac97_t * ac97)
 
 static int snd_ac97_modem_build(snd_card_t * card, ac97_t * ac97)
 {
-       /* TODO */
+       int err, idx;
+
        //printk("AC97_GPIO_CFG = %x\n",snd_ac97_read(ac97,AC97_GPIO_CFG));
        snd_ac97_write(ac97, AC97_GPIO_CFG, 0xffff & ~(AC97_GPIO_LINE1_OH));
        snd_ac97_write(ac97, AC97_GPIO_POLARITY, 0xffff & ~(AC97_GPIO_LINE1_OH));
        snd_ac97_write(ac97, AC97_GPIO_STICKY, 0xffff);
        snd_ac97_write(ac97, AC97_GPIO_WAKEUP, 0x0);
        snd_ac97_write(ac97, AC97_MISC_AFE, 0x0);
+
+       /* build modem switches */
+       for (idx = 0; idx < ARRAY_SIZE(snd_ac97_controls_modem_switches); idx++)
+               if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_modem_switches[idx], ac97))) < 0)
+                       return err;
+
+       /* build chip specific controls */
+       if (ac97->build_ops->build_specific)
+               if ((err = ac97->build_ops->build_specific(ac97)) < 0)
+                       return err;
+
        return 0;
 }
 
@@ -1872,7 +1889,11 @@ int snd_ac97_mixer(ac97_bus_t *bus, ac97_template_t *template, ac97_t **rac97)
                        goto __access_ok;
        }
 
-       snd_ac97_write(ac97, AC97_RESET, 0);    /* reset to defaults */
+       /* reset to defaults */
+       if (!(ac97->scaps & AC97_SCAP_SKIP_AUDIO))
+               snd_ac97_write(ac97, AC97_RESET, 0);
+       if (!(ac97->scaps & AC97_SCAP_SKIP_MODEM))
+               snd_ac97_write(ac97, AC97_EXTENDED_MID, 0);
        if (bus->ops->wait)
                bus->ops->wait(ac97);
        else {
@@ -1964,21 +1985,21 @@ int snd_ac97_mixer(ac97_bus_t *bus, ac97_template_t *template, ac97_t **rac97)
                /* note: it's important to set the rate at first */
                tmp = AC97_MEA_GPIO;
                if (ac97->ext_mid & AC97_MEI_LINE1) {
-                       snd_ac97_write_cache(ac97, AC97_LINE1_RATE, 12000);
+                       snd_ac97_write_cache(ac97, AC97_LINE1_RATE, 8000);
                        tmp |= AC97_MEA_ADC1 | AC97_MEA_DAC1;
                }
                if (ac97->ext_mid & AC97_MEI_LINE2) {
-                       snd_ac97_write_cache(ac97, AC97_LINE2_RATE, 12000);
+                       snd_ac97_write_cache(ac97, AC97_LINE2_RATE, 8000);
                        tmp |= AC97_MEA_ADC2 | AC97_MEA_DAC2;
                }
                if (ac97->ext_mid & AC97_MEI_HANDSET) {
-                       snd_ac97_write_cache(ac97, AC97_HANDSET_RATE, 12000);
+                       snd_ac97_write_cache(ac97, AC97_HANDSET_RATE, 8000);
                        tmp |= AC97_MEA_HADC | AC97_MEA_HDAC;
                }
-               snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0xff00 & ~(tmp << 8));
+               snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0);
                udelay(100);
                /* nothing should be in powerdown mode */
-               snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0xff00 & ~(tmp << 8));
+               snd_ac97_write_cache(ac97, AC97_EXTENDED_MSTATUS, 0);
                end_time = jiffies + (HZ / 10);
                do {
                        if ((snd_ac97_read(ac97, AC97_EXTENDED_MSTATUS) & tmp) == tmp)
@@ -2521,11 +2542,11 @@ int snd_ac97_tune_hardware(ac97_t *ac97, struct ac97_quirk *quirk, const char *o
                return result;
        }
 
-       for (; quirk->vendor; quirk++) {
-               if (quirk->vendor != ac97->subsystem_vendor)
+       for (; quirk->subvendor; quirk++) {
+               if (quirk->subvendor != ac97->subsystem_vendor)
                        continue;
-               if ((! quirk->mask && quirk->device == ac97->subsystem_device) ||
-                   quirk->device == (quirk->mask & ac97->subsystem_device)) {
+               if ((! quirk->mask && quirk->subdevice == ac97->subsystem_device) ||
+                   quirk->subdevice == (quirk->mask & ac97->subsystem_device)) {
                        if (quirk->codec_id && quirk->codec_id != ac97->id)
                                continue;
                        snd_printdd("ac97 quirk for %s (%04x:%04x)\n", quirk->name, ac97->subsystem_vendor, ac97->subsystem_device);
index 13c34a5d8206dd5a92319a49459019683a49d6f5..a15eb8522b7cc90f098362bcb49e4242b6b4fe41 100644 (file)
@@ -64,6 +64,116 @@ static int ac97_update_bits_page(ac97_t *ac97, unsigned short reg, unsigned shor
        return ret;
 }
 
+/*
+ * shared line-in/mic controls
+ */
+static int ac97_enum_text_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo,
+                              const char **texts, unsigned int nums)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = nums;
+       if (uinfo->value.enumerated.item > nums - 1)
+               uinfo->value.enumerated.item = nums - 1;
+       strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+       return 0;
+}
+
+static int ac97_surround_jack_mode_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+       static const char *texts[] = { "Shared", "Independent" };
+       return ac97_enum_text_info(kcontrol, uinfo, texts, 2);
+}
+
+static int ac97_surround_jack_mode_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.enumerated.item[0] = ac97->indep_surround;
+       return 0;
+}
+
+static int ac97_surround_jack_mode_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+       unsigned char indep = !!ucontrol->value.enumerated.item[0];
+
+       if (indep != ac97->indep_surround) {
+               ac97->indep_surround = indep;
+               if (ac97->build_ops->update_jacks)
+                       ac97->build_ops->update_jacks(ac97);
+               return 1;
+       }
+       return 0;
+}
+
+static int ac97_channel_mode_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+       static const char *texts[] = { "2ch", "4ch", "6ch" };
+       if (kcontrol->private_value)
+               return ac97_enum_text_info(kcontrol, uinfo, texts, 2); /* 4ch only */
+       return ac97_enum_text_info(kcontrol, uinfo, texts, 3);
+}
+
+static int ac97_channel_mode_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.enumerated.item[0] = ac97->channel_mode;
+       return 0;
+}
+
+static int ac97_channel_mode_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
+       unsigned char mode = ucontrol->value.enumerated.item[0];
+
+       if (mode != ac97->channel_mode) {
+               ac97->channel_mode = mode;
+               if (ac97->build_ops->update_jacks)
+                       ac97->build_ops->update_jacks(ac97);
+               return 1;
+       }
+       return 0;
+}
+
+#define AC97_SURROUND_JACK_MODE_CTL \
+       { \
+               .iface  = SNDRV_CTL_ELEM_IFACE_MIXER, \
+               .name   = "Surround Jack Mode", \
+               .info = ac97_surround_jack_mode_info, \
+               .get = ac97_surround_jack_mode_get, \
+               .put = ac97_surround_jack_mode_put, \
+       }
+#define AC97_CHANNEL_MODE_CTL \
+       { \
+               .iface  = SNDRV_CTL_ELEM_IFACE_MIXER, \
+               .name   = "Channel Mode", \
+               .info = ac97_channel_mode_info, \
+               .get = ac97_channel_mode_get, \
+               .put = ac97_channel_mode_put, \
+       }
+#define AC97_CHANNEL_MODE_4CH_CTL \
+       { \
+               .iface  = SNDRV_CTL_ELEM_IFACE_MIXER, \
+               .name   = "Channel Mode", \
+               .info = ac97_channel_mode_info, \
+               .get = ac97_channel_mode_get, \
+               .put = ac97_channel_mode_put, \
+               .private_value = 1, \
+       }
+
+static inline int is_shared_linein(ac97_t *ac97)
+{
+       return ! ac97->indep_surround && ac97->channel_mode >= 1;
+}
+
+static inline int is_shared_micin(ac97_t *ac97)
+{
+       return ! ac97->indep_surround && ac97->channel_mode >= 2;
+}
+
+
 /* The following snd_ac97_ymf753_... items added by David Shust (dshust@shustring.com) */
 
 /* It is possible to indicate to the Yamaha YMF753 the type of speakers being used. */
@@ -1390,6 +1500,16 @@ static int snd_ac97_ad1888_downmix_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_va
                                    AC97_AD198X_DMIX0 | AC97_AD198X_DMIX1, val);
 }
 
+static void ad1888_update_jacks(ac97_t *ac97)
+{
+       /* shared Line-In */
+       snd_ac97_update_bits(ac97, AC97_AD_MISC, 1 << 12,
+                            is_shared_linein(ac97) ? 0 : 1 << 12);
+       /* shared Mic */
+       snd_ac97_update_bits(ac97, AC97_AD_MISC, 1 << 11,
+                            is_shared_micin(ac97) ? 0 : 1 << 11);
+}
+
 static const snd_kcontrol_new_t snd_ac97_ad1888_controls[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1406,8 +1526,8 @@ static const snd_kcontrol_new_t snd_ac97_ad1888_controls[] = {
                .get = snd_ac97_ad1888_downmix_get,
                .put = snd_ac97_ad1888_downmix_put
        },
-       AC97_SINGLE("Surround Jack as Input", AC97_AD_MISC, 12, 1, 0),
-       AC97_SINGLE("Center/LFE Jack as Input", AC97_AD_MISC, 11, 1, 0),
+       AC97_SURROUND_JACK_MODE_CTL,
+       AC97_CHANNEL_MODE_CTL,
 };
 
 static int patch_ad1888_specific(ac97_t *ac97)
@@ -1422,8 +1542,9 @@ static struct snd_ac97_build_ops patch_ad1888_build_ops = {
        .build_post_spdif = patch_ad198x_post_spdif,
        .build_specific = patch_ad1888_specific,
 #ifdef CONFIG_PM
-       .resume = ad18xx_resume
+       .resume = ad18xx_resume,
 #endif
+       .update_jacks = ad1888_update_jacks,
 };
 
 int patch_ad1888(ac97_t * ac97)
@@ -1459,8 +1580,9 @@ static struct snd_ac97_build_ops patch_ad1980_build_ops = {
        .build_post_spdif = patch_ad198x_post_spdif,
        .build_specific = patch_ad1980_specific,
 #ifdef CONFIG_PM
-       .resume = ad18xx_resume
+       .resume = ad18xx_resume,
 #endif
+       .update_jacks = ad1888_update_jacks,
 };
 
 int patch_ad1980(ac97_t * ac97)
@@ -1471,10 +1593,21 @@ int patch_ad1980(ac97_t * ac97)
 }
 
 static const snd_kcontrol_new_t snd_ac97_ad1985_controls[] = {
-       AC97_SINGLE("Center/LFE Jack as Mic", AC97_AD_SERIAL_CFG, 9, 1, 0),
        AC97_SINGLE("Exchange Center/LFE", AC97_AD_SERIAL_CFG, 3, 1, 0)
 };
 
+static void ad1985_update_jacks(ac97_t *ac97)
+{
+       /* shared Line-In */
+       snd_ac97_update_bits(ac97, AC97_AD_MISC, 1 << 12,
+                            is_shared_linein(ac97) ? 0 : 1 << 12);
+       /* shared Mic */
+       snd_ac97_update_bits(ac97, AC97_AD_MISC, 1 << 11,
+                            is_shared_micin(ac97) ? 0 : 1 << 11);
+       snd_ac97_update_bits(ac97, AC97_AD_SERIAL_CFG, 1 << 9,
+                            is_shared_micin(ac97) ? 0 : 1 << 9);
+}
+
 static int patch_ad1985_specific(ac97_t *ac97)
 {
        int err;
@@ -1488,8 +1621,9 @@ static struct snd_ac97_build_ops patch_ad1985_build_ops = {
        .build_post_spdif = patch_ad198x_post_spdif,
        .build_specific = patch_ad1985_specific,
 #ifdef CONFIG_PM
-       .resume = ad18xx_resume
+       .resume = ad18xx_resume,
 #endif
+       .update_jacks = ad1985_update_jacks,
 };
 
 int patch_ad1985(ac97_t * ac97)
@@ -1521,31 +1655,25 @@ int patch_ad1985(ac97_t * ac97)
 /*
  * realtek ALC65x/850 codecs
  */
-static int snd_ac97_alc650_mic_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-        ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
-        ucontrol->value.integer.value[0] = (ac97->regs[AC97_ALC650_MULTICH] >> 10) & 1;
-        return 0;
-}
-
-static int snd_ac97_alc650_mic_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
+static void alc650_update_jacks(ac97_t *ac97)
 {
-        ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
-       int change, val;
-       val = !!(snd_ac97_read(ac97, AC97_ALC650_MULTICH) & (1 << 10));
-       change = (ucontrol->value.integer.value[0] != val);
-       if (change) {
-               /* disable/enable vref */
-               snd_ac97_update_bits(ac97, AC97_ALC650_CLOCK, 1 << 12,
-                                    ucontrol->value.integer.value[0] ? (1 << 12) : 0);
-               /* turn on/off center-on-mic */
-               snd_ac97_update_bits(ac97, AC97_ALC650_MULTICH, 1 << 10,
-                                    ucontrol->value.integer.value[0] ? (1 << 10) : 0);
-               /* GPIO0 high for mic */
-               snd_ac97_update_bits(ac97, AC97_ALC650_GPIO_STATUS, 0x100,
-                                    ucontrol->value.integer.value[0] ? 0 : 0x100);
-        }
-        return change;
+       int shared;
+       
+       /* shared Line-In */
+       shared = is_shared_linein(ac97);
+       snd_ac97_update_bits(ac97, AC97_ALC650_MULTICH, 1 << 9,
+                            shared ? (1 << 9) : 0);
+       /* update shared Mic */
+       shared = is_shared_micin(ac97);
+       /* disable/enable vref */
+       snd_ac97_update_bits(ac97, AC97_ALC650_CLOCK, 1 << 12,
+                            shared ? (1 << 12) : 0);
+       /* turn on/off center-on-mic */
+       snd_ac97_update_bits(ac97, AC97_ALC650_MULTICH, 1 << 10,
+                            shared ? (1 << 10) : 0);
+       /* GPIO0 high for mic */
+       snd_ac97_update_bits(ac97, AC97_ALC650_GPIO_STATUS, 0x100,
+                            shared ? 0 : 0x100);
 }
 
 static const snd_kcontrol_new_t snd_ac97_controls_alc650[] = {
@@ -1558,8 +1686,8 @@ static const snd_kcontrol_new_t snd_ac97_controls_alc650[] = {
        /* 6: Independent Master Volume Right */
        /* 7: Independent Master Volume Left */
        /* 8: reserved */
-       AC97_SINGLE("Line-In As Surround", AC97_ALC650_MULTICH, 9, 1, 0),
-       /* 10: mic, see below */
+       /* 9: Line-In/Surround share */
+       /* 10: Mic/CLFE share */
        /* 11-13: in IEC958 controls */
        AC97_SINGLE("Swap Surround Slot", AC97_ALC650_MULTICH, 14, 1, 0),
 #if 0 /* always set in patch_alc650 */
@@ -1570,14 +1698,8 @@ static const snd_kcontrol_new_t snd_ac97_controls_alc650[] = {
        AC97_SINGLE("Center/LFE DAC Switch", AC97_ALC650_LFE_DAC_VOL, 15, 1, 1),
        AC97_DOUBLE("Center/LFE DAC Volume", AC97_ALC650_LFE_DAC_VOL, 8, 0, 31, 1),
 #endif
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Mic As Center/LFE",
-               .info = snd_ac97_info_volsw,
-               .get = snd_ac97_alc650_mic_get,
-               .put = snd_ac97_alc650_mic_put,
-               .private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */
-       },
+       AC97_SURROUND_JACK_MODE_CTL,
+       AC97_CHANNEL_MODE_CTL,
 };
 
 static const snd_kcontrol_new_t snd_ac97_spdif_controls_alc650[] = {
@@ -1601,7 +1723,8 @@ static int patch_alc650_specific(ac97_t * ac97)
 }
 
 static struct snd_ac97_build_ops patch_alc650_ops = {
-       .build_specific = patch_alc650_specific
+       .build_specific = patch_alc650_specific,
+       .update_jacks = alc650_update_jacks
 };
 
 int patch_alc650(ac97_t * ac97)
@@ -1659,37 +1782,27 @@ int patch_alc650(ac97_t * ac97)
        return 0;
 }
 
-static int snd_ac97_alc655_mic_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-        ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
-        ucontrol->value.integer.value[0] = (ac97->regs[AC97_ALC650_MULTICH] >> 10) & 1;
-        return 0;
-}
-
-static int snd_ac97_alc655_mic_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
+static void alc655_update_jacks(ac97_t *ac97)
 {
-        ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
-
+       int shared;
+       
+       /* shared Line-In */
+       shared = is_shared_linein(ac97);
+       ac97_update_bits_page(ac97, AC97_ALC650_MULTICH, 1 << 9,
+                             shared ? (1 << 9) : 0, 0);
+       /* update shared mic */
+       shared = is_shared_micin(ac97);
        /* misc control; vrefout disable */
        snd_ac97_update_bits(ac97, AC97_ALC650_CLOCK, 1 << 12,
-                            ucontrol->value.integer.value[0] ? (1 << 12) : 0);
-       return ac97_update_bits_page(ac97, AC97_ALC650_MULTICH, 1 << 10,
-                                    ucontrol->value.integer.value[0] ? (1 << 10) : 0,
-                                    0);
+                            shared ? (1 << 12) : 0);
+       ac97_update_bits_page(ac97, AC97_ALC650_MULTICH, 1 << 10,
+                             shared ? (1 << 10) : 0, 0);
 }
 
-
 static const snd_kcontrol_new_t snd_ac97_controls_alc655[] = {
        AC97_PAGE_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0, 0),
-       AC97_PAGE_SINGLE("Line-In As Surround", AC97_ALC650_MULTICH, 9, 1, 0, 0),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Mic As Center/LFE",
-               .info = snd_ac97_info_volsw,
-               .get = snd_ac97_alc655_mic_get,
-               .put = snd_ac97_alc655_mic_put,
-               .private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */
-       },
+       AC97_SURROUND_JACK_MODE_CTL,
+       AC97_CHANNEL_MODE_CTL,
 };
 
 static int alc655_iec958_route_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
@@ -1759,7 +1872,8 @@ static int patch_alc655_specific(ac97_t * ac97)
 }
 
 static struct snd_ac97_build_ops patch_alc655_ops = {
-       .build_specific = patch_alc655_specific
+       .build_specific = patch_alc655_specific,
+       .update_jacks = alc655_update_jacks
 };
 
 int patch_alc655(ac97_t * ac97)
@@ -1798,63 +1912,33 @@ int patch_alc655(ac97_t * ac97)
 #define AC97_ALC850_JACK_SELECT        0x76
 #define AC97_ALC850_MISC1      0x7a
 
-static int ac97_alc850_surround_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-        ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
-        ucontrol->value.integer.value[0] = ((ac97->regs[AC97_ALC850_JACK_SELECT] >> 12) & 7) == 2;
-        return 0;
-}
-
-static int ac97_alc850_surround_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
+static void alc850_update_jacks(ac97_t *ac97)
 {
-        ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
-
+       int shared;
+       
+       /* shared Line-In */
+       shared = is_shared_linein(ac97);
        /* SURR 1kOhm (bit4), Amp (bit5) */
        snd_ac97_update_bits(ac97, AC97_ALC850_MISC1, (1<<4)|(1<<5),
-                            ucontrol->value.integer.value[0] ? (1<<5) : (1<<4));
+                            shared ? (1<<5) : (1<<4));
        /* LINE-IN = 0, SURROUND = 2 */
-       return snd_ac97_update_bits(ac97, AC97_ALC850_JACK_SELECT, 7 << 12,
-                                   ucontrol->value.integer.value[0] ? (2<<12) : (0<<12));
-}
-
-static int ac97_alc850_mic_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-        ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
-        ucontrol->value.integer.value[0] = ((ac97->regs[AC97_ALC850_JACK_SELECT] >> 4) & 7) == 2;
-        return 0;
-}
-
-static int ac97_alc850_mic_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-        ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
-
+       snd_ac97_update_bits(ac97, AC97_ALC850_JACK_SELECT, 7 << 12,
+                            shared ? (2<<12) : (0<<12));
+       /* update shared mic */
+       shared = is_shared_micin(ac97);
        /* Vref disable (bit12), 1kOhm (bit13) */
        snd_ac97_update_bits(ac97, AC97_ALC850_MISC1, (1<<12)|(1<<13),
-                            ucontrol->value.integer.value[0] ? (1<<12) : (1<<13));
+                            shared ? (1<<12) : (1<<13));
        /* MIC-IN = 1, CENTER-LFE = 2 */
-       return snd_ac97_update_bits(ac97, AC97_ALC850_JACK_SELECT, 7 << 4,
-                                   ucontrol->value.integer.value[0] ? (2<<4) : (1<<4));
+       snd_ac97_update_bits(ac97, AC97_ALC850_JACK_SELECT, 7 << 4,
+                            shared ? (2<<4) : (1<<4));
 }
 
 static const snd_kcontrol_new_t snd_ac97_controls_alc850[] = {
        AC97_PAGE_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0, 0),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Line-In As Surround",
-               .info = snd_ac97_info_volsw,
-               .get = ac97_alc850_surround_get,
-               .put = ac97_alc850_surround_put,
-               .private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */
-       },
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Mic As Center/LFE",
-               .info = snd_ac97_info_volsw,
-               .get = ac97_alc850_mic_get,
-               .put = ac97_alc850_mic_put,
-               .private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */
-       },
-
+       AC97_SINGLE("Mic Front Input Switch", AC97_ALC850_JACK_SELECT, 15, 1, 1),
+       AC97_SURROUND_JACK_MODE_CTL,
+       AC97_CHANNEL_MODE_CTL,
 };
 
 static int patch_alc850_specific(ac97_t *ac97)
@@ -1871,7 +1955,8 @@ static int patch_alc850_specific(ac97_t *ac97)
 }
 
 static struct snd_ac97_build_ops patch_alc850_ops = {
-       .build_specific = patch_alc850_specific
+       .build_specific = patch_alc850_specific,
+       .update_jacks = alc850_update_jacks
 };
 
 int patch_alc850(ac97_t *ac97)
@@ -1911,9 +1996,17 @@ int patch_alc850(ac97_t *ac97)
 /*
  * C-Media CM97xx codecs
  */
+static void cm9738_update_jacks(ac97_t *ac97)
+{
+       /* shared Line-In */
+       snd_ac97_update_bits(ac97, AC97_CM9738_VENDOR_CTRL, 1 << 10,
+                            is_shared_linein(ac97) ? (1 << 10) : 0);
+}
+
 static const snd_kcontrol_new_t snd_ac97_cm9738_controls[] = {
-       AC97_SINGLE("Line-In As Surround", AC97_CM9738_VENDOR_CTRL, 10, 1, 0),
        AC97_SINGLE("Duplicate Front", AC97_CM9738_VENDOR_CTRL, 13, 1, 0),
+       AC97_SURROUND_JACK_MODE_CTL,
+       AC97_CHANNEL_MODE_4CH_CTL,
 };
 
 static int patch_cm9738_specific(ac97_t * ac97)
@@ -1922,7 +2015,8 @@ static int patch_cm9738_specific(ac97_t * ac97)
 }
 
 static struct snd_ac97_build_ops patch_cm9738_ops = {
-       .build_specific = patch_cm9738_specific
+       .build_specific = patch_cm9738_specific,
+       .update_jacks = cm9738_update_jacks
 };
 
 int patch_cm9738(ac97_t * ac97)
@@ -1986,34 +2080,19 @@ static const snd_kcontrol_new_t snd_ac97_cm9739_controls_spdif[] = {
        /* BIT 8: SPD32 - 32bit SPDIF - not supported yet */
 };
 
-static int snd_ac97_cm9739_center_mic_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-       ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
-       if (ac97->regs[AC97_CM9739_MULTI_CHAN] & 0x1000)
-               ucontrol->value.integer.value[0] = 1;
-       else
-               ucontrol->value.integer.value[0] = 0;
-       return 0;
-}
-
-static int snd_ac97_cm9739_center_mic_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+static void cm9739_update_jacks(ac97_t *ac97)
 {
-       ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
-       return snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 0x3000,
-                                   ucontrol->value.integer.value[0] ? 
-                                   0x1000 : 0x2000);
+       /* shared Line-In */
+       snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 1 << 10,
+                            is_shared_linein(ac97) ? (1 << 10) : 0);
+       /* shared Mic */
+       snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 0x3000,
+                            is_shared_micin(ac97) ? 0x1000 : 0x2000);
 }
 
 static const snd_kcontrol_new_t snd_ac97_cm9739_controls[] = {
-       AC97_SINGLE("Line-In As Surround", AC97_CM9739_MULTI_CHAN, 10, 1, 0),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Mic As Center/LFE",
-               .info = snd_ac97_info_volsw,
-               .get = snd_ac97_cm9739_center_mic_get,
-               .put = snd_ac97_cm9739_center_mic_put,
-               .private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */
-       },
+       AC97_SURROUND_JACK_MODE_CTL,
+       AC97_CHANNEL_MODE_CTL,
 };
 
 static int patch_cm9739_specific(ac97_t * ac97)
@@ -2028,7 +2107,8 @@ static int patch_cm9739_post_spdif(ac97_t * ac97)
 
 static struct snd_ac97_build_ops patch_cm9739_ops = {
        .build_specific = patch_cm9739_specific,
-       .build_post_spdif = patch_cm9739_post_spdif
+       .build_post_spdif = patch_cm9739_post_spdif,
+       .update_jacks = cm9739_update_jacks
 };
 
 int patch_cm9739(ac97_t * ac97)
@@ -2087,71 +2167,97 @@ int patch_cm9739(ac97_t * ac97)
 }
 
 #define AC97_CM9761_MULTI_CHAN 0x64
+#define AC97_CM9761_FUNC       0x66
 #define AC97_CM9761_SPDIF_CTRL 0x6c
 
-static int snd_ac97_cm9761_linein_rear_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-       ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
-       if (ac97->regs[AC97_CM9739_MULTI_CHAN] & 0x0400)
-               ucontrol->value.integer.value[0] = 1;
-       else
-               ucontrol->value.integer.value[0] = 0;
-       return 0;
-}
-
-static int snd_ac97_cm9761_linein_rear_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+static void cm9761_update_jacks(ac97_t *ac97)
 {
-       ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
-       unsigned short vals[2][2] = {
+       unsigned short surr_vals[2][2] = {
                { 0x0008, 0x0400 }, /* off, on */
                { 0x0000, 0x0408 }, /* off, on (9761-82 rev.B) */
        };
-       return snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 0x0408,
-                                   vals[ac97->spec.dev_flags][!!ucontrol->value.integer.value[0]]);
+       unsigned short clfe_vals[2][2] = {
+               { 0x2000, 0x1880 }, /* off, on */
+               { 0x1000, 0x2880 }, /* off, on (9761-82 rev.B) */
+       };
+
+       /* shared Line-In */
+       snd_ac97_update_bits(ac97, AC97_CM9761_MULTI_CHAN, 0x0408,
+                            surr_vals[ac97->spec.dev_flags][is_shared_linein(ac97)]);
+       /* shared Mic */
+       snd_ac97_update_bits(ac97, AC97_CM9761_MULTI_CHAN, 0x3880,
+                            clfe_vals[ac97->spec.dev_flags][is_shared_micin(ac97)]);
+}
+
+static const snd_kcontrol_new_t snd_ac97_cm9761_controls[] = {
+       AC97_SURROUND_JACK_MODE_CTL,
+       AC97_CHANNEL_MODE_CTL,
+};
+
+static int cm9761_spdif_out_source_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+       static char *texts[] = { "AC-Link", "ADC", "SPDIF-In" };
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 3;
+       if (uinfo->value.enumerated.item > 2)
+               uinfo->value.enumerated.item = 2;
+       strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+       return 0;
 }
 
-static int snd_ac97_cm9761_center_mic_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+static int cm9761_spdif_out_source_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
 {
        ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
-       if (ac97->regs[AC97_CM9739_MULTI_CHAN] & 0x1000)
-               ucontrol->value.integer.value[0] = 1;
+
+       if (ac97->regs[AC97_CM9761_FUNC] & 0x1)
+               ucontrol->value.enumerated.item[0] = 2; /* SPDIF-loopback */
+       else if (ac97->regs[AC97_CM9761_SPDIF_CTRL] & 0x2)
+               ucontrol->value.enumerated.item[0] = 1; /* ADC loopback */
        else
-               ucontrol->value.integer.value[0] = 0;
-       if (ac97->spec.dev_flags) /* 9761-82 rev.B */
-               ucontrol->value.integer.value[0] = !ucontrol->value.integer.value[0];
+               ucontrol->value.enumerated.item[0] = 0; /* AC-link */
        return 0;
 }
 
-static int snd_ac97_cm9761_center_mic_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+static int cm9761_spdif_out_source_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
 {
        ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
-       unsigned short vals[2][2] = {
-               { 0x2000, 0x1880 }, /* off, on */
-               { 0x1000, 0x2880 }, /* off, on (9761-82 rev.B) */
-       };
-       return snd_ac97_update_bits(ac97, AC97_CM9739_MULTI_CHAN, 0x3880,
-                                   vals[ac97->spec.dev_flags][!!ucontrol->value.integer.value[0]]);
+
+       if (ucontrol->value.enumerated.item[0] == 2)
+               return snd_ac97_update_bits(ac97, AC97_CM9761_FUNC, 0x1, 0x1);
+       snd_ac97_update_bits(ac97, AC97_CM9761_FUNC, 0x1, 0);
+       return snd_ac97_update_bits(ac97, AC97_CM9761_SPDIF_CTRL, 0x2,
+                                   ucontrol->value.enumerated.item[0] == 1 ? 0x2 : 0);
 }
 
-static const snd_kcontrol_new_t snd_ac97_cm9761_controls[] = {
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Line-In As Surround",
-               .info = snd_ac97_info_volsw,
-               .get = snd_ac97_cm9761_linein_rear_get,
-               .put = snd_ac97_cm9761_linein_rear_put,
-               .private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */
-       },
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Mic As Center/LFE",
-               .info = snd_ac97_info_volsw,
-               .get = snd_ac97_cm9761_center_mic_get,
-               .put = snd_ac97_cm9761_center_mic_put,
-               .private_value = AC97_SINGLE_VALUE(0, 0, 1, 0) /* only mask needed */
+static const char *cm9761_dac_clock[] = { "AC-Link", "SPDIF-In", "Both" };
+static const struct ac97_enum cm9761_dac_clock_enum =
+       AC97_ENUM_SINGLE(AC97_CM9761_SPDIF_CTRL, 9, 3, cm9761_dac_clock);
+
+static const snd_kcontrol_new_t snd_ac97_cm9761_controls_spdif[] = {
+       { /* BIT 1: SPDIFS */
+               .iface  = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name   = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
+               .info = cm9761_spdif_out_source_info,
+               .get = cm9761_spdif_out_source_get,
+               .put = cm9761_spdif_out_source_put,
        },
+       /* BIT 2: IG_SPIV */
+       AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,NONE) "Valid Switch", AC97_CM9761_SPDIF_CTRL, 2, 1, 0),
+       /* BIT 3: SPI2F */
+       AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,NONE) "Monitor", AC97_CM9761_SPDIF_CTRL, 3, 1, 0), 
+       /* BIT 4: SPI2SDI */
+       AC97_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), AC97_CM9761_SPDIF_CTRL, 4, 1, 0),
+       /* BIT 9-10: DAC_CTL */
+       AC97_ENUM("DAC Clock Source", cm9761_dac_clock_enum),
 };
 
+static int patch_cm9761_post_spdif(ac97_t * ac97)
+{
+       return patch_build_controls(ac97, snd_ac97_cm9761_controls_spdif, ARRAY_SIZE(snd_ac97_cm9761_controls_spdif));
+}
+
 static int patch_cm9761_specific(ac97_t * ac97)
 {
        return patch_build_controls(ac97, snd_ac97_cm9761_controls, ARRAY_SIZE(snd_ac97_cm9761_controls));
@@ -2159,7 +2265,8 @@ static int patch_cm9761_specific(ac97_t * ac97)
 
 static struct snd_ac97_build_ops patch_cm9761_ops = {
        .build_specific = patch_cm9761_specific,
-       .build_post_spdif = patch_cm9739_post_spdif /* hope it's identical... */
+       .build_post_spdif = patch_cm9761_post_spdif,
+       .update_jacks = cm9761_update_jacks
 };
 
 int patch_cm9761(ac97_t *ac97)
@@ -2193,24 +2300,25 @@ int patch_cm9761(ac97_t *ac97)
        /* to be sure: we overwrite the ext status bits */
        snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, 0x05c0);
        /* Don't set 0x0200 here.  This results in the silent analog output */
-       snd_ac97_write_cache(ac97, AC97_CM9761_SPDIF_CTRL, 0x0009);
+       snd_ac97_write_cache(ac97, AC97_CM9761_SPDIF_CTRL, 0x0001); /* enable spdif-in */
        ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000; /* 48k only */
 
        /* set-up multi channel */
        /* bit 15: pc master beep off
-        * bit 14: ??
+        * bit 14: pin47 = EAPD/SPDIF
         * bit 13: vref ctl [= cm9739]
-        * bit 12: center/mic [= cm9739] (reverted on rev B)
-        * bit 11: ?? (mic/center/lfe) (reverted on rev B)
-        * bit 10: suddound/line [= cm9739]
-        * bit  9: mix 2 surround
-        * bit  8: ?
-        * bit  7: ?? (mic/center/lfe)
-        * bit  4: ?? (front)
-        * bit  3: ?? (line-in/rear share) (revereted with rev B)
-        * bit  2: ?? (surround)
-        * bit  1: front mic
-        * bit  0: mic boost
+        * bit 12: CLFE control (reverted on rev B)
+        * bit 11: Mic/center share (reverted on rev B)
+        * bit 10: suddound/line share
+        * bit  9: Analog-in mix -> surround
+        * bit  8: Analog-in mix -> CLFE
+        * bit  7: Mic/LFE share (mic/center/lfe)
+        * bit  5: vref select (9761A)
+        * bit  4: front control
+        * bit  3: surround control (revereted with rev B)
+        * bit  2: front mic
+        * bit  1: stereo mic
+        * bit  0: mic boost level (0=20dB, 1=30dB)
         */
 
 #if 0
@@ -2230,6 +2338,47 @@ int patch_cm9761(ac97_t *ac97)
        return 0;
 }
        
+#define AC97_CM9780_SIDE       0x60
+#define AC97_CM9780_JACK       0x62
+#define AC97_CM9780_MIXER      0x64
+#define AC97_CM9780_MULTI_CHAN 0x66
+#define AC97_CM9780_SPDIF      0x6c
+
+static const char *cm9780_ch_select[] = { "Front", "Side", "Center/LFE", "Rear" };
+static const struct ac97_enum cm9780_ch_select_enum =
+       AC97_ENUM_SINGLE(AC97_CM9780_MULTI_CHAN, 6, 4, cm9780_ch_select);
+static const snd_kcontrol_new_t cm9780_controls[] = {
+       AC97_DOUBLE("Side Playback Switch", AC97_CM9780_SIDE, 15, 7, 1, 1),
+       AC97_DOUBLE("Side Playback Volume", AC97_CM9780_SIDE, 8, 0, 31, 0),
+       AC97_ENUM("Side Playback Route", cm9780_ch_select_enum),
+};
+
+static int patch_cm9780_specific(ac97_t *ac97)
+{
+       return patch_build_controls(ac97, cm9780_controls, ARRAY_SIZE(cm9780_controls));
+}
+
+static struct snd_ac97_build_ops patch_cm9780_ops = {
+       .build_specific = patch_cm9780_specific,
+       .build_post_spdif = patch_cm9761_post_spdif     /* identical with CM9761 */
+};
+
+int patch_cm9780(ac97_t *ac97)
+{
+       unsigned short val;
+
+       ac97->build_ops = &patch_cm9780_ops;
+
+       /* enable spdif */
+       if (ac97->ext_id & AC97_EI_SPDIF) {
+               ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000; /* 48k only */
+               val = snd_ac97_read(ac97, AC97_CM9780_SPDIF);
+               val |= 0x1; /* SPDI_EN */
+               snd_ac97_write_cache(ac97, AC97_CM9780_SPDIF, val);
+       }
+
+       return 0;
+}
 
 /*
  * VIA VT1616 codec
@@ -2263,9 +2412,21 @@ int patch_vt1616(ac97_t * ac97)
        return 0;
 }
 
+/*
+ */
+static void it2646_update_jacks(ac97_t *ac97)
+{
+       /* shared Line-In */
+       snd_ac97_update_bits(ac97, 0x76, 1 << 9,
+                            is_shared_linein(ac97) ? (1<<9) : 0);
+       /* shared Mic */
+       snd_ac97_update_bits(ac97, 0x76, 1 << 10,
+                            is_shared_micin(ac97) ? (1<<10) : 0);
+}
+
 static const snd_kcontrol_new_t snd_ac97_controls_it2646[] = {
-       AC97_SINGLE("Line-In As Surround", 0x76, 9, 1, 0),
-       AC97_SINGLE("Mic As Center/LFE", 0x76, 10, 1, 0),
+       AC97_SURROUND_JACK_MODE_CTL,
+       AC97_CHANNEL_MODE_CTL,
 };
 
 static const snd_kcontrol_new_t snd_ac97_spdif_controls_it2646[] = {
@@ -2285,7 +2446,8 @@ static int patch_it2646_specific(ac97_t * ac97)
 }
 
 static struct snd_ac97_build_ops patch_it2646_ops = {
-       .build_specific = patch_it2646_specific
+       .build_specific = patch_it2646_specific,
+       .update_jacks = it2646_update_jacks
 };
 
 int patch_it2646(ac97_t * ac97)
@@ -2297,12 +2459,29 @@ int patch_it2646(ac97_t * ac97)
        return 0;
 }
 
-/* Si3036/8 specific registers */
+/*
+ * Si3036 codec
+ */
+
 #define AC97_SI3036_CHIP_ID     0x5a
+#define AC97_SI3036_LINE_CFG    0x5c
+
+static const snd_kcontrol_new_t snd_ac97_controls_si3036[] = {
+AC97_DOUBLE("Modem Speaker Volume", 0x5c, 14, 12, 3, 1)
+};
+
+static int patch_si3036_specific(ac97_t * ac97)
+{
+       return patch_build_controls(ac97, snd_ac97_controls_si3036, ARRAY_SIZE(snd_ac97_controls_si3036));
+}
+
+static struct snd_ac97_build_ops patch_si3036_ops = {
+       .build_specific = patch_si3036_specific,
+};
 
 int mpatch_si3036(ac97_t * ac97)
 {
-       //printk("mpatch_si3036: chip id = %x\n", snd_ac97_read(ac97, 0x5a));
+       ac97->build_ops = &patch_si3036_ops;
        snd_ac97_write_cache(ac97, 0x5c, 0xf210 );
        snd_ac97_write_cache(ac97, 0x68, 0);
        return 0;
index 6db51c96f5d01053529642cdb2cdcc2e0cccede3..7b7377d0f2ae4d3926e3cd7e379be16d488d3027 100644 (file)
@@ -54,6 +54,7 @@ int patch_alc850(ac97_t * ac97);
 int patch_cm9738(ac97_t * ac97);
 int patch_cm9739(ac97_t * ac97);
 int patch_cm9761(ac97_t * ac97);
+int patch_cm9780(ac97_t * ac97);
 int patch_vt1616(ac97_t * ac97);
 int patch_it2646(ac97_t * ac97);
 int mpatch_si3036(ac97_t * ac97);
index 984d5d4ba4e15eb905bd3df47c5e5f3a6cdd1eea..eb5c36d31a52ae75a7e802407b71d7f760529796 100644 (file)
@@ -98,6 +98,8 @@ MODULE_PARM_DESC(spdif, "Support SPDIF I/O");
 #define ALI_LEF_CHANNEL                23
 #define ALI_SURR_LEFT_CHANNEL  26
 #define ALI_SURR_RIGHT_CHANNEL 25
+#define ALI_MODEM_IN_CHANNEL    21
+#define ALI_MODEM_OUT_CHANNEL   20
 
 #define        SNDRV_ALI_VOICE_TYPE_PCM        01
 #define SNDRV_ALI_VOICE_TYPE_OTH       02
@@ -122,7 +124,15 @@ MODULE_PARM_DESC(spdif, "Support SPDIF I/O");
 
 #define ALI_SCTRL              0x48
 #define   ALI_SPDIF_OUT_ENABLE         0x20
+#define   ALI_SCTRL_LINE_IN2           (1 << 9)
+#define   ALI_SCTRL_GPIO_IN2           (1 << 13)
+#define   ALI_SCTRL_LINE_OUT_EN        (1 << 20)
+#define   ALI_SCTRL_GPIO_OUT_EN        (1 << 23)
+#define   ALI_SCTRL_CODEC1_READY       (1 << 24)
+#define   ALI_SCTRL_CODEC2_READY       (1 << 25)
 #define ALI_AC97_GPIO          0x4c
+#define   ALI_AC97_GPIO_ENABLE         0x8000
+#define   ALI_AC97_GPIO_DATA_SHIFT     16
 #define ALI_SPDIF_CS           0x70
 #define ALI_SPDIF_CTRL         0x74
 #define   ALI_SPDIF_IN_FUNC_ENABLE     0x02
@@ -143,6 +153,7 @@ MODULE_PARM_DESC(spdif, "Support SPDIF I/O");
        #define TARGET_REACHED          0x00008000
        #define MIXER_OVERFLOW          0x00000800
        #define MIXER_UNDERFLOW         0x00000400
+       #define GPIO_IRQ                0x01000000
 #define ALI_SBBL_SBCL           0xc0
 #define ALI_SBCTRL_SBE2R_SBDD   0xc4
 #define ALI_STIMER             0xc8
@@ -162,6 +173,9 @@ MODULE_PARM_DESC(spdif, "Support SPDIF I/O");
 
 #define ALI_REG(codec, x) ((codec)->port + x)
 
+#define MAX_CODECS 2
+
+
 typedef struct snd_stru_ali ali_t;
 typedef struct snd_ali_stru_voice snd_ali_voice_t;
 
@@ -245,7 +259,7 @@ struct snd_stru_ali {
        struct pci_dev  *pci_m7101;
 
        snd_card_t      *card;
-       snd_pcm_t       *pcm;
+       snd_pcm_t       *pcm[MAX_CODECS];
        alidev_t        synth;
        snd_ali_channel_control_t chregs;
 
@@ -255,8 +269,10 @@ struct snd_stru_ali {
        unsigned int spurious_irq_count;
        unsigned int spurious_irq_max_delta;
 
+       unsigned int num_of_codecs;
+
        ac97_bus_t *ac97_bus;
-       ac97_t *ac97;
+       ac97_t *ac97[MAX_CODECS];
        unsigned short  ac97_ext_id;
        unsigned short  ac97_ext_status;
 
@@ -489,7 +505,12 @@ static void snd_ali_codec_write(ac97_t *ac97,
        ali_t *codec = ac97->private_data;
 
        snd_ali_printk("codec_write: reg=%xh data=%xh.\n", reg, val);
-       snd_ali_codec_poke(codec, 0, reg, val);
+       if(reg == AC97_GPIO_STATUS) {
+               outl((val << ALI_AC97_GPIO_DATA_SHIFT)|ALI_AC97_GPIO_ENABLE,
+                       ALI_REG(codec, ALI_AC97_GPIO));
+               return;
+       }
+       snd_ali_codec_poke(codec, ac97->num, reg, val);
        return ;
 }
 
@@ -499,7 +520,7 @@ static unsigned short snd_ali_codec_read(ac97_t *ac97, unsigned short reg)
        ali_t *codec = ac97->private_data;
 
        snd_ali_printk("codec_read reg=%xh.\n", reg);
-       return (snd_ali_codec_peek(codec, 0, reg));
+       return (snd_ali_codec_peek(codec, ac97->num, reg));
 }
 
 /*
@@ -1051,7 +1072,7 @@ static irqreturn_t snd_ali_card_interrupt(int irq,
 }
 
 
-static snd_ali_voice_t *snd_ali_alloc_voice(ali_t * codec, int type, int rec)
+static snd_ali_voice_t *snd_ali_alloc_voice(ali_t * codec, int type, int rec, int channel)
 {
        snd_ali_voice_t *pvoice = NULL;
        unsigned long flags;
@@ -1061,7 +1082,8 @@ static snd_ali_voice_t *snd_ali_alloc_voice(ali_t * codec, int type, int rec)
 
        spin_lock_irqsave(&codec->voice_alloc, flags);
        if (type == SNDRV_ALI_VOICE_TYPE_PCM) {
-               idx = snd_ali_find_free_channel(codec,rec);
+               idx = channel > 0 ? snd_ali_alloc_pcm_channel(codec, channel) :
+                       snd_ali_find_free_channel(codec,rec);
                if(idx < 0) {
                        snd_printk("ali_alloc_voice: err.\n");
                        spin_unlock_irqrestore(&codec->voice_alloc, flags);
@@ -1297,7 +1319,7 @@ static int snd_ali_playback_hw_params(snd_pcm_substream_t * substream,
 
        if (params_buffer_size(hw_params)/2 != params_period_size(hw_params)) {
                if (evoice == NULL) {
-                       evoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, 0);
+                       evoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, 0, -1);
                        if (evoice == NULL)
                                return -ENOMEM;
                        pvoice->extra = evoice;
@@ -1328,13 +1350,13 @@ static int snd_ali_playback_hw_free(snd_pcm_substream_t * substream)
        return 0;
 }
 
-static int snd_ali_capture_hw_params(snd_pcm_substream_t * substream,
+static int snd_ali_hw_params(snd_pcm_substream_t * substream,
                                 snd_pcm_hw_params_t * hw_params)
 {
        return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
 }
 
-static int snd_ali_capture_hw_free(snd_pcm_substream_t * substream)
+static int snd_ali_hw_free(snd_pcm_substream_t * substream)
 {
        return snd_pcm_lib_free_pages(substream);
 }
@@ -1428,7 +1450,7 @@ static int snd_ali_playback_prepare(snd_pcm_substream_t * substream)
 }
 
 
-static int snd_ali_capture_prepare(snd_pcm_substream_t * substream)
+static int snd_ali_prepare(snd_pcm_substream_t * substream)
 {
        ali_t *codec = snd_pcm_substream_chip(substream);
        snd_pcm_runtime_t *runtime = substream->runtime;
@@ -1446,11 +1468,13 @@ static int snd_ali_capture_prepare(snd_pcm_substream_t * substream)
 
        spin_lock_irqsave(&codec->reg_lock, flags);
 
-       snd_ali_printk("capture_prepare...\n");
+       snd_ali_printk("ali_prepare...\n");
 
        snd_ali_enable_special_channel(codec,pvoice->number);
 
-       Delta = snd_ali_convert_rate(runtime->rate, 1);
+       Delta = (pvoice->number == ALI_MODEM_IN_CHANNEL ||
+                pvoice->number == ALI_MODEM_OUT_CHANNEL) ? 
+               0x1000 : snd_ali_convert_rate(runtime->rate, pvoice->mode);
 
        // Prepare capture intr channel
        if (pvoice->number == ALI_SPDIF_IN_CHANNEL) {
@@ -1534,7 +1558,7 @@ static snd_pcm_uframes_t snd_ali_playback_pointer(snd_pcm_substream_t *substream
 }
 
 
-static snd_pcm_uframes_t snd_ali_capture_pointer(snd_pcm_substream_t *substream)
+static snd_pcm_uframes_t snd_ali_pointer(snd_pcm_substream_t *substream)
 {
        ali_t *codec = snd_pcm_substream_chip(substream);
        snd_pcm_runtime_t *runtime = substream->runtime;
@@ -1616,7 +1640,8 @@ static void snd_ali_pcm_free_substream(snd_pcm_runtime_t *runtime)
        }
 }
 
-static int snd_ali_playback_open(snd_pcm_substream_t * substream)
+static int snd_ali_open(snd_pcm_substream_t * substream, int rec, int channel,
+               snd_pcm_hardware_t *phw)
 {
        ali_t *codec = snd_pcm_substream_chip(substream);
        snd_pcm_runtime_t *runtime = substream->runtime;
@@ -1624,7 +1649,7 @@ static int snd_ali_playback_open(snd_pcm_substream_t * substream)
        unsigned long flags = 0;
 
        spin_lock_irqsave(&codec->reg_lock, flags);
-       pvoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, 0);
+       pvoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, rec, channel);
        if (pvoice == NULL) {
                spin_unlock_irqrestore(&codec->reg_lock, flags);
                return -EAGAIN;
@@ -1636,49 +1661,31 @@ static int snd_ali_playback_open(snd_pcm_substream_t * substream)
        runtime->private_data = pvoice;
        runtime->private_free = snd_ali_pcm_free_substream;
 
-       runtime->hw = snd_ali_playback;
+       runtime->hw = *phw;
        snd_pcm_set_sync(substream);
        snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
        return 0;
 }
 
+static int snd_ali_playback_open(snd_pcm_substream_t * substream)
+{
+       return snd_ali_open(substream, 0, -1, &snd_ali_playback);
+}
 
 static int snd_ali_capture_open(snd_pcm_substream_t * substream)
 {
-       ali_t *codec = snd_pcm_substream_chip(substream);
-       snd_pcm_runtime_t *runtime = substream->runtime;
-       snd_ali_voice_t *pvoice;
-       unsigned long flags;
-
-       spin_lock_irqsave(&codec->reg_lock, flags);
-       pvoice = snd_ali_alloc_voice(codec, SNDRV_ALI_VOICE_TYPE_PCM, 1);
-       if (pvoice == NULL) {
-               spin_unlock_irqrestore(&codec->reg_lock, flags);
-               return -EAGAIN;
-       }
-       pvoice->codec = codec;
-       spin_unlock_irqrestore(&codec->reg_lock, flags);
-
-       pvoice->substream = substream;
-       runtime->private_data = pvoice;
-       runtime->private_free = snd_ali_pcm_free_substream;
-       runtime->hw = snd_ali_capture;
-       snd_pcm_set_sync(substream);
-       snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 0, 64*1024);
-       return 0;
+       return snd_ali_open(substream, 1, -1, &snd_ali_capture);
 }
 
-
 static int snd_ali_playback_close(snd_pcm_substream_t * substream)
 {
        return 0;
 }
 
-static int snd_ali_capture_close(snd_pcm_substream_t * substream)
+static int snd_ali_close(snd_pcm_substream_t * substream)
 {
        ali_t *codec = snd_pcm_substream_chip(substream);
-       snd_pcm_runtime_t *runtime = substream->runtime;
-       snd_ali_voice_t *pvoice = (snd_ali_voice_t *) runtime->private_data;
+       snd_ali_voice_t *pvoice = (snd_ali_voice_t *) substream->runtime->private_data;
 
        snd_ali_disable_special_channel(codec,pvoice->number);
 
@@ -1698,29 +1705,121 @@ static snd_pcm_ops_t snd_ali_playback_ops = {
 
 static snd_pcm_ops_t snd_ali_capture_ops = {
        .open =         snd_ali_capture_open,
-       .close =        snd_ali_capture_close,
+       .close =        snd_ali_close,
        .ioctl =        snd_ali_ioctl,
-       .hw_params =    snd_ali_capture_hw_params,
-       .hw_free =      snd_ali_capture_hw_free,
-       .prepare =      snd_ali_capture_prepare,
+       .hw_params =    snd_ali_hw_params,
+       .hw_free =      snd_ali_hw_free,
+       .prepare =      snd_ali_prepare,
+       .trigger =      snd_ali_trigger,
+       .pointer =      snd_ali_pointer,
+};
+
+/*
+ * Modem PCM
+ */
+
+static int snd_ali_modem_hw_params(snd_pcm_substream_t * substream,
+                                snd_pcm_hw_params_t * hw_params)
+{
+       ali_t *chip = snd_pcm_substream_chip(substream);
+       unsigned int modem_num = chip->num_of_codecs - 1;
+       snd_ac97_write(chip->ac97[modem_num], AC97_LINE1_RATE, params_rate(hw_params));
+       snd_ac97_write(chip->ac97[modem_num], AC97_LINE1_LEVEL, 0);
+       return snd_ali_hw_params(substream, hw_params);
+}
+
+static snd_pcm_hardware_t snd_ali_modem =
+{
+       .info =                 (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+                                SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                SNDRV_PCM_INFO_MMAP_VALID |
+                                SNDRV_PCM_INFO_RESUME |
+                                SNDRV_PCM_INFO_SYNC_START),
+       .formats =              SNDRV_PCM_FMTBIT_S16_LE,
+       .rates =                SNDRV_PCM_RATE_KNOT|SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000,
+       .rate_min =             8000,
+       .rate_max =             16000,
+       .channels_min =         1,
+       .channels_max =         1,
+       .buffer_bytes_max =     (256*1024),
+       .period_bytes_min =     64,
+       .period_bytes_max =     (256*1024),
+       .periods_min =          1,
+       .periods_max =          1024,
+       .fifo_size =            0,
+};
+
+static int snd_ali_modem_open(snd_pcm_substream_t * substream, int rec, int channel)
+{
+       static unsigned int rates [] = {8000,9600,12000,16000};
+       static snd_pcm_hw_constraint_list_t hw_constraint_rates = {
+               .count = ARRAY_SIZE(rates),
+               .list = rates,
+               .mask = 0,
+       };
+       int err = snd_ali_open(substream, rec, channel, &snd_ali_modem);
+       if (err)
+               return err;
+       return snd_pcm_hw_constraint_list(substream->runtime, 0,
+                       SNDRV_PCM_HW_PARAM_RATE, &hw_constraint_rates);
+}
+
+static int snd_ali_modem_playback_open(snd_pcm_substream_t * substream)
+{
+       return snd_ali_modem_open(substream, 0, ALI_MODEM_OUT_CHANNEL);
+}
+
+static int snd_ali_modem_capture_open(snd_pcm_substream_t * substream)
+{
+       return snd_ali_modem_open(substream, 1, ALI_MODEM_IN_CHANNEL);
+}
+
+static snd_pcm_ops_t snd_ali_modem_playback_ops = {
+       .open =         snd_ali_modem_playback_open,
+       .close =        snd_ali_close,
+       .ioctl =        snd_pcm_lib_ioctl,
+       .hw_params =    snd_ali_modem_hw_params,
+       .hw_free =      snd_ali_hw_free,
+       .prepare =      snd_ali_prepare,
+       .trigger =      snd_ali_trigger,
+       .pointer =      snd_ali_pointer,
+};
+
+static snd_pcm_ops_t snd_ali_modem_capture_ops = {
+       .open =         snd_ali_modem_capture_open,
+       .close =        snd_ali_close,
+       .ioctl =        snd_pcm_lib_ioctl,
+       .hw_params =    snd_ali_modem_hw_params,
+       .hw_free =      snd_ali_hw_free,
+       .prepare =      snd_ali_prepare,
        .trigger =      snd_ali_trigger,
-       .pointer =      snd_ali_capture_pointer,
+       .pointer =      snd_ali_pointer,
+};
+
+
+struct ali_pcm_description {
+       char *name;
+       unsigned int playback_num;
+       unsigned int capture_num;
+       snd_pcm_ops_t *playback_ops;
+       snd_pcm_ops_t *capture_ops;
 };
 
 
 static void snd_ali_pcm_free(snd_pcm_t *pcm)
 {
        ali_t *codec = pcm->private_data;
-       codec->pcm = NULL;
+       codec->pcm[pcm->device] = NULL;
 }
 
-static int __devinit snd_ali_pcm(ali_t * codec, int device, snd_pcm_t ** rpcm)
+
+static int __devinit snd_ali_pcm(ali_t * codec, int device, struct ali_pcm_description *desc)
 {
        snd_pcm_t *pcm;
        int err;
 
-       if (rpcm) *rpcm = NULL;
-       err = snd_pcm_new(codec->card, "ALI 5451", device, ALI_CHANNELS, 1, &pcm);
+       err = snd_pcm_new(codec->card, desc->name, device,
+                         desc->playback_num, desc->capture_num, &pcm);
        if (err < 0) {
                snd_printk("snd_ali_pcm: err called snd_pcm_new.\n");
                return err;
@@ -1728,20 +1827,36 @@ static int __devinit snd_ali_pcm(ali_t * codec, int device, snd_pcm_t ** rpcm)
        pcm->private_data = codec;
        pcm->private_free = snd_ali_pcm_free;
        pcm->info_flags = 0;
-       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ali_playback_ops);
-       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ali_capture_ops);
+       if (desc->playback_ops)
+               snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, desc->playback_ops);
+       if (desc->capture_ops)
+               snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, desc->capture_ops);
 
        snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
                                              snd_dma_pci_data(codec->pci), 64*1024, 128*1024);
 
        pcm->info_flags = 0;
        pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
-       strcpy(pcm->name, "ALI 5451");
-       codec->pcm = pcm;
-       if (rpcm) *rpcm = pcm;
+       strcpy(pcm->name, desc->name);
+       codec->pcm[0] = pcm;
        return 0;
 }
 
+struct ali_pcm_description ali_pcms[] = {
+       { "ALI 5451", ALI_CHANNELS, 1, &snd_ali_playback_ops, &snd_ali_capture_ops },
+       { "ALI 5451 modem", 1, 1, &snd_ali_modem_playback_ops, &snd_ali_modem_capture_ops }
+};
+
+static int __devinit snd_ali_build_pcms(ali_t *codec)
+{
+       int i, err;
+       for(i = 0 ; i < codec->num_of_codecs && i < ARRAY_SIZE(ali_pcms) ; i++)
+               if((err = snd_ali_pcm(codec, i, &ali_pcms[i])) < 0)
+                       return err;
+       return 0;
+}
+
+
 #define ALI5451_SPDIF(xname, xindex, value) \
 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\
 .info = snd_ali5451_spdif_info, .get = snd_ali5451_spdif_get, \
@@ -1860,14 +1975,14 @@ static void snd_ali_mixer_free_ac97_bus(ac97_bus_t *bus)
 static void snd_ali_mixer_free_ac97(ac97_t *ac97)
 {
        ali_t *codec = ac97->private_data;
-       codec->ac97 = NULL;
+       codec->ac97[ac97->num] = NULL;
 }
 
 static int __devinit snd_ali_mixer(ali_t * codec)
 {
        ac97_template_t ac97;
        unsigned int idx;
-       int err;
+       int i, err;
        static ac97_bus_ops_t ops = {
                .write = snd_ali_codec_write,
                .read = snd_ali_codec_read,
@@ -1880,10 +1995,16 @@ static int __devinit snd_ali_mixer(ali_t * codec)
        memset(&ac97, 0, sizeof(ac97));
        ac97.private_data = codec;
        ac97.private_free = snd_ali_mixer_free_ac97;
-       if ((err = snd_ac97_mixer(codec->ac97_bus, &ac97, &codec->ac97)) < 0) {
-               snd_printk("ali mixer creating error.\n");
+
+       for ( i = 0 ; i < codec->num_of_codecs ; i++) {
+               ac97.num = i;
+               if ((err = snd_ac97_mixer(codec->ac97_bus, &ac97, &codec->ac97[i])) < 0) {
+                       snd_printk("ali mixer %d creating error.\n", i);
+                       if(i == 0)
                return err;
        }
+       }
+
        if (codec->spdif_support) {
                for(idx = 0; idx < ARRAY_SIZE(snd_ali5451_mixer_spdif); idx++) {
                        err=snd_ctl_add(codec->card, snd_ctl_new1(&snd_ali5451_mixer_spdif[idx], codec));
@@ -1904,8 +2025,12 @@ static int ali_suspend(snd_card_t *card, pm_message_t state)
        if (! im)
                return 0;
 
-       snd_pcm_suspend_all(chip->pcm);
-       snd_ac97_suspend(chip->ac97);
+       for(i = 0 ; i < chip->num_of_codecs ; i++) {
+               if (chip->pcm[i])
+                       snd_pcm_suspend_all(chip->pcm[i]);
+               if(chip->ac97[i])
+                       snd_ac97_suspend(chip->ac97[i]);
+       }
 
        spin_lock_irq(&chip->reg_lock);
        
@@ -1969,7 +2094,9 @@ static int ali_resume(snd_card_t *card)
        
        spin_unlock_irq(&chip->reg_lock);
 
-       snd_ac97_resume(chip->ac97);
+       for(i = 0 ; i < chip->num_of_codecs ; i++)
+               if(chip->ac97[i])
+                       snd_ac97_resume(chip->ac97[i]);
        
        return 0;
 }
@@ -2036,11 +2163,37 @@ static int snd_ali_chip_init(ali_t *codec)
                codec->spdif_mask = 0x00000002;
        }
 
+       codec->num_of_codecs = 1;
+
+       /* secondary codec - modem */
+       if (inl(ALI_REG(codec, ALI_SCTRL)) & ALI_SCTRL_CODEC2_READY) {
+               codec->num_of_codecs++;
+               outl(inl(ALI_REG(codec, ALI_SCTRL)) |
+                       (ALI_SCTRL_LINE_IN2|ALI_SCTRL_GPIO_IN2|ALI_SCTRL_LINE_OUT_EN),
+                       ALI_REG(codec, ALI_SCTRL));
+       }
+
        snd_ali_printk("chip initialize succeed.\n");
        return 0;
 
 }
 
+/* proc for register dump */
+static void snd_ali_proc_read(snd_info_entry_t *entry, snd_info_buffer_t *buf)
+{
+       ali_t *codec = entry->private_data;
+       int i;
+       for(i = 0 ; i < 256 ; i+= 4)
+               snd_iprintf(buf, "%02x: %08x\n", i, inl(ALI_REG(codec, i)));
+}
+
+static void __devinit snd_ali_proc_init(ali_t *codec)
+{
+       snd_info_entry_t *entry;
+       if(!snd_card_proc_new(codec->card, "ali5451", &entry))
+               snd_info_set_text_ops(entry, codec, 1024, snd_ali_proc_read);
+}
+
 static int __devinit snd_ali_resources(ali_t *codec)
 {
        int err;
@@ -2233,11 +2386,13 @@ static int __devinit snd_ali_probe(struct pci_dev *pci,
        }
        
        snd_ali_printk("pcm building ...\n");
-       if ((err = snd_ali_pcm(codec, 0, NULL)) < 0) {
+       if ((err = snd_ali_build_pcms(codec)) < 0) {
                snd_card_free(card);
                return err;
        }
 
+       snd_ali_proc_init(codec);
+
        strcpy(card->driver, "ALI5451");
        strcpy(card->shortname, "ALI 5451");
        
@@ -2270,7 +2425,7 @@ static struct pci_driver driver = {
 
 static int __init alsa_card_ali_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_ali_exit(void)
index f1a5f5723ee6b5f89ad3be89130146bd22a398f8..ca28b229c704051154ef1a05aae2a485f7234e64 100644 (file)
@@ -367,7 +367,7 @@ static irqreturn_t snd_als4000_interrupt(int irq, void *dev_id, struct pt_regs *
        if ((gcr_status & 0x40) && (chip->capture_substream)) /* capturing */
                snd_pcm_period_elapsed(chip->capture_substream);
        if ((gcr_status & 0x10) && (chip->rmidi)) /* MPU401 interrupt */
-               snd_mpu401_uart_interrupt(irq, chip->rmidi, regs);
+               snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
        /* release the gcr */
        outb(gcr_status, chip->alt_port + 0xe);
        
@@ -777,7 +777,7 @@ static struct pci_driver driver = {
 
 static int __init alsa_card_als4000_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_als4000_exit(void)
index 6b04c0acc6f722e47eddc6357d0f205ce2c90a5d..cafab4af5c571752765ad1a4b552c9013f4e4aad 100644 (file)
@@ -1334,8 +1334,8 @@ static irqreturn_t snd_atiixp_interrupt(int irq, void *dev_id, struct pt_regs *r
 
 static struct ac97_quirk ac97_quirks[] __devinitdata = {
        {
-               .vendor = 0x103c,
-               .device = 0x006b,
+               .subvendor = 0x103c,
+               .subdevice = 0x006b,
                .name = "HP Pavilion ZV5030US",
                .type = AC97_TUNE_MUTE_LED
        },
@@ -1645,7 +1645,7 @@ static struct pci_driver driver = {
 
 static int __init alsa_card_atiixp_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_atiixp_exit(void)
index fb7cecea846d069334d0b83e84401790ec6e78fc..a6b4b8d589fd672918da5efda911e280f59cfc10 100644 (file)
@@ -463,6 +463,11 @@ static unsigned short snd_atiixp_ac97_read(ac97_t *ac97, unsigned short reg)
 static void snd_atiixp_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val)
 {
        atiixp_t *chip = ac97->private_data;
+       if (reg == AC97_GPIO_STATUS) {
+               atiixp_write(chip, MODEM_OUT_GPIO,
+                       (val << ATI_REG_MODEM_OUT_GPIO_DATA_SHIFT) | ATI_REG_MODEM_OUT_GPIO_EN);
+               return;
+       }
        snd_atiixp_codec_write(chip, ac97->num, reg, val);
 }
 
@@ -663,44 +668,33 @@ static int snd_atiixp_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
 {
        atiixp_t *chip = snd_pcm_substream_chip(substream);
        atiixp_dma_t *dma = (atiixp_dma_t *)substream->runtime->private_data;
-       unsigned int reg = 0;
-       int i;
+       int err = 0;
 
        snd_assert(dma->ops->enable_transfer && dma->ops->flush_dma, return -EINVAL);
 
-       if (cmd != SNDRV_PCM_TRIGGER_START && cmd != SNDRV_PCM_TRIGGER_STOP)
-               return -EINVAL;
-
        spin_lock(&chip->reg_lock);
-
-       /* hook off/on: via GPIO_OUT */
-       for (i = 0; i < NUM_ATI_CODECS; i++) {
-               if (chip->ac97[i]) {
-                       reg = snd_ac97_read(chip->ac97[i], AC97_GPIO_STATUS);
-                       break;
-       }
-       }
-       if(cmd == SNDRV_PCM_TRIGGER_START)
-               reg |= AC97_GPIO_LINE1_OH;
-       else
-               reg &= ~AC97_GPIO_LINE1_OH;
-       reg = (reg << ATI_REG_MODEM_OUT_GPIO_DATA_SHIFT) | ATI_REG_MODEM_OUT_GPIO_EN ;
-       atiixp_write(chip, MODEM_OUT_GPIO, reg);
-
-       if (cmd == SNDRV_PCM_TRIGGER_START) {
+       switch(cmd) {
+       case SNDRV_PCM_TRIGGER_START:
                dma->ops->enable_transfer(chip, 1);
                dma->running = 1;
-       } else {
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
                dma->ops->enable_transfer(chip, 0);
                dma->running = 0;
+               break;
+       default:
+               err = -EINVAL;
+               break;
        }
+       if (! err) {
        snd_atiixp_check_bus_busy(chip);
        if (cmd == SNDRV_PCM_TRIGGER_STOP) {
                dma->ops->flush_dma(chip);
                snd_atiixp_check_bus_busy(chip);
        }
+       }
        spin_unlock(&chip->reg_lock);
-       return 0;
+       return err;
 }
 
 
@@ -1332,7 +1326,7 @@ static struct pci_driver driver = {
 
 static int __init alsa_card_atiixp_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_atiixp_exit(void)
index 889b4a1a51a1adcb8d8e4160e0d512dcfd689be9..f6236c63aaaae2c3d5cdaafe9facf7bd85e56281 100644 (file)
@@ -375,7 +375,7 @@ static struct pci_driver driver = {
 // initialization of the module
 static int __init alsa_card_vortex_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 // clean up the module
index b8ae534125c1ecf9812a4570b11dc9530d88ef91..72bba7b2d983811bbba99a098cbfccbd5740ef20 100644 (file)
@@ -1520,7 +1520,7 @@ static int __init alsa_card_azf3328_init(void)
 {
        int err;
        snd_azf3328_dbgcallenter();
-       err = pci_module_init(&driver);
+       err = pci_register_driver(&driver);
        snd_azf3328_dbgcallleave();
        return err;
 }
index 89a7ffe5e7d7723a0a2317d95144c588c31dfc20..defdc5a459f0ffea3ff7732160bdfabfa0db9b58 100644 (file)
@@ -918,7 +918,7 @@ static int __init alsa_card_bt87x_init(void)
 {
        if (load_all)
                driver.id_table = snd_bt87x_default_ids;
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_bt87x_exit(void)
index deb028851056bd85fad5f8171cf405987e88776f..da09cab405a92748904bd925aba7662b488fc7a7 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
  *  Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
- *  Version: 0.0.20
+ *  Version: 0.0.21
  *
  *  FEATURES currently supported:
  *    See ca0106_main.c for features.
@@ -45,6 +45,8 @@
  *    Added I2C and SPI registers. Filled in interrupt enable.
  *  0.0.20
  *    Added GPIO info for SB Live 24bit.
+ *  0.0.21
+ *   Implement support for Line-in capture on SB Live 24bit.
  *
  *
  *  This code was initally based on code from ALSA's emu10k1x.c which is:
                                                 * bit 9 0 = Mute / 1 = Analog out.
                                                 * bit 10 0 = Line-in / 1 = Mic-in.
                                                 * bit 11 0 = ? / 1 = ?
-                                                * bit 12 0 = ? / 1 = ?
+                                                * bit 12 0 = 48 Khz / 1 = 96 Khz Analog out on SB Live 24bit.
                                                 * bit 13 0 = ? / 1 = ?
                                                 * bit 14 0 = Mute / 1 = Analog out
                                                 * bit 15 0 = ? / 1 = ?
                                                /* Causes interrupts based on timer intervals. */
 #define SPI                    0x7a            /* SPI: Serial Interface Register */
 #define I2C_A                  0x7b            /* I2C Address. 32 bit */
-#define I2C_0                  0x7c            /* I2C Data Port 0. 32 bit */
-#define I2C_1                  0x7d            /* I2C Data Port 1. 32 bit */
+#define I2C_D0                 0x7c            /* I2C Data Port 0. 32 bit */
+#define I2C_D1                 0x7d            /* I2C Data Port 1. 32 bit */
+//I2C values
+#define I2C_A_ADC_ADD_MASK     0x000000fe      //The address is a 7 bit address
+#define I2C_A_ADC_RW_MASK      0x00000001      //bit mask for R/W
+#define I2C_A_ADC_TRANS_MASK   0x00000010      //Bit mask for I2c address DAC value
+#define I2C_A_ADC_ABORT_MASK   0x00000020      //Bit mask for I2C transaction abort flag
+#define I2C_A_ADC_LAST_MASK    0x00000040      //Bit mask for Last word transaction
+#define I2C_A_ADC_BYTE_MASK    0x00000080      //Bit mask for Byte Mode
 
+#define I2C_A_ADC_ADD          0x00000034      //This is the Device address for ADC 
+#define I2C_A_ADC_READ         0x00000001      //To perform a read operation
+#define I2C_A_ADC_START                0x00000100      //Start I2C transaction
+#define I2C_A_ADC_ABORT                0x00000200      //I2C transaction abort
+#define I2C_A_ADC_LAST         0x00000400      //I2C last transaction
+#define I2C_A_ADC_BYTE         0x00000800      //I2C one byte mode
+
+#define I2C_D_ADC_REG_MASK     0xfe000000      //ADC address register 
+#define I2C_D_ADC_DAT_MASK     0x01ff0000      //ADC data register
+
+#define ADC_TIMEOUT            0x00000007      //ADC Timeout Clock Disable
+#define ADC_IFC_CTRL           0x0000000b      //ADC Interface Control
+#define ADC_MASTER             0x0000000c      //ADC Master Mode Control
+#define ADC_POWER              0x0000000d      //ADC PowerDown Control
+#define ADC_ATTEN_ADCL         0x0000000e      //ADC Attenuation ADCL
+#define ADC_ATTEN_ADCR         0x0000000f      //ADC Attenuation ADCR
+#define ADC_ALC_CTRL1          0x00000010      //ADC ALC Control 1
+#define ADC_ALC_CTRL2          0x00000011      //ADC ALC Control 2
+#define ADC_ALC_CTRL3          0x00000012      //ADC ALC Control 3
+#define ADC_NOISE_CTRL         0x00000013      //ADC Noise Gate Control
+#define ADC_LIMIT_CTRL         0x00000014      //ADC Limiter Control
+#define ADC_MUX                        0x00000015      //ADC Mux offset
+
+#if 0
+/* FIXME: Not tested yet. */
+#define ADC_GAIN_MASK          0x000000ff      //Mask for ADC Gain
+#define ADC_ZERODB             0x000000cf      //Value to set ADC to 0dB
+#define ADC_MUTE_MASK          0x000000c0      //Mask for ADC mute
+#define ADC_MUTE               0x000000c0      //Value to mute ADC
+#define ADC_OSR                        0x00000008      //Mask for ADC oversample rate select
+#define ADC_TIMEOUT_DISABLE    0x00000008      //Value and mask to disable Timeout clock
+#define ADC_HPF_DISABLE                0x00000100      //Value and mask to disable High pass filter
+#define ADC_TRANWIN_MASK       0x00000070      //Mask for Length of Transient Window
+#endif
+
+#define ADC_MUX_MASK           0x0000000f      //Mask for ADC Mux
+#define ADC_MUX_MIC            0x00000002      //Value to select Mic at ADC Mux
+#define ADC_MUX_LINEIN         0x00000004      //Value to select LineIn at ADC Mux
+#define ADC_MUX_PHONE          0x00000001      //Value to select TAD at ADC Mux (Not used)
+#define ADC_MUX_AUX            0x00000008      //Value to select Aux at ADC Mux
 
 #define SET_CHANNEL 0  /* Testing channel outputs 0=Front, 1=Center/LFE, 2=Unknown, 3=Rear */
 #define PCM_FRONT_CHANNEL 0
@@ -508,9 +557,18 @@ struct snd_ca0106_pcm {
        unsigned short running;
 };
 
+typedef struct {
+        u32 serial;
+        char * name;
+        int ac97;
+       int gpio_type;
+       int i2c_adc;
+} ca0106_details_t;
+
 // definition of the chip-specific record
 struct snd_ca0106 {
        snd_card_t *card;
+       ca0106_details_t *details;
        struct pci_dev *pci;
 
        unsigned long port;
@@ -531,6 +589,7 @@ struct snd_ca0106 {
        u32 spdif_bits[4];             /* s/pdif out setup */
        int spdif_enable;
        int capture_source;
+       int capture_mic_line_in;
 
        struct snd_dma_buffer buffer;
 };
@@ -547,3 +606,6 @@ void snd_ca0106_ptr_write(ca0106_t *emu,
                                   unsigned int chn, 
                                   unsigned int data);
 
+int snd_ca0106_i2c_write(ca0106_t *emu, u32 reg, u32 value);
+
+
index 82533b45bc8cd5afed5f515ac124c7a2f10452b4..95c289284267fdf0a33edfd507e5b7277826eac9 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
  *  Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
- *  Version: 0.0.22
+ *  Version: 0.0.23
  *
  *  FEATURES currently supported:
  *    Front, Rear and Center/LFE.
@@ -77,6 +77,8 @@
  *    Add SPDIF capture using optional digital I/O module for SB Live 24bit. (Analog capture does not yet work.)
  *  0.0.22
  *    Add support for MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97. From kiksen, bug #901
+ *  0.0.23
+ *    Implement support for Line-in capture on SB Live 24bit.
  *
  *  BUGS:
  *    Some stability problems when unloading the snd-ca0106 kernel module.
 #include <linux/pci.h>
 #include <linux/slab.h>
 #include <linux/moduleparam.h>
+#include <linux/dma-mapping.h>
 #include <sound/core.h>
 #include <sound/initval.h>
 #include <sound/pcm.h>
@@ -161,18 +164,32 @@ MODULE_PARM_DESC(enable, "Enable the CA0106 soundcard.");
 
 #include "ca0106.h"
 
-typedef struct {
-       u32 serial;
-       char * name;
-} ca0106_names_t;
-
-static ca0106_names_t ca0106_chip_names[] = {
-        { 0x10021102, "AudigyLS [SB0310]"} , 
-        { 0x10051102, "AudigyLS [SB0310b]"} , /* Unknown AudigyLS that also says SB0310 on it */
-        { 0x10061102, "Live! 7.1 24bit [SB0410]"} , /* New Sound Blaster Live! 7.1 24bit. This does not have an AC97. 53SB041000001 */
-        { 0x10071102, "Live! 7.1 24bit [SB0413]"} , /* New Dell Sound Blaster Live! 7.1 24bit. This does not have an AC97.  */
-        { 0x10091462, "MSI K8N Diamond MB [SB0438]"}, /* MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97 */
-        { 0, "AudigyLS [Unknown]" }
+static ca0106_details_t ca0106_chip_details[] = {
+        /* AudigyLS[SB0310] */
+        { .serial = 0x10021102,
+          .name   = "AudigyLS [SB0310]",
+          .ac97   = 1 } , 
+        /* Unknown AudigyLS that also says SB0310 on it */
+        { .serial = 0x10051102,
+          .name   = "AudigyLS [SB0310b]",
+          .ac97   = 1 } ,
+        /* New Sound Blaster Live! 7.1 24bit. This does not have an AC97. 53SB041000001 */
+        { .serial = 0x10061102,
+          .name   = "Live! 7.1 24bit [SB0410]",
+          .gpio_type = 1,
+          .i2c_adc = 1 } ,
+        /* New Dell Sound Blaster Live! 7.1 24bit. This does not have an AC97.  */
+        { .serial = 0x10071102,
+          .name   = "Live! 7.1 24bit [SB0413]",
+          .gpio_type = 1,
+          .i2c_adc = 1 } ,
+        /* MSI K8N Diamond Motherboard with onboard SB Live 24bit without AC97 */
+        { .serial = 0x10091462,
+          .name   = "MSI K8N Diamond MB [SB0438]",
+          .gpio_type = 1,
+          .i2c_adc = 1 } ,
+        { .serial = 0,
+          .name   = "AudigyLS [Unknown]" }
 };
 
 /* hardware definition */
@@ -200,10 +217,10 @@ static snd_pcm_hardware_t snd_ca0106_capture_hw = {
                                 SNDRV_PCM_INFO_INTERLEAVED |
                                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
                                 SNDRV_PCM_INFO_MMAP_VALID),
-       .formats =              SNDRV_PCM_FMTBIT_S16_LE,
-       .rates =                SNDRV_PCM_RATE_48000,
-       .rate_min =             48000,
-       .rate_max =             48000,
+       .formats =              SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+       .rates =                SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000,
+       .rate_min =             44100,
+       .rate_max =             192000,
        .channels_min =         2,
        .channels_max =         2,
        .buffer_bytes_max =     ((65536 - 64) * 8),
@@ -246,6 +263,62 @@ void snd_ca0106_ptr_write(ca0106_t *emu,
        spin_unlock_irqrestore(&emu->emu_lock, flags);
 }
 
+int snd_ca0106_i2c_write(ca0106_t *emu,
+                               u32 reg,
+                               u32 value)
+{
+       u32 tmp;
+       int timeout=0;
+       int status;
+       int retry;
+       if ((reg > 0x7f) || (value > 0x1ff))
+       {
+                snd_printk("i2c_write: invalid values.\n");
+               return -EINVAL;
+       }
+
+       tmp = reg << 25 | value << 16;
+       /* Not sure what this I2C channel controls. */
+       /* snd_ca0106_ptr_write(emu, I2C_D0, 0, tmp); */
+
+       /* This controls the I2C connected to the WM8775 ADC Codec */
+       snd_ca0106_ptr_write(emu, I2C_D1, 0, tmp);
+
+       for(retry=0;retry<10;retry++)
+       {
+               /* Send the data to i2c */
+               tmp = snd_ca0106_ptr_read(emu, I2C_A, 0);
+               tmp = tmp & ~(I2C_A_ADC_READ|I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD_MASK);
+               tmp = tmp | (I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD);
+               snd_ca0106_ptr_write(emu, I2C_A, 0, tmp);
+
+               /* Wait till the transaction ends */
+               while(1)
+               {
+                       status = snd_ca0106_ptr_read(emu, I2C_A, 0);
+                       //snd_printk("I2C:status=0x%x\n", status);
+                       timeout++;
+                       if((status & I2C_A_ADC_START)==0)
+                               break;
+
+                       if(timeout>1000)
+                               break;
+               }
+               //Read back and see if the transaction is successful
+               if((status & I2C_A_ADC_ABORT)==0)
+                       break;
+       }
+
+       if(retry==10)
+       {
+                snd_printk("Writing to ADC failed!\n");
+               return -EINVAL;
+       }
+    
+       return 0;
+}
+
+
 static void snd_ca0106_intr_enable(ca0106_t *emu, unsigned int intrenb)
 {
        unsigned long flags;
@@ -259,11 +332,7 @@ static void snd_ca0106_intr_enable(ca0106_t *emu, unsigned int intrenb)
 
 static void snd_ca0106_pcm_free_substream(snd_pcm_runtime_t *runtime)
 {
-       ca0106_pcm_t *epcm = runtime->private_data;
-  
-       if (epcm) {
-               kfree(epcm);
-       }
+       kfree(runtime->private_data);
 }
 
 /* open_playback callback */
@@ -538,6 +607,61 @@ static int snd_ca0106_pcm_prepare_capture(snd_pcm_substream_t *substream)
        snd_pcm_runtime_t *runtime = substream->runtime;
        ca0106_pcm_t *epcm = runtime->private_data;
        int channel = epcm->channel_id;
+       u32 hcfg_mask = HCFG_CAPTURE_S32_LE;
+       u32 hcfg_set = 0x00000000;
+       u32 hcfg;
+       u32 over_sampling=0x2;
+       u32 reg71_mask = 0x0000c000 ; /* Global. Set ADC rate. */
+       u32 reg71_set = 0;
+       u32 reg71;
+       
+        //snd_printk("prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, periods=%u, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size, runtime->periods, frames_to_bytes(runtime, 1));
+        //snd_printk("dma_addr=%x, dma_area=%p, table_base=%p\n",runtime->dma_addr, runtime->dma_area, table_base);
+       //snd_printk("dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",emu->buffer.addr, emu->buffer.area, emu->buffer.bytes);
+       /* reg71 controls ADC rate. */
+       switch (runtime->rate) {
+       case 44100:
+               reg71_set = 0x00004000;
+               break;
+        case 48000:
+               reg71_set = 0; 
+               break;
+       case 96000:
+               reg71_set = 0x00008000;
+               over_sampling=0xa;
+               break;
+       case 192000:
+               reg71_set = 0x0000c000; 
+               over_sampling=0xa;
+               break;
+       default:
+               reg71_set = 0; 
+               break;
+       }
+       /* Format is a global setting */
+       /* FIXME: Only let the first channel accessed set this. */
+       switch (runtime->format) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               hcfg_set = 0;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               hcfg_set = HCFG_CAPTURE_S32_LE;
+               break;
+       default:
+               hcfg_set = 0;
+               break;
+       }
+       hcfg = inl(emu->port + HCFG) ;
+       hcfg = (hcfg & ~hcfg_mask) | hcfg_set;
+       outl(hcfg, emu->port + HCFG);
+       reg71 = snd_ca0106_ptr_read(emu, 0x71, 0);
+       reg71 = (reg71 & ~reg71_mask) | reg71_set;
+       snd_ca0106_ptr_write(emu, 0x71, 0, reg71);
+        if (emu->details->i2c_adc == 1) { /* The SB0410 and SB0413 use I2C to control ADC. */
+               snd_ca0106_i2c_write(emu, ADC_MASTER, over_sampling); /* Adjust the over sampler to better suit the capture rate. */
+       }
+
+
         //printk("prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size,  frames_to_bytes(runtime, 1));
        snd_ca0106_ptr_write(emu, 0x13, channel, 0);
        snd_ca0106_ptr_write(emu, CAPTURE_DMA_ADDR, channel, runtime->dma_addr);
@@ -810,6 +934,7 @@ static int snd_ca0106_ac97(ca0106_t *chip)
 
        memset(&ac97, 0, sizeof(ac97));
        ac97.private_data = chip;
+       ac97.scaps = AC97_SCAP_NO_SPDIF;
        return snd_ac97_mixer(pbus, &ac97, &chip->ac97);
 }
 
@@ -993,6 +1118,7 @@ static int __devinit snd_ca0106_create(snd_card_t *card,
                                         ca0106_t **rchip)
 {
        ca0106_t *chip;
+       ca0106_details_t *c;
        int err;
        int ch;
        static snd_device_ops_t ops = {
@@ -1003,8 +1129,8 @@ static int __devinit snd_ca0106_create(snd_card_t *card,
   
        if ((err = pci_enable_device(pci)) < 0)
                return err;
-       if (pci_set_dma_mask(pci, 0xffffffffUL) < 0 ||
-           pci_set_consistent_dma_mask(pci, 0xffffffffUL) < 0) {
+       if (pci_set_dma_mask(pci, DMA_32BIT_MASK) < 0 ||
+           pci_set_consistent_dma_mask(pci, DMA_32BIT_MASK) < 0) {
                printk(KERN_ERR "error to set 32bit mask DMA\n");
                pci_disable_device(pci);
                return -ENXIO;
@@ -1054,6 +1180,15 @@ static int __devinit snd_ca0106_create(snd_card_t *card,
        printk(KERN_INFO "Model %04x Rev %08x Serial %08x\n", chip->model,
               chip->revision, chip->serial);
 #endif
+       strcpy(card->driver, "CA0106");
+       strcpy(card->shortname, "CA0106");
+
+       for (c=ca0106_chip_details; c->serial; c++) {
+               if (c->serial == chip->serial) break;
+       }
+       chip->details = c;
+       sprintf(card->longname, "%s at 0x%lx irq %i",
+               c->name, chip->port, chip->irq);
 
        outl(0, chip->port + INTE);
 
@@ -1113,7 +1248,7 @@ static int __devinit snd_ca0106_create(snd_card_t *card,
        //snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0xf0f003f); /* OSS drivers set this. */
        /* Analog or Digital output */
        snd_ca0106_ptr_write(chip, SPDIF_SELECT1, 0, 0xf);
-       snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0x000b0000); /* 0x0b000000 for digital, 0x000b0000 for analog, from win2000 drivers */
+       snd_ca0106_ptr_write(chip, SPDIF_SELECT2, 0, 0x000f0000); /* 0x0b000000 for digital, 0x000b0000 for analog, from win2000 drivers. Use 0x000f0000 for surround71 */
        chip->spdif_enable = 0; /* Set digital SPDIF output off */
        chip->capture_source = 3; /* Set CAPTURE_SOURCE */
        //snd_ca0106_ptr_write(chip, 0x45, 0, 0); /* Analogue out */
@@ -1138,13 +1273,11 @@ static int __devinit snd_ca0106_create(snd_card_t *card,
         snd_ca0106_ptr_write(chip, CAPTURE_SOURCE, 0x0, 0x333300e4); /* Select MIC, Line in, TAD in, AUX in */
        chip->capture_source = 3; /* Set CAPTURE_SOURCE */
 
-        if ((chip->serial == 0x10061102) || 
-           (chip->serial == 0x10071102) ||
-           (chip->serial == 0x10091462)) { /* The SB0410 and SB0413 use GPIO differently. */
+        if (chip->details->gpio_type == 1) { /* The SB0410 and SB0413 use GPIO differently. */
                /* FIXME: Still need to find out what the other GPIO bits do. E.g. For digital spdif out. */
                outl(0x0, chip->port+GPIO);
                //outl(0x00f0e000, chip->port+GPIO); /* Analog */
-               outl(0x005f4300, chip->port+GPIO); /* Analog */
+               outl(0x005f5301, chip->port+GPIO); /* Analog */
        } else {
                outl(0x0, chip->port+GPIO);
                outl(0x005f03a3, chip->port+GPIO); /* Analog */
@@ -1157,6 +1290,10 @@ static int __devinit snd_ca0106_create(snd_card_t *card,
        //outl(0x00000009, chip->port+HCFG);
        outl(HCFG_AC97 | HCFG_AUDIOENABLE, chip->port+HCFG); /* AC97 2.0, Enable outputs. */
 
+        if (chip->details->i2c_adc == 1) { /* The SB0410 and SB0413 use I2C to control ADC. */
+               snd_ca0106_i2c_write(chip, ADC_MUX, ADC_MUX_LINEIN); /* Enable Line-in capture. MIC in currently untested. */
+       }
+
        if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
                                  chip, &ops)) < 0) {
                snd_ca0106_free(chip);
@@ -1172,7 +1309,6 @@ static int __devinit snd_ca0106_probe(struct pci_dev *pci,
        static int dev;
        snd_card_t *card;
        ca0106_t *chip;
-       ca0106_names_t *c;
        int err;
 
        if (dev >= SNDRV_CARDS)
@@ -1207,9 +1343,7 @@ static int __devinit snd_ca0106_probe(struct pci_dev *pci,
                snd_card_free(card);
                return err;
        }
-        if ((chip->serial != 0x10061102) && 
-           (chip->serial != 0x10071102) && 
-           (chip->serial != 0x10091462) ) { /* The SB0410 and SB0413 do not have an ac97 chip. */
+        if (chip->details->ac97 == 1) { /* The SB0410 and SB0413 do not have an AC97 chip. */
                if ((err = snd_ca0106_ac97(chip)) < 0) {
                        snd_card_free(card);
                        return err;
@@ -1222,15 +1356,6 @@ static int __devinit snd_ca0106_probe(struct pci_dev *pci,
 
        snd_ca0106_proc_init(chip);
 
-       strcpy(card->driver, "CA0106");
-       strcpy(card->shortname, "CA0106");
-
-       for (c=ca0106_chip_names; c->serial; c++) {
-               if (c->serial == chip->serial) break;
-       }
-       sprintf(card->longname, "%s at 0x%lx irq %i",
-               c->name, chip->port, chip->irq);
-
        if ((err = snd_card_register(card)) < 0) {
                snd_card_free(card);
                return err;
@@ -1267,7 +1392,7 @@ static int __init alsa_card_ca0106_init(void)
 {
        int err;
 
-       if ((err = pci_module_init(&driver)) > 0)
+       if ((err = pci_register_driver(&driver)) > 0)
                return err;
 
        return 0;
index 97bed1b0899d43dee4bb0771bf3492edcf605739..0e5e9ce0ff28be76943cf8d9809a0028cbaa9bfe 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
  *  Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
- *  Version: 0.0.16
+ *  Version: 0.0.17
  *
  *  FEATURES currently supported:
  *    See ca0106_main.c for features.
@@ -37,6 +37,8 @@
  *    Separated ca0106.c into separate functional .c files.
  *  0.0.16
  *    Modified Copyright message.
+ *  0.0.17
+ *    Implement Mic and Line in Capture.
  *
  *  This code was initally based on code from ALSA's emu10k1x.c which is:
  *  Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
@@ -113,7 +115,7 @@ static int snd_ca0106_shared_spdif_put(snd_kcontrol_t * kcontrol,
                } else {
                        /* Analog */
                        snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
-                       snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000b0000);
+                       snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000);
                        snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0,
                                snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000);
                        mask = inl(emu->port + GPIO) | 0x101;
@@ -183,6 +185,65 @@ static snd_kcontrol_new_t snd_ca0106_capture_source __devinitdata =
        .put =          snd_ca0106_capture_source_put
 };
 
+static int snd_ca0106_capture_mic_line_in_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+       static char *texts[2] = { "Line in", "Mic in" };
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 2;
+       if (uinfo->value.enumerated.item > 1)
+                uinfo->value.enumerated.item = 1;
+       strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+       return 0;
+}
+
+static int snd_ca0106_capture_mic_line_in_get(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       ca0106_t *emu = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.enumerated.item[0] = emu->capture_mic_line_in;
+       return 0;
+}
+
+static int snd_ca0106_capture_mic_line_in_put(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       ca0106_t *emu = snd_kcontrol_chip(kcontrol);
+       unsigned int val;
+       int change = 0;
+       u32 tmp;
+
+       val = ucontrol->value.enumerated.item[0] ;
+       change = (emu->capture_mic_line_in != val);
+       if (change) {
+               emu->capture_mic_line_in = val;
+               if (val) {
+                       snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_PHONE); /* Mute input */
+                       tmp = inl(emu->port+GPIO) & ~0x400;
+                       tmp = tmp | 0x400;
+                       outl(tmp, emu->port+GPIO);
+                       snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC);
+               } else {
+                       snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_PHONE); /* Mute input */
+                       tmp = inl(emu->port+GPIO) & ~0x400;
+                       outl(tmp, emu->port+GPIO);
+                       snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN);
+               }
+       }
+        return change;
+}
+
+static snd_kcontrol_new_t snd_ca0106_capture_mic_line_in __devinitdata =
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name =         "Mic/Line in Capture",
+       .info =         snd_ca0106_capture_mic_line_in_info,
+       .get =          snd_ca0106_capture_mic_line_in_get,
+       .put =          snd_ca0106_capture_mic_line_in_put
+};
+
 static int snd_ca0106_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
        uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
@@ -437,7 +498,7 @@ static snd_kcontrol_new_t snd_ca0106_volume_control_analog_center_lfe =
 static snd_kcontrol_new_t snd_ca0106_volume_control_analog_unknown =
 {
         .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
-        .name =         "Analog Unknown Volume",
+        .name =         "Analog Side Volume",
         .info =         snd_ca0106_volume_info,
         .get =          snd_ca0106_volume_get_analog_unknown,
         .put =          snd_ca0106_volume_put_analog_unknown
@@ -620,10 +681,11 @@ int __devinit snd_ca0106_mixer(ca0106_t *emu)
                return -ENOMEM;
        if ((err = snd_ctl_add(card, kctl)))
                return err;
-       if ((kctl = ctl_find(card, SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT))) != NULL) {
-               /* already defined by ac97, remove it */
-               /* FIXME: or do we need both controls? */
-               remove_ctl(card, SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT));
+       if (emu->details->i2c_adc == 1) {
+               if ((kctl = snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu)) == NULL)
+                       return -ENOMEM;
+               if ((err = snd_ctl_add(card, kctl)))
+                       return err;
        }
        if ((kctl = snd_ctl_new1(&snd_ca0106_spdif_control, emu)) == NULL)
                return -ENOMEM;
index afb711421e479e7e70c4fa95e90607cab1a32f46..1c9cc821d1b96c3cb60ff359fcad82aa1773a790 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
  *  Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
- *  Version: 0.0.17
+ *  Version: 0.0.18
  *
  *  FEATURES currently supported:
  *    See ca0106_main.c for features.
@@ -39,7 +39,9 @@
  *    Modified Copyright message.
  *  0.0.17
  *    Add iec958 file in proc file system to show status of SPDIF in.
- *    
+ *  0.0.18
+ *    Implement support for Line-in capture on SB Live 24bit.
+ *
  *  This code was initally based on code from ALSA's emu10k1x.c which is:
  *  Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
  *
@@ -95,7 +97,7 @@ static struct snd_ca0106_category_str snd_ca0106_con_category[] = {
 };
 
 
-void snd_ca0106_proc_dump_iec958( snd_info_buffer_t *buffer, u32 value)
+static void snd_ca0106_proc_dump_iec958( snd_info_buffer_t *buffer, u32 value)
 {
        int i;
        u32 status[4];
@@ -407,6 +409,20 @@ static void snd_ca0106_proc_reg_write(snd_info_entry_t *entry,
         }
 }
 
+static void snd_ca0106_proc_i2c_write(snd_info_entry_t *entry, 
+                                      snd_info_buffer_t * buffer)
+{
+       ca0106_t *emu = entry->private_data;
+        char line[64];
+        unsigned int reg, val;
+        while (!snd_info_get_line(buffer, line, sizeof(line))) {
+                if (sscanf(line, "%x %x", &reg, &val) != 2)
+                        continue;
+                if ((reg <= 0x7f) || (val <= 0x1ff)) {
+                        snd_ca0106_i2c_write(emu, reg, val);
+               }
+        }
+}
 
 int __devinit snd_ca0106_proc_init(ca0106_t * emu)
 {
@@ -418,6 +434,7 @@ int __devinit snd_ca0106_proc_init(ca0106_t * emu)
                snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read32);
                entry->c.text.write_size = 64;
                entry->c.text.write = snd_ca0106_proc_reg_write32;
+               entry->mode |= S_IWUSR;
        }
        if(! snd_card_proc_new(emu->card, "ca0106_reg16", &entry))
                snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read16);
@@ -427,6 +444,14 @@ int __devinit snd_ca0106_proc_init(ca0106_t * emu)
                snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_reg_read1);
                entry->c.text.write_size = 64;
                entry->c.text.write = snd_ca0106_proc_reg_write;
+               entry->mode |= S_IWUSR;
+//             entry->private_data = emu;
+       }
+       if(! snd_card_proc_new(emu->card, "ca0106_i2c", &entry)) {
+               snd_info_set_text_ops(entry, emu, 1024, snd_ca0106_proc_i2c_write);
+               entry->c.text.write_size = 64;
+               entry->c.text.write = snd_ca0106_proc_i2c_write;
+               entry->mode |= S_IWUSR;
 //             entry->private_data = emu;
        }
        if(! snd_card_proc_new(emu->card, "ca0106_regs2", &entry)) 
index 113208fbde1bc3487063f2106cc55db578f40b47..b4503385ea69b62b9717a244bc6d9d0c571bb4e8 100644 (file)
@@ -519,40 +519,50 @@ inline static unsigned char snd_cmipci_read_b(cmipci_t *cm, unsigned int cmd)
 }
 
 /* bit operations for dword register */
-static void snd_cmipci_set_bit(cmipci_t *cm, unsigned int cmd, unsigned int flag)
+static int snd_cmipci_set_bit(cmipci_t *cm, unsigned int cmd, unsigned int flag)
 {
-       unsigned int val;
-       val = inl(cm->iobase + cmd);
+       unsigned int val, oval;
+       val = oval = inl(cm->iobase + cmd);
        val |= flag;
+       if (val == oval)
+               return 0;
        outl(val, cm->iobase + cmd);
+       return 1;
 }
 
-static void snd_cmipci_clear_bit(cmipci_t *cm, unsigned int cmd, unsigned int flag)
+static int snd_cmipci_clear_bit(cmipci_t *cm, unsigned int cmd, unsigned int flag)
 {
-       unsigned int val;
-       val = inl(cm->iobase + cmd);
+       unsigned int val, oval;
+       val = oval = inl(cm->iobase + cmd);
        val &= ~flag;
+       if (val == oval)
+               return 0;
        outl(val, cm->iobase + cmd);
+       return 1;
 }
 
-#if 0 // not used
 /* bit operations for byte register */
-static void snd_cmipci_set_bit_b(cmipci_t *cm, unsigned int cmd, unsigned char flag)
+static int snd_cmipci_set_bit_b(cmipci_t *cm, unsigned int cmd, unsigned char flag)
 {
-       unsigned char val;
-       val = inb(cm->iobase + cmd);
+       unsigned char val, oval;
+       val = oval = inb(cm->iobase + cmd);
        val |= flag;
+       if (val == oval)
+               return 0;
        outb(val, cm->iobase + cmd);
+       return 1;
 }
 
-static void snd_cmipci_clear_bit_b(cmipci_t *cm, unsigned int cmd, unsigned char flag)
+static int snd_cmipci_clear_bit_b(cmipci_t *cm, unsigned int cmd, unsigned char flag)
 {
-       unsigned char val;
-       val = inb(cm->iobase + cmd);
+       unsigned char val, oval;
+       val = oval = inb(cm->iobase + cmd);
        val &= ~flag;
+       if (val == oval)
+               return 0;
        outb(val, cm->iobase + cmd);
+       return 1;
 }
-#endif
 
 
 /*
@@ -2250,8 +2260,8 @@ DEFINE_SWITCH_ARG(exchange_dac, CM_REG_MISC_CTRL, CM_XCHGDAC, 0, 0, 0); /* rever
 DEFINE_SWITCH_ARG(exchange_dac, CM_REG_MISC_CTRL, CM_XCHGDAC, CM_XCHGDAC, 0, 0);
 #endif
 DEFINE_BIT_SWITCH_ARG(fourch, CM_REG_MISC_CTRL, CM_N4SPK3D, 0, 0);
-DEFINE_BIT_SWITCH_ARG(line_rear, CM_REG_MIXER1, CM_SPK4, 1, 0);
-DEFINE_BIT_SWITCH_ARG(line_bass, CM_REG_LEGACY_CTRL, CM_LINE_AS_BASS, 0, 0);
+// DEFINE_BIT_SWITCH_ARG(line_rear, CM_REG_MIXER1, CM_SPK4, 1, 0);
+// DEFINE_BIT_SWITCH_ARG(line_bass, CM_REG_LEGACY_CTRL, CM_LINE_AS_BASS, 0, 0);
 // DEFINE_BIT_SWITCH_ARG(joystick, CM_REG_FUNCTRL1, CM_JYSTK_EN, 0, 0); /* now module option */
 DEFINE_SWITCH_ARG(modem, CM_REG_MISC_CTRL, CM_FLINKON|CM_FLINKOFF, CM_FLINKON, 0, 0);
 
@@ -2300,10 +2310,114 @@ static int snd_cmipci_spdout_enable_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_v
 }
 
 
+static int snd_cmipci_line_in_mode_info(snd_kcontrol_t *kcontrol,
+                                       snd_ctl_elem_info_t *uinfo)
+{
+       cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+       static char *texts[3] = { "Line-In", "Rear Output", "Bass Output" };
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = cm->chip_version >= 39 ? 3 : 2;
+       if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+               uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+       strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+       return 0;
+}
+
+static inline unsigned int get_line_in_mode(cmipci_t *cm)
+{
+       unsigned int val;
+       if (cm->chip_version >= 39) {
+               val = snd_cmipci_read(cm, CM_REG_LEGACY_CTRL);
+               if (val & CM_LINE_AS_BASS)
+                       return 2;
+       }
+       val = snd_cmipci_read_b(cm, CM_REG_MIXER1);
+       if (val & CM_SPK4)
+               return 1;
+       return 0;
+}
+
+static int snd_cmipci_line_in_mode_get(snd_kcontrol_t *kcontrol,
+                                      snd_ctl_elem_value_t *ucontrol)
+{
+       cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+
+       spin_lock_irq(&cm->reg_lock);
+       ucontrol->value.enumerated.item[0] = get_line_in_mode(cm);
+       spin_unlock_irq(&cm->reg_lock);
+       return 0;
+}
+
+static int snd_cmipci_line_in_mode_put(snd_kcontrol_t *kcontrol,
+                                      snd_ctl_elem_value_t *ucontrol)
+{
+       cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+       int change;
+
+       spin_lock_irq(&cm->reg_lock);
+       if (ucontrol->value.enumerated.item[0] == 2)
+               change = snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_LINE_AS_BASS);
+       else
+               change = snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_LINE_AS_BASS);
+       if (ucontrol->value.enumerated.item[0] == 1)
+               change |= snd_cmipci_set_bit_b(cm, CM_REG_MIXER1, CM_SPK4);
+       else
+               change |= snd_cmipci_clear_bit_b(cm, CM_REG_MIXER1, CM_SPK4);
+       spin_unlock_irq(&cm->reg_lock);
+       return change;
+}
+
+static int snd_cmipci_mic_in_mode_info(snd_kcontrol_t *kcontrol,
+                                      snd_ctl_elem_info_t *uinfo)
+{
+       static char *texts[2] = { "Mic-In", "Center/LFE Output" };
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 2;
+       if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+               uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+       strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+       return 0;
+}
+
+static int snd_cmipci_mic_in_mode_get(snd_kcontrol_t *kcontrol,
+                                     snd_ctl_elem_value_t *ucontrol)
+{
+       cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+       /* same bit as spdi_phase */
+       spin_lock_irq(&cm->reg_lock);
+       ucontrol->value.enumerated.item[0] = 
+               (snd_cmipci_read_b(cm, CM_REG_MISC) & CM_SPDIF_INVERSE) ? 1 : 0;
+       spin_unlock_irq(&cm->reg_lock);
+       return 0;
+}
+
+static int snd_cmipci_mic_in_mode_put(snd_kcontrol_t *kcontrol,
+                                     snd_ctl_elem_value_t *ucontrol)
+{
+       cmipci_t *cm = snd_kcontrol_chip(kcontrol);
+       int change;
+
+       spin_lock_irq(&cm->reg_lock);
+       if (ucontrol->value.enumerated.item[0])
+               change = snd_cmipci_set_bit_b(cm, CM_REG_MISC, CM_SPDIF_INVERSE);
+       else
+               change = snd_cmipci_clear_bit_b(cm, CM_REG_MISC, CM_SPDIF_INVERSE);
+       spin_unlock_irq(&cm->reg_lock);
+       return change;
+}
+
 /* both for CM8338/8738 */
 static snd_kcontrol_new_t snd_cmipci_mixer_switches[] __devinitdata = {
        DEFINE_MIXER_SWITCH("Four Channel Mode", fourch),
-       DEFINE_MIXER_SWITCH("Line-In As Rear", line_rear),
+       {
+               .name = "Line-In Mode",
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .info = snd_cmipci_line_in_mode_info,
+               .get = snd_cmipci_line_in_mode_get,
+               .put = snd_cmipci_line_in_mode_put,
+       },
 };
 
 /* for non-multichannel chips */
@@ -2341,10 +2455,15 @@ static snd_kcontrol_new_t snd_cmipci_old_mixer_switches[] __devinitdata = {
 
 /* only for model 039 or later */
 static snd_kcontrol_new_t snd_cmipci_extra_mixer_switches[] __devinitdata = {
-       DEFINE_MIXER_SWITCH("Line-In As Bass", line_bass),
        DEFINE_MIXER_SWITCH("IEC958 In Select", spdif_in_sel2),
        DEFINE_MIXER_SWITCH("IEC958 In Phase Inverse", spdi_phase2),
-       DEFINE_MIXER_SWITCH("Mic As Center/LFE", spdi_phase), /* same bit as spdi_phase */
+       {
+               .name = "Mic-In Mode",
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .info = snd_cmipci_mic_in_mode_info,
+               .get = snd_cmipci_mic_in_mode_get,
+               .put = snd_cmipci_mic_in_mode_put,
+       }
 };
 
 /* card control switches */
@@ -2944,7 +3063,7 @@ static struct pci_driver driver = {
        
 static int __init alsa_card_cmipci_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_cmipci_exit(void)
index d7e06b3caf973bae1af659f170dacbca38ccf438..b6e1854e938980e4678cdb89da4da1d3cd2f9b48 100644 (file)
@@ -206,7 +206,10 @@ MODULE_PARM_DESC(dual_codec, "Secondary Codec ID (0 = disabled).");
 
 #define BA0_PMCS               0x0344  /* Power Management Control/Status */
 #define BA0_CWPR               0x03e0  /* Configuration Write Protect */
+
 #define BA0_EPPMC              0x03e4  /* Extended PCI Power Management Control */
+#define BA0_EPPMC_FPDN         (1<<14) /* Full Power DowN */
+
 #define BA0_GPIOR              0x03e8  /* GPIO Pin Interface Register */
 
 #define BA0_SPMC               0x03ec  /* Serial Port Power Management Control (& ASDIN2 enable) */
@@ -1461,6 +1464,11 @@ static int snd_cs4281_chip_init(cs4281_t *chip)
        int timeout;
        int retry_count = 2;
 
+       /* Having EPPMC.FPDN=1 prevent proper chip initialisation */
+       tmp = snd_cs4281_peekBA0(chip, BA0_EPPMC);
+       if (tmp & BA0_EPPMC_FPDN)
+               snd_cs4281_pokeBA0(chip, BA0_EPPMC, tmp & ~BA0_EPPMC_FPDN);
+
       __retry:
        tmp = snd_cs4281_peekBA0(chip, BA0_CFLR);
        if (tmp != BA0_CFLR_DEFAULT) {
@@ -2124,7 +2132,7 @@ static struct pci_driver driver = {
        
 static int __init alsa_card_cs4281_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_cs4281_exit(void)
index 25d6466a867c638ea2d5edd93a1cf45d663ece98..db212ecd792aac6080700f44b365c16290d6286b 100644 (file)
@@ -171,7 +171,7 @@ static struct pci_driver driver = {
 
 static int __init alsa_card_cs46xx_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_cs46xx_exit(void)
index 5f2ffb7efa061dee5c256d479b178d2f8d326c0c..fd4c50c88bc996d441f1aa9eb38e4b52f8ddcc83 100644 (file)
@@ -1295,8 +1295,7 @@ static snd_pcm_hw_constraint_list_t hw_constraints_period_sizes = {
 
 static void snd_cs46xx_pcm_free_substream(snd_pcm_runtime_t *runtime)
 {
-       cs46xx_pcm_t * cpcm = runtime->private_data;
-       kfree(cpcm);
+       kfree(runtime->private_data);
 }
 
 static int _cs46xx_playback_open_channel (snd_pcm_substream_t * substream,int pcm_channel_id)
index 6446afe19d80cf2147424ab62bc7a4580bedd7a0..2085a998eaeb8c79a5e3d1aa9acb1f2a6101ac40 100644 (file)
@@ -228,7 +228,7 @@ static struct pci_driver driver = {
 
 static int __init alsa_card_emu10k1_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_emu10k1_exit(void)
index c3c96f9f2c7fe4cd580073567b27aedca8e872aa..a341e758acde20340901f0868ab8c959e229bb6c 100644 (file)
@@ -170,7 +170,7 @@ static int __devinit snd_emu10k1_init(emu10k1_t * emu, int enable_ir)
                        SPCS_GENERATIONSTATUS | 0x00001200 |
                        0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT);
 
-       if (emu->audigy && emu->revision == 4) { /* audigy2 */
+       if (emu->card_capabilities->ca0151_chip) { /* audigy2 */
                /* Hacks for Alice3 to work independent of haP16V driver */
                u32 tmp;
 
@@ -189,7 +189,7 @@ static int __devinit snd_emu10k1_init(emu10k1_t * emu, int enable_ir)
                /* Enabled Phased (8-channel) P16V playback */
                outl(0x0201, emu->port + HCFG2);
                /* Set playback routing. */
-               snd_emu10k1_ptr_write(emu, CAPTURE_P16V_SOURCE, 0, 78e4);
+               snd_emu10k1_ptr20_write(emu, CAPTURE_P16V_SOURCE, 0, 0x78e4);
        }
        if (emu->audigy && (emu->serial == 0x10011102) ) { /* audigy2 Value */
                /* Hacks for Alice3 to work independent of haP16V driver */
@@ -600,7 +600,7 @@ static int snd_emu10k1_free(emu10k1_t *emu)
        if (emu->port)
                pci_release_regions(emu->pci);
        pci_disable_device(emu->pci);
-       if (emu->audigy && emu->revision == 4) /* P16V */       
+       if (emu->card_capabilities->ca0151_chip) /* P16V */     
                snd_p16v_free(emu);
        kfree(emu);
        return 0;
@@ -612,21 +612,24 @@ static int snd_emu10k1_dev_free(snd_device_t *device)
        return snd_emu10k1_free(emu);
 }
 
-/* vendor, device, subsystem, emu10k1_chip, emu10k2_chip, ca0102_chip, ca0108_chip, ca0151_chip, spk71, spdif_bug, ac97_chip, ecard, driver, name */
-
 static emu_chip_details_t emu_chip_details[] = {
        /* Audigy 2 Value AC3 out does not work yet. Need to find out how to turn off interpolators.*/
        {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10011102,
         .driver = "Audigy2", .name = "Audigy 2 Value [SB0400]", 
+        .id = "Audigy2",
         .emu10k2_chip = 1,
         .ca0108_chip = 1,
-        .spk71 = 1} ,
+        .spk71 = 1,
+        .ac97_chip = 1} ,
        {.vendor = 0x1102, .device = 0x0008, 
         .driver = "Audigy2", .name = "Audigy 2 Value [Unknown]", 
+        .id = "Audigy2",
         .emu10k2_chip = 1,
-        .ca0108_chip = 1} ,
+        .ca0108_chip = 1,
+        .ac97_chip = 1} ,
        {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20071102,
         .driver = "Audigy2", .name = "Audigy 4 PRO [SB0380]", 
+        .id = "Audigy2",
         .emu10k2_chip = 1,
         .ca0102_chip = 1,
         .ca0151_chip = 1,
@@ -635,6 +638,7 @@ static emu_chip_details_t emu_chip_details[] = {
         .ac97_chip = 1} ,
        {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20021102,
         .driver = "Audigy2", .name = "Audigy 2 ZS [SB0350]", 
+        .id = "Audigy2",
         .emu10k2_chip = 1,
         .ca0102_chip = 1,
         .ca0151_chip = 1,
@@ -643,6 +647,7 @@ static emu_chip_details_t emu_chip_details[] = {
         .ac97_chip = 1} ,
        {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x20011102,
         .driver = "Audigy2", .name = "Audigy 2 ZS [2001]", 
+        .id = "Audigy2",
         .emu10k2_chip = 1,
         .ca0102_chip = 1,
         .ca0151_chip = 1,
@@ -651,6 +656,7 @@ static emu_chip_details_t emu_chip_details[] = {
         .ac97_chip = 1} ,
        {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10071102,
         .driver = "Audigy2", .name = "Audigy 2 [SB0240]", 
+        .id = "Audigy2",
         .emu10k2_chip = 1,
         .ca0102_chip = 1,
         .ca0151_chip = 1,
@@ -659,35 +665,165 @@ static emu_chip_details_t emu_chip_details[] = {
         .ac97_chip = 1} ,
        {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10051102,
         .driver = "Audigy2", .name = "Audigy 2 EX [1005]", 
+        .id = "Audigy2",
         .emu10k2_chip = 1,
         .ca0102_chip = 1,
         .ca0151_chip = 1,
         .spdif_bug = 1} ,
        {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10021102,
         .driver = "Audigy2", .name = "Audigy 2 Platinum [SB0240P]", 
+        .id = "Audigy2",
         .emu10k2_chip = 1,
         .ca0102_chip = 1,
         .ca0151_chip = 1,
         .spk71 = 1,
         .spdif_bug = 1,
         .ac97_chip = 1} ,
+       {.vendor = 0x1102, .device = 0x0004, .revision = 0x04,
+        .driver = "Audigy2", .name = "Audigy 2 [Unknown]",
+        .id = "Audigy2",
+        .emu10k2_chip = 1,
+        .ca0102_chip = 1,
+        .ca0151_chip = 1,
+        .spdif_bug = 1,
+        .ac97_chip = 1} ,
+       {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x10020052,
+        .driver = "Audigy", .name = "Audigy 1 ES [SB0160]", 
+        .id = "Audigy",
+        .emu10k2_chip = 1,
+        .ca0102_chip = 1,
+        .spdif_bug = 1,
+        .ac97_chip = 1} ,
+       {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00531102,
+        .driver = "Audigy", .name = "Audigy 1 [SB0090]", 
+        .id = "Audigy",
+        .emu10k2_chip = 1,
+        .ca0102_chip = 1,
+        .ac97_chip = 1} ,
+       {.vendor = 0x1102, .device = 0x0004, .subsystem = 0x00511102,
+        .driver = "Audigy", .name = "Audigy 1 [SB0090]", 
+        .id = "Audigy",
+        .emu10k2_chip = 1,
+        .ca0102_chip = 1,
+        .ac97_chip = 1} ,
        {.vendor = 0x1102, .device = 0x0004,
-        .driver = "Audigy", .name = "Audigy 1 or 2 [Unknown]", 
+        .driver = "Audigy", .name = "Audigy 1 [Unknown]", 
+        .id = "Audigy",
         .emu10k2_chip = 1,
         .ca0102_chip = 1,
-        .spdif_bug = 1} ,
+        .ac97_chip = 1} ,
        {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x40011102,
         .driver = "EMU10K1", .name = "E-mu APS [4001]", 
+        .id = "APS",
         .emu10k1_chip = 1,
         .ecard = 1} ,
+       {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80611102,
+        .driver = "EMU10K1", .name = "SBLive! Player 5.1 [SB0060]", 
+        .id = "Live",
+        .emu10k1_chip = 1,
+        .ac97_chip = 1,
+        .sblive51 = 1} ,
        {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80641102,
         .driver = "EMU10K1", .name = "SB Live 5.1", 
+        .id = "Live",
+        .emu10k1_chip = 1,
+        .ac97_chip = 1,
+        .sblive51 = 1} ,
+       {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80401102,
+        .driver = "EMU10K1", .name = "SBLive! Platinum [CT4760P]", 
+        .id = "Live",
         .emu10k1_chip = 1,
         .ac97_chip = 1} ,
+       {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x00211102,
+        .driver = "EMU10K1", .name = "SBLive! [CT4620]", 
+        .id = "Live",
+        .emu10k1_chip = 1,
+        .ac97_chip = 1,
+        .sblive51 = 1} ,
+       {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x00201102,
+        .driver = "EMU10K1", .name = "SBLive! Value [CT4670]", 
+        .id = "Live",
+        .emu10k1_chip = 1,
+        .ac97_chip = 1,
+        .sblive51 = 1} ,
+       {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80221102,
+        .driver = "EMU10K1", .name = "SBLive! Value [CT4780]", 
+        .id = "Live",
+        .emu10k1_chip = 1,
+        .ac97_chip = 1,
+        .sblive51 = 1} ,
+       {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80231102,
+        .driver = "EMU10K1", .name = "SB PCI512 [CT4790]", 
+        .id = "Live",
+        .emu10k1_chip = 1,
+        .ac97_chip = 1,
+        .sblive51 = 1} ,
+       {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80261102,
+        .driver = "EMU10K1", .name = "SBLive! Value [CT4830]", 
+        .id = "Live",
+        .emu10k1_chip = 1,
+        .ac97_chip = 1,
+        .sblive51 = 1} ,
+       {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80311102,
+        .driver = "EMU10K1", .name = "SBLive! Value [CT4831]", 
+        .id = "Live",
+        .emu10k1_chip = 1,
+        .ac97_chip = 1,
+        .sblive51 = 1} ,
+       {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80271102,
+        .driver = "EMU10K1", .name = "SBLive! Value [CT4832]", 
+        .id = "Live",
+        .emu10k1_chip = 1,
+        .ac97_chip = 1,
+        .sblive51 = 1} ,
+       {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80511102,
+        .driver = "EMU10K1", .name = "SBLive! Value [CT4850]", 
+        .id = "Live",
+        .emu10k1_chip = 1,
+        .ac97_chip = 1,
+        .sblive51 = 1} ,
+       {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80281102,
+        .driver = "EMU10K1", .name = "SBLive! Value [CT4870]", 
+        .id = "Live",
+        .emu10k1_chip = 1,
+        .ac97_chip = 1,
+        .sblive51 = 1} ,
+       {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80321102,
+        .driver = "EMU10K1", .name = "SBLive! Value [CT4871]", 
+        .id = "Live",
+        .emu10k1_chip = 1,
+        .ac97_chip = 1,
+        .sblive51 = 1} ,
+       {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80611102,
+        .driver = "EMU10K1", .name = "SBLive! Value [SB0060]", 
+        .id = "Live",
+        .emu10k1_chip = 1,
+        .ac97_chip = 1,
+        .sblive51 = 1} ,
+       {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x80691102,
+        .driver = "EMU10K1", .name = "SBLive! Value [SB0101]", 
+        .id = "Live",
+        .emu10k1_chip = 1,
+        .ac97_chip = 1,
+        .sblive51 = 1} ,
+       {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x806A1102,
+        .driver = "EMU10K1", .name = "SBLive! Value [SB0103]", 
+        .id = "Live",
+        .emu10k1_chip = 1,
+        .ac97_chip = 1,
+        .sblive51 = 1} ,
+       {.vendor = 0x1102, .device = 0x0002, .subsystem = 0x806B1102,
+        .driver = "EMU10K1", .name = "SBLive! [SB0105]", 
+        .id = "Live",
+        .emu10k1_chip = 1,
+        .ac97_chip = 1,
+        .sblive51 = 1} ,
        {.vendor = 0x1102, .device = 0x0002,
         .driver = "EMU10K1", .name = "SB Live [Unknown]", 
+        .id = "Live",
         .emu10k1_chip = 1,
-        .ac97_chip = 1} ,
+        .ac97_chip = 1,
+        .sblive51 = 1} ,
        { } /* terminator */
 };
 
@@ -738,13 +874,15 @@ int __devinit snd_emu10k1_create(snd_card_t * card,
        emu->revision = revision;
        pci_read_config_dword(pci, PCI_SUBSYSTEM_VENDOR_ID, &emu->serial);
        pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &emu->model);
-       emu->card_type = EMU10K1_CARD_CREATIVE;
        snd_printdd("vendor=0x%x, device=0x%x, subsystem_vendor_id=0x%x, subsystem_id=0x%x\n",pci->vendor, pci->device, emu->serial, emu->model);
 
        for (c = emu_chip_details; c->vendor; c++) {
                if (c->vendor == pci->vendor && c->device == pci->device) {
-                       if (c->subsystem == emu->serial) break;
-                       if (c->subsystem == 0) break;
+                       if (c->subsystem && c->subsystem != emu->serial)
+                               continue;
+                       if (c->revision && c->revision != emu->revision)
+                               continue;
+                       break;
                }
        }
        if (c->vendor == 0) {
@@ -759,6 +897,23 @@ int __devinit snd_emu10k1_create(snd_card_t * card,
        else
                snd_printdd("Sound card name=%s, vendor=0x%x, device=0x%x, subsystem=0x%x\n", c->name, pci->vendor, pci->device, emu->serial);
        
+       if (!*card->id && c->id) {
+               int i, n = 0;
+               strlcpy(card->id, c->id, sizeof(card->id));
+               for (;;) {
+                       for (i = 0; i < snd_ecards_limit; i++) {
+                               if (snd_cards[i] && !strcmp(snd_cards[i]->id, card->id))
+                                       break;
+                       }
+                       if (i >= snd_ecards_limit)
+                               break;
+                       n++;
+                       if (n >= SNDRV_CARDS)
+                               break;
+                       snprintf(card->id, sizeof(card->id), "%s_%d", c->id, n);
+               }
+       }
+
        is_audigy = emu->audigy = c->emu10k2_chip;
 
        /* set the DMA transfer mask */
@@ -816,15 +971,6 @@ int __devinit snd_emu10k1_create(snd_card_t * card,
 
        pci_set_master(pci);
 
-       if (c->ecard) {
-               emu->card_type = EMU10K1_CARD_EMUAPS;
-               emu->APS = 1;
-       }
-       if (! c->ac97_chip)
-               emu->no_ac97 = 1;
-       
-       emu->spk71 = c->spk71;
-       
        emu->fx8010.fxbus_mask = 0x303f;
        if (extin_mask == 0)
                extin_mask = 0x3fcf;
@@ -833,7 +979,7 @@ int __devinit snd_emu10k1_create(snd_card_t * card,
        emu->fx8010.extin_mask = extin_mask;
        emu->fx8010.extout_mask = extout_mask;
 
-       if (emu->APS) {
+       if (emu->card_capabilities->ecard) {
                if ((err = snd_emu10k1_ecard_init(emu)) < 0) {
                        snd_emu10k1_free(emu);
                        return err;
index 27dfd8ddddf486d0c0832d72c6140d9274399814..e90c5ddd1d17ef0451e87010040c72bb71966ac7 100644 (file)
@@ -361,10 +361,7 @@ static void snd_emu10k1x_gpio_write(emu10k1x_t *emu, unsigned int value)
 
 static void snd_emu10k1x_pcm_free_substream(snd_pcm_runtime_t *runtime)
 {
-       emu10k1x_pcm_t *epcm = runtime->private_data;
-  
-       if (epcm)
-               kfree(epcm);
+       kfree(runtime->private_data);
 }
 
 static void snd_emu10k1x_pcm_interrupt(emu10k1x_t *emu, emu10k1x_voice_t *voice)
@@ -1075,6 +1072,7 @@ static int __devinit snd_emu10k1x_proc_init(emu10k1x_t * emu)
                snd_info_set_text_ops(entry, emu, 1024, snd_emu10k1x_proc_reg_read);
                entry->c.text.write_size = 64;
                entry->c.text.write = snd_emu10k1x_proc_reg_write;
+               entry->mode |= S_IWUSR;
                entry->private_data = emu;
        }
        
@@ -1627,7 +1625,7 @@ static int __init alsa_card_emu10k1x_init(void)
 {
        int err;
 
-       if ((err = pci_module_init(&driver)) > 0)
+       if ((err = pci_register_driver(&driver)) > 0)
                return err;
 
        return 0;
index b9fa2e887fee3003782d65140ad3650465bbc639..0529fb281125cf949d2de4ebd2163fbe71fb9d67 100644 (file)
@@ -1077,7 +1077,7 @@ static int __devinit _snd_emu10k1_audigy_init_efx(emu10k1_t *emu)
        gpr += 2;
        
        /* PCM Side Playback (independent from stereo mix) */
-       if (emu->spk71) {
+       if (emu->card_capabilities->spk71) {
                A_OP(icode, &ptr, iMAC0, A_GPR(playback+6), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_SIDE));
                A_OP(icode, &ptr, iMAC0, A_GPR(playback+7), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_SIDE));
                snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Side Playback Volume", gpr, 100);
@@ -1145,14 +1145,14 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
        A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_SPDIF_CD_L);
        A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_SPDIF_CD_R);
        snd_emu10k1_init_stereo_control(&controls[nctl++],
-                                       emu->no_ac97 ? "CD Playback Volume" : "Audigy CD Playback Volume",
+                                       emu->card_capabilities->ac97_chip ? "Audigy CD Playback Volume" : "CD Playback Volume",
                                        gpr, 0);
        gpr += 2;
        /* Audigy CD Capture Volume */
        A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_SPDIF_CD_L);
        A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_SPDIF_CD_R);
        snd_emu10k1_init_stereo_control(&controls[nctl++],
-                                       emu->no_ac97 ? "CD Capture Volume" : "Audigy CD Capture Volume",
+                                       emu->card_capabilities->ac97_chip ? "Audigy CD Capture Volume" : "CD Capture Volume",
                                        gpr, 0);
        gpr += 2;
 
@@ -1171,14 +1171,14 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
        A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_LINE2_L);
        A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_LINE2_R);
        snd_emu10k1_init_stereo_control(&controls[nctl++],
-                                       emu->no_ac97 ? "Line Playback Volume" : "Line2 Playback Volume",
+                                       emu->card_capabilities->ac97_chip ? "Line2 Playback Volume" : "Line Playback Volume",
                                        gpr, 0);
        gpr += 2;
        /* Line2 Capture Volume */
        A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_LINE2_L);
        A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_LINE2_R);
        snd_emu10k1_init_stereo_control(&controls[nctl++],
-                                       emu->no_ac97 ? "Line Capture Volume" : "Line2 Capture Volume",
+                                       emu->card_capabilities->ac97_chip ? "Line2 Capture Volume" : "Line Capture Volume",
                                        gpr, 0);
        gpr += 2;
         
@@ -1197,14 +1197,14 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
        A_ADD_VOLUME_IN(stereo_mix, gpr, A_EXTIN_AUX2_L);
        A_ADD_VOLUME_IN(stereo_mix+1, gpr+1, A_EXTIN_AUX2_R);
        snd_emu10k1_init_stereo_control(&controls[nctl++],
-                                       emu->no_ac97 ? "Aux Playback Volume" : "Aux2 Playback Volume",
+                                       emu->card_capabilities->ac97_chip ? "Aux2 Playback Volume" : "Aux Playback Volume",
                                        gpr, 0);
        gpr += 2;
        /* Aux2 Capture Volume */
        A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_AUX2_L);
        A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_AUX2_R);
        snd_emu10k1_init_stereo_control(&controls[nctl++],
-                                       emu->no_ac97 ? "Aux Capture Volume" : "Aux2 Capture Volume",
+                                       emu->card_capabilities->ac97_chip ? "Aux2 Capture Volume" : "Aux Capture Volume",
                                        gpr, 0);
        gpr += 2;
        
@@ -1232,7 +1232,7 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
        snd_emu10k1_init_mono_control(&controls[nctl++], "LFE Playback Volume", gpr, 0);
        gpr++;
        
-       if (emu->spk71) {
+       if (emu->card_capabilities->spk71) {
                /* Stereo Mix Side Playback */
                A_OP(icode, &ptr, iMAC0, A_GPR(playback+6), A_GPR(playback+6), A_GPR(gpr), A_GPR(stereo_mix));
                A_OP(icode, &ptr, iMAC0, A_GPR(playback+7), A_GPR(playback+7), A_GPR(gpr+1), A_GPR(stereo_mix+1));
@@ -1266,7 +1266,7 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
        A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 3), A_GPR(playback + 3), A_C_00000000, A_C_00000000); /* rear right */
        A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 4), A_GPR(playback + 4), A_C_00000000, A_C_00000000); /* center */
        A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 5), A_GPR(playback + 5), A_C_00000000, A_C_00000000); /* LFE */
-       if (emu->spk71) {
+       if (emu->card_capabilities->spk71) {
                A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 6), A_GPR(playback + 6), A_C_00000000, A_C_00000000); /* side left */
                A_OP(icode, &ptr, iACC3, A_GPR(playback + SND_EMU10K1_PLAYBACK_CHANNELS + 7), A_GPR(playback + 7), A_C_00000000, A_C_00000000); /* side right */
        }
@@ -1359,7 +1359,7 @@ A_OP(icode, &ptr, iMAC0, A_GPR(var), A_GPR(var), A_GPR(vol), A_EXTIN(input))
        A_PUT_STEREO_OUTPUT(A_EXTOUT_AREAR_L, A_EXTOUT_AREAR_R, playback+2 + SND_EMU10K1_PLAYBACK_CHANNELS);
        A_PUT_OUTPUT(A_EXTOUT_ACENTER, playback+4 + SND_EMU10K1_PLAYBACK_CHANNELS);
        A_PUT_OUTPUT(A_EXTOUT_ALFE, playback+5 + SND_EMU10K1_PLAYBACK_CHANNELS);
-       if (emu->spk71)
+       if (emu->card_capabilities->spk71)
                A_PUT_STEREO_OUTPUT(A_EXTOUT_ASIDE_L, A_EXTOUT_ASIDE_R, playback+6 + SND_EMU10K1_PLAYBACK_CHANNELS);
 
        /* headphone */
@@ -1982,22 +1982,27 @@ static int __devinit _snd_emu10k1_init_efx(emu10k1_t *emu)
                OP(icode, &ptr, iACC3, EXTOUT(EXTOUT_MIC_CAP), GPR(capture + 2), C_00000000, C_00000000);
 
        /* EFX capture - capture the 16 EXTINS */
-       OP(icode, &ptr, iACC3, FXBUS2(14), C_00000000, C_00000000, EXTIN(0));
-       OP(icode, &ptr, iACC3, FXBUS2(15), C_00000000, C_00000000, EXTIN(1));
-       OP(icode, &ptr, iACC3, FXBUS2(0), C_00000000, C_00000000, EXTIN(2));
-       OP(icode, &ptr, iACC3, FXBUS2(3), C_00000000, C_00000000, EXTIN(3));
-       /* Dont connect anything to FXBUS2 1 and 2.  These are shared with 
-        * Center/LFE on the SBLive 5.1.  The kX driver only changes the 
-        * routing when it detects an SBLive 5.1.
-        *
-        * Since only 14 of the 16 EXTINs are used, this is not a big problem.  
-        * We route AC97L and R to FX capture 14 and 15, SPDIF CD in to FX capture 
-        * 0 and 3, then the rest of the EXTINs to the corresponding FX capture 
-        * channel.
-        */
-       for (z = 4; z < 14; z++) {
-               OP(icode, &ptr, iACC3, FXBUS2(z), C_00000000, C_00000000, EXTIN(z));
+       if (emu->card_capabilities->sblive51) {
+               /* On the Live! 5.1, FXBUS2(1) and FXBUS(2) are shared with EXTOUT_ACENTER
+                * and EXTOUT_ALFE, so we can't connect inputs to them for multitrack recording.
+                *
+                * Since only 14 of the 16 EXTINs are used, this is not a big problem.  
+                * We route AC97L and R to FX capture 14 and 15, SPDIF CD in to FX capture 
+                * 0 and 3, then the rest of the EXTINs to the corresponding FX capture 
+                * channel.  Multitrack recorders will still see the center/lfe output signal 
+                * on the second and third channels.
+                */
+               OP(icode, &ptr, iACC3, FXBUS2(14), C_00000000, C_00000000, EXTIN(0));
+               OP(icode, &ptr, iACC3, FXBUS2(15), C_00000000, C_00000000, EXTIN(1));
+               OP(icode, &ptr, iACC3, FXBUS2(0), C_00000000, C_00000000, EXTIN(2));
+               OP(icode, &ptr, iACC3, FXBUS2(3), C_00000000, C_00000000, EXTIN(3));
+               for (z = 4; z < 14; z++)
+                       OP(icode, &ptr, iACC3, FXBUS2(z), C_00000000, C_00000000, EXTIN(z));
+       } else {
+               for (z = 0; z < 16; z++)
+                       OP(icode, &ptr, iACC3, FXBUS2(z), C_00000000, C_00000000, EXTIN(z));
        }
+           
 
        if (gpr > tmp) {
                snd_BUG();
@@ -2128,7 +2133,6 @@ static int snd_emu10k1_fx8010_info(emu10k1_t *emu, emu10k1_fx8010_info_t *info)
        int res;
 
        memset(info, 0, sizeof(info));
-       info->card = emu->card_type;
        info->internal_tram_size = emu->fx8010.itram_size;
        info->external_tram_size = emu->fx8010.etram_pages.bytes / 2;
        fxbus = fxbuses;
index 044663d31aa7724d090fe791b88ff4bd6998c231..6be82c5fe138089363749889afbda70fa118b8fe 100644 (file)
@@ -68,6 +68,7 @@ static int snd_emu10k1_spdif_get_mask(snd_kcontrol_t * kcontrol,
        return 0;
 }
 
+#if 0
 static int snd_audigy_spdif_output_rate_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
        static char *texts[] = {"44100", "48000", "96000"};
@@ -152,6 +153,7 @@ static snd_kcontrol_new_t snd_audigy_spdif_output_rate =
        .get =          snd_audigy_spdif_output_rate_get,
        .put =          snd_audigy_spdif_output_rate_put
 };
+#endif
 
 static int snd_emu10k1_spdif_put(snd_kcontrol_t * kcontrol,
                                  snd_ctl_elem_value_t * ucontrol)
@@ -791,7 +793,7 @@ int __devinit snd_emu10k1_mixer(emu10k1_t *emu)
                NULL
        };
 
-       if (!emu->no_ac97) {
+       if (emu->card_capabilities->ac97_chip) {
                ac97_bus_t *pbus;
                ac97_template_t ac97;
                static ac97_bus_ops_t ops = {
@@ -833,7 +835,7 @@ int __devinit snd_emu10k1_mixer(emu10k1_t *emu)
                for (; *c; c++)
                        remove_ctl(card, *c);
        } else {
-               if (emu->APS)
+               if (emu->card_capabilities->ecard)
                        strcpy(emu->card->mixername, "EMU APS");
                else if (emu->audigy)
                        strcpy(emu->card->mixername, "SB Audigy");
@@ -918,7 +920,7 @@ int __devinit snd_emu10k1_mixer(emu10k1_t *emu)
                mix->attn[0] = 0xffff;
        }
        
-       if (! emu->APS) { /* FIXME: APS has these controls? */
+       if (! emu->card_capabilities->ecard) { /* FIXME: APS has these controls? */
                /* sb live! and audigy */
                if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_mask_control, emu)) == NULL)
                        return -ENOMEM;
@@ -935,18 +937,20 @@ int __devinit snd_emu10k1_mixer(emu10k1_t *emu)
                        return -ENOMEM;
                if ((err = snd_ctl_add(card, kctl)))
                        return err;
+#if 0
                if ((kctl = snd_ctl_new1(&snd_audigy_spdif_output_rate, emu)) == NULL)
                        return -ENOMEM;
                if ((err = snd_ctl_add(card, kctl)))
                        return err;
-       } else if (! emu->APS) {
+#endif
+       } else if (! emu->card_capabilities->ecard) {
                /* sb live! */
                if ((kctl = snd_ctl_new1(&snd_emu10k1_shared_spdif, emu)) == NULL)
                        return -ENOMEM;
                if ((err = snd_ctl_add(card, kctl)))
                        return err;
        }
-       if (emu->audigy && emu->revision == 4) { /* P16V */
+       if (emu->card_capabilities->ca0151_chip) { /* P16V */
                if ((err = snd_p16v_mixer(emu)))
                        return err;
        }
index d1c2a02c486bcbcb466062d4453c13a364b66231..520b99af5f550d0429ef030c8c90c0d8027c0171 100644 (file)
@@ -262,7 +262,7 @@ static unsigned int emu10k1_select_interprom(unsigned int pitch_target)
  *
  * returns: cache invalidate size in samples
  */
-static int inline emu10k1_ccis(int stereo, int w_16)
+static inline int emu10k1_ccis(int stereo, int w_16)
 {
        if (w_16) {
                return stereo ? 24 : 26;
@@ -991,9 +991,7 @@ static void snd_emu10k1_pcm_efx_mixer_notify(emu10k1_t *emu, int idx, int activa
 
 static void snd_emu10k1_pcm_free_substream(snd_pcm_runtime_t *runtime)
 {
-       emu10k1_pcm_t *epcm = runtime->private_data;
-
-       kfree(epcm);
+       kfree(runtime->private_data);
 }
 
 static int snd_emu10k1_efx_playback_close(snd_pcm_substream_t * substream)
index d990d5eb45a811df59db01e98ba42e3b35355c53..cc22707c91fae86b4cda85809c250311b7c97269 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/init.h>
 #include <sound/core.h>
 #include <sound/emu10k1.h>
+#include "p16v.h"
 
 static void snd_emu10k1_proc_spdif_status(emu10k1_t * emu,
                                          snd_info_buffer_t * buffer,
@@ -44,28 +45,34 @@ static void snd_emu10k1_proc_spdif_status(emu10k1_t * emu,
        unsigned int status, rate = 0;
        
        status = snd_emu10k1_ptr_read(emu, status_reg, 0);
-       if (rate_reg > 0)
-               rate = snd_emu10k1_ptr_read(emu, rate_reg, 0);
 
        snd_iprintf(buffer, "\n%s\n", title);
 
-       snd_iprintf(buffer, "Professional Mode     : %s\n", (status & SPCS_PROFESSIONAL) ? "yes" : "no");
-       snd_iprintf(buffer, "Not Audio Data        : %s\n", (status & SPCS_NOTAUDIODATA) ? "yes" : "no");
-       snd_iprintf(buffer, "Copyright             : %s\n", (status & SPCS_COPYRIGHT) ? "yes" : "no");
-       snd_iprintf(buffer, "Emphasis              : %s\n", emphasis[(status & SPCS_EMPHASISMASK) >> 3]);
-       snd_iprintf(buffer, "Mode                  : %i\n", (status & SPCS_MODEMASK) >> 6);
-       snd_iprintf(buffer, "Category Code         : 0x%x\n", (status & SPCS_CATEGORYCODEMASK) >> 8);
-       snd_iprintf(buffer, "Generation Status     : %s\n", status & SPCS_GENERATIONSTATUS ? "original" : "copy");
-       snd_iprintf(buffer, "Source Mask           : %i\n", (status & SPCS_SOURCENUMMASK) >> 16);
-       snd_iprintf(buffer, "Channel Number        : %s\n", channel[(status & SPCS_CHANNELNUMMASK) >> 20]);
-       snd_iprintf(buffer, "Sample Rate           : %iHz\n", samplerate[(status & SPCS_SAMPLERATEMASK) >> 24]);
-       snd_iprintf(buffer, "Clock Accuracy        : %s\n", clkaccy[(status & SPCS_CLKACCYMASK) >> 28]);
-
-       if (rate_reg > 0) {
-               snd_iprintf(buffer, "S/PDIF Locked         : %s\n", rate & SRCS_SPDIFLOCKED ? "on" : "off");
-               snd_iprintf(buffer, "Rate Locked           : %s\n", rate & SRCS_RATELOCKED ? "on" : "off");
-               snd_iprintf(buffer, "Estimated Sample Rate : 0x%x\n", rate & SRCS_ESTSAMPLERATE);
+       if (status != 0xffffffff) {
+               snd_iprintf(buffer, "Professional Mode     : %s\n", (status & SPCS_PROFESSIONAL) ? "yes" : "no");
+               snd_iprintf(buffer, "Not Audio Data        : %s\n", (status & SPCS_NOTAUDIODATA) ? "yes" : "no");
+               snd_iprintf(buffer, "Copyright             : %s\n", (status & SPCS_COPYRIGHT) ? "yes" : "no");
+               snd_iprintf(buffer, "Emphasis              : %s\n", emphasis[(status & SPCS_EMPHASISMASK) >> 3]);
+               snd_iprintf(buffer, "Mode                  : %i\n", (status & SPCS_MODEMASK) >> 6);
+               snd_iprintf(buffer, "Category Code         : 0x%x\n", (status & SPCS_CATEGORYCODEMASK) >> 8);
+               snd_iprintf(buffer, "Generation Status     : %s\n", status & SPCS_GENERATIONSTATUS ? "original" : "copy");
+               snd_iprintf(buffer, "Source Mask           : %i\n", (status & SPCS_SOURCENUMMASK) >> 16);
+               snd_iprintf(buffer, "Channel Number        : %s\n", channel[(status & SPCS_CHANNELNUMMASK) >> 20]);
+               snd_iprintf(buffer, "Sample Rate           : %iHz\n", samplerate[(status & SPCS_SAMPLERATEMASK) >> 24]);
+               snd_iprintf(buffer, "Clock Accuracy        : %s\n", clkaccy[(status & SPCS_CLKACCYMASK) >> 28]);
+
+               if (rate_reg > 0) {
+                       rate = snd_emu10k1_ptr_read(emu, rate_reg, 0);
+                       snd_iprintf(buffer, "S/PDIF Valid          : %s\n", rate & SRCS_SPDIFVALID ? "on" : "off");
+                       snd_iprintf(buffer, "S/PDIF Locked         : %s\n", rate & SRCS_SPDIFLOCKED ? "on" : "off");
+                       snd_iprintf(buffer, "Rate Locked           : %s\n", rate & SRCS_RATELOCKED ? "on" : "off");
+                       /* From ((Rate * 48000 ) / 262144); */
+                       snd_iprintf(buffer, "Estimated Sample Rate : %d\n", ((rate & 0xFFFFF ) * 375) >> 11); 
+               }
+       } else {
+               snd_iprintf(buffer, "No signal detected.\n");
        }
+
 }
 
 static void snd_emu10k1_proc_read(snd_info_entry_t *entry, 
@@ -182,7 +189,7 @@ static void snd_emu10k1_proc_read(snd_info_entry_t *entry,
        
        snd_iprintf(buffer, "EMU10K1\n\n");
        snd_iprintf(buffer, "Card                  : %s\n",
-                   emu->audigy ? "Audigy" : (emu->APS ? "EMU APS" : "Creative"));
+                   emu->audigy ? "Audigy" : (emu->card_capabilities->ecard ? "EMU APS" : "Creative"));
        snd_iprintf(buffer, "Internal TRAM (words) : 0x%x\n", emu->fx8010.itram_size);
        snd_iprintf(buffer, "External TRAM (words) : 0x%x\n", (int)emu->fx8010.etram_pages.bytes / 2);
        snd_iprintf(buffer, "\n");
@@ -223,15 +230,35 @@ static void snd_emu10k1_proc_read(snd_info_entry_t *entry,
        snd_iprintf(buffer, "\nAll FX Outputs        :\n");
        for (idx = 0; idx < (emu->audigy ? 64 : 32); idx++)
                snd_iprintf(buffer, "  Output %02i [%s]\n", idx, outputs[idx]);
-       snd_emu10k1_proc_spdif_status(emu, buffer, "S/PDIF Output 0", SPCS0, -1);
-       snd_emu10k1_proc_spdif_status(emu, buffer, "S/PDIF Output 1", SPCS1, -1);
-       snd_emu10k1_proc_spdif_status(emu, buffer, "S/PDIF Output 2/3", SPCS2, -1);
-       snd_emu10k1_proc_spdif_status(emu, buffer, "CD-ROM S/PDIF", CDCS, CDSRCS);
-       snd_emu10k1_proc_spdif_status(emu, buffer, "General purpose S/PDIF", GPSCS, GPSRCS);
+}
+
+static void snd_emu10k1_proc_spdif_read(snd_info_entry_t *entry, 
+                                 snd_info_buffer_t * buffer)
+{
+       emu10k1_t *emu = entry->private_data;
+       snd_emu10k1_proc_spdif_status(emu, buffer, "CD-ROM S/PDIF In", CDCS, CDSRCS);
+       snd_emu10k1_proc_spdif_status(emu, buffer, "Optical or Coax S/PDIF In", GPSCS, GPSRCS);
+#if 0
        val = snd_emu10k1_ptr_read(emu, ZVSRCS, 0);
        snd_iprintf(buffer, "\nZoomed Video\n");
        snd_iprintf(buffer, "Rate Locked           : %s\n", val & SRCS_RATELOCKED ? "on" : "off");
        snd_iprintf(buffer, "Estimated Sample Rate : 0x%x\n", val & SRCS_ESTSAMPLERATE);
+#endif
+}
+
+static void snd_emu10k1_proc_rates_read(snd_info_entry_t *entry, 
+                                 snd_info_buffer_t * buffer)
+{
+       static int samplerate[8] = { 44100, 48000, 96000, 192000, 4, 5, 6, 7 };
+       emu10k1_t *emu = entry->private_data;
+       unsigned int val, tmp, n;
+       val = snd_emu10k1_ptr20_read(emu, CAPTURE_RATE_STATUS, 0);
+       tmp = (val >> 16) & 0x8;
+       for (n=0;n<4;n++) {
+               tmp = val >> (16 + (n*4));
+               if (tmp & 0x8) snd_iprintf(buffer, "Channel %d: Rate=%d\n", n, samplerate[tmp & 0x7]);
+               else snd_iprintf(buffer, "Channel %d: No input\n", n);
+       }
 }
 
 static void snd_emu10k1_proc_acode_read(snd_info_entry_t *entry, 
@@ -500,32 +527,46 @@ int __devinit snd_emu10k1_proc_init(emu10k1_t * emu)
                snd_info_set_text_ops(entry, emu, 1024, snd_emu_proc_io_reg_read);
                entry->c.text.write_size = 64;
                entry->c.text.write = snd_emu_proc_io_reg_write;
+               entry->mode |= S_IWUSR;
        }
        if (! snd_card_proc_new(emu->card, "ptr_regs00a", &entry)) {
                snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read00a);
                entry->c.text.write_size = 64;
                entry->c.text.write = snd_emu_proc_ptr_reg_write00;
+               entry->mode |= S_IWUSR;
        }
        if (! snd_card_proc_new(emu->card, "ptr_regs00b", &entry)) {
                snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read00b);
                entry->c.text.write_size = 64;
                entry->c.text.write = snd_emu_proc_ptr_reg_write00;
+               entry->mode |= S_IWUSR;
        }
        if (! snd_card_proc_new(emu->card, "ptr_regs20a", &entry)) {
                snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read20a);
                entry->c.text.write_size = 64;
                entry->c.text.write = snd_emu_proc_ptr_reg_write20;
+               entry->mode |= S_IWUSR;
        }
        if (! snd_card_proc_new(emu->card, "ptr_regs20b", &entry)) {
                snd_info_set_text_ops(entry, emu, 65536, snd_emu_proc_ptr_reg_read20b);
                entry->c.text.write_size = 64;
                entry->c.text.write = snd_emu_proc_ptr_reg_write20;
+               entry->mode |= S_IWUSR;
        }
 #endif
        
        if (! snd_card_proc_new(emu->card, "emu10k1", &entry))
                snd_info_set_text_ops(entry, emu, 2048, snd_emu10k1_proc_read);
 
+       if (emu->card_capabilities->emu10k2_chip) {
+               if (! snd_card_proc_new(emu->card, "spdif-in", &entry))
+                       snd_info_set_text_ops(entry, emu, 2048, snd_emu10k1_proc_spdif_read);
+       }
+       if (emu->card_capabilities->ca0151_chip) {
+               if (! snd_card_proc_new(emu->card, "capture-rates", &entry))
+                       snd_info_set_text_ops(entry, emu, 2048, snd_emu10k1_proc_rates_read);
+       }
+
        if (! snd_card_proc_new(emu->card, "voices", &entry))
                snd_info_set_text_ops(entry, emu, 2048, snd_emu10k1_proc_voices_read);
 
index b81a7cafff39e5d1d6bf2dd3185b86e8bbc512e5..cd8460d56752d4edfb53a5415111835b7787b5e9 100644 (file)
@@ -37,7 +37,7 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id, struct pt_regs *regs)
        int handled = 0;
 
        while ((status = inl(emu->port + IPR)) != 0) {
-               // printk("irq - status = 0x%x\n", status);
+               //printk("emu10k1 irq - status = 0x%x\n", status);
                orig_status = status;
                handled = 1;
                if (status & IPR_PCIERROR) {
@@ -147,9 +147,36 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                                snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE);
                        status &= ~IPR_FXDSP;
                }
+               if (status & IPR_P16V) {
+                       while ((status2 = inl(emu->port + IPR2)) != 0) {
+                               u32 mask = INTE2_PLAYBACK_CH_0_LOOP;  /* Full Loop */
+                               emu10k1_voice_t *pvoice = &(emu->p16v_voices[0]);
+                               emu10k1_voice_t *cvoice = &(emu->p16v_capture_voice);
+
+                               //printk(KERN_INFO "status2=0x%x\n", status2);
+                               orig_status2 = status2;
+                               if(status2 & mask) {
+                                       if(pvoice->use) {
+                                               snd_pcm_period_elapsed(pvoice->epcm->substream);
+                                       } else { 
+                                               snd_printk(KERN_ERR "p16v: status: 0x%08x, mask=0x%08x, pvoice=%p, use=%d\n", status2, mask, pvoice, pvoice->use);
+                                       }
+                               }
+                               if(status2 & 0x110000) {
+                                       //printk(KERN_INFO "capture int found\n");
+                                       if(cvoice->use) {
+                                               //printk(KERN_INFO "capture period_elapsed\n");
+                                               snd_pcm_period_elapsed(cvoice->epcm->substream);
+                                       }
+                               }
+                               outl(orig_status2, emu->port + IPR2); /* ack all */
+                       }
+                       status &= ~IPR_P16V;
+               }
+
                if (status) {
                        unsigned int bits;
-                       //snd_printk(KERN_ERR "emu10k1: unhandled interrupt: 0x%08x\n", status);
+                       snd_printk(KERN_ERR "emu10k1: unhandled interrupt: 0x%08x\n", status);
                        //make sure any interrupts we don't handle are disabled:
                        bits = INTE_FXDSPENABLE |
                                INTE_PCIERRORENABLE |
@@ -170,20 +197,5 @@ irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                }
                outl(orig_status, emu->port + IPR); /* ack all */
        }
-       if (emu->audigy && emu->revision == 4) { /* P16V */     
-               while ((status2 = inl(emu->port + IPR2)) != 0) {
-                       u32 mask = INTE2_PLAYBACK_CH_0_LOOP;  /* Full Loop */
-                       emu10k1_voice_t *pvoice = &(emu->p16v_voices[0]);
-                       orig_status2 = status2;
-                       if(status2 & mask) {
-                               if(pvoice->use) {
-                                       snd_pcm_period_elapsed(pvoice->epcm->substream);
-                               } else { 
-                                       snd_printk(KERN_ERR "p16v: status: 0x%08x, mask=0x%08x, pvoice=%p, use=%d\n", status2, mask, pvoice, pvoice->use);
-                               }
-                       }
-                       outl(orig_status2, emu->port + IPR2); /* ack all */
-               }
-       }
        return IRQ_RETVAL(handled);
 }
index d03cb2fefc9e2adb5bf69fc26907795ddb4a796a..98f980189892b1cc5dae7e0446150b390ea32495 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
  *  Driver p16v chips
- *  Version: 0.22
+ *  Version: 0.25
  *
  *  FEATURES currently supported:
  *    Output fixed at S32_LE, 2 channel to hw:0,0
  *    Integrated with snd-emu10k1 driver.
  *  0.22
  *    Removed #if 0 ... #endif
- *
+ *  0.23
+ *    Implement different capture rates.
+ *  0.24
+ *    Implement different capture source channels.
+ *    e.g. When HD Capture source is set to SPDIF,
+ *    setting HD Capture channel to 0 captures from CDROM digital input.
+ *    setting HD Capture channel to 1 captures from SPDIF in.
+ *  0.25
+ *    Include capture buffer sizes.
  *
  *  BUGS:
  *    Some stability problems when unloading the snd-p16v kernel module.
@@ -119,22 +127,41 @@ static snd_pcm_hardware_t snd_p16v_playback_hw = {
                                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
                                 SNDRV_PCM_INFO_MMAP_VALID),
        .formats =              SNDRV_PCM_FMTBIT_S32_LE, /* Only supports 24-bit samples padded to 32 bits. */
-       .rates =                SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_48000 ,
-       .rate_min =             48000,
+       .rates =                SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100, 
+       .rate_min =             44100,
        .rate_max =             192000,
        .channels_min =         8, 
        .channels_max =         8,
-       .buffer_bytes_max =     (32*1024),
+       .buffer_bytes_max =     ((65536 - 64) * 8),
        .period_bytes_min =     64,
-       .period_bytes_max =     (16*1024),
+       .period_bytes_max =     (65536 - 64),
        .periods_min =          2,
        .periods_max =          8,
        .fifo_size =            0,
 };
 
+static snd_pcm_hardware_t snd_p16v_capture_hw = {
+       .info =                 (SNDRV_PCM_INFO_MMAP |
+                                SNDRV_PCM_INFO_INTERLEAVED |
+                                SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                SNDRV_PCM_INFO_MMAP_VALID),
+       .formats =              SNDRV_PCM_FMTBIT_S32_LE,
+       .rates =                SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100, 
+       .rate_min =             44100,
+       .rate_max =             192000,
+       .channels_min =         2,
+       .channels_max =         2,
+       .buffer_bytes_max =     (65536 - 64),
+       .period_bytes_min =     64,
+       .period_bytes_max =     (65536 - 128) >> 1,  /* size has to be N*64 bytes */
+       .periods_min =          2,
+       .periods_max =          2,
+       .fifo_size =            0,
+};
+
 static void snd_p16v_pcm_free_substream(snd_pcm_runtime_t *runtime)
 {
-       snd_pcm_t *epcm = runtime->private_data;
+       emu10k1_pcm_t *epcm = runtime->private_data;
   
        if (epcm) {
                //snd_printk("epcm free: %p\n", epcm);
@@ -178,15 +205,63 @@ static int snd_p16v_pcm_open_playback_channel(snd_pcm_substream_t *substream, in
 
        return 0;
 }
+/* open_capture callback */
+static int snd_p16v_pcm_open_capture_channel(snd_pcm_substream_t *substream, int channel_id)
+{
+       emu10k1_t *emu = snd_pcm_substream_chip(substream);
+       emu10k1_voice_t *channel = &(emu->p16v_capture_voice);
+       emu10k1_pcm_t *epcm;
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       int err;
+
+       epcm = kcalloc(1, sizeof(*epcm), GFP_KERNEL);
+       //snd_printk("epcm kcalloc: %p\n", epcm);
+
+       if (epcm == NULL)
+               return -ENOMEM;
+       epcm->emu = emu;
+       epcm->substream = substream;
+       //snd_printk("epcm device=%d, channel_id=%d\n", substream->pcm->device, channel_id);
+
+       runtime->private_data = epcm;
+       runtime->private_free = snd_p16v_pcm_free_substream;
+  
+       runtime->hw = snd_p16v_capture_hw;
+
+       channel->emu = emu;
+       channel->number = channel_id;
+
+       channel->use=1;
+       //snd_printk("p16v: open channel_id=%d, channel=%p, use=0x%x\n", channel_id, channel, channel->use);
+       //printk("open:channel_id=%d, chip=%p, channel=%p\n",channel_id, chip, channel);
+       //channel->interrupt = snd_p16v_pcm_channel_interrupt;
+       channel->epcm=epcm;
+       if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+               return err;
+
+       return 0;
+}
+
 
 /* close callback */
 static int snd_p16v_pcm_close_playback(snd_pcm_substream_t *substream)
 {
        emu10k1_t *emu = snd_pcm_substream_chip(substream);
        //snd_pcm_runtime_t *runtime = substream->runtime;
-        //emu10k1_pcm_t *epcm = runtime->private_data;
-        emu->p16v_voices[substream->pcm->device - emu->p16v_device_offset].use=0;
-/* FIXME: maybe zero others */
+       //emu10k1_pcm_t *epcm = runtime->private_data;
+       emu->p16v_voices[substream->pcm->device - emu->p16v_device_offset].use=0;
+       /* FIXME: maybe zero others */
+       return 0;
+}
+
+/* close callback */
+static int snd_p16v_pcm_close_capture(snd_pcm_substream_t *substream)
+{
+       emu10k1_t *emu = snd_pcm_substream_chip(substream);
+       //snd_pcm_runtime_t *runtime = substream->runtime;
+       //emu10k1_pcm_t *epcm = runtime->private_data;
+       emu->p16v_capture_voice.use=0;
+       /* FIXME: maybe zero others */
        return 0;
 }
 
@@ -195,36 +270,55 @@ static int snd_p16v_pcm_open_playback_front(snd_pcm_substream_t *substream)
        return snd_p16v_pcm_open_playback_channel(substream, PCM_FRONT_CHANNEL);
 }
 
+static int snd_p16v_pcm_open_capture(snd_pcm_substream_t *substream)
+{
+       // Only using channel 0 for now, but the card has 2 channels.
+       return snd_p16v_pcm_open_capture_channel(substream, 0);
+}
+
 /* hw_params callback */
 static int snd_p16v_pcm_hw_params_playback(snd_pcm_substream_t *substream,
                                      snd_pcm_hw_params_t * hw_params)
 {
        int result;
-        //snd_printk("hw_params alloc: substream=%p\n", substream);
        result = snd_pcm_lib_malloc_pages(substream,
                                        params_buffer_bytes(hw_params));
-        //snd_printk("hw_params alloc: result=%d\n", result);
-       //dump_stack();
        return result;
 }
 
+/* hw_params callback */
+static int snd_p16v_pcm_hw_params_capture(snd_pcm_substream_t *substream,
+                                     snd_pcm_hw_params_t * hw_params)
+{
+       int result;
+       result = snd_pcm_lib_malloc_pages(substream,
+                                       params_buffer_bytes(hw_params));
+       return result;
+}
+
+
 /* hw_free callback */
 static int snd_p16v_pcm_hw_free_playback(snd_pcm_substream_t *substream)
 {
        int result;
-        //snd_printk("hw_params free: substream=%p\n", substream);
        result = snd_pcm_lib_free_pages(substream);
-        //snd_printk("hw_params free: result=%d\n", result);
-       //dump_stack();
        return result;
 }
 
+/* hw_free callback */
+static int snd_p16v_pcm_hw_free_capture(snd_pcm_substream_t *substream)
+{
+       int result;
+       result = snd_pcm_lib_free_pages(substream);
+       return result;
+}
+
+
 /* prepare playback callback */
 static int snd_p16v_pcm_prepare_playback(snd_pcm_substream_t *substream)
 {
        emu10k1_t *emu = snd_pcm_substream_chip(substream);
        snd_pcm_runtime_t *runtime = substream->runtime;
-       //emu10k1_pcm_t *epcm = runtime->private_data;
        int channel = substream->pcm->device - emu->p16v_device_offset;
        u32 *table_base = (u32 *)(emu->p16v_buffer.area+(8*16*channel));
        u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size);
@@ -237,23 +331,21 @@ static int snd_p16v_pcm_prepare_playback(snd_pcm_substream_t *substream)
        tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, channel);
         switch (runtime->rate) {
        case 44100:
-         snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe000) | 0x8000); /* FIXME: This will change the capture rate as well! */
-         break;
-       case 48000:
-         snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe000) | 0x0000); /* FIXME: This will change the capture rate as well! */
+         snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x8080);
          break;
        case 96000:
-         snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe000) | 0x4000); /* FIXME: This will change the capture rate as well! */
+         snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x4040);
          break;
        case 192000:
-         snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe000) | 0x2000); /* FIXME: This will change the capture rate as well! */
+         snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x2020);
          break;
+       case 48000:
        default:
-         snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, 0x0000); /* FIXME: This will change the capture rate as well! */
+         snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x0000);
          break;
        }
        /* FIXME: Check emu->buffer.size before actually writing to it. */
-        for(i=0; i < runtime->periods; i++) {
+       for(i=0; i < runtime->periods; i++) {
                table_base[i*2]=runtime->dma_addr+(i*period_size_bytes);
                table_base[(i*2)+1]=period_size_bytes<<16;
        }
@@ -262,7 +354,8 @@ static int snd_p16v_pcm_prepare_playback(snd_pcm_substream_t *substream)
        snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_SIZE, channel, (runtime->periods - 1) << 19);
        snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_PTR, channel, 0);
        snd_emu10k1_ptr20_write(emu, PLAYBACK_DMA_ADDR, channel, runtime->dma_addr);
-       snd_emu10k1_ptr20_write(emu, PLAYBACK_PERIOD_SIZE, channel, frames_to_bytes(runtime, runtime->period_size)<<16); // buffer size in bytes
+       //snd_emu10k1_ptr20_write(emu, PLAYBACK_PERIOD_SIZE, channel, frames_to_bytes(runtime, runtime->period_size)<<16); // buffer size in bytes
+       snd_emu10k1_ptr20_write(emu, PLAYBACK_PERIOD_SIZE, channel, 0); // buffer size in bytes
        snd_emu10k1_ptr20_write(emu, PLAYBACK_POINTER, channel, 0);
        snd_emu10k1_ptr20_write(emu, 0x07, channel, 0x0);
        snd_emu10k1_ptr20_write(emu, 0x08, channel, 0);
@@ -270,6 +363,41 @@ static int snd_p16v_pcm_prepare_playback(snd_pcm_substream_t *substream)
        return 0;
 }
 
+/* prepare capture callback */
+static int snd_p16v_pcm_prepare_capture(snd_pcm_substream_t *substream)
+{
+       emu10k1_t *emu = snd_pcm_substream_chip(substream);
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       int channel = substream->pcm->device - emu->p16v_device_offset;
+       u32 tmp;
+       //printk("prepare capture:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size,  frames_to_bytes(runtime, 1));
+       tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, channel);
+        switch (runtime->rate) {
+       case 44100:
+         snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0800);
+         break;
+       case 96000:
+         snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0400);
+         break;
+       case 192000:
+         snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0200);
+         break;
+       case 48000:
+       default:
+         snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0000);
+         break;
+       }
+       /* FIXME: Check emu->buffer.size before actually writing to it. */
+       snd_emu10k1_ptr20_write(emu, 0x13, channel, 0);
+       snd_emu10k1_ptr20_write(emu, CAPTURE_DMA_ADDR, channel, runtime->dma_addr);
+       snd_emu10k1_ptr20_write(emu, CAPTURE_BUFFER_SIZE, channel, frames_to_bytes(runtime, runtime->buffer_size)<<16); // buffer size in bytes
+       snd_emu10k1_ptr20_write(emu, CAPTURE_POINTER, channel, 0);
+       //snd_emu10k1_ptr20_write(emu, CAPTURE_SOURCE, 0x0, 0x333300e4); /* Select MIC or Line in */
+       //snd_emu10k1_ptr20_write(emu, EXTENDED_INT_MASK, 0, snd_emu10k1_ptr20_read(emu, EXTENDED_INT_MASK, 0) | (0x110000<<channel));
+
+       return 0;
+}
+
 static void snd_p16v_intr_enable(emu10k1_t *emu, unsigned int intrenb)
 {
        unsigned long flags;
@@ -345,6 +473,36 @@ static int snd_p16v_pcm_trigger_playback(snd_pcm_substream_t *substream,
        return result;
 }
 
+/* trigger_capture callback */
+static int snd_p16v_pcm_trigger_capture(snd_pcm_substream_t *substream,
+                                   int cmd)
+{
+       emu10k1_t *emu = snd_pcm_substream_chip(substream);
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       emu10k1_pcm_t *epcm = runtime->private_data;
+       int channel = 0;
+       int result = 0;
+       u32 inte = INTE2_CAPTURE_CH_0_LOOP | INTE2_CAPTURE_CH_0_HALF_LOOP;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               snd_p16v_intr_enable(emu, inte);
+               snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0)|(0x100<<channel));
+               epcm->running = 1;
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0) & ~(0x100<<channel));
+               snd_p16v_intr_disable(emu, inte);
+               //snd_emu10k1_ptr20_write(emu, EXTENDED_INT_MASK, 0, snd_emu10k1_ptr20_read(emu, EXTENDED_INT_MASK, 0) & ~(0x110000<<channel));
+               epcm->running = 0;
+               break;
+       default:
+               result = -EINVAL;
+               break;
+       }
+       return result;
+}
+
 /* pointer_playback callback */
 static snd_pcm_uframes_t
 snd_p16v_pcm_pointer_playback(snd_pcm_substream_t *substream)
@@ -370,6 +528,31 @@ snd_p16v_pcm_pointer_playback(snd_pcm_substream_t *substream)
        return ptr;
 }
 
+/* pointer_capture callback */
+static snd_pcm_uframes_t
+snd_p16v_pcm_pointer_capture(snd_pcm_substream_t *substream)
+{
+       emu10k1_t *emu = snd_pcm_substream_chip(substream);
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       emu10k1_pcm_t *epcm = runtime->private_data;
+       snd_pcm_uframes_t ptr, ptr1, ptr2 = 0;
+       int channel = 0;
+
+       if (!epcm->running)
+               return 0;
+
+       ptr1 = snd_emu10k1_ptr20_read(emu, CAPTURE_POINTER, channel);
+       ptr2 = bytes_to_frames(runtime, ptr1);
+       ptr=ptr2;
+       if (ptr >= runtime->buffer_size) {
+               ptr -= runtime->buffer_size;
+               printk("buffer capture limited!\n");
+       }
+       //printk("ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n", ptr1, ptr2, ptr, (int)runtime->buffer_size, (int)runtime->period_size, (int)runtime->frame_bits, (int)runtime->rate);
+
+       return ptr;
+}
+
 /* operators */
 static snd_pcm_ops_t snd_p16v_playback_front_ops = {
        .open =        snd_p16v_pcm_open_playback_front,
@@ -382,6 +565,18 @@ static snd_pcm_ops_t snd_p16v_playback_front_ops = {
        .pointer =     snd_p16v_pcm_pointer_playback,
 };
 
+static snd_pcm_ops_t snd_p16v_capture_ops = {
+       .open =        snd_p16v_pcm_open_capture,
+       .close =       snd_p16v_pcm_close_capture,
+       .ioctl =       snd_pcm_lib_ioctl,
+       .hw_params =   snd_p16v_pcm_hw_params_capture,
+       .hw_free =     snd_p16v_pcm_hw_free_capture,
+       .prepare =     snd_p16v_pcm_prepare_capture,
+       .trigger =     snd_p16v_pcm_trigger_capture,
+       .pointer =     snd_p16v_pcm_pointer_capture,
+};
+
+
 int snd_p16v_free(emu10k1_t *chip)
 {
        // release the data
@@ -405,20 +600,22 @@ int snd_p16v_pcm(emu10k1_t *emu, int device, snd_pcm_t **rpcm)
        snd_pcm_t *pcm;
        snd_pcm_substream_t *substream;
        int err;
-        int capture=0;
+        int capture=1;
   
        //snd_printk("snd_p16v_pcm called. device=%d\n", device);
        emu->p16v_device_offset = device;
        if (rpcm)
                *rpcm = NULL;
-        //if (device == 0) capture=1; 
+
        if ((err = snd_pcm_new(emu->card, "p16v", device, 1, capture, &pcm)) < 0)
                return err;
   
        pcm->private_data = emu;
        pcm->private_free = snd_p16v_pcm_free;
-
+       // Single playback 8 channel device.
+       // Single capture 2 channel device.
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_p16v_playback_front_ops);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_p16v_capture_ops);
 
        pcm->info_flags = 0;
        pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
@@ -431,7 +628,7 @@ int snd_p16v_pcm(emu10k1_t *emu, int device, snd_pcm_t **rpcm)
                if ((err = snd_pcm_lib_preallocate_pages(substream, 
                                                         SNDRV_DMA_TYPE_DEV, 
                                                         snd_dma_pci_data(emu->pci), 
-                                                        64*1024, 64*1024)) < 0) /* FIXME: 32*1024 for sound buffer, between 32and64 for Periods table. */
+                                                        ((65536 - 64) * 8), ((65536 - 64) * 8))) < 0) 
                        return err;
                //snd_printk("preallocate playback substream: err=%d\n", err);
        }
@@ -442,7 +639,7 @@ int snd_p16v_pcm(emu10k1_t *emu, int device, snd_pcm_t **rpcm)
                if ((err = snd_pcm_lib_preallocate_pages(substream, 
                                                   SNDRV_DMA_TYPE_DEV, 
                                                   snd_dma_pci_data(emu->pci), 
-                                                  64*1024, 64*1024)) < 0)
+                                                  65536 - 64, 65536 - 64)) < 0)
                        return err;
                //snd_printk("preallocate capture substream: err=%d\n", err);
        }
@@ -694,6 +891,106 @@ static snd_kcontrol_new_t snd_p16v_volume_control_spdif_rear =
         .put =          snd_p16v_volume_put_spdif_rear
 };
 
+static int snd_p16v_capture_source_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+       static char *texts[8] = { "SPDIF", "I2S", "SRC48", "SRCMulti_SPDIF", "SRCMulti_I2S", "CDIF", "FX", "AC97" };
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 8;
+       if (uinfo->value.enumerated.item > 7)
+                uinfo->value.enumerated.item = 7;
+       strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+       return 0;
+}
+
+static int snd_p16v_capture_source_get(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.enumerated.item[0] = emu->p16v_capture_source;
+       return 0;
+}
+
+static int snd_p16v_capture_source_put(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+       unsigned int val;
+       int change = 0;
+       u32 mask;
+       u32 source;
+
+       val = ucontrol->value.enumerated.item[0] ;
+       change = (emu->p16v_capture_source != val);
+       if (change) {
+               emu->p16v_capture_source = val;
+               source = (val << 28) | (val << 24) | (val << 20) | (val << 16);
+               mask = snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0) & 0xffff;
+               snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, source | mask);
+       }
+        return change;
+}
+
+static snd_kcontrol_new_t snd_p16v_capture_source __devinitdata =
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name =         "HD Capture source",
+       .info =         snd_p16v_capture_source_info,
+       .get =          snd_p16v_capture_source_get,
+       .put =          snd_p16v_capture_source_put
+};
+
+static int snd_p16v_capture_channel_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+       static char *texts[4] = { "0", "1", "2", "3",  };
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 4;
+       if (uinfo->value.enumerated.item > 3)
+                uinfo->value.enumerated.item = 3;
+       strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+       return 0;
+}
+
+static int snd_p16v_capture_channel_get(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.enumerated.item[0] = emu->p16v_capture_channel;
+       return 0;
+}
+
+static int snd_p16v_capture_channel_put(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       emu10k1_t *emu = snd_kcontrol_chip(kcontrol);
+       unsigned int val;
+       int change = 0;
+       u32 tmp;
+
+       val = ucontrol->value.enumerated.item[0] ;
+       change = (emu->p16v_capture_channel != val);
+       if (change) {
+               emu->p16v_capture_channel = val;
+               tmp = snd_emu10k1_ptr20_read(emu, CAPTURE_P16V_SOURCE, 0) & 0xfffc;
+               snd_emu10k1_ptr20_write(emu, CAPTURE_P16V_SOURCE, 0, tmp | val);
+       }
+        return change;
+}
+
+static snd_kcontrol_new_t snd_p16v_capture_channel __devinitdata =
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name =         "HD Capture channel",
+       .info =         snd_p16v_capture_channel_info,
+       .get =          snd_p16v_capture_channel_get,
+       .put =          snd_p16v_capture_channel_put
+};
+
 int snd_p16v_mixer(emu10k1_t *emu)
 {
         int err;
@@ -731,6 +1028,14 @@ int snd_p16v_mixer(emu10k1_t *emu)
                 return -ENOMEM;
         if ((err = snd_ctl_add(card, kctl)))
                 return err;
+        if ((kctl = snd_ctl_new1(&snd_p16v_capture_source, emu)) == NULL)
+                return -ENOMEM;
+        if ((err = snd_ctl_add(card, kctl)))
+                return err;
+        if ((kctl = snd_ctl_new1(&snd_p16v_capture_channel, emu)) == NULL)
+                return -ENOMEM;
+        if ((err = snd_ctl_add(card, kctl)))
+                return err;
         return 0;
 }
 
index f910399db5c39a6eedb9e4901df6fa183fd7b68d..4e63498a58b229e9a338a7eeae40deb31d6be0f0 100644 (file)
@@ -2401,7 +2401,7 @@ static struct pci_driver driver = {
        
 static int __init alsa_card_ens137x_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_ens137x_exit(void)
index b4ca8adf393c6a89d72be570607960b5fc66c879..b492777bc30ffda252e86e1677b9d77c75dd9148 100644 (file)
@@ -1761,7 +1761,7 @@ static struct pci_driver driver = {
 
 static int __init alsa_card_es1938_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_es1938_exit(void)
index faf63ff19c42abf17d15f22c7223f631e2ef58f6..ea889b3113902251af682e1a7d49842276fc1eb9 100644 (file)
@@ -2559,6 +2559,7 @@ static struct ess_device_list pm_whitelist[] __devinitdata = {
        { TYPE_MAESTRO2E, 0x103c },
        { TYPE_MAESTRO2E, 0x1179 },
        { TYPE_MAESTRO2E, 0x14c0 },     /* HP omnibook 4150 */
+       { TYPE_MAESTRO2E, 0x1558 },
 };
 
 static struct ess_device_list mpu_blacklist[] __devinitdata = {
@@ -2795,7 +2796,7 @@ static struct pci_driver driver = {
 
 static int __init alsa_card_es1968_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_es1968_exit(void)
index 08e7c5a296d544159e5907182f75bbeece2ad983..ff10e637a95e0ffb1b5d2882e59765ea1b557efb 100644 (file)
@@ -195,6 +195,7 @@ struct _snd_fm801 {
 
 static struct pci_device_id snd_fm801_ids[] = {
        { 0x1319, 0x0801, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, },   /* FM801 */
+       { 0x5213, 0x0510, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, },   /* Gallant Odyssey Sound 4 */
        { 0, }
 };
 
@@ -1468,7 +1469,7 @@ static struct pci_driver driver = {
 
 static int __init alsa_card_fm801_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_fm801_exit(void)
index 570a59d33b4194ab1ef1327caf0d9e204096b62d..bd8cb33c4fb492fa4d2a198b3cfbe60b6983d96f 100644 (file)
@@ -1,5 +1,5 @@
 snd-hda-intel-objs := hda_intel.o
-snd-hda-codec-objs := hda_codec.o hda_generic.o patch_realtek.o patch_cmedia.o patch_analog.o
+snd-hda-codec-objs := hda_codec.o hda_generic.o patch_realtek.o patch_cmedia.o patch_analog.o patch_sigmatel.o
 ifdef CONFIG_PROC_FS
 snd-hda-codec-objs += hda_proc.o
 endif
index 9ed117ac0c090bd5e9948322cfe6a3204f20f0e3..e2cf0238728925bef2183adeb72bf9cb8abadb1b 100644 (file)
@@ -49,8 +49,10 @@ struct hda_vendor_id {
 /* codec vendor labels */
 static struct hda_vendor_id hda_vendor_ids[] = {
        { 0x10ec, "Realtek" },
+       { 0x11d4, "Analog Devices" },
        { 0x13f6, "C-Media" },
        { 0x434d, "C-Media" },
+       { 0x8384, "SigmaTel" },
        {} /* terminator */
 };
 
@@ -508,7 +510,7 @@ int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
        /* FIXME: support for multiple AFGs? */
        codec->afg = look_for_afg_node(codec);
        if (! codec->afg) {
-               snd_printk(KERN_ERR "hda_codec: no AFG node found\n");
+               snd_printdd("hda_codec: no AFG node found\n");
                snd_hda_codec_free(codec);
                return -ENODEV;
        }
@@ -548,6 +550,9 @@ int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
 void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, u32 stream_tag,
                                int channel_id, int format)
 {
+       if (! nid)
+               return;
+
        snd_printdd("hda_codec_setup_stream: NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n",
                    nid, stream_tag, channel_id, format);
        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID,
@@ -561,9 +566,10 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, u32 stre
  * amp access functions
  */
 
-#define HDA_HASH_KEY(nid,dir,idx) (u32)((nid) + (idx) * 32 + (dir) * 64)
+/* FIXME: more better hash key? */
+#define HDA_HASH_KEY(nid,dir,idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24))
 #define INFO_AMP_CAPS  (1<<0)
-#define INFO_AMP_VOL   (1<<1)
+#define INFO_AMP_VOL(ch)       (1 << (1 + (ch)))
 
 /* initialize the hash table */
 static void init_amp_hash(struct hda_codec *codec)
@@ -622,28 +628,29 @@ static u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
 
 /*
  * read the current volume to info
- * if the cache exists, read from the cache.
+ * if the cache exists, read the cache value.
  */
-static void get_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
+static unsigned int get_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
                         hda_nid_t nid, int ch, int direction, int index)
 {
        u32 val, parm;
 
-       if (info->status & (INFO_AMP_VOL << ch))
-               return;
+       if (info->status & INFO_AMP_VOL(ch))
+               return info->vol[ch];
 
        parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT;
        parm |= direction == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
        parm |= index;
        val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, parm);
        info->vol[ch] = val & 0xff;
-       info->status |= INFO_AMP_VOL << ch;
+       info->status |= INFO_AMP_VOL(ch);
+       return info->vol[ch];
 }
 
 /*
- * write the current volume in info to the h/w
+ * write the current volume in info to the h/w and update the cache
  */
-static void put_vol_mute(struct hda_codec *codec,
+static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
                         hda_nid_t nid, int ch, int direction, int index, int val)
 {
        u32 parm;
@@ -653,30 +660,34 @@ static void put_vol_mute(struct hda_codec *codec,
        parm |= index << AC_AMP_SET_INDEX_SHIFT;
        parm |= val;
        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, parm);
+       info->vol[ch] = val;
 }
 
 /*
- * read/write AMP value.  The volume is between 0 to 0x7f, 0x80 = mute bit.
+ * read AMP value.  The volume is between 0 to 0x7f, 0x80 = mute bit.
  */
-int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int index)
+static int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int index)
 {
        struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index));
        if (! info)
                return 0;
-       get_vol_mute(codec, info, nid, ch, direction, index);
-       return info->vol[ch];
+       return get_vol_mute(codec, info, nid, ch, direction, index);
 }
 
-int snd_hda_codec_amp_write(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int idx, int val)
+/*
+ * update the AMP value, mask = bit mask to set, val = the value
+ */
+static int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int idx, int mask, int val)
 {
        struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, idx));
+
        if (! info)
                return 0;
-       get_vol_mute(codec, info, nid, ch, direction, idx);
+       val &= mask;
+       val |= get_vol_mute(codec, info, nid, ch, direction, idx) & ~mask;
        if (info->vol[ch] == val && ! codec->in_resume)
                return 0;
-       put_vol_mute(codec, nid, ch, direction, idx, val);
-       info->vol[ch] = val;
+       put_vol_mute(codec, info, nid, ch, direction, idx, val);
        return 1;
 }
 
@@ -735,21 +746,15 @@ int snd_hda_mixer_amp_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
        int chs = get_amp_channels(kcontrol);
        int dir = get_amp_direction(kcontrol);
        int idx = get_amp_index(kcontrol);
-       int val;
        long *valp = ucontrol->value.integer.value;
        int change = 0;
 
-       if (chs & 1) {
-               val = *valp & 0x7f;
-               val |= snd_hda_codec_amp_read(codec, nid, 0, dir, idx) & 0x80;
-               change = snd_hda_codec_amp_write(codec, nid, 0, dir, idx, val);
-               valp++;
-       }
-       if (chs & 2) {
-               val = *valp & 0x7f;
-               val |= snd_hda_codec_amp_read(codec, nid, 1, dir, idx) & 0x80;
-               change |= snd_hda_codec_amp_write(codec, nid, 1, dir, idx, val);
-       }
+       if (chs & 1)
+               change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx,
+                                                 0x7f, *valp);
+       if (chs & 2)
+               change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx,
+                                                  0x7f, valp[1]);
        return change;
 }
 
@@ -788,21 +793,15 @@ int snd_hda_mixer_amp_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
        int chs = get_amp_channels(kcontrol);
        int dir = get_amp_direction(kcontrol);
        int idx = get_amp_index(kcontrol);
-       int val;
        long *valp = ucontrol->value.integer.value;
        int change = 0;
 
-       if (chs & 1) {
-               val = snd_hda_codec_amp_read(codec, nid, 0, dir, idx) & 0x7f;
-               val |= *valp ? 0 : 0x80;
-               change = snd_hda_codec_amp_write(codec, nid, 0, dir, idx, val);
-               valp++;
-       }
-       if (chs & 2) {
-               val = snd_hda_codec_amp_read(codec, nid, 1, dir, idx) & 0x7f;
-               val |= *valp ? 0 : 0x80;
-               change = snd_hda_codec_amp_write(codec, nid, 1, dir, idx, val);
-       }
+       if (chs & 1)
+               change = snd_hda_codec_amp_update(codec, nid, 0, dir, idx,
+                                                 0x80, *valp ? 0 : 0x80);
+       if (chs & 2)
+               change |= snd_hda_codec_amp_update(codec, nid, 1, dir, idx,
+                                                  0x80, valp[1] ? 0 : 0x80);
        return change;
 }
 
@@ -1448,10 +1447,6 @@ static int set_pcm_default_values(struct hda_codec *codec, struct hda_pcm_stream
                snd_assert(info->nid, return -EINVAL);
                info->ops.prepare = hda_pcm_default_prepare;
        }
-       if (info->ops.prepare == NULL) {
-               snd_assert(info->nid, return -EINVAL);
-               info->ops.prepare = hda_pcm_default_prepare;
-       }
        if (info->ops.cleanup == NULL) {
                snd_assert(info->nid, return -EINVAL);
                info->ops.cleanup = hda_pcm_default_cleanup;
@@ -1525,12 +1520,12 @@ int snd_hda_build_pcms(struct hda_bus *bus)
  *
  * If no entries are matching, the function returns a negative value.
  */
-int snd_hda_check_board_config(struct hda_codec *codec, struct hda_board_config *tbl)
+int snd_hda_check_board_config(struct hda_codec *codec, const struct hda_board_config *tbl)
 {
-       struct hda_board_config *c;
+       const struct hda_board_config *c;
 
        if (codec->bus->modelname) {
-               for (c = tbl; c->modelname || c->pci_vendor; c++) {
+               for (c = tbl; c->modelname || c->pci_subvendor; c++) {
                        if (c->modelname &&
                            ! strcmp(codec->bus->modelname, c->modelname)) {
                                snd_printd(KERN_INFO "hda_codec: model '%s' is selected\n", c->modelname);
@@ -1543,9 +1538,10 @@ int snd_hda_check_board_config(struct hda_codec *codec, struct hda_board_config
                u16 subsystem_vendor, subsystem_device;
                pci_read_config_word(codec->bus->pci, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor);
                pci_read_config_word(codec->bus->pci, PCI_SUBSYSTEM_ID, &subsystem_device);
-               for (c = tbl; c->modelname || c->pci_vendor; c++) {
-                       if (c->pci_vendor == subsystem_vendor &&
-                           c->pci_device == subsystem_device)
+               for (c = tbl; c->modelname || c->pci_subvendor; c++) {
+                       if (c->pci_subvendor == subsystem_vendor &&
+                           (! c->pci_subdevice /* all match */||
+                            (c->pci_subdevice == subsystem_device)))
                                return c->config;
                }
        }
@@ -1687,11 +1683,12 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec, struct hda_multi_o
                snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 0, format);
        /* surrounds */
        for (i = 1; i < mout->num_dacs; i++) {
-               if (i == HDA_REAR && chs == 2) /* copy front to rear */
-                       snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 0, format);
-               else if (chs >= (i + 1) * 2) /* independent out */
+               if (chs >= (i + 1) * 2) /* independent out */
                        snd_hda_codec_setup_stream(codec, nids[i], stream_tag, i * 2,
                                                   format);
+               else /* copy front */
+                       snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 0,
+                                                  format);
        }
        return 0;
 }
@@ -1717,6 +1714,105 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, struct hda_multi_o
        return 0;
 }
 
+/*
+ * Helper for automatic ping configuration
+ */
+/* parse all pin widgets and store the useful pin nids to cfg */
+int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *cfg)
+{
+       hda_nid_t nid, nid_start;
+       int i, j, nodes;
+       short seq, sequences[4], assoc_line_out;
+
+       memset(cfg, 0, sizeof(*cfg));
+
+       memset(sequences, 0, sizeof(sequences));
+       assoc_line_out = 0;
+
+       nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid_start);
+       for (nid = nid_start; nid < nodes + nid_start; nid++) {
+               unsigned int wid_caps = snd_hda_param_read(codec, nid,
+                                                          AC_PAR_AUDIO_WIDGET_CAP);
+               unsigned int wid_type = (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+               unsigned int def_conf;
+               short assoc, loc;
+
+               /* read all default configuration for pin complex */
+               if (wid_type != AC_WID_PIN)
+                       continue;
+               def_conf = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+               if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
+                       continue;
+               loc = get_defcfg_location(def_conf);
+               switch (get_defcfg_device(def_conf)) {
+               case AC_JACK_LINE_OUT:
+               case AC_JACK_SPEAKER:
+                       seq = get_defcfg_sequence(def_conf);
+                       assoc = get_defcfg_association(def_conf);
+                       if (! assoc)
+                               continue;
+                       if (! assoc_line_out)
+                               assoc_line_out = assoc;
+                       else if (assoc_line_out != assoc)
+                               continue;
+                       if (cfg->line_outs >= ARRAY_SIZE(cfg->line_out_pins))
+                               continue;
+                       cfg->line_out_pins[cfg->line_outs] = nid;
+                       sequences[cfg->line_outs] = seq;
+                       cfg->line_outs++;
+                       break;
+               case AC_JACK_HP_OUT:
+                       cfg->hp_pin = nid;
+                       break;
+               case AC_JACK_MIC_IN:
+                       if (loc == AC_JACK_LOC_FRONT)
+                               cfg->input_pins[AUTO_PIN_FRONT_MIC] = nid;
+                       else
+                               cfg->input_pins[AUTO_PIN_MIC] = nid;
+                       break;
+               case AC_JACK_LINE_IN:
+                       if (loc == AC_JACK_LOC_FRONT)
+                               cfg->input_pins[AUTO_PIN_FRONT_LINE] = nid;
+                       else
+                               cfg->input_pins[AUTO_PIN_LINE] = nid;
+                       break;
+               case AC_JACK_CD:
+                       cfg->input_pins[AUTO_PIN_CD] = nid;
+                       break;
+               case AC_JACK_AUX:
+                       cfg->input_pins[AUTO_PIN_AUX] = nid;
+                       break;
+               case AC_JACK_SPDIF_OUT:
+                       cfg->dig_out_pin = nid;
+                       break;
+               case AC_JACK_SPDIF_IN:
+                       cfg->dig_in_pin = nid;
+                       break;
+               }
+       }
+
+       /* sort by sequence */
+       for (i = 0; i < cfg->line_outs; i++)
+               for (j = i + 1; j < cfg->line_outs; j++)
+                       if (sequences[i] > sequences[j]) {
+                               seq = sequences[i];
+                               sequences[i] = sequences[j];
+                               sequences[j] = seq;
+                               nid = cfg->line_out_pins[i];
+                               cfg->line_out_pins[i] = cfg->line_out_pins[j];
+                               cfg->line_out_pins[j] = nid;
+                       }
+
+       /* Swap surround and CLFE: the association order is front/CLFE/surr/back */
+       if (cfg->line_outs >= 3) {
+               nid = cfg->line_out_pins[1];
+               cfg->line_out_pins[1] = cfg->line_out_pins[2];
+               cfg->line_out_pins[2] = nid;
+       }
+
+       return 0;
+}
+
 #ifdef CONFIG_PM
 /*
  * power management
index c9e9dc9c7c98eae856eea9ef1bccf8fbb3d0b4b8..59991560d492260b9d407fd00ddb11a5f907de36 100644 (file)
@@ -75,6 +75,9 @@ enum {
 #define AC_VERB_GET_DIGI_CONVERT               0x0f0d
 #define AC_VERB_GET_VOLUME_KNOB_CONTROL                0x0f0f
 /* f10-f1a: GPIO */
+#define AC_VERB_GET_GPIO_DATA                  0x0f15
+#define AC_VERB_GET_GPIO_MASK                  0x0f16
+#define AC_VERB_GET_GPIO_DIRECTION             0x0f17
 #define AC_VERB_GET_CONFIG_DEFAULT             0x0f1c
 
 /*
@@ -97,6 +100,9 @@ enum {
 #define AC_VERB_SET_DIGI_CONVERT_1             0x70d
 #define AC_VERB_SET_DIGI_CONVERT_2             0x70e
 #define AC_VERB_SET_VOLUME_KNOB_CONTROL                0x70f
+#define AC_VERB_SET_GPIO_DATA                  0x715
+#define AC_VERB_SET_GPIO_MASK                  0x716
+#define AC_VERB_SET_GPIO_DIRECTION             0x717
 #define AC_VERB_SET_CONFIG_DEFAULT_BYTES_0     0x71c
 #define AC_VERB_SET_CONFIG_DEFAULT_BYTES_1     0x71d
 #define AC_VERB_SET_CONFIG_DEFAULT_BYTES_2     0x71e
@@ -176,16 +182,15 @@ enum {
 #define AC_PINCAP_OUT                  (1<<4)  /* output capable */
 #define AC_PINCAP_IN                   (1<<5)  /* input capable */
 #define AC_PINCAP_BALANCE              (1<<6)  /* balanced I/O capable */
-#define AC_PINCAP_VREF                 (7<<8)
+#define AC_PINCAP_VREF                 (0x37<<8)
 #define AC_PINCAP_VREF_SHIFT           8
 #define AC_PINCAP_EAPD                 (1<<16) /* EAPD capable */
-/* Vref status (used in pin cap and pin ctl) */
-#define AC_PIN_VREF_HIZ                        (1<<0)  /* Hi-Z */
-#define AC_PIN_VREF_50                 (1<<1)  /* 50% */
-#define AC_PIN_VREF_GRD                        (1<<2)  /* ground */
-#define AC_PIN_VREF_80                 (1<<4)  /* 80% */
-#define AC_PIN_VREF_100                        (1<<5)  /* 100% */
-
+/* Vref status (used in pin cap) */
+#define AC_PINCAP_VREF_HIZ             (1<<0)  /* Hi-Z */
+#define AC_PINCAP_VREF_50              (1<<1)  /* 50% */
+#define AC_PINCAP_VREF_GRD             (1<<2)  /* ground */
+#define AC_PINCAP_VREF_80              (1<<4)  /* 80% */
+#define AC_PINCAP_VREF_100             (1<<5)  /* 100% */
 
 /* Amplifier capabilities */
 #define AC_AMPCAP_OFFSET               (0x7f<<0)  /* 0dB offset */
@@ -248,6 +253,11 @@ enum {
 
 /* Pin widget control - 8bit */
 #define AC_PINCTL_VREFEN               (0x7<<0)
+#define AC_PINCTL_VREF_HIZ             0       /* Hi-Z */
+#define AC_PINCTL_VREF_50              1       /* 50% */
+#define AC_PINCTL_VREF_GRD             2       /* ground */
+#define AC_PINCTL_VREF_80              4       /* 80% */
+#define AC_PINCTL_VREF_100             5       /* 100% */
 #define AC_PINCTL_IN_EN                        (1<<5)
 #define AC_PINCTL_OUT_EN               (1<<6)
 #define AC_PINCTL_HP_EN                        (1<<7)
@@ -255,7 +265,9 @@ enum {
 /* configuration default - 32bit */
 #define AC_DEFCFG_SEQUENCE             (0xf<<0)
 #define AC_DEFCFG_DEF_ASSOC            (0xf<<4)
+#define AC_DEFCFG_ASSOC_SHIFT          4
 #define AC_DEFCFG_MISC                 (0xf<<8)
+#define AC_DEFCFG_MISC_SHIFT           8
 #define AC_DEFCFG_COLOR                        (0xf<<12)
 #define AC_DEFCFG_COLOR_SHIFT          12
 #define AC_DEFCFG_CONN_TYPE            (0xf<<16)
@@ -413,7 +425,7 @@ struct hda_bus {
 
        /* codec linked list */
        struct list_head codec_list;
-       struct hda_codec *caddr_tbl[HDA_MAX_CODEC_ADDRESS]; /* caddr -> codec */
+       struct hda_codec *caddr_tbl[HDA_MAX_CODEC_ADDRESS + 1]; /* caddr -> codec */
 
        struct semaphore cmd_mutex;
 
index 69f7b6c4cf83a32b9a90d5e312ea8fda8ea21f79..2d046abb591108bc9033be2f5ce16b6a4ab95c6b 100644 (file)
@@ -44,7 +44,7 @@ struct hda_gnode {
        struct list_head list;
 };
 
-/* pathc-specific record */
+/* patch-specific record */
 struct hda_gspec {
        struct hda_gnode *dac_node;     /* DAC node */
        struct hda_gnode *out_pin_node; /* Output pin (Line-Out) node */
@@ -68,8 +68,8 @@ struct hda_gspec {
 /*
  * retrieve the default device type from the default config value
  */
-#define get_defcfg_type(node) (((node)->def_cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT)
-#define get_defcfg_location(node) (((node)->def_cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT)
+#define defcfg_type(node) (((node)->def_cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT)
+#define defcfg_location(node) (((node)->def_cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT)
 
 /*
  * destructor
@@ -323,7 +323,7 @@ static struct hda_gnode *parse_output_jack(struct hda_codec *codec,
                if (! (node->pin_caps & AC_PINCAP_OUT))
                        continue;
                if (jack_type >= 0) {
-                       if (jack_type != get_defcfg_type(node))
+                       if (jack_type != defcfg_type(node))
                                continue;
                        if (node->wid_caps & AC_WCAP_DIGITAL)
                                continue; /* skip SPDIF */
@@ -418,15 +418,15 @@ static int capture_source_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *uc
  */
 static const char *get_input_type(struct hda_gnode *node, unsigned int *pinctl)
 {
-       unsigned int location = get_defcfg_location(node);
-       switch (get_defcfg_type(node)) {
+       unsigned int location = defcfg_location(node);
+       switch (defcfg_type(node)) {
        case AC_JACK_LINE_IN:
                if ((location & 0x0f) == AC_JACK_LOC_FRONT)
                        return "Front Line";
                return "Line";
        case AC_JACK_CD:
                if (pinctl)
-                       *pinctl |= AC_PIN_VREF_GRD;
+                       *pinctl |= AC_PINCTL_VREF_GRD;
                return "CD";
        case AC_JACK_AUX:
                if ((location & 0x0f) == AC_JACK_LOC_FRONT)
index 959953ca320a16a7904ecd016e9a66d798564001..5e0cca36ed57d0215e5d407d3ef51f5c4fd610aa 100644 (file)
@@ -51,6 +51,7 @@ static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
 static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
 static char *model[SNDRV_CARDS];
+static int position_fix[SNDRV_CARDS];
 
 module_param_array(index, int, NULL, 0444);
 MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
@@ -60,12 +61,17 @@ module_param_array(enable, bool, NULL, 0444);
 MODULE_PARM_DESC(enable, "Enable Intel HD audio interface.");
 module_param_array(model, charp, NULL, 0444);
 MODULE_PARM_DESC(model, "Use the given board model.");
+module_param_array(position_fix, int, NULL, 0444);
+MODULE_PARM_DESC(position_fix, "Fix DMA pointer (0 = FIFO size, 1 = none, 2 = POSBUF).");
 
 MODULE_LICENSE("GPL");
 MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
                         "{Intel, ICH6M},"
                         "{Intel, ICH7},"
-                        "{Intel, ESB2}}");
+                        "{Intel, ESB2},"
+                        "{ATI, SB450},"
+                        "{VIA, VT8251},"
+                        "{VIA, VT8237A}}");
 MODULE_DESCRIPTION("Intel HDA driver");
 
 #define SFX    "hda-intel: "
@@ -150,7 +156,7 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
 
 /* STATESTS int mask: SD2,SD1,SD0 */
 #define STATESTS_INT_MASK      0x07
-#define AZX_MAX_CODECS         3
+#define AZX_MAX_CODECS         4
 
 /* SD_CTL bits */
 #define SD_CTL_STREAM_RESET    0x01    /* stream reset bit */
@@ -183,6 +189,18 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
 #define ICH6_MAX_CORB_ENTRIES  256
 #define ICH6_MAX_RIRB_ENTRIES  256
 
+/* position fix mode */
+enum {
+       POS_FIX_FIFO,
+       POS_FIX_NONE,
+       POS_FIX_POSBUF
+};
+
+/* Defines for ATI HD Audio support in SB450 south bridge */
+#define ATI_SB450_HDAUDIO_PCI_DEVICE_ID     0x437b
+#define ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR   0x42
+#define ATI_SB450_HDAUDIO_ENABLE_SNOOP      0x02
+
 
 /*
  * Use CORB/RIRB for communication from/to codecs.
@@ -190,12 +208,6 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
  */
 #define USE_CORB_RIRB
 
-/*
- * Define this if use the position buffer instead of reading SD_LPIB
- * It's not used as default since SD_LPIB seems to give more accurate position
- */
-/* #define USE_POSBUF */
-
 /*
  */
 
@@ -271,6 +283,10 @@ struct snd_azx {
        struct snd_dma_buffer bdl;
        struct snd_dma_buffer rb;
        struct snd_dma_buffer posbuf;
+
+       /* flags */
+       int position_fix;
+       unsigned int initialized: 1;
 };
 
 /*
@@ -638,7 +654,7 @@ static void azx_stream_stop(azx_t *chip, azx_dev_t *azx_dev)
  */
 static void azx_init_chip(azx_t *chip)
 {
-       unsigned char tcsel_reg;
+       unsigned char tcsel_reg, ati_misc_cntl2;
 
        /* Clear bits 0-2 of PCI register TCSEL (at offset 0x44)
         * TCSEL == Traffic Class Select Register, which sets PCI express QOS
@@ -657,11 +673,20 @@ static void azx_init_chip(azx_t *chip)
        /* initialize the codec command I/O */
        azx_init_cmd_io(chip);
 
-#ifdef USE_POSBUF
-       /* program the position buffer */
-       azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr);
-       azx_writel(chip, DPUBASE, upper_32bit(chip->posbuf.addr));
-#endif
+       if (chip->position_fix == POS_FIX_POSBUF) {
+               /* program the position buffer */
+               azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr);
+               azx_writel(chip, DPUBASE, upper_32bit(chip->posbuf.addr));
+       }
+
+       /* For ATI SB450 azalia HD audio, we need to enable snoop */
+       if (chip->pci->vendor == PCI_VENDOR_ID_ATI && 
+           chip->pci->device == ATI_SB450_HDAUDIO_PCI_DEVICE_ID) {
+               pci_read_config_byte(chip->pci, ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR, 
+                                    &ati_misc_cntl2);
+               pci_write_config_byte(chip->pci, ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR, 
+                                     (ati_misc_cntl2 & 0xf8) | ATI_SB450_HDAUDIO_ENABLE_SNOOP);
+       }
 }
 
 
@@ -791,11 +816,12 @@ static int azx_setup_controller(azx_t *chip, azx_dev_t *azx_dev)
        /* upper BDL address */
        azx_sd_writel(azx_dev, SD_BDLPU, upper_32bit(azx_dev->bdl_addr));
 
-#ifdef USE_POSBUF
-       /* enable the position buffer */
-       if (! (azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
-               azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE);
-#endif
+       if (chip->position_fix == POS_FIX_POSBUF) {
+               /* enable the position buffer */
+               if (! (azx_readl(chip, DPLBASE) & ICH6_DPLBASE_ENABLE))
+                       azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr | ICH6_DPLBASE_ENABLE);
+       }
+
        /* set the interrupt enable bits in the descriptor control register */
        azx_sd_writel(azx_dev, SD_CTL, azx_sd_readl(azx_dev, SD_CTL) | SD_INT_MASK);
 
@@ -1036,16 +1062,20 @@ static int azx_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
 
 static snd_pcm_uframes_t azx_pcm_pointer(snd_pcm_substream_t *substream)
 {
+       struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+       azx_t *chip = apcm->chip;
        azx_dev_t *azx_dev = get_azx_dev(substream);
        unsigned int pos;
 
-#ifdef USE_POSBUF
-       /* use the position buffer */
-       pos = *azx_dev->posbuf;
-#else
-       /* read LPIB */
-       pos = azx_sd_readl(azx_dev, SD_LPIB) + azx_dev->fifo_size;
-#endif
+       if (chip->position_fix == POS_FIX_POSBUF) {
+               /* use the position buffer */
+               pos = *azx_dev->posbuf;
+       } else {
+               /* read LPIB */
+               pos = azx_sd_readl(azx_dev, SD_LPIB);
+               if (chip->position_fix == POS_FIX_FIFO)
+                       pos += azx_dev->fifo_size;
+       }
        if (pos >= azx_dev->bufsize)
                pos = 0;
        return bytes_to_frames(substream->runtime, pos);
@@ -1155,9 +1185,8 @@ static int __devinit azx_init_stream(azx_t *chip)
                azx_dev_t *azx_dev = &chip->azx_dev[i];
                azx_dev->bdl = (u32 *)(chip->bdl.area + off);
                azx_dev->bdl_addr = chip->bdl.addr + off;
-#ifdef USE_POSBUF
-               azx_dev->posbuf = (volatile u32 *)(chip->posbuf.area + i * 8);
-#endif
+               if (chip->position_fix == POS_FIX_POSBUF)
+                       azx_dev->posbuf = (volatile u32 *)(chip->posbuf.area + i * 8);
                /* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
                azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80);
                /* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */
@@ -1207,7 +1236,7 @@ static int azx_resume(snd_card_t *card)
  */
 static int azx_free(azx_t *chip)
 {
-       if (chip->remap_addr) {
+       if (chip->initialized) {
                int i;
 
                for (i = 0; i < MAX_ICH6_DEV; i++)
@@ -1237,10 +1266,8 @@ static int azx_free(azx_t *chip)
                snd_dma_free_pages(&chip->bdl);
        if (chip->rb.area)
                snd_dma_free_pages(&chip->rb);
-#ifdef USE_POSBUF
        if (chip->posbuf.area)
                snd_dma_free_pages(&chip->posbuf);
-#endif
        pci_release_regions(chip->pci);
        pci_disable_device(chip->pci);
        kfree(chip);
@@ -1256,7 +1283,8 @@ static int azx_dev_free(snd_device_t *device)
 /*
  * constructor
  */
-static int __devinit azx_create(snd_card_t *card, struct pci_dev *pci, azx_t **rchip)
+static int __devinit azx_create(snd_card_t *card, struct pci_dev *pci,
+                               int posfix, azx_t **rchip)
 {
        azx_t *chip;
        int err = 0;
@@ -1283,6 +1311,8 @@ static int __devinit azx_create(snd_card_t *card, struct pci_dev *pci, azx_t **r
        chip->pci = pci;
        chip->irq = -1;
 
+       chip->position_fix = posfix;
+
        if ((err = pci_request_regions(pci, "ICH HD audio")) < 0) {
                kfree(chip);
                pci_disable_device(pci);
@@ -1314,14 +1344,14 @@ static int __devinit azx_create(snd_card_t *card, struct pci_dev *pci, azx_t **r
                snd_printk(KERN_ERR SFX "cannot allocate BDL\n");
                goto errout;
        }
-#ifdef USE_POSBUF
-       /* allocate memory for the position buffer */
-       if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
-                                      MAX_ICH6_DEV * 8, &chip->posbuf)) < 0) {
-               snd_printk(KERN_ERR SFX "cannot allocate posbuf\n");
-               goto errout;
+       if (chip->position_fix == POS_FIX_POSBUF) {
+               /* allocate memory for the position buffer */
+               if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+                                              MAX_ICH6_DEV * 8, &chip->posbuf)) < 0) {
+                       snd_printk(KERN_ERR SFX "cannot allocate posbuf\n");
+                       goto errout;
+               }
        }
-#endif
        /* allocate CORB/RIRB */
        if ((err = azx_alloc_cmd_io(chip)) < 0)
                goto errout;
@@ -1332,6 +1362,8 @@ static int __devinit azx_create(snd_card_t *card, struct pci_dev *pci, azx_t **r
        /* initialize chip */
        azx_init_chip(chip);
 
+       chip->initialized = 1;
+
        /* codec detection */
        if (! chip->codec_mask) {
                snd_printk(KERN_ERR SFX "no codecs found!\n");
@@ -1372,7 +1404,7 @@ static int __devinit azx_probe(struct pci_dev *pci, const struct pci_device_id *
                return -ENOMEM;
        }
 
-       if ((err = azx_create(card, pci, &chip)) < 0) {
+       if ((err = azx_create(card, pci, position_fix[dev], &chip)) < 0) {
                snd_card_free(card);
                return err;
        }
@@ -1424,6 +1456,9 @@ static struct pci_device_id azx_ids[] = {
        { 0x8086, 0x2668, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICH6 */
        { 0x8086, 0x27d8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ICH7 */
        { 0x8086, 0x269a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ESB2 */
+       { 0x1002, 0x437b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ATI SB450 */
+       { 0x1106, 0x3288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* VIA VT8251/VT8237A */
+       { 0x10b9, 0x5461, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* ALI 5461? */
        { 0, }
 };
 MODULE_DEVICE_TABLE(pci, azx_ids);
@@ -1439,7 +1474,7 @@ static struct pci_driver driver = {
 
 static int __init alsa_card_azx_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_azx_exit(void)
index 7c7b849875a0015a2c4e7878d3744d1d8b2920a8..810cfd2d9bba29b99d6726edb2fb301a69f534ed 100644 (file)
@@ -126,11 +126,11 @@ static inline int snd_hda_codec_proc_new(struct hda_codec *codec) { return 0; }
 struct hda_board_config {
        const char *modelname;
        int config;
-       unsigned short pci_vendor;
-       unsigned short pci_device;
+       unsigned short pci_subvendor;
+       unsigned short pci_subdevice;
 };
 
-int snd_hda_check_board_config(struct hda_codec *codec, struct hda_board_config *tbl);
+int snd_hda_check_board_config(struct hda_codec *codec, const struct hda_board_config *tbl);
 int snd_hda_add_new_ctls(struct hda_codec *codec, snd_kcontrol_new_t *knew);
 
 /*
@@ -158,4 +158,35 @@ struct hda_bus_unsolicited {
        struct work_struct work;
 };
 
+/*
+ * Helper for automatic ping configuration
+ */
+
+enum {
+       AUTO_PIN_MIC,
+       AUTO_PIN_FRONT_MIC,
+       AUTO_PIN_LINE,
+       AUTO_PIN_FRONT_LINE,
+       AUTO_PIN_CD,
+       AUTO_PIN_AUX,
+       AUTO_PIN_LAST
+};
+
+struct auto_pin_cfg {
+       int line_outs;
+       hda_nid_t line_out_pins[4]; /* sorted in the order of Front/Surr/CLFE/Side */
+       hda_nid_t hp_pin;
+       hda_nid_t input_pins[AUTO_PIN_LAST];
+       hda_nid_t dig_out_pin;
+       hda_nid_t dig_in_pin;
+};
+
+#define get_defcfg_connect(cfg) ((cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT)
+#define get_defcfg_association(cfg) ((cfg & AC_DEFCFG_DEF_ASSOC) >> AC_DEFCFG_ASSOC_SHIFT)
+#define get_defcfg_location(cfg) ((cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT)
+#define get_defcfg_sequence(cfg) (cfg & AC_DEFCFG_SEQUENCE)
+#define get_defcfg_device(cfg) ((cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT)
+
+int snd_hda_parse_pin_def_config(struct hda_codec *codec, struct auto_pin_cfg *cfg);
+
 #endif /* __SOUND_HDA_LOCAL_H */
index cf6abce42bc9ea15666e50f8b7b2d64b079b782a..a5de684b69446a6c958324ba566d91794f5313a9 100644 (file)
@@ -8,10 +8,13 @@ extern struct hda_codec_preset snd_hda_preset_realtek[];
 extern struct hda_codec_preset snd_hda_preset_cmedia[];
 /* Analog Devices codecs */
 extern struct hda_codec_preset snd_hda_preset_analog[];
+/* SigmaTel codecs */
+extern struct hda_codec_preset snd_hda_preset_sigmatel[];
 
 static const struct hda_codec_preset *hda_preset_tables[] = {
        snd_hda_preset_realtek,
        snd_hda_preset_cmedia,
        snd_hda_preset_analog,
+       snd_hda_preset_sigmatel,
        NULL
 };
index 4d5db7faad8d9db4ed437d16e655a2e1d869661e..de1217bd8e68288733f59fcdab0f4ea6441c33e2 100644 (file)
@@ -68,21 +68,27 @@ static void print_amp_caps(snd_info_buffer_t *buffer,
 
 static void print_amp_vals(snd_info_buffer_t *buffer,
                           struct hda_codec *codec, hda_nid_t nid,
-                          int dir, int stereo)
+                          int dir, int stereo, int indices)
 {
        unsigned int val;
-       if (stereo) {
+       int i;
+
+       if (dir == HDA_OUTPUT)
+               dir = AC_AMP_GET_OUTPUT;
+       else
+               dir = AC_AMP_GET_INPUT;
+       for (i = 0; i < indices; i++) {
+               snd_iprintf(buffer, " [");
+               if (stereo) {
+                       val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE,
+                                                AC_AMP_GET_LEFT | dir | i);
+                       snd_iprintf(buffer, "0x%02x ", val);
+               }
                val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE,
-                                         AC_AMP_GET_LEFT |
-                                        (dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT :
-                                         AC_AMP_GET_INPUT));
-               snd_iprintf(buffer, "0x%02x ", val);
+                                        AC_AMP_GET_RIGHT | dir | i);
+               snd_iprintf(buffer, "0x%02x]", val);
        }
-       val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE,
-                                AC_AMP_GET_RIGHT |
-                                (dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT :
-                                 AC_AMP_GET_INPUT));
-       snd_iprintf(buffer, "0x%02x\n", val);
+       snd_iprintf(buffer, "\n");
 }
 
 static void print_pcm_caps(snd_info_buffer_t *buffer,
@@ -157,6 +163,7 @@ static const char *get_jack_color(u32 cfg)
 static void print_pin_caps(snd_info_buffer_t *buffer,
                           struct hda_codec *codec, hda_nid_t nid)
 {
+       static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" };
        static char *jack_types[16] = {
                "Line Out", "Speaker", "HP Out", "CD",
                "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand",
@@ -176,7 +183,8 @@ static void print_pin_caps(snd_info_buffer_t *buffer,
                snd_iprintf(buffer, " HP");
        snd_iprintf(buffer, "\n");
        caps = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
-       snd_iprintf(buffer, "  Pin Default 0x%08x: %s at %s %s\n", caps,
+       snd_iprintf(buffer, "  Pin Default 0x%08x: [%s] %s at %s %s\n", caps,
+                   jack_conns[(caps & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT],
                    jack_types[(caps & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT],
                    jack_locations[(caps >> (AC_DEFCFG_LOCATION_SHIFT + 4)) & 3],
                    get_jack_location(caps));
@@ -215,6 +223,9 @@ static void print_codec_info(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
                unsigned int wid_caps = snd_hda_param_read(codec, nid,
                                                           AC_PAR_AUDIO_WIDGET_CAP);
                unsigned int wid_type = (wid_caps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+               int conn_len = 0; 
+               hda_nid_t conn[HDA_MAX_CONNECTIONS];
+
                snd_iprintf(buffer, "Node 0x%02x [%s] wcaps 0x%x:", nid,
                            get_wid_type_name(wid_type), wid_caps);
                if (wid_caps & AC_WCAP_STEREO)
@@ -229,19 +240,23 @@ static void print_codec_info(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
                        snd_iprintf(buffer, " Amp-Out");
                snd_iprintf(buffer, "\n");
 
+               if (wid_caps & AC_WCAP_CONN_LIST)
+                       conn_len = snd_hda_get_connections(codec, nid, conn,
+                                                          HDA_MAX_CONNECTIONS);
+
                if (wid_caps & AC_WCAP_IN_AMP) {
                        snd_iprintf(buffer, "  Amp-In caps: ");
                        print_amp_caps(buffer, codec, nid, HDA_INPUT);
                        snd_iprintf(buffer, "  Amp-In vals: ");
                        print_amp_vals(buffer, codec, nid, HDA_INPUT,
-                                      wid_caps & AC_WCAP_STEREO);
+                                      wid_caps & AC_WCAP_STEREO, conn_len);
                }
                if (wid_caps & AC_WCAP_OUT_AMP) {
                        snd_iprintf(buffer, "  Amp-Out caps: ");
                        print_amp_caps(buffer, codec, nid, HDA_OUTPUT);
                        snd_iprintf(buffer, "  Amp-Out vals: ");
                        print_amp_vals(buffer, codec, nid, HDA_OUTPUT,
-                                      wid_caps & AC_WCAP_STEREO);
+                                      wid_caps & AC_WCAP_STEREO, 1);
                }
 
                if (wid_type == AC_WID_PIN) {
@@ -265,14 +280,17 @@ static void print_codec_info(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
                }
 
                if (wid_caps & AC_WCAP_CONN_LIST) {
-                       hda_nid_t conn[HDA_MAX_CONNECTIONS];
-                       int c, conn_len;
-                       conn_len = snd_hda_get_connections(codec, nid, conn,
-                                                          HDA_MAX_CONNECTIONS);
+                       int c, curr = -1;
+                       if (conn_len > 1 && wid_type != AC_WID_AUD_MIX)
+                               curr = snd_hda_codec_read(codec, nid, 0,
+                                       AC_VERB_GET_CONNECT_SEL, 0);
                        snd_iprintf(buffer, "  Connection: %d\n", conn_len);
                        snd_iprintf(buffer, "    ");
-                       for (c = 0; c < conn_len; c++)
+                       for (c = 0; c < conn_len; c++) {
                                snd_iprintf(buffer, " 0x%02x", conn[c]);
+                               if (c == curr)
+                                       snd_iprintf(buffer, "*");
+                       }
                        snd_iprintf(buffer, "\n");
                }
        }
index 75d23849f71ac53e737d4c4a4da26be9eb47f829..2fd05bb841365ee67ebc5f8639307e133c4e0d0a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * HD audio interface patch for AD1986A
+ * HD audio interface patch for AD1981HD, AD1983, AD1986A
  *
  * Copyright (c) 2005 Takashi Iwai <tiwai@suse.de>
  *
 #include "hda_codec.h"
 #include "hda_local.h"
 
-struct ad1986a_spec {
+struct ad198x_spec {
        struct semaphore amp_mutex;     /* PCM volume/mute control mutex */
        struct hda_multi_out multiout;  /* playback */
+       hda_nid_t adc_nid;
+       const struct hda_input_mux *input_mux;
        unsigned int cur_mux;           /* capture source */
+       unsigned int spdif_route;
+       snd_kcontrol_new_t *mixers;
+       const struct hda_verb *init_verbs;
        struct hda_pcm pcm_rec[2];      /* PCM information */
 };
 
+/*
+ * input MUX handling (common part)
+ */
+static int ad198x_mux_enum_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct ad198x_spec *spec = codec->spec;
+
+       return snd_hda_input_mux_info(spec->input_mux, uinfo);
+}
+
+static int ad198x_mux_enum_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct ad198x_spec *spec = codec->spec;
+
+       ucontrol->value.enumerated.item[0] = spec->cur_mux;
+       return 0;
+}
+
+static int ad198x_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct ad198x_spec *spec = codec->spec;
+
+       return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
+                                    spec->adc_nid, &spec->cur_mux);
+}
+
+/*
+ * initialization (common callbacks)
+ */
+static int ad198x_init(struct hda_codec *codec)
+{
+       struct ad198x_spec *spec = codec->spec;
+       snd_hda_sequence_write(codec, spec->init_verbs);
+       return 0;
+}
+
+static int ad198x_build_controls(struct hda_codec *codec)
+{
+       struct ad198x_spec *spec = codec->spec;
+       int err;
+
+       err = snd_hda_add_new_ctls(codec, spec->mixers);
+       if (err < 0)
+               return err;
+       if (spec->multiout.dig_out_nid)
+               err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
+       if (err < 0)
+               return err;
+       return 0;
+}
+
+/*
+ * Analog playback callbacks
+ */
+static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
+                                   struct hda_codec *codec,
+                                   snd_pcm_substream_t *substream)
+{
+       struct ad198x_spec *spec = codec->spec;
+       return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
+}
+
+static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+                                      struct hda_codec *codec,
+                                      unsigned int stream_tag,
+                                      unsigned int format,
+                                      snd_pcm_substream_t *substream)
+{
+       struct ad198x_spec *spec = codec->spec;
+       return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
+                                               format, substream);
+}
+
+static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                                      struct hda_codec *codec,
+                                      snd_pcm_substream_t *substream)
+{
+       struct ad198x_spec *spec = codec->spec;
+       return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+}
+
+/*
+ * Digital out
+ */
+static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+                                       struct hda_codec *codec,
+                                       snd_pcm_substream_t *substream)
+{
+       struct ad198x_spec *spec = codec->spec;
+       return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+                                        struct hda_codec *codec,
+                                        snd_pcm_substream_t *substream)
+{
+       struct ad198x_spec *spec = codec->spec;
+       return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+/*
+ * Analog capture
+ */
+static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+                                     struct hda_codec *codec,
+                                     unsigned int stream_tag,
+                                     unsigned int format,
+                                     snd_pcm_substream_t *substream)
+{
+       struct ad198x_spec *spec = codec->spec;
+       snd_hda_codec_setup_stream(codec, spec->adc_nid, stream_tag, 0, format);
+       return 0;
+}
+
+static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                                     struct hda_codec *codec,
+                                     snd_pcm_substream_t *substream)
+{
+       struct ad198x_spec *spec = codec->spec;
+       snd_hda_codec_setup_stream(codec, spec->adc_nid, 0, 0, 0);
+       return 0;
+}
+
+
+/*
+ */
+static struct hda_pcm_stream ad198x_pcm_analog_playback = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 6,
+       .nid = 0, /* fill later */
+       .ops = {
+               .open = ad198x_playback_pcm_open,
+               .prepare = ad198x_playback_pcm_prepare,
+               .cleanup = ad198x_playback_pcm_cleanup
+       },
+};
+
+static struct hda_pcm_stream ad198x_pcm_analog_capture = {
+       .substreams = 2,
+       .channels_min = 2,
+       .channels_max = 2,
+       .nid = 0, /* fill later */
+       .ops = {
+               .prepare = ad198x_capture_pcm_prepare,
+               .cleanup = ad198x_capture_pcm_cleanup
+       },
+};
+
+static struct hda_pcm_stream ad198x_pcm_digital_playback = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       .nid = 0, /* fill later */
+       .ops = {
+               .open = ad198x_dig_playback_pcm_open,
+               .close = ad198x_dig_playback_pcm_close
+       },
+};
+
+static int ad198x_build_pcms(struct hda_codec *codec)
+{
+       struct ad198x_spec *spec = codec->spec;
+       struct hda_pcm *info = spec->pcm_rec;
+
+       codec->num_pcms = 1;
+       codec->pcm_info = info;
+
+       info->name = "AD198x Analog";
+       info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback;
+       info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels;
+       info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
+       info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture;
+       info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nid;
+
+       if (spec->multiout.dig_out_nid) {
+               info++;
+               codec->num_pcms++;
+               info->name = "AD198x Digital";
+               info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;
+               info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
+       }
+
+       return 0;
+}
+
+static void ad198x_free(struct hda_codec *codec)
+{
+       kfree(codec->spec);
+}
+
+#ifdef CONFIG_PM
+static int ad198x_resume(struct hda_codec *codec)
+{
+       struct ad198x_spec *spec = codec->spec;
+
+       ad198x_init(codec);
+       snd_hda_resume_ctls(codec, spec->mixers);
+       snd_hda_resume_spdif_out(codec);
+       return 0;
+}
+#endif
+
+static struct hda_codec_ops ad198x_patch_ops = {
+       .build_controls = ad198x_build_controls,
+       .build_pcms = ad198x_build_pcms,
+       .init = ad198x_init,
+       .free = ad198x_free,
+#ifdef CONFIG_PM
+       .resume = ad198x_resume,
+#endif
+};
+
+
+/*
+ * AD1986A specific
+ */
+
 #define AD1986A_SPDIF_OUT      0x02
 #define AD1986A_FRONT_DAC      0x03
 #define AD1986A_SURR_DAC       0x04
@@ -68,7 +294,7 @@ static struct hda_input_mux ad1986a_capture_source = {
 static int ad1986a_pcm_amp_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct ad1986a_spec *ad = codec->spec;
+       struct ad198x_spec *ad = codec->spec;
 
        down(&ad->amp_mutex);
        snd_hda_mixer_amp_volume_get(kcontrol, ucontrol);
@@ -79,7 +305,7 @@ static int ad1986a_pcm_amp_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_
 static int ad1986a_pcm_amp_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct ad1986a_spec *ad = codec->spec;
+       struct ad198x_spec *ad = codec->spec;
        int i, change = 0;
 
        down(&ad->amp_mutex);
@@ -92,12 +318,12 @@ static int ad1986a_pcm_amp_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_
        return change;
 }
 
-#define ad1986a_pcm_amp_sw_info                snd_hda_mixer_amp_volume_info
+#define ad1986a_pcm_amp_sw_info                snd_hda_mixer_amp_switch_info
 
 static int ad1986a_pcm_amp_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct ad1986a_spec *ad = codec->spec;
+       struct ad198x_spec *ad = codec->spec;
 
        down(&ad->amp_mutex);
        snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);
@@ -108,7 +334,7 @@ static int ad1986a_pcm_amp_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
 static int ad1986a_pcm_amp_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct ad1986a_spec *ad = codec->spec;
+       struct ad198x_spec *ad = codec->spec;
        int i, change = 0;
 
        down(&ad->amp_mutex);
@@ -121,32 +347,6 @@ static int ad1986a_pcm_amp_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
        return change;
 }
 
-/*
- * input MUX handling
- */
-static int ad1986a_mux_enum_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
-{
-       return snd_hda_input_mux_info(&ad1986a_capture_source, uinfo);
-}
-
-static int ad1986a_mux_enum_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct ad1986a_spec *spec = codec->spec;
-
-       ucontrol->value.enumerated.item[0] = spec->cur_mux;
-       return 0;
-}
-
-static int ad1986a_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct ad1986a_spec *spec = codec->spec;
-
-       return snd_hda_input_mux_put(codec, &ad1986a_capture_source, ucontrol,
-                                    AD1986A_ADC, &spec->cur_mux);
-}
-
 /*
  * mixers
  */
@@ -194,9 +394,9 @@ static snd_kcontrol_new_t ad1986a_mixers[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Capture Source",
-               .info = ad1986a_mux_enum_info,
-               .get = ad1986a_mux_enum_get,
-               .put = ad1986a_mux_enum_put,
+               .info = ad198x_mux_enum_info,
+               .get = ad198x_mux_enum_get,
+               .put = ad198x_mux_enum_put,
        },
        HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
        { } /* end */
@@ -241,183 +441,328 @@ static struct hda_verb ad1986a_init_verbs[] = {
        {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
        {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
        {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       /* HP Pin */
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+       /* Front, Surround, CLFE Pins */
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+       {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+       /* Mono Pin */
+       {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+       /* Mic Pin */
+       {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+       /* Line, Aux, CD, Beep-In Pin */
+       {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+       {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+       {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+       {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+       {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
        { } /* end */
 };
 
 
-static int ad1986a_init(struct hda_codec *codec)
+static int patch_ad1986a(struct hda_codec *codec)
 {
-       snd_hda_sequence_write(codec, ad1986a_init_verbs);
-       return 0;
-}
+       struct ad198x_spec *spec;
 
-static int ad1986a_build_controls(struct hda_codec *codec)
-{
-       int err;
+       spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+
+       init_MUTEX(&spec->amp_mutex);
+       codec->spec = spec;
+
+       spec->multiout.max_channels = 6;
+       spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
+       spec->multiout.dac_nids = ad1986a_dac_nids;
+       spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
+       spec->adc_nid = AD1986A_ADC;
+       spec->input_mux = &ad1986a_capture_source;
+       spec->mixers = ad1986a_mixers;
+       spec->init_verbs = ad1986a_init_verbs;
+
+       codec->patch_ops = ad198x_patch_ops;
 
-       err = snd_hda_add_new_ctls(codec, ad1986a_mixers);
-       if (err < 0)
-               return err;
-       err = snd_hda_create_spdif_out_ctls(codec, AD1986A_SPDIF_OUT);
-       if (err < 0)
-               return err;
        return 0;
 }
 
 /*
- * Analog playback callbacks
+ * AD1983 specific
  */
-static int ad1986a_playback_pcm_open(struct hda_pcm_stream *hinfo,
-                                    struct hda_codec *codec,
-                                    snd_pcm_substream_t *substream)
-{
-       struct ad1986a_spec *spec = codec->spec;
-       return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
-}
 
-static int ad1986a_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
-                                       struct hda_codec *codec,
-                                       unsigned int stream_tag,
-                                       unsigned int format,
-                                       snd_pcm_substream_t *substream)
-{
-       struct ad1986a_spec *spec = codec->spec;
-       return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
-                                               format, substream);
-}
+#define AD1983_SPDIF_OUT       0x02
+#define AD1983_DAC             0x03
+#define AD1983_ADC             0x04
 
-static int ad1986a_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
-                                       struct hda_codec *codec,
-                                       snd_pcm_substream_t *substream)
-{
-       struct ad1986a_spec *spec = codec->spec;
-       return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
-}
+static hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
+
+static struct hda_input_mux ad1983_capture_source = {
+       .num_items = 4,
+       .items = {
+               { "Mic", 0x0 },
+               { "Line", 0x1 },
+               { "Mix", 0x2 },
+               { "Mix Mono", 0x3 },
+       },
+};
 
 /*
- * Digital out
+ * SPDIF playback route
  */
-static int ad1986a_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
-                                        struct hda_codec *codec,
-                                        snd_pcm_substream_t *substream)
+static int ad1983_spdif_route_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
 {
-       struct ad1986a_spec *spec = codec->spec;
-       return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+       static char *texts[] = { "PCM", "ADC" };
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 2;
+       if (uinfo->value.enumerated.item > 1)
+               uinfo->value.enumerated.item = 1;
+       strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+       return 0;
 }
 
-static int ad1986a_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
-                                         struct hda_codec *codec,
-                                         snd_pcm_substream_t *substream)
+static int ad1983_spdif_route_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
 {
-       struct ad1986a_spec *spec = codec->spec;
-       return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct ad198x_spec *spec = codec->spec;
 
-/*
- * Analog capture
- */
-static int ad1986a_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
-                                      struct hda_codec *codec,
-                                      unsigned int stream_tag,
-                                      unsigned int format,
-                                      snd_pcm_substream_t *substream)
-{
-       snd_hda_codec_setup_stream(codec, AD1986A_ADC, stream_tag, 0, format);
+       ucontrol->value.enumerated.item[0] = spec->spdif_route;
        return 0;
 }
 
-static int ad1986a_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
-                                      struct hda_codec *codec,
-                                      snd_pcm_substream_t *substream)
+static int ad1983_spdif_route_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
 {
-       snd_hda_codec_setup_stream(codec, AD1986A_ADC, 0, 0, 0);
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct ad198x_spec *spec = codec->spec;
+
+       if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
+               spec->spdif_route = ucontrol->value.enumerated.item[0];
+               snd_hda_codec_write(codec, spec->multiout.dig_out_nid, 0,
+                                   AC_VERB_SET_CONNECT_SEL, spec->spdif_route);
+               return 1;
+       }
        return 0;
 }
 
-
-/*
- */
-static struct hda_pcm_stream ad1986a_pcm_analog_playback = {
-       .substreams = 1,
-       .channels_min = 2,
-       .channels_max = 6,
-       .nid = AD1986A_FRONT_DAC, /* NID to query formats and rates */
-       .ops = {
-               .open = ad1986a_playback_pcm_open,
-               .prepare = ad1986a_playback_pcm_prepare,
-               .cleanup = ad1986a_playback_pcm_cleanup
+static snd_kcontrol_new_t ad1983_mixers[] = {
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MONO("PC Speaker Playback Volume", 0x10, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_MONO("PC Speaker Playback Switch", 0x10, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Mic Boost", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Capture Source",
+               .info = ad198x_mux_enum_info,
+               .get = ad198x_mux_enum_get,
+               .put = ad198x_mux_enum_put,
        },
-};
-
-static struct hda_pcm_stream ad1986a_pcm_analog_capture = {
-       .substreams = 2,
-       .channels_min = 2,
-       .channels_max = 2,
-       .nid = AD1986A_ADC, /* NID to query formats and rates */
-       .ops = {
-               .prepare = ad1986a_capture_pcm_prepare,
-               .cleanup = ad1986a_capture_pcm_cleanup
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "IEC958 Playback Route",
+               .info = ad1983_spdif_route_info,
+               .get = ad1983_spdif_route_get,
+               .put = ad1983_spdif_route_put,
        },
+       { } /* end */
 };
 
-static struct hda_pcm_stream ad1986a_pcm_digital_playback = {
-       .substreams = 1,
-       .channels_min = 2,
-       .channels_max = 2,
-       .nid = AD1986A_SPDIF_OUT, 
-       .ops = {
-               .open = ad1986a_dig_playback_pcm_open,
-               .close = ad1986a_dig_playback_pcm_close
-       },
+static struct hda_verb ad1983_init_verbs[] = {
+       /* Front, HP, Mono; mute as default */
+       {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       /* Beep, PCM, Mic, Line-In: mute */
+       {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       /* Front, HP selectors; from Mix */
+       {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
+       {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
+       /* Mono selector; from Mix */
+       {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
+       /* Mic selector; Mic */
+       {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
+       /* Line-in selector: Line-in */
+       {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
+       /* Mic boost: 0dB */
+       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+       /* Record selector: mic */
+       {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       /* SPDIF route: PCM */
+       {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
+       /* Front Pin */
+       {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+       /* HP Pin */
+       {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+       /* Mono Pin */
+       {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+       /* Mic Pin */
+       {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+       /* Line Pin */
+       {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+       { } /* end */
 };
 
-static int ad1986a_build_pcms(struct hda_codec *codec)
+static int patch_ad1983(struct hda_codec *codec)
 {
-       struct ad1986a_spec *spec = codec->spec;
-       struct hda_pcm *info = spec->pcm_rec;
+       struct ad198x_spec *spec;
 
-       codec->num_pcms = 2;
-       codec->pcm_info = info;
+       spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
 
-       info->name = "AD1986A Analog";
-       info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad1986a_pcm_analog_playback;
-       info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1986a_pcm_analog_capture;
-       info++;
+       init_MUTEX(&spec->amp_mutex);
+       codec->spec = spec;
+
+       spec->multiout.max_channels = 2;
+       spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
+       spec->multiout.dac_nids = ad1983_dac_nids;
+       spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
+       spec->adc_nid = AD1983_ADC;
+       spec->input_mux = &ad1983_capture_source;
+       spec->mixers = ad1983_mixers;
+       spec->init_verbs = ad1983_init_verbs;
+       spec->spdif_route = 0;
 
-       info->name = "AD1986A Digital";
-       info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad1986a_pcm_digital_playback;
+       codec->patch_ops = ad198x_patch_ops;
 
        return 0;
 }
 
-static void ad1986a_free(struct hda_codec *codec)
-{
-       kfree(codec->spec);
-}
 
-#ifdef CONFIG_PM
-static int ad1986a_resume(struct hda_codec *codec)
-{
-       ad1986a_init(codec);
-       snd_hda_resume_ctls(codec, ad1986a_mixers);
-       snd_hda_resume_spdif_out(codec);
-       return 0;
-}
-#endif
+/*
+ * AD1981 HD specific
+ */
 
-static struct hda_codec_ops ad1986a_patch_ops = {
-       .build_controls = ad1986a_build_controls,
-       .build_pcms = ad1986a_build_pcms,
-       .init = ad1986a_init,
-       .free = ad1986a_free,
-#ifdef CONFIG_PM
-       .resume = ad1986a_resume,
-#endif
+#define AD1981_SPDIF_OUT       0x02
+#define AD1981_DAC             0x03
+#define AD1981_ADC             0x04
+
+static hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
+
+/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
+static struct hda_input_mux ad1981_capture_source = {
+       .num_items = 7,
+       .items = {
+               { "Front Mic", 0x0 },
+               { "Line", 0x1 },
+               { "Mix", 0x2 },
+               { "Mix Mono", 0x3 },
+               { "CD", 0x4 },
+               { "Mic", 0x6 },
+               { "Aux", 0x7 },
+       },
 };
 
-static int patch_ad1986a(struct hda_codec *codec)
+static snd_kcontrol_new_t ad1981_mixers[] = {
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MONO("PC Speaker Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE_MONO("PC Speaker Playback Switch", 0x0d, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Front Mic Boost", 0x08, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Capture Source",
+               .info = ad198x_mux_enum_info,
+               .get = ad198x_mux_enum_get,
+               .put = ad198x_mux_enum_put,
+       },
+       /* identical with AD1983 */
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "IEC958 Playback Route",
+               .info = ad1983_spdif_route_info,
+               .get = ad1983_spdif_route_get,
+               .put = ad1983_spdif_route_put,
+       },
+       { } /* end */
+};
+
+static struct hda_verb ad1981_init_verbs[] = {
+       /* Front, HP, Mono; mute as default */
+       {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
+       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       /* Front, HP selectors; from Mix */
+       {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
+       {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
+       /* Mono selector; from Mix */
+       {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
+       /* Mic Mixer; select Front Mic */
+       {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+       {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       /* Mic boost: 0dB */
+       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+       /* Record selector: Front mic */
+       {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       /* SPDIF route: PCM */
+       {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
+       /* Front Pin */
+       {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+       /* HP Pin */
+       {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+       /* Mono Pin */
+       {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+       /* Front & Rear Mic Pins */
+       {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+       /* Line Pin */
+       {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+       /* Digital Beep */
+       {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
+       /* Line-Out as Input: disabled */
+       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       { } /* end */
+};
+
+static int patch_ad1981(struct hda_codec *codec)
 {
-       struct ad1986a_spec *spec;
+       struct ad198x_spec *spec;
 
        spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
        if (spec == NULL)
@@ -426,20 +771,28 @@ static int patch_ad1986a(struct hda_codec *codec)
        init_MUTEX(&spec->amp_mutex);
        codec->spec = spec;
 
-       spec->multiout.max_channels = 6;
-       spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
-       spec->multiout.dac_nids = ad1986a_dac_nids;
-       spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
+       spec->multiout.max_channels = 2;
+       spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
+       spec->multiout.dac_nids = ad1981_dac_nids;
+       spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
+       spec->adc_nid = AD1981_ADC;
+       spec->input_mux = &ad1981_capture_source;
+       spec->mixers = ad1981_mixers;
+       spec->init_verbs = ad1981_init_verbs;
+       spec->spdif_route = 0;
 
-       codec->patch_ops = ad1986a_patch_ops;
+       codec->patch_ops = ad198x_patch_ops;
 
        return 0;
 }
 
+
 /*
  * patch entries
  */
 struct hda_codec_preset snd_hda_preset_analog[] = {
+       { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
+       { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
        { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
        {} /* terminator */
 };
index b7cc8e4bffb7f2e099a8601463935500f972e03e..2d6e3e3d0a38c9354aa5200ef5edf10776564d8d 100644 (file)
@@ -29,6 +29,7 @@
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#define NUM_PINS       11
 
 
 /* board config type */
@@ -38,6 +39,7 @@ enum {
        CMI_FULL,       /* back 6-jack + front-panel 2-jack */
        CMI_FULL_DIG,   /* back 6-jack + front-panel 2-jack + digital I/O */
        CMI_ALLOUT,     /* back 5-jack + front-panel 2-jack + digital out */
+       CMI_AUTO,       /* let driver guess it */
 };
 
 struct cmi_spec {
@@ -48,6 +50,8 @@ struct cmi_spec {
 
        /* playback */
        struct hda_multi_out multiout;
+       hda_nid_t dac_nids[4];          /* NID for each DAC */
+       int num_dacs;
 
        /* capture */
        hda_nid_t *adc_nids;
@@ -63,8 +67,30 @@ struct cmi_spec {
        const struct cmi_channel_mode *channel_modes;
 
        struct hda_pcm pcm_rec[2];      /* PCM information */
+
+       /* pin deafault configuration */
+       hda_nid_t pin_nid[NUM_PINS];
+       unsigned int def_conf[NUM_PINS];
+       unsigned int pin_def_confs;
+
+       /* multichannel pins */
+       hda_nid_t multich_pin[4];       /* max 8-channel */
+       struct hda_verb multi_init[9];  /* 2 verbs for each pin + terminator */
 };
 
+/* amp values */
+#define AMP_IN_MUTE(idx)       (0x7080 | ((idx)<<8))
+#define AMP_IN_UNMUTE(idx)     (0x7000 | ((idx)<<8))
+#define AMP_OUT_MUTE   0xb080
+#define AMP_OUT_UNMUTE 0xb000
+#define AMP_OUT_ZERO   0xb000
+/* pinctl values */
+#define PIN_IN         0x20
+#define PIN_VREF80     0x24
+#define PIN_VREF50     0x21
+#define PIN_OUT                0x40
+#define PIN_HP         0xc0
+
 /*
  * input MUX
  */
@@ -102,9 +128,9 @@ static int cmi_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucon
 /* 3-stack / 2 channel */
 static struct hda_verb cmi9880_ch2_init[] = {
        /* set line-in PIN for input */
-       { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+       { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
        /* set mic PIN for input, also enable vref */
-       { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+       { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
        /* route front PCM (DAC1) to HP */
        { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
        {}
@@ -113,9 +139,9 @@ static struct hda_verb cmi9880_ch2_init[] = {
 /* 3-stack / 6 channel */
 static struct hda_verb cmi9880_ch6_init[] = {
        /* set line-in PIN for output */
-       { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+       { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
        /* set mic PIN for output */
-       { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+       { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
        /* route front PCM (DAC1) to HP */
        { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
        {}
@@ -124,9 +150,9 @@ static struct hda_verb cmi9880_ch6_init[] = {
 /* 3-stack+front / 8 channel */
 static struct hda_verb cmi9880_ch8_init[] = {
        /* set line-in PIN for output */
-       { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+       { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
        /* set mic PIN for output */
-       { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
+       { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
        /* route rear-surround PCM (DAC4) to HP */
        { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x03 },
        {}
@@ -269,25 +295,27 @@ static hda_nid_t cmi9880_adc_nids[2] = {
  */
 static struct hda_verb cmi9880_basic_init[] = {
        /* port-D for line out (rear panel) */
-       { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+       { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
        /* port-E for HP out (front panel) */
-       { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+       { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
        /* route front PCM to HP */
        { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
        /* port-A for surround (rear panel) */
-       { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+       { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
        /* port-G for CLFE (rear panel) */
-       { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+       { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
+       { 0x1f, AC_VERB_SET_CONNECT_SEL, 0x02 },
        /* port-H for side (rear panel) */
-       { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+       { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
+       { 0x20, AC_VERB_SET_CONNECT_SEL, 0x01 },
        /* port-C for line-in (rear panel) */
-       { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+       { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
        /* port-B for mic-in (rear panel) with vref */
-       { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+       { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
        /* port-F for mic-in (front panel) with vref */
-       { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+       { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
        /* CD-in */
-       { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+       { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
        /* route front mic to ADC1/2 */
        { 0x08, AC_VERB_SET_CONNECT_SEL, 0x05 },
        { 0x09, AC_VERB_SET_CONNECT_SEL, 0x05 },
@@ -296,23 +324,27 @@ static struct hda_verb cmi9880_basic_init[] = {
 
 static struct hda_verb cmi9880_allout_init[] = {
        /* port-D for line out (rear panel) */
-       { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+       { 0x0b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
        /* port-E for HP out (front panel) */
-       { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+       { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
        /* route front PCM to HP */
        { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x00 },
        /* port-A for side (rear panel) */
-       { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+       { 0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
        /* port-G for CLFE (rear panel) */
-       { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+       { 0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
+       { 0x1f, AC_VERB_SET_CONNECT_SEL, 0x02 },
+       /* port-H for side (rear panel) */
+       { 0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
+       { 0x20, AC_VERB_SET_CONNECT_SEL, 0x01 },
        /* port-C for surround (rear panel) */
-       { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
+       { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP },
        /* port-B for mic-in (rear panel) with vref */
-       { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+       { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
        /* port-F for mic-in (front panel) with vref */
-       { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
+       { 0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
        /* CD-in */
-       { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+       { 0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
        /* route front mic to ADC1/2 */
        { 0x08, AC_VERB_SET_CONNECT_SEL, 0x05 },
        { 0x09, AC_VERB_SET_CONNECT_SEL, 0x05 },
@@ -347,6 +379,81 @@ static int cmi9880_build_controls(struct hda_codec *codec)
        return 0;
 }
 
+/* fill in the multi_dac_nids table, which will decide
+   which audio widget to use for each channel */
+static int cmi9880_fill_multi_dac_nids(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
+{
+       struct cmi_spec *spec = codec->spec;
+       hda_nid_t nid;
+       int assigned[4];
+       int i, j;
+
+       /* clear the table, only one c-media dac assumed here */
+       memset(spec->dac_nids, 0, sizeof(spec->dac_nids));
+       memset(assigned, 0, sizeof(assigned));
+       /* check the pins we found */
+       for (i = 0; i < cfg->line_outs; i++) {
+               nid = cfg->line_out_pins[i];
+               /* nid 0x0b~0x0e is hardwired to audio widget 0x3~0x6 */
+               if (nid >= 0x0b && nid <= 0x0e) {
+                       spec->dac_nids[i] = (nid - 0x0b) + 0x03;
+                       assigned[nid - 0x0b] = 1;
+               }
+       }
+       /* left pin can be connect to any audio widget */
+       for (i = 0; i < cfg->line_outs; i++) {
+               nid = cfg->line_out_pins[i];
+               if (nid <= 0x0e)
+                       continue;
+               /* search for an empty channel */
+               for (j = 0; j < cfg->line_outs; j++) {
+                       if (! assigned[j]) {
+                               spec->dac_nids[i] = i + 0x03;
+                               assigned[j] = 1;
+                               break;
+                       }
+               }
+       }
+       spec->num_dacs = cfg->line_outs;
+       return 0;
+}
+
+/* create multi_init table, which is used for multichannel initialization */
+static int cmi9880_fill_multi_init(struct hda_codec *codec, const struct auto_pin_cfg *cfg)
+{
+       struct cmi_spec *spec = codec->spec;
+       hda_nid_t nid;
+       int i, j, k, len;
+
+       /* clear the table, only one c-media dac assumed here */
+       memset(spec->multi_init, 0, sizeof(spec->multi_init));
+       for (j = 0, i = 0; i < cfg->line_outs; i++) {
+               hda_nid_t conn[4];
+               nid = cfg->line_out_pins[i];
+               /* set as output */
+               spec->multi_init[j].nid = nid;
+               spec->multi_init[j].verb = AC_VERB_SET_PIN_WIDGET_CONTROL;
+               spec->multi_init[j].param = PIN_OUT;
+               j++;
+               if (nid > 0x0e) {
+                       /* set connection */
+                       spec->multi_init[j].nid = nid;
+                       spec->multi_init[j].verb = AC_VERB_SET_CONNECT_SEL;
+                       spec->multi_init[j].param = 0;
+                       /* find the index in connect list */
+                       len = snd_hda_get_connections(codec, nid, conn, 4);
+                       for (k = 0; k < len; k++)
+                               if (conn[k] == spec->dac_nids[i]) {
+                                       spec->multi_init[j].param = j;
+                                       break;
+                               }
+                       j++;
+                       break;
+               }
+       }
+       return 0;
+}
+
 static int cmi9880_init(struct hda_codec *codec)
 {
        struct cmi_spec *spec = codec->spec;
@@ -354,6 +461,8 @@ static int cmi9880_init(struct hda_codec *codec)
                snd_hda_sequence_write(codec, cmi9880_allout_init);
        else
                snd_hda_sequence_write(codec, cmi9880_basic_init);
+       if (spec->board_config == CMI_AUTO)
+               snd_hda_sequence_write(codec, spec->multi_init);
        return 0;
 }
 
@@ -540,6 +649,7 @@ static struct hda_board_config cmi9880_cfg_tbl[] = {
        { .modelname = "full", .config = CMI_FULL },
        { .modelname = "full_dig", .config = CMI_FULL_DIG },
        { .modelname = "allout", .config = CMI_ALLOUT },
+       { .modelname = "auto", .config = CMI_AUTO },
        {} /* terminator */
 };
 
@@ -564,10 +674,14 @@ static int patch_cmi9880(struct hda_codec *codec)
        codec->spec = spec;
        spec->board_config = snd_hda_check_board_config(codec, cmi9880_cfg_tbl);
        if (spec->board_config < 0) {
-               snd_printd(KERN_INFO "hda_codec: Unknown model for CMI9880\n");
-               spec->board_config = CMI_FULL_DIG; /* try everything */
+               snd_printdd(KERN_INFO "hda_codec: Unknown model for CMI9880\n");
+               spec->board_config = CMI_AUTO; /* try everything */
        }
 
+       /* copy default DAC NIDs */
+       memcpy(spec->dac_nids, cmi9880_dac_nids, sizeof(spec->dac_nids));
+       spec->num_dacs = 4;
+
        switch (spec->board_config) {
        case CMI_MINIMAL:
        case CMI_MIN_FP:
@@ -599,10 +713,58 @@ static int patch_cmi9880(struct hda_codec *codec)
                spec->input_mux = &cmi9880_no_line_mux;
                spec->multiout.dig_out_nid = CMI_DIG_OUT_NID;
                break;
+       case CMI_AUTO:
+               {
+               unsigned int port_e, port_f, port_g, port_h;
+               unsigned int port_spdifi, port_spdifo;
+               struct auto_pin_cfg cfg;
+
+               /* collect pin default configuration */
+               port_e = snd_hda_codec_read(codec, 0x0f, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+               port_f = snd_hda_codec_read(codec, 0x10, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+               spec->front_panel = 1;
+               if (get_defcfg_connect(port_e) == AC_JACK_PORT_NONE ||
+                   get_defcfg_connect(port_f) == AC_JACK_PORT_NONE) {
+                       port_g = snd_hda_codec_read(codec, 0x1f, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+                       port_h = snd_hda_codec_read(codec, 0x20, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+                       spec->surr_switch = 1;
+                       /* no front panel */
+                       if (get_defcfg_connect(port_g) == AC_JACK_PORT_NONE ||
+                           get_defcfg_connect(port_h) == AC_JACK_PORT_NONE) {
+                               /* no optional rear panel */
+                               spec->board_config = CMI_MINIMAL;
+                               spec->front_panel = 0;
+                               spec->num_ch_modes = 2;
+                       } else {
+                               spec->board_config = CMI_MIN_FP;
+                               spec->num_ch_modes = 3;
+                       }
+                       spec->channel_modes = cmi9880_channel_modes;
+                       spec->input_mux = &cmi9880_basic_mux;
+                       spec->multiout.max_channels = cmi9880_channel_modes[0].channels;
+               } else {
+                       spec->input_mux = &cmi9880_basic_mux;
+                       port_spdifi = snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+                       port_spdifo = snd_hda_codec_read(codec, 0x12, 0, AC_VERB_GET_CONFIG_DEFAULT, 0);
+                       if (get_defcfg_connect(port_spdifo) != AC_JACK_PORT_NONE)
+                               spec->multiout.dig_out_nid = CMI_DIG_OUT_NID;
+                       if (get_defcfg_connect(port_spdifi) != AC_JACK_PORT_NONE)
+                               spec->dig_in_nid = CMI_DIG_IN_NID;
+                       spec->multiout.max_channels = 8;
+               }
+               snd_hda_parse_pin_def_config(codec, &cfg);
+               if (cfg.line_outs) {
+                       spec->multiout.max_channels = cfg.line_outs * 2;
+                       cmi9880_fill_multi_dac_nids(codec, &cfg);
+                       cmi9880_fill_multi_init(codec, &cfg);
+               } else
+                       snd_printd("patch_cmedia: cannot detect association in defcfg\n");
+               break;
+               }
        }
 
-       spec->multiout.num_dacs = 4;
-       spec->multiout.dac_nids = cmi9880_dac_nids;
+       spec->multiout.num_dacs = spec->num_dacs;
+       spec->multiout.dac_nids = spec->dac_nids;
 
        spec->adc_nids = cmi9880_adc_nids;
 
index 17c5062423aee236922d940bb697ffd5822b1ea5..9edd558d6bd34359ce03714598d0de14c384f117 100644 (file)
 
 /* ALC880 board config type */
 enum {
-       ALC880_MINIMAL,
        ALC880_3ST,
        ALC880_3ST_DIG,
        ALC880_5ST,
        ALC880_5ST_DIG,
        ALC880_W810,
+       ALC880_Z71V,
+       ALC880_AUTO,
+       ALC880_6ST_DIG,
+       ALC880_F1734,
+       ALC880_ASUS,
+       ALC880_ASUS_DIG,
+       ALC880_ASUS_W1V,
+       ALC880_UNIWILL_DIG,
+#ifdef CONFIG_SND_DEBUG
+       ALC880_TEST,
+#endif
+       ALC880_MODEL_LAST /* last tag */
+};
+
+/* ALC260 models */
+enum {
+       ALC260_BASIC,
+       ALC260_HP,
+       ALC260_MODEL_LAST /* last tag */
 };
 
+/* amp values */
+#define AMP_IN_MUTE(idx)       (0x7080 | ((idx)<<8))
+#define AMP_IN_UNMUTE(idx)     (0x7000 | ((idx)<<8))
+#define AMP_OUT_MUTE   0xb080
+#define AMP_OUT_UNMUTE 0xb000
+#define AMP_OUT_ZERO   0xb000
+/* pinctl values */
+#define PIN_IN         0x20
+#define PIN_VREF80     0x24
+#define PIN_VREF50     0x21
+#define PIN_OUT                0x40
+#define PIN_HP         0xc0
+
 struct alc_spec {
        /* codec parameterization */
-       unsigned int front_panel: 1;
-
-       snd_kcontrol_new_t* mixers[2];
+       snd_kcontrol_new_t *mixers[3];  /* mixer arrays */
        unsigned int num_mixers;
 
-       struct hda_verb *init_verbs;
+       const struct hda_verb *init_verbs[3];   /* initialization verbs
+                                                * don't forget NULL termination!
+                                                */
+       unsigned int num_init_verbs;
 
-       char* stream_name_analog;
+       char *stream_name_analog;       /* analog PCM stream */
        struct hda_pcm_stream *stream_analog_playback;
        struct hda_pcm_stream *stream_analog_capture;
 
-       char* stream_name_digital;
+       char *stream_name_digital;      /* digital PCM stream */ 
        struct hda_pcm_stream *stream_digital_playback;
        struct hda_pcm_stream *stream_digital_capture;
 
        /* playback */
-       struct hda_multi_out multiout;
+       struct hda_multi_out multiout;  /* playback set-up
+                                        * max_channels, dacs must be set
+                                        * dig_out_nid and hp_nid are optional
+                                        */
 
        /* capture */
        unsigned int num_adc_nids;
        hda_nid_t *adc_nids;
-       hda_nid_t dig_in_nid;
+       hda_nid_t dig_in_nid;           /* digital-in NID; optional */
 
        /* capture source */
        const struct hda_input_mux *input_mux;
@@ -75,61 +110,17 @@ struct alc_spec {
        int num_channel_mode;
 
        /* PCM information */
-       struct hda_pcm pcm_rec[2];
-};
-
-/* DAC/ADC assignment */
-
-static hda_nid_t alc880_dac_nids[4] = {
-       /* front, rear, clfe, rear_surr */
-       0x02, 0x05, 0x04, 0x03
-};
-
-static hda_nid_t alc880_w810_dac_nids[3] = {
-       /* front, rear/surround, clfe */
-       0x02, 0x03, 0x04
-};
-
-static hda_nid_t alc880_adc_nids[3] = {
-       /* ADC0-2 */
-       0x07, 0x08, 0x09,
-};
-
-#define ALC880_DIGOUT_NID      0x06
-#define ALC880_DIGIN_NID       0x0a
-
-static hda_nid_t alc260_dac_nids[1] = {
-       /* front */
-       0x02,
-};
-
-static hda_nid_t alc260_adc_nids[2] = {
-       /* ADC0-1 */
-       0x04, 0x05,
-};
+       struct hda_pcm pcm_rec[2];      /* used in alc_build_pcms() */
 
-#define ALC260_DIGOUT_NID      0x03
-#define ALC260_DIGIN_NID       0x06
+       struct semaphore bind_mutex;    /* for bound controls */
 
-static struct hda_input_mux alc880_capture_source = {
-       .num_items = 4,
-       .items = {
-               { "Mic", 0x0 },
-               { "Front Mic", 0x3 },
-               { "Line", 0x2 },
-               { "CD", 0x4 },
-       },
+       /* dynamic controls, init_verbs and input_mux */
+       struct auto_pin_cfg autocfg;
+       unsigned int num_kctl_alloc, num_kctl_used;
+       snd_kcontrol_new_t *kctl_alloc;
+       struct hda_input_mux private_imux;
 };
 
-static struct hda_input_mux alc260_capture_source = {
-       .num_items = 4,
-       .items = {
-               { "Mic", 0x0 },
-               { "Front Mic", 0x1 },
-               { "Line", 0x2 },
-               { "CD", 0x4 },
-       },
-};
 
 /*
  * input MUX handling
@@ -160,6 +151,7 @@ static int alc_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucon
                                     spec->adc_nids[adc_idx], &spec->cur_mux[adc_idx]);
 }
 
+
 /*
  * channel mode setting
  */
@@ -168,135 +160,18 @@ struct alc_channel_mode {
        const struct hda_verb *sequence;
 };
 
-
-/*
- * 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 alc880_threestack_ch2_init[] = {
-       /* set pin widget 1Ah (line in) for input */
-       { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
-       /* set pin widget 18h (mic1) for input, for mic also enable the vref */
-       { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
-       /* mute the output for Line In PW */
-       { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
-       /* mute for Mic1 PW */
-       { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
-       { } /* end */
-};
-
-/*
- * 6ch mode
- * need to set the codec line out and mic 1 pin widgets to outputs
- */
-static struct hda_verb alc880_threestack_ch6_init[] = {
-       /* set pin widget 1Ah (line in) for output */
-       { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
-       /* set pin widget 18h (mic1) for output */
-       { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
-       /* unmute the output for Line In PW */
-       { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000 },
-       /* unmute for Mic1 PW */
-       { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000 },
-       /* for rear channel output using Line In 1
-        * set select widget connection (nid = 0x12) - to summer node
-        * for rear NID = 0x0f...offset 3 in connection list
-        */
-       { 0x12, AC_VERB_SET_CONNECT_SEL, 0x3 },
-       /* for Mic1 - retask for center/lfe */
-       /* set select widget connection (nid = 0x10) - to summer node for
-        * front CLFE NID = 0x0e...offset 2 in connection list
-        */
-       { 0x10, AC_VERB_SET_CONNECT_SEL, 0x2 },
-       { } /* end */
-};
-
-static struct alc_channel_mode alc880_threestack_modes[2] = {
-       { 2, alc880_threestack_ch2_init },
-       { 6, alc880_threestack_ch6_init },
-};
-
-
-/*
- * channel source setting (6/8 channel selection for 5-stack)
- */
-
-/* set the path ways for 6 channel output
- * need to set the codec line out and mic 1 pin widgets to inputs
- */
-static struct hda_verb alc880_fivestack_ch6_init[] = {
-       /* set pin widget 1Ah (line in) for input */
-       { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
-       /* mute the output for Line In PW */
-       { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
-       { } /* end */
-};
-
-/* need to set the codec line out and mic 1 pin widgets to outputs */
-static struct hda_verb alc880_fivestack_ch8_init[] = {
-       /* set pin widget 1Ah (line in) for output */
-       { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
-       /* unmute the output for Line In PW */
-       { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000 },
-       /* output for surround channel output using Line In 1 */
-       /* set select widget connection (nid = 0x12) - to summer node
-        * for surr_rear NID = 0x0d...offset 1 in connection list
-        */
-       { 0x12, AC_VERB_SET_CONNECT_SEL, 0x1 },
-       { } /* end */
-};
-
-static struct alc_channel_mode alc880_fivestack_modes[2] = {
-       { 6, alc880_fivestack_ch6_init },
-       { 8, alc880_fivestack_ch8_init },
-};
-
-/*
- * channel source setting for W810 system
- *
- * W810 has rear IO for:
- * Front (DAC 02)
- * Surround (DAC 03)
- * Center/LFE (DAC 04)
- * Digital out (06)
- *
- * The system also has a pair of internal speakers, and a headphone jack.
- * These are both connected to Line2 on the codec, hence to DAC 02.
- * 
- * There is a variable resistor to control the speaker or headphone
- * volume. This is a hardware-only device without a software API.
- *
- * Plugging headphones in will disable the internal speakers. This is
- * implemented in hardware, not via the driver using jack sense. In
- * a similar fashion, plugging into the rear socket marked "front" will
- * disable both the speakers and headphones.
- *
- * For input, there's a microphone jack, and an "audio in" jack.
- * These may not do anything useful with this driver yet, because I
- * haven't setup any initialization verbs for these yet...
- */
-
-static struct alc_channel_mode alc880_w810_modes[1] = {
-       { 6, NULL }
-};
-
-/*
- */
 static int alc880_ch_mode_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct alc_spec *spec = codec->spec;
+       int items = kcontrol->private_value ? (int)kcontrol->private_value : 2;
 
        snd_assert(spec->channel_mode, return -ENXIO);
        uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
        uinfo->count = 1;
-       uinfo->value.enumerated.items = 2;
-       if (uinfo->value.enumerated.item >= 2)
-               uinfo->value.enumerated.item = 1;
+       uinfo->value.enumerated.items = items;
+       if (uinfo->value.enumerated.item >= items)
+               uinfo->value.enumerated.item = items - 1;
        sprintf(uinfo->value.enumerated.name, "%dch",
                spec->channel_mode[uinfo->value.enumerated.item].channels);
        return 0;
@@ -306,10 +181,16 @@ static int alc880_ch_mode_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *uc
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct alc_spec *spec = codec->spec;
+       int items = kcontrol->private_value ? (int)kcontrol->private_value : 2;
+       int i;
 
        snd_assert(spec->channel_mode, return -ENXIO);
-       ucontrol->value.enumerated.item[0] =
-               (spec->multiout.max_channels == spec->channel_mode[0].channels) ? 0 : 1;
+       for (i = 0; i < items; i++) {
+               if (spec->multiout.max_channels == spec->channel_mode[i].channels) {
+                       ucontrol->value.enumerated.item[0] = i;
+                       break;
+               }
+       }
        return 0;
 }
 
@@ -335,21 +216,149 @@ static int alc880_ch_mode_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *uc
 
 
 /*
+ * bound volume controls
+ *
+ * bind multiple volumes (# indices, from 0)
+ */
+
+#define AMP_VAL_IDX_SHIFT      19
+#define AMP_VAL_IDX_MASK       (0x0f<<19)
+
+static int alc_bind_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct alc_spec *spec = codec->spec;
+       unsigned long pval;
+
+       down(&spec->bind_mutex);
+       pval = kcontrol->private_value;
+       kcontrol->private_value = pval & ~AMP_VAL_IDX_MASK; /* index 0 */
+       snd_hda_mixer_amp_switch_info(kcontrol, uinfo);
+       kcontrol->private_value = pval;
+       up(&spec->bind_mutex);
+       return 0;
+}
+
+static int alc_bind_switch_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct alc_spec *spec = codec->spec;
+       unsigned long pval;
+
+       down(&spec->bind_mutex);
+       pval = kcontrol->private_value;
+       kcontrol->private_value = pval & ~AMP_VAL_IDX_MASK; /* index 0 */
+       snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);
+       kcontrol->private_value = pval;
+       up(&spec->bind_mutex);
+       return 0;
+}
+
+static int alc_bind_switch_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct alc_spec *spec = codec->spec;
+       unsigned long pval;
+       int i, indices, change = 0;
+
+       down(&spec->bind_mutex);
+       pval = kcontrol->private_value;
+       indices = (pval & AMP_VAL_IDX_MASK) >> AMP_VAL_IDX_SHIFT;
+       for (i = 0; i < indices; i++) {
+               kcontrol->private_value = (pval & ~AMP_VAL_IDX_MASK) | (i << AMP_VAL_IDX_SHIFT);
+               change |= snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
+       }
+       kcontrol->private_value = pval;
+       up(&spec->bind_mutex);
+       return change;
+}
+
+#define ALC_BIND_MUTE_MONO(xname, nid, channel, indices, direction) \
+       { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0,  \
+         .info = alc_bind_switch_info, \
+         .get = alc_bind_switch_get, \
+         .put = alc_bind_switch_put, \
+         .private_value = HDA_COMPOSE_AMP_VAL(nid, channel, indices, direction) }
+
+#define ALC_BIND_MUTE(xname,nid,indices,dir) ALC_BIND_MUTE_MONO(xname,nid,3,indices,dir)
+
+
+/*
+ * ALC880 3-stack model
+ *
+ * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0e)
+ * Pin assignment: Front = 0x14, Line-In/Surr = 0x1a, Mic/CLFE = 0x18, F-Mic = 0x1b
+ *                 HP = 0x19
  */
 
-/* 3-stack mode
- * Pin assignment: Front=0x14, Line-In/Rear=0x1a, Mic/CLFE=0x18, F-Mic=0x1b
- *                 HP=0x19
+static hda_nid_t alc880_dac_nids[4] = {
+       /* front, rear, clfe, rear_surr */
+       0x02, 0x05, 0x04, 0x03
+};
+
+static hda_nid_t alc880_adc_nids[3] = {
+       /* ADC0-2 */
+       0x07, 0x08, 0x09,
+};
+
+/* The datasheet says the node 0x07 is connected from inputs,
+ * but it shows zero connection in the real implementation on some devices.
  */
-static snd_kcontrol_new_t alc880_base_mixer[] = {
+static hda_nid_t alc880_adc_nids_alt[2] = {
+       /* ADC1-2 */
+       0x08, 0x09,
+};
+
+#define ALC880_DIGOUT_NID      0x06
+#define ALC880_DIGIN_NID       0x0a
+
+static struct hda_input_mux alc880_capture_source = {
+       .num_items = 4,
+       .items = {
+               { "Mic", 0x0 },
+               { "Front Mic", 0x3 },
+               { "Line", 0x2 },
+               { "CD", 0x4 },
+       },
+};
+
+/* channel source setting (2/6 channel selection for 3-stack) */
+/* 2ch mode */
+static struct hda_verb alc880_threestack_ch2_init[] = {
+       /* set line-in to input, mute it */
+       { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+       { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+       /* set mic-in to input vref 80%, mute it */
+       { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+       { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+       { } /* end */
+};
+
+/* 6ch mode */
+static struct hda_verb alc880_threestack_ch6_init[] = {
+       /* set line-in to output, unmute it */
+       { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+       { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+       /* set mic-in to output, unmute it */
+       { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+       { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+       { } /* end */
+};
+
+static struct alc_channel_mode alc880_threestack_modes[2] = {
+       { 2, alc880_threestack_ch2_init },
+       { 6, alc880_threestack_ch6_init },
+};
+
+static snd_kcontrol_new_t alc880_three_stack_mixer[] = {
        HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+       ALC_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
        HDA_CODEC_VOLUME("Surround Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Surround Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
+       ALC_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_CODEC_MUTE_MONO("Center Playback Switch", 0x18, 1, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x18, 2, 0x0, HDA_OUTPUT),
+       ALC_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+       ALC_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
        HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
        HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
        HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
@@ -360,25 +369,7 @@ static snd_kcontrol_new_t alc880_base_mixer[] = {
        HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x3, 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("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
        HDA_CODEC_MUTE("Headphone Playback Switch", 0x19, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
-       {
-               .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 = alc_mux_enum_info,
-               .get = alc_mux_enum_get,
-               .put = alc_mux_enum_put,
-       },
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Channel Mode",
@@ -389,37 +380,14 @@ static snd_kcontrol_new_t alc880_base_mixer[] = {
        { } /* end */
 };
 
-/* 5-stack mode
- * Pin assignment: Front=0x14, Rear=0x17, CLFE=0x16
- *                 Line-In/Side=0x1a, Mic=0x18, F-Mic=0x1b, HP=0x19
- */
-static snd_kcontrol_new_t alc880_five_stack_mixer[] = {
-       HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Surround Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Surround Playback Switch", 0x17, 0x0, HDA_OUTPUT),
-       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_CODEC_MUTE_MONO("Center Playback Switch", 0x16, 1, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Side Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Side Playback Switch", 0x1a, 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_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x3, HDA_INPUT),
-       HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x3, 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("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Headphone Playback Switch", 0x19, 0x0, HDA_OUTPUT),
+/* capture mixer elements */
+static snd_kcontrol_new_t alc880_capture_mixer[] = {
        HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
        HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
        HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
        HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT),
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                /* The multiple "Capture Source" controls confuse alsamixer
@@ -428,37 +396,20 @@ static snd_kcontrol_new_t alc880_five_stack_mixer[] = {
                 */
                /* .name = "Capture Source", */
                .name = "Input Source",
-               .count = 2,
+               .count = 3,
                .info = alc_mux_enum_info,
                .get = alc_mux_enum_get,
                .put = alc_mux_enum_put,
        },
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Channel Mode",
-               .info = alc880_ch_mode_info,
-               .get = alc880_ch_mode_get,
-               .put = alc880_ch_mode_put,
-       },
        { } /* end */
 };
 
-static snd_kcontrol_new_t alc880_w810_base_mixer[] = {
-       HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
-       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_CODEC_MUTE_MONO("Center Playback Switch", 0x16, 1, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x07, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x07, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x09, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x09, 0x0, HDA_INPUT),
+/* capture mixer elements (in case NID 0x07 not available) */
+static snd_kcontrol_new_t alc880_capture_alt_mixer[] = {
+       HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                /* The multiple "Capture Source" controls confuse alsamixer
@@ -467,7 +418,7 @@ static snd_kcontrol_new_t alc880_w810_base_mixer[] = {
                 */
                /* .name = "Capture Source", */
                .name = "Input Source",
-               .count = 3,
+               .count = 2,
                .info = alc_mux_enum_info,
                .get = alc_mux_enum_get,
                .put = alc_mux_enum_put,
@@ -475,7 +426,268 @@ static snd_kcontrol_new_t alc880_w810_base_mixer[] = {
        { } /* end */
 };
 
-/*
+
+
+/*
+ * ALC880 5-stack model
+ *
+ * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0d), Side = 0x02 (0xd)
+ * Pin assignment: Front = 0x14, Surr = 0x17, CLFE = 0x16
+ *                 Line-In/Side = 0x1a, Mic = 0x18, F-Mic = 0x1b, HP = 0x19
+ */
+
+/* additional mixers to alc880_three_stack_mixer */
+static snd_kcontrol_new_t alc880_five_stack_mixer[] = {
+       HDA_CODEC_VOLUME("Side Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+       ALC_BIND_MUTE("Side Playback Switch", 0x0d, 2, HDA_INPUT),
+       { } /* end */
+};
+
+/* channel source setting (6/8 channel selection for 5-stack) */
+/* 6ch mode */
+static struct hda_verb alc880_fivestack_ch6_init[] = {
+       /* set line-in to input, mute it */
+       { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+       { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+       { } /* end */
+};
+
+/* 8ch mode */
+static struct hda_verb alc880_fivestack_ch8_init[] = {
+       /* set line-in to output, unmute it */
+       { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+       { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+       { } /* end */
+};
+
+static struct alc_channel_mode alc880_fivestack_modes[2] = {
+       { 6, alc880_fivestack_ch6_init },
+       { 8, alc880_fivestack_ch8_init },
+};
+
+
+/*
+ * ALC880 6-stack model
+ *
+ * DAC: Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e), Side = 0x05 (0x0f)
+ * Pin assignment: Front = 0x14, Surr = 0x15, CLFE = 0x16, Side = 0x17,
+ *   Mic = 0x18, F-Mic = 0x19, Line = 0x1a, HP = 0x1b
+ */
+
+static hda_nid_t alc880_6st_dac_nids[4] = {
+       /* front, rear, clfe, rear_surr */
+       0x02, 0x03, 0x04, 0x05
+};     
+
+static struct hda_input_mux alc880_6stack_capture_source = {
+       .num_items = 4,
+       .items = {
+               { "Mic", 0x0 },
+               { "Front Mic", 0x1 },
+               { "Line", 0x2 },
+               { "CD", 0x4 },
+       },
+};
+
+/* fixed 8-channels */
+static struct alc_channel_mode alc880_sixstack_modes[1] = {
+       { 8, NULL },
+};
+
+static snd_kcontrol_new_t alc880_six_stack_mixer[] = {
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+       ALC_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+       ALC_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+       ALC_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+       ALC_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+       ALC_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+       HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+       HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+       HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+       HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+       HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
+       HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Channel Mode",
+               .info = alc880_ch_mode_info,
+               .get = alc880_ch_mode_get,
+               .put = alc880_ch_mode_put,
+       },
+       { } /* end */
+};
+
+
+/*
+ * ALC880 W810 model
+ *
+ * W810 has rear IO for:
+ * Front (DAC 02)
+ * Surround (DAC 03)
+ * Center/LFE (DAC 04)
+ * Digital out (06)
+ *
+ * The system also has a pair of internal speakers, and a headphone jack.
+ * These are both connected to Line2 on the codec, hence to DAC 02.
+ * 
+ * There is a variable resistor to control the speaker or headphone
+ * volume. This is a hardware-only device without a software API.
+ *
+ * Plugging headphones in will disable the internal speakers. This is
+ * implemented in hardware, not via the driver using jack sense. In
+ * a similar fashion, plugging into the rear socket marked "front" will
+ * disable both the speakers and headphones.
+ *
+ * For input, there's a microphone jack, and an "audio in" jack.
+ * These may not do anything useful with this driver yet, because I
+ * haven't setup any initialization verbs for these yet...
+ */
+
+static hda_nid_t alc880_w810_dac_nids[3] = {
+       /* front, rear/surround, clfe */
+       0x02, 0x03, 0x04
+};
+
+/* fixed 6 channels */
+static struct alc_channel_mode alc880_w810_modes[1] = {
+       { 6, NULL }
+};
+
+/* Pin assignment: Front = 0x14, Surr = 0x15, CLFE = 0x16, HP = 0x1b */
+static snd_kcontrol_new_t alc880_w810_base_mixer[] = {
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+       ALC_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+       ALC_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+       ALC_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+       ALC_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+       HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+       { } /* end */
+};
+
+
+/*
+ * Z710V model
+ *
+ * DAC: Front = 0x02 (0x0c), HP = 0x03 (0x0d)
+ * Pin assignment: Front = 0x14, HP = 0x15, Mic = 0x18, Mic2 = 0x19(?), Line = 0x1a
+ */
+
+static hda_nid_t alc880_z71v_dac_nids[1] = {
+       0x02
+};
+#define ALC880_Z71V_HP_DAC     0x03
+
+/* fixed 2 channels */
+static struct alc_channel_mode alc880_2_jack_modes[1] = {
+       { 2, NULL }
+};
+
+static snd_kcontrol_new_t alc880_z71v_mixer[] = {
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+       ALC_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+       ALC_BIND_MUTE("Headphone Playback Switch", 0x0d, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+       HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+       { } /* end */
+};
+
+
+/* FIXME! */
+/*
+ * ALC880 F1734 model
+ *
+ * DAC: HP = 0x02 (0x0c), Front = 0x03 (0x0d)
+ * Pin assignment: HP = 0x14, Front = 0x15, Mic = 0x18
+ */
+
+static hda_nid_t alc880_f1734_dac_nids[1] = {
+       0x03
+};
+#define ALC880_F1734_HP_DAC    0x02
+
+static snd_kcontrol_new_t alc880_f1734_mixer[] = {
+       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+       ALC_BIND_MUTE("Headphone Playback Switch", 0x0c, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME("Internal Speaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+       ALC_BIND_MUTE("Internal Speaker Playback Switch", 0x0d, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+       HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+       { } /* end */
+};
+
+
+/* FIXME! */
+/*
+ * ALC880 ASUS model
+ *
+ * DAC: HP/Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e)
+ * Pin assignment: HP/Front = 0x14, Surr = 0x15, CLFE = 0x16,
+ *  Mic = 0x18, Line = 0x1a
+ */
+
+#define alc880_asus_dac_nids   alc880_w810_dac_nids    /* identical with w810 */
+#define alc880_asus_modes      alc880_threestack_modes /* 2/6 channel mode */
+
+static snd_kcontrol_new_t alc880_asus_mixer[] = {
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+       ALC_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+       ALC_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
+       ALC_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+       ALC_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+       HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+       HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+       HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Channel Mode",
+               .info = alc880_ch_mode_info,
+               .get = alc880_ch_mode_get,
+               .put = alc880_ch_mode_put,
+       },
+       { } /* end */
+};
+
+/* FIXME! */
+/*
+ * ALC880 ASUS W1V model
+ *
+ * DAC: HP/Front = 0x02 (0x0c), Surr = 0x03 (0x0d), CLFE = 0x04 (0x0e)
+ * Pin assignment: HP/Front = 0x14, Surr = 0x15, CLFE = 0x16,
+ *  Mic = 0x18, Line = 0x1a, Line2 = 0x1b
+ */
+
+/* additional mixers to alc880_asus_mixer */
+static snd_kcontrol_new_t alc880_asus_w1v_mixer[] = {
+       HDA_CODEC_VOLUME("Line2 Playback Volume", 0x0b, 0x03, HDA_INPUT),
+       HDA_CODEC_MUTE("Line2 Playback Switch", 0x0b, 0x03, HDA_INPUT),
+       { } /* end */
+};
+
+
+/*
+ * build control elements
  */
 static int alc_build_controls(struct hda_codec *codec)
 {
@@ -502,227 +714,297 @@ static int alc_build_controls(struct hda_codec *codec)
        return 0;
 }
 
+
 /*
  * initialize the codec volumes, etc
  */
 
-static struct hda_verb alc880_init_verbs_three_stack[] = {
-       /* Line In pin widget for input */
-       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
-       /* CD pin widget for input */
-       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
-       /* Mic1 (rear panel) pin widget for input and vref at 80% */
-       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
-       /* Mic2 (front panel) pin widget for input and vref at 80% */
-       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
-       /* unmute amp left and right */
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
-       /* set connection select to line in (default select for this ADC) */
-       {0x07, AC_VERB_SET_CONNECT_SEL, 0x02},
-       /* unmute front mixer amp left (volume = 0) */
-       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
-       /* mute pin widget amp left and right (no gain on this amp) */
-       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
-       /* unmute rear mixer amp left and right (volume = 0) */
-       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
-       /* mute pin widget amp left and right (no gain on this amp) */
-       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
-       /* unmute rear mixer amp left and right (volume = 0) */
-       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
-       /* mute pin widget amp left and right (no gain on this amp) */
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
-
-       /* using rear surround as the path for headphone output */
-       /* unmute rear surround mixer amp left and right (volume = 0) */
-       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
-       /* PASD 3 stack boards use the Mic 2 as the headphone output */
-       /* need to program the selector associated with the Mic 2 pin widget to
-        * surround path (index 0x01) for headphone output */
-       {0x11, AC_VERB_SET_CONNECT_SEL, 0x01},
-       /* mute pin widget amp left and right (no gain on this amp) */
-       {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
-       /* need to retask the Mic 2 pin widget to output */
-       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
-
-       /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) for mixer widget(nid=0x0B)
-        * to support the input path of analog loopback
+/*
+ * generic initialization of ADC, input mixers and output mixers
+ */
+static struct hda_verb alc880_volume_init_verbs[] = {
+       /*
+        * Unmute ADC0-2 and set the default input to mic-in
+        */
+       {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+       /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
+        * mixer widget
         * Note: PASD motherboards uses the Line In 2 as the input for front panel
         * mic (mic 2)
         */
-       /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & Line In 2 = 0x03 */
-       /* unmute CD */
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
-       /* unmute Line In */
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
-       /* unmute Mic 1 */
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-       /* unmute Line In 2 (for PASD boards Mic 2) */
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
-
-       /* Unmute input amps for the line out paths to support the output path of
-        * analog loopback
-        * the mixers on the output path has 2 inputs, one from the DAC and one
-        * from the mixer
+       /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+
+       /*
+        * Set up output mixers (0x0c - 0x0f)
         */
-       /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
-       /* Unmute Front out path */
-       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
-       /* Unmute Surround (used as HP) out path */
-       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
-       /* Unmute C/LFE out path */
-       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))}, /* mute */
-       /* Unmute rear Surround out path */
-       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+       /* set vol=0 to output mixers */
+       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+       /* set up input amps for analog loopback */
+       /* Amp Indices: DAC = 0, mixer = 1 */
+       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
 
        { }
 };
 
-static struct hda_verb alc880_init_verbs_five_stack[] = {
-       /* Line In pin widget for input */
-       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
-       /* CD pin widget for input */
-       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
-       /* Mic1 (rear panel) pin widget for input and vref at 80% */
-       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
-       /* Mic2 (front panel) pin widget for input and vref at 80% */
-       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
-       /* unmute amp left and right */
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
-       /* set connection select to line in (default select for this ADC) */
-       {0x07, AC_VERB_SET_CONNECT_SEL, 0x02},
-       /* unmute front mixer amp left and right (volume = 0) */
-       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
-       /* mute pin widget amp left and right (no gain on this amp) */
-       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
-       /* five rear and clfe */
-       /* unmute rear mixer amp left and right (volume = 0)  */
-       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
-       /* mute pin widget amp left and right (no gain on this amp) */
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
-       /* unmute clfe mixer amp left and right (volume = 0) */
-       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
-       /* mute pin widget amp left and right (no gain on this amp) */
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
-
-       /* using rear surround as the path for headphone output */
-       /* unmute rear surround mixer amp left and right (volume = 0) */
-       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
-       /* PASD 3 stack boards use the Mic 2 as the headphone output */
-       /* need to program the selector associated with the Mic 2 pin widget to
-        * surround path (index 0x01) for headphone output
-        */
-       {0x11, AC_VERB_SET_CONNECT_SEL, 0x01},
-       /* mute pin widget amp left and right (no gain on this amp) */
-       {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
-       /* need to retask the Mic 2 pin widget to output */
-       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
-
-       /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) for mixer
-        * widget(nid=0x0B) to support the input path of analog loopback
+/*
+ * 3-stack pin configuration:
+ * front = 0x14, mic/clfe = 0x18, HP = 0x19, line/surr = 0x1a, f-mic = 0x1b
+ */
+static struct hda_verb alc880_pin_3stack_init_verbs[] = {
+       /*
+        * preset connection lists of input pins
+        * 0 = front, 1 = rear_surr, 2 = CLFE, 3 = surround
         */
-       /* Note: PASD motherboards uses the Line In 2 as the input for front panel mic (mic 2) */
-       /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & Line In 2 = 0x03*/
-       /* unmute CD */
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
-       /* unmute Line In */
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
-       /* unmute Mic 1 */
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-       /* unmute Line In 2 (for PASD boards Mic 2) */
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
-
-       /* Unmute input amps for the line out paths to support the output path of
-        * analog loopback
-        * the mixers on the output path has 2 inputs, one from the DAC and
-        * one from the mixer
+       {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */
+       {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
+       {0x12, AC_VERB_SET_CONNECT_SEL, 0x03}, /* line/surround */
+
+       /*
+        * Set pin mode and muting
         */
-       /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
-       /* Unmute Front out path */
-       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
-       /* Unmute Surround (used as HP) out path */
-       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
-       /* Unmute C/LFE out path */
-       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))}, /* mute */
-       /* Unmute rear Surround out path */
-       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+       /* set front pin widgets 0x14 for output */
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       /* Mic1 (rear panel) pin widget for input and vref at 80% */
+       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* Mic2 (as headphone out) for HP output */
+       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       /* Line In pin widget for input */
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* Line2 (as front mic) pin widget for input and vref at 80% */
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* CD pin widget for input */
+       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
 
        { }
 };
 
-static struct hda_verb alc880_w810_init_verbs[] = {
-       /* front channel selector/amp: input 0: DAC: unmuted, (no volume selection) */
-       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
-
-       /* front channel selector/amp: input 1: capture mix: muted, (no volume selection) */
-       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7180},
-
-       /* front channel selector/amp: output 0: unmuted, max volume */
-       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
-
-       /* front out pin: muted, (no volume selection)  */
-       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-
-       /* front out pin: NOT headphone enable, out enable, vref disabled */
-       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+/*
+ * 5-stack pin configuration:
+ * front = 0x14, surround = 0x17, clfe = 0x16, mic = 0x18, HP = 0x19,
+ * line-in/side = 0x1a, f-mic = 0x1b
+ */
+static struct hda_verb alc880_pin_5stack_init_verbs[] = {
+       /*
+        * preset connection lists of input pins
+        * 0 = front, 1 = rear_surr, 2 = CLFE, 3 = surround
+        */
+       {0x11, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
+       {0x12, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/side */
 
+       /*
+        * Set pin mode and muting
+        */
+       /* set pin widgets 0x14-0x17 for output */
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       /* unmute pins for output (no gain on this amp) */
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 
-       /* surround channel selector/amp: input 0: DAC: unmuted, (no volume selection) */
-       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+       /* Mic1 (rear panel) pin widget for input and vref at 80% */
+       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* Mic2 (as headphone out) for HP output */
+       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       /* Line In pin widget for input */
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* Line2 (as front mic) pin widget for input and vref at 80% */
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* CD pin widget for input */
+       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
 
-       /* surround channel selector/amp: input 1: capture mix: muted, (no volume selection) */
-       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7180},
+       { }
+};
 
-       /* surround channel selector/amp: output 0: unmuted, max volume */
-       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+/*
+ * W810 pin configuration:
+ * front = 0x14, surround = 0x15, clfe = 0x16, HP = 0x1b
+ */
+static struct hda_verb alc880_pin_w810_init_verbs[] = {
+       /* hphone/speaker input selector: front DAC */
+       {0x13, AC_VERB_SET_CONNECT_SEL, 0x0},
 
-       /* surround out pin: muted, (no volume selection)  */
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x15, 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},
 
-       /* surround out pin: NOT headphone enable, out enable, vref disabled */
-       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
 
+       { }
+};
 
-       /* c/lfe channel selector/amp: input 0: DAC: unmuted, (no volume selection) */
-       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+/*
+ * Z71V pin configuration:
+ * Speaker-out = 0x14, HP = 0x15, Mic = 0x18, Line-in = 0x1a, Mic2 = 0x1b (?)
+ */
+static struct hda_verb alc880_pin_z71v_init_verbs[] = {
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 
-       /* c/lfe channel selector/amp: input 1: capture mix: muted, (no volume selection) */
-       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0x7180},
+       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
 
-       /* c/lfe channel selector/amp: output 0: unmuted, max volume */
-       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
+       { }
+};
 
-       /* c/lfe out pin: muted, (no volume selection)  */
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+/*
+ * 6-stack pin configuration:
+ * front = 0x14, surr = 0x15, clfe = 0x16, side = 0x17, mic = 0x18, f-mic = 0x19,
+ * line = 0x1a, HP = 0x1b
+ */
+static struct hda_verb alc880_pin_6stack_init_verbs[] = {
+       {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
+
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x15, 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},
+       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       
+       { }
+};
 
-       /* c/lfe out pin: NOT headphone enable, out enable, vref disabled */
-       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+/* FIXME! */
+/*
+ * F1734 pin configuration:
+ * HP = 0x14, speaker-out = 0x15, mic = 0x18
+ */
+static struct hda_verb alc880_pin_f1734_init_verbs[] = {
+       {0x10, AC_VERB_SET_CONNECT_SEL, 0x02},
+       {0x11, AC_VERB_SET_CONNECT_SEL, 0x00},
+       {0x12, AC_VERB_SET_CONNECT_SEL, 0x01},
+       {0x13, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
 
+       { }
+};
 
-       /* hphone/speaker input selector: front DAC */
-       {0x13, AC_VERB_SET_CONNECT_SEL, 0x0},
+/* FIXME! */
+/*
+ * ASUS pin configuration:
+ * HP/front = 0x14, surr = 0x15, clfe = 0x16, mic = 0x18, line = 0x1a
+ */
+static struct hda_verb alc880_pin_asus_init_verbs[] = {
+       {0x10, AC_VERB_SET_CONNECT_SEL, 0x02},
+       {0x11, AC_VERB_SET_CONNECT_SEL, 0x00},
+       {0x12, AC_VERB_SET_CONNECT_SEL, 0x01},
+       {0x13, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       
+       { }
+};
 
-       /* hphone/speaker out pin: muted, (no volume selection)  */
-       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+/* Enable GPIO mask and set output */
+static struct hda_verb alc880_gpio1_init_verbs[] = {
+       {0x01, AC_VERB_SET_GPIO_MASK, 0x01},
+       {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01},
+       {0x01, AC_VERB_SET_GPIO_DATA, 0x01},
+};
 
-       /* hphone/speaker out pin: NOT headphone enable, out enable, vref disabled */
-       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+/* Enable GPIO mask and set output */
+static struct hda_verb alc880_gpio2_init_verbs[] = {
+       {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
+       {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
+       {0x01, AC_VERB_SET_GPIO_DATA, 0x02},
+};
 
 
-       { }
-};
+/*
+ */
 
 static int alc_init(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       snd_hda_sequence_write(codec, spec->init_verbs);
+       unsigned int i;
+
+       for (i = 0; i < spec->num_init_verbs; i++)
+               snd_hda_sequence_write(codec, spec->init_verbs[i]);
        return 0;
 }
 
@@ -736,9 +1018,8 @@ static int alc_resume(struct hda_codec *codec)
        int i;
 
        alc_init(codec);
-       for (i = 0; i < spec->num_mixers; i++) {
+       for (i = 0; i < spec->num_mixers; i++)
                snd_hda_resume_ctls(codec, spec->mixers[i]);
-       }
        if (spec->multiout.dig_out_nid)
                snd_hda_resume_spdif_out(codec);
        if (spec->dig_in_nid)
@@ -813,261 +1094,1036 @@ static int alc880_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
        return 0;
 }
 
-static int alc880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
-                                     struct hda_codec *codec,
-                                     snd_pcm_substream_t *substream)
+static int alc880_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                                     struct hda_codec *codec,
+                                     snd_pcm_substream_t *substream)
+{
+       struct alc_spec *spec = codec->spec;
+
+       snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0);
+       return 0;
+}
+
+
+/*
+ */
+static struct hda_pcm_stream alc880_pcm_analog_playback = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 8,
+       /* NID is set in alc_build_pcms */
+       .ops = {
+               .open = alc880_playback_pcm_open,
+               .prepare = alc880_playback_pcm_prepare,
+               .cleanup = alc880_playback_pcm_cleanup
+       },
+};
+
+static struct hda_pcm_stream alc880_pcm_analog_capture = {
+       .substreams = 2,
+       .channels_min = 2,
+       .channels_max = 2,
+       /* NID is set in alc_build_pcms */
+       .ops = {
+               .prepare = alc880_capture_pcm_prepare,
+               .cleanup = alc880_capture_pcm_cleanup
+       },
+};
+
+static struct hda_pcm_stream alc880_pcm_digital_playback = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       /* NID is set in alc_build_pcms */
+       .ops = {
+               .open = alc880_dig_playback_pcm_open,
+               .close = alc880_dig_playback_pcm_close
+       },
+};
+
+static struct hda_pcm_stream alc880_pcm_digital_capture = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       /* NID is set in alc_build_pcms */
+};
+
+static int alc_build_pcms(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       struct hda_pcm *info = spec->pcm_rec;
+       int i;
+
+       codec->num_pcms = 1;
+       codec->pcm_info = info;
+
+       info->name = spec->stream_name_analog;
+       info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback);
+       info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
+       info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
+       info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
+
+       info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 0;
+       for (i = 0; i < spec->num_channel_mode; i++) {
+               if (spec->channel_mode[i].channels > info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max) {
+                   info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->channel_mode[i].channels;
+               }
+       }
+
+       if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
+               codec->num_pcms++;
+               info++;
+               info->name = spec->stream_name_digital;
+               if (spec->multiout.dig_out_nid) {
+                       info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_digital_playback);
+                       info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
+               }
+               if (spec->dig_in_nid) {
+                       info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_digital_capture);
+                       info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
+               }
+       }
+
+       return 0;
+}
+
+static void alc_free(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       unsigned int i;
+
+       if (! spec)
+               return;
+
+       if (spec->kctl_alloc) {
+               for (i = 0; i < spec->num_kctl_used; i++)
+                       kfree(spec->kctl_alloc[i].name);
+               kfree(spec->kctl_alloc);
+       }
+       kfree(spec);
+}
+
+/*
+ */
+static struct hda_codec_ops alc_patch_ops = {
+       .build_controls = alc_build_controls,
+       .build_pcms = alc_build_pcms,
+       .init = alc_init,
+       .free = alc_free,
+#ifdef CONFIG_PM
+       .resume = alc_resume,
+#endif
+};
+
+
+/*
+ * Test configuration for debugging
+ *
+ * Almost all inputs/outputs are enabled.  I/O pins can be configured via
+ * enum controls.
+ */
+#ifdef CONFIG_SND_DEBUG
+static hda_nid_t alc880_test_dac_nids[4] = {
+       0x02, 0x03, 0x04, 0x05
+};
+
+static struct hda_input_mux alc880_test_capture_source = {
+       .num_items = 5,
+       .items = {
+               { "In-1", 0x0 },
+               { "In-2", 0x1 },
+               { "In-3", 0x2 },
+               { "In-4", 0x3 },
+               { "CD", 0x4 },
+       },
+};
+
+static struct alc_channel_mode alc880_test_modes[4] = {
+       { 2, NULL },
+       { 4, NULL },
+       { 6, NULL },
+       { 8, NULL },
+};
+
+static int alc_test_pin_ctl_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+       static char *texts[] = {
+               "N/A", "Line Out", "HP Out",
+               "In Hi-Z", "In 50%", "In Grd", "In 80%", "In 100%"
+       };
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 8;
+       if (uinfo->value.enumerated.item >= 8)
+               uinfo->value.enumerated.item = 7;
+       strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+       return 0;
+}
+
+static int alc_test_pin_ctl_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
+       unsigned int pin_ctl, item = 0;
+
+       pin_ctl = snd_hda_codec_read(codec, nid, 0,
+                                    AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+       if (pin_ctl & AC_PINCTL_OUT_EN) {
+               if (pin_ctl & AC_PINCTL_HP_EN)
+                       item = 2;
+               else
+                       item = 1;
+       } else if (pin_ctl & AC_PINCTL_IN_EN) {
+               switch (pin_ctl & AC_PINCTL_VREFEN) {
+               case AC_PINCTL_VREF_HIZ: item = 3; break;
+               case AC_PINCTL_VREF_50:  item = 4; break;
+               case AC_PINCTL_VREF_GRD: item = 5; break;
+               case AC_PINCTL_VREF_80:  item = 6; break;
+               case AC_PINCTL_VREF_100: item = 7; break;
+               }
+       }
+       ucontrol->value.enumerated.item[0] = item;
+       return 0;
+}
+
+static int alc_test_pin_ctl_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
+       static unsigned int ctls[] = {
+               0, AC_PINCTL_OUT_EN, AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN,
+               AC_PINCTL_IN_EN | AC_PINCTL_VREF_HIZ,
+               AC_PINCTL_IN_EN | AC_PINCTL_VREF_50,
+               AC_PINCTL_IN_EN | AC_PINCTL_VREF_GRD,
+               AC_PINCTL_IN_EN | AC_PINCTL_VREF_80,
+               AC_PINCTL_IN_EN | AC_PINCTL_VREF_100,
+       };
+       unsigned int old_ctl, new_ctl;
+
+       old_ctl = snd_hda_codec_read(codec, nid, 0,
+                                    AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+       new_ctl = ctls[ucontrol->value.enumerated.item[0]];
+       if (old_ctl != new_ctl) {
+               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, new_ctl);
+               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+                                   ucontrol->value.enumerated.item[0] >= 3 ? 0xb080 : 0xb000);
+               return 1;
+       }
+       return 0;
+}
+
+static int alc_test_pin_src_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+       static char *texts[] = {
+               "Front", "Surround", "CLFE", "Side"
+       };
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 4;
+       if (uinfo->value.enumerated.item >= 4)
+               uinfo->value.enumerated.item = 3;
+       strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+       return 0;
+}
+
+static int alc_test_pin_src_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
+       unsigned int sel;
+
+       sel = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0);
+       ucontrol->value.enumerated.item[0] = sel & 3;
+       return 0;
+}
+
+static int alc_test_pin_src_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       hda_nid_t nid = (hda_nid_t)kcontrol->private_value;
+       unsigned int sel;
+
+       sel = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONNECT_SEL, 0) & 3;
+       if (ucontrol->value.enumerated.item[0] != sel) {
+               sel = ucontrol->value.enumerated.item[0] & 3;
+               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, sel);
+               return 1;
+       }
+       return 0;
+}
+
+#define PIN_CTL_TEST(xname,nid) {                      \
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,    \
+                       .name = xname,                 \
+                       .info = alc_test_pin_ctl_info, \
+                       .get = alc_test_pin_ctl_get,   \
+                       .put = alc_test_pin_ctl_put,   \
+                       .private_value = nid           \
+                       }
+
+#define PIN_SRC_TEST(xname,nid) {                      \
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,    \
+                       .name = xname,                 \
+                       .info = alc_test_pin_src_info, \
+                       .get = alc_test_pin_src_get,   \
+                       .put = alc_test_pin_src_put,   \
+                       .private_value = nid           \
+                       }
+
+static snd_kcontrol_new_t alc880_test_mixer[] = {
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("CLFE Playback Volume", 0x0e, 0x0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+       ALC_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+       ALC_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
+       ALC_BIND_MUTE("CLFE Playback Volume", 0x0e, 2, HDA_INPUT),
+       ALC_BIND_MUTE("Side Playback Volume", 0x0f, 2, HDA_INPUT),
+       PIN_CTL_TEST("Front Pin Mode", 0x14),
+       PIN_CTL_TEST("Surround Pin Mode", 0x15),
+       PIN_CTL_TEST("CLFE Pin Mode", 0x16),
+       PIN_CTL_TEST("Side Pin Mode", 0x17),
+       PIN_CTL_TEST("In-1 Pin Mode", 0x18),
+       PIN_CTL_TEST("In-2 Pin Mode", 0x19),
+       PIN_CTL_TEST("In-3 Pin Mode", 0x1a),
+       PIN_CTL_TEST("In-4 Pin Mode", 0x1b),
+       PIN_SRC_TEST("In-1 Pin Source", 0x18),
+       PIN_SRC_TEST("In-2 Pin Source", 0x19),
+       PIN_SRC_TEST("In-3 Pin Source", 0x1a),
+       PIN_SRC_TEST("In-4 Pin Source", 0x1b),
+       HDA_CODEC_VOLUME("In-1 Playback Volume", 0x0b, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("In-1 Playback Switch", 0x0b, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("In-2 Playback Volume", 0x0b, 0x1, HDA_INPUT),
+       HDA_CODEC_MUTE("In-2 Playback Switch", 0x0b, 0x1, HDA_INPUT),
+       HDA_CODEC_VOLUME("In-3 Playback Volume", 0x0b, 0x2, HDA_INPUT),
+       HDA_CODEC_MUTE("In-3 Playback Switch", 0x0b, 0x2, HDA_INPUT),
+       HDA_CODEC_VOLUME("In-4 Playback Volume", 0x0b, 0x3, HDA_INPUT),
+       HDA_CODEC_MUTE("In-4 Playback Switch", 0x0b, 0x3, HDA_INPUT),
+       HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x4, HDA_INPUT),
+       HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x4, 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 = "Input Source",
+               .count = 2,
+               .info = alc_mux_enum_info,
+               .get = alc_mux_enum_get,
+               .put = alc_mux_enum_put,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Channel Mode",
+               .info = alc880_ch_mode_info,
+               .get = alc880_ch_mode_get,
+               .put = alc880_ch_mode_put,
+       },
+       { } /* end */
+};
+
+static struct hda_verb alc880_test_init_verbs[] = {
+       /* Unmute inputs of 0x0c - 0x0f */
+       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+       /* Vol output for 0x0c-0x0f */
+       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+       /* Set output pins 0x14-0x17 */
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       /* Unmute output pins 0x14-0x17 */
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       /* Set input pins 0x18-0x1c */
+       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       /* Mute input pins 0x18-0x1b */
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* ADC set up */
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
+       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
+       /* Analog input/passthru */
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+       { }
+};
+#endif
+
+/*
+ */
+
+static struct hda_board_config alc880_cfg_tbl[] = {
+       /* Back 3 jack, front 2 jack */
+       { .modelname = "3stack", .config = ALC880_3ST },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe200, .config = ALC880_3ST },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe201, .config = ALC880_3ST },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe202, .config = ALC880_3ST },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe203, .config = ALC880_3ST },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe204, .config = ALC880_3ST },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe205, .config = ALC880_3ST },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe206, .config = ALC880_3ST },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe207, .config = ALC880_3ST },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe208, .config = ALC880_3ST },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe209, .config = ALC880_3ST },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20a, .config = ALC880_3ST },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20b, .config = ALC880_3ST },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20c, .config = ALC880_3ST },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20d, .config = ALC880_3ST },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20e, .config = ALC880_3ST },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe20f, .config = ALC880_3ST },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe210, .config = ALC880_3ST },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe211, .config = ALC880_3ST },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe214, .config = ALC880_3ST },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe302, .config = ALC880_3ST },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe303, .config = ALC880_3ST },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe304, .config = ALC880_3ST },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe306, .config = ALC880_3ST },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe307, .config = ALC880_3ST },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe404, .config = ALC880_3ST },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xa101, .config = ALC880_3ST },
+       { .pci_subvendor = 0x107b, .pci_subdevice = 0x3031, .config = ALC880_3ST },
+       { .pci_subvendor = 0x107b, .pci_subdevice = 0x4036, .config = ALC880_3ST },
+       { .pci_subvendor = 0x107b, .pci_subdevice = 0x4037, .config = ALC880_3ST },
+       { .pci_subvendor = 0x107b, .pci_subdevice = 0x4038, .config = ALC880_3ST },
+       { .pci_subvendor = 0x107b, .pci_subdevice = 0x4040, .config = ALC880_3ST },
+       { .pci_subvendor = 0x107b, .pci_subdevice = 0x4041, .config = ALC880_3ST },
+
+       /* Back 3 jack, front 2 jack (Internal add Aux-In) */
+       { .pci_subvendor = 0x1025, .pci_subdevice = 0xe310, .config = ALC880_3ST },
+       { .pci_subvendor = 0x104d, .pci_subdevice = 0x81d6, .config = ALC880_3ST }, 
+
+       /* Back 3 jack plus 1 SPDIF out jack, front 2 jack */
+       { .modelname = "3stack-digout", .config = ALC880_3ST_DIG },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe308, .config = ALC880_3ST_DIG },
+
+       /* Back 3 jack plus 1 SPDIF out jack, front 2 jack (Internal add Aux-In)*/
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe305, .config = ALC880_3ST_DIG },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xd402, .config = ALC880_3ST_DIG },
+       { .pci_subvendor = 0x1025, .pci_subdevice = 0xe309, .config = ALC880_3ST_DIG },
+
+       /* Back 5 jack, front 2 jack */
+       { .modelname = "5stack", .config = ALC880_5ST },
+       { .pci_subvendor = 0x107b, .pci_subdevice = 0x3033, .config = ALC880_5ST },
+       { .pci_subvendor = 0x107b, .pci_subdevice = 0x4039, .config = ALC880_5ST },
+       { .pci_subvendor = 0x107b, .pci_subdevice = 0x3032, .config = ALC880_5ST },
+       { .pci_subvendor = 0x103c, .pci_subdevice = 0x2a09, .config = ALC880_5ST },
+       { .pci_subvendor = 0x1043, .pci_subdevice = 0x814e, .config = ALC880_5ST },
+
+       /* Back 5 jack plus 1 SPDIF out jack, front 2 jack */
+       { .modelname = "5stack-digout", .config = ALC880_5ST_DIG },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe224, .config = ALC880_5ST_DIG },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe400, .config = ALC880_5ST_DIG },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe401, .config = ALC880_5ST_DIG },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xe402, .config = ALC880_5ST_DIG },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xd400, .config = ALC880_5ST_DIG },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xd401, .config = ALC880_5ST_DIG },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0xa100, .config = ALC880_5ST_DIG },
+       { .pci_subvendor = 0x1565, .pci_subdevice = 0x8202, .config = ALC880_5ST_DIG },
+       { .pci_subvendor = 0x1019, .pci_subdevice = 0xa880, .config = ALC880_5ST_DIG },
+       { .pci_subvendor = 0x1019, .pci_subdevice = 0xa884, .config = ALC880_5ST_DIG },
+       { .pci_subvendor = 0x1695, .pci_subdevice = 0x400d, .config = ALC880_5ST_DIG },
+       { .pci_subvendor = 0x0000, .pci_subdevice = 0x8086, .config = ALC880_5ST_DIG },
+
+       { .modelname = "w810", .config = ALC880_W810 },
+       { .pci_subvendor = 0x161f, .pci_subdevice = 0x203d, .config = ALC880_W810 },
+
+       { .modelname = "z71v", .config = ALC880_Z71V },
+       { .pci_subvendor = 0x1043, .pci_subdevice = 0x1964, .config = ALC880_Z71V },
+
+       { .modelname = "6statack-digout", .config = ALC880_6ST_DIG },
+       { .pci_subvendor = 0x2668, .pci_subdevice = 0x8086, .config = ALC880_6ST_DIG },
+       { .pci_subvendor = 0x8086, .pci_subdevice = 0x2668, .config = ALC880_6ST_DIG },
+       { .pci_subvendor = 0x1462, .pci_subdevice = 0x1150, .config = ALC880_6ST_DIG },
+       { .pci_subvendor = 0xe803, .pci_subdevice = 0x1019, .config = ALC880_6ST_DIG },
+
+       { .modelname = "asus", .config = ALC880_ASUS },
+       { .pci_subvendor = 0x1043, .pci_subdevice = 0x1964, .config = ALC880_ASUS_DIG },
+       { .pci_subvendor = 0x1043, .pci_subdevice = 0x1973, .config = ALC880_ASUS_DIG },
+       { .pci_subvendor = 0x1043, .pci_subdevice = 0x19b3, .config = ALC880_ASUS_DIG },
+       { .pci_subvendor = 0x1043, .pci_subdevice = 0x1113, .config = ALC880_ASUS_DIG },
+       { .pci_subvendor = 0x1043, .pci_subdevice = 0x1993, .config = ALC880_ASUS },
+       { .pci_subvendor = 0x1043, .pci_subdevice = 0x10c3, .config = ALC880_ASUS_DIG },
+       { .pci_subvendor = 0x1043, .pci_subdevice = 0x1133, .config = ALC880_ASUS },
+       { .pci_subvendor = 0x1043, .pci_subdevice = 0x1123, .config = ALC880_ASUS_DIG },
+       { .pci_subvendor = 0x1043, .pci_subdevice = 0x1143, .config = ALC880_ASUS },
+       { .pci_subvendor = 0x1043, .pci_subdevice = 0x10b3, .config = ALC880_ASUS_W1V },
+
+       { .modelname = "uniwill", .config = ALC880_UNIWILL_DIG },
+       { .pci_subvendor = 0x1584, .pci_subdevice = 0x9050, .config = ALC880_UNIWILL_DIG },     
+
+       { .modelname = "F1734", .config = ALC880_F1734 },
+       { .pci_subvendor = 0x1734, .pci_subdevice = 0x107c, .config = ALC880_F1734 },
+
+#ifdef CONFIG_SND_DEBUG
+       { .modelname = "test", .config = ALC880_TEST },
+#endif
+
+       {}
+};
+
+/*
+ * configuration template - to be copied to the spec instance
+ */
+struct alc_config_preset {
+       snd_kcontrol_new_t *mixers[4];
+       const struct hda_verb *init_verbs[4];
+       unsigned int num_dacs;
+       hda_nid_t *dac_nids;
+       hda_nid_t dig_out_nid;          /* optional */
+       hda_nid_t hp_nid;               /* optional */
+       unsigned int num_adc_nids;
+       hda_nid_t *adc_nids;
+       unsigned int num_channel_mode;
+       const struct alc_channel_mode *channel_mode;
+       const struct hda_input_mux *input_mux;
+};
+
+static struct alc_config_preset alc880_presets[] = {
+       [ALC880_3ST] = {
+               .mixers = { alc880_three_stack_mixer },
+               .init_verbs = { alc880_volume_init_verbs, alc880_pin_3stack_init_verbs },
+               .num_dacs = ARRAY_SIZE(alc880_dac_nids),
+               .dac_nids = alc880_dac_nids,
+               .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
+               .channel_mode = alc880_threestack_modes,
+               .input_mux = &alc880_capture_source,
+       },
+       [ALC880_3ST_DIG] = {
+               .mixers = { alc880_three_stack_mixer },
+               .init_verbs = { alc880_volume_init_verbs, alc880_pin_3stack_init_verbs },
+               .num_dacs = ARRAY_SIZE(alc880_dac_nids),
+               .dac_nids = alc880_dac_nids,
+               .dig_out_nid = ALC880_DIGOUT_NID,
+               .num_channel_mode = ARRAY_SIZE(alc880_threestack_modes),
+               .channel_mode = alc880_threestack_modes,
+               .input_mux = &alc880_capture_source,
+       },
+       [ALC880_5ST] = {
+               .mixers = { alc880_three_stack_mixer, alc880_five_stack_mixer},
+               .init_verbs = { alc880_volume_init_verbs, alc880_pin_5stack_init_verbs },
+               .num_dacs = ARRAY_SIZE(alc880_dac_nids),
+               .dac_nids = alc880_dac_nids,
+               .num_channel_mode = ARRAY_SIZE(alc880_fivestack_modes),
+               .channel_mode = alc880_fivestack_modes,
+               .input_mux = &alc880_capture_source,
+       },
+       [ALC880_5ST_DIG] = {
+               .mixers = { alc880_three_stack_mixer, alc880_five_stack_mixer },
+               .init_verbs = { alc880_volume_init_verbs, alc880_pin_5stack_init_verbs },
+               .num_dacs = ARRAY_SIZE(alc880_dac_nids),
+               .dac_nids = alc880_dac_nids,
+               .dig_out_nid = ALC880_DIGOUT_NID,
+               .num_channel_mode = ARRAY_SIZE(alc880_fivestack_modes),
+               .channel_mode = alc880_fivestack_modes,
+               .input_mux = &alc880_capture_source,
+       },
+       [ALC880_6ST_DIG] = {
+               .mixers = { alc880_six_stack_mixer },
+               .init_verbs = { alc880_volume_init_verbs, alc880_pin_6stack_init_verbs },
+               .num_dacs = ARRAY_SIZE(alc880_6st_dac_nids),
+               .dac_nids = alc880_6st_dac_nids,
+               .dig_out_nid = ALC880_DIGOUT_NID,
+               .num_channel_mode = ARRAY_SIZE(alc880_sixstack_modes),
+               .channel_mode = alc880_sixstack_modes,
+               .input_mux = &alc880_6stack_capture_source,
+       },
+       [ALC880_W810] = {
+               .mixers = { alc880_w810_base_mixer },
+               .init_verbs = { alc880_volume_init_verbs, alc880_pin_w810_init_verbs },
+               .num_dacs = ARRAY_SIZE(alc880_w810_dac_nids),
+               .dac_nids = alc880_w810_dac_nids,
+               .dig_out_nid = ALC880_DIGOUT_NID,
+               .num_channel_mode = ARRAY_SIZE(alc880_w810_modes),
+               .channel_mode = alc880_w810_modes,
+               .input_mux = &alc880_capture_source,
+       },
+       [ALC880_Z71V] = {
+               .mixers = { alc880_z71v_mixer },
+               .init_verbs = { alc880_volume_init_verbs, alc880_pin_z71v_init_verbs,
+                               alc880_gpio2_init_verbs },
+               .num_dacs = ARRAY_SIZE(alc880_z71v_dac_nids),
+               .dac_nids = alc880_z71v_dac_nids,
+               .dig_out_nid = ALC880_DIGOUT_NID,
+               .hp_nid = 0x03,
+               .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
+               .channel_mode = alc880_2_jack_modes,
+               .input_mux = &alc880_capture_source,
+       },
+       [ALC880_F1734] = {
+               .mixers = { alc880_f1734_mixer },
+               .init_verbs = { alc880_volume_init_verbs, alc880_pin_f1734_init_verbs },
+               .num_dacs = ARRAY_SIZE(alc880_f1734_dac_nids),
+               .dac_nids = alc880_f1734_dac_nids,
+               .hp_nid = 0x02,
+               .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
+               .channel_mode = alc880_2_jack_modes,
+               .input_mux = &alc880_capture_source,
+       },
+       [ALC880_ASUS] = {
+               .mixers = { alc880_asus_mixer },
+               .init_verbs = { alc880_volume_init_verbs, alc880_pin_asus_init_verbs,
+                               alc880_gpio1_init_verbs },
+               .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
+               .dac_nids = alc880_asus_dac_nids,
+               .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
+               .channel_mode = alc880_asus_modes,
+               .input_mux = &alc880_capture_source,
+       },
+       [ALC880_ASUS_DIG] = {
+               .mixers = { alc880_asus_mixer },
+               .init_verbs = { alc880_volume_init_verbs, alc880_pin_asus_init_verbs,
+                               alc880_gpio1_init_verbs },
+               .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
+               .dac_nids = alc880_asus_dac_nids,
+               .dig_out_nid = ALC880_DIGOUT_NID,
+               .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
+               .channel_mode = alc880_asus_modes,
+               .input_mux = &alc880_capture_source,
+       },
+       [ALC880_ASUS_W1V] = {
+               .mixers = { alc880_asus_mixer, alc880_asus_w1v_mixer },
+               .init_verbs = { alc880_volume_init_verbs, alc880_pin_asus_init_verbs,
+                               alc880_gpio1_init_verbs },
+               .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
+               .dac_nids = alc880_asus_dac_nids,
+               .dig_out_nid = ALC880_DIGOUT_NID,
+               .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
+               .channel_mode = alc880_asus_modes,
+               .input_mux = &alc880_capture_source,
+       },
+       [ALC880_UNIWILL_DIG] = {
+               .mixers = { alc880_asus_mixer },
+               .init_verbs = { alc880_volume_init_verbs, alc880_pin_asus_init_verbs },
+               .num_dacs = ARRAY_SIZE(alc880_asus_dac_nids),
+               .dac_nids = alc880_asus_dac_nids,
+               .dig_out_nid = ALC880_DIGOUT_NID,
+               .num_channel_mode = ARRAY_SIZE(alc880_asus_modes),
+               .channel_mode = alc880_asus_modes,
+               .input_mux = &alc880_capture_source,
+       },
+#ifdef CONFIG_SND_DEBUG
+       [ALC880_TEST] = {
+               .mixers = { alc880_test_mixer },
+               .init_verbs = { alc880_test_init_verbs },
+               .num_dacs = ARRAY_SIZE(alc880_test_dac_nids),
+               .dac_nids = alc880_test_dac_nids,
+               .dig_out_nid = ALC880_DIGOUT_NID,
+               .num_channel_mode = ARRAY_SIZE(alc880_test_modes),
+               .channel_mode = alc880_test_modes,
+               .input_mux = &alc880_test_capture_source,
+       },
+#endif
+};
+
+/*
+ * Automatic parse of I/O pins from the BIOS configuration
+ */
+
+#define NUM_CONTROL_ALLOC      32
+#define NUM_VERB_ALLOC         32
+
+enum {
+       ALC_CTL_WIDGET_VOL,
+       ALC_CTL_WIDGET_MUTE,
+       ALC_CTL_BIND_MUTE,
+};
+static snd_kcontrol_new_t alc880_control_templates[] = {
+       HDA_CODEC_VOLUME(NULL, 0, 0, 0),
+       HDA_CODEC_MUTE(NULL, 0, 0, 0),
+       ALC_BIND_MUTE(NULL, 0, 0, 0),
+};
+
+/* add dynamic controls */
+static int add_control(struct alc_spec *spec, int type, const char *name, unsigned long val)
+{
+       snd_kcontrol_new_t *knew;
+
+       if (spec->num_kctl_used >= spec->num_kctl_alloc) {
+               int num = spec->num_kctl_alloc + NUM_CONTROL_ALLOC;
+
+               knew = kcalloc(num + 1, sizeof(*knew), GFP_KERNEL); /* array + terminator */
+               if (! knew)
+                       return -ENOMEM;
+               if (spec->kctl_alloc) {
+                       memcpy(knew, spec->kctl_alloc, sizeof(*knew) * spec->num_kctl_alloc);
+                       kfree(spec->kctl_alloc);
+               }
+               spec->kctl_alloc = knew;
+               spec->num_kctl_alloc = num;
+       }
+
+       knew = &spec->kctl_alloc[spec->num_kctl_used];
+       *knew = alc880_control_templates[type];
+       knew->name = snd_kmalloc_strdup(name, GFP_KERNEL);
+       if (! knew->name)
+               return -ENOMEM;
+       knew->private_value = val;
+       spec->num_kctl_used++;
+       return 0;
+}
+
+#define alc880_is_fixed_pin(nid)       ((nid) >= 0x14 && (nid) <= 0x17)
+#define alc880_fixed_pin_idx(nid)      ((nid) - 0x14)
+#define alc880_is_multi_pin(nid)       ((nid) >= 0x18)
+#define alc880_multi_pin_idx(nid)      ((nid) - 0x18)
+#define alc880_is_input_pin(nid)       ((nid) >= 0x18)
+#define alc880_input_pin_idx(nid)      ((nid) - 0x18)
+#define alc880_idx_to_dac(nid)         ((nid) + 0x02)
+#define alc880_dac_to_idx(nid)         ((nid) - 0x02)
+#define alc880_idx_to_mixer(nid)       ((nid) + 0x0c)
+#define alc880_idx_to_selector(nid)    ((nid) + 0x10)
+#define ALC880_PIN_CD_NID              0x1c
+
+/* fill in the dac_nids table from the parsed pin configuration */
+static int alc880_auto_fill_dac_nids(struct alc_spec *spec, const struct auto_pin_cfg *cfg)
+{
+       hda_nid_t nid;
+       int assigned[4];
+       int i, j;
+
+       memset(assigned, 0, sizeof(assigned));
+
+       /* check the pins hardwired to audio widget */
+       for (i = 0; i < cfg->line_outs; i++) {
+               nid = cfg->line_out_pins[i];
+               if (alc880_is_fixed_pin(nid)) {
+                       int idx = alc880_fixed_pin_idx(nid);
+                       spec->multiout.dac_nids[i] = alc880_dac_to_idx(idx);
+                       assigned[idx] = 1;
+               }
+       }
+       /* left pins can be connect to any audio widget */
+       for (i = 0; i < cfg->line_outs; i++) {
+               nid = cfg->line_out_pins[i];
+               if (alc880_is_fixed_pin(nid))
+                       continue;
+               /* search for an empty channel */
+               for (j = 0; j < cfg->line_outs; j++) {
+                       if (! assigned[j]) {
+                               spec->multiout.dac_nids[i] = alc880_idx_to_dac(j);
+                               assigned[j] = 1;
+                               break;
+                       }
+               }
+       }
+       spec->multiout.num_dacs = cfg->line_outs;
+       return 0;
+}
+
+/* add playback controls from the parsed DAC table */
+static int alc880_auto_create_multi_out_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg)
 {
-       struct alc_spec *spec = codec->spec;
+       char name[32];
+       static const char *chname[4] = { "Front", "Surround", NULL /*CLFE*/, "Side" };
+       hda_nid_t nid;
+       int i, err;
+
+       for (i = 0; i < cfg->line_outs; i++) {
+               if (! spec->multiout.dac_nids[i])
+                       continue;
+               nid = alc880_idx_to_mixer(alc880_dac_to_idx(spec->multiout.dac_nids[i]));
+               if (i == 2) {
+                       /* Center/LFE */
+                       if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "Center Playback Volume",
+                                              HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT))) < 0)
+                               return err;
+                       if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "LFE Playback Volume",
+                                              HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT))) < 0)
+                               return err;
+                       if ((err = add_control(spec, ALC_CTL_BIND_MUTE, "Center Playback Switch",
+                                              HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT))) < 0)
+                               return err;
+                       if ((err = add_control(spec, ALC_CTL_BIND_MUTE, "LFE Playback Switch",
+                                              HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT))) < 0)
+                               return err;
+               } else {
+                       sprintf(name, "%s Playback Volume", chname[i]);
+                       if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+                                              HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
+                               return err;
+                       sprintf(name, "%s Playback Switch", chname[i]);
+                       if ((err = add_control(spec, ALC_CTL_BIND_MUTE, name,
+                                              HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
+                               return err;
+               }
+       }
 
-       snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0);
        return 0;
 }
 
+/* add playback controls for HP output */
+static int alc880_auto_create_hp_ctls(struct alc_spec *spec, hda_nid_t pin)
+{
+       hda_nid_t nid;
+       int err;
 
-/*
- */
-static struct hda_pcm_stream alc880_pcm_analog_playback = {
-       .substreams = 1,
-       .channels_min = 2,
-       .channels_max = 8,
-       .nid = 0x02, /* NID to query formats and rates */
-       .ops = {
-               .open = alc880_playback_pcm_open,
-               .prepare = alc880_playback_pcm_prepare,
-               .cleanup = alc880_playback_pcm_cleanup
-       },
-};
+       if (! pin)
+               return 0;
 
-static struct hda_pcm_stream alc880_pcm_analog_capture = {
-       .substreams = 2,
-       .channels_min = 2,
-       .channels_max = 2,
-       .nid = 0x07, /* NID to query formats and rates */
-       .ops = {
-               .prepare = alc880_capture_pcm_prepare,
-               .cleanup = alc880_capture_pcm_cleanup
-       },
-};
+       if (alc880_is_fixed_pin(pin)) {
+               nid = alc880_idx_to_dac(alc880_fixed_pin_idx(pin));
+               if (! spec->multiout.dac_nids[0]) {
+                       /* use this as the primary output */
+                       spec->multiout.dac_nids[0] = nid;
+                       if (! spec->multiout.num_dacs)
+                               spec->multiout.num_dacs = 1;
+               } else 
+                       /* specify the DAC as the extra HP output */
+                       spec->multiout.hp_nid = nid;
+               /* control HP volume/switch on the output mixer amp */
+               nid = alc880_idx_to_mixer(alc880_fixed_pin_idx(pin));
+               if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, "Headphone Playback Volume",
+                                      HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT))) < 0)
+                       return err;
+               if ((err = add_control(spec, ALC_CTL_BIND_MUTE, "Headphone Playback Switch",
+                                      HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT))) < 0)
+                       return err;
+       } else if (alc880_is_multi_pin(pin)) {
+               /* set manual connection */
+               if (! spec->multiout.dac_nids[0]) {
+                       /* use this as the primary output */
+                       spec->multiout.dac_nids[0] = alc880_idx_to_dac(alc880_multi_pin_idx(pin));
+                       if (! spec->multiout.num_dacs)
+                               spec->multiout.num_dacs = 1;
+               }
+               /* we have only a switch on HP-out PIN */
+               if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, "Headphone Playback Switch",
+                                      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT))) < 0)
+                       return err;
+       }
+       return 0;
+}
 
-static struct hda_pcm_stream alc880_pcm_digital_playback = {
-       .substreams = 1,
-       .channels_min = 2,
-       .channels_max = 2,
-       /* NID is set in alc_build_pcms */
-       .ops = {
-               .open = alc880_dig_playback_pcm_open,
-               .close = alc880_dig_playback_pcm_close
-       },
-};
+/* create input playback/capture controls for the given pin */
+static int new_analog_input(struct alc_spec *spec, hda_nid_t pin, const char *ctlname)
+{
+       char name[32];
+       int err, idx;
+
+       sprintf(name, "%s Playback Volume", ctlname);
+       idx = alc880_input_pin_idx(pin);
+       if ((err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
+                              HDA_COMPOSE_AMP_VAL(0x0b, 3, idx, HDA_INPUT))) < 0)
+               return err;
+       sprintf(name, "%s Playback Switch", ctlname);
+       if ((err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
+                              HDA_COMPOSE_AMP_VAL(0x0b, 3, idx, HDA_INPUT))) < 0)
+               return err;
+       return 0;
+}
 
-static struct hda_pcm_stream alc880_pcm_digital_capture = {
-       .substreams = 1,
-       .channels_min = 2,
-       .channels_max = 2,
-       /* NID is set in alc_build_pcms */
-};
+/* create playback/capture controls for input pins */
+static int alc880_auto_create_analog_input_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg)
+{
+       static char *labels[AUTO_PIN_LAST] = {
+               "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux"
+       };
+       struct hda_input_mux *imux = &spec->private_imux;
+       int i, err;
+
+       for (i = 0; i < AUTO_PIN_LAST; i++) {
+               if (alc880_is_input_pin(cfg->input_pins[i])) {
+                       err = new_analog_input(spec, cfg->input_pins[i], labels[i]);
+                       if (err < 0)
+                               return err;
+                       imux->items[imux->num_items].label = labels[i];
+                       imux->items[imux->num_items].index = alc880_input_pin_idx(cfg->input_pins[i]);
+                       imux->num_items++;
+               }
+       }
+       return 0;
+}
 
-static int alc_build_pcms(struct hda_codec *codec)
+static void alc880_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t nid, int pin_type,
+                                             int dac_idx)
+{
+       /* set as output */
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+       /* need the manual connection? */
+       if (alc880_is_multi_pin(nid)) {
+               struct alc_spec *spec = codec->spec;
+               int idx = alc880_multi_pin_idx(nid);
+               snd_hda_codec_write(codec, alc880_idx_to_selector(idx), 0,
+                                   AC_VERB_SET_CONNECT_SEL,
+                                   alc880_dac_to_idx(spec->multiout.dac_nids[dac_idx]));
+       }
+}
+
+static void alc880_auto_init_multi_out(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       struct hda_pcm *info = spec->pcm_rec;
        int i;
 
-       codec->num_pcms = 1;
-       codec->pcm_info = info;
-
-       info->name = spec->stream_name_analog;
-       info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback);
-       info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
-
-       info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 0;
-       for (i = 0; i < spec->num_channel_mode; i++) {
-               if (spec->channel_mode[i].channels > info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max) {
-                   info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->channel_mode[i].channels;
-               }
+       for (i = 0; i < spec->autocfg.line_outs; i++) {
+               hda_nid_t nid = spec->autocfg.line_out_pins[i];
+               alc880_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
        }
+}
 
-       if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
-               codec->num_pcms++;
-               info++;
-               info->name = spec->stream_name_digital;
-               if (spec->multiout.dig_out_nid) {
-                       info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_digital_playback);
-                       info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
-               }
-               if (spec->dig_in_nid) {
-                       info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_digital_capture);
-                       info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
-               }
-       }
+static void alc880_auto_init_hp_out(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       hda_nid_t pin;
 
-       return 0;
+       pin = spec->autocfg.hp_pin;
+       if (pin) /* connect to front */
+               alc880_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
 }
 
-static void alc_free(struct hda_codec *codec)
+static void alc880_auto_init_analog_input(struct hda_codec *codec)
 {
-       kfree(codec->spec);
+       struct alc_spec *spec = codec->spec;
+       int i;
+
+       for (i = 0; i < AUTO_PIN_LAST; i++) {
+               hda_nid_t nid = spec->autocfg.input_pins[i];
+               if (alc880_is_input_pin(nid)) {
+                       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                           i <= AUTO_PIN_FRONT_MIC ? PIN_VREF80 : PIN_IN);
+                       if (nid != ALC880_PIN_CD_NID)
+                               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+                                                   AMP_OUT_MUTE);
+               }
+       }
 }
 
-/*
- */
-static struct hda_codec_ops alc_patch_ops = {
-       .build_controls = alc_build_controls,
-       .build_pcms = alc_build_pcms,
-       .init = alc_init,
-       .free = alc_free,
-#ifdef CONFIG_PM
-       .resume = alc_resume,
-#endif
-};
+/* parse the BIOS configuration and set up the alc_spec */
+/* return 1 if successful, 0 if the proper config is not found, or a negative error code */
+static int alc880_parse_auto_config(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       int err;
 
-/*
- */
+       if ((err = snd_hda_parse_pin_def_config(codec, &spec->autocfg)) < 0)
+               return err;
+       if ((err = alc880_auto_fill_dac_nids(spec, &spec->autocfg)) < 0)
+               return err;
+       if (! spec->autocfg.line_outs && ! spec->autocfg.hp_pin)
+               return 0; /* can't find valid BIOS pin config */
+       if ((err = alc880_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 ||
+           (err = alc880_auto_create_hp_ctls(spec, spec->autocfg.hp_pin)) < 0 ||
+           (err = alc880_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0)
+               return err;
 
-static struct hda_board_config alc880_cfg_tbl[] = {
-       /* Back 3 jack, front 2 jack */
-       { .modelname = "3stack", .config = ALC880_3ST },
-       { .pci_vendor = 0x8086, .pci_device = 0xe200, .config = ALC880_3ST },
-       { .pci_vendor = 0x8086, .pci_device = 0xe201, .config = ALC880_3ST },
-       { .pci_vendor = 0x8086, .pci_device = 0xe202, .config = ALC880_3ST },
-       { .pci_vendor = 0x8086, .pci_device = 0xe203, .config = ALC880_3ST },
-       { .pci_vendor = 0x8086, .pci_device = 0xe204, .config = ALC880_3ST },
-       { .pci_vendor = 0x8086, .pci_device = 0xe205, .config = ALC880_3ST },
-       { .pci_vendor = 0x8086, .pci_device = 0xe206, .config = ALC880_3ST },
-       { .pci_vendor = 0x8086, .pci_device = 0xe207, .config = ALC880_3ST },
-       { .pci_vendor = 0x8086, .pci_device = 0xe208, .config = ALC880_3ST },
-       { .pci_vendor = 0x8086, .pci_device = 0xe209, .config = ALC880_3ST },
-       { .pci_vendor = 0x8086, .pci_device = 0xe20a, .config = ALC880_3ST },
-       { .pci_vendor = 0x8086, .pci_device = 0xe20b, .config = ALC880_3ST },
-       { .pci_vendor = 0x8086, .pci_device = 0xe20c, .config = ALC880_3ST },
-       { .pci_vendor = 0x8086, .pci_device = 0xe20d, .config = ALC880_3ST },
-       { .pci_vendor = 0x8086, .pci_device = 0xe20e, .config = ALC880_3ST },
-       { .pci_vendor = 0x8086, .pci_device = 0xe20f, .config = ALC880_3ST },
-       { .pci_vendor = 0x8086, .pci_device = 0xe210, .config = ALC880_3ST },
-       { .pci_vendor = 0x8086, .pci_device = 0xe211, .config = ALC880_3ST },
-       { .pci_vendor = 0x8086, .pci_device = 0xe214, .config = ALC880_3ST },
-       { .pci_vendor = 0x8086, .pci_device = 0xe302, .config = ALC880_3ST },
-       { .pci_vendor = 0x8086, .pci_device = 0xe303, .config = ALC880_3ST },
-       { .pci_vendor = 0x8086, .pci_device = 0xe304, .config = ALC880_3ST },
-       { .pci_vendor = 0x8086, .pci_device = 0xe306, .config = ALC880_3ST },
-       { .pci_vendor = 0x8086, .pci_device = 0xe307, .config = ALC880_3ST },
-       { .pci_vendor = 0x8086, .pci_device = 0xe404, .config = ALC880_3ST },
-       { .pci_vendor = 0x8086, .pci_device = 0xa101, .config = ALC880_3ST },
-       { .pci_vendor = 0x107b, .pci_device = 0x3031, .config = ALC880_3ST },
-       { .pci_vendor = 0x107b, .pci_device = 0x4036, .config = ALC880_3ST },
-       { .pci_vendor = 0x107b, .pci_device = 0x4037, .config = ALC880_3ST },
-       { .pci_vendor = 0x107b, .pci_device = 0x4038, .config = ALC880_3ST },
-       { .pci_vendor = 0x107b, .pci_device = 0x4040, .config = ALC880_3ST },
-       { .pci_vendor = 0x107b, .pci_device = 0x4041, .config = ALC880_3ST },
+       spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 
-       /* Back 3 jack, front 2 jack (Internal add Aux-In) */
-       { .pci_vendor = 0x1025, .pci_device = 0xe310, .config = ALC880_3ST },
+       if (spec->autocfg.dig_out_pin)
+               spec->multiout.dig_out_nid = ALC880_DIGOUT_NID;
+       if (spec->autocfg.dig_in_pin)
+               spec->dig_in_nid = ALC880_DIGIN_NID;
 
-       /* Back 3 jack plus 1 SPDIF out jack, front 2 jack */
-       { .modelname = "3stack-digout", .config = ALC880_3ST_DIG },
-       { .pci_vendor = 0x8086, .pci_device = 0xe308, .config = ALC880_3ST_DIG },
+       if (spec->kctl_alloc)
+               spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
 
-       /* Back 3 jack plus 1 SPDIF out jack, front 2 jack (Internal add Aux-In)*/
-       { .pci_vendor = 0x8086, .pci_device = 0xe305, .config = ALC880_3ST_DIG },
-       { .pci_vendor = 0x8086, .pci_device = 0xd402, .config = ALC880_3ST_DIG },
-       { .pci_vendor = 0x1025, .pci_device = 0xe309, .config = ALC880_3ST_DIG },
+       spec->init_verbs[spec->num_init_verbs++] = alc880_volume_init_verbs;
 
-       /* Back 5 jack, front 2 jack */
-       { .modelname = "5stack", .config = ALC880_5ST },
-       { .pci_vendor = 0x107b, .pci_device = 0x3033, .config = ALC880_5ST },
-       { .pci_vendor = 0x107b, .pci_device = 0x4039, .config = ALC880_5ST },
-       { .pci_vendor = 0x107b, .pci_device = 0x3032, .config = ALC880_5ST },
-       { .pci_vendor = 0x103c, .pci_device = 0x2a09, .config = ALC880_5ST },
+       spec->input_mux = &spec->private_imux;
 
-       /* Back 5 jack plus 1 SPDIF out jack, front 2 jack */
-       { .modelname = "5stack-digout", .config = ALC880_5ST_DIG },
-       { .pci_vendor = 0x8086, .pci_device = 0xe224, .config = ALC880_5ST_DIG },
-       { .pci_vendor = 0x8086, .pci_device = 0xe400, .config = ALC880_5ST_DIG },
-       { .pci_vendor = 0x8086, .pci_device = 0xe401, .config = ALC880_5ST_DIG },
-       { .pci_vendor = 0x8086, .pci_device = 0xe402, .config = ALC880_5ST_DIG },
-       { .pci_vendor = 0x8086, .pci_device = 0xd400, .config = ALC880_5ST_DIG },
-       { .pci_vendor = 0x8086, .pci_device = 0xd401, .config = ALC880_5ST_DIG },
-       { .pci_vendor = 0x8086, .pci_device = 0xa100, .config = ALC880_5ST_DIG },
-       { .pci_vendor = 0x1565, .pci_device = 0x8202, .config = ALC880_5ST_DIG },
+       return 1;
+}
 
-       { .modelname = "w810", .config = ALC880_W810 },
-       { .pci_vendor = 0x161f, .pci_device = 0x203d, .config = ALC880_W810 },
+/* init callback for auto-configuration model -- overriding the default init */
+static int alc880_auto_init(struct hda_codec *codec)
+{
+       alc_init(codec);
+       alc880_auto_init_multi_out(codec);
+       alc880_auto_init_hp_out(codec);
+       alc880_auto_init_analog_input(codec);
+       return 0;
+}
 
-       {}
-};
+/*
+ * OK, here we have finally the patch for ALC880
+ */
 
 static int patch_alc880(struct hda_codec *codec)
 {
        struct alc_spec *spec;
        int board_config;
+       int i, err;
 
        spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
        if (spec == NULL)
                return -ENOMEM;
 
+       init_MUTEX(&spec->bind_mutex);
        codec->spec = spec;
 
        board_config = snd_hda_check_board_config(codec, alc880_cfg_tbl);
-       if (board_config < 0) {
-               snd_printd(KERN_INFO "hda_codec: Unknown model for ALC880\n");
-               board_config = ALC880_MINIMAL;
+       if (board_config < 0 || board_config >= ALC880_MODEL_LAST) {
+               printk(KERN_INFO "hda_codec: Unknown model for ALC880, trying auto-probe from BIOS...\n");
+               board_config = ALC880_AUTO;
        }
 
-       switch (board_config) {
-       case ALC880_W810:
-               spec->mixers[spec->num_mixers] = alc880_w810_base_mixer;
-               spec->num_mixers++;
-               break;
-       case ALC880_5ST:
-       case ALC880_5ST_DIG:
-               spec->mixers[spec->num_mixers] = alc880_five_stack_mixer;
-               spec->num_mixers++;
-               break;
-       default:
-               spec->mixers[spec->num_mixers] = alc880_base_mixer;
-               spec->num_mixers++;
-               break;
+       if (board_config == ALC880_AUTO) {
+               /* automatic parse from the BIOS config */
+               err = alc880_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 3-stack mode...\n");
+                       board_config = ALC880_3ST;
+               }
        }
 
-       switch (board_config) {
-       case ALC880_3ST_DIG:
-       case ALC880_5ST_DIG:
-       case ALC880_W810:
-               spec->multiout.dig_out_nid = ALC880_DIGOUT_NID;
-               break;
-       default:
-               break;
-       }
+       if (board_config != ALC880_AUTO) {
+               /* set up from the preset table */
+               const struct alc_config_preset *preset;
 
-       switch (board_config) {
-       case ALC880_3ST:
-       case ALC880_3ST_DIG:
-       case ALC880_5ST:
-       case ALC880_5ST_DIG:
-       case ALC880_W810:
-               spec->front_panel = 1;
-               break;
-       default:
-               break;
-       }
+               preset = &alc880_presets[board_config];
 
-       switch (board_config) {
-       case ALC880_5ST:
-       case ALC880_5ST_DIG:
-               spec->init_verbs = alc880_init_verbs_five_stack;
-               spec->channel_mode = alc880_fivestack_modes;
-               spec->num_channel_mode = ARRAY_SIZE(alc880_fivestack_modes);
-               break;
-       case ALC880_W810:
-               spec->init_verbs = alc880_w810_init_verbs;
-               spec->channel_mode = alc880_w810_modes;
-               spec->num_channel_mode = ARRAY_SIZE(alc880_w810_modes);
-               break;
-       default:
-               spec->init_verbs = alc880_init_verbs_three_stack;
-               spec->channel_mode = alc880_threestack_modes;
-               spec->num_channel_mode = ARRAY_SIZE(alc880_threestack_modes);
-               break;
+               for (i = 0; preset->mixers[i]; i++) {
+                       snd_assert(spec->num_mixers < ARRAY_SIZE(spec->mixers), break);
+                       spec->mixers[spec->num_mixers++] = preset->mixers[i];
+               }
+               for (i = 0; preset->init_verbs[i]; i++) {
+                       snd_assert(spec->num_init_verbs < ARRAY_SIZE(spec->init_verbs), break);
+                       spec->init_verbs[spec->num_init_verbs++] = preset->init_verbs[i];
+               }
+
+               spec->channel_mode = preset->channel_mode;
+               spec->num_channel_mode = preset->num_channel_mode;
+
+               spec->multiout.max_channels = spec->channel_mode[0].channels;
+
+               spec->multiout.num_dacs = preset->num_dacs;
+               spec->multiout.dac_nids = preset->dac_nids;
+               spec->multiout.dig_out_nid = preset->dig_out_nid;
+               spec->multiout.hp_nid = preset->hp_nid;
+
+               spec->input_mux = preset->input_mux;
+
+               spec->num_adc_nids = preset->num_adc_nids;
+               spec->adc_nids = preset->adc_nids;
        }
 
        spec->stream_name_analog = "ALC880 Analog";
@@ -1078,34 +2134,64 @@ static int patch_alc880(struct hda_codec *codec)
        spec->stream_digital_playback = &alc880_pcm_digital_playback;
        spec->stream_digital_capture = &alc880_pcm_digital_capture;
 
-       spec->multiout.max_channels = spec->channel_mode[0].channels;
-
-       switch (board_config) {
-       case ALC880_W810:
-               spec->multiout.num_dacs = ARRAY_SIZE(alc880_w810_dac_nids);
-               spec->multiout.dac_nids = alc880_w810_dac_nids;
-               // No dedicated headphone socket - it's shared with built-in speakers.
-               break;
-       default:
-               spec->multiout.num_dacs = ARRAY_SIZE(alc880_dac_nids);
-               spec->multiout.dac_nids = alc880_dac_nids;
-               spec->multiout.hp_nid = 0x03; /* rear-surround NID */
-               break;
+       if (! spec->adc_nids && spec->input_mux) {
+               /* check whether NID 0x07 is valid */
+               unsigned int wcap = snd_hda_param_read(codec, alc880_adc_nids[0],
+                                                      AC_PAR_AUDIO_WIDGET_CAP);
+               wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; /* get type */
+               if (wcap != AC_WID_AUD_IN) {
+                       spec->adc_nids = alc880_adc_nids_alt;
+                       spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids_alt);
+                       spec->mixers[spec->num_mixers] = alc880_capture_alt_mixer;
+                       spec->num_mixers++;
+               } else {
+                       spec->adc_nids = alc880_adc_nids;
+                       spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids);
+                       spec->mixers[spec->num_mixers] = alc880_capture_mixer;
+                       spec->num_mixers++;
+               }
        }
 
-       spec->input_mux = &alc880_capture_source;
-       spec->num_adc_nids = ARRAY_SIZE(alc880_adc_nids);
-       spec->adc_nids = alc880_adc_nids;
-
        codec->patch_ops = alc_patch_ops;
+       if (board_config == ALC880_AUTO)
+               codec->patch_ops.init = alc880_auto_init;
 
        return 0;
 }
 
+
 /*
  * ALC260 support
  */
 
+static hda_nid_t alc260_dac_nids[1] = {
+       /* front */
+       0x02,
+};
+
+static hda_nid_t alc260_adc_nids[1] = {
+       /* ADC0 */
+       0x04,
+};
+
+static hda_nid_t alc260_hp_adc_nids[1] = {
+       /* ADC1 */
+       0x05,
+};
+
+#define ALC260_DIGOUT_NID      0x03
+#define ALC260_DIGIN_NID       0x06
+
+static struct hda_input_mux alc260_capture_source = {
+       .num_items = 4,
+       .items = {
+               { "Mic", 0x0 },
+               { "Front Mic", 0x1 },
+               { "Line", 0x2 },
+               { "CD", 0x4 },
+       },
+};
+
 /*
  * This is just place-holder, so there's something for alc_build_pcms to look
  * at when it calculates the maximum number of channels. ALC260 has no mixer
@@ -1116,11 +2202,9 @@ static struct alc_channel_mode alc260_modes[1] = {
        { 2, NULL },
 };
 
-snd_kcontrol_new_t alc260_base_mixer[] = {
+static snd_kcontrol_new_t alc260_base_mixer[] = {
        HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT),
-       /* use LINE2 for the output */
-       /* HDA_CODEC_MUTE("Front Playback Switch", 0x0f, 0x0, HDA_OUTPUT), */
-       HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+       ALC_BIND_MUTE("Front Playback Switch", 0x08, 2, HDA_INPUT),
        HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
        HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
        HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT),
@@ -1132,9 +2216,9 @@ snd_kcontrol_new_t alc260_base_mixer[] = {
        HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x07, 0x05, HDA_INPUT),
        HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x07, 0x05, HDA_INPUT),
        HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Headphone Playback Switch", 0x10, 0x0, HDA_OUTPUT),
+       ALC_BIND_MUTE("Headphone Playback Switch", 0x09, 2, HDA_INPUT),
        HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x11, 1, 0x0, HDA_OUTPUT),
+       ALC_BIND_MUTE_MONO("Mono Playback Switch", 0x0a, 1, 2, HDA_OUTPUT),
        HDA_CODEC_VOLUME("Capture Volume", 0x04, 0x0, HDA_INPUT),
        HDA_CODEC_MUTE("Capture Switch", 0x04, 0x0, HDA_INPUT),
        {
@@ -1147,60 +2231,91 @@ snd_kcontrol_new_t alc260_base_mixer[] = {
        { } /* end */
 };
 
+static snd_kcontrol_new_t alc260_hp_mixer[] = {
+       HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT),
+       ALC_BIND_MUTE("Front Playback Switch", 0x08, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
+       HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
+       HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT),
+       HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x07, 0x01, HDA_INPUT),
+       HDA_CODEC_MUTE("Front Mic Playback Switch", 0x07, 0x01, HDA_INPUT),
+       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT),
+       ALC_BIND_MUTE("Headphone Playback Switch", 0x09, 2, HDA_INPUT),
+       HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
+       ALC_BIND_MUTE_MONO("Mono Playback Switch", 0x0a, 1, 2, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Capture Volume", 0x05, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x05, 0x0, HDA_INPUT),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Capture Source",
+               .info = alc_mux_enum_info,
+               .get = alc_mux_enum_get,
+               .put = alc_mux_enum_put,
+       },
+       { } /* end */
+};
+
 static struct hda_verb alc260_init_verbs[] = {
        /* Line In pin widget for input */
-       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
        /* CD pin widget for input */
-       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
        /* Mic1 (rear panel) pin widget for input and vref at 80% */
-       {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+       {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
        /* Mic2 (front panel) pin widget for input and vref at 80% */
-       {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
+       {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
        /* LINE-2 is used for line-out in rear */
-       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
        /* select line-out */
        {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
        /* LINE-OUT pin */
-       {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+       {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
        /* enable HP */
-       {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
+       {0x10, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
        /* enable Mono */
-       {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
-       /* unmute amp left and right */
-       {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
+       {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       /* mute capture amp left and right */
+       {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
        /* set connection select to line in (default select for this ADC) */
        {0x04, AC_VERB_SET_CONNECT_SEL, 0x02},
-       /* unmute Line-Out mixer amp left and right (volume = 0) */
-       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
-       /* mute pin widget amp left and right (no gain on this amp) */
-       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
-       /* unmute HP mixer amp left and right (volume = 0) */
-       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
-       /* mute pin widget amp left and right (no gain on this amp) */
-       {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       /* unmute Mono mixer amp left and right (volume = 0) */
-       {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
-       /* mute pin widget amp left and right (no gain on this amp) */
-       {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       /* mute LINE-2 out */
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
+       /* mute capture amp left and right */
+       {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       /* set connection select to line in (default select for this ADC) */
+       {0x05, AC_VERB_SET_CONNECT_SEL, 0x02},
+       /* set vol=0 Line-Out mixer amp left and right */
+       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+       /* unmute pin widget amp left and right (no gain on this amp) */
+       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       /* set vol=0 HP mixer amp left and right */
+       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+       /* unmute pin widget amp left and right (no gain on this amp) */
+       {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       /* set vol=0 Mono mixer amp left and right */
+       {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+       /* unmute pin widget amp left and right (no gain on this amp) */
+       {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       /* unmute LINE-2 out pin */
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
        /* Amp Indexes: CD = 0x04, Line In 1 = 0x02, Mic 1 = 0x00 & Line In 2 = 0x03 */
-       /* unmute CD */
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
-       /* unmute Line In */
-       {0x07,  AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
-       /* unmute Mic */
-       {0x07,  AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+       /* mute CD */
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
+       /* mute Line In */
+       {0x07,  AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
+       /* mute Mic */
+       {0x07,  AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
        /* Amp Indexes: DAC = 0x01 & mixer = 0x00 */
-       /* Unmute Front out path */
-       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
-       /* Unmute Headphone out path */
-       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
-       /* Unmute Mono out path */
-       {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-       {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+       /* mute Front out path */
+       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       /* mute Headphone out path */
+       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+       /* mute Mono out path */
+       {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
        { }
 };
 
@@ -1208,30 +2323,52 @@ static struct hda_pcm_stream alc260_pcm_analog_playback = {
        .substreams = 1,
        .channels_min = 2,
        .channels_max = 2,
-       .nid = 0x2,
 };
 
 static struct hda_pcm_stream alc260_pcm_analog_capture = {
        .substreams = 1,
        .channels_min = 2,
        .channels_max = 2,
-       .nid = 0x4,
+};
+
+static struct hda_board_config alc260_cfg_tbl[] = {
+       { .modelname = "hp", .config = ALC260_HP },
+       { .pci_subvendor = 0x103c, .config = ALC260_HP },
+       {}
 };
 
 static int patch_alc260(struct hda_codec *codec)
 {
        struct alc_spec *spec;
+       int board_config;
 
        spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
        if (spec == NULL)
                return -ENOMEM;
 
+       init_MUTEX(&spec->bind_mutex);
        codec->spec = spec;
 
-       spec->mixers[spec->num_mixers] = alc260_base_mixer;
-       spec->num_mixers++;
+       board_config = snd_hda_check_board_config(codec, alc260_cfg_tbl);
+       if (board_config < 0 || board_config >= ALC260_MODEL_LAST) {
+               snd_printd(KERN_INFO "hda_codec: Unknown model for ALC260\n");
+               board_config = ALC260_BASIC;
+       }
+
+       switch (board_config) {
+       case ALC260_HP:
+               spec->mixers[spec->num_mixers] = alc260_hp_mixer;
+               spec->num_mixers++;
+               break;
+       default:
+               spec->mixers[spec->num_mixers] = alc260_base_mixer;
+               spec->num_mixers++;
+               break;
+       }
+
+       spec->init_verbs[0] = alc260_init_verbs;
+       spec->num_init_verbs = 1;
 
-       spec->init_verbs = alc260_init_verbs;
        spec->channel_mode = alc260_modes;
        spec->num_channel_mode = ARRAY_SIZE(alc260_modes);
 
@@ -1244,14 +2381,23 @@ static int patch_alc260(struct hda_codec *codec)
        spec->multiout.dac_nids = alc260_dac_nids;
 
        spec->input_mux = &alc260_capture_source;
-       spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids);
-       spec->adc_nids = alc260_adc_nids;
+       switch (board_config) {
+       case ALC260_HP:
+               spec->num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids);
+               spec->adc_nids = alc260_hp_adc_nids;
+               break;
+       default:
+               spec->num_adc_nids = ARRAY_SIZE(alc260_adc_nids);
+               spec->adc_nids = alc260_adc_nids;
+               break;
+       }
 
        codec->patch_ops = alc_patch_ops;
 
        return 0;
 }
 
+
 /*
  * ALC882 support
  *
@@ -1324,15 +2470,15 @@ static int alc882_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *u
  */
 static snd_kcontrol_new_t alc882_base_mixer[] = {
        HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+       ALC_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
        HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+       ALC_BIND_MUTE("Surround Playback Switch", 0x0d, 2, HDA_INPUT),
        HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
        HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x16, 1, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT),
+       ALC_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
+       ALC_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_OUTPUT),
        HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Side Playback Switch", 0x17, 0x0, HDA_OUTPUT),
+       ALC_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),
@@ -1364,89 +2510,80 @@ static snd_kcontrol_new_t alc882_base_mixer[] = {
 
 static struct hda_verb alc882_init_verbs[] = {
        /* Front mixer: unmute input/output amp left and right (volume = 0) */
-       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
-       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
        /* Rear mixer */
-       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
-       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
        /* CLFE mixer */
-       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
-       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
+       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
        /* Side mixer */
-       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
-       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-
-       /* Front Pin: to output mode */
-       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
-       /* Front Pin: mute amp left and right (no volume) */
-       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
-       /* select Front mixer (0x0c, index 0) */
+       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+
+       /* Front Pin: output 0 (0x0c) */
+       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
        {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
-       /* Rear Pin */
-       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
-       /* Rear Pin: mute amp left and right (no volume) */
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
-       /* select Rear mixer (0x0d, index 1) */
+       /* Rear Pin: output 1 (0x0d) */
+       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
        {0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
-       /* CLFE Pin */
-       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
-       /* CLFE Pin: mute amp left and right (no volume) */
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
-       /* select CLFE mixer (0x0e, index 2) */
+       /* CLFE Pin: output 2 (0x0e) */
+       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
        {0x16, AC_VERB_SET_CONNECT_SEL, 0x02},
-       /* Side Pin */
-       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
-       /* Side Pin: mute amp left and right (no volume) */
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
-       /* select Side mixer (0x0f, index 3) */
+       /* Side Pin: output 3 (0x0f) */
+       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
        {0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
-       /* Headphone Pin */
-       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
-       /* Headphone Pin: mute amp left and right (no volume) */
-       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0x0000},
-       /* select Front mixer (0x0c, index 0) */
+       /* Mic (rear) pin: input vref at 80% */
+       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* Front Mic pin: input vref at 80% */
+       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* Line In pin: input */
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+       /* Line-2 In: Headphone output (output 0 - 0x0c) */
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
        {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
-       /* Mic (rear) pin widget for input and vref at 80% */
-       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
-       /* Front Mic pin widget for input and vref at 80% */
-       {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
-       /* Line In pin widget for input */
-       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
        /* CD pin widget for input */
-       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
+       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
 
        /* FIXME: use matrix-type input source selection */
        /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
        /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
-       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
-       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
-       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
        /* Input mixer2 */
-       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
-       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
-       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
        /* Input mixer3 */
-       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
-       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
-       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
-       /* ADC1: unmute amp left and right */
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
-       /* ADC2: unmute amp left and right */
-       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
-       /* ADC3: unmute amp left and right */
-       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000},
-
-       /* Unmute front loopback */
-       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
-       /* Unmute rear loopback */
-       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
-       /* Mute CLFE loopback */
-       {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))},
-       /* Unmute side loopback */
-       {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
+       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+       /* ADC1: mute amp left and right */
+       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x07, AC_VERB_SET_CONNECT_SEL, 0x00},
+       /* ADC2: mute amp left and right */
+       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
+       /* ADC3: mute amp left and right */
+       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+       {0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
 
        { }
 };
@@ -1459,6 +2596,7 @@ static int patch_alc882(struct hda_codec *codec)
        if (spec == NULL)
                return -ENOMEM;
 
+       init_MUTEX(&spec->bind_mutex);
        codec->spec = spec;
 
        spec->mixers[spec->num_mixers] = alc882_base_mixer;
@@ -1466,8 +2604,9 @@ static int patch_alc882(struct hda_codec *codec)
 
        spec->multiout.dig_out_nid = ALC880_DIGOUT_NID;
        spec->dig_in_nid = ALC880_DIGIN_NID;
-       spec->front_panel = 1;
-       spec->init_verbs = alc882_init_verbs;
+       spec->init_verbs[0] = alc882_init_verbs;
+       spec->num_init_verbs = 1;
+
        spec->channel_mode = alc882_ch_modes;
        spec->num_channel_mode = ARRAY_SIZE(alc882_ch_modes);
 
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
new file mode 100644 (file)
index 0000000..013be2e
--- /dev/null
@@ -0,0 +1,666 @@
+/*
+ * Universal Interface for Intel High Definition Audio Codec
+ *
+ * HD audio interface patch for SigmaTel STAC92xx
+ *
+ * Copyright (c) 2005 Embedded Alley Solutions, Inc.
+ * <matt@embeddedalley.com>
+ *
+ * Based on patch_cmedia.c and patch_realtek.c
+ * Copyright (c) 2004 Takashi Iwai <tiwai@suse.de>
+ *
+ *  This driver is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This driver is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+#undef STAC_TEST
+
+struct sigmatel_spec {
+       /* playback */
+       struct hda_multi_out multiout;
+       hda_nid_t playback_nid;
+
+       /* capture */
+       hda_nid_t *adc_nids;
+       unsigned int num_adcs;
+       hda_nid_t *mux_nids;
+       unsigned int num_muxes;
+       hda_nid_t capture_nid;
+       hda_nid_t dig_in_nid;
+
+       /* power management*/
+       hda_nid_t *pstate_nids;
+       unsigned int num_pstates;
+
+       /* pin widgets */
+       hda_nid_t *pin_nids;
+       unsigned int num_pins;
+#ifdef STAC_TEST
+       unsigned int *pin_configs;
+#endif
+
+       /* codec specific stuff */
+       struct hda_verb *init;
+       snd_kcontrol_new_t *mixer;
+
+       /* capture source */
+       struct hda_input_mux input_mux;
+       char input_labels[HDA_MAX_NUM_INPUTS][16];
+       unsigned int cur_mux[2];
+
+       /* channel mode */
+       unsigned int num_ch_modes;
+       unsigned int cur_ch_mode;
+       const struct sigmatel_channel_mode *channel_modes;
+
+       struct hda_pcm pcm_rec[1];      /* PCM information */
+};
+
+static hda_nid_t stac9200_adc_nids[1] = {
+        0x03,
+};
+
+static hda_nid_t stac9200_mux_nids[1] = {
+        0x0c,
+};
+
+static hda_nid_t stac9200_dac_nids[1] = {
+        0x02,
+};
+
+static hda_nid_t stac9200_pstate_nids[3] = {
+       0x01, 0x02, 0x03,
+};
+
+static hda_nid_t stac9200_pin_nids[8] = {
+       0x08, 0x09, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
+};
+
+static hda_nid_t stac922x_adc_nids[2] = {
+        0x06, 0x07,
+};
+
+static hda_nid_t stac922x_mux_nids[2] = {
+        0x12, 0x13,
+};
+
+static hda_nid_t stac922x_dac_nids[4] = {
+        0x02, 0x03, 0x04, 0x05,
+};
+
+static hda_nid_t stac922x_pstate_nids[8] = {
+       0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x11,
+};
+
+static hda_nid_t stac922x_pin_nids[10] = {
+       0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+       0x0f, 0x10, 0x11, 0x15, 0x1b,
+};
+
+static int stac92xx_mux_enum_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct sigmatel_spec *spec = codec->spec;
+       return snd_hda_input_mux_info(&spec->input_mux, uinfo);
+}
+
+static int stac92xx_mux_enum_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct sigmatel_spec *spec = codec->spec;
+       unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+       ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
+       return 0;
+}
+
+static int stac92xx_mux_enum_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct sigmatel_spec *spec = codec->spec;
+       unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+
+       return snd_hda_input_mux_put(codec, &spec->input_mux, ucontrol,
+                                    spec->mux_nids[adc_idx], &spec->cur_mux[adc_idx]);
+}
+
+static struct hda_verb stac9200_ch2_init[] = {
+       /* set dac0mux for dac converter */
+       { 0x07, 0x701, 0x00},
+       {}
+};
+
+static struct hda_verb stac922x_ch2_init[] = {
+       /* set master volume and direct control */      
+       { 0x16, 0x70f, 0xff},
+       {}
+};
+
+struct sigmatel_channel_mode {
+       unsigned int channels;
+       const struct hda_verb *sequence;
+};
+
+static snd_kcontrol_new_t stac9200_mixer[] = {
+       HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Input Source",
+               .count = 1,
+               .info = stac92xx_mux_enum_info,
+               .get = stac92xx_mux_enum_get,
+               .put = stac92xx_mux_enum_put,
+       },
+       HDA_CODEC_VOLUME("Capture Volume", 0x0a, 0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x0a, 0, HDA_OUTPUT),
+       HDA_CODEC_VOLUME("Input Mux Volume", 0x0c, 0, HDA_OUTPUT),
+       { } /* end */
+};
+
+static snd_kcontrol_new_t stac922x_mixer[] = {
+       HDA_CODEC_VOLUME("PCM Playback Volume", 0x2, 0x0, HDA_OUTPUT),
+       HDA_CODEC_MUTE("PCM Playback Switch", 0x2, 0x0, HDA_OUTPUT),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Input Source",
+               .count = 1,
+               .info = stac92xx_mux_enum_info,
+               .get = stac92xx_mux_enum_get,
+               .put = stac92xx_mux_enum_put,
+       },
+       HDA_CODEC_VOLUME("Capture Volume", 0x17, 0x0, HDA_INPUT),
+       HDA_CODEC_MUTE("Capture Switch", 0x17, 0x0, HDA_INPUT),
+       HDA_CODEC_VOLUME("Mux Capture Volume", 0x12, 0x0, HDA_OUTPUT),
+       { } /* end */
+};
+
+static int stac92xx_build_controls(struct hda_codec *codec)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       int err;
+
+       err = snd_hda_add_new_ctls(codec, spec->mixer);
+       if (err < 0)
+               return err;
+       if (spec->multiout.dig_out_nid) {
+               err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
+               if (err < 0)
+                       return err;
+       }
+       if (spec->dig_in_nid) {
+               err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
+               if (err < 0)
+                       return err;
+       }
+       return 0;       
+}
+
+#ifdef STAC_TEST
+static unsigned int stac9200_pin_configs[8] = {
+       0x01c47010, 0x01447010, 0x0221401f, 0x01114010,
+       0x02a19020, 0x01a19021, 0x90100140, 0x01813122,
+};
+
+static unsigned int stac922x_pin_configs[14] = {
+       0x40000100, 0x40000100, 0x40000100, 0x01114010,
+       0x01813122, 0x40000100, 0x01447010, 0x01c47010,
+       0x40000100, 0x40000100,
+};
+
+static void stac92xx_set_config_regs(struct hda_codec *codec)
+{
+       int i;
+       struct sigmatel_spec *spec = codec->spec;
+       unsigned int pin_cfg;
+
+       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);     
+               printk("pin nid %2.2x pin config %8.8x\n", spec->pin_nids[i], pin_cfg);
+       }
+}
+#endif
+
+static int stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid, unsigned int value)
+{
+       unsigned int pin_ctl;
+
+       pin_ctl = snd_hda_codec_read(codec, nid, 0,
+                                    AC_VERB_GET_PIN_WIDGET_CONTROL,
+                                    0x00);
+       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+                           pin_ctl | value);
+
+       return 0;
+}
+
+static int stac92xx_set_vref(struct hda_codec *codec, hda_nid_t nid)
+{
+       unsigned int vref_caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP) >> AC_PINCAP_VREF_SHIFT;
+       unsigned int vref_ctl = AC_PINCTL_VREF_HIZ;
+
+       if (vref_caps & AC_PINCAP_VREF_100)
+               vref_ctl = AC_PINCTL_VREF_100;
+       else if (vref_caps & AC_PINCAP_VREF_80)
+               vref_ctl = AC_PINCTL_VREF_80;
+       else if (vref_caps & AC_PINCAP_VREF_50)
+               vref_ctl = AC_PINCTL_VREF_50;
+       else if (vref_caps & AC_PINCAP_VREF_GRD)
+               vref_ctl = AC_PINCTL_VREF_GRD;
+
+       stac92xx_set_pinctl(codec, nid, vref_ctl);
+       
+       return 0;
+}
+
+/*
+ * retrieve the default device type from the default config value
+ */
+#define get_defcfg_type(cfg) ((cfg & AC_DEFCFG_DEVICE) >> AC_DEFCFG_DEVICE_SHIFT)
+#define get_defcfg_location(cfg) ((cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT)
+
+static int stac92xx_config_pin(struct hda_codec *codec, hda_nid_t nid, unsigned int pin_cfg)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       u32 location = get_defcfg_location(pin_cfg);
+       char *label;
+       const char *type = NULL;
+       int ainput = 0;
+
+       switch(get_defcfg_type(pin_cfg)) {
+               case AC_JACK_HP_OUT:
+                       /* Enable HP amp */
+                       stac92xx_set_pinctl(codec, nid, AC_PINCTL_HP_EN);
+                       /* Fall through */
+               case AC_JACK_SPDIF_OUT:
+               case AC_JACK_LINE_OUT:
+               case AC_JACK_SPEAKER:
+                       /* Enable output */
+                       stac92xx_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
+                       break;
+               case AC_JACK_SPDIF_IN:
+                       stac92xx_set_pinctl(codec, nid, AC_PINCTL_IN_EN);
+                       break;
+               case AC_JACK_MIC_IN:
+                       if ((location & 0x0f) == AC_JACK_LOC_FRONT)
+                               type = "Front Mic";
+                       else
+                               type = "Mic";
+                       ainput = 1;
+                       /* Set vref */
+                       stac92xx_set_vref(codec, nid);
+                       stac92xx_set_pinctl(codec, nid, AC_PINCTL_IN_EN);
+                       break;
+               case AC_JACK_CD:
+                       type = "CD";
+                       ainput = 1;
+                       stac92xx_set_pinctl(codec, nid, AC_PINCTL_IN_EN);
+                       break;
+               case AC_JACK_LINE_IN:
+                       if ((location & 0x0f) == AC_JACK_LOC_FRONT)
+                               type = "Front Line";
+                       else
+                               type = "Line";
+                       ainput = 1;
+                       stac92xx_set_pinctl(codec, nid, AC_PINCTL_IN_EN);
+                       break;
+               case AC_JACK_AUX:
+                       if ((location & 0x0f) == AC_JACK_LOC_FRONT)
+                               type = "Front Aux";
+                       else
+                               type = "Aux";
+                       ainput = 1;
+                       stac92xx_set_pinctl(codec, nid, AC_PINCTL_IN_EN);
+                       break;
+       }
+
+       if (ainput) {
+               hda_nid_t con_lst[HDA_MAX_NUM_INPUTS];
+               int i, j, num_cons, index = -1;
+               if (!type)
+                       type = "Input";
+               label = spec->input_labels[spec->input_mux.num_items];
+               strcpy(label, type);
+               spec->input_mux.items[spec->input_mux.num_items].label = label;
+               for (i=0; i<spec->num_muxes; i++) {
+                       num_cons = snd_hda_get_connections(codec, spec->mux_nids[i], con_lst, HDA_MAX_NUM_INPUTS);
+                       for (j=0; j<num_cons; j++)
+                               if (con_lst[j] == nid) {
+                                       index = j;
+                                       break;
+                               }
+                       if (index >= 0)
+                               break;
+               }
+               spec->input_mux.items[spec->input_mux.num_items].index = index;
+               spec->input_mux.num_items++;
+       }
+
+       return 0;
+}
+
+static int stac92xx_config_pins(struct hda_codec *codec)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       int i;
+       unsigned int pin_cfg;
+
+       for (i=0; i < spec->num_pins; i++) {
+               /* Default to disabled */
+               snd_hda_codec_write(codec, spec->pin_nids[i], 0,
+                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
+                                   0x00);
+
+               pin_cfg = snd_hda_codec_read(codec, spec->pin_nids[i], 0,
+                                            AC_VERB_GET_CONFIG_DEFAULT,
+                                            0x00);
+               if (((pin_cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT) == AC_JACK_PORT_NONE)
+                       continue;       /* Move on */
+
+               stac92xx_config_pin(codec, spec->pin_nids[i], pin_cfg);
+       }
+
+       return 0;
+}
+
+static int stac92xx_init(struct hda_codec *codec)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       int i;
+
+       for (i=0; i < spec->num_pstates; i++)
+               snd_hda_codec_write(codec, spec->pstate_nids[i], 0,
+                                   AC_VERB_SET_POWER_STATE, 0x00);
+
+       mdelay(100);
+
+       snd_hda_sequence_write(codec, spec->init);
+
+#ifdef STAC_TEST
+       stac92xx_set_config_regs(codec);
+#endif
+
+       stac92xx_config_pins(codec);
+
+       return 0;
+}
+
+/*
+ * Analog playback callbacks
+ */
+static int stac92xx_playback_pcm_open(struct hda_pcm_stream *hinfo,
+                                     struct hda_codec *codec,
+                                     snd_pcm_substream_t *substream)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream);
+}
+
+static int stac92xx_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+                                        struct hda_codec *codec,
+                                        unsigned int stream_tag,
+                                        unsigned int format,
+                                        snd_pcm_substream_t *substream)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
+                                               format, substream);
+}
+
+static int stac92xx_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                                       struct hda_codec *codec,
+                                       snd_pcm_substream_t *substream)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+}
+
+/*
+ * Digital playback callbacks
+ */
+static int stac92xx_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+                                         struct hda_codec *codec,
+                                         snd_pcm_substream_t *substream)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int stac92xx_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+                                          struct hda_codec *codec,
+                                          snd_pcm_substream_t *substream)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+
+/*
+ * Analog capture callbacks
+ */
+static int stac92xx_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+                                       struct hda_codec *codec,
+                                       unsigned int stream_tag,
+                                       unsigned int format,
+                                       snd_pcm_substream_t *substream)
+{
+       struct sigmatel_spec *spec = codec->spec;
+
+       snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
+                                   stream_tag, 0, format);
+       return 0;
+}
+
+static int stac92xx_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                                       struct hda_codec *codec,
+                                       snd_pcm_substream_t *substream)
+{
+       struct sigmatel_spec *spec = codec->spec;
+
+       snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 0, 0, 0);
+       return 0;
+}
+
+static struct hda_pcm_stream stac92xx_pcm_digital_playback = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       /* NID is set in stac92xx_build_pcms */
+       .ops = {
+               .open = stac92xx_dig_playback_pcm_open,
+               .close = stac92xx_dig_playback_pcm_close
+       },
+};
+
+static struct hda_pcm_stream stac92xx_pcm_digital_capture = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       /* NID is set in stac92xx_build_pcms */
+};
+
+static struct hda_pcm_stream stac92xx_pcm_analog_playback = {
+       .substreams = 1,
+       .channels_min = 2,
+       .channels_max = 2,
+       .nid = 0x02, /* NID to query formats and rates */
+       .ops = {
+               .open = stac92xx_playback_pcm_open,
+               .prepare = stac92xx_playback_pcm_prepare,
+               .cleanup = stac92xx_playback_pcm_cleanup
+       },
+};
+
+static struct hda_pcm_stream stac92xx_pcm_analog_capture = {
+       .substreams = 2,
+       .channels_min = 2,
+       .channels_max = 2,
+       .nid = 0x06, /* NID to query formats and rates */
+       .ops = {
+               .prepare = stac92xx_capture_pcm_prepare,
+               .cleanup = stac92xx_capture_pcm_cleanup
+       },
+};
+
+static int stac92xx_build_pcms(struct hda_codec *codec)
+{
+       struct sigmatel_spec *spec = codec->spec;
+       struct hda_pcm *info = spec->pcm_rec;
+
+       codec->num_pcms = 1;
+       codec->pcm_info = info;
+
+       info->name = "STAC92xx";
+       info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_analog_playback;
+       info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->playback_nid;
+       info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_analog_capture;
+       info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->capture_nid;
+
+       if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
+               codec->num_pcms++;
+               info++;
+               info->name = "STAC92xx Digital";
+               if (spec->multiout.dig_out_nid) {
+                       info->stream[SNDRV_PCM_STREAM_PLAYBACK] = stac92xx_pcm_digital_playback;
+                       info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
+               }
+               if (spec->dig_in_nid) {
+                       info->stream[SNDRV_PCM_STREAM_CAPTURE] = stac92xx_pcm_digital_capture;
+                       info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
+               }
+       }
+
+       return 0;
+}
+
+static void stac92xx_free(struct hda_codec *codec)
+{
+       kfree(codec->spec);
+}
+
+static struct hda_codec_ops stac92xx_patch_ops = {
+       .build_controls = stac92xx_build_controls,
+       .build_pcms = stac92xx_build_pcms,
+       .init = stac92xx_init,
+       .free = stac92xx_free,
+};
+
+static int patch_stac9200(struct hda_codec *codec)
+{
+       struct sigmatel_spec *spec;
+
+       spec  = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+
+       codec->spec = spec;
+
+       spec->multiout.max_channels = 2;
+       spec->multiout.num_dacs = 1;
+       spec->multiout.dac_nids = stac9200_dac_nids;
+       spec->multiout.dig_out_nid = 0x05;
+       spec->dig_in_nid = 0x04;
+       spec->adc_nids = stac9200_adc_nids;
+       spec->mux_nids = stac9200_mux_nids;
+       spec->num_muxes = 1;
+       spec->input_mux.num_items = 0;
+       spec->pstate_nids = stac9200_pstate_nids;
+       spec->num_pstates = 3;
+       spec->pin_nids = stac9200_pin_nids;
+#ifdef STAC_TEST
+       spec->pin_configs = stac9200_pin_configs;
+#endif
+       spec->num_pins = 8;
+       spec->init = stac9200_ch2_init;
+       spec->mixer = stac9200_mixer;
+       spec->playback_nid = 0x02;
+       spec->capture_nid = 0x03;
+
+       codec->patch_ops = stac92xx_patch_ops;
+
+       return 0;
+}
+
+static int patch_stac922x(struct hda_codec *codec)
+{
+       struct sigmatel_spec *spec;
+
+       spec  = kcalloc(1, sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+
+       codec->spec = spec;
+
+       spec->multiout.max_channels = 2;
+       spec->multiout.num_dacs = 4;
+       spec->multiout.dac_nids = stac922x_dac_nids;
+       spec->multiout.dig_out_nid = 0x08;
+       spec->dig_in_nid = 0x09;
+       spec->adc_nids = stac922x_adc_nids;
+       spec->mux_nids = stac922x_mux_nids;
+       spec->num_muxes = 2;
+       spec->input_mux.num_items = 0;
+       spec->pstate_nids = stac922x_pstate_nids;
+       spec->num_pstates = 8;
+       spec->pin_nids = stac922x_pin_nids;
+#ifdef STAC_TEST
+       spec->pin_configs = stac922x_pin_configs;
+#endif
+       spec->num_pins = 10;
+       spec->init = stac922x_ch2_init;
+       spec->mixer = stac922x_mixer;
+       spec->playback_nid = 0x02;
+       spec->capture_nid = 0x06;
+
+       codec->patch_ops = stac92xx_patch_ops;
+
+       return 0;
+}
+
+/*
+ * patch entries
+ */
+struct hda_codec_preset snd_hda_preset_sigmatel[] = {
+       { .id = 0x83847690, .name = "STAC9200", .patch = patch_stac9200 },
+       { .id = 0x83847882, .name = "STAC9220 A1", .patch = patch_stac922x },
+       { .id = 0x83847680, .name = "STAC9221 A1", .patch = patch_stac922x },
+       { .id = 0x83847880, .name = "STAC9220 A2", .patch = patch_stac922x },
+       { .id = 0x83847681, .name = "STAC9220D/9223D A2", .patch = patch_stac922x },
+       { .id = 0x83847682, .name = "STAC9221 A2", .patch = patch_stac922x },
+       { .id = 0x83847683, .name = "STAC9221D A2", .patch = patch_stac922x },
+       {} /* terminator */
+};
index 779951725e1e44acdc1317e4dc0092a5ff4b7aaf..289b0b5711e47fb6a53b1eeb5e8ada875242652d 100644 (file)
 #include <sound/core.h>
 
 #include "ice1712.h"
+#include "envy24ht.h"
 #include "amp.h"
 
+static void wm_put(ice1712_t *ice, int reg, unsigned short val)
+{
+       unsigned short cval;
+       cval = (reg << 9) | val;
+       snd_vt1724_write_i2c(ice, WM_DEV, cval >> 8, cval & 0xff);
+}
 
 static int __devinit snd_vt1724_amp_init(ice1712_t *ice)
 {
+       static unsigned short wm_inits[] = {
+               WM_ATTEN_L,     0x0000, /* 0 db */
+               WM_ATTEN_R,     0x0000, /* 0 db */
+               WM_DAC_CTRL,    0x0008, /* 24bit I2S */
+               WM_INT_CTRL,    0x0001, /* 24bit I2S */ 
+       };
+
+       unsigned int i;
+
        /* only use basic functionality for now */
 
        ice->num_total_dacs = 2;        /* only PSDOUT0 is connected */
        ice->num_total_adcs = 2;
 
+       /* Chaintech AV-710 has another codecs, which need initialization */
+       /* initialize WM8728 codec */
+       if (ice->eeprom.subvendor == VT1724_SUBDEVICE_AV710) {
+               for (i = 0; i < ARRAY_SIZE(wm_inits); i += 2)
+                       wm_put(ice, wm_inits[i], wm_inits[i+1]);
+       }
+
        return 0;
 }
 
@@ -53,6 +76,13 @@ static int __devinit snd_vt1724_amp_add_controls(ice1712_t *ice)
 
 /* entry point */
 struct snd_ice1712_card_info snd_vt1724_amp_cards[] __devinitdata = {
+       {
+               .subvendor = VT1724_SUBDEVICE_AV710,
+               .name = "Chaintech AV-710",
+               .model = "av710",
+               .chip_init = snd_vt1724_amp_init,
+               .build_controls = snd_vt1724_amp_add_controls,
+       },
        {
                .subvendor = VT1724_SUBDEVICE_AUDIO2000,
                .name = "AMP Ltd AUDIO2000",
index d58d43383e839fbe5c67992fa31c5941f8ed356f..a0fc89b48122d4a65779acec06542eb90d90a293 100644 (file)
  *
  */      
 
-#define  AMP_AUDIO2000_DEVICE_DESC            "{AMP Ltd,AUDIO2000},"
+#define  AMP_AUDIO2000_DEVICE_DESC            "{AMP Ltd,AUDIO2000},"\
+                                              "{Chaintech,AV-710},"
 
+#if 0
 #define VT1724_SUBDEVICE_AUDIO2000     0x12142417      /* Advanced Micro Peripherals Ltd AUDIO2000 */
+#else
+#define VT1724_SUBDEVICE_AUDIO2000     0x00030003      /* a dummy ID for AMP Audio2000 */
+#endif
+#define VT1724_SUBDEVICE_AV710         0x12142417      /* AV710 - the same ID with Audio2000! */
+
+/* WM8728 on I2C for AV710 */
+#define WM_DEV         0x36
+
+#define WM_ATTEN_L     0x00
+#define WM_ATTEN_R     0x01
+#define WM_DAC_CTRL    0x02
+#define WM_INT_CTRL    0x03
 
 extern struct snd_ice1712_card_info  snd_vt1724_amp_cards[];
 
index 79fba6be3503a0cbe25ddf5a207286bd67024aca..a2545a5b26c48b1546ea9d35b6a0c31cbeff069b 100644 (file)
@@ -2748,7 +2748,7 @@ static struct pci_driver driver = {
 
 static int __init alsa_card_ice1712_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_ice1712_exit(void)
index 8bb1c58c26a005950b90c0f07115709d4ee0eb02..5ad4728daa7b83fb3cef05ec5e47793b31d6e1ed 100644 (file)
@@ -373,6 +373,11 @@ struct _snd_ice1712 {
                        unsigned short master[2];
                        unsigned short vol[8];
                } aureon;
+               /* AC97 register cache for Phase28 */
+               struct phase28_spec {
+                       unsigned short master[2];
+                       unsigned short vol[8];
+               } phase28;
                /* Hoontech-specific setting */
                struct hoontech_spec {
                        unsigned char boxbits[4];
index 95500f06f0c62375b391bd79615b2719dfc667f6..79b5f12e06fc4afba100a65e8eacefbefa697b52 100644 (file)
@@ -2328,7 +2328,7 @@ static struct pci_driver driver = {
 
 static int __init alsa_card_ice1724_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_ice1724_exit(void)
index d1f90832443c44cfe0b7b745b63d8dfb1249be33..5bf734b04fa065f0e6999828ee177f8479e0967e 100644 (file)
 #include "envy24ht.h"
 #include "phase.h"
 
+/* WM8770 registers */
+#define WM_DAC_ATTEN           0x00    /* DAC1-8 analog attenuation */
+#define WM_DAC_MASTER_ATTEN    0x08    /* DAC master analog attenuation */
+#define WM_DAC_DIG_ATTEN       0x09    /* DAC1-8 digital attenuation */
+#define WM_DAC_DIG_MASTER_ATTEN        0x11    /* DAC master digital attenuation */
+#define WM_PHASE_SWAP          0x12    /* DAC phase */
+#define WM_DAC_CTRL1           0x13    /* DAC control bits */
+#define WM_MUTE                        0x14    /* mute controls */
+#define WM_DAC_CTRL2           0x15    /* de-emphasis and zefo-flag */
+#define WM_INT_CTRL            0x16    /* interface control */
+#define WM_MASTER              0x17    /* master clock and mode */
+#define WM_POWERDOWN           0x18    /* power-down controls */
+#define WM_ADC_GAIN            0x19    /* ADC gain L(19)/R(1a) */
+#define WM_ADC_MUX             0x1b    /* input MUX */
+#define WM_OUT_MUX1            0x1c    /* output MUX */
+#define WM_OUT_MUX2            0x1e    /* output MUX */
+#define WM_RESET               0x1f    /* software reset */
+
+
+/*
+ * Logarithmic volume values for WM8770
+ * Computed as 20 * Log10(255 / x)
+ */
+static unsigned char wm_vol[256] = {
+       127, 48, 42, 39, 36, 34, 33, 31, 30, 29, 28, 27, 27, 26, 25, 25, 24, 24, 23,
+       23, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17,
+       17, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 13, 13, 13,
+       13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, 11,
+       11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8,
+       8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6,
+       6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+       5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3,
+       3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0
+};
+
+#define WM_VOL_MAX     (sizeof(wm_vol) - 1)
+#define WM_VOL_MUTE    0x8000
+
 static akm4xxx_t akm_phase22 __devinitdata = {
        .type = SND_AK4524,
        .num_dacs = 2,
@@ -124,6 +165,684 @@ static unsigned char phase22_eeprom[] __devinitdata = {
        0x00,   /* GPIO_STATE2 */
 };
 
+static unsigned char phase28_eeprom[] __devinitdata = {
+       0x0b,   /* SYSCONF: clock 512, spdif-in/ADC, 4DACs */
+       0x80,   /* ACLINK: I2S */
+       0xfc,   /* I2S: vol, 96k, 24bit, 192k */
+       0xc3,   /* SPDIF: out-en, out-int, spdif-in */
+       0xff,   /* GPIO_DIR */
+       0xff,   /* GPIO_DIR1 */
+       0x5f,   /* GPIO_DIR2 */
+       0x00,   /* GPIO_MASK */
+       0x00,   /* GPIO_MASK1 */
+       0x00,   /* GPIO_MASK2 */
+       0x00,   /* GPIO_STATE */
+       0x00,   /* GPIO_STATE1 */
+       0x00,   /* GPIO_STATE2 */
+};
+
+/*
+ * write data in the SPI mode
+ */
+static void phase28_spi_write(ice1712_t *ice, unsigned int cs, unsigned int data, int bits)
+{
+       unsigned int tmp;
+       int i;
+
+       tmp = snd_ice1712_gpio_read(ice);
+
+       snd_ice1712_gpio_set_mask(ice, ~(PHASE28_WM_RW|PHASE28_SPI_MOSI|PHASE28_SPI_CLK|
+                                        PHASE28_WM_CS));
+       tmp |= PHASE28_WM_RW;
+       tmp &= ~cs;
+       snd_ice1712_gpio_write(ice, tmp);
+       udelay(1);
+
+       for (i = bits - 1; i >= 0; i--) {
+               tmp &= ~PHASE28_SPI_CLK;
+               snd_ice1712_gpio_write(ice, tmp);
+               udelay(1);
+               if (data & (1 << i))
+                       tmp |= PHASE28_SPI_MOSI;
+               else
+                       tmp &= ~PHASE28_SPI_MOSI;
+               snd_ice1712_gpio_write(ice, tmp);
+               udelay(1);
+               tmp |= PHASE28_SPI_CLK;
+               snd_ice1712_gpio_write(ice, tmp);
+               udelay(1);
+       }
+
+       tmp &= ~PHASE28_SPI_CLK;
+       tmp |= cs;
+       snd_ice1712_gpio_write(ice, tmp);
+       udelay(1);
+       tmp |= PHASE28_SPI_CLK;
+       snd_ice1712_gpio_write(ice, tmp);
+       udelay(1);
+}
+
+/*
+ * get the current register value of WM codec
+ */
+static unsigned short wm_get(ice1712_t *ice, int reg)
+{
+       reg <<= 1;
+       return ((unsigned short)ice->akm[0].images[reg] << 8) |
+               ice->akm[0].images[reg + 1];
+}
+
+/*
+ * set the register value of WM codec
+ */
+static void wm_put_nocache(ice1712_t *ice, int reg, unsigned short val)
+{
+       phase28_spi_write(ice, PHASE28_WM_CS, (reg << 9) | (val & 0x1ff), 16);
+}
+
+/*
+ * set the register value of WM codec and remember it
+ */
+static void wm_put(ice1712_t *ice, int reg, unsigned short val)
+{
+       wm_put_nocache(ice, reg, val);
+       reg <<= 1;
+       ice->akm[0].images[reg] = val >> 8;
+       ice->akm[0].images[reg + 1] = val;
+}
+
+static void wm_set_vol(ice1712_t *ice, unsigned int index, unsigned short vol, unsigned short master)
+{
+       unsigned char nvol;
+
+       if ((master & WM_VOL_MUTE) || (vol & WM_VOL_MUTE))
+               nvol = 0;
+       else
+               nvol = 127 - wm_vol[(((vol & ~WM_VOL_MUTE) * (master & ~WM_VOL_MUTE)) / 127) & WM_VOL_MAX];
+
+       wm_put(ice, index, nvol);
+       wm_put_nocache(ice, index, 0x180 | nvol);
+}
+
+/*
+ * DAC mute control
+ */
+#define wm_pcm_mute_info       phase28_mono_bool_info
+
+static int wm_pcm_mute_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+       down(&ice->gpio_mutex);
+       ucontrol->value.integer.value[0] = (wm_get(ice, WM_MUTE) & 0x10) ? 0 : 1;
+       up(&ice->gpio_mutex);
+       return 0;
+}
+
+static int wm_pcm_mute_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+       ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+       unsigned short nval, oval;
+       int change;
+
+       snd_ice1712_save_gpio_status(ice);
+       oval = wm_get(ice, WM_MUTE);
+       nval = (oval & ~0x10) | (ucontrol->value.integer.value[0] ? 0 : 0x10);
+       if ((change = (nval != oval)))
+               wm_put(ice, WM_MUTE, nval);
+       snd_ice1712_restore_gpio_status(ice);
+
+       return change;
+}
+
+/*
+ * Master volume attenuation mixer control
+ */
+static int wm_master_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 2;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = WM_VOL_MAX;
+       return 0;
+}
+
+static int wm_master_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+       int i;
+       for (i=0; i<2; i++)
+               ucontrol->value.integer.value[i] = ice->spec.phase28.master[i] & ~WM_VOL_MUTE;
+       return 0;
+}
+
+static int wm_master_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+       int ch, change = 0;
+
+       snd_ice1712_save_gpio_status(ice);
+       for (ch = 0; ch < 2; ch++) {
+               if (ucontrol->value.integer.value[ch] != ice->spec.phase28.master[ch]) {
+                       int dac;
+                       ice->spec.phase28.master[ch] &= WM_VOL_MUTE;
+                       ice->spec.phase28.master[ch] |= ucontrol->value.integer.value[ch];
+                       for (dac = 0; dac < ice->num_total_dacs; dac += 2)
+                               wm_set_vol(ice, WM_DAC_ATTEN + dac + ch,
+                                          ice->spec.phase28.vol[dac + ch],
+                                          ice->spec.phase28.master[ch]);
+                       change = 1;
+               }
+       }
+       snd_ice1712_restore_gpio_status(ice);
+       return change;
+}
+
+static int __devinit phase28_init(ice1712_t *ice)
+{
+       static unsigned short wm_inits_phase28[] = {
+               /* These come first to reduce init pop noise */
+               0x1b, 0x044,            /* ADC Mux (AC'97 source) */
+               0x1c, 0x00B,            /* Out Mux1 (VOUT1 = DAC+AUX, VOUT2 = DAC) */
+               0x1d, 0x009,            /* Out Mux2 (VOUT2 = DAC, VOUT3 = DAC) */
+
+               0x18, 0x000,            /* All power-up */
+
+               0x16, 0x122,            /* I2S, normal polarity, 24bit */
+               0x17, 0x022,            /* 256fs, slave mode */
+               0x00, 0,                /* DAC1 analog mute */
+               0x01, 0,                /* DAC2 analog mute */
+               0x02, 0,                /* DAC3 analog mute */
+               0x03, 0,                /* DAC4 analog mute */
+               0x04, 0,                /* DAC5 analog mute */
+               0x05, 0,                /* DAC6 analog mute */
+               0x06, 0,                /* DAC7 analog mute */
+               0x07, 0,                /* DAC8 analog mute */
+               0x08, 0x100,            /* master analog mute */
+               0x09, 0xff,             /* DAC1 digital full */
+               0x0a, 0xff,             /* DAC2 digital full */
+               0x0b, 0xff,             /* DAC3 digital full */
+               0x0c, 0xff,             /* DAC4 digital full */
+               0x0d, 0xff,             /* DAC5 digital full */
+               0x0e, 0xff,             /* DAC6 digital full */
+               0x0f, 0xff,             /* DAC7 digital full */
+               0x10, 0xff,             /* DAC8 digital full */
+               0x11, 0x1ff,            /* master digital full */
+               0x12, 0x000,            /* phase normal */
+               0x13, 0x090,            /* unmute DAC L/R */
+               0x14, 0x000,            /* all unmute */
+               0x15, 0x000,            /* no deemphasis, no ZFLG */
+               0x19, 0x000,            /* -12dB ADC/L */
+               0x1a, 0x000,            /* -12dB ADC/R */
+               (unsigned short)-1
+       };
+
+       unsigned int tmp;
+       akm4xxx_t *ak;
+       unsigned short *p;
+       int i;
+
+       ice->num_total_dacs = 8;
+       ice->num_total_adcs = 2;
+
+       // Initialize analog chips
+       ak = ice->akm = kcalloc(1, sizeof(akm4xxx_t), GFP_KERNEL);
+       if (!ak)
+               return -ENOMEM;
+       ice->akm_codecs = 1;
+
+       snd_ice1712_gpio_set_dir(ice, 0x5fffff); /* fix this for the time being */
+
+       /* reset the wm codec as the SPI mode */
+       snd_ice1712_save_gpio_status(ice);
+       snd_ice1712_gpio_set_mask(ice, ~(PHASE28_WM_RESET|PHASE28_WM_CS|PHASE28_HP_SEL));
+
+       tmp = snd_ice1712_gpio_read(ice);
+       tmp &= ~PHASE28_WM_RESET;
+       snd_ice1712_gpio_write(ice, tmp);
+       udelay(1);
+       tmp |= PHASE28_WM_CS;
+       snd_ice1712_gpio_write(ice, tmp);
+       udelay(1);
+       tmp |= PHASE28_WM_RESET;
+       snd_ice1712_gpio_write(ice, tmp);
+       udelay(1);
+
+       p = wm_inits_phase28;
+       for (; *p != (unsigned short)-1; p += 2)
+               wm_put(ice, p[0], p[1]);
+
+       snd_ice1712_restore_gpio_status(ice);
+
+       ice->spec.phase28.master[0] = WM_VOL_MUTE;
+       ice->spec.phase28.master[1] = WM_VOL_MUTE;
+       for (i = 0; i < ice->num_total_dacs; i++) {
+               ice->spec.phase28.vol[i] = WM_VOL_MUTE;
+               wm_set_vol(ice, i, ice->spec.phase28.vol[i], ice->spec.phase28.master[i % 2]);
+       }
+
+       return 0;
+}
+
+/*
+ * DAC volume attenuation mixer control
+ */
+static int wm_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+       int voices = kcontrol->private_value >> 8;
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = voices;
+       uinfo->value.integer.min = 0;           /* mute (-101dB) */
+       uinfo->value.integer.max = 0x7F;        /* 0dB */
+       return 0;
+}
+
+static int wm_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+       int i, ofs, voices;
+
+       voices = kcontrol->private_value >> 8;
+       ofs = kcontrol->private_value & 0xff;
+       for (i = 0; i < voices; i++)
+               ucontrol->value.integer.value[i] = ice->spec.phase28.vol[ofs+i] & ~WM_VOL_MUTE;
+       return 0;
+}
+
+static int wm_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+       int i, idx, ofs, voices;
+       int change = 0;
+
+       voices = kcontrol->private_value >> 8;
+       ofs = kcontrol->private_value & 0xff;
+       snd_ice1712_save_gpio_status(ice);
+       for (i = 0; i < voices; i++) {
+               idx  = WM_DAC_ATTEN + ofs + i;
+               if (ucontrol->value.integer.value[i] != ice->spec.phase28.vol[ofs+i]) {
+                       ice->spec.phase28.vol[ofs+i] &= WM_VOL_MUTE;
+                       ice->spec.phase28.vol[ofs+i] |= ucontrol->value.integer.value[i];
+                       wm_set_vol(ice, idx, ice->spec.phase28.vol[ofs+i],
+                                  ice->spec.phase28.master[i]);
+                       change = 1;
+               }
+       }
+       snd_ice1712_restore_gpio_status(ice);
+       return change;
+}
+
+/*
+ * WM8770 mute control
+ */
+static int wm_mute_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) {
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count = kcontrol->private_value >> 8;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 1;
+       return 0;
+}
+
+static int wm_mute_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+       int voices, ofs, i;
+
+       voices = kcontrol->private_value >> 8;
+       ofs = kcontrol->private_value & 0xFF;
+
+       for (i = 0; i < voices; i++)
+               ucontrol->value.integer.value[i] = (ice->spec.phase28.vol[ofs+i] & WM_VOL_MUTE) ? 0 : 1;
+       return 0;
+}
+
+static int wm_mute_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+       ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+       int change = 0, voices, ofs, i;
+
+       voices = kcontrol->private_value >> 8;
+       ofs = kcontrol->private_value & 0xFF;
+
+       snd_ice1712_save_gpio_status(ice);
+       for (i = 0; i < voices; i++) {
+               int val = (ice->spec.phase28.vol[ofs + i] & WM_VOL_MUTE) ? 0 : 1;
+               if (ucontrol->value.integer.value[i] != val) {
+                       ice->spec.phase28.vol[ofs + i] &= ~WM_VOL_MUTE;
+                       ice->spec.phase28.vol[ofs + i] |=
+                               ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE;
+                       wm_set_vol(ice, ofs + i, ice->spec.phase28.vol[ofs + i],
+                                  ice->spec.phase28.master[i]);
+                       change = 1;
+               }
+       }
+       snd_ice1712_restore_gpio_status(ice);
+
+       return change;
+}
+
+/*
+ * WM8770 master mute control
+ */
+static int wm_master_mute_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) {
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count = 2;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 1;
+       return 0;
+}
+
+static int wm_master_mute_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.integer.value[0] = (ice->spec.phase28.master[0] & WM_VOL_MUTE) ? 0 : 1;
+       ucontrol->value.integer.value[1] = (ice->spec.phase28.master[1] & WM_VOL_MUTE) ? 0 : 1;
+       return 0;
+}
+
+static int wm_master_mute_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+       ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+       int change = 0, i;
+
+       snd_ice1712_save_gpio_status(ice);
+       for (i = 0; i < 2; i++) {
+               int val = (ice->spec.phase28.master[i] & WM_VOL_MUTE) ? 0 : 1;
+               if (ucontrol->value.integer.value[i] != val) {
+                       int dac;
+                       ice->spec.phase28.master[i] &= ~WM_VOL_MUTE;
+                       ice->spec.phase28.master[i] |=
+                               ucontrol->value.integer.value[i] ? 0 : WM_VOL_MUTE;
+                       for (dac = 0; dac < ice->num_total_dacs; dac += 2)
+                               wm_set_vol(ice, WM_DAC_ATTEN + dac + i,
+                                          ice->spec.phase28.vol[dac + i],
+                                          ice->spec.phase28.master[i]);
+                       change = 1;
+               }
+       }
+       snd_ice1712_restore_gpio_status(ice);
+
+       return change;
+}
+
+/* digital master volume */
+#define PCM_0dB 0xff
+#define PCM_RES 128    /* -64dB */
+#define PCM_MIN (PCM_0dB - PCM_RES)
+static int wm_pcm_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;           /* mute (-64dB) */
+       uinfo->value.integer.max = PCM_RES;     /* 0dB */
+       return 0;
+}
+
+static int wm_pcm_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+       unsigned short val;
+
+       down(&ice->gpio_mutex);
+       val = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
+       val = val > PCM_MIN ? (val - PCM_MIN) : 0;
+       ucontrol->value.integer.value[0] = val;
+       up(&ice->gpio_mutex);
+       return 0;
+}
+
+static int wm_pcm_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+       unsigned short ovol, nvol;
+       int change = 0;
+
+       snd_ice1712_save_gpio_status(ice);
+       nvol = ucontrol->value.integer.value[0];
+       nvol = (nvol ? (nvol + PCM_MIN) : 0) & 0xff;
+       ovol = wm_get(ice, WM_DAC_DIG_MASTER_ATTEN) & 0xff;
+       if (ovol != nvol) {
+               wm_put(ice, WM_DAC_DIG_MASTER_ATTEN, nvol); /* prelatch */
+               wm_put_nocache(ice, WM_DAC_DIG_MASTER_ATTEN, nvol | 0x100); /* update */
+               change = 1;
+       }
+       snd_ice1712_restore_gpio_status(ice);
+       return change;
+}
+
+/*
+ */
+static int phase28_mono_bool_info(snd_kcontrol_t *k, snd_ctl_elem_info_t *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 1;
+       return 0;
+}
+
+/*
+ * Deemphasis
+ */
+#define phase28_deemp_info     phase28_mono_bool_info
+
+static int phase28_deemp_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+       ucontrol->value.integer.value[0] = (wm_get(ice, WM_DAC_CTRL2) & 0xf) == 0xf;
+       return 0;
+}
+
+static int phase28_deemp_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+       int temp, temp2;
+       temp2 = temp = wm_get(ice, WM_DAC_CTRL2);
+       if (ucontrol->value.integer.value[0])
+               temp |= 0xf;
+       else
+               temp &= ~0xf;
+       if (temp != temp2) {
+               wm_put(ice, WM_DAC_CTRL2, temp);
+               return 1;
+       }
+       return 0;
+}
+
+/*
+ * ADC Oversampling
+ */
+static int phase28_oversampling_info(snd_kcontrol_t *k, snd_ctl_elem_info_t *uinfo)
+{
+       static char *texts[2] = { "128x", "64x" };
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 2;
+
+       if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+               uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+       strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+
+        return 0;
+}
+
+static int phase28_oversampling_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+       ucontrol->value.enumerated.item[0] = (wm_get(ice, WM_MASTER) & 0x8) == 0x8;
+       return 0;
+}
+
+static int phase28_oversampling_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       int temp, temp2;
+       ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+
+       temp2 = temp = wm_get(ice, WM_MASTER);
+
+       if (ucontrol->value.enumerated.item[0])
+               temp |= 0x8;
+       else
+               temp &= ~0x8;
+
+       if (temp != temp2) {
+               wm_put(ice, WM_MASTER, temp);
+               return 1;
+       }
+       return 0;
+}
+
+static snd_kcontrol_new_t phase28_dac_controls[] __devinitdata = {
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Master Playback Switch",
+               .info = wm_master_mute_info,
+               .get = wm_master_mute_get,
+               .put = wm_master_mute_put
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Master Playback Volume",
+               .info = wm_master_vol_info,
+               .get = wm_master_vol_get,
+               .put = wm_master_vol_put
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Front Playback Switch",
+               .info = wm_mute_info,
+               .get = wm_mute_get,
+               .put = wm_mute_put,
+               .private_value = (2 << 8) | 0
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Front Playback Volume",
+               .info = wm_vol_info,
+               .get = wm_vol_get,
+               .put = wm_vol_put,
+               .private_value = (2 << 8) | 0
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Rear Playback Switch",
+               .info = wm_mute_info,
+               .get = wm_mute_get,
+               .put = wm_mute_put,
+               .private_value = (2 << 8) | 2
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Rear Playback Volume",
+               .info = wm_vol_info,
+               .get = wm_vol_get,
+               .put = wm_vol_put,
+               .private_value = (2 << 8) | 2
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Center Playback Switch",
+               .info = wm_mute_info,
+               .get = wm_mute_get,
+               .put = wm_mute_put,
+               .private_value = (1 << 8) | 4
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Center Playback Volume",
+               .info = wm_vol_info,
+               .get = wm_vol_get,
+               .put = wm_vol_put,
+               .private_value = (1 << 8) | 4
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "LFE Playback Switch",
+               .info = wm_mute_info,
+               .get = wm_mute_get,
+               .put = wm_mute_put,
+               .private_value = (1 << 8) | 5
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "LFE Playback Volume",
+               .info = wm_vol_info,
+               .get = wm_vol_get,
+               .put = wm_vol_put,
+               .private_value = (1 << 8) | 5
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Side Playback Switch",
+               .info = wm_mute_info,
+               .get = wm_mute_get,
+               .put = wm_mute_put,
+               .private_value = (2 << 8) | 6
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Side Playback Volume",
+               .info = wm_vol_info,
+               .get = wm_vol_get,
+               .put = wm_vol_put,
+               .private_value = (2 << 8) | 6
+       }
+};
+
+static snd_kcontrol_new_t wm_controls[] __devinitdata = {
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "PCM Playback Switch",
+               .info = wm_pcm_mute_info,
+               .get = wm_pcm_mute_get,
+               .put = wm_pcm_mute_put
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "PCM Playback Volume",
+               .info = wm_pcm_vol_info,
+               .get = wm_pcm_vol_get,
+               .put = wm_pcm_vol_put
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "DAC Deemphasis Switch",
+               .info = phase28_deemp_info,
+               .get = phase28_deemp_get,
+               .put = phase28_deemp_put
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "ADC Oversampling",
+               .info = phase28_oversampling_info,
+               .get = phase28_oversampling_get,
+               .put = phase28_oversampling_put
+       }
+};
+
+static int __devinit phase28_add_controls(ice1712_t *ice)
+{
+       unsigned int i, counts;
+       int err;
+
+       counts = ARRAY_SIZE(phase28_dac_controls);
+       for (i = 0; i < counts; i++) {
+               err = snd_ctl_add(ice->card, snd_ctl_new1(&phase28_dac_controls[i], ice));
+               if (err < 0)
+                       return err;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(wm_controls); i++) {
+               err = snd_ctl_add(ice->card, snd_ctl_new1(&wm_controls[i], ice));
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
 struct snd_ice1712_card_info snd_vt1724_phase_cards[] __devinitdata = {
        {
                .subvendor = VT1724_SUBDEVICE_PHASE22,
@@ -134,5 +853,14 @@ struct snd_ice1712_card_info snd_vt1724_phase_cards[] __devinitdata = {
                .eeprom_size = sizeof(phase22_eeprom),
                .eeprom_data = phase22_eeprom,
        },
+       {
+               .subvendor = VT1724_SUBDEVICE_PHASE28,
+               .name = "Terratec PHASE 28",
+               .model = "phase28",
+               .chip_init = phase28_init,
+               .build_controls = phase28_add_controls,
+               .eeprom_size = sizeof(phase28_eeprom),
+               .eeprom_data = phase28_eeprom,
+       },
        { } /* terminator */
 };
index 6230cf16989f74dfb5abe518cf0c194eff3e79ab..13e841b554887a8c6b8ae9eac8ff3d17c762087c 100644 (file)
  *
  */      
 
-#define PHASE_DEVICE_DESC "{Terratec,Phase 22},"
+#define PHASE_DEVICE_DESC "{Terratec,Phase 22},"\
+                          "{Terratec,Phase 28},"
 
 #define VT1724_SUBDEVICE_PHASE22       0x3b155011
+#define VT1724_SUBDEVICE_PHASE28       0x3b154911
 
 /* entry point */
 extern struct snd_ice1712_card_info snd_vt1724_phase_cards[];
 
+/* PHASE28 GPIO bits */
+#define PHASE28_SPI_MISO       (1 << 21)
+#define PHASE28_WM_RESET       (1 << 20)
+#define PHASE28_SPI_CLK                (1 << 19)
+#define PHASE28_SPI_MOSI       (1 << 18)
+#define PHASE28_WM_RW          (1 << 17)
+#define PHASE28_AC97_RESET     (1 << 16)
+#define PHASE28_DIGITAL_SEL1   (1 << 15)
+#define PHASE28_HP_SEL         (1 << 14)
+#define PHASE28_WM_CS          (1 << 12)
+#define PHASE28_AC97_COMMIT    (1 << 11)
+#define PHASE28_AC97_ADDR      (1 << 10)
+#define PHASE28_AC97_DATA_LOW  (1 << 9)
+#define PHASE28_AC97_DATA_HIGH (1 << 8)
+#define PHASE28_AC97_DATA_MASK 0xFF
 #endif /* __SOUND_PHASE */
index 3bd92627231c6e0beb1d4b1472c3712a900f26c5..ab61e383024f2218315332cac7213a585e902553 100644 (file)
@@ -110,6 +110,15 @@ struct snd_ice1712_card_info snd_vt1720_mobo_cards[] __devinitdata = {
                .eeprom_size = sizeof(k8x800_eeprom),
                .eeprom_data = k8x800_eeprom,
        },
+       {
+               .subvendor = VT1720_SUBDEVICE_SN25P,
+               .name = "Shuttle SN25P",
+               /* identical with k8x800 */
+               .chip_init = k8x800_init,
+               .build_controls = k8x800_add_controls,
+               .eeprom_size = sizeof(k8x800_eeprom),
+               .eeprom_data = k8x800_eeprom,
+       },
        { } /* terminator */
 };
 
index f949eb804cae03b182551e40006d93d2dc9b0b72..0b1b0ee1bea7aba566eec6206793009a44dd718c 100644 (file)
 #define VT1720_MOBO_DEVICE_DESC        "{Albatron,K8X800 Pro II},"\
                                       "{Chaintech,ZNF3-150},"\
                                       "{Chaintech,ZNF3-250},"\
-                                      "{Chaintech,9CJS},"
+                                      "{Chaintech,9CJS},"\
+                                      "{Shuttle,SN25P},"
 
 #define VT1720_SUBDEVICE_K8X800                0xf217052c
 #define VT1720_SUBDEVICE_ZNF3_150      0x0f2741f6
 #define VT1720_SUBDEVICE_ZNF3_250      0x0f2745f6
 #define VT1720_SUBDEVICE_9CJS          0x0f272327
+#define VT1720_SUBDEVICE_SN25P         0x97123650
 
 extern struct snd_ice1712_card_info  snd_vt1720_mobo_cards[];
 
index 8b33b12fa5dc7e65fa438efebcbdd88ab65be118..cc16f95f9ceff2b71cda1e227956cdc851b93daf 100644 (file)
@@ -1725,229 +1725,235 @@ static struct ac97_pcm ac97_pcm_defs[] __devinitdata = {
 
 static struct ac97_quirk ac97_quirks[] __devinitdata = {
        {
-               .vendor = 0x0e11,
-               .device = 0x008a,
+               .subvendor = 0x0e11,
+               .subdevice = 0x008a,
                .name = "Compaq Evo W4000",     /* AD1885 */
                .type = AC97_TUNE_HP_ONLY
        },
        {
-               .vendor = 0x0e11,
-               .device = 0x00b8,
+               .subvendor = 0x0e11,
+               .subdevice = 0x00b8,
                .name = "Compaq Evo D510C",
                .type = AC97_TUNE_HP_ONLY
        },
         {
-               .vendor = 0x0e11,
-               .device = 0x0860,
+               .subvendor = 0x0e11,
+               .subdevice = 0x0860,
                .name = "HP/Compaq nx7010",
                .type = AC97_TUNE_MUTE_LED
         },
        {
-               .vendor = 0x1014,
-               .device = 0x1f00,
+               .subvendor = 0x1014,
+               .subdevice = 0x1f00,
                .name = "MS-9128",
                .type = AC97_TUNE_ALC_JACK
        },
        {
-               .vendor = 0x1028,
-               .device = 0x00d8,
+               .subvendor = 0x1028,
+               .subdevice = 0x00d8,
                .name = "Dell Precision 530",   /* AD1885 */
                .type = AC97_TUNE_HP_ONLY
        },
        {
-               .vendor = 0x1028,
-               .device = 0x010d,
+               .subvendor = 0x1028,
+               .subdevice = 0x010d,
                .name = "Dell", /* which model?  AD1885 */
                .type = AC97_TUNE_HP_ONLY
        },
        {
-               .vendor = 0x1028,
-               .device = 0x0126,
+               .subvendor = 0x1028,
+               .subdevice = 0x0126,
                .name = "Dell Optiplex GX260",  /* AD1981A */
                .type = AC97_TUNE_HP_ONLY
        },
        {
-               .vendor = 0x1028,
-               .device = 0x012c,
+               .subvendor = 0x1028,
+               .subdevice = 0x012c,
                .name = "Dell Precision 650",   /* AD1981A */
                .type = AC97_TUNE_HP_ONLY
        },
        {
-               .vendor = 0x1028,
-               .device = 0x012d,
+               .subvendor = 0x1028,
+               .subdevice = 0x012d,
                .name = "Dell Precision 450",   /* AD1981B*/
                .type = AC97_TUNE_HP_ONLY
        },
        {
-               .vendor = 0x1028,
-               .device = 0x0147,
+               .subvendor = 0x1028,
+               .subdevice = 0x0147,
                .name = "Dell", /* which model?  AD1981B*/
                .type = AC97_TUNE_HP_ONLY
        },
        {
-               .vendor = 0x1028,
-               .device = 0x0163,
+               .subvendor = 0x1028,
+               .subdevice = 0x0163,
                .name = "Dell Unknown", /* STAC9750/51 */
                .type = AC97_TUNE_HP_ONLY
        },
        {
-               .vendor = 0x103c,
-               .device = 0x006d,
+               .subvendor = 0x103c,
+               .subdevice = 0x006d,
                .name = "HP zv5000",
                .type = AC97_TUNE_MUTE_LED      /*AD1981B*/
        },
        {       /* FIXME: which codec? */
-               .vendor = 0x103c,
-               .device = 0x00c3,
+               .subvendor = 0x103c,
+               .subdevice = 0x00c3,
                .name = "HP xw6000",
                .type = AC97_TUNE_HP_ONLY
        },
        {
-               .vendor = 0x103c,
-               .device = 0x088c,
+               .subvendor = 0x103c,
+               .subdevice = 0x088c,
                .name = "HP nc8000",
                .type = AC97_TUNE_MUTE_LED
        },
        {
-               .vendor = 0x103c,
-               .device = 0x0890,
+               .subvendor = 0x103c,
+               .subdevice = 0x0890,
                .name = "HP nc6000",
                .type = AC97_TUNE_MUTE_LED
        },
        {
-               .vendor = 0x103c,
-               .device = 0x129d,
+               .subvendor = 0x103c,
+               .subdevice = 0x129d,
                .name = "HP xw8000",
                .type = AC97_TUNE_HP_ONLY
        },
        {
-               .vendor = 0x103c,
-               .device = 0x12f1,
+               .subvendor = 0x103c,
+               .subdevice = 0x12f1,
                .name = "HP xw8200",    /* AD1981B*/
                .type = AC97_TUNE_HP_ONLY
        },
        {
-               .vendor = 0x103c,
-               .device = 0x12f2,
+               .subvendor = 0x103c,
+               .subdevice = 0x12f2,
                .name = "HP xw6200",
                .type = AC97_TUNE_HP_ONLY
        },
        {
-               .vendor = 0x103c,
-               .device = 0x3008,
+               .subvendor = 0x103c,
+               .subdevice = 0x3008,
                .name = "HP xw4200",    /* AD1981B*/
                .type = AC97_TUNE_HP_ONLY
        },
        {
-               .vendor = 0x104d,
-               .device = 0x8197,
+               .subvendor = 0x104d,
+               .subdevice = 0x8197,
                .name = "Sony S1XP",
                .type = AC97_TUNE_INV_EAPD
        },
        {
-               .vendor = 0x1043,
-               .device = 0x80f3,
+               .subvendor = 0x1043,
+               .subdevice = 0x80f3,
                .name = "ASUS ICH5/AD1985",
                .type = AC97_TUNE_AD_SHARING
        },
        {
-               .vendor = 0x10cf,
-               .device = 0x11c3,
+               .subvendor = 0x10cf,
+               .subdevice = 0x11c3,
                .name = "Fujitsu-Siemens E4010",
                .type = AC97_TUNE_HP_ONLY
        },
        {
-               .vendor = 0x10cf,
-               .device = 0x1253,
+               .subvendor = 0x10cf,
+               .subdevice = 0x1225,
+               .name = "Fujitsu-Siemens T3010",
+               .type = AC97_TUNE_HP_ONLY
+       },
+       {
+               .subvendor = 0x10cf,
+               .subdevice = 0x1253,
                .name = "Fujitsu S6210",        /* STAC9750/51 */
                .type = AC97_TUNE_HP_ONLY
        },
        {
-               .vendor = 0x10f1,
-               .device = 0x2665,
+               .subvendor = 0x10f1,
+               .subdevice = 0x2665,
                .name = "Fujitsu-Siemens Celsius",      /* AD1981? */
                .type = AC97_TUNE_HP_ONLY
        },
        {
-               .vendor = 0x10f1,
-               .device = 0x2885,
+               .subvendor = 0x10f1,
+               .subdevice = 0x2885,
                .name = "AMD64 Mobo",   /* ALC650 */
                .type = AC97_TUNE_HP_ONLY
        },
        {
-               .vendor = 0x110a,
-               .device = 0x0056,
+               .subvendor = 0x110a,
+               .subdevice = 0x0056,
                .name = "Fujitsu-Siemens Scenic",       /* AD1981? */
                .type = AC97_TUNE_HP_ONLY
        },
        {
-               .vendor = 0x11d4,
-               .device = 0x5375,
+               .subvendor = 0x11d4,
+               .subdevice = 0x5375,
                .name = "ADI AD1985 (discrete)",
                .type = AC97_TUNE_HP_ONLY
        },
        {
-               .vendor = 0x1462,
-               .device = 0x5470,
+               .subvendor = 0x1462,
+               .subdevice = 0x5470,
                .name = "MSI P4 ATX 645 Ultra",
                .type = AC97_TUNE_HP_ONLY
        },
        {
-               .vendor = 0x1734,
-               .device = 0x0088,
+               .subvendor = 0x1734,
+               .subdevice = 0x0088,
                .name = "Fujitsu-Siemens D1522",        /* AD1981 */
                .type = AC97_TUNE_HP_ONLY
        },
        {
-               .vendor = 0x8086,
-               .device = 0x2000,
+               .subvendor = 0x8086,
+               .subdevice = 0x2000,
                .mask = 0xfff0,
                .name = "Intel ICH5/AD1985",
                .type = AC97_TUNE_AD_SHARING
        },
        {
-               .vendor = 0x8086,
-               .device = 0x4000,
+               .subvendor = 0x8086,
+               .subdevice = 0x4000,
                .mask = 0xfff0,
                .name = "Intel ICH5/AD1985",
                .type = AC97_TUNE_AD_SHARING
        },
        {
-               .vendor = 0x8086,
-               .device = 0x4856,
+               .subvendor = 0x8086,
+               .subdevice = 0x4856,
                .name = "Intel D845WN (82801BA)",
                .type = AC97_TUNE_SWAP_HP
        },
        {
-               .vendor = 0x8086,
-               .device = 0x4d44,
+               .subvendor = 0x8086,
+               .subdevice = 0x4d44,
                .name = "Intel D850EMV2",       /* AD1885 */
                .type = AC97_TUNE_HP_ONLY
        },
        {
-               .vendor = 0x8086,
-               .device = 0x4d56,
+               .subvendor = 0x8086,
+               .subdevice = 0x4d56,
                .name = "Intel ICH/AD1885",
                .type = AC97_TUNE_HP_ONLY
        },
        {
-               .vendor = 0x8086,
-               .device = 0x6000,
+               .subvendor = 0x8086,
+               .subdevice = 0x6000,
                .mask = 0xfff0,
                .name = "Intel ICH5/AD1985",
                .type = AC97_TUNE_AD_SHARING
        },
        {
-               .vendor = 0x8086,
-               .device = 0xe000,
+               .subvendor = 0x8086,
+               .subdevice = 0xe000,
                .mask = 0xfff0,
                .name = "Intel ICH5/AD1985",
                .type = AC97_TUNE_AD_SHARING
        },
 #if 0 /* FIXME: this seems wrong on most boards */
        {
-               .vendor = 0x8086,
-               .device = 0xa000,
+               .subvendor = 0x8086,
+               .subdevice = 0xa000,
                .mask = 0xfff0,
                .name = "Intel ICH5/AD1985",
                .type = AC97_TUNE_HP_ONLY
@@ -2849,7 +2855,7 @@ static struct pci_driver driver = {
 
 static int __init alsa_card_intel8x0_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_intel8x0_exit(void)
index 67da096d659b3d2e35ce894f0979d0b353bf9d27..bb758c77d211067ab49979f0fb962f7db654ae2c 100644 (file)
@@ -35,7 +35,6 @@
 #include <sound/pcm.h>
 #include <sound/ac97_codec.h>
 #include <sound/info.h>
-#include <sound/control.h>
 #include <sound/initval.h>
 
 MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
@@ -292,60 +291,9 @@ static struct pci_device_id snd_intel8x0m_ids[] = {
 #endif
        { 0, }
 };
-static int snd_intel8x0m_switch_default_get(snd_kcontrol_t *kcontrol,
-                                           snd_ctl_elem_value_t *ucontrol);
-static int snd_intel8x0m_switch_default_put(snd_kcontrol_t *kcontrol,
-                                           snd_ctl_elem_value_t *ucontrol);
-static int snd_intel8x0m_switch_default_info(snd_kcontrol_t *kcontrol,
-                                            snd_ctl_elem_info_t *uinfo);
-
-#define PRIVATE_VALUE_INITIALIZER(r,m) (((r) & 0xffff) << 16 | ((m) & 0xffff))
-#define PRIVATE_VALUE_MASK(control) ((control)->private_value & 0xffff)
-#define PRIVATE_VALUE_REG(control) (((control)->private_value >> 16) & 0xffff)
-
-static snd_kcontrol_new_t snd_intel8x0m_mixer_switches[] __devinitdata = {
-  { .name  = "Off-hook Switch",
-    .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-    .info  = snd_intel8x0m_switch_default_info,
-    .get   = snd_intel8x0m_switch_default_get,
-    .put   = snd_intel8x0m_switch_default_put,
-    .private_value = PRIVATE_VALUE_INITIALIZER(AC97_GPIO_STATUS,AC97_GPIO_LINE1_OH)
-  }
-};
 
 MODULE_DEVICE_TABLE(pci, snd_intel8x0m_ids);
 
-static int snd_intel8x0m_switch_default_info(snd_kcontrol_t *kcontrol,
-                                            snd_ctl_elem_info_t *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 snd_intel8x0m_switch_default_get(snd_kcontrol_t *kcontrol,
-                                           snd_ctl_elem_value_t *ucontrol)
-{
-       unsigned short mask = PRIVATE_VALUE_MASK(kcontrol);
-       unsigned short reg = PRIVATE_VALUE_REG(kcontrol);
-       intel8x0_t *chip = snd_kcontrol_chip(kcontrol);
-       unsigned int status;
-       status = snd_ac97_read(chip->ac97, reg) & mask ? 1 : 0;
-       ucontrol->value.integer.value[0] = status;
-       return 0;
-}
-static int snd_intel8x0m_switch_default_put(snd_kcontrol_t *kcontrol,
-                                           snd_ctl_elem_value_t *ucontrol)
-{
-       unsigned short mask = PRIVATE_VALUE_MASK(kcontrol);
-       unsigned short reg = PRIVATE_VALUE_REG(kcontrol);
-       intel8x0_t *chip = snd_kcontrol_chip(kcontrol);
-       unsigned short new_status = ucontrol->value.integer.value[0] ? mask : ~mask;
-       return snd_ac97_update_bits(chip->ac97, reg,
-                                   mask, new_status);
-}
 /*
  *  Lowlevel I/O - busmaster
  */
@@ -500,6 +448,8 @@ static unsigned short snd_intel8x0_codec_read(ac97_t *ac97,
                        res = 0xffff;
                }
        }
+       if (reg == AC97_GPIO_STATUS)
+               iagetword(chip, 0); /* clear semaphore */
        return res;
 }
 
@@ -698,21 +648,6 @@ static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(snd_pcm_substream_t * substrea
        return bytes_to_frames(substream->runtime, ptr);
 }
 
-static int snd_intel8x0m_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
-{
-       /* hook off/on on start/stop */
-       /* Moved this to mixer control */
-       switch (cmd) {
-       case SNDRV_PCM_TRIGGER_START:
-               break;
-       case SNDRV_PCM_TRIGGER_STOP:
-               break;
-       default:
-               return -EINVAL;
-       }
-       return snd_intel8x0_pcm_trigger(substream,cmd);
-}
-
 static int snd_intel8x0m_pcm_prepare(snd_pcm_substream_t * substream)
 {
        intel8x0_t *chip = snd_pcm_substream_chip(substream);
@@ -808,7 +743,7 @@ static snd_pcm_ops_t snd_intel8x0m_playback_ops = {
        .hw_params =    snd_intel8x0_hw_params,
        .hw_free =      snd_intel8x0_hw_free,
        .prepare =      snd_intel8x0m_pcm_prepare,
-       .trigger =      snd_intel8x0m_pcm_trigger,
+       .trigger =      snd_intel8x0_pcm_trigger,
        .pointer =      snd_intel8x0_pcm_pointer,
 };
 
@@ -819,7 +754,7 @@ static snd_pcm_ops_t snd_intel8x0m_capture_ops = {
        .hw_params =    snd_intel8x0_hw_params,
        .hw_free =      snd_intel8x0_hw_free,
        .prepare =      snd_intel8x0m_pcm_prepare,
-       .trigger =      snd_intel8x0m_pcm_trigger,
+       .trigger =      snd_intel8x0_pcm_trigger,
        .pointer =      snd_intel8x0_pcm_pointer,
 };
 
@@ -947,7 +882,6 @@ static int __devinit snd_intel8x0_mixer(intel8x0_t *chip, int ac97_clock)
        ac97_t *x97;
        int err;
        unsigned int glob_sta = 0;
-       unsigned int idx;
        static ac97_bus_ops_t ops = {
                .write = snd_intel8x0_codec_write,
                .read = snd_intel8x0_codec_read,
@@ -983,10 +917,6 @@ static int __devinit snd_intel8x0_mixer(intel8x0_t *chip, int ac97_clock)
                chip->ichd[ICHD_MDMIN].ac97 = x97;
                chip->ichd[ICHD_MDMOUT].ac97 = x97;
        }
-       for (idx = 0; idx < ARRAY_SIZE(snd_intel8x0m_mixer_switches); idx++) {
-               if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_intel8x0m_mixer_switches[idx], chip))) < 0)
-                       goto __err;
-       }
 
        chip->in_ac97_init = 0;
        return 0;
@@ -1450,7 +1380,7 @@ static struct pci_driver driver = {
 
 static int __init alsa_card_intel8x0m_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_intel8x0m_exit(void)
index bb1de20081760d7857652b85e2db3164c305e3c0..79d8eda54f0d48dfa60690db217f7251d451bcfd 100644 (file)
@@ -2541,7 +2541,7 @@ static struct pci_driver driver = {
 
 static int __init alsa_card_korg1212_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_korg1212_exit(void)
index 2cf33083d7ccb0b3a7c04090bec1f63fc4f2e969..096f15132853b49e61d7e8c13b361d3f2c0e5334 100644 (file)
@@ -779,6 +779,12 @@ struct m3_quirk {
                                   (e.g. for IrDA on Dell Inspirons) */
 };
 
+struct m3_hv_quirk {
+       u16 vendor, device, subsystem_vendor, subsystem_device;
+       u32 config;             /* ALLEGRO_CONFIG hardware volume bits */
+       int is_omnibook;        /* Do HP OmniBook GPIO magic? */
+};
+
 struct m3_list {
        int curlen;
        int mem_addr;
@@ -828,6 +834,7 @@ struct snd_m3 {
 
        struct pci_dev *pci;
        struct m3_quirk *quirk;
+       struct m3_hv_quirk *hv_quirk;
 
        int dacs_active;
        int timer_users;
@@ -851,6 +858,11 @@ struct snd_m3 {
        m3_dma_t *substreams;
 
        spinlock_t reg_lock;
+       spinlock_t ac97_lock;
+
+       snd_kcontrol_t *master_switch;
+       snd_kcontrol_t *master_volume;
+       struct tasklet_struct hwvol_tq;
 
 #ifdef CONFIG_PM
        u16 *suspend_mem;
@@ -968,6 +980,71 @@ static struct m3_quirk m3_quirk_list[] = {
        { NULL }
 };
 
+/* These values came from the Windows driver. */
+static struct m3_hv_quirk m3_hv_quirk_list[] = {
+       /* Allegro chips */
+       { 0x125D, 0x1988, 0x0E11, 0x002E, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+       { 0x125D, 0x1988, 0x0E11, 0x0094, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+       { 0x125D, 0x1988, 0x0E11, 0xB112, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+       { 0x125D, 0x1988, 0x0E11, 0xB114, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+       { 0x125D, 0x1988, 0x103C, 0x0012, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+       { 0x125D, 0x1988, 0x103C, 0x0018, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+       { 0x125D, 0x1988, 0x103C, 0x001C, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+       { 0x125D, 0x1988, 0x103C, 0x001D, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+       { 0x125D, 0x1988, 0x103C, 0x001E, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+       { 0x125D, 0x1988, 0x107B, 0x3350, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+       { 0x125D, 0x1988, 0x10F7, 0x8338, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+       { 0x125D, 0x1988, 0x10F7, 0x833C, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+       { 0x125D, 0x1988, 0x10F7, 0x833D, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+       { 0x125D, 0x1988, 0x10F7, 0x833E, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+       { 0x125D, 0x1988, 0x10F7, 0x833F, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+       { 0x125D, 0x1988, 0x13BD, 0x1018, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+       { 0x125D, 0x1988, 0x13BD, 0x1019, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+       { 0x125D, 0x1988, 0x13BD, 0x101A, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+       { 0x125D, 0x1988, 0x14FF, 0x0F03, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+       { 0x125D, 0x1988, 0x14FF, 0x0F04, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+       { 0x125D, 0x1988, 0x14FF, 0x0F05, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+       { 0x125D, 0x1988, 0x156D, 0xB400, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+       { 0x125D, 0x1988, 0x156D, 0xB795, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+       { 0x125D, 0x1988, 0x156D, 0xB797, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+       { 0x125D, 0x1988, 0x156D, 0xC700, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
+       { 0x125D, 0x1988, 0x1033, 0x80F1, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+       { 0x125D, 0x1988, 0x103C, 0x001A, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 }, /* HP OmniBook 6100 */
+       { 0x125D, 0x1988, 0x107B, 0x340A, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+       { 0x125D, 0x1988, 0x107B, 0x3450, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+       { 0x125D, 0x1988, 0x109F, 0x3134, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+       { 0x125D, 0x1988, 0x109F, 0x3161, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+       { 0x125D, 0x1988, 0x144D, 0x3280, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+       { 0x125D, 0x1988, 0x144D, 0x3281, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+       { 0x125D, 0x1988, 0x144D, 0xC002, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+       { 0x125D, 0x1988, 0x144D, 0xC003, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+       { 0x125D, 0x1988, 0x1509, 0x1740, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+       { 0x125D, 0x1988, 0x1610, 0x0010, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD | REDUCED_DEBOUNCE, 0 },
+       { 0x125D, 0x1988, 0x1042, 0x1042, HV_CTRL_ENABLE, 0 },
+       { 0x125D, 0x1988, 0x107B, 0x9500, HV_CTRL_ENABLE, 0 },
+       { 0x125D, 0x1988, 0x14FF, 0x0F06, HV_CTRL_ENABLE, 0 },
+       { 0x125D, 0x1988, 0x1558, 0x8586, HV_CTRL_ENABLE, 0 },
+       { 0x125D, 0x1988, 0x161F, 0x2011, HV_CTRL_ENABLE, 0 },
+       /* Maestro3 chips */
+       { 0x125D, 0x1998, 0x103C, 0x000E, HV_CTRL_ENABLE, 0 },
+       { 0x125D, 0x1998, 0x103C, 0x0010, HV_CTRL_ENABLE, 1 }, /* HP OmniBook 6000 */
+       { 0x125D, 0x1998, 0x103C, 0x0011, HV_CTRL_ENABLE, 1 }, /* HP OmniBook 500 */
+       { 0x125D, 0x1998, 0x103C, 0x001B, HV_CTRL_ENABLE, 0 },
+       { 0x125D, 0x1998, 0x104D, 0x80A6, HV_CTRL_ENABLE, 0 },
+       { 0x125D, 0x1998, 0x104D, 0x80AA, HV_CTRL_ENABLE, 0 },
+       { 0x125D, 0x1998, 0x107B, 0x5300, HV_CTRL_ENABLE, 0 },
+       { 0x125D, 0x1998, 0x110A, 0x1998, HV_CTRL_ENABLE, 0 },
+       { 0x125D, 0x1998, 0x13BD, 0x1015, HV_CTRL_ENABLE, 0 },
+       { 0x125D, 0x1998, 0x13BD, 0x101C, HV_CTRL_ENABLE, 0 },
+       { 0x125D, 0x1998, 0x13BD, 0x1802, HV_CTRL_ENABLE, 0 },
+       { 0x125D, 0x1998, 0x1599, 0x0715, HV_CTRL_ENABLE, 0 },
+       { 0x125D, 0x1998, 0x5643, 0x5643, HV_CTRL_ENABLE, 0 },
+       { 0x125D, 0x199A, 0x144D, 0x3260, HV_CTRL_ENABLE | REDUCED_DEBOUNCE, 0 },
+       { 0x125D, 0x199A, 0x144D, 0x3261, HV_CTRL_ENABLE | REDUCED_DEBOUNCE, 0 },
+       { 0x125D, 0x199A, 0x144D, 0xC000, HV_CTRL_ENABLE | REDUCED_DEBOUNCE, 0 },
+       { 0x125D, 0x199A, 0x144D, 0xC001, HV_CTRL_ENABLE | REDUCED_DEBOUNCE, 0 },
+       { 0 }
+};
 
 /*
  * lowlevel functions
@@ -1565,6 +1642,68 @@ static void snd_m3_update_ptr(m3_t *chip, m3_dma_t *s)
        }
 }
 
+static void snd_m3_update_hw_volume(unsigned long private_data)
+{
+       m3_t *chip = (m3_t *) private_data;
+       int x, val;
+       unsigned long flags;
+
+       /* Figure out which volume control button was pushed,
+          based on differences from the default register
+          values. */
+       x = inb(chip->iobase + SHADOW_MIX_REG_VOICE) & 0xee;
+
+       /* Reset the volume control registers. */
+       outb(0x88, chip->iobase + SHADOW_MIX_REG_VOICE);
+       outb(0x88, chip->iobase + HW_VOL_COUNTER_VOICE);
+       outb(0x88, chip->iobase + SHADOW_MIX_REG_MASTER);
+       outb(0x88, chip->iobase + HW_VOL_COUNTER_MASTER);
+
+       if (!chip->master_switch || !chip->master_volume)
+               return;
+
+       /* FIXME: we can't call snd_ac97_* functions since here is in tasklet. */
+       spin_lock_irqsave(&chip->ac97_lock, flags);
+
+       val = chip->ac97->regs[AC97_MASTER_VOL];
+       switch (x) {
+       case 0x88:
+               /* mute */
+               val ^= 0x8000;
+               chip->ac97->regs[AC97_MASTER_VOL] = val;
+               outw(val, chip->iobase + CODEC_DATA);
+               outb(AC97_MASTER_VOL, chip->iobase + CODEC_COMMAND);
+               snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                              &chip->master_switch->id);
+               break;
+       case 0xaa:
+               /* volume up */
+               if ((val & 0x7f) > 0)
+                       val--;
+               if ((val & 0x7f00) > 0)
+                       val -= 0x0100;
+               chip->ac97->regs[AC97_MASTER_VOL] = val;
+               outw(val, chip->iobase + CODEC_DATA);
+               outb(AC97_MASTER_VOL, chip->iobase + CODEC_COMMAND);
+               snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                              &chip->master_volume->id);
+               break;
+       case 0x66:
+               /* volume down */
+               if ((val & 0x7f) < 0x1f)
+                       val++;
+               if ((val & 0x7f00) < 0x1f00)
+                       val += 0x0100;
+               chip->ac97->regs[AC97_MASTER_VOL] = val;
+               outw(val, chip->iobase + CODEC_DATA);
+               outb(AC97_MASTER_VOL, chip->iobase + CODEC_COMMAND);
+               snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                              &chip->master_volume->id);
+               break;
+       }
+       spin_unlock_irqrestore(&chip->ac97_lock, flags);
+}
+
 static irqreturn_t
 snd_m3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
@@ -1576,7 +1715,10 @@ snd_m3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 
        if (status == 0xff)
                return IRQ_NONE;
-   
+
+       if (status & HV_INT_PENDING)
+               tasklet_hi_schedule(&chip->hwvol_tq);
+
        /*
         * ack an assp int if its running
         * and has an int pending
@@ -1605,7 +1747,7 @@ snd_m3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 #endif
 
        /* ack ints */
-       snd_m3_outw(chip, HOST_INT_STATUS, status);
+       outb(status, chip->iobase + HOST_INT_STATUS);
 
        return IRQ_HANDLED;
 }
@@ -1842,24 +1984,32 @@ static unsigned short
 snd_m3_ac97_read(ac97_t *ac97, unsigned short reg)
 {
        m3_t *chip = ac97->private_data;
+       unsigned long flags;
+       unsigned short data;
 
        if (snd_m3_ac97_wait(chip))
                return 0xffff;
+       spin_lock_irqsave(&chip->ac97_lock, flags);
        snd_m3_outb(chip, 0x80 | (reg & 0x7f), CODEC_COMMAND);
        if (snd_m3_ac97_wait(chip))
                return 0xffff;
-       return snd_m3_inw(chip, CODEC_DATA);
+       data = snd_m3_inw(chip, CODEC_DATA);
+       spin_unlock_irqrestore(&chip->ac97_lock, flags);
+       return data;
 }
 
 static void
 snd_m3_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val)
 {
        m3_t *chip = ac97->private_data;
+       unsigned long flags;
 
        if (snd_m3_ac97_wait(chip))
                return;
+       spin_lock_irqsave(&chip->ac97_lock, flags);
        snd_m3_outw(chip, val, CODEC_DATA);
        snd_m3_outb(chip, reg & 0x7f, CODEC_COMMAND);
+       spin_unlock_irqrestore(&chip->ac97_lock, flags);
 }
 
 
@@ -1968,6 +2118,7 @@ static int __devinit snd_m3_mixer(m3_t *chip)
 {
        ac97_bus_t *pbus;
        ac97_template_t ac97;
+       snd_ctl_elem_id_t id;
        int err;
        static ac97_bus_ops_t ops = {
                .write = snd_m3_ac97_write,
@@ -1988,6 +2139,15 @@ static int __devinit snd_m3_mixer(m3_t *chip)
        schedule_timeout(HZ / 10);
        snd_ac97_write(chip->ac97, AC97_PCM, 0);
 
+       memset(&id, 0, sizeof(id));
+       id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+       strcpy(id.name, "Master Playback Switch");
+       chip->master_switch = snd_ctl_find_id(chip->card, &id);
+       memset(&id, 0, sizeof(id));
+       id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+       strcpy(id.name, "Master Playback Volume");
+       chip->master_volume = snd_ctl_find_id(chip->card, &id);
+
        return 0;
 }
 
@@ -2293,6 +2453,7 @@ static int
 snd_m3_chip_init(m3_t *chip)
 {
        struct pci_dev *pcidev = chip->pci;
+       unsigned long io = chip->iobase;
        u32 n;
        u16 w;
        u8 t; /* makes as much sense as 'n', no? */
@@ -2303,8 +2464,27 @@ snd_m3_chip_init(m3_t *chip)
               DISABLE_LEGACY);
        pci_write_config_word(pcidev, PCI_LEGACY_AUDIO_CTRL, w);
 
+       if (chip->hv_quirk && chip->hv_quirk->is_omnibook) {
+               /*
+                * Volume buttons on some HP OmniBook laptops don't work
+                * correctly. This makes them work for the most part.
+                *
+                * Volume up and down buttons on the laptop side work.
+                * Fn+cursor_up (volme up) works.
+                * Fn+cursor_down (volume down) doesn't work.
+                * Fn+F7 (mute) works acts as volume up.
+                */
+               outw(~(GPI_VOL_DOWN|GPI_VOL_UP), io + GPIO_MASK);
+               outw(inw(io + GPIO_DIRECTION) & ~(GPI_VOL_DOWN|GPI_VOL_UP), io + GPIO_DIRECTION);
+               outw((GPI_VOL_DOWN|GPI_VOL_UP), io + GPIO_DATA);
+               outw(0xffff, io + GPIO_MASK);
+       }
        pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n);
-       n &= REDUCED_DEBOUNCE;
+       n &= ~(HV_CTRL_ENABLE | REDUCED_DEBOUNCE | HV_BUTTON_FROM_GD);
+       if (chip->hv_quirk)
+               n |= chip->hv_quirk->config;
+       /* For some reason we must always use reduced debounce. */
+       n |= REDUCED_DEBOUNCE;
        n |= PM_CTRL_ENABLE | CLK_DIV_BY_49 | USE_PCI_TIMING;
        pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n);
 
@@ -2332,6 +2512,12 @@ snd_m3_chip_init(m3_t *chip)
 
        outb(RUN_ASSP, chip->iobase + ASSP_CONTROL_B); 
 
+       outb(0x00, io + HARDWARE_VOL_CTRL);
+       outb(0x88, io + SHADOW_MIX_REG_VOICE);
+       outb(0x88, io + HW_VOL_COUNTER_VOICE);
+       outb(0x88, io + SHADOW_MIX_REG_MASTER);
+       outb(0x88, io + HW_VOL_COUNTER_MASTER);
+
        return 0;
 } 
 
@@ -2341,7 +2527,7 @@ snd_m3_enable_ints(m3_t *chip)
        unsigned long io = chip->iobase;
 
        /* TODO: MPU401 not supported yet */
-       outw(ASSP_INT_ENABLE /*| MPU401_INT_ENABLE*/, io + HOST_INT_CTRL);
+       outw(ASSP_INT_ENABLE | HV_INT_ENABLE /*| MPU401_INT_ENABLE*/, io + HOST_INT_CTRL);
        outb(inb(io + ASSP_CONTROL_C) | ASSP_HOST_INT_ENABLE,
             io + ASSP_CONTROL_C);
 }
@@ -2367,7 +2553,7 @@ static int snd_m3_free(m3_t *chip)
                kfree(chip->substreams);
        }
        if (chip->iobase) {
-               snd_m3_outw(chip, HOST_INT_CTRL, 0); /* disable ints */
+               outw(0, chip->iobase + HOST_INT_CTRL); /* disable ints */
        }
 
 #ifdef CONFIG_PM
@@ -2486,7 +2672,7 @@ snd_m3_create(snd_card_t *card, struct pci_dev *pci,
        m3_t *chip;
        int i, err;
        struct m3_quirk *quirk;
-       u16 subsystem_vendor, subsystem_device;
+       struct m3_hv_quirk *hv_quirk;
        static snd_device_ops_t ops = {
                .dev_free =     snd_m3_dev_free,
        };
@@ -2524,18 +2710,25 @@ snd_m3_create(snd_card_t *card, struct pci_dev *pci,
        chip->pci = pci;
        chip->irq = -1;
 
-       pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor);
-       pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsystem_device);
-
        for (quirk = m3_quirk_list; quirk->vendor; quirk++) {
-               if (subsystem_vendor == quirk->vendor &&
-                   subsystem_device == quirk->device) {
+               if (pci->subsystem_vendor == quirk->vendor &&
+                   pci->subsystem_device == quirk->device) {
                        printk(KERN_INFO "maestro3: enabled hack for '%s'\n", quirk->name);
                        chip->quirk = quirk;
                        break;
                }
        }
 
+       for (hv_quirk = m3_hv_quirk_list; hv_quirk->vendor; hv_quirk++) {
+               if (pci->vendor == hv_quirk->vendor &&
+                   pci->device == hv_quirk->device &&
+                   pci->subsystem_vendor == hv_quirk->subsystem_vendor &&
+                   pci->subsystem_device == hv_quirk->subsystem_device) {
+                       chip->hv_quirk = hv_quirk;
+                       break;
+               }
+       }
+
        chip->external_amp = enable_amp;
        if (amp_gpio >= 0 && amp_gpio <= 0x0f)
                chip->amp_gpio = amp_gpio;
@@ -2593,6 +2786,9 @@ snd_m3_create(snd_card_t *card, struct pci_dev *pci,
                return err;
        }
 
+       spin_lock_init(&chip->ac97_lock);
+       tasklet_init(&chip->hwvol_tq, snd_m3_update_hw_volume, (unsigned long)chip);
+
        if ((err = snd_m3_mixer(chip)) < 0)
                return err;
 
@@ -2702,7 +2898,7 @@ static struct pci_driver driver = {
        
 static int __init alsa_card_m3_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_m3_exit(void)
index 65bb0f47af2c1fffb0bfe641441ece2d06823d7b..082c0d0f73d2ff6afea55c342d9517483efbeed3 100644 (file)
@@ -1431,7 +1431,7 @@ static struct pci_driver driver = {
 
 static int __init alsa_card_mixart_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_mixart_exit(void)
index 356fbeac6f9e025143f724bb13402830f7e4560a..8a52091f8552a2b1f42bafb821368eb5755adc41 100644 (file)
@@ -1645,7 +1645,7 @@ static struct pci_driver driver = {
 
 static int __init alsa_card_nm256_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_nm256_exit(void)
index b96acd5a57dbdfbf246efdbc36820249153ea7e2..b7b554df6705b368e4610e71ad9552dc6e79965e 100644 (file)
@@ -2031,7 +2031,7 @@ static struct pci_driver driver = {
 
 static int __init alsa_card_rme32_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_rme32_exit(void)
index 8e2666841d21154204b6158282e8586d283881ba..10c4f45a913c7bc449c8403d03edb3f7f987cfac 100644 (file)
@@ -2437,7 +2437,7 @@ static struct pci_driver driver = {
 
 static int __init alsa_card_rme96_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_rme96_exit(void)
index 917374c9cd40763c7013b669f14ca2152a520158..d2c294e136f9c05f225e74e2661b4c39698faa22 100644 (file)
@@ -5,7 +5,9 @@
 
 snd-rme9652-objs := rme9652.o
 snd-hdsp-objs := hdsp.o
+snd-hdspm-objs := hdspm.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_RME9652) += snd-rme9652.o
 obj-$(CONFIG_SND_HDSP) += snd-hdsp.o
+obj-$(CONFIG_SND_HDSPM) +=snd-hdspm.o
index 12efbf0fab549241de4fdfb32f129a1d87eefb4b..a673cc438b917fb0f5ced486e4ceab17c7e53e27 100644 (file)
@@ -559,18 +559,22 @@ static int snd_hammerfall_get_buffer(struct pci_dev *pci, struct snd_dma_buffer
 {
        dmab->dev.type = SNDRV_DMA_TYPE_DEV;
        dmab->dev.dev = snd_dma_pci_data(pci);
-       if (! snd_dma_get_reserved_buf(dmab, snd_dma_pci_buf_id(pci))) {
-               if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
-                                       size, dmab) < 0)
-                       return -ENOMEM;
+       if (snd_dma_get_reserved_buf(dmab, snd_dma_pci_buf_id(pci))) {
+               if (dmab->bytes >= size)
+                       return 0;
        }
+       if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+                               size, dmab) < 0)
+               return -ENOMEM;
        return 0;
 }
 
 static void snd_hammerfall_free_buffer(struct snd_dma_buffer *dmab, struct pci_dev *pci)
 {
-       if (dmab->area)
+       if (dmab->area) {
+               dmab->dev.dev = NULL; /* make it anonymous */
                snd_dma_reserve_buf(dmab, snd_dma_pci_buf_id(pci));
+       }
 }
 
 
@@ -4912,19 +4916,9 @@ static int __devinit hdsp_request_fw_loader(hdsp_t *hdsp)
                release_firmware(fw);
                return -EINVAL;
        }
-#ifdef SNDRV_BIG_ENDIAN
-       {
-               int i;
-               u32 *src = (u32*)fw->data;
-               for (i = 0; i < ARRAY_SIZE(hdsp->firmware_cache); i++, src++)
-                       hdsp->firmware_cache[i] = ((*src & 0x000000ff) << 16) |
-                               ((*src & 0x0000ff00) << 8)  |
-                               ((*src & 0x00ff0000) >> 8)  |
-                               ((*src & 0xff000000) >> 16);
-       }
-#else
+
        memcpy(hdsp->firmware_cache, fw->data, sizeof(hdsp->firmware_cache));
-#endif
+
        release_firmware(fw);
                
        hdsp->state |= HDSP_FirmwareCached;
@@ -5194,7 +5188,7 @@ static struct pci_driver driver = {
 
 static int __init alsa_card_hdsp_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_hdsp_exit(void)
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
new file mode 100644 (file)
index 0000000..9e86d0e
--- /dev/null
@@ -0,0 +1,3671 @@
+/*   -*- linux-c -*-
+ *
+ *   ALSA driver for RME Hammerfall DSP MADI audio interface(s)
+ *
+ *      Copyright (c) 2003 Winfried Ritsch (IEM)
+ *      code based on hdsp.c   Paul Davis
+ *                             Marcus Andersson
+ *                             Thomas Charbonnel
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/info.h>
+#include <sound/asoundef.h>
+#include <sound/rawmidi.h>
+#include <sound/hwdep.h>
+#include <sound/initval.h>
+
+#include <sound/hdspm.h>
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;       /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;        /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */
+
+/* Disable precise pointer at start */
+static int precise_ptr[SNDRV_CARDS];
+
+/* Send all playback to line outs */
+static int line_outs_monitor[SNDRV_CARDS];
+
+/* Enable Analog Outs on Channel 63/64 by default */
+static int enable_monitor[SNDRV_CARDS];
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for RME HDSPM interface.");
+
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for RME HDSPM interface.");
+
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable/disable specific HDSPM soundcards.");
+
+module_param_array(precise_ptr, bool, NULL, 0444);
+MODULE_PARM_DESC(precise_ptr, "Enable precise pointer, or disable.");
+
+module_param_array(line_outs_monitor, bool, NULL, 0444);
+MODULE_PARM_DESC(line_outs_monitor,
+                "Send playback streams to analog outs by default.");
+
+module_param_array(enable_monitor, bool, NULL, 0444);
+MODULE_PARM_DESC(enable_monitor,
+                "Enable Analog Out on Channel 63/64 by default.");
+
+MODULE_AUTHOR
+      ("Winfried Ritsch <ritsch_AT_iem.at>, Paul Davis <paul@linuxaudiosystems.com>, "
+       "Marcus Andersson, Thomas Charbonnel <thomas@undata.org>");
+MODULE_DESCRIPTION("RME HDSPM");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
+
+/* --- Write registers. --- 
+  These are defined as byte-offsets from the iobase value.  */
+
+#define HDSPM_controlRegister       64
+#define HDSPM_interruptConfirmation  96
+#define HDSPM_control2Reg           256  /* not in specs ???????? */
+#define HDSPM_midiDataOut0          352  /* just believe in old code */
+#define HDSPM_midiDataOut1          356
+
+/* DMA enable for 64 channels, only Bit 0 is relevant */
+#define HDSPM_outputEnableBase       512  /* 512-767  input  DMA */ 
+#define HDSPM_inputEnableBase        768  /* 768-1023 output DMA */
+
+/* 16 page addresses for each of the 64 channels DMA buffer in and out 
+   (each 64k=16*4k) Buffer must be 4k aligned (which is default i386 ????) */
+#define HDSPM_pageAddressBufferOut       8192
+#define HDSPM_pageAddressBufferIn        (HDSPM_pageAddressBufferOut+64*16*4)
+
+#define HDSPM_MADI_mixerBase    32768  /* 32768-65535 for 2x64x64 Fader */
+
+#define HDSPM_MATRIX_MIXER_SIZE  8192  /* = 2*64*64 * 4 Byte => 32kB */
+
+/* --- Read registers. ---
+   These are defined as byte-offsets from the iobase value */
+#define HDSPM_statusRegister    0
+#define HDSPM_statusRegister2  96
+
+#define HDSPM_midiDataIn0     360
+#define HDSPM_midiDataIn1     364
+
+/* status is data bytes in MIDI-FIFO (0-128) */
+#define HDSPM_midiStatusOut0  384      
+#define HDSPM_midiStatusOut1  388      
+#define HDSPM_midiStatusIn0   392      
+#define HDSPM_midiStatusIn1   396      
+
+
+/* the meters are regular i/o-mapped registers, but offset
+   considerably from the rest. the peak registers are reset
+   when read; the least-significant 4 bits are full-scale counters; 
+   the actual peak value is in the most-significant 24 bits.
+*/
+#define HDSPM_MADI_peakrmsbase         4096    /* 4096-8191 2x64x32Bit Meters */
+
+/* --- Control Register bits --------- */
+#define HDSPM_Start                (1<<0) /* start engine */
+
+#define HDSPM_Latency0             (1<<1) /* buffer size = 2^n */
+#define HDSPM_Latency1             (1<<2) /* where n is defined */
+#define HDSPM_Latency2             (1<<3) /* by Latency{2,1,0} */
+
+#define HDSPM_ClockModeMaster      (1<<4) /* 1=Master, 0=Slave/Autosync */
+
+#define HDSPM_AudioInterruptEnable (1<<5) /* what do you think ? */
+
+#define HDSPM_Frequency0  (1<<6)  /* 0=44.1kHz/88.2kHz 1=48kHz/96kHz */
+#define HDSPM_Frequency1  (1<<7)  /* 0=32kHz/64kHz */
+#define HDSPM_DoubleSpeed (1<<8)  /* 0=normal speed, 1=double speed */
+#define HDSPM_QuadSpeed   (1<<31) /* quad speed bit, not implemented now */
+
+#define HDSPM_TX_64ch     (1<<10) /* Output 64channel MODE=1,
+                                    56channelMODE=0 */
+
+#define HDSPM_AutoInp     (1<<11) /* Auto Input (takeover) == Safe Mode, 
+                                     0=off, 1=on  */
+
+#define HDSPM_InputSelect0 (1<<14) /* Input select 0= optical, 1=coax */
+#define HDSPM_InputSelect1 (1<<15) /* should be 0 */
+
+#define HDSPM_SyncRef0     (1<<16) /* 0=WOrd, 1=MADI */
+#define HDSPM_SyncRef1     (1<<17) /* should be 0 */
+
+#define HDSPM_clr_tms      (1<<19) /* clear track marker, do not use 
+                                      AES additional bits in
+                                     lower 5 Audiodatabits ??? */
+
+#define HDSPM_Midi0InterruptEnable (1<<22)
+#define HDSPM_Midi1InterruptEnable (1<<23)
+
+#define HDSPM_LineOut (1<<24) /* Analog Out on channel 63/64 on=1, mute=0 */
+
+
+/* --- bit helper defines */
+#define HDSPM_LatencyMask    (HDSPM_Latency0|HDSPM_Latency1|HDSPM_Latency2)
+#define HDSPM_FrequencyMask  (HDSPM_Frequency0|HDSPM_Frequency1)
+#define HDSPM_InputMask      (HDSPM_InputSelect0|HDSPM_InputSelect1)
+#define HDSPM_InputOptical   0
+#define HDSPM_InputCoaxial   (HDSPM_InputSelect0)
+#define HDSPM_SyncRefMask    (HDSPM_SyncRef0|HDSPM_SyncRef1)
+#define HDSPM_SyncRef_Word   0
+#define HDSPM_SyncRef_MADI   (HDSPM_SyncRef0)
+
+#define HDSPM_SYNC_FROM_WORD 0 /* Preferred sync reference */
+#define HDSPM_SYNC_FROM_MADI 1 /* choices - used by "pref_sync_ref" */
+
+#define HDSPM_Frequency32KHz    HDSPM_Frequency0
+#define HDSPM_Frequency44_1KHz  HDSPM_Frequency1
+#define HDSPM_Frequency48KHz   (HDSPM_Frequency1|HDSPM_Frequency0)
+#define HDSPM_Frequency64KHz   (HDSPM_DoubleSpeed|HDSPM_Frequency0)
+#define HDSPM_Frequency88_2KHz (HDSPM_DoubleSpeed|HDSPM_Frequency1)
+#define HDSPM_Frequency96KHz   (HDSPM_DoubleSpeed|HDSPM_Frequency1|HDSPM_Frequency0)
+
+/* --- for internal discrimination */
+#define HDSPM_CLOCK_SOURCE_AUTOSYNC          0 /* Sample Clock Sources */
+#define HDSPM_CLOCK_SOURCE_INTERNAL_32KHZ    1
+#define HDSPM_CLOCK_SOURCE_INTERNAL_44_1KHZ  2
+#define HDSPM_CLOCK_SOURCE_INTERNAL_48KHZ    3
+#define HDSPM_CLOCK_SOURCE_INTERNAL_64KHZ    4
+#define HDSPM_CLOCK_SOURCE_INTERNAL_88_2KHZ  5
+#define HDSPM_CLOCK_SOURCE_INTERNAL_96KHZ    6
+#define HDSPM_CLOCK_SOURCE_INTERNAL_128KHZ   7
+#define HDSPM_CLOCK_SOURCE_INTERNAL_176_4KHZ 8
+#define HDSPM_CLOCK_SOURCE_INTERNAL_192KHZ   9
+
+/* Synccheck Status */
+#define HDSPM_SYNC_CHECK_NO_LOCK 0
+#define HDSPM_SYNC_CHECK_LOCK    1
+#define HDSPM_SYNC_CHECK_SYNC   2
+
+/* AutoSync References - used by "autosync_ref" control switch */
+#define HDSPM_AUTOSYNC_FROM_WORD      0
+#define HDSPM_AUTOSYNC_FROM_MADI      1
+#define HDSPM_AUTOSYNC_FROM_NONE      2
+
+/* Possible sources of MADI input */
+#define HDSPM_OPTICAL 0                /* optical   */
+#define HDSPM_COAXIAL 1                /* BNC */
+
+#define hdspm_encode_latency(x)       (((x)<<1) & HDSPM_LatencyMask)
+#define hdspm_decode_latency(x)       (((x) & HDSPM_LatencyMask)>>1)
+
+#define hdspm_encode_in(x) (((x)&0x3)<<14)
+#define hdspm_decode_in(x) (((x)>>14)&0x3)
+
+/* --- control2 register bits --- */
+#define HDSPM_TMS             (1<<0)
+#define HDSPM_TCK             (1<<1)
+#define HDSPM_TDI             (1<<2)
+#define HDSPM_JTAG            (1<<3)
+#define HDSPM_PWDN            (1<<4)
+#define HDSPM_PROGRAM        (1<<5)
+#define HDSPM_CONFIG_MODE_0   (1<<6)
+#define HDSPM_CONFIG_MODE_1   (1<<7)
+/*#define HDSPM_VERSION_BIT     (1<<8) not defined any more*/
+#define HDSPM_BIGENDIAN_MODE  (1<<9)
+#define HDSPM_RD_MULTIPLE     (1<<10)
+
+/* --- Status Register bits --- */
+#define HDSPM_audioIRQPending    (1<<0)        /* IRQ is high and pending */
+#define HDSPM_RX_64ch            (1<<1)        /* Input 64chan. MODE=1, 56chn. MODE=0 */
+#define HDSPM_AB_int             (1<<2)        /* InputChannel Opt=0, Coax=1 (like inp0) */
+#define HDSPM_madiLock           (1<<3)        /* MADI Locked =1, no=0 */
+
+#define HDSPM_BufferPositionMask 0x000FFC0 /* Bit 6..15 : h/w buffer pointer */
+                                           /* since 64byte accurate last 6 bits 
+                                              are not used */
+
+#define HDSPM_madiSync          (1<<18) /* MADI is in sync */
+#define HDSPM_DoubleSpeedStatus (1<<19) /* (input) card in double speed */
+
+#define HDSPM_madiFreq0         (1<<22)        /* system freq 0=error */
+#define HDSPM_madiFreq1         (1<<23)        /* 1=32, 2=44.1 3=48 */
+#define HDSPM_madiFreq2         (1<<24)        /* 4=64, 5=88.2 6=96 */
+#define HDSPM_madiFreq3         (1<<25)        /* 7=128, 8=176.4 9=192 */
+
+#define HDSPM_BufferID          (1<<26)        /* (Double)Buffer ID toggles with Interrupt */
+#define HDSPM_midi0IRQPending   (1<<30)        /* MIDI IRQ is pending  */
+#define HDSPM_midi1IRQPending   (1<<31)        /* and aktiv */
+
+/* --- status bit helpers */
+#define HDSPM_madiFreqMask  (HDSPM_madiFreq0|HDSPM_madiFreq1|HDSPM_madiFreq2|HDSPM_madiFreq3)
+#define HDSPM_madiFreq32    (HDSPM_madiFreq0)
+#define HDSPM_madiFreq44_1  (HDSPM_madiFreq1)
+#define HDSPM_madiFreq48    (HDSPM_madiFreq0|HDSPM_madiFreq1)
+#define HDSPM_madiFreq64    (HDSPM_madiFreq2)
+#define HDSPM_madiFreq88_2  (HDSPM_madiFreq0|HDSPM_madiFreq2)
+#define HDSPM_madiFreq96    (HDSPM_madiFreq1|HDSPM_madiFreq2)
+#define HDSPM_madiFreq128   (HDSPM_madiFreq0|HDSPM_madiFreq1|HDSPM_madiFreq2)
+#define HDSPM_madiFreq176_4 (HDSPM_madiFreq3)
+#define HDSPM_madiFreq192   (HDSPM_madiFreq3|HDSPM_madiFreq0)
+
+/* Status2 Register bits */
+
+#define HDSPM_version0 (1<<0)  /* not realy defined but I guess */
+#define HDSPM_version1 (1<<1)  /* in former cards it was ??? */
+#define HDSPM_version2 (1<<2)
+
+#define HDSPM_wcLock (1<<3)    /* Wordclock is detected and locked */
+#define HDSPM_wcSync (1<<4)    /* Wordclock is in sync with systemclock */
+
+#define HDSPM_wc_freq0 (1<<5)  /* input freq detected via autosync  */
+#define HDSPM_wc_freq1 (1<<6)  /* 001=32, 010==44.1, 011=48, */
+#define HDSPM_wc_freq2 (1<<7)  /* 100=64, 101=88.2, 110=96, */
+/* missing Bit   for               111=128, 1000=176.4, 1001=192 */
+
+#define HDSPM_SelSyncRef0 (1<<8)       /* Sync Source in slave mode */
+#define HDSPM_SelSyncRef1 (1<<9)       /* 000=word, 001=MADI, */
+#define HDSPM_SelSyncRef2 (1<<10)      /* 111=no valid signal */
+
+#define HDSPM_wc_valid (HDSPM_wcLock|HDSPM_wcSync)
+
+#define HDSPM_wcFreqMask  (HDSPM_wc_freq0|HDSPM_wc_freq1|HDSPM_wc_freq2)
+#define HDSPM_wcFreq32    (HDSPM_wc_freq0)
+#define HDSPM_wcFreq44_1  (HDSPM_wc_freq1)
+#define HDSPM_wcFreq48    (HDSPM_wc_freq0|HDSPM_wc_freq1)
+#define HDSPM_wcFreq64    (HDSPM_wc_freq2)
+#define HDSPM_wcFreq88_2  (HDSPM_wc_freq0|HDSPM_wc_freq2)
+#define HDSPM_wcFreq96    (HDSPM_wc_freq1|HDSPM_wc_freq2)
+
+
+#define HDSPM_SelSyncRefMask       (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|HDSPM_SelSyncRef2)
+#define HDSPM_SelSyncRef_WORD      0
+#define HDSPM_SelSyncRef_MADI      (HDSPM_SelSyncRef0)
+#define HDSPM_SelSyncRef_NVALID    (HDSPM_SelSyncRef0|HDSPM_SelSyncRef1|HDSPM_SelSyncRef2)
+
+/* Mixer Values */
+#define UNITY_GAIN          32768      /* = 65536/2 */
+#define MINUS_INFINITY_GAIN 0
+
+/* PCI info */
+#ifndef PCI_VENDOR_ID_XILINX
+#define PCI_VENDOR_ID_XILINX           0x10ee
+#endif
+#ifndef PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP
+#define PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP 0x3fc5
+#endif
+#ifndef PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI
+#define PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI 0x3fc6
+#endif
+
+
+/* Number of channels for different Speed Modes */
+#define MADI_SS_CHANNELS       64
+#define MADI_DS_CHANNELS       32
+#define MADI_QS_CHANNELS       16
+
+/* the size of a substream (1 mono data stream) */
+#define HDSPM_CHANNEL_BUFFER_SAMPLES  (16*1024)
+#define HDSPM_CHANNEL_BUFFER_BYTES    (4*HDSPM_CHANNEL_BUFFER_SAMPLES)
+
+/* the size of the area we need to allocate for DMA transfers. the
+   size is the same regardless of the number of channels, and
+   also the latency to use. 
+   for one direction !!!
+*/
+#define HDSPM_DMA_AREA_BYTES (HDSPM_MAX_CHANNELS * HDSPM_CHANNEL_BUFFER_BYTES)
+#define HDSPM_DMA_AREA_KILOBYTES (HDSPM_DMA_AREA_BYTES/1024)
+
+typedef struct _hdspm hdspm_t;
+typedef struct _hdspm_midi hdspm_midi_t;
+
+struct _hdspm_midi {
+       hdspm_t *hdspm;
+       int id;
+       snd_rawmidi_t *rmidi;
+       snd_rawmidi_substream_t *input;
+       snd_rawmidi_substream_t *output;
+       char istimer;           /* timer in use */
+       struct timer_list timer;
+       spinlock_t lock;
+       int pending;
+};
+
+struct _hdspm {
+        spinlock_t lock;
+        snd_pcm_substream_t *capture_substream;         /* only one playback */
+        snd_pcm_substream_t *playback_substream; /* and/or capture stream */
+
+       char *card_name;             /* for procinfo */
+       unsigned short firmware_rev; /* dont know if relevant */
+
+       int precise_ptr;        /* use precise pointers, to be tested */
+       int monitor_outs;       /* set up monitoring outs init flag */
+
+       u32 control_register;   /* cached value */
+       u32 control2_register;  /* cached value */
+
+       hdspm_midi_t midi[2];
+       struct tasklet_struct midi_tasklet;
+
+       size_t period_bytes;
+       unsigned char ss_channels;      /* channels of card in single speed */
+       unsigned char ds_channels;      /* Double Speed */
+       unsigned char qs_channels;      /* Quad Speed */
+
+       unsigned char *playback_buffer; /* suitably aligned address */
+       unsigned char *capture_buffer;  /* suitably aligned address */
+
+       pid_t capture_pid;      /* process id which uses capture */
+       pid_t playback_pid;     /* process id which uses capture */
+       int running;            /* running status */
+
+       int last_external_sample_rate;  /* samplerate mystic ... */
+       int last_internal_sample_rate;
+       int system_sample_rate;
+
+       char *channel_map;      /* channel map for DS and Quadspeed */
+
+       int dev;                /* Hardware vars... */
+       int irq;
+       unsigned long port;
+       void __iomem *iobase;
+
+       int irq_count;          /* for debug */
+
+       snd_card_t *card;       /* one card */
+       snd_pcm_t *pcm;         /* has one pcm */
+       snd_hwdep_t *hwdep;     /* and a hwdep for additional ioctl */
+       struct pci_dev *pci;    /* and an pci info */
+
+       /* Mixer vars */
+       snd_kcontrol_t *playback_mixer_ctls[HDSPM_MAX_CHANNELS];        /* fast alsa mixer */
+       snd_kcontrol_t *input_mixer_ctls[HDSPM_MAX_CHANNELS];   /* but input to much, so not used */
+       hdspm_mixer_t *mixer;   /* full mixer accessable over mixer ioctl or hwdep-device */
+
+};
+
+/* These tables map the ALSA channels 1..N to the channels that we
+   need to use in order to find the relevant channel buffer. RME
+   refer to this kind of mapping as between "the ADAT channel and
+   the DMA channel." We index it using the logical audio channel,
+   and the value is the DMA channel (i.e. channel buffer number)
+   where the data for that channel can be read/written from/to.
+*/
+
+static char channel_map_madi_ss[HDSPM_MAX_CHANNELS] = {
+   0, 1, 2, 3, 4, 5, 6, 7,
+   8, 9, 10, 11, 12, 13, 14, 15,
+   16, 17, 18, 19, 20, 21, 22, 23,
+   24, 25, 26, 27, 28, 29, 30, 31,
+   32, 33, 34, 35, 36, 37, 38, 39,
+   40, 41, 42, 43, 44, 45, 46, 47,
+   48, 49, 50, 51, 52, 53, 54, 55,
+   56, 57, 58, 59, 60, 61, 62, 63
+};
+
+static char channel_map_madi_ds[HDSPM_MAX_CHANNELS] = {
+  0, 2, 4, 6, 8, 10, 12, 14,
+  16, 18, 20, 22, 24, 26, 28, 30,
+  32, 34, 36, 38, 40, 42, 44, 46,
+  48, 50, 52, 54, 56, 58, 60, 62,
+  -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1,
+  -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+static char channel_map_madi_qs[HDSPM_MAX_CHANNELS] = {
+  0,   4,  8, 12, 16, 20, 24,  28,  
+  32, 36, 40, 44, 48, 52, 56,  60
+  -1, -1, -1, -1, -1, -1, -1, -1,  
+  -1, -1, -1, -1, -1, -1, -1, -1,  
+  -1, -1, -1, -1, -1, -1, -1, -1, 
+  -1, -1, -1, -1, -1, -1, -1, -1, 
+  -1, -1, -1, -1, -1, -1, -1, -1, 
+  -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+
+static struct pci_device_id snd_hdspm_ids[] = {
+       {
+        .vendor = PCI_VENDOR_ID_XILINX,
+        .device = PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI,
+        .subvendor = PCI_ANY_ID,
+        .subdevice = PCI_ANY_ID,
+        .class = 0,
+        .class_mask = 0,
+        .driver_data = 0},
+       {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, snd_hdspm_ids);
+
+/* prototypes */
+static int __devinit snd_hdspm_create_alsa_devices(snd_card_t * card,
+                                                  hdspm_t * hdspm);
+static int __devinit snd_hdspm_create_pcm(snd_card_t * card,
+                                         hdspm_t * hdspm);
+
+static inline void snd_hdspm_initialize_midi_flush(hdspm_t * hdspm);
+static int hdspm_update_simple_mixer_controls(hdspm_t * hdspm);
+static int hdspm_autosync_ref(hdspm_t * hdspm);
+static int snd_hdspm_set_defaults(hdspm_t * hdspm);
+static void hdspm_set_sgbuf(hdspm_t * hdspm, struct snd_sg_buf *sgbuf,
+                            unsigned int reg, int channels);
+
+/* Write/read to/from HDSPM with Adresses in Bytes
+   not words but only 32Bit writes are allowed */
+
+static inline void hdspm_write(hdspm_t * hdspm, unsigned int reg,
+                              unsigned int val)
+{
+       writel(val, hdspm->iobase + reg);
+}
+
+static inline unsigned int hdspm_read(hdspm_t * hdspm, unsigned int reg)
+{
+       return readl(hdspm->iobase + reg);
+}
+
+/* for each output channel (chan) I have an Input (in) and Playback (pb) Fader 
+   mixer is write only on hardware so we have to cache him for read 
+   each fader is a u32, but uses only the first 16 bit */
+
+static inline int hdspm_read_in_gain(hdspm_t * hdspm, unsigned int chan,
+                                    unsigned int in)
+{
+       if (chan > HDSPM_MIXER_CHANNELS || in > HDSPM_MIXER_CHANNELS)
+               return 0;
+
+       return hdspm->mixer->ch[chan].in[in];
+}
+
+static inline int hdspm_read_pb_gain(hdspm_t * hdspm, unsigned int chan,
+                                    unsigned int pb)
+{
+       if (chan > HDSPM_MIXER_CHANNELS || pb > HDSPM_MIXER_CHANNELS)
+               return 0;
+       return hdspm->mixer->ch[chan].pb[pb];
+}
+
+static inline int hdspm_write_in_gain(hdspm_t * hdspm, unsigned int chan,
+                                     unsigned int in, unsigned short data)
+{
+       if (chan >= HDSPM_MIXER_CHANNELS || in >= HDSPM_MIXER_CHANNELS)
+               return -1;
+
+       hdspm_write(hdspm,
+                   HDSPM_MADI_mixerBase +
+                   ((in + 128 * chan) * sizeof(u32)),
+                   (hdspm->mixer->ch[chan].in[in] = data & 0xFFFF));
+       return 0;
+}
+
+static inline int hdspm_write_pb_gain(hdspm_t * hdspm, unsigned int chan,
+                                     unsigned int pb, unsigned short data)
+{
+       if (chan >= HDSPM_MIXER_CHANNELS || pb >= HDSPM_MIXER_CHANNELS)
+               return -1;
+
+       hdspm_write(hdspm,
+                   HDSPM_MADI_mixerBase +
+                   ((64 + pb + 128 * chan) * sizeof(u32)),
+                   (hdspm->mixer->ch[chan].pb[pb] = data & 0xFFFF));
+       return 0;
+}
+
+
+/* enable DMA for specific channels, now available for DSP-MADI */
+static inline void snd_hdspm_enable_in(hdspm_t * hdspm, int i, int v)
+{
+       hdspm_write(hdspm, HDSPM_inputEnableBase + (4 * i), v);
+}
+
+static inline void snd_hdspm_enable_out(hdspm_t * hdspm, int i, int v)
+{
+       hdspm_write(hdspm, HDSPM_outputEnableBase + (4 * i), v);
+}
+
+/* check if same process is writing and reading */
+static inline int snd_hdspm_use_is_exclusive(hdspm_t * hdspm)
+{
+       unsigned long flags;
+       int ret = 1;
+
+       spin_lock_irqsave(&hdspm->lock, flags);
+       if ((hdspm->playback_pid != hdspm->capture_pid) &&
+           (hdspm->playback_pid >= 0) && (hdspm->capture_pid >= 0)) {
+               ret = 0;
+       }
+       spin_unlock_irqrestore(&hdspm->lock, flags);
+       return ret;
+}
+
+/* check for external sample rate */
+static inline int hdspm_external_sample_rate(hdspm_t * hdspm)
+{
+       unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
+       unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister);
+       unsigned int rate_bits;
+       int rate = 0;
+
+       /* if wordclock has synced freq and wordclock is valid */
+       if ((status2 & HDSPM_wcLock) != 0 &&
+           (status & HDSPM_SelSyncRef0) == 0) {
+
+               rate_bits = status2 & HDSPM_wcFreqMask;
+
+               switch (rate_bits) {
+               case HDSPM_wcFreq32:
+                       rate = 32000;
+                       break;
+               case HDSPM_wcFreq44_1:
+                       rate = 44100;
+                       break;
+               case HDSPM_wcFreq48:
+                       rate = 48000;
+                       break;
+               case HDSPM_wcFreq64:
+                       rate = 64000;
+                       break;
+               case HDSPM_wcFreq88_2:
+                       rate = 88200;
+                       break;
+               case HDSPM_wcFreq96:
+                       rate = 96000;
+                       break;
+                       /* Quadspeed Bit missing ???? */
+               default:
+                       rate = 0;
+                       break;
+               }
+       }
+
+       /* if rate detected and Syncref is Word than have it, word has priority to MADI */
+       if (rate != 0
+           && (status2 & HDSPM_SelSyncRefMask) == HDSPM_SelSyncRef_WORD)
+               return rate;
+
+       /* maby a madi input (which is taken if sel sync is madi) */
+       if (status & HDSPM_madiLock) {
+               rate_bits = status & HDSPM_madiFreqMask;
+
+               switch (rate_bits) {
+               case HDSPM_madiFreq32:
+                       rate = 32000;
+                       break;
+               case HDSPM_madiFreq44_1:
+                       rate = 44100;
+                       break;
+               case HDSPM_madiFreq48:
+                       rate = 48000;
+                       break;
+               case HDSPM_madiFreq64:
+                       rate = 64000;
+                       break;
+               case HDSPM_madiFreq88_2:
+                       rate = 88200;
+                       break;
+               case HDSPM_madiFreq96:
+                       rate = 96000;
+                       break;
+               case HDSPM_madiFreq128:
+                       rate = 128000;
+                       break;
+               case HDSPM_madiFreq176_4:
+                       rate = 176400;
+                       break;
+               case HDSPM_madiFreq192:
+                       rate = 192000;
+                       break;
+               default:
+                       rate = 0;
+                       break;
+               }
+       }
+       return rate;
+}
+
+/* Latency function */
+static inline void hdspm_compute_period_size(hdspm_t * hdspm)
+{
+       hdspm->period_bytes =
+           1 << ((hdspm_decode_latency(hdspm->control_register) + 8));
+}
+
+static snd_pcm_uframes_t hdspm_hw_pointer(hdspm_t * hdspm)
+{
+       int position;
+
+       position = hdspm_read(hdspm, HDSPM_statusRegister);
+
+       if (!hdspm->precise_ptr) {
+               return (position & HDSPM_BufferID) ? (hdspm->period_bytes /
+                                                     4) : 0;
+       }
+
+       /* hwpointer comes in bytes and is 64Bytes accurate (by docu since PCI Burst)
+          i have experimented that it is at most 64 Byte to much for playing 
+          so substraction of 64 byte should be ok for ALSA, but use it only
+          for application where you know what you do since if you come to
+          near with record pointer it can be a disaster */
+
+       position &= HDSPM_BufferPositionMask;
+       position = ((position - 64) % (2 * hdspm->period_bytes)) / 4;
+
+       return position;
+}
+
+
+static inline void hdspm_start_audio(hdspm_t * s)
+{
+       s->control_register |= (HDSPM_AudioInterruptEnable | HDSPM_Start);
+       hdspm_write(s, HDSPM_controlRegister, s->control_register);
+}
+
+static inline void hdspm_stop_audio(hdspm_t * s)
+{
+       s->control_register &= ~(HDSPM_Start | HDSPM_AudioInterruptEnable);
+       hdspm_write(s, HDSPM_controlRegister, s->control_register);
+}
+
+/* should I silence all or only opened ones ? doit all for first even is 4MB*/
+static inline void hdspm_silence_playback(hdspm_t * hdspm)
+{
+       int i;
+       int n = hdspm->period_bytes;
+       void *buf = hdspm->playback_buffer;
+
+       snd_assert(buf != NULL, return);
+
+       for (i = 0; i < HDSPM_MAX_CHANNELS; i++) {
+               memset(buf, 0, n);
+               buf += HDSPM_CHANNEL_BUFFER_BYTES;
+       }
+}
+
+static int hdspm_set_interrupt_interval(hdspm_t * s, unsigned int frames)
+{
+       int n;
+
+       spin_lock_irq(&s->lock);
+
+       frames >>= 7;
+       n = 0;
+       while (frames) {
+               n++;
+               frames >>= 1;
+       }
+       s->control_register &= ~HDSPM_LatencyMask;
+       s->control_register |= hdspm_encode_latency(n);
+
+       hdspm_write(s, HDSPM_controlRegister, s->control_register);
+
+       hdspm_compute_period_size(s);
+
+       spin_unlock_irq(&s->lock);
+
+       return 0;
+}
+
+
+/* dummy set rate lets see what happens */
+static int hdspm_set_rate(hdspm_t * hdspm, int rate, int called_internally)
+{
+       int reject_if_open = 0;
+       int current_rate;
+       int rate_bits;
+       int not_set = 0;
+
+       /* ASSUMPTION: hdspm->lock is either set, or there is no need for
+          it (e.g. during module initialization).
+        */
+
+       if (!(hdspm->control_register & HDSPM_ClockModeMaster)) {
+
+               /* SLAVE --- */ 
+               if (called_internally) {
+
+                 /* request from ctl or card initialization 
+                    just make a warning an remember setting 
+                    for future master mode switching */
+    
+                       snd_printk
+                           (KERN_WARNING "HDSPM: Warning: device is not running as a clock master.\n");
+                       not_set = 1;
+               } else {
+
+                       /* hw_param request while in AutoSync mode */
+                       int external_freq =
+                           hdspm_external_sample_rate(hdspm);
+
+                       if ((hdspm_autosync_ref(hdspm) ==
+                            HDSPM_AUTOSYNC_FROM_NONE)) {
+
+                               snd_printk(KERN_WARNING "HDSPM: Detected no Externel Sync \n");
+                               not_set = 1;
+
+                       } else if (rate != external_freq) {
+
+                               snd_printk
+                                   (KERN_WARNING "HDSPM: Warning: No AutoSync source for requested rate\n");
+                               not_set = 1;
+                       }
+               }
+       }
+
+       current_rate = hdspm->system_sample_rate;
+
+       /* Changing between Singe, Double and Quad speed is not
+          allowed if any substreams are open. This is because such a change
+          causes a shift in the location of the DMA buffers and a reduction
+          in the number of available buffers.
+
+          Note that a similar but essentially insoluble problem exists for
+          externally-driven rate changes. All we can do is to flag rate
+          changes in the read/write routines.  
+        */
+
+       switch (rate) {
+       case 32000:
+               if (current_rate > 48000) {
+                       reject_if_open = 1;
+               }
+               rate_bits = HDSPM_Frequency32KHz;
+               break;
+       case 44100:
+               if (current_rate > 48000) {
+                       reject_if_open = 1;
+               }
+               rate_bits = HDSPM_Frequency44_1KHz;
+               break;
+       case 48000:
+               if (current_rate > 48000) {
+                       reject_if_open = 1;
+               }
+               rate_bits = HDSPM_Frequency48KHz;
+               break;
+       case 64000:
+               if (current_rate <= 48000) {
+                       reject_if_open = 1;
+               }
+               rate_bits = HDSPM_Frequency64KHz;
+               break;
+       case 88200:
+               if (current_rate <= 48000) {
+                       reject_if_open = 1;
+               }
+               rate_bits = HDSPM_Frequency88_2KHz;
+               break;
+       case 96000:
+               if (current_rate <= 48000) {
+                       reject_if_open = 1;
+               }
+               rate_bits = HDSPM_Frequency96KHz;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (reject_if_open
+           && (hdspm->capture_pid >= 0 || hdspm->playback_pid >= 0)) {
+               snd_printk
+                   (KERN_ERR "HDSPM: cannot change between single- and double-speed mode (capture PID = %d, playback PID = %d)\n",
+                    hdspm->capture_pid, hdspm->playback_pid);
+               return -EBUSY;
+       }
+
+       hdspm->control_register &= ~HDSPM_FrequencyMask;
+       hdspm->control_register |= rate_bits;
+       hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
+
+       if (rate > 64000)
+               hdspm->channel_map = channel_map_madi_qs;
+       else if (rate > 48000)
+               hdspm->channel_map = channel_map_madi_ds;
+       else 
+               hdspm->channel_map = channel_map_madi_ss;
+
+       hdspm->system_sample_rate = rate;
+
+       if (not_set != 0)
+               return -1;
+
+       return 0;
+}
+
+/* mainly for init to 0 on load */
+static void all_in_all_mixer(hdspm_t * hdspm, int sgain)
+{
+       int i, j;
+       unsigned int gain =
+           (sgain > UNITY_GAIN) ? UNITY_GAIN : (sgain < 0) ? 0 : sgain;
+
+       for (i = 0; i < HDSPM_MIXER_CHANNELS; i++)
+               for (j = 0; j < HDSPM_MIXER_CHANNELS; j++) {
+                       hdspm_write_in_gain(hdspm, i, j, gain);
+                       hdspm_write_pb_gain(hdspm, i, j, gain);
+               }
+}
+
+/*----------------------------------------------------------------------------
+   MIDI
+  ----------------------------------------------------------------------------*/
+
+static inline unsigned char snd_hdspm_midi_read_byte (hdspm_t *hdspm, int id)
+{
+       /* the hardware already does the relevant bit-mask with 0xff */
+       if (id)
+               return hdspm_read(hdspm, HDSPM_midiDataIn1);
+       else
+               return hdspm_read(hdspm, HDSPM_midiDataIn0);
+}
+
+static inline void snd_hdspm_midi_write_byte (hdspm_t *hdspm, int id, int val)
+{
+       /* the hardware already does the relevant bit-mask with 0xff */
+       if (id)
+               return hdspm_write(hdspm, HDSPM_midiDataOut1, val);
+       else
+               return hdspm_write(hdspm, HDSPM_midiDataOut0, val);
+}
+
+static inline int snd_hdspm_midi_input_available (hdspm_t *hdspm, int id)
+{
+       if (id)
+               return (hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xff);
+       else
+               return (hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xff);
+}
+
+static inline int snd_hdspm_midi_output_possible (hdspm_t *hdspm, int id)
+{
+       int fifo_bytes_used;
+
+       if (id)
+               fifo_bytes_used = hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xff;
+       else
+               fifo_bytes_used = hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xff;
+
+       if (fifo_bytes_used < 128)
+               return  128 - fifo_bytes_used;
+       else
+               return 0;
+}
+
+static inline void snd_hdspm_flush_midi_input (hdspm_t *hdspm, int id)
+{
+       while (snd_hdspm_midi_input_available (hdspm, id))
+               snd_hdspm_midi_read_byte (hdspm, id);
+}
+
+static int snd_hdspm_midi_output_write (hdspm_midi_t *hmidi)
+{
+       unsigned long flags;
+       int n_pending;
+       int to_write;
+       int i;
+       unsigned char buf[128];
+
+       /* Output is not interrupt driven */
+               
+       spin_lock_irqsave (&hmidi->lock, flags);
+       if (hmidi->output) {
+               if (!snd_rawmidi_transmit_empty (hmidi->output)) {
+                       if ((n_pending = snd_hdspm_midi_output_possible (hmidi->hdspm, hmidi->id)) > 0) {
+                               if (n_pending > (int)sizeof (buf))
+                                       n_pending = sizeof (buf);
+                               
+                               if ((to_write = snd_rawmidi_transmit (hmidi->output, buf, n_pending)) > 0) {
+                                       for (i = 0; i < to_write; ++i) 
+                                               snd_hdspm_midi_write_byte (hmidi->hdspm, hmidi->id, buf[i]);
+                               }
+                       }
+               }
+       }
+       spin_unlock_irqrestore (&hmidi->lock, flags);
+       return 0;
+}
+
+static int snd_hdspm_midi_input_read (hdspm_midi_t *hmidi)
+{
+       unsigned char buf[128]; /* this buffer is designed to match the MIDI input FIFO size */
+       unsigned long flags;
+       int n_pending;
+       int i;
+
+       spin_lock_irqsave (&hmidi->lock, flags);
+       if ((n_pending = snd_hdspm_midi_input_available (hmidi->hdspm, hmidi->id)) > 0) {
+               if (hmidi->input) {
+                       if (n_pending > (int)sizeof (buf)) {
+                               n_pending = sizeof (buf);
+                       }
+                       for (i = 0; i < n_pending; ++i) {
+                               buf[i] = snd_hdspm_midi_read_byte (hmidi->hdspm, hmidi->id);
+                       }
+                       if (n_pending) {
+                               snd_rawmidi_receive (hmidi->input, buf, n_pending);
+                       }
+               } else {
+                       /* flush the MIDI input FIFO */
+                       while (n_pending--) {
+                               snd_hdspm_midi_read_byte (hmidi->hdspm, hmidi->id);
+                       }
+               }
+       }
+       hmidi->pending = 0;
+       if (hmidi->id) {
+               hmidi->hdspm->control_register |= HDSPM_Midi1InterruptEnable;
+       } else {
+               hmidi->hdspm->control_register |= HDSPM_Midi0InterruptEnable;
+       }
+       hdspm_write(hmidi->hdspm, HDSPM_controlRegister, hmidi->hdspm->control_register);
+       spin_unlock_irqrestore (&hmidi->lock, flags);
+       return snd_hdspm_midi_output_write (hmidi);
+}
+
+static void snd_hdspm_midi_input_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+       hdspm_t *hdspm;
+       hdspm_midi_t *hmidi;
+       unsigned long flags;
+       u32 ie;
+
+       hmidi = (hdspm_midi_t *) substream->rmidi->private_data;
+       hdspm = hmidi->hdspm;
+       ie = hmidi->id ? HDSPM_Midi1InterruptEnable : HDSPM_Midi0InterruptEnable;
+       spin_lock_irqsave (&hdspm->lock, flags);
+       if (up) {
+               if (!(hdspm->control_register & ie)) {
+                       snd_hdspm_flush_midi_input (hdspm, hmidi->id);
+                       hdspm->control_register |= ie;
+               }
+       } else {
+               hdspm->control_register &= ~ie;
+       }
+
+       hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
+       spin_unlock_irqrestore (&hdspm->lock, flags);
+}
+
+static void snd_hdspm_midi_output_timer(unsigned long data)
+{
+       hdspm_midi_t *hmidi = (hdspm_midi_t *) data;
+       unsigned long flags;
+       
+       snd_hdspm_midi_output_write(hmidi);
+       spin_lock_irqsave (&hmidi->lock, flags);
+
+       /* this does not bump hmidi->istimer, because the
+          kernel automatically removed the timer when it
+          expired, and we are now adding it back, thus
+          leaving istimer wherever it was set before.  
+       */
+
+       if (hmidi->istimer) {
+               hmidi->timer.expires = 1 + jiffies;
+               add_timer(&hmidi->timer);
+       }
+
+       spin_unlock_irqrestore (&hmidi->lock, flags);
+}
+
+static void snd_hdspm_midi_output_trigger(snd_rawmidi_substream_t * substream, int up)
+{
+       hdspm_midi_t *hmidi;
+       unsigned long flags;
+
+       hmidi = (hdspm_midi_t *) substream->rmidi->private_data;
+       spin_lock_irqsave (&hmidi->lock, flags);
+       if (up) {
+               if (!hmidi->istimer) {
+                       init_timer(&hmidi->timer);
+                       hmidi->timer.function = snd_hdspm_midi_output_timer;
+                       hmidi->timer.data = (unsigned long) hmidi;
+                       hmidi->timer.expires = 1 + jiffies;
+                       add_timer(&hmidi->timer);
+                       hmidi->istimer++;
+               }
+       } else {
+               if (hmidi->istimer && --hmidi->istimer <= 0) {
+                       del_timer (&hmidi->timer);
+               }
+       }
+       spin_unlock_irqrestore (&hmidi->lock, flags);
+       if (up)
+               snd_hdspm_midi_output_write(hmidi);
+}
+
+static int snd_hdspm_midi_input_open(snd_rawmidi_substream_t * substream)
+{
+       hdspm_midi_t *hmidi;
+
+       hmidi = (hdspm_midi_t *) substream->rmidi->private_data;
+       spin_lock_irq (&hmidi->lock);
+       snd_hdspm_flush_midi_input (hmidi->hdspm, hmidi->id);
+       hmidi->input = substream;
+       spin_unlock_irq (&hmidi->lock);
+
+       return 0;
+}
+
+static int snd_hdspm_midi_output_open(snd_rawmidi_substream_t * substream)
+{
+       hdspm_midi_t *hmidi;
+
+       hmidi = (hdspm_midi_t *) substream->rmidi->private_data;
+       spin_lock_irq (&hmidi->lock);
+       hmidi->output = substream;
+       spin_unlock_irq (&hmidi->lock);
+
+       return 0;
+}
+
+static int snd_hdspm_midi_input_close(snd_rawmidi_substream_t * substream)
+{
+       hdspm_midi_t *hmidi;
+
+       snd_hdspm_midi_input_trigger (substream, 0);
+
+       hmidi = (hdspm_midi_t *) substream->rmidi->private_data;
+       spin_lock_irq (&hmidi->lock);
+       hmidi->input = NULL;
+       spin_unlock_irq (&hmidi->lock);
+
+       return 0;
+}
+
+static int snd_hdspm_midi_output_close(snd_rawmidi_substream_t * substream)
+{
+       hdspm_midi_t *hmidi;
+
+       snd_hdspm_midi_output_trigger (substream, 0);
+
+       hmidi = (hdspm_midi_t *) substream->rmidi->private_data;
+       spin_lock_irq (&hmidi->lock);
+       hmidi->output = NULL;
+       spin_unlock_irq (&hmidi->lock);
+
+       return 0;
+}
+
+snd_rawmidi_ops_t snd_hdspm_midi_output =
+{
+       .open =         snd_hdspm_midi_output_open,
+       .close =        snd_hdspm_midi_output_close,
+       .trigger =      snd_hdspm_midi_output_trigger,
+};
+
+snd_rawmidi_ops_t snd_hdspm_midi_input =
+{
+       .open =         snd_hdspm_midi_input_open,
+       .close =        snd_hdspm_midi_input_close,
+       .trigger =      snd_hdspm_midi_input_trigger,
+};
+
+static int __devinit snd_hdspm_create_midi (snd_card_t *card, hdspm_t *hdspm, int id)
+{
+       int err;
+       char buf[32];
+
+       hdspm->midi[id].id = id;
+       hdspm->midi[id].rmidi = NULL;
+       hdspm->midi[id].input = NULL;
+       hdspm->midi[id].output = NULL;
+       hdspm->midi[id].hdspm = hdspm;
+       hdspm->midi[id].istimer = 0;
+       hdspm->midi[id].pending = 0;
+       spin_lock_init (&hdspm->midi[id].lock);
+
+       sprintf (buf, "%s MIDI %d", card->shortname, id+1);
+       if ((err = snd_rawmidi_new (card, buf, id, 1, 1, &hdspm->midi[id].rmidi)) < 0)
+               return err;
+
+       sprintf (hdspm->midi[id].rmidi->name, "%s MIDI %d", card->id, id+1);
+       hdspm->midi[id].rmidi->private_data = &hdspm->midi[id];
+
+       snd_rawmidi_set_ops (hdspm->midi[id].rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_hdspm_midi_output);
+       snd_rawmidi_set_ops (hdspm->midi[id].rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_hdspm_midi_input);
+
+       hdspm->midi[id].rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
+               SNDRV_RAWMIDI_INFO_INPUT |
+               SNDRV_RAWMIDI_INFO_DUPLEX;
+
+       return 0;
+}
+
+
+static void hdspm_midi_tasklet(unsigned long arg)
+{
+       hdspm_t *hdspm = (hdspm_t *)arg;
+       
+       if (hdspm->midi[0].pending)
+               snd_hdspm_midi_input_read (&hdspm->midi[0]);
+       if (hdspm->midi[1].pending)
+               snd_hdspm_midi_input_read (&hdspm->midi[1]);
+} 
+
+
+/*-----------------------------------------------------------------------------
+  Status Interface
+  ----------------------------------------------------------------------------*/
+
+/* get the system sample rate which is set */
+
+#define HDSPM_SYSTEM_SAMPLE_RATE(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+  .name = xname, \
+  .index = xindex, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READ, \
+  .info = snd_hdspm_info_system_sample_rate, \
+  .get = snd_hdspm_get_system_sample_rate \
+}
+
+static int snd_hdspm_info_system_sample_rate(snd_kcontrol_t * kcontrol,
+                                            snd_ctl_elem_info_t * uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       return 0;
+}
+
+static int snd_hdspm_get_system_sample_rate(snd_kcontrol_t * kcontrol,
+                                           snd_ctl_elem_value_t *
+                                           ucontrol)
+{
+       hdspm_t *hdspm = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.enumerated.item[0] = hdspm->system_sample_rate;
+       return 0;
+}
+
+#define HDSPM_AUTOSYNC_SAMPLE_RATE(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, \
+  .name = xname, \
+  .index = xindex, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READ, \
+  .info = snd_hdspm_info_autosync_sample_rate, \
+  .get = snd_hdspm_get_autosync_sample_rate \
+}
+
+static int snd_hdspm_info_autosync_sample_rate(snd_kcontrol_t * kcontrol,
+                                              snd_ctl_elem_info_t * uinfo)
+{
+       static char *texts[] = { "32000", "44100", "48000",
+               "64000", "88200", "96000",
+               "128000", "176400", "192000",
+               "None"
+       };
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 10;
+       if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+               uinfo->value.enumerated.item =
+                   uinfo->value.enumerated.items - 1;
+       strcpy(uinfo->value.enumerated.name,
+              texts[uinfo->value.enumerated.item]);
+       return 0;
+}
+
+static int snd_hdspm_get_autosync_sample_rate(snd_kcontrol_t * kcontrol,
+                                             snd_ctl_elem_value_t *
+                                             ucontrol)
+{
+       hdspm_t *hdspm = snd_kcontrol_chip(kcontrol);
+
+       switch (hdspm_external_sample_rate(hdspm)) {
+       case 32000:
+               ucontrol->value.enumerated.item[0] = 0;
+               break;
+       case 44100:
+               ucontrol->value.enumerated.item[0] = 1;
+               break;
+       case 48000:
+               ucontrol->value.enumerated.item[0] = 2;
+               break;
+       case 64000:
+               ucontrol->value.enumerated.item[0] = 3;
+               break;
+       case 88200:
+               ucontrol->value.enumerated.item[0] = 4;
+               break;
+       case 96000:
+               ucontrol->value.enumerated.item[0] = 5;
+               break;
+       case 128000:
+               ucontrol->value.enumerated.item[0] = 6;
+               break;
+       case 176400:
+               ucontrol->value.enumerated.item[0] = 7;
+               break;
+       case 192000:
+               ucontrol->value.enumerated.item[0] = 8;
+               break;
+
+       default:
+               ucontrol->value.enumerated.item[0] = 9;
+       }
+       return 0;
+}
+
+#define HDSPM_SYSTEM_CLOCK_MODE(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+  .name = xname, \
+  .index = xindex, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READ, \
+  .info = snd_hdspm_info_system_clock_mode, \
+  .get = snd_hdspm_get_system_clock_mode, \
+}
+
+
+
+static int hdspm_system_clock_mode(hdspm_t * hdspm)
+{
+        /* Always reflect the hardware info, rme is never wrong !!!! */
+
+       if (hdspm->control_register & HDSPM_ClockModeMaster)
+               return 0;
+       return 1;
+}
+
+static int snd_hdspm_info_system_clock_mode(snd_kcontrol_t * kcontrol,
+                                           snd_ctl_elem_info_t * uinfo)
+{
+       static char *texts[] = { "Master", "Slave" };
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 2;
+       if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+               uinfo->value.enumerated.item =
+                   uinfo->value.enumerated.items - 1;
+       strcpy(uinfo->value.enumerated.name,
+              texts[uinfo->value.enumerated.item]);
+       return 0;
+}
+
+static int snd_hdspm_get_system_clock_mode(snd_kcontrol_t * kcontrol,
+                                          snd_ctl_elem_value_t * ucontrol)
+{
+       hdspm_t *hdspm = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.enumerated.item[0] =
+           hdspm_system_clock_mode(hdspm);
+       return 0;
+}
+
+#define HDSPM_CLOCK_SOURCE(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, \
+  .name = xname, \
+  .index = xindex, \
+  .info = snd_hdspm_info_clock_source, \
+  .get = snd_hdspm_get_clock_source, \
+  .put = snd_hdspm_put_clock_source \
+}
+
+static int hdspm_clock_source(hdspm_t * hdspm)
+{
+       if (hdspm->control_register & HDSPM_ClockModeMaster) {
+               switch (hdspm->system_sample_rate) {
+               case 32000:
+                       return 1;
+               case 44100:
+                       return 2;
+               case 48000:
+                       return 3;
+               case 64000:
+                       return 4;
+               case 88200:
+                       return 5;
+               case 96000:
+                       return 6;
+               case 128000:
+                       return 7;
+               case 176400:
+                       return 8;
+               case 192000:
+                       return 9;
+               default:
+                       return 3;
+               }
+       } else {
+               return 0;
+       }
+}
+
+static int hdspm_set_clock_source(hdspm_t * hdspm, int mode)
+{
+       int rate;
+       switch (mode) {
+
+       case HDSPM_CLOCK_SOURCE_AUTOSYNC:
+               if (hdspm_external_sample_rate(hdspm) != 0) {
+                       hdspm->control_register &= ~HDSPM_ClockModeMaster;
+                       hdspm_write(hdspm, HDSPM_controlRegister,
+                                   hdspm->control_register);
+                       return 0;
+               }
+               return -1;
+       case HDSPM_CLOCK_SOURCE_INTERNAL_32KHZ:
+               rate = 32000;
+               break;
+       case HDSPM_CLOCK_SOURCE_INTERNAL_44_1KHZ:
+               rate = 44100;
+               break;
+       case HDSPM_CLOCK_SOURCE_INTERNAL_48KHZ:
+               rate = 48000;
+               break;
+       case HDSPM_CLOCK_SOURCE_INTERNAL_64KHZ:
+               rate = 64000;
+               break;
+       case HDSPM_CLOCK_SOURCE_INTERNAL_88_2KHZ:
+               rate = 88200;
+               break;
+       case HDSPM_CLOCK_SOURCE_INTERNAL_96KHZ:
+               rate = 96000;
+               break;
+       case HDSPM_CLOCK_SOURCE_INTERNAL_128KHZ:
+               rate = 128000;
+               break;
+       case HDSPM_CLOCK_SOURCE_INTERNAL_176_4KHZ:
+               rate = 176400;
+               break;
+       case HDSPM_CLOCK_SOURCE_INTERNAL_192KHZ:
+               rate = 192000;
+               break;
+
+       default:
+               rate = 44100;
+       }
+       hdspm->control_register |= HDSPM_ClockModeMaster;
+       hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
+       hdspm_set_rate(hdspm, rate, 1);
+       return 0;
+}
+
+static int snd_hdspm_info_clock_source(snd_kcontrol_t * kcontrol,
+                                      snd_ctl_elem_info_t * uinfo)
+{
+       static char *texts[] = { "AutoSync",
+               "Internal 32.0 kHz", "Internal 44.1 kHz",
+                   "Internal 48.0 kHz",
+               "Internal 64.0 kHz", "Internal 88.2 kHz",
+                   "Internal 96.0 kHz",
+               "Internal 128.0 kHz", "Internal 176.4 kHz",
+                   "Internal 192.0 kHz"
+       };
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 10;
+
+       if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+               uinfo->value.enumerated.item =
+                   uinfo->value.enumerated.items - 1;
+
+       strcpy(uinfo->value.enumerated.name,
+              texts[uinfo->value.enumerated.item]);
+
+       return 0;
+}
+
+static int snd_hdspm_get_clock_source(snd_kcontrol_t * kcontrol,
+                                     snd_ctl_elem_value_t * ucontrol)
+{
+       hdspm_t *hdspm = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.enumerated.item[0] = hdspm_clock_source(hdspm);
+       return 0;
+}
+
+static int snd_hdspm_put_clock_source(snd_kcontrol_t * kcontrol,
+                                     snd_ctl_elem_value_t * ucontrol)
+{
+       hdspm_t *hdspm = snd_kcontrol_chip(kcontrol);
+       int change;
+       int val;
+
+       if (!snd_hdspm_use_is_exclusive(hdspm))
+               return -EBUSY;
+       val = ucontrol->value.enumerated.item[0];
+       if (val < 0)
+               val = 0;
+       if (val > 6)
+               val = 6;
+       spin_lock_irq(&hdspm->lock);
+       if (val != hdspm_clock_source(hdspm))
+               change = (hdspm_set_clock_source(hdspm, val) == 0) ? 1 : 0;
+       else
+               change = 0;
+       spin_unlock_irq(&hdspm->lock);
+       return change;
+}
+
+#define HDSPM_PREF_SYNC_REF(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+  .name = xname, \
+  .index = xindex, \
+  .info = snd_hdspm_info_pref_sync_ref, \
+  .get = snd_hdspm_get_pref_sync_ref, \
+  .put = snd_hdspm_put_pref_sync_ref \
+}
+
+static int hdspm_pref_sync_ref(hdspm_t * hdspm)
+{
+       /* Notice that this looks at the requested sync source,
+          not the one actually in use.
+        */
+       switch (hdspm->control_register & HDSPM_SyncRefMask) {
+       case HDSPM_SyncRef_Word:
+               return HDSPM_SYNC_FROM_WORD;
+       case HDSPM_SyncRef_MADI:
+               return HDSPM_SYNC_FROM_MADI;
+       }
+
+       return HDSPM_SYNC_FROM_WORD;
+}
+
+static int hdspm_set_pref_sync_ref(hdspm_t * hdspm, int pref)
+{
+       hdspm->control_register &= ~HDSPM_SyncRefMask;
+
+       switch (pref) {
+       case HDSPM_SYNC_FROM_MADI:
+               hdspm->control_register |= HDSPM_SyncRef_MADI;
+               break;
+       case HDSPM_SYNC_FROM_WORD:
+               hdspm->control_register |= HDSPM_SyncRef_Word;
+               break;
+       default:
+               return -1;
+       }
+       hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
+       return 0;
+}
+
+static int snd_hdspm_info_pref_sync_ref(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_info_t * uinfo)
+{
+       static char *texts[] = { "Word", "MADI" };
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+
+       uinfo->value.enumerated.items = 2;
+
+       if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+               uinfo->value.enumerated.item =
+                   uinfo->value.enumerated.items - 1;
+       strcpy(uinfo->value.enumerated.name,
+              texts[uinfo->value.enumerated.item]);
+       return 0;
+}
+
+static int snd_hdspm_get_pref_sync_ref(snd_kcontrol_t * kcontrol,
+                                      snd_ctl_elem_value_t * ucontrol)
+{
+       hdspm_t *hdspm = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.enumerated.item[0] = hdspm_pref_sync_ref(hdspm);
+       return 0;
+}
+
+static int snd_hdspm_put_pref_sync_ref(snd_kcontrol_t * kcontrol,
+                                      snd_ctl_elem_value_t * ucontrol)
+{
+       hdspm_t *hdspm = snd_kcontrol_chip(kcontrol);
+       int change, max;
+       unsigned int val;
+
+       max = 2;
+
+       if (!snd_hdspm_use_is_exclusive(hdspm))
+               return -EBUSY;
+
+       val = ucontrol->value.enumerated.item[0] % max;
+
+       spin_lock_irq(&hdspm->lock);
+       change = (int) val != hdspm_pref_sync_ref(hdspm);
+       hdspm_set_pref_sync_ref(hdspm, val);
+       spin_unlock_irq(&hdspm->lock);
+       return change;
+}
+
+#define HDSPM_AUTOSYNC_REF(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+  .name = xname, \
+  .index = xindex, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READ, \
+  .info = snd_hdspm_info_autosync_ref, \
+  .get = snd_hdspm_get_autosync_ref, \
+}
+
+static int hdspm_autosync_ref(hdspm_t * hdspm)
+{
+       /* This looks at the autosync selected sync reference */
+       unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
+
+       switch (status2 & HDSPM_SelSyncRefMask) {
+
+       case HDSPM_SelSyncRef_WORD:
+               return HDSPM_AUTOSYNC_FROM_WORD;
+
+       case HDSPM_SelSyncRef_MADI:
+               return HDSPM_AUTOSYNC_FROM_MADI;
+
+       case HDSPM_SelSyncRef_NVALID:
+               return HDSPM_AUTOSYNC_FROM_NONE;
+
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int snd_hdspm_info_autosync_ref(snd_kcontrol_t * kcontrol,
+                                      snd_ctl_elem_info_t * uinfo)
+{
+       static char *texts[] = { "WordClock", "MADI", "None" };
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 3;
+       if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+               uinfo->value.enumerated.item =
+                   uinfo->value.enumerated.items - 1;
+       strcpy(uinfo->value.enumerated.name,
+              texts[uinfo->value.enumerated.item]);
+       return 0;
+}
+
+static int snd_hdspm_get_autosync_ref(snd_kcontrol_t * kcontrol,
+                                     snd_ctl_elem_value_t * ucontrol)
+{
+       hdspm_t *hdspm = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.enumerated.item[0] = hdspm_pref_sync_ref(hdspm);
+       return 0;
+}
+
+#define HDSPM_LINE_OUT(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+  .name = xname, \
+  .index = xindex, \
+  .info = snd_hdspm_info_line_out, \
+  .get = snd_hdspm_get_line_out, \
+  .put = snd_hdspm_put_line_out \
+}
+
+static int hdspm_line_out(hdspm_t * hdspm)
+{
+       return (hdspm->control_register & HDSPM_LineOut) ? 1 : 0;
+}
+
+
+static int hdspm_set_line_output(hdspm_t * hdspm, int out)
+{
+       if (out)
+               hdspm->control_register |= HDSPM_LineOut;
+       else
+               hdspm->control_register &= ~HDSPM_LineOut;
+       hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
+
+       return 0;
+}
+
+static int snd_hdspm_info_line_out(snd_kcontrol_t * kcontrol,
+                                  snd_ctl_elem_info_t * 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 snd_hdspm_get_line_out(snd_kcontrol_t * kcontrol,
+                                 snd_ctl_elem_value_t * ucontrol)
+{
+       hdspm_t *hdspm = snd_kcontrol_chip(kcontrol);
+
+       spin_lock_irq(&hdspm->lock);
+       ucontrol->value.integer.value[0] = hdspm_line_out(hdspm);
+       spin_unlock_irq(&hdspm->lock);
+       return 0;
+}
+
+static int snd_hdspm_put_line_out(snd_kcontrol_t * kcontrol,
+                                 snd_ctl_elem_value_t * ucontrol)
+{
+       hdspm_t *hdspm = snd_kcontrol_chip(kcontrol);
+       int change;
+       unsigned int val;
+
+       if (!snd_hdspm_use_is_exclusive(hdspm))
+               return -EBUSY;
+       val = ucontrol->value.integer.value[0] & 1;
+       spin_lock_irq(&hdspm->lock);
+       change = (int) val != hdspm_line_out(hdspm);
+       hdspm_set_line_output(hdspm, val);
+       spin_unlock_irq(&hdspm->lock);
+       return change;
+}
+
+#define HDSPM_TX_64(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+  .name = xname, \
+  .index = xindex, \
+  .info = snd_hdspm_info_tx_64, \
+  .get = snd_hdspm_get_tx_64, \
+  .put = snd_hdspm_put_tx_64 \
+}
+
+static int hdspm_tx_64(hdspm_t * hdspm)
+{
+       return (hdspm->control_register & HDSPM_TX_64ch) ? 1 : 0;
+}
+
+static int hdspm_set_tx_64(hdspm_t * hdspm, int out)
+{
+       if (out)
+               hdspm->control_register |= HDSPM_TX_64ch;
+       else
+               hdspm->control_register &= ~HDSPM_TX_64ch;
+       hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
+
+       return 0;
+}
+
+static int snd_hdspm_info_tx_64(snd_kcontrol_t * kcontrol,
+                               snd_ctl_elem_info_t * 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 snd_hdspm_get_tx_64(snd_kcontrol_t * kcontrol,
+                              snd_ctl_elem_value_t * ucontrol)
+{
+       hdspm_t *hdspm = snd_kcontrol_chip(kcontrol);
+
+       spin_lock_irq(&hdspm->lock);
+       ucontrol->value.integer.value[0] = hdspm_tx_64(hdspm);
+       spin_unlock_irq(&hdspm->lock);
+       return 0;
+}
+
+static int snd_hdspm_put_tx_64(snd_kcontrol_t * kcontrol,
+                              snd_ctl_elem_value_t * ucontrol)
+{
+       hdspm_t *hdspm = snd_kcontrol_chip(kcontrol);
+       int change;
+       unsigned int val;
+
+       if (!snd_hdspm_use_is_exclusive(hdspm))
+               return -EBUSY;
+       val = ucontrol->value.integer.value[0] & 1;
+       spin_lock_irq(&hdspm->lock);
+       change = (int) val != hdspm_tx_64(hdspm);
+       hdspm_set_tx_64(hdspm, val);
+       spin_unlock_irq(&hdspm->lock);
+       return change;
+}
+
+#define HDSPM_C_TMS(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+  .name = xname, \
+  .index = xindex, \
+  .info = snd_hdspm_info_c_tms, \
+  .get = snd_hdspm_get_c_tms, \
+  .put = snd_hdspm_put_c_tms \
+}
+
+static int hdspm_c_tms(hdspm_t * hdspm)
+{
+       return (hdspm->control_register & HDSPM_clr_tms) ? 1 : 0;
+}
+
+static int hdspm_set_c_tms(hdspm_t * hdspm, int out)
+{
+       if (out)
+               hdspm->control_register |= HDSPM_clr_tms;
+       else
+               hdspm->control_register &= ~HDSPM_clr_tms;
+       hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
+
+       return 0;
+}
+
+static int snd_hdspm_info_c_tms(snd_kcontrol_t * kcontrol,
+                               snd_ctl_elem_info_t * 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 snd_hdspm_get_c_tms(snd_kcontrol_t * kcontrol,
+                              snd_ctl_elem_value_t * ucontrol)
+{
+       hdspm_t *hdspm = snd_kcontrol_chip(kcontrol);
+
+       spin_lock_irq(&hdspm->lock);
+       ucontrol->value.integer.value[0] = hdspm_c_tms(hdspm);
+       spin_unlock_irq(&hdspm->lock);
+       return 0;
+}
+
+static int snd_hdspm_put_c_tms(snd_kcontrol_t * kcontrol,
+                              snd_ctl_elem_value_t * ucontrol)
+{
+       hdspm_t *hdspm = snd_kcontrol_chip(kcontrol);
+       int change;
+       unsigned int val;
+
+       if (!snd_hdspm_use_is_exclusive(hdspm))
+               return -EBUSY;
+       val = ucontrol->value.integer.value[0] & 1;
+       spin_lock_irq(&hdspm->lock);
+       change = (int) val != hdspm_c_tms(hdspm);
+       hdspm_set_c_tms(hdspm, val);
+       spin_unlock_irq(&hdspm->lock);
+       return change;
+}
+
+#define HDSPM_SAFE_MODE(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+  .name = xname, \
+  .index = xindex, \
+  .info = snd_hdspm_info_safe_mode, \
+  .get = snd_hdspm_get_safe_mode, \
+  .put = snd_hdspm_put_safe_mode \
+}
+
+static int hdspm_safe_mode(hdspm_t * hdspm)
+{
+       return (hdspm->control_register & HDSPM_AutoInp) ? 1 : 0;
+}
+
+static int hdspm_set_safe_mode(hdspm_t * hdspm, int out)
+{
+       if (out)
+               hdspm->control_register |= HDSPM_AutoInp;
+       else
+               hdspm->control_register &= ~HDSPM_AutoInp;
+       hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
+
+       return 0;
+}
+
+static int snd_hdspm_info_safe_mode(snd_kcontrol_t * kcontrol,
+                                   snd_ctl_elem_info_t * 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 snd_hdspm_get_safe_mode(snd_kcontrol_t * kcontrol,
+                                  snd_ctl_elem_value_t * ucontrol)
+{
+       hdspm_t *hdspm = snd_kcontrol_chip(kcontrol);
+
+       spin_lock_irq(&hdspm->lock);
+       ucontrol->value.integer.value[0] = hdspm_safe_mode(hdspm);
+       spin_unlock_irq(&hdspm->lock);
+       return 0;
+}
+
+static int snd_hdspm_put_safe_mode(snd_kcontrol_t * kcontrol,
+                                  snd_ctl_elem_value_t * ucontrol)
+{
+       hdspm_t *hdspm = snd_kcontrol_chip(kcontrol);
+       int change;
+       unsigned int val;
+
+       if (!snd_hdspm_use_is_exclusive(hdspm))
+               return -EBUSY;
+       val = ucontrol->value.integer.value[0] & 1;
+       spin_lock_irq(&hdspm->lock);
+       change = (int) val != hdspm_safe_mode(hdspm);
+       hdspm_set_safe_mode(hdspm, val);
+       spin_unlock_irq(&hdspm->lock);
+       return change;
+}
+
+#define HDSPM_INPUT_SELECT(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+  .name = xname, \
+  .index = xindex, \
+  .info = snd_hdspm_info_input_select, \
+  .get = snd_hdspm_get_input_select, \
+  .put = snd_hdspm_put_input_select \
+}
+
+static int hdspm_input_select(hdspm_t * hdspm)
+{
+       return (hdspm->control_register & HDSPM_InputSelect0) ? 1 : 0;
+}
+
+static int hdspm_set_input_select(hdspm_t * hdspm, int out)
+{
+       if (out)
+               hdspm->control_register |= HDSPM_InputSelect0;
+       else
+               hdspm->control_register &= ~HDSPM_InputSelect0;
+       hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
+
+       return 0;
+}
+
+static int snd_hdspm_info_input_select(snd_kcontrol_t * kcontrol,
+                                      snd_ctl_elem_info_t * uinfo)
+{
+       static char *texts[] = { "optical", "coaxial" };
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 2;
+
+       if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+               uinfo->value.enumerated.item =
+                   uinfo->value.enumerated.items - 1;
+       strcpy(uinfo->value.enumerated.name,
+              texts[uinfo->value.enumerated.item]);
+
+       return 0;
+}
+
+static int snd_hdspm_get_input_select(snd_kcontrol_t * kcontrol,
+                                     snd_ctl_elem_value_t * ucontrol)
+{
+       hdspm_t *hdspm = snd_kcontrol_chip(kcontrol);
+
+       spin_lock_irq(&hdspm->lock);
+       ucontrol->value.enumerated.item[0] = hdspm_input_select(hdspm);
+       spin_unlock_irq(&hdspm->lock);
+       return 0;
+}
+
+static int snd_hdspm_put_input_select(snd_kcontrol_t * kcontrol,
+                                     snd_ctl_elem_value_t * ucontrol)
+{
+       hdspm_t *hdspm = snd_kcontrol_chip(kcontrol);
+       int change;
+       unsigned int val;
+
+       if (!snd_hdspm_use_is_exclusive(hdspm))
+               return -EBUSY;
+       val = ucontrol->value.integer.value[0] & 1;
+       spin_lock_irq(&hdspm->lock);
+       change = (int) val != hdspm_input_select(hdspm);
+       hdspm_set_input_select(hdspm, val);
+       spin_unlock_irq(&hdspm->lock);
+       return change;
+}
+
+/*           Simple Mixer
+  deprecated since to much faders ???
+  MIXER interface says output (source, destination, value)
+   where source > MAX_channels are playback channels 
+   on MADICARD 
+  - playback mixer matrix: [channelout+64] [output] [value]
+  - input(thru) mixer matrix: [channelin] [output] [value]
+  (better do 2 kontrols for seperation ?)
+*/
+
+#define HDSPM_MIXER(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+  .name = xname, \
+  .index = xindex, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+                SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+  .info = snd_hdspm_info_mixer, \
+  .get = snd_hdspm_get_mixer, \
+  .put = snd_hdspm_put_mixer \
+}
+
+static int snd_hdspm_info_mixer(snd_kcontrol_t * kcontrol,
+                               snd_ctl_elem_info_t * uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 3;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 65535;
+       uinfo->value.integer.step = 1;
+       return 0;
+}
+
+static int snd_hdspm_get_mixer(snd_kcontrol_t * kcontrol,
+                              snd_ctl_elem_value_t * ucontrol)
+{
+       hdspm_t *hdspm = snd_kcontrol_chip(kcontrol);
+       int source;
+       int destination;
+
+       source = ucontrol->value.integer.value[0];
+       if (source < 0)
+               source = 0;
+       else if (source >= 2 * HDSPM_MAX_CHANNELS)
+               source = 2 * HDSPM_MAX_CHANNELS - 1;
+
+       destination = ucontrol->value.integer.value[1];
+       if (destination < 0)
+               destination = 0;
+       else if (destination >= HDSPM_MAX_CHANNELS)
+               destination = HDSPM_MAX_CHANNELS - 1;
+
+       spin_lock_irq(&hdspm->lock);
+       if (source >= HDSPM_MAX_CHANNELS)
+               ucontrol->value.integer.value[2] =
+                   hdspm_read_pb_gain(hdspm, destination,
+                                      source - HDSPM_MAX_CHANNELS);
+       else
+               ucontrol->value.integer.value[2] =
+                   hdspm_read_in_gain(hdspm, destination, source);
+
+       spin_unlock_irq(&hdspm->lock);
+
+       return 0;
+}
+
+static int snd_hdspm_put_mixer(snd_kcontrol_t * kcontrol,
+                              snd_ctl_elem_value_t * ucontrol)
+{
+       hdspm_t *hdspm = snd_kcontrol_chip(kcontrol);
+       int change;
+       int source;
+       int destination;
+       int gain;
+
+       if (!snd_hdspm_use_is_exclusive(hdspm))
+               return -EBUSY;
+
+       source = ucontrol->value.integer.value[0];
+       destination = ucontrol->value.integer.value[1];
+
+       if (source < 0 || source >= 2 * HDSPM_MAX_CHANNELS)
+               return -1;
+       if (destination < 0 || destination >= HDSPM_MAX_CHANNELS)
+               return -1;
+
+       gain = ucontrol->value.integer.value[2];
+
+       spin_lock_irq(&hdspm->lock);
+
+       if (source >= HDSPM_MAX_CHANNELS)
+               change = gain != hdspm_read_pb_gain(hdspm, destination,
+                                                   source -
+                                                   HDSPM_MAX_CHANNELS);
+       else
+               change =
+                   gain != hdspm_read_in_gain(hdspm, destination, source);
+
+       if (change) {
+               if (source >= HDSPM_MAX_CHANNELS)
+                       hdspm_write_pb_gain(hdspm, destination,
+                                           source - HDSPM_MAX_CHANNELS,
+                                           gain);
+               else
+                       hdspm_write_in_gain(hdspm, destination, source,
+                                           gain);
+       }
+       spin_unlock_irq(&hdspm->lock);
+
+       return change;
+}
+
+/* The simple mixer control(s) provide gain control for the
+   basic 1:1 mappings of playback streams to output
+   streams. 
+*/
+
+#define HDSPM_PLAYBACK_MIXER \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE | \
+                SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+  .info = snd_hdspm_info_playback_mixer, \
+  .get = snd_hdspm_get_playback_mixer, \
+  .put = snd_hdspm_put_playback_mixer \
+}
+
+static int snd_hdspm_info_playback_mixer(snd_kcontrol_t * kcontrol,
+                                        snd_ctl_elem_info_t * uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 65536;
+       uinfo->value.integer.step = 1;
+       return 0;
+}
+
+static int snd_hdspm_get_playback_mixer(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       hdspm_t *hdspm = snd_kcontrol_chip(kcontrol);
+       int channel;
+       int mapped_channel;
+
+       channel = ucontrol->id.index - 1;
+
+       snd_assert(channel >= 0
+                  || channel < HDSPM_MAX_CHANNELS, return -EINVAL);
+
+       if ((mapped_channel = hdspm->channel_map[channel]) < 0)
+               return -EINVAL;
+
+       spin_lock_irq(&hdspm->lock);
+       ucontrol->value.integer.value[0] =
+           hdspm_read_pb_gain(hdspm, mapped_channel, mapped_channel);
+       spin_unlock_irq(&hdspm->lock);
+
+       /*    snd_printdd("get pb mixer index %d, channel %d, mapped_channel %d, value %d\n",
+          ucontrol->id.index,        channel, mapped_channel,  ucontrol->value.integer.value[0]); 
+        */
+
+       return 0;
+}
+
+static int snd_hdspm_put_playback_mixer(snd_kcontrol_t * kcontrol,
+                                       snd_ctl_elem_value_t * ucontrol)
+{
+       hdspm_t *hdspm = snd_kcontrol_chip(kcontrol);
+       int change;
+       int channel;
+       int mapped_channel;
+       int gain;
+
+       if (!snd_hdspm_use_is_exclusive(hdspm))
+               return -EBUSY;
+
+       channel = ucontrol->id.index - 1;
+
+       snd_assert(channel >= 0
+                  || channel < HDSPM_MAX_CHANNELS, return -EINVAL);
+
+       if ((mapped_channel = hdspm->channel_map[channel]) < 0)
+               return -EINVAL;
+
+       gain = ucontrol->value.integer.value[0];
+
+       spin_lock_irq(&hdspm->lock);
+       change =
+           gain != hdspm_read_pb_gain(hdspm, mapped_channel,
+                                      mapped_channel);
+       if (change)
+               hdspm_write_pb_gain(hdspm, mapped_channel, mapped_channel,
+                                   gain);
+       spin_unlock_irq(&hdspm->lock);
+       return change;
+}
+
+#define HDSPM_WC_SYNC_CHECK(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+  .name = xname, \
+  .index = xindex, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+  .info = snd_hdspm_info_sync_check, \
+  .get = snd_hdspm_get_wc_sync_check \
+}
+
+static int snd_hdspm_info_sync_check(snd_kcontrol_t * kcontrol,
+                                    snd_ctl_elem_info_t * uinfo)
+{
+       static char *texts[] = { "No Lock", "Lock", "Sync" };
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 3;
+       if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+               uinfo->value.enumerated.item =
+                   uinfo->value.enumerated.items - 1;
+       strcpy(uinfo->value.enumerated.name,
+              texts[uinfo->value.enumerated.item]);
+       return 0;
+}
+
+static int hdspm_wc_sync_check(hdspm_t * hdspm)
+{
+       int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
+       if (status2 & HDSPM_wcLock) {
+               if (status2 & HDSPM_wcSync)
+                       return 2;
+               else
+                       return 1;
+       }
+       return 0;
+}
+
+static int snd_hdspm_get_wc_sync_check(snd_kcontrol_t * kcontrol,
+                                      snd_ctl_elem_value_t * ucontrol)
+{
+       hdspm_t *hdspm = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.enumerated.item[0] = hdspm_wc_sync_check(hdspm);
+       return 0;
+}
+
+
+#define HDSPM_MADI_SYNC_CHECK(xname, xindex) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, \
+  .name = xname, \
+  .index = xindex, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+  .info = snd_hdspm_info_sync_check, \
+  .get = snd_hdspm_get_madisync_sync_check \
+}
+
+static int hdspm_madisync_sync_check(hdspm_t * hdspm)
+{
+       int status = hdspm_read(hdspm, HDSPM_statusRegister);
+       if (status & HDSPM_madiLock) {
+               if (status & HDSPM_madiSync)
+                       return 2;
+               else
+                       return 1;
+       }
+       return 0;
+}
+
+static int snd_hdspm_get_madisync_sync_check(snd_kcontrol_t * kcontrol,
+                                            snd_ctl_elem_value_t *
+                                            ucontrol)
+{
+       hdspm_t *hdspm = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.enumerated.item[0] =
+           hdspm_madisync_sync_check(hdspm);
+       return 0;
+}
+
+
+
+
+static snd_kcontrol_new_t snd_hdspm_controls[] = {
+
+       HDSPM_MIXER("Mixer", 0),
+/* 'Sample Clock Source' complies with the alsa control naming scheme */
+       HDSPM_CLOCK_SOURCE("Sample Clock Source", 0),
+
+       HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
+       HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0),
+       HDSPM_AUTOSYNC_REF("AutoSync Reference", 0),
+       HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
+/* 'External Rate' complies with the alsa control naming scheme */
+       HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0),
+       HDSPM_WC_SYNC_CHECK("Word Clock Lock Status", 0),
+       HDSPM_MADI_SYNC_CHECK("MADI Sync Lock Status", 0),
+       HDSPM_LINE_OUT("Line Out", 0),
+       HDSPM_TX_64("TX 64 channels mode", 0),
+       HDSPM_C_TMS("Clear Track Marker", 0),
+       HDSPM_SAFE_MODE("Safe Mode", 0),
+       HDSPM_INPUT_SELECT("Input Select", 0),
+};
+
+static snd_kcontrol_new_t snd_hdspm_playback_mixer = HDSPM_PLAYBACK_MIXER;
+
+
+static int hdspm_update_simple_mixer_controls(hdspm_t * hdspm)
+{
+       int i;
+
+       for (i = hdspm->ds_channels; i < hdspm->ss_channels; ++i) {
+               if (hdspm->system_sample_rate > 48000) {
+                       hdspm->playback_mixer_ctls[i]->vd[0].access =
+                           SNDRV_CTL_ELEM_ACCESS_INACTIVE |
+                           SNDRV_CTL_ELEM_ACCESS_READ |
+                           SNDRV_CTL_ELEM_ACCESS_VOLATILE;
+               } else {
+                       hdspm->playback_mixer_ctls[i]->vd[0].access =
+                           SNDRV_CTL_ELEM_ACCESS_READWRITE |
+                           SNDRV_CTL_ELEM_ACCESS_VOLATILE;
+               }
+               snd_ctl_notify(hdspm->card, SNDRV_CTL_EVENT_MASK_VALUE |
+                              SNDRV_CTL_EVENT_MASK_INFO,
+                              &hdspm->playback_mixer_ctls[i]->id);
+       }
+
+       return 0;
+}
+
+
+static int snd_hdspm_create_controls(snd_card_t * card, hdspm_t * hdspm)
+{
+       unsigned int idx, limit;
+       int err;
+       snd_kcontrol_t *kctl;
+
+       /* add control list first */
+
+       for (idx = 0; idx < ARRAY_SIZE(snd_hdspm_controls); idx++) {
+               if ((err =
+                    snd_ctl_add(card, kctl =
+                                snd_ctl_new1(&snd_hdspm_controls[idx],
+                                             hdspm))) < 0) {
+                       return err;
+               }
+       }
+
+       /* Channel playback mixer as default control 
+          Note: the whole matrix would be 128*HDSPM_MIXER_CHANNELS Faders, thats to big for any alsamixer 
+          they are accesible via special IOCTL on hwdep
+          and the mixer 2dimensional mixer control */
+
+       snd_hdspm_playback_mixer.name = "Chn";
+       limit = HDSPM_MAX_CHANNELS;
+
+       /* The index values are one greater than the channel ID so that alsamixer
+          will display them correctly. We want to use the index for fast lookup
+          of the relevant channel, but if we use it at all, most ALSA software
+          does the wrong thing with it ...
+        */
+
+       for (idx = 0; idx < limit; ++idx) {
+               snd_hdspm_playback_mixer.index = idx + 1;
+               if ((err = snd_ctl_add(card,
+                                      kctl =
+                                      snd_ctl_new1
+                                      (&snd_hdspm_playback_mixer,
+                                       hdspm)))) {
+                       return err;
+               }
+               hdspm->playback_mixer_ctls[idx] = kctl;
+       }
+
+       return 0;
+}
+
+/*------------------------------------------------------------
+   /proc interface 
+ ------------------------------------------------------------*/
+
+static void
+snd_hdspm_proc_read(snd_info_entry_t * entry, snd_info_buffer_t * buffer)
+{
+       hdspm_t *hdspm = (hdspm_t *) entry->private_data;
+       unsigned int status;
+       unsigned int status2;
+       char *pref_sync_ref;
+       char *autosync_ref;
+       char *system_clock_mode;
+       char *clock_source;
+       char *insel;
+       char *syncref;
+       int x, x2;
+
+       status = hdspm_read(hdspm, HDSPM_statusRegister);
+       status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
+
+       snd_iprintf(buffer, "%s (Card #%d) Rev.%x Status2first3bits: %x\n",
+                   hdspm->card_name, hdspm->card->number + 1,
+                   hdspm->firmware_rev,
+                   (status2 & HDSPM_version0) |
+                   (status2 & HDSPM_version1) | (status2 &
+                                                 HDSPM_version2));
+
+       snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n",
+                   hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase);
+
+       snd_iprintf(buffer, "--- System ---\n");
+
+       snd_iprintf(buffer,
+                   "IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n",
+                   status & HDSPM_audioIRQPending,
+                   (status & HDSPM_midi0IRQPending) ? 1 : 0,
+                   (status & HDSPM_midi1IRQPending) ? 1 : 0,
+                   hdspm->irq_count);
+       snd_iprintf(buffer,
+                   "HW pointer: id = %d, rawptr = %d (%d->%d) estimated= %ld (bytes)\n",
+                   ((status & HDSPM_BufferID) ? 1 : 0),
+                   (status & HDSPM_BufferPositionMask),
+                   (status & HDSPM_BufferPositionMask) % (2 *
+                                                          (int)hdspm->
+                                                          period_bytes),
+                   ((status & HDSPM_BufferPositionMask) -
+                    64) % (2 * (int)hdspm->period_bytes),
+                   (long) hdspm_hw_pointer(hdspm) * 4);
+
+       snd_iprintf(buffer,
+                   "MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n",
+                   hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF,
+                   hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF,
+                   hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF,
+                   hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF);
+       snd_iprintf(buffer,
+                   "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, status2=0x%x\n",
+                   hdspm->control_register, hdspm->control2_register,
+                   status, status2);
+
+       snd_iprintf(buffer, "--- Settings ---\n");
+
+       x = 1 << (6 +
+                 hdspm_decode_latency(hdspm->
+                                      control_register &
+                                      HDSPM_LatencyMask));
+
+       snd_iprintf(buffer,
+                   "Size (Latency): %d samples (2 periods of %lu bytes)\n",
+                   x, (unsigned long) hdspm->period_bytes);
+
+       snd_iprintf(buffer, "Line out: %s,   Precise Pointer: %s\n",
+                   (hdspm->
+                    control_register & HDSPM_LineOut) ? "on " : "off",
+                   (hdspm->precise_ptr) ? "on" : "off");
+
+       switch (hdspm->control_register & HDSPM_InputMask) {
+       case HDSPM_InputOptical:
+               insel = "Optical";
+               break;
+       case HDSPM_InputCoaxial:
+               insel = "Coaxial";
+               break;
+       default:
+               insel = "Unkown";
+       }
+
+       switch (hdspm->control_register & HDSPM_SyncRefMask) {
+       case HDSPM_SyncRef_Word:
+               syncref = "WordClock";
+               break;
+       case HDSPM_SyncRef_MADI:
+               syncref = "MADI";
+               break;
+       default:
+               syncref = "Unkown";
+       }
+       snd_iprintf(buffer, "Inputsel = %s, SyncRef = %s\n", insel,
+                   syncref);
+
+       snd_iprintf(buffer,
+                   "ClearTrackMarker = %s, Transmit in %s Channel Mode, Auto Input %s\n",
+                   (hdspm->
+                    control_register & HDSPM_clr_tms) ? "on" : "off",
+                   (hdspm->
+                    control_register & HDSPM_TX_64ch) ? "64" : "56",
+                   (hdspm->
+                    control_register & HDSPM_AutoInp) ? "on" : "off");
+
+       switch (hdspm_clock_source(hdspm)) {
+       case HDSPM_CLOCK_SOURCE_AUTOSYNC:
+               clock_source = "AutoSync";
+               break;
+       case HDSPM_CLOCK_SOURCE_INTERNAL_32KHZ:
+               clock_source = "Internal 32 kHz";
+               break;
+       case HDSPM_CLOCK_SOURCE_INTERNAL_44_1KHZ:
+               clock_source = "Internal 44.1 kHz";
+               break;
+       case HDSPM_CLOCK_SOURCE_INTERNAL_48KHZ:
+               clock_source = "Internal 48 kHz";
+               break;
+       case HDSPM_CLOCK_SOURCE_INTERNAL_64KHZ:
+               clock_source = "Internal 64 kHz";
+               break;
+       case HDSPM_CLOCK_SOURCE_INTERNAL_88_2KHZ:
+               clock_source = "Internal 88.2 kHz";
+               break;
+       case HDSPM_CLOCK_SOURCE_INTERNAL_96KHZ:
+               clock_source = "Internal 96 kHz";
+               break;
+       default:
+               clock_source = "Error";
+       }
+       snd_iprintf(buffer, "Sample Clock Source: %s\n", clock_source);
+       if (!(hdspm->control_register & HDSPM_ClockModeMaster)) {
+               system_clock_mode = "Slave";
+       } else {
+               system_clock_mode = "Master";
+       }
+       snd_iprintf(buffer, "System Clock Mode: %s\n", system_clock_mode);
+
+       switch (hdspm_pref_sync_ref(hdspm)) {
+       case HDSPM_SYNC_FROM_WORD:
+               pref_sync_ref = "Word Clock";
+               break;
+       case HDSPM_SYNC_FROM_MADI:
+               pref_sync_ref = "MADI Sync";
+               break;
+       default:
+               pref_sync_ref = "XXXX Clock";
+               break;
+       }
+       snd_iprintf(buffer, "Preferred Sync Reference: %s\n",
+                   pref_sync_ref);
+
+       snd_iprintf(buffer, "System Clock Frequency: %d\n",
+                   hdspm->system_sample_rate);
+
+
+       snd_iprintf(buffer, "--- Status:\n");
+
+       x = status & HDSPM_madiSync;
+       x2 = status2 & HDSPM_wcSync;
+
+       snd_iprintf(buffer, "Inputs MADI=%s, WordClock=%s\n",
+                   (status & HDSPM_madiLock) ? (x ? "Sync" : "Lock") :
+                   "NoLock",
+                   (status2 & HDSPM_wcLock) ? (x2 ? "Sync" : "Lock") :
+                   "NoLock");
+
+       switch (hdspm_autosync_ref(hdspm)) {
+       case HDSPM_AUTOSYNC_FROM_WORD:
+               autosync_ref = "Word Clock";
+               break;
+       case HDSPM_AUTOSYNC_FROM_MADI:
+               autosync_ref = "MADI Sync";
+               break;
+       case HDSPM_AUTOSYNC_FROM_NONE:
+               autosync_ref = "Input not valid";
+               break;
+       default:
+               autosync_ref = "---";
+               break;
+       }
+       snd_iprintf(buffer,
+                   "AutoSync: Reference= %s, Freq=%d (MADI = %d, Word = %d)\n",
+                   autosync_ref, hdspm_external_sample_rate(hdspm),
+                   (status & HDSPM_madiFreqMask) >> 22,
+                   (status2 & HDSPM_wcFreqMask) >> 5);
+
+       snd_iprintf(buffer, "Input: %s, Mode=%s\n",
+                   (status & HDSPM_AB_int) ? "Coax" : "Optical",
+                   (status & HDSPM_RX_64ch) ? "64 channels" :
+                   "56 channels");
+
+       snd_iprintf(buffer, "\n");
+}
+
+static void __devinit snd_hdspm_proc_init(hdspm_t * hdspm)
+{
+       snd_info_entry_t *entry;
+
+       if (!snd_card_proc_new(hdspm->card, "hdspm", &entry))
+               snd_info_set_text_ops(entry, hdspm, 1024,
+                                     snd_hdspm_proc_read);
+}
+
+/*------------------------------------------------------------
+   hdspm intitialize 
+ ------------------------------------------------------------*/
+
+static int snd_hdspm_set_defaults(hdspm_t * hdspm)
+{
+       unsigned int i;
+
+       /* ASSUMPTION: hdspm->lock is either held, or there is no need to
+          hold it (e.g. during module initalization).
+        */
+
+       /* set defaults:       */
+
+       hdspm->control_register = HDSPM_ClockModeMaster |       /* Master Cloack Mode on */
+           hdspm_encode_latency(7) |   /* latency maximum = 8192 samples */
+           HDSPM_InputCoaxial |        /* Input Coax not Optical */
+           HDSPM_SyncRef_MADI |        /* Madi is syncclock */
+           HDSPM_LineOut |     /* Analog output in */
+           HDSPM_TX_64ch |     /* transmit in 64ch mode */
+           HDSPM_AutoInp;      /* AutoInput chossing (takeover) */
+
+       /* ! HDSPM_Frequency0|HDSPM_Frequency1 = 44.1khz */
+       /* !  HDSPM_DoubleSpeed HDSPM_QuadSpeed = normal speed */
+       /* ! HDSPM_clr_tms = do not clear bits in track marks */
+
+       hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
+
+#ifdef SNDRV_BIG_ENDIAN
+       hdspm->control2_register = HDSPM_BIGENDIAN_MODE;
+#else
+       hdspm->control2_register = 0;
+#endif
+
+       hdspm_write(hdspm, HDSPM_control2Reg, hdspm->control2_register);
+       hdspm_compute_period_size(hdspm);
+
+       /* silence everything */
+
+       all_in_all_mixer(hdspm, 0 * UNITY_GAIN);
+
+       if (line_outs_monitor[hdspm->dev]) {
+
+               snd_printk(KERN_INFO "HDSPM: sending all playback streams to line outs.\n");
+
+               for (i = 0; i < HDSPM_MIXER_CHANNELS; i++) {
+                       if (hdspm_write_pb_gain(hdspm, i, i, UNITY_GAIN))
+                               return -EIO;
+               }
+       }
+
+       /* set a default rate so that the channel map is set up. */
+       hdspm->channel_map = channel_map_madi_ss;
+       hdspm_set_rate(hdspm, 44100, 1);
+
+       return 0;
+}
+
+
+/*------------------------------------------------------------
+   interupt 
+ ------------------------------------------------------------*/
+
+static irqreturn_t snd_hdspm_interrupt(int irq, void *dev_id,
+                                      struct pt_regs *regs)
+{
+       hdspm_t *hdspm = (hdspm_t *) dev_id;
+       unsigned int status;
+       int audio;
+       int midi0;
+       int midi1;
+       unsigned int midi0status;
+       unsigned int midi1status;
+       int schedule = 0;
+
+       status = hdspm_read(hdspm, HDSPM_statusRegister);
+
+       audio = status & HDSPM_audioIRQPending;
+       midi0 = status & HDSPM_midi0IRQPending;
+       midi1 = status & HDSPM_midi1IRQPending;
+
+       if (!audio && !midi0 && !midi1)
+               return IRQ_NONE;
+
+       hdspm_write(hdspm, HDSPM_interruptConfirmation, 0);
+       hdspm->irq_count++;
+
+       midi0status = hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xff;
+       midi1status = hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xff;
+
+       if (audio) {
+
+               if (hdspm->capture_substream)
+                       snd_pcm_period_elapsed(hdspm->pcm->
+                                              streams
+                                              [SNDRV_PCM_STREAM_CAPTURE].
+                                              substream);
+
+               if (hdspm->playback_substream)
+                       snd_pcm_period_elapsed(hdspm->pcm->
+                                              streams
+                                              [SNDRV_PCM_STREAM_PLAYBACK].
+                                              substream);
+       }
+
+       if (midi0 && midi0status) {
+               /* we disable interrupts for this input until processing is done */
+               hdspm->control_register &= ~HDSPM_Midi0InterruptEnable;
+               hdspm_write(hdspm, HDSPM_controlRegister,
+                           hdspm->control_register);
+               hdspm->midi[0].pending = 1;
+               schedule = 1;
+       }
+       if (midi1 && midi1status) {
+               /* we disable interrupts for this input until processing is done */
+               hdspm->control_register &= ~HDSPM_Midi1InterruptEnable;
+               hdspm_write(hdspm, HDSPM_controlRegister,
+                           hdspm->control_register);
+               hdspm->midi[1].pending = 1;
+               schedule = 1;
+       }
+       if (schedule)
+               tasklet_hi_schedule(&hdspm->midi_tasklet);
+       return IRQ_HANDLED;
+}
+
+/*------------------------------------------------------------
+   pcm interface 
+  ------------------------------------------------------------*/
+
+
+static snd_pcm_uframes_t snd_hdspm_hw_pointer(snd_pcm_substream_t *
+                                             substream)
+{
+       hdspm_t *hdspm = snd_pcm_substream_chip(substream);
+       return hdspm_hw_pointer(hdspm);
+}
+
+static char *hdspm_channel_buffer_location(hdspm_t * hdspm,
+                                          int stream, int channel)
+{
+       int mapped_channel;
+
+       snd_assert(channel >= 0
+                  || channel < HDSPM_MAX_CHANNELS, return NULL);
+
+       if ((mapped_channel = hdspm->channel_map[channel]) < 0)
+               return NULL;
+
+       if (stream == SNDRV_PCM_STREAM_CAPTURE) {
+               return hdspm->capture_buffer +
+                   mapped_channel * HDSPM_CHANNEL_BUFFER_BYTES;
+       } else {
+               return hdspm->playback_buffer +
+                   mapped_channel * HDSPM_CHANNEL_BUFFER_BYTES;
+       }
+}
+
+
+/* dont know why need it ??? */
+static int snd_hdspm_playback_copy(snd_pcm_substream_t * substream,
+                                  int channel, snd_pcm_uframes_t pos,
+                                  void __user *src, snd_pcm_uframes_t count)
+{
+       hdspm_t *hdspm = snd_pcm_substream_chip(substream);
+       char *channel_buf;
+
+       snd_assert(pos + count <= HDSPM_CHANNEL_BUFFER_BYTES / 4,
+                  return -EINVAL);
+
+       channel_buf = hdspm_channel_buffer_location(hdspm,
+                                                   substream->pstr->
+                                                   stream, channel);
+
+       snd_assert(channel_buf != NULL, return -EIO);
+
+       return copy_from_user(channel_buf + pos * 4, src, count * 4);
+}
+
+static int snd_hdspm_capture_copy(snd_pcm_substream_t * substream,
+                                 int channel, snd_pcm_uframes_t pos,
+                                 void __user *dst, snd_pcm_uframes_t count)
+{
+       hdspm_t *hdspm = snd_pcm_substream_chip(substream);
+       char *channel_buf;
+
+       snd_assert(pos + count <= HDSPM_CHANNEL_BUFFER_BYTES / 4,
+                  return -EINVAL);
+
+       channel_buf = hdspm_channel_buffer_location(hdspm,
+                                                   substream->pstr->
+                                                   stream, channel);
+       snd_assert(channel_buf != NULL, return -EIO);
+       return copy_to_user(dst, channel_buf + pos * 4, count * 4);
+}
+
+static int snd_hdspm_hw_silence(snd_pcm_substream_t * substream,
+                               int channel, snd_pcm_uframes_t pos,
+                               snd_pcm_uframes_t count)
+{
+       hdspm_t *hdspm = snd_pcm_substream_chip(substream);
+       char *channel_buf;
+
+       channel_buf =
+           hdspm_channel_buffer_location(hdspm, substream->pstr->stream,
+                                         channel);
+       snd_assert(channel_buf != NULL, return -EIO);
+       memset(channel_buf + pos * 4, 0, count * 4);
+       return 0;
+}
+
+static int snd_hdspm_reset(snd_pcm_substream_t * substream)
+{
+       snd_pcm_runtime_t *runtime = substream->runtime;
+       hdspm_t *hdspm = snd_pcm_substream_chip(substream);
+       snd_pcm_substream_t *other;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               other = hdspm->capture_substream;
+       else
+               other = hdspm->playback_substream;
+
+       if (hdspm->running)
+               runtime->status->hw_ptr = hdspm_hw_pointer(hdspm);
+       else
+               runtime->status->hw_ptr = 0;
+       if (other) {
+               struct list_head *pos;
+               snd_pcm_substream_t *s;
+               snd_pcm_runtime_t *oruntime = other->runtime;
+               snd_pcm_group_for_each(pos, substream) {
+                       s = snd_pcm_group_substream_entry(pos);
+                       if (s == other) {
+                               oruntime->status->hw_ptr =
+                                   runtime->status->hw_ptr;
+                               break;
+                       }
+               }
+       }
+       return 0;
+}
+
+static int snd_hdspm_hw_params(snd_pcm_substream_t * substream,
+                              snd_pcm_hw_params_t * params)
+{
+       hdspm_t *hdspm = snd_pcm_substream_chip(substream);
+       int err;
+       int i;
+       pid_t this_pid;
+       pid_t other_pid;
+       struct snd_sg_buf *sgbuf;
+
+
+       spin_lock_irq(&hdspm->lock);
+
+       if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               this_pid = hdspm->playback_pid;
+               other_pid = hdspm->capture_pid;
+       } else {
+               this_pid = hdspm->capture_pid;
+               other_pid = hdspm->playback_pid;
+       }
+
+       if ((other_pid > 0) && (this_pid != other_pid)) {
+
+               /* The other stream is open, and not by the same
+                  task as this one. Make sure that the parameters
+                  that matter are the same.
+                */
+
+               if (params_rate(params) != hdspm->system_sample_rate) {
+                       spin_unlock_irq(&hdspm->lock);
+                       _snd_pcm_hw_param_setempty(params,
+                                                  SNDRV_PCM_HW_PARAM_RATE);
+                       return -EBUSY;
+               }
+
+               if (params_period_size(params) != hdspm->period_bytes / 4) {
+                       spin_unlock_irq(&hdspm->lock);
+                       _snd_pcm_hw_param_setempty(params,
+                                                  SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+                       return -EBUSY;
+               }
+
+       }
+       /* We're fine. */
+       spin_unlock_irq(&hdspm->lock);
+
+       /* how to make sure that the rate matches an externally-set one ?   */
+
+       spin_lock_irq(&hdspm->lock);
+       if ((err = hdspm_set_rate(hdspm, params_rate(params), 0)) < 0) {
+               spin_unlock_irq(&hdspm->lock);
+               _snd_pcm_hw_param_setempty(params,
+                                          SNDRV_PCM_HW_PARAM_RATE);
+               return err;
+       }
+       spin_unlock_irq(&hdspm->lock);
+
+       if ((err =
+            hdspm_set_interrupt_interval(hdspm,
+                                         params_period_size(params))) <
+           0) {
+               _snd_pcm_hw_param_setempty(params,
+                                          SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+               return err;
+       }
+
+       /* Memory allocation, takashi's method, dont know if we should spinlock  */
+       /* malloc all buffer even if not enabled to get sure */
+       /* malloc only needed bytes */
+       err =
+           snd_pcm_lib_malloc_pages(substream,
+                                    HDSPM_CHANNEL_BUFFER_BYTES *
+                                    params_channels(params));
+       if (err < 0)
+               return err;
+
+       sgbuf = snd_pcm_substream_sgbuf(substream);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+
+               hdspm_set_sgbuf(hdspm, sgbuf, HDSPM_pageAddressBufferOut,
+                               params_channels(params));
+
+               for (i = 0; i < params_channels(params); ++i)
+                       snd_hdspm_enable_out(hdspm, i, 1);
+
+               hdspm->playback_buffer =
+                   (unsigned char *) substream->runtime->dma_area;
+       } else {
+               hdspm_set_sgbuf(hdspm, sgbuf, HDSPM_pageAddressBufferIn,
+                               params_channels(params));
+
+               for (i = 0; i < params_channels(params); ++i)
+                       snd_hdspm_enable_in(hdspm, i, 1);
+
+               hdspm->capture_buffer =
+                   (unsigned char *) substream->runtime->dma_area;
+       }
+       return 0;
+}
+
+static int snd_hdspm_hw_free(snd_pcm_substream_t * substream)
+{
+       int i;
+       hdspm_t *hdspm = snd_pcm_substream_chip(substream);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+
+               /* params_channels(params) should be enough, 
+                  but to get sure in case of error */
+               for (i = 0; i < HDSPM_MAX_CHANNELS; ++i)
+                       snd_hdspm_enable_out(hdspm, i, 0);
+
+               hdspm->playback_buffer = NULL;
+       } else {
+               for (i = 0; i < HDSPM_MAX_CHANNELS; ++i)
+                       snd_hdspm_enable_in(hdspm, i, 0);
+
+               hdspm->capture_buffer = NULL;
+
+       }
+
+       snd_pcm_lib_free_pages(substream);
+
+       return 0;
+}
+
+static int snd_hdspm_channel_info(snd_pcm_substream_t * substream,
+                                 snd_pcm_channel_info_t * info)
+{
+       hdspm_t *hdspm = snd_pcm_substream_chip(substream);
+       int mapped_channel;
+
+       snd_assert(info->channel < HDSPM_MAX_CHANNELS, return -EINVAL);
+
+       if ((mapped_channel = hdspm->channel_map[info->channel]) < 0)
+               return -EINVAL;
+
+       info->offset = mapped_channel * HDSPM_CHANNEL_BUFFER_BYTES;
+       info->first = 0;
+       info->step = 32;
+       return 0;
+}
+
+static int snd_hdspm_ioctl(snd_pcm_substream_t * substream,
+                          unsigned int cmd, void *arg)
+{
+       switch (cmd) {
+       case SNDRV_PCM_IOCTL1_RESET:
+               {
+                       return snd_hdspm_reset(substream);
+               }
+
+       case SNDRV_PCM_IOCTL1_CHANNEL_INFO:
+               {
+                       snd_pcm_channel_info_t *info = arg;
+                       return snd_hdspm_channel_info(substream, info);
+               }
+       default:
+               break;
+       }
+
+       return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static int snd_hdspm_trigger(snd_pcm_substream_t * substream, int cmd)
+{
+       hdspm_t *hdspm = snd_pcm_substream_chip(substream);
+       snd_pcm_substream_t *other;
+       int running;
+
+       spin_lock(&hdspm->lock);
+       running = hdspm->running;
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               running |= 1 << substream->stream;
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               running &= ~(1 << substream->stream);
+               break;
+       default:
+               snd_BUG();
+               spin_unlock(&hdspm->lock);
+               return -EINVAL;
+       }
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               other = hdspm->capture_substream;
+       else
+               other = hdspm->playback_substream;
+
+       if (other) {
+               struct list_head *pos;
+               snd_pcm_substream_t *s;
+               snd_pcm_group_for_each(pos, substream) {
+                       s = snd_pcm_group_substream_entry(pos);
+                       if (s == other) {
+                               snd_pcm_trigger_done(s, substream);
+                               if (cmd == SNDRV_PCM_TRIGGER_START)
+                                       running |= 1 << s->stream;
+                               else
+                                       running &= ~(1 << s->stream);
+                               goto _ok;
+                       }
+               }
+               if (cmd == SNDRV_PCM_TRIGGER_START) {
+                       if (!(running & (1 << SNDRV_PCM_STREAM_PLAYBACK))
+                           && substream->stream ==
+                           SNDRV_PCM_STREAM_CAPTURE)
+                               hdspm_silence_playback(hdspm);
+               } else {
+                       if (running &&
+                           substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                               hdspm_silence_playback(hdspm);
+               }
+       } else {
+               if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+                       hdspm_silence_playback(hdspm);
+       }
+      _ok:
+       snd_pcm_trigger_done(substream, substream);
+       if (!hdspm->running && running)
+               hdspm_start_audio(hdspm);
+       else if (hdspm->running && !running)
+               hdspm_stop_audio(hdspm);
+       hdspm->running = running;
+       spin_unlock(&hdspm->lock);
+
+       return 0;
+}
+
+static int snd_hdspm_prepare(snd_pcm_substream_t * substream)
+{
+       return 0;
+}
+
+static unsigned int period_sizes[] =
+    { 64, 128, 256, 512, 1024, 2048, 4096, 8192 };
+
+static snd_pcm_hardware_t snd_hdspm_playback_subinfo = {
+       .info = (SNDRV_PCM_INFO_MMAP |
+                SNDRV_PCM_INFO_MMAP_VALID |
+                SNDRV_PCM_INFO_NONINTERLEAVED |
+                SNDRV_PCM_INFO_SYNC_START | SNDRV_PCM_INFO_DOUBLE),
+       .formats = SNDRV_PCM_FMTBIT_S32_LE,
+       .rates = (SNDRV_PCM_RATE_32000 |
+                 SNDRV_PCM_RATE_44100 |
+                 SNDRV_PCM_RATE_48000 |
+                 SNDRV_PCM_RATE_64000 |
+                 SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000),
+       .rate_min = 32000,
+       .rate_max = 96000,
+       .channels_min = 1,
+       .channels_max = HDSPM_MAX_CHANNELS,
+       .buffer_bytes_max =
+           HDSPM_CHANNEL_BUFFER_BYTES * HDSPM_MAX_CHANNELS,
+       .period_bytes_min = (64 * 4),
+       .period_bytes_max = (8192 * 4) * HDSPM_MAX_CHANNELS,
+       .periods_min = 2,
+       .periods_max = 2,
+       .fifo_size = 0
+};
+
+static snd_pcm_hardware_t snd_hdspm_capture_subinfo = {
+       .info = (SNDRV_PCM_INFO_MMAP |
+                SNDRV_PCM_INFO_MMAP_VALID |
+                SNDRV_PCM_INFO_NONINTERLEAVED |
+                SNDRV_PCM_INFO_SYNC_START),
+       .formats = SNDRV_PCM_FMTBIT_S32_LE,
+       .rates = (SNDRV_PCM_RATE_32000 |
+                 SNDRV_PCM_RATE_44100 |
+                 SNDRV_PCM_RATE_48000 |
+                 SNDRV_PCM_RATE_64000 |
+                 SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000),
+       .rate_min = 32000,
+       .rate_max = 96000,
+       .channels_min = 1,
+       .channels_max = HDSPM_MAX_CHANNELS,
+       .buffer_bytes_max =
+           HDSPM_CHANNEL_BUFFER_BYTES * HDSPM_MAX_CHANNELS,
+       .period_bytes_min = (64 * 4),
+       .period_bytes_max = (8192 * 4) * HDSPM_MAX_CHANNELS,
+       .periods_min = 2,
+       .periods_max = 2,
+       .fifo_size = 0
+};
+
+static snd_pcm_hw_constraint_list_t hw_constraints_period_sizes = {
+       .count = ARRAY_SIZE(period_sizes),
+       .list = period_sizes,
+       .mask = 0
+};
+
+
+static int snd_hdspm_hw_rule_channels_rate(snd_pcm_hw_params_t * params,
+                                          snd_pcm_hw_rule_t * rule)
+{
+       hdspm_t *hdspm = rule->private;
+       snd_interval_t *c =
+           hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+       snd_interval_t *r =
+           hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+
+       if (r->min > 48000) {
+               snd_interval_t t = {
+                       .min = 1,
+                       .max = hdspm->ds_channels,
+                       .integer = 1,
+               };
+               return snd_interval_refine(c, &t);
+       } else if (r->max < 64000) {
+               snd_interval_t t = {
+                       .min = 1,
+                       .max = hdspm->ss_channels,
+                       .integer = 1,
+               };
+               return snd_interval_refine(c, &t);
+       }
+       return 0;
+}
+
+static int snd_hdspm_hw_rule_rate_channels(snd_pcm_hw_params_t * params,
+                                          snd_pcm_hw_rule_t * rule)
+{
+       hdspm_t *hdspm = rule->private;
+       snd_interval_t *c =
+           hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+       snd_interval_t *r =
+           hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+
+       if (c->min <= hdspm->ss_channels) {
+               snd_interval_t t = {
+                       .min = 32000,
+                       .max = 48000,
+                       .integer = 1,
+               };
+               return snd_interval_refine(r, &t);
+       } else if (c->max > hdspm->ss_channels) {
+               snd_interval_t t = {
+                       .min = 64000,
+                       .max = 96000,
+                       .integer = 1,
+               };
+
+               return snd_interval_refine(r, &t);
+       }
+       return 0;
+}
+
+static int snd_hdspm_playback_open(snd_pcm_substream_t * substream)
+{
+       hdspm_t *hdspm = snd_pcm_substream_chip(substream);
+       snd_pcm_runtime_t *runtime = substream->runtime;
+
+       snd_printdd("Open device substream %d\n", substream->stream);
+
+       spin_lock_irq(&hdspm->lock);
+
+       snd_pcm_set_sync(substream);
+
+       runtime->hw = snd_hdspm_playback_subinfo;
+
+       if (hdspm->capture_substream == NULL)
+               hdspm_stop_audio(hdspm);
+
+       hdspm->playback_pid = current->pid;
+       hdspm->playback_substream = substream;
+
+       spin_unlock_irq(&hdspm->lock);
+
+       snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+
+       snd_pcm_hw_constraint_list(runtime, 0,
+                                  SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+                                  &hw_constraints_period_sizes);
+
+       snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+                           snd_hdspm_hw_rule_channels_rate, hdspm,
+                           SNDRV_PCM_HW_PARAM_RATE, -1);
+
+       snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+                           snd_hdspm_hw_rule_rate_channels, hdspm,
+                           SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+
+       return 0;
+}
+
+static int snd_hdspm_playback_release(snd_pcm_substream_t * substream)
+{
+       hdspm_t *hdspm = snd_pcm_substream_chip(substream);
+
+       spin_lock_irq(&hdspm->lock);
+
+       hdspm->playback_pid = -1;
+       hdspm->playback_substream = NULL;
+
+       spin_unlock_irq(&hdspm->lock);
+
+       return 0;
+}
+
+
+static int snd_hdspm_capture_open(snd_pcm_substream_t * substream)
+{
+       hdspm_t *hdspm = snd_pcm_substream_chip(substream);
+       snd_pcm_runtime_t *runtime = substream->runtime;
+
+       spin_lock_irq(&hdspm->lock);
+       snd_pcm_set_sync(substream);
+       runtime->hw = snd_hdspm_capture_subinfo;
+
+       if (hdspm->playback_substream == NULL)
+               hdspm_stop_audio(hdspm);
+
+       hdspm->capture_pid = current->pid;
+       hdspm->capture_substream = substream;
+
+       spin_unlock_irq(&hdspm->lock);
+
+       snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+       snd_pcm_hw_constraint_list(runtime, 0,
+                                  SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+                                  &hw_constraints_period_sizes);
+
+       snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+                           snd_hdspm_hw_rule_channels_rate, hdspm,
+                           SNDRV_PCM_HW_PARAM_RATE, -1);
+
+       snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+                           snd_hdspm_hw_rule_rate_channels, hdspm,
+                           SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+       return 0;
+}
+
+static int snd_hdspm_capture_release(snd_pcm_substream_t * substream)
+{
+       hdspm_t *hdspm = snd_pcm_substream_chip(substream);
+
+       spin_lock_irq(&hdspm->lock);
+
+       hdspm->capture_pid = -1;
+       hdspm->capture_substream = NULL;
+
+       spin_unlock_irq(&hdspm->lock);
+       return 0;
+}
+
+static int snd_hdspm_hwdep_dummy_op(snd_hwdep_t * hw, struct file *file)
+{
+       /* we have nothing to initialize but the call is required */
+       return 0;
+}
+
+
+static int snd_hdspm_hwdep_ioctl(snd_hwdep_t * hw, struct file *file,
+                                unsigned int cmd, unsigned long arg)
+{
+       hdspm_t *hdspm = (hdspm_t *) hw->private_data;
+       struct sndrv_hdspm_mixer_ioctl mixer;
+       hdspm_config_info_t info;
+       hdspm_version_t hdspm_version;
+       struct sndrv_hdspm_peak_rms_ioctl rms;
+
+       switch (cmd) {
+
+               
+       case SNDRV_HDSPM_IOCTL_GET_PEAK_RMS:
+               if (copy_from_user(&rms, (void __user *)arg, sizeof(rms)))
+                       return -EFAULT;
+               /* maybe there is a chance to memorymap in future so dont touch just copy */
+               if(copy_to_user_fromio((void __user *)rms.peak,
+                                      hdspm->iobase+HDSPM_MADI_peakrmsbase,
+                                      sizeof(hdspm_peak_rms_t)) != 0 )
+                       return -EFAULT;
+
+               break;
+               
+
+       case SNDRV_HDSPM_IOCTL_GET_CONFIG_INFO:
+
+               spin_lock_irq(&hdspm->lock);
+               info.pref_sync_ref =
+                   (unsigned char) hdspm_pref_sync_ref(hdspm);
+               info.wordclock_sync_check =
+                   (unsigned char) hdspm_wc_sync_check(hdspm);
+
+               info.system_sample_rate = hdspm->system_sample_rate;
+               info.autosync_sample_rate =
+                   hdspm_external_sample_rate(hdspm);
+               info.system_clock_mode =
+                   (unsigned char) hdspm_system_clock_mode(hdspm);
+               info.clock_source =
+                   (unsigned char) hdspm_clock_source(hdspm);
+               info.autosync_ref =
+                   (unsigned char) hdspm_autosync_ref(hdspm);
+               info.line_out = (unsigned char) hdspm_line_out(hdspm);
+               info.passthru = 0;
+               spin_unlock_irq(&hdspm->lock);
+               if (copy_to_user((void __user *) arg, &info, sizeof(info)))
+                       return -EFAULT;
+               break;
+
+       case SNDRV_HDSPM_IOCTL_GET_VERSION:
+               hdspm_version.firmware_rev = hdspm->firmware_rev;
+               if (copy_to_user((void __user *) arg, &hdspm_version,
+                                sizeof(hdspm_version)))
+                       return -EFAULT;
+               break;
+
+       case SNDRV_HDSPM_IOCTL_GET_MIXER:
+               if (copy_from_user(&mixer, (void __user *)arg, sizeof(mixer)))
+                       return -EFAULT;
+               if (copy_to_user
+                   ((void __user *)mixer.mixer, hdspm->mixer, sizeof(hdspm_mixer_t)))
+                       return -EFAULT;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static snd_pcm_ops_t snd_hdspm_playback_ops = {
+       .open = snd_hdspm_playback_open,
+       .close = snd_hdspm_playback_release,
+       .ioctl = snd_hdspm_ioctl,
+       .hw_params = snd_hdspm_hw_params,
+       .hw_free = snd_hdspm_hw_free,
+       .prepare = snd_hdspm_prepare,
+       .trigger = snd_hdspm_trigger,
+       .pointer = snd_hdspm_hw_pointer,
+       .copy = snd_hdspm_playback_copy,
+       .silence = snd_hdspm_hw_silence,
+       .page = snd_pcm_sgbuf_ops_page,
+};
+
+static snd_pcm_ops_t snd_hdspm_capture_ops = {
+       .open = snd_hdspm_capture_open,
+       .close = snd_hdspm_capture_release,
+       .ioctl = snd_hdspm_ioctl,
+       .hw_params = snd_hdspm_hw_params,
+       .hw_free = snd_hdspm_hw_free,
+       .prepare = snd_hdspm_prepare,
+       .trigger = snd_hdspm_trigger,
+       .pointer = snd_hdspm_hw_pointer,
+       .copy = snd_hdspm_capture_copy,
+       .page = snd_pcm_sgbuf_ops_page,
+};
+
+static int __devinit snd_hdspm_create_hwdep(snd_card_t * card,
+                                           hdspm_t * hdspm)
+{
+       snd_hwdep_t *hw;
+       int err;
+
+       if ((err = snd_hwdep_new(card, "HDSPM hwdep", 0, &hw)) < 0)
+               return err;
+
+       hdspm->hwdep = hw;
+       hw->private_data = hdspm;
+       strcpy(hw->name, "HDSPM hwdep interface");
+
+       hw->ops.open = snd_hdspm_hwdep_dummy_op;
+       hw->ops.ioctl = snd_hdspm_hwdep_ioctl;
+       hw->ops.release = snd_hdspm_hwdep_dummy_op;
+
+       return 0;
+}
+
+
+/*------------------------------------------------------------
+   memory interface 
+ ------------------------------------------------------------*/
+static int __devinit snd_hdspm_preallocate_memory(hdspm_t * hdspm)
+{
+       int err;
+       snd_pcm_t *pcm;
+       size_t wanted;
+
+       pcm = hdspm->pcm;
+
+       wanted = HDSPM_DMA_AREA_BYTES + 4096;   /* dont know why, but it works */
+
+       if ((err =
+            snd_pcm_lib_preallocate_pages_for_all(pcm,
+                                                  SNDRV_DMA_TYPE_DEV_SG,
+                                                  snd_dma_pci_data(hdspm->pci),
+                                                  wanted,
+                                                  wanted)) < 0) {
+               snd_printdd("Could not preallocate %d  Bytes\n", wanted);
+
+               return err;
+       } else
+               snd_printdd(" Preallocated %d  Bytes\n", wanted);
+
+       return 0;
+}
+
+static int snd_hdspm_memory_free(hdspm_t * hdspm)
+{
+       snd_printdd("memory_free_for_all %p\n", hdspm->pcm);
+
+       snd_pcm_lib_preallocate_free_for_all(hdspm->pcm);
+       return 0;
+}
+
+
+static void hdspm_set_sgbuf(hdspm_t * hdspm, struct snd_sg_buf *sgbuf,
+                            unsigned int reg, int channels)
+{
+       int i;
+       for (i = 0; i < (channels * 16); i++)
+               hdspm_write(hdspm, reg + 4 * i,
+                           snd_pcm_sgbuf_get_addr(sgbuf,
+                                                  (size_t) 4096 * i));
+}
+
+/* ------------- ALSA Devices ---------------------------- */
+static int __devinit snd_hdspm_create_pcm(snd_card_t * card,
+                                         hdspm_t * hdspm)
+{
+       snd_pcm_t *pcm;
+       int err;
+
+       if ((err = snd_pcm_new(card, hdspm->card_name, 0, 1, 1, &pcm)) < 0)
+               return err;
+
+       hdspm->pcm = pcm;
+       pcm->private_data = hdspm;
+       strcpy(pcm->name, hdspm->card_name);
+
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+                       &snd_hdspm_playback_ops);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+                       &snd_hdspm_capture_ops);
+
+       pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
+
+       if ((err = snd_hdspm_preallocate_memory(hdspm)) < 0)
+               return err;
+
+       return 0;
+}
+
+static inline void snd_hdspm_initialize_midi_flush(hdspm_t * hdspm)
+{
+       snd_hdspm_flush_midi_input(hdspm, 0);
+       snd_hdspm_flush_midi_input(hdspm, 1);
+}
+
+static int __devinit snd_hdspm_create_alsa_devices(snd_card_t * card,
+                                                  hdspm_t * hdspm)
+{
+       int err;
+
+       snd_printdd("Create card...\n");
+       if ((err = snd_hdspm_create_pcm(card, hdspm)) < 0)
+               return err;
+
+       if ((err = snd_hdspm_create_midi(card, hdspm, 0)) < 0)
+               return err;
+
+       if ((err = snd_hdspm_create_midi(card, hdspm, 1)) < 0)
+               return err;
+
+       if ((err = snd_hdspm_create_controls(card, hdspm)) < 0)
+               return err;
+
+       if ((err = snd_hdspm_create_hwdep(card, hdspm)) < 0)
+               return err;
+
+       snd_printdd("proc init...\n");
+       snd_hdspm_proc_init(hdspm);
+
+       hdspm->system_sample_rate = -1;
+       hdspm->last_external_sample_rate = -1;
+       hdspm->last_internal_sample_rate = -1;
+       hdspm->playback_pid = -1;
+       hdspm->capture_pid = -1;
+       hdspm->capture_substream = NULL;
+       hdspm->playback_substream = NULL;
+
+       snd_printdd("Set defaults...\n");
+       if ((err = snd_hdspm_set_defaults(hdspm)) < 0)
+               return err;
+
+       snd_printdd("Update mixer controls...\n");
+       hdspm_update_simple_mixer_controls(hdspm);
+
+       snd_printdd("Initializeing complete ???\n");
+
+       if ((err = snd_card_register(card)) < 0) {
+               snd_printk(KERN_ERR "HDSPM: error registering card\n");
+               return err;
+       }
+
+       snd_printdd("... yes now\n");
+
+       return 0;
+}
+
+static int __devinit snd_hdspm_create(snd_card_t * card, hdspm_t * hdspm,
+                                     int precise_ptr, int enable_monitor)
+{
+       struct pci_dev *pci = hdspm->pci;
+       int err;
+       int i;
+
+       unsigned long io_extent;
+
+       hdspm->irq = -1;
+       hdspm->irq_count = 0;
+
+       hdspm->midi[0].rmidi = NULL;
+       hdspm->midi[1].rmidi = NULL;
+       hdspm->midi[0].input = NULL;
+       hdspm->midi[1].input = NULL;
+       hdspm->midi[0].output = NULL;
+       hdspm->midi[1].output = NULL;
+       spin_lock_init(&hdspm->midi[0].lock);
+       spin_lock_init(&hdspm->midi[1].lock);
+       hdspm->iobase = NULL;
+       hdspm->control_register = 0;
+       hdspm->control2_register = 0;
+
+       hdspm->playback_buffer = NULL;
+       hdspm->capture_buffer = NULL;
+
+       for (i = 0; i < HDSPM_MAX_CHANNELS; ++i)
+               hdspm->playback_mixer_ctls[i] = NULL;
+       hdspm->mixer = NULL;
+
+       hdspm->card = card;
+
+       spin_lock_init(&hdspm->lock);
+
+       tasklet_init(&hdspm->midi_tasklet,
+                    hdspm_midi_tasklet, (unsigned long) hdspm);
+
+       pci_read_config_word(hdspm->pci,
+                            PCI_CLASS_REVISION, &hdspm->firmware_rev);
+
+       strcpy(card->driver, "HDSPM");
+       strcpy(card->mixername, "Xilinx FPGA");
+       hdspm->card_name = "RME HDSPM MADI";
+
+       if ((err = pci_enable_device(pci)) < 0)
+               return err;
+
+       pci_set_master(hdspm->pci);
+
+       if ((err = pci_request_regions(pci, "hdspm")) < 0)
+               return err;
+
+       hdspm->port = pci_resource_start(pci, 0);
+       io_extent = pci_resource_len(pci, 0);
+
+       snd_printdd("grabbed memory region 0x%lx-0x%lx\n",
+                  hdspm->port, hdspm->port + io_extent - 1);
+
+
+       if ((hdspm->iobase = ioremap_nocache(hdspm->port, io_extent)) == NULL) {
+               snd_printk(KERN_ERR "HDSPM: unable to remap region 0x%lx-0x%lx\n",
+                          hdspm->port, hdspm->port + io_extent - 1);
+               return -EBUSY;
+       }
+       snd_printdd("remapped region (0x%lx) 0x%lx-0x%lx\n",
+                  (unsigned long)hdspm->iobase, hdspm->port,
+                  hdspm->port + io_extent - 1);
+
+       if (request_irq(pci->irq, snd_hdspm_interrupt,
+                       SA_INTERRUPT | SA_SHIRQ, "hdspm",
+                       (void *) hdspm)) {
+               snd_printk(KERN_ERR "HDSPM: unable to use IRQ %d\n", pci->irq);
+               return -EBUSY;
+       }
+
+       snd_printdd("use IRQ %d\n", pci->irq);
+
+       hdspm->irq = pci->irq;
+       hdspm->precise_ptr = precise_ptr;
+
+       hdspm->monitor_outs = enable_monitor;
+
+       snd_printdd("kmalloc Mixer memory of %d Bytes\n",
+                  sizeof(hdspm_mixer_t));
+       if ((hdspm->mixer =
+            (hdspm_mixer_t *) kmalloc(sizeof(hdspm_mixer_t), GFP_KERNEL))
+           == NULL) {
+               snd_printk(KERN_ERR "HDSPM: unable to kmalloc Mixer memory of %d Bytes\n",
+                          (int)sizeof(hdspm_mixer_t));
+               return err;
+       }
+
+       hdspm->ss_channels = MADI_SS_CHANNELS;
+       hdspm->ds_channels = MADI_DS_CHANNELS;
+       hdspm->qs_channels = MADI_QS_CHANNELS;
+
+       snd_printdd("create alsa devices.\n");
+       if ((err = snd_hdspm_create_alsa_devices(card, hdspm)) < 0)
+               return err;
+
+       snd_hdspm_initialize_midi_flush(hdspm);
+
+       return 0;
+}
+
+static int snd_hdspm_free(hdspm_t * hdspm)
+{
+
+       if (hdspm->port) {
+
+               /* stop th audio, and cancel all interrupts */
+               hdspm->control_register &=
+                   ~(HDSPM_Start | HDSPM_AudioInterruptEnable
+                     | HDSPM_Midi0InterruptEnable |
+                     HDSPM_Midi1InterruptEnable);
+               hdspm_write(hdspm, HDSPM_controlRegister,
+                           hdspm->control_register);
+       }
+
+       if (hdspm->irq >= 0)
+               free_irq(hdspm->irq, (void *) hdspm);
+
+
+       if (hdspm->mixer)
+               kfree(hdspm->mixer);
+
+       if (hdspm->iobase)
+               iounmap(hdspm->iobase);
+
+       snd_hdspm_memory_free(hdspm);
+
+       if (hdspm->port)
+               pci_release_regions(hdspm->pci);
+
+       pci_disable_device(hdspm->pci);
+       return 0;
+}
+
+static void snd_hdspm_card_free(snd_card_t * card)
+{
+       hdspm_t *hdspm = (hdspm_t *) card->private_data;
+
+       if (hdspm)
+               snd_hdspm_free(hdspm);
+}
+
+static int __devinit snd_hdspm_probe(struct pci_dev *pci,
+                                    const struct pci_device_id *pci_id)
+{
+       static int dev;
+       hdspm_t *hdspm;
+       snd_card_t *card;
+       int err;
+
+       if (dev >= SNDRV_CARDS)
+               return -ENODEV;
+       if (!enable[dev]) {
+               dev++;
+               return -ENOENT;
+       }
+
+       if (!(card = snd_card_new(index[dev], id[dev],
+                                 THIS_MODULE, sizeof(hdspm_t))))
+               return -ENOMEM;
+
+       hdspm = (hdspm_t *) card->private_data;
+       card->private_free = snd_hdspm_card_free;
+       hdspm->dev = dev;
+       hdspm->pci = pci;
+
+       if ((err =
+            snd_hdspm_create(card, hdspm, precise_ptr[dev],
+                             enable_monitor[dev])) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+
+       strcpy(card->shortname, "HDSPM MADI");
+       sprintf(card->longname, "%s at 0x%lx, irq %d", hdspm->card_name,
+               hdspm->port, hdspm->irq);
+
+       if ((err = snd_card_register(card)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+
+       pci_set_drvdata(pci, card);
+
+       dev++;
+       return 0;
+}
+
+static void __devexit snd_hdspm_remove(struct pci_dev *pci)
+{
+       snd_card_free(pci_get_drvdata(pci));
+       pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+       .name = "RME Hammerfall DSP MADI",
+       .id_table = snd_hdspm_ids,
+       .probe = snd_hdspm_probe,
+       .remove = __devexit_p(snd_hdspm_remove),
+};
+
+
+static int __init alsa_card_hdspm_init(void)
+{
+       return pci_register_driver(&driver);
+}
+
+static void __exit alsa_card_hdspm_exit(void)
+{
+       pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_hdspm_init)
+module_exit(alsa_card_hdspm_exit)
index 69cd81eaa111bc976932fd2a5838ff81b662add3..f3037402d58fd199bb05c78f7669b84d4ecb4bf2 100644 (file)
@@ -303,18 +303,22 @@ static int snd_hammerfall_get_buffer(struct pci_dev *pci, struct snd_dma_buffer
 {
        dmab->dev.type = SNDRV_DMA_TYPE_DEV;
        dmab->dev.dev = snd_dma_pci_data(pci);
-       if (! snd_dma_get_reserved_buf(dmab, snd_dma_pci_buf_id(pci))) {
-               if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
-                                       size, dmab) < 0)
-                       return -ENOMEM;
+       if (snd_dma_get_reserved_buf(dmab, snd_dma_pci_buf_id(pci))) {
+               if (dmab->bytes >= size)
+                       return 0;
        }
+       if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
+                               size, dmab) < 0)
+               return -ENOMEM;
        return 0;
 }
 
 static void snd_hammerfall_free_buffer(struct snd_dma_buffer *dmab, struct pci_dev *pci)
 {
-       if (dmab->area)
+       if (dmab->area) {
+               dmab->dev.dev = NULL; /* make it anonymous */
                snd_dma_reserve_buf(dmab, snd_dma_pci_buf_id(pci));
+       }
 }
 
 
@@ -2664,7 +2668,7 @@ static struct pci_driver driver = {
 
 static int __init alsa_card_hammerfall_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_hammerfall_exit(void)
index cfd2c5fd6ddf5ae77c20579fb11e959edf897e89..60ecb2bdb65e79231e6bc888d7d51a98734c4f20 100644 (file)
@@ -1522,7 +1522,7 @@ static struct pci_driver driver = {
 
 static int __init alsa_card_sonicvibes_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_sonicvibes_exit(void)
index ad58e08d66e2cdf7f058b0ef75a20bc91318386d..940d531575c098cd736776420b0b674c45094e3d 100644 (file)
@@ -143,7 +143,8 @@ static int __devinit snd_trident_probe(struct pci_dev *pci,
                        return err;
                }
        }
-       if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_TRID4DWAVE,
+       if (trident->device != TRIDENT_DEVICE_ID_SI7018 &&
+           (err = snd_mpu401_uart_new(card, 0, MPU401_HW_TRID4DWAVE,
                                       trident->midi_port, 1,
                                       trident->irq, 0, &trident->rmidi)) < 0) {
                snd_card_free(card);
@@ -184,7 +185,7 @@ static struct pci_driver driver = {
 
 static int __init alsa_card_trident_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_trident_exit(void)
index 9b4d74d49f984c9d6a19bc9fd4ccd5c35df1d49b..42c48f0ce8e829e2cf8ceb7068c9cab31e54c73b 100644 (file)
@@ -101,7 +101,7 @@ MODULE_PARM_DESC(ac97_clock, "AC'97 codec clock (default 48000Hz).");
 module_param_array(ac97_quirk, charp, NULL, 0444);
 MODULE_PARM_DESC(ac97_quirk, "AC'97 workaround for strange hardware.");
 module_param_array(dxs_support, int, NULL, 0444);
-MODULE_PARM_DESC(dxs_support, "Support for DXS channels (0 = auto, 1 = enable, 2 = disable, 3 = 48k only, 4 = no VRA)");
+MODULE_PARM_DESC(dxs_support, "Support for DXS channels (0 = auto, 1 = enable, 2 = disable, 3 = 48k only, 4 = no VRA, 5 = enable any sample rate)");
 
 
 /* pci ids */
@@ -302,6 +302,7 @@ DEFINE_VIA_REGSET(CAPTURE_8233, 0x60);
 #define VIA_DXS_DISABLE        2
 #define VIA_DXS_48K    3
 #define VIA_DXS_NO_VRA 4
+#define VIA_DXS_SRC    5
 
 
 /*
@@ -380,6 +381,7 @@ struct _snd_via82xx {
        struct via_rate_lock rates[2]; /* playback and capture */
        unsigned int dxs_fixed: 1;      /* DXS channel accepts only 48kHz */
        unsigned int no_vra: 1;         /* no need to set VRA on DXS channels */
+       unsigned int dxs_src: 1;        /* use full SRC capabilities of DXS */
        unsigned int spdif_on: 1;       /* only spdif rates work to external DACs */
 
        snd_pcm_t *pcms[2];
@@ -489,10 +491,8 @@ static int clean_via_table(viadev_t *dev, snd_pcm_substream_t *substream,
                snd_dma_free_pages(&dev->table);
                dev->table.area = NULL;
        }
-       if (dev->idx_table) {
-               kfree(dev->idx_table);
-               dev->idx_table = NULL;
-       }
+       kfree(dev->idx_table);
+       dev->idx_table = NULL;
        return 0;
 }
 
@@ -924,15 +924,17 @@ static int snd_via8233_playback_prepare(snd_pcm_substream_t *substream)
        via82xx_t *chip = snd_pcm_substream_chip(substream);
        viadev_t *viadev = (viadev_t *)substream->runtime->private_data;
        snd_pcm_runtime_t *runtime = substream->runtime;
+       int ac97_rate = chip->dxs_src ? 48000 : runtime->rate;
        int rate_changed;
        u32 rbits;
 
-       if ((rate_changed = via_lock_rate(&chip->rates[0], runtime->rate)) < 0)
+       if ((rate_changed = via_lock_rate(&chip->rates[0], ac97_rate)) < 0)
                return rate_changed;
        if (rate_changed) {
                snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE,
                                  chip->no_vra ? 48000 : runtime->rate);
-               snd_ac97_set_rate(chip->ac97, AC97_SPDIF, runtime->rate);
+               snd_ac97_set_rate(chip->ac97, AC97_SPDIF,
+                                 chip->no_vra ? 48000 : runtime->rate);
        }
        if (runtime->rate == 48000)
                rbits = 0xfffff;
@@ -1074,6 +1076,12 @@ static int snd_via82xx_pcm_open(via82xx_t *chip, viadev_t *viadev, snd_pcm_subst
                /* fixed DXS playback rate */
                runtime->hw.rates = SNDRV_PCM_RATE_48000;
                runtime->hw.rate_min = runtime->hw.rate_max = 48000;
+       } else if (chip->dxs_src && viadev->reg_offset < 0x40) {
+               /* use full SRC capabilities of DXS */
+               runtime->hw.rates = (SNDRV_PCM_RATE_CONTINUOUS |
+                                    SNDRV_PCM_RATE_8000_48000);
+               runtime->hw.rate_min = 8000;
+               runtime->hw.rate_max = 48000;
        } else if (! ratep->rate) {
                int idx = viadev->direction ? AC97_RATES_ADC : AC97_RATES_FRONT_DAC;
                runtime->hw.rates = chip->ac97->rates[idx];
@@ -1550,51 +1558,51 @@ static void snd_via82xx_mixer_free_ac97(ac97_t *ac97)
 
 static struct ac97_quirk ac97_quirks[] = {
        {
-               .vendor = 0x1106,
-               .device = 0x4161,
+               .subvendor = 0x1106,
+               .subdevice = 0x4161,
                .codec_id = 0x56494161, /* VT1612A */
                .name = "Soltek SL-75DRV5",
                .type = AC97_TUNE_NONE
        },
        {       /* FIXME: which codec? */
-               .vendor = 0x1106,
-               .device = 0x4161,
+               .subvendor = 0x1106,
+               .subdevice = 0x4161,
                .name = "ASRock K7VT2",
                .type = AC97_TUNE_HP_ONLY
        },
        {
-               .vendor = 0x1019,
-               .device = 0x0a81,
+               .subvendor = 0x1019,
+               .subdevice = 0x0a81,
                .name = "ECS K7VTA3",
                .type = AC97_TUNE_HP_ONLY
        },
        {
-               .vendor = 0x1019,
-               .device = 0x0a85,
+               .subvendor = 0x1019,
+               .subdevice = 0x0a85,
                .name = "ECS L7VMM2",
                .type = AC97_TUNE_HP_ONLY
        },
        {
-               .vendor = 0x1849,
-               .device = 0x3059,
+               .subvendor = 0x1849,
+               .subdevice = 0x3059,
                .name = "ASRock K7VM2",
                .type = AC97_TUNE_HP_ONLY       /* VT1616 */
        },
        {
-               .vendor = 0x14cd,
-               .device = 0x7002,
+               .subvendor = 0x14cd,
+               .subdevice = 0x7002,
                .name = "Unknown",
                .type = AC97_TUNE_ALC_JACK
        },
        {
-               .vendor = 0x1071,
-               .device = 0x8590,
+               .subvendor = 0x1071,
+               .subdevice = 0x8590,
                .name = "Mitac Mobo",
                .type = AC97_TUNE_ALC_JACK
        },
        {
-               .vendor = 0x161f,
-               .device = 0x202b,
+               .subvendor = 0x161f,
+               .subdevice = 0x202b,
                .name = "Arima Notebook",
                .type = AC97_TUNE_HP_ONLY,
        },
@@ -2132,8 +2140,8 @@ static struct via823x_info via823x_cards[] __devinitdata = {
  * auto detection of DXS channel supports.
  */
 struct dxs_whitelist {
-       unsigned short vendor;
-       unsigned short device; 
+       unsigned short subvendor;
+       unsigned short subdevice; 
        unsigned short mask; 
        short action;   /* new dxs_support value */
 };
@@ -2141,38 +2149,44 @@ struct dxs_whitelist {
 static int __devinit check_dxs_list(struct pci_dev *pci)
 {
        static struct dxs_whitelist whitelist[] = {
-               { .vendor = 0x1005, .device = 0x4710, .action = VIA_DXS_ENABLE }, /* Avance Logic Mobo */
-               { .vendor = 0x1019, .device = 0x0996, .action = VIA_DXS_48K },
-               { .vendor = 0x1019, .device = 0x0a81, .action = VIA_DXS_NO_VRA }, /* ECS K7VTA3 v8.0 */
-               { .vendor = 0x1019, .device = 0x0a85, .action = VIA_DXS_NO_VRA }, /* ECS L7VMM2 */
-               { .vendor = 0x1025, .device = 0x0033, .action = VIA_DXS_NO_VRA }, /* Acer Inspire 1353LM */
-               { .vendor = 0x1043, .device = 0x8095, .action = VIA_DXS_NO_VRA }, /* ASUS A7V8X (FIXME: possibly VIA_DXS_ENABLE?)*/
-               { .vendor = 0x1043, .device = 0x80a1, .action = VIA_DXS_NO_VRA }, /* ASUS A7V8-X */
-               { .vendor = 0x1043, .device = 0x80b0, .action = VIA_DXS_NO_VRA }, /* ASUS A7V600 & K8V*/ 
-               { .vendor = 0x1071, .device = 0x8375, .action = VIA_DXS_NO_VRA }, /* Vobis/Yakumo/Mitac notebook */
-               { .vendor = 0x10cf, .device = 0x118e, .action = VIA_DXS_ENABLE }, /* FSC laptop */
-               { .vendor = 0x1106, .device = 0x4161, .action = VIA_DXS_NO_VRA }, /* ASRock K7VT2 */
-               { .vendor = 0x1106, .device = 0x4552, .action = VIA_DXS_NO_VRA }, /* QDI Kudoz 7X/600-6AL */
-               { .vendor = 0x1106, .device = 0xaa01, .action = VIA_DXS_NO_VRA }, /* EPIA MII */
-               { .vendor = 0x1297, .device = 0xa232, .action = VIA_DXS_ENABLE }, /* Shuttle ?? */
-               { .vendor = 0x1297, .device = 0xc160, .action = VIA_DXS_ENABLE }, /* Shuttle SK41G */
-               { .vendor = 0x1458, .device = 0xa002, .action = VIA_DXS_ENABLE }, /* Gigabyte GA-7VAXP */
-               { .vendor = 0x1462, .device = 0x3800, .action = VIA_DXS_ENABLE }, /* MSI KT266 */
-               { .vendor = 0x1462, .device = 0x5901, .action = VIA_DXS_NO_VRA }, /* MSI KT6 Delta-SR */
-               { .vendor = 0x1462, .device = 0x7023, .action = VIA_DXS_NO_VRA }, /* MSI K8T Neo2-FI */
-               { .vendor = 0x1462, .device = 0x7120, .action = VIA_DXS_ENABLE }, /* MSI KT4V */
-               { .vendor = 0x147b, .device = 0x1401, .action = VIA_DXS_ENABLE }, /* ABIT KD7(-RAID) */
-               { .vendor = 0x147b, .device = 0x1411, .action = VIA_DXS_ENABLE }, /* ABIT VA-20 */
-               { .vendor = 0x147b, .device = 0x1413, .action = VIA_DXS_ENABLE }, /* ABIT KV8 Pro */
-               { .vendor = 0x147b, .device = 0x1415, .action = VIA_DXS_NO_VRA }, /* Abit AV8 */
-               { .vendor = 0x14ff, .device = 0x0403, .action = VIA_DXS_ENABLE }, /* Twinhead mobo */
-               { .vendor = 0x1584, .device = 0x8120, .action = VIA_DXS_ENABLE }, /* Gericom/Targa/Vobis/Uniwill laptop */
-               { .vendor = 0x1584, .device = 0x8123, .action = VIA_DXS_NO_VRA }, /* Uniwill (Targa Visionary XP-210) */
-               { .vendor = 0x161f, .device = 0x202b, .action = VIA_DXS_NO_VRA }, /* Amira Note book */
-               { .vendor = 0x161f, .device = 0x2032, .action = VIA_DXS_48K }, /* m680x machines */
-               { .vendor = 0x1631, .device = 0xe004, .action = VIA_DXS_ENABLE }, /* Easy Note 3174, Packard Bell */
-               { .vendor = 0x1695, .device = 0x3005, .action = VIA_DXS_ENABLE }, /* EPoX EP-8K9A */
-               { .vendor = 0x1849, .device = 0x3059, .action = VIA_DXS_NO_VRA }, /* ASRock K7VM2 */
+               { .subvendor = 0x1005, .subdevice = 0x4710, .action = VIA_DXS_ENABLE }, /* Avance Logic Mobo */
+               { .subvendor = 0x1019, .subdevice = 0x0996, .action = VIA_DXS_48K },
+               { .subvendor = 0x1019, .subdevice = 0x0a81, .action = VIA_DXS_NO_VRA }, /* ECS K7VTA3 v8.0 */
+               { .subvendor = 0x1019, .subdevice = 0x0a85, .action = VIA_DXS_NO_VRA }, /* ECS L7VMM2 */
+               { .subvendor = 0x1025, .subdevice = 0x0033, .action = VIA_DXS_NO_VRA }, /* Acer Inspire 1353LM */
+               { .subvendor = 0x1043, .subdevice = 0x8095, .action = VIA_DXS_NO_VRA }, /* ASUS A7V8X (FIXME: possibly VIA_DXS_ENABLE?)*/
+               { .subvendor = 0x1043, .subdevice = 0x80a1, .action = VIA_DXS_NO_VRA }, /* ASUS A7V8-X */
+               { .subvendor = 0x1043, .subdevice = 0x80b0, .action = VIA_DXS_NO_VRA }, /* ASUS A7V600 & K8V*/ 
+               { .subvendor = 0x1043, .subdevice = 0x812a, .action = VIA_DXS_SRC    }, /* ASUS A8V Deluxe */ 
+               { .subvendor = 0x1071, .subdevice = 0x8375, .action = VIA_DXS_NO_VRA }, /* Vobis/Yakumo/Mitac notebook */
+               { .subvendor = 0x1071, .subdevice = 0x8399, .action = VIA_DXS_NO_VRA }, /* Umax AB 595T (VIA K8N800A - VT8237) */
+               { .subvendor = 0x10cf, .subdevice = 0x118e, .action = VIA_DXS_ENABLE }, /* FSC laptop */
+               { .subvendor = 0x1106, .subdevice = 0x4161, .action = VIA_DXS_NO_VRA }, /* ASRock K7VT2 */
+               { .subvendor = 0x1106, .subdevice = 0x4552, .action = VIA_DXS_NO_VRA }, /* QDI Kudoz 7X/600-6AL */
+               { .subvendor = 0x1106, .subdevice = 0xaa01, .action = VIA_DXS_NO_VRA }, /* EPIA MII */
+               { .subvendor = 0x1106, .subdevice = 0xc001, .action = VIA_DXS_SRC }, /* Insight P4-ITX */
+               { .subvendor = 0x1297, .subdevice = 0xa232, .action = VIA_DXS_ENABLE }, /* Shuttle ?? */
+               { .subvendor = 0x1297, .subdevice = 0xc160, .action = VIA_DXS_ENABLE }, /* Shuttle SK41G */
+               { .subvendor = 0x1458, .subdevice = 0xa002, .action = VIA_DXS_ENABLE }, /* Gigabyte GA-7VAXP */
+               { .subvendor = 0x1462, .subdevice = 0x0080, .action = VIA_DXS_SRC }, /* MSI K8T Neo-FIS2R */
+               { .subvendor = 0x1462, .subdevice = 0x3800, .action = VIA_DXS_ENABLE }, /* MSI KT266 */
+               { .subvendor = 0x1462, .subdevice = 0x5901, .action = VIA_DXS_NO_VRA }, /* MSI KT6 Delta-SR */
+               { .subvendor = 0x1462, .subdevice = 0x7023, .action = VIA_DXS_NO_VRA }, /* MSI K8T Neo2-FI */
+               { .subvendor = 0x1462, .subdevice = 0x7120, .action = VIA_DXS_ENABLE }, /* MSI KT4V */
+               { .subvendor = 0x147b, .subdevice = 0x1401, .action = VIA_DXS_ENABLE }, /* ABIT KD7(-RAID) */
+               { .subvendor = 0x147b, .subdevice = 0x1411, .action = VIA_DXS_ENABLE }, /* ABIT VA-20 */
+               { .subvendor = 0x147b, .subdevice = 0x1413, .action = VIA_DXS_ENABLE }, /* ABIT KV8 Pro */
+               { .subvendor = 0x147b, .subdevice = 0x1415, .action = VIA_DXS_NO_VRA }, /* Abit AV8 */
+               { .subvendor = 0x14ff, .subdevice = 0x0403, .action = VIA_DXS_ENABLE }, /* Twinhead mobo */
+               { .subvendor = 0x14ff, .subdevice = 0x0408, .action = VIA_DXS_NO_VRA }, /* Twinhead mobo */
+               { .subvendor = 0x1584, .subdevice = 0x8120, .action = VIA_DXS_ENABLE }, /* Gericom/Targa/Vobis/Uniwill laptop */
+               { .subvendor = 0x1584, .subdevice = 0x8123, .action = VIA_DXS_NO_VRA }, /* Uniwill (Targa Visionary XP-210) */
+               { .subvendor = 0x161f, .subdevice = 0x202b, .action = VIA_DXS_NO_VRA }, /* Amira Note book */
+               { .subvendor = 0x161f, .subdevice = 0x2032, .action = VIA_DXS_48K }, /* m680x machines */
+               { .subvendor = 0x1631, .subdevice = 0xe004, .action = VIA_DXS_ENABLE }, /* Easy Note 3174, Packard Bell */
+               { .subvendor = 0x1695, .subdevice = 0x3005, .action = VIA_DXS_ENABLE }, /* EPoX EP-8K9A */
+               { .subvendor = 0x1849, .subdevice = 0x3059, .action = VIA_DXS_NO_VRA }, /* ASRock K7VM2 */
+               { .subvendor = 0x1919, .subdevice = 0x200a, .action = VIA_DXS_NO_VRA }, /* Soltek SL-K8Tpro-939 */
                { } /* terminator */
        };
        struct dxs_whitelist *w;
@@ -2182,14 +2196,14 @@ static int __devinit check_dxs_list(struct pci_dev *pci)
        pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor);
        pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsystem_device);
 
-       for (w = whitelist; w->vendor; w++) {
-               if (w->vendor != subsystem_vendor)
+       for (w = whitelist; w->subvendor; w++) {
+               if (w->subvendor != subsystem_vendor)
                        continue;
                if (w->mask) {
-                       if ((w->mask & subsystem_device) == w->device)
+                       if ((w->mask & subsystem_device) == w->subdevice)
                                return w->action;
                } else {
-                       if (subsystem_device == w->device)
+                       if (subsystem_device == w->subdevice)
                                return w->action;
                }
        }
@@ -2198,8 +2212,9 @@ static int __devinit check_dxs_list(struct pci_dev *pci)
         * not detected, try 48k rate only to be sure.
         */
        printk(KERN_INFO "via82xx: Assuming DXS channels with 48k fixed sample rate.\n");
-       printk(KERN_INFO "         Please try dxs_support=1 or dxs_support=4 option\n");
+       printk(KERN_INFO "         Please try dxs_support=5 option\n");
        printk(KERN_INFO "         and report if it works on your machine.\n");
+       printk(KERN_INFO "         For more details, read ALSA-Configuration.txt.\n");
        return VIA_DXS_48K;
 };
 
@@ -2288,6 +2303,10 @@ static int __devinit snd_via82xx_probe(struct pci_dev *pci,
                                chip->dxs_fixed = 1;
                        else if (dxs_support[dev] == VIA_DXS_NO_VRA)
                                chip->no_vra = 1;
+                       else if (dxs_support[dev] == VIA_DXS_SRC) {
+                               chip->no_vra = 1;
+                               chip->dxs_src = 1;
+                       }
                }
                if ((err = snd_via8233_init_misc(chip, dev)) < 0)
                        goto __error;
@@ -2334,7 +2353,7 @@ static struct pci_driver driver = {
 
 static int __init alsa_card_via82xx_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_via82xx_exit(void)
index ea5c6f6401593fc150b8dfcc1236f6d012eb5fdd..5896d289f9ac3d28238b40dc913e30e03347d9c5 100644 (file)
@@ -352,10 +352,8 @@ static int clean_via_table(viadev_t *dev, snd_pcm_substream_t *substream,
                snd_dma_free_pages(&dev->table);
                dev->table.area = NULL;
        }
-       if (dev->idx_table) {
-               kfree(dev->idx_table);
-               dev->idx_table = NULL;
-       }
+       kfree(dev->idx_table);
+       dev->idx_table = NULL;
        return 0;
 }
 
@@ -420,7 +418,10 @@ static void snd_via82xx_codec_write(ac97_t *ac97,
 {
        via82xx_t *chip = ac97->private_data;
        unsigned int xval;
-       
+       if(reg == AC97_GPIO_STATUS) {
+               outl(val, VIAREG(chip, GPI_STATUS));
+               return;
+       }       
        xval = !ac97->num ? VIA_REG_AC97_CODEC_ID_PRIMARY : VIA_REG_AC97_CODEC_ID_SECONDARY;
        xval <<= VIA_REG_AC97_CODEC_ID_SHIFT;
        xval |= reg << VIA_REG_AC97_CMD_SHIFT;
@@ -544,25 +545,6 @@ static int snd_via82xx_pcm_trigger(snd_pcm_substream_t * substream, int cmd)
        return 0;
 }
 
-static int snd_via82xx_modem_pcm_trigger(snd_pcm_substream_t * substream, int cmd)
-{
-       via82xx_t *chip = snd_pcm_substream_chip(substream);
-       unsigned int val = 0;
-       switch (cmd) {
-       case SNDRV_PCM_TRIGGER_START:
-               val = snd_ac97_read(chip->ac97, AC97_GPIO_STATUS);
-               outl(val|AC97_GPIO_LINE1_OH, VIAREG(chip, GPI_STATUS));
-               break;
-       case SNDRV_PCM_TRIGGER_STOP:
-               val = snd_ac97_read(chip->ac97, AC97_GPIO_STATUS);
-               outl(val&~AC97_GPIO_LINE1_OH, VIAREG(chip, GPI_STATUS));
-               break;
-       default:
-               break;
-       }
-       return snd_via82xx_pcm_trigger(substream, cmd);
-}
-
 /*
  * pointer callbacks
  */
@@ -806,7 +788,7 @@ static snd_pcm_ops_t snd_via686_playback_ops = {
        .hw_params =    snd_via82xx_hw_params,
        .hw_free =      snd_via82xx_hw_free,
        .prepare =      snd_via82xx_pcm_prepare,
-       .trigger =      snd_via82xx_modem_pcm_trigger,
+       .trigger =      snd_via82xx_pcm_trigger,
        .pointer =      snd_via686_pcm_pointer,
        .page =         snd_pcm_sgbuf_ops_page,
 };
@@ -819,7 +801,7 @@ static snd_pcm_ops_t snd_via686_capture_ops = {
        .hw_params =    snd_via82xx_hw_params,
        .hw_free =      snd_via82xx_hw_free,
        .prepare =      snd_via82xx_pcm_prepare,
-       .trigger =      snd_via82xx_modem_pcm_trigger,
+       .trigger =      snd_via82xx_pcm_trigger,
        .pointer =      snd_via686_pcm_pointer,
        .page =         snd_pcm_sgbuf_ops_page,
 };
@@ -938,7 +920,7 @@ static void __devinit snd_via82xx_proc_init(via82xx_t *chip)
  *
  */
 
-static int __devinit snd_via82xx_chip_init(via82xx_t *chip)
+static int snd_via82xx_chip_init(via82xx_t *chip)
 {
        unsigned int val;
        int max_count;
@@ -1233,7 +1215,7 @@ static struct pci_driver driver = {
 
 static int __init alsa_card_via82xx_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_via82xx_exit(void)
index 4ffbb25658a57807115e641bbb0140b823f5661e..dca6bd2c75806298eeacf5e5468f7141a7141240 100644 (file)
@@ -260,7 +260,7 @@ static struct pci_driver driver = {
 
 static int __init alsa_card_vx222_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_vx222_exit(void)
index 9f3ef22df08dd1fec6bf3cba29b8135b02c8f6f5..5b5b624b47d0c6623f79c39893de23fe2d9451f8 100644 (file)
@@ -360,7 +360,7 @@ static struct pci_driver driver = {
 
 static int __init alsa_card_ymfpci_init(void)
 {
-       return pci_module_init(&driver);
+       return pci_register_driver(&driver);
 }
 
 static void __exit alsa_card_ymfpci_exit(void)
index 05f1629760bc7cc38ca1bd71daa6c1e42979b7d3..2ae79610ecb58cad4d4af120d0826a3ecaf01591 100644 (file)
@@ -829,9 +829,7 @@ static snd_pcm_hardware_t snd_ymfpci_capture =
 
 static void snd_ymfpci_pcm_free_substream(snd_pcm_runtime_t *runtime)
 {
-       ymfpci_pcm_t *ypcm = runtime->private_data;
-       
-       kfree(ypcm);
+       kfree(runtime->private_data);
 }
 
 static int snd_ymfpci_playback_open_1(snd_pcm_substream_t * substream)
@@ -1421,17 +1419,15 @@ static snd_kcontrol_new_t snd_ymfpci_drec_source __devinitdata = {
 
 static int snd_ymfpci_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
-       unsigned int mask = 1;
-
        switch (kcontrol->private_value) {
        case YDSXGR_SPDIFOUTCTRL: break;
        case YDSXGR_SPDIFINCTRL: break;
        default: return -EINVAL;
        }
-       uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
        uinfo->count = 1;
        uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = mask;
+       uinfo->value.integer.max = 1;
        return 0;
 }
 
@@ -1439,7 +1435,7 @@ static int snd_ymfpci_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t
 {
        ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
        int reg = kcontrol->private_value;
-       unsigned int shift = 0, mask = 1, invert = 0;
+       unsigned int shift = 0, mask = 1;
        
        switch (kcontrol->private_value) {
        case YDSXGR_SPDIFOUTCTRL: break;
@@ -1447,8 +1443,6 @@ static int snd_ymfpci_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t
        default: return -EINVAL;
        }
        ucontrol->value.integer.value[0] = (snd_ymfpci_readl(chip, reg) >> shift) & mask;
-       if (invert)
-               ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
        return 0;
 }
 
@@ -1456,7 +1450,7 @@ static int snd_ymfpci_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t
 {
        ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
        int reg = kcontrol->private_value;
-       unsigned int shift = 0, mask = 1, invert = 0;
+       unsigned int shift = 0, mask = 1;
        int change;
        unsigned int val, oval;
        
@@ -1466,8 +1460,6 @@ static int snd_ymfpci_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t
        default: return -EINVAL;
        }
        val = (ucontrol->value.integer.value[0] & mask);
-       if (invert)
-               val = mask - val;
        val <<= shift;
        spin_lock_irq(&chip->reg_lock);
        oval = snd_ymfpci_readl(chip, reg);
@@ -1487,14 +1479,13 @@ static int snd_ymfpci_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t
 static int snd_ymfpci_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
        unsigned int reg = kcontrol->private_value;
-       unsigned int mask = 16383;
 
        if (reg < 0x80 || reg >= 0xc0)
                return -EINVAL;
-       uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
        uinfo->count = 2;
        uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = mask;
+       uinfo->value.integer.max = 16383;
        return 0;
 }
 
@@ -1502,7 +1493,7 @@ static int snd_ymfpci_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t
 {
        ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
        unsigned int reg = kcontrol->private_value;
-       unsigned int shift_left = 0, shift_right = 16, mask = 16383, invert = 0;
+       unsigned int shift_left = 0, shift_right = 16, mask = 16383;
        unsigned int val;
        
        if (reg < 0x80 || reg >= 0xc0)
@@ -1512,10 +1503,6 @@ static int snd_ymfpci_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t
        spin_unlock_irq(&chip->reg_lock);
        ucontrol->value.integer.value[0] = (val >> shift_left) & mask;
        ucontrol->value.integer.value[1] = (val >> shift_right) & mask;
-       if (invert) {
-               ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
-               ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
-       }
        return 0;
 }
 
@@ -1523,7 +1510,7 @@ static int snd_ymfpci_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t
 {
        ymfpci_t *chip = snd_kcontrol_chip(kcontrol);
        unsigned int reg = kcontrol->private_value;
-       unsigned int shift_left = 0, shift_right = 16, mask = 16383, invert = 0;
+       unsigned int shift_left = 0, shift_right = 16, mask = 16383;
        int change;
        unsigned int val1, val2, oval;
        
@@ -1531,10 +1518,6 @@ static int snd_ymfpci_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t
                return -EINVAL;
        val1 = ucontrol->value.integer.value[0] & mask;
        val2 = ucontrol->value.integer.value[1] & mask;
-       if (invert) {
-               val1 = mask - val1;
-               val2 = mask - val2;
-       }
        val1 <<= shift_left;
        val2 <<= shift_right;
        spin_lock_irq(&chip->reg_lock);
index 53d8172c52ae328a024a1bd14be54e7d086977f8..332bbca3dfc4ff0459ca7b57526204c90e4a268c 100644 (file)
@@ -68,8 +68,7 @@ static int snd_vxpocket_free(vx_core_t *chip)
        if (hw)
                hw->card_list[vxp->index] = NULL;
        chip->card = NULL;
-       if (chip->dev)
-               kfree(chip->dev);
+       kfree(chip->dev);
 
        snd_vx_free_firmware(chip);
        kfree(chip);
index ec3fc1ba7fcabfe9a948bf8b5c05416b83979dec..4764940f11a050cff04f9e44690ea3338d1f1a9f 100644 (file)
@@ -291,10 +291,8 @@ snd_emux_create_effect(snd_emux_port_t *p)
 void
 snd_emux_delete_effect(snd_emux_port_t *p)
 {
-       if (p->effect) {
-               kfree(p->effect);
-               p->effect = NULL;
-       }
+       kfree(p->effect);
+       p->effect = NULL;
 }
 
 void
index 9329e992c841936c00a4b8522e9b4db803fb5380..f05d02f5b69f9233aca85c8927446db08b2fd27e 100644 (file)
@@ -6,6 +6,7 @@ menu "USB devices"
 config SND_USB_AUDIO
        tristate "USB Audio/MIDI driver"
        depends on SND && USB
+       select SND_HWDEP
        select SND_RAWMIDI
        select SND_PCM
        help
index aae66144d411ce579eee7185b8e3cca84e730d9e..a75695045f2924709009aa910c77eaed3715cd48 100644 (file)
@@ -98,7 +98,7 @@ MODULE_PARM_DESC(async_unlink, "Use async unlink mode.");
 #define MAX_PACKS      10
 #define MAX_PACKS_HS   (MAX_PACKS * 8) /* in high speed mode */
 #define MAX_URBS       5       /* max. 20ms long packets */
-#define SYNC_URBS      2       /* always two urbs for sync */
+#define SYNC_URBS      4       /* always four urbs for sync */
 #define MIN_PACKS_URB  1       /* minimum 1 packet per urb */
 
 typedef struct snd_usb_substream snd_usb_substream_t;
@@ -177,7 +177,7 @@ struct snd_usb_substream {
        unsigned int nurbs;                     /* # urbs */
        snd_urb_ctx_t dataurb[MAX_URBS];        /* data urb table */
        snd_urb_ctx_t syncurb[SYNC_URBS];       /* sync urb table */
-       char syncbuf[SYNC_URBS * MAX_PACKS * 4]; /* sync buffer; it's so small - let's get static */
+       char syncbuf[SYNC_URBS * 4];    /* sync buffer; it's so small - let's get static */
        char *tmpbuf;                   /* temporary buffer for playback */
 
        u64 formats;                    /* format bitmasks (all or'ed) */
@@ -251,17 +251,13 @@ static int prepare_capture_sync_urb(snd_usb_substream_t *subs,
 {
        unsigned char *cp = urb->transfer_buffer;
        snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;
-       int i, offs;
 
-       urb->number_of_packets = ctx->packets;
        urb->dev = ctx->subs->dev; /* we need to set this at each time */
-       for (i = offs = 0; i < urb->number_of_packets; i++, offs += 4, cp += 4) {
-               urb->iso_frame_desc[i].length = 3;
-               urb->iso_frame_desc[i].offset = offs;
-               cp[0] = subs->freqn >> 2;
-               cp[1] = subs->freqn >> 10;
-               cp[2] = subs->freqn >> 18;
-       }
+       urb->iso_frame_desc[0].length = 3;
+       urb->iso_frame_desc[0].offset = 0;
+       cp[0] = subs->freqn >> 2;
+       cp[1] = subs->freqn >> 10;
+       cp[2] = subs->freqn >> 18;
        return 0;
 }
 
@@ -277,18 +273,14 @@ static int prepare_capture_sync_urb_hs(snd_usb_substream_t *subs,
 {
        unsigned char *cp = urb->transfer_buffer;
        snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;
-       int i, offs;
 
-       urb->number_of_packets = ctx->packets;
        urb->dev = ctx->subs->dev; /* we need to set this at each time */
-       for (i = offs = 0; i < urb->number_of_packets; i++, offs += 4, cp += 4) {
-               urb->iso_frame_desc[i].length = 4;
-               urb->iso_frame_desc[i].offset = offs;
-               cp[0] = subs->freqn;
-               cp[1] = subs->freqn >> 8;
-               cp[2] = subs->freqn >> 16;
-               cp[3] = subs->freqn >> 24;
-       }
+       urb->iso_frame_desc[0].length = 4;
+       urb->iso_frame_desc[0].offset = 0;
+       cp[0] = subs->freqn;
+       cp[1] = subs->freqn >> 8;
+       cp[2] = subs->freqn >> 16;
+       cp[3] = subs->freqn >> 24;
        return 0;
 }
 
@@ -418,15 +410,11 @@ static int prepare_playback_sync_urb(snd_usb_substream_t *subs,
                                     snd_pcm_runtime_t *runtime,
                                     struct urb *urb)
 {
-       int i, offs;
        snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;
 
-       urb->number_of_packets = ctx->packets;
        urb->dev = ctx->subs->dev; /* we need to set this at each time */
-       for (i = offs = 0; i < urb->number_of_packets; i++, offs += 4) {
-               urb->iso_frame_desc[i].length = 3;
-               urb->iso_frame_desc[i].offset = offs;
-       }
+       urb->iso_frame_desc[0].length = 3;
+       urb->iso_frame_desc[0].offset = 0;
        return 0;
 }
 
@@ -440,15 +428,11 @@ static int prepare_playback_sync_urb_hs(snd_usb_substream_t *subs,
                                        snd_pcm_runtime_t *runtime,
                                        struct urb *urb)
 {
-       int i, offs;
        snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;
 
-       urb->number_of_packets = ctx->packets;
        urb->dev = ctx->subs->dev; /* we need to set this at each time */
-       for (i = offs = 0; i < urb->number_of_packets; i++, offs += 4) {
-               urb->iso_frame_desc[i].length = 4;
-               urb->iso_frame_desc[i].offset = offs;
-       }
+       urb->iso_frame_desc[0].length = 4;
+       urb->iso_frame_desc[0].offset = 0;
        return 0;
 }
 
@@ -462,31 +446,17 @@ static int retire_playback_sync_urb(snd_usb_substream_t *subs,
                                    snd_pcm_runtime_t *runtime,
                                    struct urb *urb)
 {
-       int i;
-       unsigned int f, found;
-       unsigned char *cp = urb->transfer_buffer;
+       unsigned int f;
        unsigned long flags;
 
-       found = 0;
-       for (i = 0; i < urb->number_of_packets; i++, cp += 4) {
-               if (urb->iso_frame_desc[i].status ||
-                   urb->iso_frame_desc[i].actual_length < 3)
-                       continue;
-               f = combine_triple(cp) << 2;
-#if 0
-               if (f < subs->freqn - (subs->freqn>>3) || f > subs->freqmax) {
-                       snd_printd(KERN_WARNING "requested frequency %d (%u,%03uHz) out of range (current nominal %d (%u,%03uHz))\n",
-                                  f, f >> 14, (f & ((1 << 14) - 1) * 1000) / ((1 << 14) - 1),
-                                  subs->freqn, subs->freqn >> 14, (subs->freqn & ((1 << 14) - 1) * 1000) / ((1 << 14) - 1));
-                       continue;
+       if (urb->iso_frame_desc[0].status == 0 &&
+           urb->iso_frame_desc[0].actual_length == 3) {
+               f = combine_triple((u8*)urb->transfer_buffer) << 2;
+               if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) {
+                       spin_lock_irqsave(&subs->lock, flags);
+                       subs->freqm = f;
+                       spin_unlock_irqrestore(&subs->lock, flags);
                }
-#endif
-               found = f;
-       }
-       if (found) {
-               spin_lock_irqsave(&subs->lock, flags);
-               subs->freqm = found;
-               spin_unlock_irqrestore(&subs->lock, flags);
        }
 
        return 0;
@@ -502,22 +472,17 @@ static int retire_playback_sync_urb_hs(snd_usb_substream_t *subs,
                                       snd_pcm_runtime_t *runtime,
                                       struct urb *urb)
 {
-       int i;
-       unsigned int found;
-       unsigned char *cp = urb->transfer_buffer;
+       unsigned int f;
        unsigned long flags;
 
-       found = 0;
-       for (i = 0; i < urb->number_of_packets; i++, cp += 4) {
-               if (urb->iso_frame_desc[i].status ||
-                   urb->iso_frame_desc[i].actual_length < 4)
-                       continue;
-               found = combine_quad(cp) & 0x0fffffff;
-       }
-       if (found) {
-               spin_lock_irqsave(&subs->lock, flags);
-               subs->freqm = found;
-               spin_unlock_irqrestore(&subs->lock, flags);
+       if (urb->iso_frame_desc[0].status == 0 &&
+           urb->iso_frame_desc[0].actual_length == 4) {
+               f = combine_quad((u8*)urb->transfer_buffer) & 0x0fffffff;
+               if (f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax) {
+                       spin_lock_irqsave(&subs->lock, flags);
+                       subs->freqm = f;
+                       spin_unlock_irqrestore(&subs->lock, flags);
+               }
        }
 
        return 0;
@@ -600,6 +565,8 @@ static int prepare_playback_urb(snd_usb_substream_t *subs,
                /* set the buffer pointer */
                urb->transfer_buffer = runtime->dma_area + subs->hwptr * stride;
                subs->hwptr += offs;
+               if (subs->hwptr == runtime->buffer_size)
+                       subs->hwptr = 0;
        }
        spin_unlock_irqrestore(&subs->lock, flags);
        urb->transfer_buffer_length = offs * stride;
@@ -892,10 +859,8 @@ static void release_urb_ctx(snd_urb_ctx_t *u)
                usb_free_urb(u->urb);
                u->urb = NULL;
        }
-       if (u->buf) {
-               kfree(u->buf);
-               u->buf = NULL;
-       }
+       kfree(u->buf);
+       u->buf = NULL;
 }
 
 /*
@@ -913,10 +878,8 @@ static void release_substream_urbs(snd_usb_substream_t *subs, int force)
                release_urb_ctx(&subs->dataurb[i]);
        for (i = 0; i < SYNC_URBS; i++)
                release_urb_ctx(&subs->syncurb[i]);
-       if (subs->tmpbuf) {
-               kfree(subs->tmpbuf);
-               subs->tmpbuf = NULL;
-       }
+       kfree(subs->tmpbuf);
+       subs->tmpbuf = NULL;
        subs->nurbs = 0;
 }
 
@@ -1039,22 +1002,19 @@ static int init_substream_urbs(snd_usb_substream_t *subs, unsigned int period_by
                        snd_urb_ctx_t *u = &subs->syncurb[i];
                        u->index = i;
                        u->subs = subs;
-                       u->packets = nrpacks;
-                       u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
+                       u->packets = 1;
+                       u->urb = usb_alloc_urb(1, GFP_KERNEL);
                        if (! u->urb) {
                                release_substream_urbs(subs, 0);
                                return -ENOMEM;
                        }
-                       u->urb->transfer_buffer = subs->syncbuf + i * nrpacks * 4;
-                       u->urb->transfer_buffer_length = nrpacks * 4;
+                       u->urb->transfer_buffer = subs->syncbuf + i * 4;
+                       u->urb->transfer_buffer_length = 4;
                        u->urb->dev = subs->dev;
                        u->urb->pipe = subs->syncpipe;
                        u->urb->transfer_flags = URB_ISO_ASAP;
-                       u->urb->number_of_packets = u->packets;
-                       if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH)
-                               u->urb->interval = 8;
-                       else
-                               u->urb->interval = 1;
+                       u->urb->number_of_packets = 1;
+                       u->urb->interval = 1 << subs->syncinterval;
                        u->urb->context = u;
                        u->urb->complete = snd_usb_complete_callback(snd_complete_sync_urb);
                }
@@ -1272,7 +1232,17 @@ static int set_format(snd_usb_substream_t *subs, struct audioformat *fmt)
                        subs->syncpipe = usb_rcvisocpipe(dev, ep);
                else
                        subs->syncpipe = usb_sndisocpipe(dev, ep);
-               subs->syncinterval = get_endpoint(alts, 1)->bRefresh;
+               if (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
+                   get_endpoint(alts, 1)->bRefresh >= 1 &&
+                   get_endpoint(alts, 1)->bRefresh <= 9)
+                       subs->syncinterval = get_endpoint(alts, 1)->bRefresh;
+               else if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
+                       subs->syncinterval = 1;
+               else if (get_endpoint(alts, 1)->bInterval >= 1 &&
+                        get_endpoint(alts, 1)->bInterval <= 16)
+                       subs->syncinterval = get_endpoint(alts, 1)->bInterval - 1;
+               else
+                       subs->syncinterval = 3;
        }
 
        /* always fill max packet size */
@@ -1990,10 +1960,11 @@ static void proc_dump_substream_status(snd_usb_substream_t *subs, snd_info_buffe
                        snd_iprintf(buffer, "%d ", subs->dataurb[i].packets);
                snd_iprintf(buffer, "]\n");
                snd_iprintf(buffer, "    Packet Size = %d\n", subs->curpacksize);
-               snd_iprintf(buffer, "    Momentary freq = %u Hz\n",
+               snd_iprintf(buffer, "    Momentary freq = %u Hz (%#x.%04x)\n",
                            snd_usb_get_speed(subs->dev) == USB_SPEED_FULL
                            ? get_full_speed_hz(subs->freqm)
-                           : get_high_speed_hz(subs->freqm));
+                           : get_high_speed_hz(subs->freqm),
+                           subs->freqm >> 16, subs->freqm & 0xffff);
        } else {
                snd_iprintf(buffer, "  Status: Stop\n");
        }
@@ -2183,17 +2154,15 @@ static int add_audio_endpoint(snd_usb_audio_t *chip, int stream, struct audiofor
 /*
  * check if the device uses big-endian samples
  */
-static int is_big_endian_format(struct usb_device *dev, struct audioformat *fp)
+static int is_big_endian_format(snd_usb_audio_t *chip, struct audioformat *fp)
 {
-       /* M-Audio */
-       if (le16_to_cpu(dev->descriptor.idVendor) == 0x0763) {
-               /* Quattro: captured data only */
-               if (le16_to_cpu(dev->descriptor.idProduct) == 0x2001 &&
-                   fp->endpoint & USB_DIR_IN)
-                       return 1;
-               /* Audiophile USB */
-               if (le16_to_cpu(dev->descriptor.idProduct) == 0x2003)
+       switch (chip->usb_id) {
+       case USB_ID(0x0763, 0x2001): /* M-Audio Quattro: captured data only */
+               if (fp->endpoint & USB_DIR_IN)
                        return 1;
+               break;
+       case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
+               return 1;
        }
        return 0;
 }
@@ -2207,7 +2176,7 @@ static int is_big_endian_format(struct usb_device *dev, struct audioformat *fp)
  * @format: the format tag (wFormatTag)
  * @fmt: the format type descriptor
  */
-static int parse_audio_format_i_type(struct usb_device *dev, struct audioformat *fp,
+static int parse_audio_format_i_type(snd_usb_audio_t *chip, struct audioformat *fp,
                                     int format, unsigned char *fmt)
 {
        int pcm_format;
@@ -2220,12 +2189,12 @@ static int parse_audio_format_i_type(struct usb_device *dev, struct audioformat
        switch (format) {
        case 0: /* some devices don't define this correctly... */
                snd_printdd(KERN_INFO "%d:%u:%d : format type 0 is detected, processed as PCM\n",
-                           dev->devnum, fp->iface, fp->altsetting);
+                           chip->dev->devnum, fp->iface, fp->altsetting);
                /* fall-through */
        case USB_AUDIO_FORMAT_PCM:
                if (sample_width > sample_bytes * 8) {
                        snd_printk(KERN_INFO "%d:%u:%d : sample bitwidth %d in over sample bytes %d\n",
-                                  dev->devnum, fp->iface, fp->altsetting,
+                                  chip->dev->devnum, fp->iface, fp->altsetting,
                                   sample_width, sample_bytes);
                }
                /* check the format byte size */
@@ -2234,13 +2203,13 @@ static int parse_audio_format_i_type(struct usb_device *dev, struct audioformat
                        pcm_format = SNDRV_PCM_FORMAT_S8;
                        break;
                case 2:
-                       if (is_big_endian_format(dev, fp))
+                       if (is_big_endian_format(chip, fp))
                                pcm_format = SNDRV_PCM_FORMAT_S16_BE; /* grrr, big endian!! */
                        else
                                pcm_format = SNDRV_PCM_FORMAT_S16_LE;
                        break;
                case 3:
-                       if (is_big_endian_format(dev, fp))
+                       if (is_big_endian_format(chip, fp))
                                pcm_format = SNDRV_PCM_FORMAT_S24_3BE; /* grrr, big endian!! */
                        else
                                pcm_format = SNDRV_PCM_FORMAT_S24_3LE;
@@ -2250,14 +2219,14 @@ static int parse_audio_format_i_type(struct usb_device *dev, struct audioformat
                        break;
                default:
                        snd_printk(KERN_INFO "%d:%u:%d : unsupported sample bitwidth %d in %d bytes\n",
-                                  dev->devnum, fp->iface, fp->altsetting, sample_width, sample_bytes);
+                                  chip->dev->devnum, fp->iface,
+                                  fp->altsetting, sample_width, sample_bytes);
                        break;
                }
                break;
        case USB_AUDIO_FORMAT_PCM8:
                /* Dallas DS4201 workaround */
-               if (le16_to_cpu(dev->descriptor.idVendor) == 0x04fa &&
-                   le16_to_cpu(dev->descriptor.idProduct) == 0x4201)
+               if (chip->usb_id == USB_ID(0x04fa, 0x4201))
                        pcm_format = SNDRV_PCM_FORMAT_S8;
                else
                        pcm_format = SNDRV_PCM_FORMAT_U8;
@@ -2273,7 +2242,7 @@ static int parse_audio_format_i_type(struct usb_device *dev, struct audioformat
                break;
        default:
                snd_printk(KERN_INFO "%d:%u:%d : unsupported format type %d\n",
-                          dev->devnum, fp->iface, fp->altsetting, format);
+                          chip->dev->devnum, fp->iface, fp->altsetting, format);
                break;
        }
        return pcm_format;
@@ -2290,13 +2259,13 @@ static int parse_audio_format_i_type(struct usb_device *dev, struct audioformat
  * @offset: the start offset of descriptor pointing the rate type
  *          (7 for type I and II, 8 for type II)
  */
-static int parse_audio_format_rates(struct usb_device *dev, struct audioformat *fp,
+static int parse_audio_format_rates(snd_usb_audio_t *chip, struct audioformat *fp,
                                    unsigned char *fmt, int offset)
 {
        int nr_rates = fmt[offset];
        if (fmt[0] < offset + 1 + 3 * (nr_rates ? nr_rates : 2)) {
                snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n",
-                                  dev->devnum, fp->iface, fp->altsetting);
+                                  chip->dev->devnum, fp->iface, fp->altsetting);
                return -1;
        }
 
@@ -2343,7 +2312,7 @@ static int parse_audio_format_rates(struct usb_device *dev, struct audioformat *
 /*
  * parse the format type I and III descriptors
  */
-static int parse_audio_format_i(struct usb_device *dev, struct audioformat *fp,
+static int parse_audio_format_i(snd_usb_audio_t *chip, struct audioformat *fp,
                                int format, unsigned char *fmt)
 {
        int pcm_format;
@@ -2355,7 +2324,7 @@ static int parse_audio_format_i(struct usb_device *dev, struct audioformat *fp,
                 */
                pcm_format = SNDRV_PCM_FORMAT_S16_LE;
        } else {
-               pcm_format = parse_audio_format_i_type(dev, fp, format, fmt);
+               pcm_format = parse_audio_format_i_type(chip, fp, format, fmt);
                if (pcm_format < 0)
                        return -1;
        }
@@ -2363,16 +2332,16 @@ static int parse_audio_format_i(struct usb_device *dev, struct audioformat *fp,
        fp->channels = fmt[4];
        if (fp->channels < 1) {
                snd_printk(KERN_ERR "%d:%u:%d : invalid channels %d\n",
-                          dev->devnum, fp->iface, fp->altsetting, fp->channels);
+                          chip->dev->devnum, fp->iface, fp->altsetting, fp->channels);
                return -1;
        }
-       return parse_audio_format_rates(dev, fp, fmt, 7);
+       return parse_audio_format_rates(chip, fp, fmt, 7);
 }
 
 /*
  * prase the format type II descriptor
  */
-static int parse_audio_format_ii(struct usb_device *dev, struct audioformat *fp,
+static int parse_audio_format_ii(snd_usb_audio_t *chip, struct audioformat *fp,
                                 int format, unsigned char *fmt)
 {
        int brate, framesize;
@@ -2387,7 +2356,7 @@ static int parse_audio_format_ii(struct usb_device *dev, struct audioformat *fp,
                break;
        default:
                snd_printd(KERN_INFO "%d:%u:%d : unknown format tag 0x%x is detected.  processed as MPEG.\n",
-                          dev->devnum, fp->iface, fp->altsetting, format);
+                          chip->dev->devnum, fp->iface, fp->altsetting, format);
                fp->format = SNDRV_PCM_FORMAT_MPEG;
                break;
        }
@@ -2396,10 +2365,10 @@ static int parse_audio_format_ii(struct usb_device *dev, struct audioformat *fp,
        framesize = combine_word(&fmt[6]); /* fmt[6,7]: wSamplesPerFrame */
        snd_printd(KERN_INFO "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize);
        fp->frame_size = framesize;
-       return parse_audio_format_rates(dev, fp, fmt, 8); /* fmt[8..] sample rates */
+       return parse_audio_format_rates(chip, fp, fmt, 8); /* fmt[8..] sample rates */
 }
 
-static int parse_audio_format(struct usb_device *dev, struct audioformat *fp,
+static int parse_audio_format(snd_usb_audio_t *chip, struct audioformat *fp,
                              int format, unsigned char *fmt, int stream)
 {
        int err;
@@ -2407,29 +2376,30 @@ static int parse_audio_format(struct usb_device *dev, struct audioformat *fp,
        switch (fmt[3]) {
        case USB_FORMAT_TYPE_I:
        case USB_FORMAT_TYPE_III:
-               err = parse_audio_format_i(dev, fp, format, fmt);
+               err = parse_audio_format_i(chip, fp, format, fmt);
                break;
        case USB_FORMAT_TYPE_II:
-               err = parse_audio_format_ii(dev, fp, format, fmt);
+               err = parse_audio_format_ii(chip, fp, format, fmt);
                break;
        default:
                snd_printd(KERN_INFO "%d:%u:%d : format type %d is not supported yet\n",
-                          dev->devnum, fp->iface, fp->altsetting, fmt[3]);
+                          chip->dev->devnum, fp->iface, fp->altsetting, fmt[3]);
                return -1;
        }
        fp->fmt_type = fmt[3];
        if (err < 0)
                return err;
 #if 1
-       /* FIXME: temporary hack for extigy */
+       /* FIXME: temporary hack for extigy/audigy 2 nx */
        /* extigy apparently supports sample rates other than 48k
         * but not in ordinary way.  so we enable only 48k atm.
         */
-       if (le16_to_cpu(dev->descriptor.idVendor) == 0x041e && 
-           le16_to_cpu(dev->descriptor.idProduct) == 0x3000) {
+       if (chip->usb_id == USB_ID(0x041e, 0x3000) ||
+           chip->usb_id == USB_ID(0x041e, 0x3020)) {
                if (fmt[3] == USB_FORMAT_TYPE_I &&
                    stream == SNDRV_PCM_STREAM_PLAYBACK &&
-                   fp->rates != SNDRV_PCM_RATE_48000)
+                   fp->rates != SNDRV_PCM_RATE_48000 &&
+                   fp->rates != SNDRV_PCM_RATE_96000)
                        return -1; /* use 48k only */
        }
 #endif
@@ -2528,40 +2498,35 @@ static int parse_audio_endpoints(snd_usb_audio_t *chip, int iface_no)
 
                /* some quirks for attributes here */
 
-               /* workaround for AudioTrak Optoplay */
-               if (le16_to_cpu(dev->descriptor.idVendor) == 0x0a92 &&
-                   le16_to_cpu(dev->descriptor.idProduct) == 0x0053) {
+               switch (chip->usb_id) {
+               case USB_ID(0x0a92, 0x0053): /* AudioTrak Optoplay */
                        /* Optoplay sets the sample rate attribute although
                         * it seems not supporting it in fact.
                         */
                        fp->attributes &= ~EP_CS_ATTR_SAMPLE_RATE;
-               }
-
-               /* workaround for M-Audio Audiophile USB */
-               if (le16_to_cpu(dev->descriptor.idVendor) == 0x0763 &&
-                   le16_to_cpu(dev->descriptor.idProduct) == 0x2003) {
+                       break;
+               case USB_ID(0x041e, 0x3020): /* Creative SB Audigy 2 NX */
+               case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
                        /* doesn't set the sample rate attribute, but supports it */
                        fp->attributes |= EP_CS_ATTR_SAMPLE_RATE;
-               }
-
+                       break;
+               case USB_ID(0x047f, 0x0ca1): /* plantronics headset */
+               case USB_ID(0x077d, 0x07af): /* Griffin iMic (note that there is
+                                               an older model 77d:223) */
                /*
                 * plantronics headset and Griffin iMic have set adaptive-in
                 * although it's really not...
                 */
-               if ((le16_to_cpu(dev->descriptor.idVendor) == 0x047f &&
-                    le16_to_cpu(dev->descriptor.idProduct) == 0x0ca1) ||
-                   /* Griffin iMic (note that there is an older model 77d:223) */
-                   (le16_to_cpu(dev->descriptor.idVendor) == 0x077d &&
-                    le16_to_cpu(dev->descriptor.idProduct) == 0x07af)) {
                        fp->ep_attr &= ~EP_ATTR_MASK;
                        if (stream == SNDRV_PCM_STREAM_PLAYBACK)
                                fp->ep_attr |= EP_ATTR_ADAPTIVE;
                        else
                                fp->ep_attr |= EP_ATTR_SYNC;
+                       break;
                }
 
                /* ok, let's parse further... */
-               if (parse_audio_format(dev, fp, format, fmt, stream) < 0) {
+               if (parse_audio_format(chip, fp, format, fmt, stream) < 0) {
                        kfree(fp->rate_table);
                        kfree(fp);
                        continue;
@@ -2587,7 +2552,7 @@ static int parse_audio_endpoints(snd_usb_audio_t *chip, int iface_no)
  * disconnect streams
  * called from snd_usb_audio_disconnect()
  */
-static void snd_usb_stream_disconnect(struct list_head *head, struct usb_driver *driver)
+static void snd_usb_stream_disconnect(struct list_head *head)
 {
        int idx;
        snd_usb_stream_t *as;
@@ -2796,7 +2761,7 @@ static int create_ua700_ua25_quirk(snd_usb_audio_t *chip,
                        .type = QUIRK_MIDI_FIXED_ENDPOINT,
                        .data = &ua25_ep
                };
-               if (le16_to_cpu(chip->dev->descriptor.idProduct) == 0x002b)
+               if (chip->usb_id == USB_ID(0x0582, 0x002b))
                        return snd_usb_create_midi_interface(chip, iface,
                                                             &ua700_quirk);
                else
@@ -2959,6 +2924,25 @@ static int snd_usb_extigy_boot_quirk(struct usb_device *dev, struct usb_interfac
        return 0;
 }
 
+static int snd_usb_audigy2nx_boot_quirk(struct usb_device *dev)
+{
+#if 0
+       /* TODO: enable this when high speed synchronization actually works */
+       u8 buf = 1;
+
+       snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), 0x2a,
+                       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+                       0, 0, &buf, 1, 1000);
+       if (buf == 0) {
+               snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0x29,
+                               USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+                               1, 2000, NULL, 0, 1000);
+               return -ENODEV;
+       }
+#endif
+       return 0;
+}
+
 
 /*
  * audio-interface quirks
@@ -3015,8 +2999,8 @@ static void proc_audio_usbid_read(snd_info_entry_t *entry, snd_info_buffer_t *bu
        snd_usb_audio_t *chip = entry->private_data;
        if (! chip->shutdown)
                snd_iprintf(buffer, "%04x:%04x\n", 
-                           le16_to_cpu(chip->dev->descriptor.idVendor),
-                           le16_to_cpu(chip->dev->descriptor.idProduct));
+                           USB_ID_VENDOR(chip->usb_id),
+                           USB_ID_PRODUCT(chip->usb_id));
 }
 
 static void snd_usb_audio_create_proc(snd_usb_audio_t *chip)
@@ -3086,8 +3070,11 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
        chip->index = idx;
        chip->dev = dev;
        chip->card = card;
+       chip->usb_id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
+                             le16_to_cpu(dev->descriptor.idProduct));
        INIT_LIST_HEAD(&chip->pcm_list);
        INIT_LIST_HEAD(&chip->midi_list);
+       INIT_LIST_HEAD(&chip->mixer_list);
 
        if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
                snd_usb_audio_free(chip);
@@ -3097,8 +3084,7 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
 
        strcpy(card->driver, "USB-Audio");
        sprintf(component, "USB%04x:%04x",
-               le16_to_cpu(dev->descriptor.idVendor),
-               le16_to_cpu(dev->descriptor.idProduct));
+               USB_ID_VENDOR(chip->usb_id), USB_ID_PRODUCT(chip->usb_id));
        snd_component_add(card, component);
 
        /* retrieve the device string as shortname */
@@ -3110,8 +3096,8 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
                               card->shortname, sizeof(card->shortname)) <= 0) {
                        /* no name available from anywhere, so use ID */
                        sprintf(card->shortname, "USB Device %#04x:%#04x",
-                               le16_to_cpu(dev->descriptor.idVendor),
-                               le16_to_cpu(dev->descriptor.idProduct));
+                               USB_ID_VENDOR(chip->usb_id),
+                               USB_ID_PRODUCT(chip->usb_id));
                }
        }
 
@@ -3142,8 +3128,6 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
 
        snd_usb_audio_create_proc(chip);
 
-       snd_card_set_dev(card, &dev->dev);
-
        *rchip = chip;
        return 0;
 }
@@ -3169,21 +3153,28 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
        snd_usb_audio_t *chip;
        struct usb_host_interface *alts;
        int ifnum;
+       u32 id;
 
        alts = &intf->altsetting[0];
        ifnum = get_iface_desc(alts)->bInterfaceNumber;
+       id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
+                   le16_to_cpu(dev->descriptor.idProduct));
 
        if (quirk && quirk->ifnum >= 0 && ifnum != quirk->ifnum)
                goto __err_val;
 
        /* SB Extigy needs special boot-up sequence */
        /* if more models come, this will go to the quirk list. */
-       if (le16_to_cpu(dev->descriptor.idVendor) == 0x041e && 
-           le16_to_cpu(dev->descriptor.idProduct) == 0x3000) {
+       if (id == USB_ID(0x041e, 0x3000)) {
                if (snd_usb_extigy_boot_quirk(dev, intf) < 0)
                        goto __err_val;
                config = dev->actconfig;
        }
+       /* SB Audigy 2 NX needs its own boot-up magic, too */
+       if (id == USB_ID(0x041e, 0x3020)) {
+               if (snd_usb_audigy2nx_boot_quirk(dev) < 0)
+                       goto __err_val;
+       }
 
        /*
         * found a config.  now register to ALSA
@@ -3213,11 +3204,12 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
                }
                for (i = 0; i < SNDRV_CARDS; i++)
                        if (enable[i] && ! usb_chip[i] &&
-                           (vid[i] == -1 || vid[i] == le16_to_cpu(dev->descriptor.idVendor)) &&
-                           (pid[i] == -1 || pid[i] == le16_to_cpu(dev->descriptor.idProduct))) {
+                           (vid[i] == -1 || vid[i] == USB_ID_VENDOR(id)) &&
+                           (pid[i] == -1 || pid[i] == USB_ID_PRODUCT(id))) {
                                if (snd_usb_audio_create(dev, i, quirk, &chip) < 0) {
                                        goto __error;
                                }
+                               snd_card_set_dev(chip->card, &intf->dev);
                                break;
                        }
                if (! chip) {
@@ -3281,11 +3273,15 @@ static void snd_usb_audio_disconnect(struct usb_device *dev, void *ptr)
                snd_card_disconnect(card);
                /* release the pcm resources */
                list_for_each(p, &chip->pcm_list) {
-                       snd_usb_stream_disconnect(p, &usb_audio_driver);
+                       snd_usb_stream_disconnect(p);
                }
                /* release the midi resources */
                list_for_each(p, &chip->midi_list) {
-                       snd_usbmidi_disconnect(p, &usb_audio_driver);
+                       snd_usbmidi_disconnect(p);
+               }
+               /* release mixer resources */
+               list_for_each(p, &chip->mixer_list) {
+                       snd_usb_mixer_disconnect(p);
                }
                usb_chip[chip->index] = NULL;
                up(&register_mutex);
index eecbf19fcb6fc7ed887b6de6eb82e41923f86c1d..aedb42aaa74947e8bbf992fa2e5e873aa2fa01ff 100644 (file)
 /* maximum number of endpoints per interface */
 #define MIDI_MAX_ENDPOINTS 2
 
+/* handling of USB vendor/product ID pairs as 32-bit numbers */
+#define USB_ID(vendor, product) (((vendor) << 16) | (product))
+#define USB_ID_VENDOR(id) ((id) >> 16)
+#define USB_ID_PRODUCT(id) ((u16)(id))
+
 /*
  */
 
@@ -127,6 +132,7 @@ struct snd_usb_audio {
        int index;
        struct usb_device *dev;
        snd_card_t *card;
+       u32 usb_id;
        int shutdown;
        int num_interfaces;
 
@@ -136,7 +142,7 @@ struct snd_usb_audio {
        struct list_head midi_list;     /* list of midi interfaces */
        int next_midi_device;
 
-       unsigned int ignore_ctl_error;  /* for mixer */
+       struct list_head mixer_list;    /* list of mixer interfaces */
 };
 
 /*
@@ -219,11 +225,12 @@ void *snd_usb_find_csint_desc(void *descstart, int desclen, void *after, u8 dsub
 int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout);
 
 int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif);
+void snd_usb_mixer_disconnect(struct list_head *p);
 
 int snd_usb_create_midi_interface(snd_usb_audio_t *chip, struct usb_interface *iface, const snd_usb_audio_quirk_t *quirk);
 void snd_usbmidi_input_stop(struct list_head* p);
 void snd_usbmidi_input_start(struct list_head* p);
-void snd_usbmidi_disconnect(struct list_head *p, struct usb_driver *driver);
+void snd_usbmidi_disconnect(struct list_head *p);
 
 /*
  * retrieve usb_interface descriptor from the host interface
index 5d32857ff9554b0c4d7b8c9dbc037f67b241508d..bee70068dce083726a13a4c18481fe7abec11726 100644 (file)
@@ -912,7 +912,7 @@ static void snd_usbmidi_free(snd_usb_midi_t* umidi)
 /*
  * Unlinks all URBs (must be done before the usb_device is deleted).
  */
-void snd_usbmidi_disconnect(struct list_head* p, struct usb_driver *driver)
+void snd_usbmidi_disconnect(struct list_head* p)
 {
        snd_usb_midi_t* umidi;
        int i;
@@ -955,88 +955,87 @@ static snd_rawmidi_substream_t* snd_usbmidi_find_substream(snd_usb_midi_t* umidi
  * such as internal control or synthesizer ports.
  */
 static struct {
-       __u16 vendor;
-       __u16 product;
+       u32 id;
        int port;
        const char *name_format;
 } snd_usbmidi_port_names[] = {
        /* Roland UA-100 */
-       {0x0582, 0x0000, 2, "%s Control"},
+       { USB_ID(0x0582, 0x0000), 2, "%s Control" },
        /* Roland SC-8850 */
-       {0x0582, 0x0003, 0, "%s Part A"},
-       {0x0582, 0x0003, 1, "%s Part B"},
-       {0x0582, 0x0003, 2, "%s Part C"},
-       {0x0582, 0x0003, 3, "%s Part D"},
-       {0x0582, 0x0003, 4, "%s MIDI 1"},
-       {0x0582, 0x0003, 5, "%s MIDI 2"},
+       { USB_ID(0x0582, 0x0003), 0, "%s Part A" },
+       { USB_ID(0x0582, 0x0003), 1, "%s Part B" },
+       { USB_ID(0x0582, 0x0003), 2, "%s Part C" },
+       { USB_ID(0x0582, 0x0003), 3, "%s Part D" },
+       { USB_ID(0x0582, 0x0003), 4, "%s MIDI 1" },
+       { USB_ID(0x0582, 0x0003), 5, "%s MIDI 2" },
        /* Roland U-8 */
-       {0x0582, 0x0004, 0, "%s MIDI"},
-       {0x0582, 0x0004, 1, "%s Control"},
+       { USB_ID(0x0582, 0x0004), 0, "%s MIDI" },
+       { USB_ID(0x0582, 0x0004), 1, "%s Control" },
        /* Roland SC-8820 */
-       {0x0582, 0x0007, 0, "%s Part A"},
-       {0x0582, 0x0007, 1, "%s Part B"},
-       {0x0582, 0x0007, 2, "%s MIDI"},
+       { USB_ID(0x0582, 0x0007), 0, "%s Part A" },
+       { USB_ID(0x0582, 0x0007), 1, "%s Part B" },
+       { USB_ID(0x0582, 0x0007), 2, "%s MIDI" },
        /* Roland SK-500 */
-       {0x0582, 0x000b, 0, "%s Part A"},
-       {0x0582, 0x000b, 1, "%s Part B"},
-       {0x0582, 0x000b, 2, "%s MIDI"},
+       { USB_ID(0x0582, 0x000b), 0, "%s Part A" },
+       { USB_ID(0x0582, 0x000b), 1, "%s Part B" },
+       { USB_ID(0x0582, 0x000b), 2, "%s MIDI" },
        /* Roland SC-D70 */
-       {0x0582, 0x000c, 0, "%s Part A"},
-       {0x0582, 0x000c, 1, "%s Part B"},
-       {0x0582, 0x000c, 2, "%s MIDI"},
+       { USB_ID(0x0582, 0x000c), 0, "%s Part A" },
+       { USB_ID(0x0582, 0x000c), 1, "%s Part B" },
+       { USB_ID(0x0582, 0x000c), 2, "%s MIDI" },
        /* Edirol UM-880 */
-       {0x0582, 0x0014, 8, "%s Control"},
+       { USB_ID(0x0582, 0x0014), 8, "%s Control" },
        /* Edirol SD-90 */
-       {0x0582, 0x0016, 0, "%s Part A"},
-       {0x0582, 0x0016, 1, "%s Part B"},
-       {0x0582, 0x0016, 2, "%s MIDI 1"},
-       {0x0582, 0x0016, 3, "%s MIDI 2"},
+       { USB_ID(0x0582, 0x0016), 0, "%s Part A" },
+       { USB_ID(0x0582, 0x0016), 1, "%s Part B" },
+       { USB_ID(0x0582, 0x0016), 2, "%s MIDI 1" },
+       { USB_ID(0x0582, 0x0016), 3, "%s MIDI 2" },
        /* Edirol UM-550 */
-       {0x0582, 0x0023, 5, "%s Control"},
+       { USB_ID(0x0582, 0x0023), 5, "%s Control" },
        /* Edirol SD-20 */
-       {0x0582, 0x0027, 0, "%s Part A"},
-       {0x0582, 0x0027, 1, "%s Part B"},
-       {0x0582, 0x0027, 2, "%s MIDI"},
+       { USB_ID(0x0582, 0x0027), 0, "%s Part A" },
+       { USB_ID(0x0582, 0x0027), 1, "%s Part B" },
+       { USB_ID(0x0582, 0x0027), 2, "%s MIDI" },
        /* Edirol SD-80 */
-       {0x0582, 0x0029, 0, "%s Part A"},
-       {0x0582, 0x0029, 1, "%s Part B"},
-       {0x0582, 0x0029, 2, "%s MIDI 1"},
-       {0x0582, 0x0029, 3, "%s MIDI 2"},
+       { USB_ID(0x0582, 0x0029), 0, "%s Part A" },
+       { USB_ID(0x0582, 0x0029), 1, "%s Part B" },
+       { USB_ID(0x0582, 0x0029), 2, "%s MIDI 1" },
+       { USB_ID(0x0582, 0x0029), 3, "%s MIDI 2" },
        /* Edirol UA-700 */
-       {0x0582, 0x002b, 0, "%s MIDI"},
-       {0x0582, 0x002b, 1, "%s Control"},
+       { USB_ID(0x0582, 0x002b), 0, "%s MIDI" },
+       { USB_ID(0x0582, 0x002b), 1, "%s Control" },
        /* Roland VariOS */
-       {0x0582, 0x002f, 0, "%s MIDI"},
-       {0x0582, 0x002f, 1, "%s External MIDI"},
-       {0x0582, 0x002f, 2, "%s Sync"},
+       { USB_ID(0x0582, 0x002f), 0, "%s MIDI" },
+       { USB_ID(0x0582, 0x002f), 1, "%s External MIDI" },
+       { USB_ID(0x0582, 0x002f), 2, "%s Sync" },
        /* Edirol PCR */
-       {0x0582, 0x0033, 0, "%s MIDI"},
-       {0x0582, 0x0033, 1, "%s 1"},
-       {0x0582, 0x0033, 2, "%s 2"},
+       { USB_ID(0x0582, 0x0033), 0, "%s MIDI" },
+       { USB_ID(0x0582, 0x0033), 1, "%s 1" },
+       { USB_ID(0x0582, 0x0033), 2, "%s 2" },
        /* BOSS GS-10 */
-       {0x0582, 0x003b, 0, "%s MIDI"},
-       {0x0582, 0x003b, 1, "%s Control"},
+       { USB_ID(0x0582, 0x003b), 0, "%s MIDI" },
+       { USB_ID(0x0582, 0x003b), 1, "%s Control" },
        /* Edirol UA-1000 */
-       {0x0582, 0x0044, 0, "%s MIDI"},
-       {0x0582, 0x0044, 1, "%s Control"},
+       { USB_ID(0x0582, 0x0044), 0, "%s MIDI" },
+       { USB_ID(0x0582, 0x0044), 1, "%s Control" },
        /* Edirol UR-80 */
-       {0x0582, 0x0048, 0, "%s MIDI"},
-       {0x0582, 0x0048, 1, "%s 1"},
-       {0x0582, 0x0048, 2, "%s 2"},
+       { USB_ID(0x0582, 0x0048), 0, "%s MIDI" },
+       { USB_ID(0x0582, 0x0048), 1, "%s 1" },
+       { USB_ID(0x0582, 0x0048), 2, "%s 2" },
        /* Edirol PCR-A */
-       {0x0582, 0x004d, 0, "%s MIDI"},
-       {0x0582, 0x004d, 1, "%s 1"},
-       {0x0582, 0x004d, 2, "%s 2"},
+       { USB_ID(0x0582, 0x004d), 0, "%s MIDI" },
+       { USB_ID(0x0582, 0x004d), 1, "%s 1" },
+       { USB_ID(0x0582, 0x004d), 2, "%s 2" },
        /* M-Audio MidiSport 8x8 */
-       {0x0763, 0x1031, 8, "%s Control"},
-       {0x0763, 0x1033, 8, "%s Control"},
+       { USB_ID(0x0763, 0x1031), 8, "%s Control" },
+       { USB_ID(0x0763, 0x1033), 8, "%s Control" },
        /* MOTU Fastlane */
-       {0x07fd, 0x0001, 0, "%s MIDI A"},
-       {0x07fd, 0x0001, 1, "%s MIDI B"},
+       { USB_ID(0x07fd, 0x0001), 0, "%s MIDI A" },
+       { USB_ID(0x07fd, 0x0001), 1, "%s MIDI B" },
        /* Emagic Unitor8/AMT8/MT4 */
-       {0x086a, 0x0001, 8, "%s Broadcast"},
-       {0x086a, 0x0002, 8, "%s Broadcast"},
-       {0x086a, 0x0003, 4, "%s Broadcast"},
+       { USB_ID(0x086a, 0x0001), 8, "%s Broadcast" },
+       { USB_ID(0x086a, 0x0002), 8, "%s Broadcast" },
+       { USB_ID(0x086a, 0x0003), 4, "%s Broadcast" },
 };
 
 static void snd_usbmidi_init_substream(snd_usb_midi_t* umidi,
@@ -1044,7 +1043,6 @@ static void snd_usbmidi_init_substream(snd_usb_midi_t* umidi,
                                       snd_rawmidi_substream_t** rsubstream)
 {
        int i;
-       __u16 vendor, product;
        const char *name_format;
 
        snd_rawmidi_substream_t* substream = snd_usbmidi_find_substream(umidi, stream, number);
@@ -1055,11 +1053,8 @@ static void snd_usbmidi_init_substream(snd_usb_midi_t* umidi,
 
        /* TODO: read port name from jack descriptor */
        name_format = "%s MIDI %d";
-       vendor = le16_to_cpu(umidi->chip->dev->descriptor.idVendor);
-       product = le16_to_cpu(umidi->chip->dev->descriptor.idProduct);
        for (i = 0; i < ARRAY_SIZE(snd_usbmidi_port_names); ++i) {
-               if (snd_usbmidi_port_names[i].vendor == vendor &&
-                   snd_usbmidi_port_names[i].product == product &&
+               if (snd_usbmidi_port_names[i].id == umidi->chip->usb_id &&
                    snd_usbmidi_port_names[i].port == number) {
                        name_format = snd_usbmidi_port_names[i].name_format;
                        break;
@@ -1226,9 +1221,12 @@ static int snd_usbmidi_detect_endpoints(snd_usb_midi_t* umidi,
        struct usb_endpoint_descriptor* epd;
        int i, out_eps = 0, in_eps = 0;
 
-       if (le16_to_cpu(umidi->chip->dev->descriptor.idVendor) == 0x0582)
+       if (USB_ID_VENDOR(umidi->chip->usb_id) == 0x0582)
                snd_usbmidi_switch_roland_altsetting(umidi);
 
+       if (endpoint[0].out_ep || endpoint[0].in_ep)
+               return 0;       
+
        intf = umidi->iface;
        if (!intf || intf->num_altsetting < 1)
                return -ENOENT;
index 5f1906915aa626f66cd6e527e2484d92772c6530..e73c1c9d3e735a50c9b1edc56c31830c600f28b5 100644 (file)
 #include <linux/usb.h>
 #include <sound/core.h>
 #include <sound/control.h>
+#include <sound/hwdep.h>
+#include <sound/info.h>
 
 #include "usbaudio.h"
 
-
 /*
  */
 
@@ -50,6 +51,31 @@ typedef struct usb_audio_term usb_audio_term_t;
 typedef struct usb_mixer_elem_info usb_mixer_elem_info_t;
 
 
+struct usb_mixer_interface {
+       snd_usb_audio_t *chip;
+       unsigned int ctrlif;
+       struct list_head list;
+       unsigned int ignore_ctl_error;
+       struct urb *urb;
+       usb_mixer_elem_info_t **id_elems; /* array[256], indexed by unit id */
+
+       /* Sound Blaster remote control stuff */
+       enum {
+               RC_NONE,
+               RC_EXTIGY,
+               RC_AUDIGY2NX,
+       } rc_type;
+       unsigned long rc_hwdep_open;
+       u32 rc_code;
+       wait_queue_head_t rc_waitq;
+       struct urb *rc_urb;
+       struct usb_ctrlrequest *rc_setup_packet;
+       u8 rc_buffer[6];
+
+       u8 audigy2nx_leds[3];
+};
+
+
 struct usb_audio_term {
        int id;
        int type;
@@ -62,26 +88,26 @@ struct usbmix_name_map;
 
 struct usb_mixer_build {
        snd_usb_audio_t *chip;
+       struct usb_mixer_interface *mixer;
        unsigned char *buffer;
        unsigned int buflen;
-       unsigned int ctrlif;
-       unsigned short vendor;
-       unsigned short product;
-       DECLARE_BITMAP(unitbitmap, 32*32);
+       DECLARE_BITMAP(unitbitmap, 256);
        usb_audio_term_t oterm;
        const struct usbmix_name_map *map;
+       const struct usbmix_selector_map *selector_map;
 };
 
 struct usb_mixer_elem_info {
-       snd_usb_audio_t *chip;
-       unsigned int ctrlif;
+       struct usb_mixer_interface *mixer;
+       usb_mixer_elem_info_t *next_id_elem; /* list of controls with same id */
+       snd_ctl_elem_id_t *elem_id;
        unsigned int id;
        unsigned int control;   /* CS or ICN (high byte) */
        unsigned int cmask; /* channel mask bitmap: 0 = master */
        int channels;
        int val_type;
        int min, max, res;
-       unsigned int initialized: 1;
+       u8 initialized;
 };
 
 
@@ -187,6 +213,21 @@ static int check_ignored_ctl(mixer_build_t *state, int unitid, int control)
        return 0;
 }
 
+/* get the mapped selector source name */
+static int check_mapped_selector_name(mixer_build_t *state, int unitid,
+                                     int index, char *buf, int buflen)
+{
+       const struct usbmix_selector_map *p;
+
+       if (! state->selector_map)
+               return 0;
+       for (p = state->selector_map; p->id; p++) {
+               if (p->id == unitid && index < p->count)
+                       return strlcpy(buf, p->names[index], buflen);
+       }
+       return 0;
+}
+
 /*
  * find an audio control unit with the given unit id
  */
@@ -301,16 +342,18 @@ static int get_ctl_value(usb_mixer_elem_info_t *cval, int request, int validx, i
        int timeout = 10;
 
        while (timeout-- > 0) {
-               if (snd_usb_ctl_msg(cval->chip->dev, usb_rcvctrlpipe(cval->chip->dev, 0),
+               if (snd_usb_ctl_msg(cval->mixer->chip->dev,
+                                   usb_rcvctrlpipe(cval->mixer->chip->dev, 0),
                                    request,
                                    USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
-                                   validx, cval->ctrlif | (cval->id << 8),
+                                   validx, cval->mixer->ctrlif | (cval->id << 8),
                                    buf, val_len, 100) >= 0) {
                        *value_ret = convert_signed_value(cval, snd_usb_combine_bytes(buf, val_len));
                        return 0;
                }
        }
-       snd_printdd(KERN_ERR "cannot get ctl value: req = 0x%x, wValue = 0x%x, wIndex = 0x%x, type = %d\n", request, validx, cval->ctrlif | (cval->id << 8), cval->val_type);
+       snd_printdd(KERN_ERR "cannot get ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d\n",
+                   request, validx, cval->mixer->ctrlif | (cval->id << 8), cval->val_type);
        return -EINVAL;
 }
 
@@ -339,13 +382,15 @@ static int set_ctl_value(usb_mixer_elem_info_t *cval, int request, int validx, i
        buf[0] = value_set & 0xff;
        buf[1] = (value_set >> 8) & 0xff;
        while (timeout -- > 0)
-               if (snd_usb_ctl_msg(cval->chip->dev, usb_sndctrlpipe(cval->chip->dev, 0),
+               if (snd_usb_ctl_msg(cval->mixer->chip->dev,
+                                   usb_sndctrlpipe(cval->mixer->chip->dev, 0),
                                    request,
                                    USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
-                                   validx, cval->ctrlif | (cval->id << 8),
+                                   validx, cval->mixer->ctrlif | (cval->id << 8),
                                    buf, val_len, 100) >= 0)
                        return 0;
-       snd_printdd(KERN_ERR "cannot set ctl value: req = 0x%x, wValue = 0x%x, wIndex = 0x%x, type = %d, data = 0x%x/0x%x\n", request, validx, cval->ctrlif | (cval->id << 8), cval->val_type, buf[0], buf[1]);
+       snd_printdd(KERN_ERR "cannot set ctl value: req = %#x, wValue = %#x, wIndex = %#x, type = %d, data = %#x/%#x\n",
+                   request, validx, cval->mixer->ctrlif | (cval->id << 8), cval->val_type, buf[0], buf[1]);
        return -EINVAL;
 }
 
@@ -385,16 +430,22 @@ static int check_matrix_bitmap(unsigned char *bmap, int ich, int och, int num_ou
  * if failed, give up and free the control instance.
  */
 
-static int add_control_to_empty(snd_card_t *card, snd_kcontrol_t *kctl)
+static int add_control_to_empty(mixer_build_t *state, snd_kcontrol_t *kctl)
 {
+       usb_mixer_elem_info_t *cval = kctl->private_data;
        int err;
-       while (snd_ctl_find_id(card, &kctl->id))
+
+       while (snd_ctl_find_id(state->chip->card, &kctl->id))
                kctl->id.index++;
-       if ((err = snd_ctl_add(card, kctl)) < 0) {
+       if ((err = snd_ctl_add(state->chip->card, kctl)) < 0) {
                snd_printd(KERN_ERR "cannot add control (err = %d)\n", err);
                snd_ctl_free_one(kctl);
+               return err;
        }
-       return err;
+       cval->elem_id = &kctl->id;
+       cval->next_id_elem = state->mixer->id_elems[cval->id];
+       state->mixer->id_elems[cval->id] = cval;
+       return 0;
 }
 
 
@@ -572,10 +623,8 @@ static struct usb_feature_control_info audio_feature_info[] = {
 /* private_free callback */
 static void usb_mixer_elem_free(snd_kcontrol_t *kctl)
 {
-       if (kctl->private_data) {
-               kfree(kctl->private_data);
-               kctl->private_data = NULL;
-       }
+       kfree(kctl->private_data);
+       kctl->private_data = NULL;
 }
 
 
@@ -608,7 +657,8 @@ static int get_min_max(usb_mixer_elem_info_t *cval, int default_min)
                }
                if (get_ctl_value(cval, GET_MAX, (cval->control << 8) | minchn, &cval->max) < 0 ||
                    get_ctl_value(cval, GET_MIN, (cval->control << 8) | minchn, &cval->min) < 0) {
-                       snd_printd(KERN_ERR "%d:%d: cannot get min/max values for control %d (id %d)\n", cval->id, cval->ctrlif, cval->control, cval->id);
+                       snd_printd(KERN_ERR "%d:%d: cannot get min/max values for control %d (id %d)\n",
+                                  cval->id, cval->mixer->ctrlif, cval->control, cval->id);
                        return -EINVAL;
                }
                if (get_ctl_value(cval, GET_RES, (cval->control << 8) | minchn, &cval->res) < 0) {
@@ -668,7 +718,7 @@ static int mixer_ctl_feature_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
                        if (cval->cmask & (1 << c)) {
                                err = get_cur_mix_value(cval, c + 1, &val);
                                if (err < 0) {
-                                       if (cval->chip->ignore_ctl_error) {
+                                       if (cval->mixer->ignore_ctl_error) {
                                                ucontrol->value.integer.value[0] = cval->min;
                                                return 0;
                                        }
@@ -684,7 +734,7 @@ static int mixer_ctl_feature_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
                /* master channel */
                err = get_cur_mix_value(cval, 0, &val);
                if (err < 0) {
-                       if (cval->chip->ignore_ctl_error) {
+                       if (cval->mixer->ignore_ctl_error) {
                                ucontrol->value.integer.value[0] = cval->min;
                                return 0;
                        }
@@ -710,7 +760,7 @@ static int mixer_ctl_feature_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
                        if (cval->cmask & (1 << c)) {
                                err = get_cur_mix_value(cval, c + 1, &oval);
                                if (err < 0) {
-                                       if (cval->chip->ignore_ctl_error)
+                                       if (cval->mixer->ignore_ctl_error)
                                                return 0;
                                        return err;
                                }
@@ -727,7 +777,7 @@ static int mixer_ctl_feature_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
        } else {
                /* master channel */
                err = get_cur_mix_value(cval, 0, &oval);
-               if (err < 0 && cval->chip->ignore_ctl_error)
+               if (err < 0 && cval->mixer->ignore_ctl_error)
                        return 0;
                if (err < 0)
                        return err;
@@ -779,8 +829,7 @@ static void build_feature_ctl(mixer_build_t *state, unsigned char *desc,
                snd_printk(KERN_ERR "cannot malloc kcontrol\n");
                return;
        }
-       cval->chip = state->chip;
-       cval->ctrlif = state->ctrlif;
+       cval->mixer = state->mixer;
        cval->id = unitid;
        cval->control = control;
        cval->cmask = ctl_mask;
@@ -855,16 +904,21 @@ static void build_feature_ctl(mixer_build_t *state, unsigned char *desc,
        /* note that detection between firmware 2.1.1.7 (N101) and later 2.1.1.21 */
        /* is not very clear from datasheets */
        /* I hope that the min value is -15360 for newer firmware --jk */
-       if (((state->vendor == 0x471 && (state->product == 0x104 || state->product == 0x105 || state->product == 0x101)) ||
-            (state->vendor == 0x672 && state->product == 0x1041)) && !strcmp(kctl->id.name, "PCM Playback Volume") &&
-            cval->min == -15616) {
-               snd_printk("USB Audio: using volume control quirk for the UDA1321/N101 chip\n");
-               cval->max = -256;
+       switch (state->chip->usb_id) {
+       case USB_ID(0x0471, 0x0101):
+       case USB_ID(0x0471, 0x0104):
+       case USB_ID(0x0471, 0x0105):
+       case USB_ID(0x0672, 0x1041):
+               if (!strcmp(kctl->id.name, "PCM Playback Volume") &&
+                   cval->min == -15616) {
+                       snd_printk("using volume control quirk for the UDA1321/N101 chip\n");
+                       cval->max = -256;
+               }
        }
 
        snd_printdd(KERN_INFO "[%d] FU [%s] ch = %d, val = %d/%d/%d\n",
                    cval->id, kctl->id.name, cval->channels, cval->min, cval->max, cval->res);
-       add_control_to_empty(state->chip->card, kctl);
+       add_control_to_empty(state, kctl);
 }
 
 
@@ -947,8 +1001,7 @@ static void build_mixer_unit_ctl(mixer_build_t *state, unsigned char *desc,
        if (! cval)
                return;
 
-       cval->chip = state->chip;
-       cval->ctrlif = state->ctrlif;
+       cval->mixer = state->mixer;
        cval->id = unitid;
        cval->control = in_ch + 1; /* based on 1 */
        cval->val_type = USB_MIXER_S16;
@@ -979,7 +1032,7 @@ static void build_mixer_unit_ctl(mixer_build_t *state, unsigned char *desc,
 
        snd_printdd(KERN_INFO "[%d] MU [%s] ch = %d, val = %d/%d\n",
                    cval->id, kctl->id.name, cval->channels, cval->min, cval->max);
-       add_control_to_empty(state->chip->card, kctl);
+       add_control_to_empty(state, kctl);
 }
 
 
@@ -1042,7 +1095,7 @@ static int mixer_ctl_procunit_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
        int err, val;
 
        err = get_cur_ctl_value(cval, cval->control << 8, &val);
-       if (err < 0 && cval->chip->ignore_ctl_error) {
+       if (err < 0 && cval->mixer->ignore_ctl_error) {
                ucontrol->value.integer.value[0] = cval->min;
                return 0;
        }
@@ -1061,7 +1114,7 @@ static int mixer_ctl_procunit_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
 
        err = get_cur_ctl_value(cval, cval->control << 8, &oval);
        if (err < 0) {
-               if (cval->chip->ignore_ctl_error)
+               if (cval->mixer->ignore_ctl_error)
                        return 0;
                return err;
        }
@@ -1179,9 +1232,6 @@ static int build_audio_procunit(mixer_build_t *state, int unitid, unsigned char
        }
 
        type = combine_word(&dsc[4]);
-       if (! type)
-               return 0; /* undefined? */
-
        for (info = list; info && info->type; info++)
                if (info->type == type)
                        break;
@@ -1199,8 +1249,7 @@ static int build_audio_procunit(mixer_build_t *state, int unitid, unsigned char
                        snd_printk(KERN_ERR "cannot malloc kcontrol\n");
                        return -ENOMEM;
                }
-               cval->chip = state->chip;
-               cval->ctrlif = state->ctrlif;
+               cval->mixer = state->mixer;
                cval->id = unitid;
                cval->control = valinfo->control;
                cval->val_type = valinfo->val_type;
@@ -1241,7 +1290,7 @@ static int build_audio_procunit(mixer_build_t *state, int unitid, unsigned char
 
                snd_printdd(KERN_INFO "[%d] PU [%s] ch = %d, val = %d/%d\n",
                            cval->id, kctl->id.name, cval->channels, cval->min, cval->max);
-               if ((err = add_control_to_empty(state->chip->card, kctl)) < 0)
+               if ((err = add_control_to_empty(state, kctl)) < 0)
                        return err;
        }
        return 0;
@@ -1289,7 +1338,7 @@ static int mixer_ctl_selector_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
 
        err = get_cur_ctl_value(cval, 0, &val);
        if (err < 0) {
-               if (cval->chip->ignore_ctl_error) {
+               if (cval->mixer->ignore_ctl_error) {
                        ucontrol->value.enumerated.item[0] = 0;
                        return 0;
                }
@@ -1308,7 +1357,7 @@ static int mixer_ctl_selector_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t
 
        err = get_cur_ctl_value(cval, 0, &oval);
        if (err < 0) {
-               if (cval->chip->ignore_ctl_error)
+               if (cval->mixer->ignore_ctl_error)
                        return 0;
                return err;
        }
@@ -1386,8 +1435,7 @@ static int parse_audio_selector_unit(mixer_build_t *state, int unitid, unsigned
                snd_printk(KERN_ERR "cannot malloc kcontrol\n");
                return -ENOMEM;
        }
-       cval->chip = state->chip;
-       cval->ctrlif = state->ctrlif;
+       cval->mixer = state->mixer;
        cval->id = unitid;
        cval->val_type = USB_MIXER_U8;
        cval->channels = 1;
@@ -1415,7 +1463,9 @@ static int parse_audio_selector_unit(mixer_build_t *state, int unitid, unsigned
                        kfree(cval);
                        return -ENOMEM;
                }
-               if (check_input_term(state, desc[5 + i], &iterm) >= 0)
+               len = check_mapped_selector_name(state, unitid, i, namelist[i],
+                                                MAX_ITEM_NAME_LEN);
+               if (! len && check_input_term(state, desc[5 + i], &iterm) >= 0)
                        len = get_term_name(state, &iterm, namelist[i], MAX_ITEM_NAME_LEN, 0);
                if (! len)
                        sprintf(namelist[i], "Input %d", i);
@@ -1450,7 +1500,7 @@ static int parse_audio_selector_unit(mixer_build_t *state, int unitid, unsigned
 
        snd_printdd(KERN_INFO "[%d] SU [%s] items = %d\n",
                    cval->id, kctl->id.name, num_ins);
-       if ((err = add_control_to_empty(state->chip->card, kctl)) < 0)
+       if ((err = add_control_to_empty(state, kctl)) < 0)
                return err;
 
        return 0;
@@ -1493,41 +1543,55 @@ static int parse_audio_unit(mixer_build_t *state, int unitid)
        }
 }
 
+static void snd_usb_mixer_free(struct usb_mixer_interface *mixer)
+{
+       kfree(mixer->id_elems);
+       if (mixer->urb) {
+               kfree(mixer->urb->transfer_buffer);
+               usb_free_urb(mixer->urb);
+       }
+       if (mixer->rc_urb)
+               usb_free_urb(mixer->rc_urb);
+       kfree(mixer->rc_setup_packet);
+       kfree(mixer);
+}
+
+static int snd_usb_mixer_dev_free(snd_device_t *device)
+{
+       struct usb_mixer_interface *mixer = device->device_data;
+       snd_usb_mixer_free(mixer);
+       return 0;
+}
+
 /*
  * create mixer controls
  *
  * walk through all OUTPUT_TERMINAL descriptors to search for mixers
  */
-int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif)
+static int snd_usb_mixer_controls(struct usb_mixer_interface *mixer)
 {
        unsigned char *desc;
        mixer_build_t state;
        int err;
        const struct usbmix_ctl_map *map;
-       struct usb_device_descriptor *dev = &chip->dev->descriptor;
-       struct usb_host_interface *hostif = &usb_ifnum_to_if(chip->dev, ctrlif)->altsetting[0];
-
-       strcpy(chip->card->mixername, "USB Mixer");
+       struct usb_host_interface *hostif;
 
+       hostif = &usb_ifnum_to_if(mixer->chip->dev, mixer->ctrlif)->altsetting[0];
        memset(&state, 0, sizeof(state));
-       state.chip = chip;
+       state.chip = mixer->chip;
+       state.mixer = mixer;
        state.buffer = hostif->extra;
        state.buflen = hostif->extralen;
-       state.ctrlif = ctrlif;
-       state.vendor = le16_to_cpu(dev->idVendor);
-       state.product = le16_to_cpu(dev->idProduct);
 
        /* check the mapping table */
-       for (map = usbmix_ctl_maps; map->vendor; map++) {
-               if (map->vendor == state.vendor && map->product == state.product) {
+       for (map = usbmix_ctl_maps; map->id; map++) {
+               if (map->id == state.chip->usb_id) {
                        state.map = map->map;
-                       chip->ignore_ctl_error = map->ignore_ctl_error;
+                       state.selector_map = map->selector_map;
+                       mixer->ignore_ctl_error = map->ignore_ctl_error;
                        break;
                }
        }
-#ifdef IGNORE_CTL_ERROR
-       chip->ignore_ctl_error = 1;
-#endif
 
        desc = NULL;
        while ((desc = snd_usb_find_csint_desc(hostif->extra, hostif->extralen, desc, OUTPUT_TERMINAL)) != NULL) {
@@ -1543,3 +1607,393 @@ int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif)
        }
        return 0;
 }
+
+static void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer,
+                                   int unitid)
+{
+       usb_mixer_elem_info_t *info;
+
+       for (info = mixer->id_elems[unitid]; info; info = info->next_id_elem)
+               snd_ctl_notify(mixer->chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                              info->elem_id);
+}
+
+static void snd_usb_mixer_memory_change(struct usb_mixer_interface *mixer,
+                                       int unitid)
+{
+       if (mixer->rc_type == RC_NONE)
+               return;
+       /* unit ids specific to Extigy/Audigy 2 NX: */
+       switch (unitid) {
+       case 0: /* remote control */
+               mixer->rc_urb->dev = mixer->chip->dev;
+               usb_submit_urb(mixer->rc_urb, GFP_ATOMIC);
+               break;
+       case 4: /* digital in jack */
+       case 7: /* line in jacks */
+       case 19: /* speaker out jacks */
+       case 20: /* headphones out jack */
+               break;
+       default:
+               snd_printd(KERN_DEBUG "memory change in unknown unit %d\n", unitid);
+               break;
+       }
+}
+
+static void snd_usb_mixer_status_complete(struct urb *urb, struct pt_regs *regs)
+{
+       struct usb_mixer_interface *mixer = urb->context;
+
+       if (urb->status == 0) {
+               u8 *buf = urb->transfer_buffer;
+               int i;
+
+               for (i = urb->actual_length; i >= 2; buf += 2, i -= 2) {
+                       snd_printd(KERN_DEBUG "status interrupt: %02x %02x\n",
+                                  buf[0], buf[1]);
+                       /* ignore any notifications not from the control interface */
+                       if ((buf[0] & 0x0f) != 0)
+                               continue;
+                       if (!(buf[0] & 0x40))
+                               snd_usb_mixer_notify_id(mixer, buf[1]);
+                       else
+                               snd_usb_mixer_memory_change(mixer, buf[1]);
+               }
+       }
+       if (urb->status != -ENOENT && urb->status != -ECONNRESET) {
+               urb->dev = mixer->chip->dev;
+               usb_submit_urb(urb, GFP_ATOMIC);
+       }
+}
+
+/* create the handler for the optional status interrupt endpoint */
+static int snd_usb_mixer_status_create(struct usb_mixer_interface *mixer)
+{
+       struct usb_host_interface *hostif;
+       struct usb_endpoint_descriptor *ep;
+       void *transfer_buffer;
+       int buffer_length;
+       unsigned int epnum;
+
+       hostif = &usb_ifnum_to_if(mixer->chip->dev, mixer->ctrlif)->altsetting[0];
+       /* we need one interrupt input endpoint */
+       if (get_iface_desc(hostif)->bNumEndpoints < 1)
+               return 0;
+       ep = get_endpoint(hostif, 0);
+       if ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != USB_DIR_IN ||
+           (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT)
+               return 0;
+
+       epnum = ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+       buffer_length = le16_to_cpu(ep->wMaxPacketSize);
+       transfer_buffer = kmalloc(buffer_length, GFP_KERNEL);
+       if (!transfer_buffer)
+               return -ENOMEM;
+       mixer->urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (!mixer->urb) {
+               kfree(transfer_buffer);
+               return -ENOMEM;
+       }
+       usb_fill_int_urb(mixer->urb, mixer->chip->dev,
+                        usb_rcvintpipe(mixer->chip->dev, epnum),
+                        transfer_buffer, buffer_length,
+                        snd_usb_mixer_status_complete, mixer, ep->bInterval);
+       usb_submit_urb(mixer->urb, GFP_KERNEL);
+       return 0;
+}
+
+static void snd_usb_soundblaster_remote_complete(struct urb *urb,
+                                                struct pt_regs *regs)
+{
+       struct usb_mixer_interface *mixer = urb->context;
+       /*
+        * format of remote control data:
+        * Extigy:      xx 00
+        * Audigy 2 NX: 06 80 xx 00 00 00
+        */
+       int offset = mixer->rc_type == RC_EXTIGY ? 0 : 2;
+       u32 code;
+
+       if (urb->status < 0 || urb->actual_length <= offset)
+               return;
+       code = mixer->rc_buffer[offset];
+       /* the Mute button actually changes the mixer control */
+       if (code == 13)
+               snd_usb_mixer_notify_id(mixer, 18);
+       mixer->rc_code = code;
+       wmb();
+       wake_up(&mixer->rc_waitq);
+}
+
+static int snd_usb_sbrc_hwdep_open(snd_hwdep_t *hw, struct file *file)
+{
+       struct usb_mixer_interface *mixer = hw->private_data;
+
+       if (test_and_set_bit(0, &mixer->rc_hwdep_open))
+               return -EBUSY;
+       return 0;
+}
+
+static int snd_usb_sbrc_hwdep_release(snd_hwdep_t *hw, struct file *file)
+{
+       struct usb_mixer_interface *mixer = hw->private_data;
+
+       clear_bit(0, &mixer->rc_hwdep_open);
+       smp_mb__after_clear_bit();
+       return 0;
+}
+
+static long snd_usb_sbrc_hwdep_read(snd_hwdep_t *hw, char __user *buf,
+                                    long count, loff_t *offset)
+{
+       struct usb_mixer_interface *mixer = hw->private_data;
+       int err;
+       u32 rc_code;
+
+       if (count != 1 && count != 4)
+               return -EINVAL;
+       err = wait_event_interruptible(mixer->rc_waitq,
+                                      (rc_code = xchg(&mixer->rc_code, 0)) != 0);
+       if (err == 0) {
+               if (count == 1)
+                       err = put_user(rc_code, buf);
+               else
+                       err = put_user(rc_code, (u32 __user *)buf);
+       }
+       return err < 0 ? err : count;
+}
+
+static unsigned int snd_usb_sbrc_hwdep_poll(snd_hwdep_t *hw, struct file *file,
+                                           poll_table *wait)
+{
+       struct usb_mixer_interface *mixer = hw->private_data;
+
+       poll_wait(file, &mixer->rc_waitq, wait);
+       return mixer->rc_code ? POLLIN | POLLRDNORM : 0;
+}
+
+static int snd_usb_soundblaster_remote_init(struct usb_mixer_interface *mixer)
+{
+       snd_hwdep_t *hwdep;
+       int err, len;
+
+       switch (mixer->chip->usb_id) {
+       case USB_ID(0x041e, 0x3000):
+               mixer->rc_type = RC_EXTIGY;
+               len = 2;
+               break;
+       case USB_ID(0x041e, 0x3020):
+               mixer->rc_type = RC_AUDIGY2NX;
+               len = 6;
+               break;
+       default:
+               return 0;
+       }
+
+       init_waitqueue_head(&mixer->rc_waitq);
+       err = snd_hwdep_new(mixer->chip->card, "SB remote control", 0, &hwdep);
+       if (err < 0)
+               return err;
+       snprintf(hwdep->name, sizeof(hwdep->name),
+                "%s remote control", mixer->chip->card->shortname);
+       hwdep->iface = SNDRV_HWDEP_IFACE_SB_RC;
+       hwdep->private_data = mixer;
+       hwdep->ops.read = snd_usb_sbrc_hwdep_read;
+       hwdep->ops.open = snd_usb_sbrc_hwdep_open;
+       hwdep->ops.release = snd_usb_sbrc_hwdep_release;
+       hwdep->ops.poll = snd_usb_sbrc_hwdep_poll;
+
+       mixer->rc_urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (!mixer->rc_urb)
+               return -ENOMEM;
+       mixer->rc_setup_packet = kmalloc(sizeof(*mixer->rc_setup_packet), GFP_KERNEL);
+       if (!mixer->rc_setup_packet) {
+               usb_free_urb(mixer->rc_urb);
+               mixer->rc_urb = NULL;
+               return -ENOMEM;
+       }
+       mixer->rc_setup_packet->bRequestType =
+               USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+       mixer->rc_setup_packet->bRequest = GET_MEM;
+       mixer->rc_setup_packet->wValue = cpu_to_le16(0);
+       mixer->rc_setup_packet->wIndex = cpu_to_le16(0);
+       mixer->rc_setup_packet->wLength = cpu_to_le16(len);
+       usb_fill_control_urb(mixer->rc_urb, mixer->chip->dev,
+                            usb_rcvctrlpipe(mixer->chip->dev, 0),
+                            (u8*)mixer->rc_setup_packet, mixer->rc_buffer, len,
+                            snd_usb_soundblaster_remote_complete, mixer);
+       return 0;
+}
+
+static int snd_audigy2nx_led_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *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 snd_audigy2nx_led_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+       int index = kcontrol->private_value;
+
+       ucontrol->value.integer.value[0] = mixer->audigy2nx_leds[index];
+       return 0;
+}
+
+static int snd_audigy2nx_led_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+       struct usb_mixer_interface *mixer = snd_kcontrol_chip(kcontrol);
+       int index = kcontrol->private_value;
+       int value = ucontrol->value.integer.value[0];
+       int err, changed;
+
+       if (value > 1)
+               return -EINVAL;
+       changed = value != mixer->audigy2nx_leds[index];
+       err = snd_usb_ctl_msg(mixer->chip->dev,
+                             usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
+                             USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+                             value, index + 2, NULL, 0, 100);
+       if (err < 0)
+               return err;
+       mixer->audigy2nx_leds[index] = value;
+       return changed;
+}
+
+static snd_kcontrol_new_t snd_audigy2nx_controls[] = {
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "CMSS LED Switch",
+               .info = snd_audigy2nx_led_info,
+               .get = snd_audigy2nx_led_get,
+               .put = snd_audigy2nx_led_put,
+               .private_value = 0,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Power LED Switch",
+               .info = snd_audigy2nx_led_info,
+               .get = snd_audigy2nx_led_get,
+               .put = snd_audigy2nx_led_put,
+               .private_value = 1,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Dolby Digital LED Switch",
+               .info = snd_audigy2nx_led_info,
+               .get = snd_audigy2nx_led_get,
+               .put = snd_audigy2nx_led_put,
+               .private_value = 2,
+       },
+};
+
+static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer)
+{
+       int i, err;
+
+       for (i = 0; i < ARRAY_SIZE(snd_audigy2nx_controls); ++i) {
+               err = snd_ctl_add(mixer->chip->card,
+                                 snd_ctl_new1(&snd_audigy2nx_controls[i], mixer));
+               if (err < 0)
+                       return err;
+       }
+       mixer->audigy2nx_leds[1] = 1; /* Power LED is on by default */
+       return 0;
+}
+
+static void snd_audigy2nx_proc_read(snd_info_entry_t *entry,
+                                   snd_info_buffer_t *buffer)
+{
+       static const struct {
+               int unitid;
+               const char *name;
+       } jacks[] = {
+               {4,  "dig in "},
+               {7,  "line in"},
+               {19, "spk out"},
+               {20, "hph out"},
+       };
+       struct usb_mixer_interface *mixer = entry->private_data;
+       int i, err;
+       u8 buf[3];
+
+       snd_iprintf(buffer, "%s jacks\n\n", mixer->chip->card->shortname);
+       for (i = 0; i < ARRAY_SIZE(jacks); ++i) {
+               snd_iprintf(buffer, "%s: ", jacks[i].name);
+               err = snd_usb_ctl_msg(mixer->chip->dev,
+                                     usb_rcvctrlpipe(mixer->chip->dev, 0),
+                                     GET_MEM, USB_DIR_IN | USB_TYPE_CLASS |
+                                     USB_RECIP_INTERFACE, 0,
+                                     jacks[i].unitid << 8, buf, 3, 100);
+               if (err == 3 && buf[0] == 3)
+                       snd_iprintf(buffer, "%02x %02x\n", buf[1], buf[2]);
+               else
+                       snd_iprintf(buffer, "?\n");
+       }
+}
+
+int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif)
+{
+       static snd_device_ops_t dev_ops = {
+               .dev_free = snd_usb_mixer_dev_free
+       };
+       struct usb_mixer_interface *mixer;
+       int err;
+
+       strcpy(chip->card->mixername, "USB Mixer");
+
+       mixer = kcalloc(1, sizeof(*mixer), GFP_KERNEL);
+       if (!mixer)
+               return -ENOMEM;
+       mixer->chip = chip;
+       mixer->ctrlif = ctrlif;
+#ifdef IGNORE_CTL_ERROR
+       mixer->ignore_ctl_error = 1;
+#endif
+       mixer->id_elems = kcalloc(256, sizeof(*mixer->id_elems), GFP_KERNEL);
+       if (!mixer->id_elems) {
+               kfree(mixer);
+               return -ENOMEM;
+       }
+
+       if ((err = snd_usb_mixer_controls(mixer)) < 0 ||
+           (err = snd_usb_mixer_status_create(mixer)) < 0)
+               goto _error;
+
+       if ((err = snd_usb_soundblaster_remote_init(mixer)) < 0)
+               goto _error;
+
+       if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020)) {
+               snd_info_entry_t *entry;
+
+               if ((err = snd_audigy2nx_controls_create(mixer)) < 0)
+                       goto _error;
+               if (!snd_card_proc_new(chip->card, "audigy2nx", &entry))
+                       snd_info_set_text_ops(entry, mixer, 1024,
+                                             snd_audigy2nx_proc_read);
+       }
+
+       err = snd_device_new(chip->card, SNDRV_DEV_LOWLEVEL, mixer, &dev_ops);
+       if (err < 0)
+               goto _error;
+       list_add(&mixer->list, &chip->mixer_list);
+       return 0;
+
+_error:
+       snd_usb_mixer_free(mixer);
+       return err;
+}
+
+void snd_usb_mixer_disconnect(struct list_head *p)
+{
+       struct usb_mixer_interface *mixer;
+       
+       mixer = list_entry(p, struct usb_mixer_interface, list);
+       if (mixer->urb)
+               usb_kill_urb(mixer->urb);
+       if (mixer->rc_urb)
+               usb_kill_urb(mixer->rc_urb);
+}
index c69b4b0875f8724aed7f13341f4477e98fa180d8..f05500b05ec0a70c0dbc84306dbf44e18094723d 100644 (file)
@@ -26,10 +26,16 @@ struct usbmix_name_map {
        int control;
 };
 
+struct usbmix_selector_map {
+       int id;
+       int count;
+       const char **names;
+};
+
 struct usbmix_ctl_map {
-       int vendor;
-       int product;
+       u32 id;
        const struct usbmix_name_map *map;
+       const struct usbmix_selector_map *selector_map;
        int ignore_ctl_error;
 };
 
@@ -91,6 +97,96 @@ static struct usbmix_name_map extigy_map[] = {
        { 0 } /* terminator */
 };
 
+/* Sound Blaster MP3+ controls mapping
+ * The default mixer channels have totally misleading names,
+ * e.g. no Master and fake PCM volume
+ *                     Pavel Mihaylov <bin@bash.info>
+ */
+static struct usbmix_name_map mp3plus_map[] = {
+       /* 1: IT pcm */
+       /* 2: IT mic */
+       /* 3: IT line */
+       /* 4: IT digital in */
+       /* 5: OT digital out */
+       /* 6: OT speaker */
+       /* 7: OT pcm capture */
+       { 8, "Capture Input Source" }, /* FU, default PCM Capture Source */
+               /* (Mic, Input 1 = Line input, Input 2 = Optical input) */
+       { 9, "Master Playback" }, /* FU, default Speaker 1 */
+       /* { 10, "Mic Capture", 1 }, */ /* FU, Mic Capture */
+       /* { 10, "Mic Capture", 2 }, */ /* FU, Mic Capture */
+       { 10, "Mic Boost", 7 }, /* FU, default Auto Gain Input */
+       { 11, "Line Capture" }, /* FU, default PCM Capture */
+       { 12, "Digital In Playback" }, /* FU, default PCM 1 */
+       /* { 13, "Mic Playback" }, */ /* FU, default Mic Playback */
+       { 14, "Line Playback" }, /* FU, default Speaker */
+       /* 15: MU */
+       { 0 } /* terminator */
+};
+
+/* Topology of SB Audigy 2 NX
+
+          +----------------------------->EU[27]--+
+          |                                      v
+          | +----------------------------------->SU[29]---->FU[22]-->Dig_OUT[24]
+          | |                                    ^
+USB_IN[1]-+------------+              +->EU[17]->+->FU[11]-+
+            |          v              |          v         |
+Dig_IN[4]---+->FU[6]-->MU[16]->FU[18]-+->EU[21]->SU[31]----->FU[30]->Hph_OUT[20]
+            |          ^              |                    |
+Lin_IN[7]-+--->FU[8]---+              +->EU[23]->FU[28]------------->Spk_OUT[19]
+          | |                                              v
+          +--->FU[12]------------------------------------->SU[14]--->USB_OUT[15]
+            |                                              ^
+            +->FU[13]--------------------------------------+
+*/
+static struct usbmix_name_map audigy2nx_map[] = {
+       /* 1: IT pcm playback */
+       /* 4: IT digital in */
+       { 6, "Digital In Playback" }, /* FU */
+       /* 7: IT line in */
+       { 8, "Line Playback" }, /* FU */
+       { 11, "What-U-Hear Capture" }, /* FU */
+       { 12, "Line Capture" }, /* FU */
+       { 13, "Digital In Capture" }, /* FU */
+       { 14, "Capture Source" }, /* SU */
+       /* 15: OT pcm capture */
+       /* 16: MU w/o controls */
+       { 17, NULL }, /* DISABLED: EU (for what?) */
+       { 18, "Master Playback" }, /* FU */
+       /* 19: OT speaker */
+       /* 20: OT headphone */
+       { 21, NULL }, /* DISABLED: EU (for what?) */
+       { 22, "Digital Out Playback" }, /* FU */
+       { 23, NULL }, /* DISABLED: EU (for what?) */
+       /* 24: OT digital out */
+       { 27, NULL }, /* DISABLED: EU (for what?) */
+       { 28, "Speaker Playback" }, /* FU */
+       { 29, "Digital Out Source" }, /* SU */
+       { 30, "Headphone Playback" }, /* FU */
+       { 31, "Headphone Source" }, /* SU */
+       { 0 } /* terminator */
+};
+
+static struct usbmix_selector_map audigy2nx_selectors[] = {
+       {
+               .id = 14, /* Capture Source */
+               .count = 3,
+               .names = (const char*[]) {"Line", "Digital In", "What-U-Hear"}
+       },
+       {
+               .id = 29, /* Digital Out Source */
+               .count = 3,
+               .names = (const char*[]) {"Front", "PCM", "Digital In"}
+       },
+       {
+               .id = 31, /* Headphone Source */
+               .count = 2,
+               .names = (const char*[]) {"Front", "Side"}
+       },
+       { 0 } /* terminator */
+};
+
 /* LineX FM Transmitter entry - needed to bypass controls bug */
 static struct usbmix_name_map linex_map[] = {
        /* 1: IT pcm */
@@ -127,9 +223,29 @@ static struct usbmix_name_map justlink_map[] = {
  */
 
 static struct usbmix_ctl_map usbmix_ctl_maps[] = {
-       { 0x41e, 0x3000, extigy_map, 1 },
-       { 0x8bb, 0x2702, linex_map, 1 },
-       { 0xc45, 0x1158, justlink_map, 0 },
+       {
+               .id = USB_ID(0x041e, 0x3000),
+               .map = extigy_map,
+               .ignore_ctl_error = 1,
+       },
+       {
+               .id = USB_ID(0x041e, 0x3010),
+               .map = mp3plus_map,
+       },
+       {
+               .id = USB_ID(0x041e, 0x3020),
+               .map = audigy2nx_map,
+               .selector_map = audigy2nx_selectors,
+       },
+       {
+               .id = USB_ID(0x08bb, 0x2702),
+               .map = linex_map,
+               .ignore_ctl_error = 1,
+       },
+       {
+               .id = USB_ID(0x0c45, 0x1158),
+               .map = justlink_map,
+       },
        { 0 } /* terminator */
 };
 
index 88bbd944d4be997b1a672b3f504e15568b983855..f5135641b3e2e50a283630b7acc7a7fd52c381e0 100644 (file)
@@ -203,11 +203,28 @@ YAMAHA_DEVICE(0x7010, "UB99"),
        .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
                .vendor_name = "EDIROL",
                .product_name = "UM-4",
-               .ifnum = 2,
-               .type = QUIRK_MIDI_FIXED_ENDPOINT,
-               .data = & (const snd_usb_midi_endpoint_info_t) {
-                       .out_cables = 0x000f,
-                       .in_cables  = 0x000f
+               .ifnum = QUIRK_ANY_INTERFACE,
+               .type = QUIRK_COMPOSITE,
+               .data = (const snd_usb_audio_quirk_t[]) {
+                       {
+                               .ifnum = 0,
+                               .type = QUIRK_IGNORE_INTERFACE
+                       },
+                       {
+                               .ifnum = 1,
+                               .type = QUIRK_IGNORE_INTERFACE
+                       },
+                       {
+                               .ifnum = 2,
+                               .type = QUIRK_MIDI_FIXED_ENDPOINT,
+                               .data = & (const snd_usb_midi_endpoint_info_t) {
+                                       .out_cables = 0x000f,
+                                       .in_cables  = 0x000f
+                               }
+                       },
+                       {
+                               .ifnum = -1
+                       }
                }
        }
 },
@@ -216,11 +233,28 @@ YAMAHA_DEVICE(0x7010, "UB99"),
        .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
                .vendor_name = "Roland",
                .product_name = "SC-8850",
-               .ifnum = 2,
-               .type = QUIRK_MIDI_FIXED_ENDPOINT,
-               .data = & (const snd_usb_midi_endpoint_info_t) {
-                       .out_cables = 0x003f,
-                       .in_cables  = 0x003f
+               .ifnum = QUIRK_ANY_INTERFACE,
+               .type = QUIRK_COMPOSITE,
+               .data = (const snd_usb_audio_quirk_t[]) {
+                       {
+                               .ifnum = 0,
+                               .type = QUIRK_IGNORE_INTERFACE
+                       },
+                       {
+                               .ifnum = 1,
+                               .type = QUIRK_IGNORE_INTERFACE
+                       },
+                       {
+                               .ifnum = 2,
+                               .type = QUIRK_MIDI_FIXED_ENDPOINT,
+                               .data = & (const snd_usb_midi_endpoint_info_t) {
+                                       .out_cables = 0x003f,
+                                       .in_cables  = 0x003f
+                               }
+                       },
+                       {
+                               .ifnum = -1
+                       }
                }
        }
 },
@@ -229,11 +263,28 @@ YAMAHA_DEVICE(0x7010, "UB99"),
        .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
                .vendor_name = "Roland",
                .product_name = "U-8",
-               .ifnum = 2,
-               .type = QUIRK_MIDI_FIXED_ENDPOINT,
-               .data = & (const snd_usb_midi_endpoint_info_t) {
-                       .out_cables = 0x0005,
-                       .in_cables  = 0x0005
+               .ifnum = QUIRK_ANY_INTERFACE,
+               .type = QUIRK_COMPOSITE,
+               .data = (const snd_usb_audio_quirk_t[]) {
+                       {
+                               .ifnum = 0,
+                               .type = QUIRK_IGNORE_INTERFACE
+                       },
+                       {
+                               .ifnum = 1,
+                               .type = QUIRK_IGNORE_INTERFACE
+                       },
+                       {
+                               .ifnum = 2,
+                               .type = QUIRK_MIDI_FIXED_ENDPOINT,
+                               .data = & (const snd_usb_midi_endpoint_info_t) {
+                                       .out_cables = 0x0005,
+                                       .in_cables  = 0x0005
+                               }
+                       },
+                       {
+                               .ifnum = -1
+                       }
                }
        }
 },
@@ -242,11 +293,28 @@ YAMAHA_DEVICE(0x7010, "UB99"),
        .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
                .vendor_name = "EDIROL",
                .product_name = "UM-2",
-               .ifnum = 2,
-               .type = QUIRK_MIDI_FIXED_ENDPOINT,
-               .data = & (const snd_usb_midi_endpoint_info_t) {
-                       .out_cables = 0x0003,
-                       .in_cables  = 0x0003
+               .ifnum = QUIRK_ANY_INTERFACE,
+               .type = QUIRK_COMPOSITE,
+               .data = (const snd_usb_audio_quirk_t[]) {
+                       {
+                               .ifnum = 0,
+                               .type = QUIRK_IGNORE_INTERFACE
+                       },
+                       {
+                               .ifnum = 1,
+                               .type = QUIRK_IGNORE_INTERFACE
+                       },
+                       {
+                               .ifnum = 2,
+                               .type = QUIRK_MIDI_FIXED_ENDPOINT,
+                               .data = & (const snd_usb_midi_endpoint_info_t) {
+                                       .out_cables = 0x0003,
+                                       .in_cables  = 0x0003
+                               }
+                       },
+                       {
+                               .ifnum = -1
+                       }
                }
        }
 },
@@ -255,11 +323,28 @@ YAMAHA_DEVICE(0x7010, "UB99"),
        .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
                .vendor_name = "Roland",
                .product_name = "SC-8820",
-               .ifnum = 2,
-               .type = QUIRK_MIDI_FIXED_ENDPOINT,
-               .data = & (const snd_usb_midi_endpoint_info_t) {
-                       .out_cables = 0x0013,
-                       .in_cables  = 0x0013
+               .ifnum = QUIRK_ANY_INTERFACE,
+               .type = QUIRK_COMPOSITE,
+               .data = (const snd_usb_audio_quirk_t[]) {
+                       {
+                               .ifnum = 0,
+                               .type = QUIRK_IGNORE_INTERFACE
+                       },
+                       {
+                               .ifnum = 1,
+                               .type = QUIRK_IGNORE_INTERFACE
+                       },
+                       {
+                               .ifnum = 2,
+                               .type = QUIRK_MIDI_FIXED_ENDPOINT,
+                               .data = & (const snd_usb_midi_endpoint_info_t) {
+                                       .out_cables = 0x0013,
+                                       .in_cables  = 0x0013
+                               }
+                       },
+                       {
+                               .ifnum = -1
+                       }
                }
        }
 },
@@ -268,11 +353,28 @@ YAMAHA_DEVICE(0x7010, "UB99"),
        .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
                .vendor_name = "Roland",
                .product_name = "PC-300",
-               .ifnum = 2,
-               .type = QUIRK_MIDI_FIXED_ENDPOINT,
-               .data = & (const snd_usb_midi_endpoint_info_t) {
-                       .out_cables = 0x0001,
-                       .in_cables  = 0x0001
+               .ifnum = QUIRK_ANY_INTERFACE,
+               .type = QUIRK_COMPOSITE,
+               .data = (const snd_usb_audio_quirk_t[]) {
+                       {
+                               .ifnum = 0,
+                               .type = QUIRK_IGNORE_INTERFACE
+                       },
+                       {
+                               .ifnum = 1,
+                               .type = QUIRK_IGNORE_INTERFACE
+                       },
+                       {
+                               .ifnum = 2,
+                               .type = QUIRK_MIDI_FIXED_ENDPOINT,
+                               .data = & (const snd_usb_midi_endpoint_info_t) {
+                                       .out_cables = 0x0001,
+                                       .in_cables  = 0x0001
+                               }
+                       },
+                       {
+                               .ifnum = -1
+                       }
                }
        }
 },
@@ -281,11 +383,28 @@ YAMAHA_DEVICE(0x7010, "UB99"),
        .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
                .vendor_name = "EDIROL",
                .product_name = "UM-1",
-               .ifnum = 2,
-               .type = QUIRK_MIDI_FIXED_ENDPOINT,
-               .data = & (const snd_usb_midi_endpoint_info_t) {
-                       .out_cables = 0x0001,
-                       .in_cables  = 0x0001
+               .ifnum = QUIRK_ANY_INTERFACE,
+               .type = QUIRK_COMPOSITE,
+               .data = (const snd_usb_audio_quirk_t[]) {
+                       {
+                               .ifnum = 0,
+                               .type = QUIRK_IGNORE_INTERFACE
+                       },
+                       {
+                               .ifnum = 1,
+                               .type = QUIRK_IGNORE_INTERFACE
+                       },
+                       {
+                               .ifnum = 2,
+                               .type = QUIRK_MIDI_FIXED_ENDPOINT,
+                               .data = & (const snd_usb_midi_endpoint_info_t) {
+                                       .out_cables = 0x0001,
+                                       .in_cables  = 0x0001
+                               }
+                       },
+                       {
+                               .ifnum = -1
+                       }
                }
        }
 },
@@ -294,11 +413,28 @@ YAMAHA_DEVICE(0x7010, "UB99"),
        .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
                .vendor_name = "Roland",
                .product_name = "SK-500",
-               .ifnum = 2,
-               .type = QUIRK_MIDI_FIXED_ENDPOINT,
-               .data = & (const snd_usb_midi_endpoint_info_t) {
-                       .out_cables = 0x0013,
-                       .in_cables  = 0x0013
+               .ifnum = QUIRK_ANY_INTERFACE,
+               .type = QUIRK_COMPOSITE,
+               .data = (const snd_usb_audio_quirk_t[]) {
+                       {
+                               .ifnum = 0,
+                               .type = QUIRK_IGNORE_INTERFACE
+                       },
+                       {
+                               .ifnum = 1,
+                               .type = QUIRK_IGNORE_INTERFACE
+                       },
+                       {
+                               .ifnum = 2,
+                               .type = QUIRK_MIDI_FIXED_ENDPOINT,
+                               .data = & (const snd_usb_midi_endpoint_info_t) {
+                                       .out_cables = 0x0013,
+                                       .in_cables  = 0x0013
+                               }
+                       },
+                       {
+                               .ifnum = -1
+                       }
                }
        }
 },
@@ -421,11 +557,28 @@ YAMAHA_DEVICE(0x7010, "UB99"),
        .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
                .vendor_name = "EDIROL",
                .product_name = "SD-90",
-               .ifnum = 2,
-               .type = QUIRK_MIDI_FIXED_ENDPOINT,
-               .data = & (const snd_usb_midi_endpoint_info_t) {
-                       .out_cables = 0x000f,
-                       .in_cables  = 0x000f
+               .ifnum = QUIRK_ANY_INTERFACE,
+               .type = QUIRK_COMPOSITE,
+               .data = (const snd_usb_audio_quirk_t[]) {
+                       {
+                               .ifnum = 0,
+                               .type = QUIRK_IGNORE_INTERFACE
+                       },
+                       {
+                               .ifnum = 1,
+                               .type = QUIRK_IGNORE_INTERFACE
+                       },
+                       {
+                               .ifnum = 2,
+                               .type = QUIRK_MIDI_FIXED_ENDPOINT,
+                               .data = & (const snd_usb_midi_endpoint_info_t) {
+                                       .out_cables = 0x000f,
+                                       .in_cables  = 0x000f
+                               }
+                       },
+                       {
+                               .ifnum = -1
+                       }
                }
        }
 },
@@ -434,11 +587,28 @@ YAMAHA_DEVICE(0x7010, "UB99"),
        .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
                .vendor_name = "Roland",
                .product_name = "MMP-2",
-               .ifnum = 2,
-               .type = QUIRK_MIDI_FIXED_ENDPOINT,
-               .data = & (const snd_usb_midi_endpoint_info_t) {
-                       .out_cables = 0x0001,
-                       .in_cables  = 0x0001
+               .ifnum = QUIRK_ANY_INTERFACE,
+               .type = QUIRK_COMPOSITE,
+               .data = (const snd_usb_audio_quirk_t[]) {
+                       {
+                               .ifnum = 0,
+                               .type = QUIRK_IGNORE_INTERFACE
+                       },
+                       {
+                               .ifnum = 1,
+                               .type = QUIRK_IGNORE_INTERFACE
+                       },
+                       {
+                               .ifnum = 2,
+                               .type = QUIRK_MIDI_FIXED_ENDPOINT,
+                               .data = & (const snd_usb_midi_endpoint_info_t) {
+                                       .out_cables = 0x0001,
+                                       .in_cables  = 0x0001
+                               }
+                       },
+                       {
+                               .ifnum = -1
+                       }
                }
        }
 },
@@ -609,15 +779,33 @@ YAMAHA_DEVICE(0x7010, "UB99"),
        }
 },
 {
+       /*
+        * This quirk is for the "Advanced Driver" mode.  If off, the GS-10
+        * has ID 0x003c and is standard compliant, but has only 16-bit PCM
+        * and no MIDI.
+        */
        USB_DEVICE_VENDOR_SPEC(0x0582, 0x003b),
        .driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
                .vendor_name = "BOSS",
                .product_name = "GS-10",
-               .ifnum = 3,
-               .type = QUIRK_MIDI_FIXED_ENDPOINT,
-               .data = & (const snd_usb_midi_endpoint_info_t) {
-                       .out_cables = 0x0003,
-                       .in_cables  = 0x0003
+               .ifnum = QUIRK_ANY_INTERFACE,
+               .type = QUIRK_COMPOSITE,
+               .data = & (const snd_usb_audio_quirk_t[]) {
+                       {
+                               .ifnum = 1,
+                               .type = QUIRK_AUDIO_STANDARD_INTERFACE
+                       },
+                       {
+                               .ifnum = 2,
+                               .type = QUIRK_AUDIO_STANDARD_INTERFACE
+                       },
+                       {
+                               .ifnum = 3,
+                               .type = QUIRK_MIDI_STANDARD_INTERFACE
+                       },
+                       {
+                               .ifnum = -1
+                       }
                }
        }
 },
index 89ee8b7320134fdf97b02a03f9e30457d32099f3..e6e6da1596712633b0383293fd3203a066b37d0b 100644 (file)
@@ -442,7 +442,7 @@ static void usX2Y_usb_disconnect(struct usb_device* device, void* ptr)
                snd_card_disconnect((snd_card_t*)ptr);
                /* release the midi resources */
                list_for_each(p, &usX2Y->chip.midi_list) {
-                       snd_usbmidi_disconnect(p, &snd_usX2Y_usb_driver);
+                       snd_usbmidi_disconnect(p);
                }
                if (usX2Y->us428ctls_sharedmem) 
                        wake_up(&usX2Y->us428ctls_wait_queue_head);
index 4c292e0900699e3e33a64416a57b4fda4351b785..62dfd28b3b07f909ec66bfebca5a9c1de614a9ac 100644 (file)
@@ -401,10 +401,8 @@ static void usX2Y_urbs_release(snd_usX2Y_substream_t *subs)
        for (i = 0; i < NRURBS; i++)
                usX2Y_urb_release(subs->urb + i, subs != subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK]);
 
-       if (subs->tmpbuf) {
-               kfree(subs->tmpbuf);
-               subs->tmpbuf = NULL;
-       }
+       kfree(subs->tmpbuf);
+       subs->tmpbuf = NULL;
 }
 /*
  * initialize a substream's urbs