r2398 - branches/src/target/kernel/2.6.22.x/patches
shoragan at sita.openmoko.org
shoragan at sita.openmoko.org
Thu Jul 26 17:24:39 CEST 2007
Author: shoragan
Date: 2007-07-26 17:24:28 +0200 (Thu, 26 Jul 2007)
New Revision: 2398
Added:
branches/src/target/kernel/2.6.22.x/patches/alsa-2.6.23-rc1-commit.diff
branches/src/target/kernel/2.6.22.x/patches/asoc-kconfig-fix.patch
branches/src/target/kernel/2.6.22.x/patches/s3c2410_udc_from_upstream.patch
Removed:
branches/src/target/kernel/2.6.22.x/patches/asoc-asm_hardware_h.patch
branches/src/target/kernel/2.6.22.x/patches/asoc.patch
branches/src/target/kernel/2.6.22.x/patches/s3c2410-usb-switch.patch
branches/src/target/kernel/2.6.22.x/patches/s3c2410_udc-vbus_draw_pdata.patch
branches/src/target/kernel/2.6.22.x/patches/s3c2410_udc.patch
branches/src/target/kernel/2.6.22.x/patches/series.old
Modified:
branches/src/target/kernel/2.6.22.x/patches/gta01-core.patch
branches/src/target/kernel/2.6.22.x/patches/gta01-no_nand_partitions.patch
branches/src/target/kernel/2.6.22.x/patches/gta01-pcf50606.patch
branches/src/target/kernel/2.6.22.x/patches/gta02-core.patch
branches/src/target/kernel/2.6.22.x/patches/hxd8-core.patch
branches/src/target/kernel/2.6.22.x/patches/qt2410-s3c_mci-pdata.patch
branches/src/target/kernel/2.6.22.x/patches/s3c_mci.patch
branches/src/target/kernel/2.6.22.x/patches/s3c_mci_platform.patch
branches/src/target/kernel/2.6.22.x/patches/s3cmci-dma-free.patch
branches/src/target/kernel/2.6.22.x/patches/s3cmci-stop-fix.patch
branches/src/target/kernel/2.6.22.x/patches/s3cmci_dbg.patch
branches/src/target/kernel/2.6.22.x/patches/series
branches/src/target/kernel/2.6.22.x/patches/smedia-glamo.patch
Log:
Port patches to 2.6.22.1
Added: branches/src/target/kernel/2.6.22.x/patches/alsa-2.6.23-rc1-commit.diff
===================================================================
--- branches/src/target/kernel/2.6.22.x/patches/alsa-2.6.23-rc1-commit.diff 2007-07-26 14:40:11 UTC (rev 2397)
+++ branches/src/target/kernel/2.6.22.x/patches/alsa-2.6.23-rc1-commit.diff 2007-07-26 15:24:28 UTC (rev 2398)
@@ -0,0 +1,10960 @@
+--- linux-2.6.22.1.orig/CREDITS
++++ linux-2.6.22.1/CREDITS
+@@ -2212,13 +2212,13 @@
+ S: Denmark
+
+ N: Claudio S. Matsuoka
+-E: claudio at conectiva.com
+-E: claudio at helllabs.org
++E: cmatsuoka at gmail.com
++E: claudio at mandriva.com
+ W: http://helllabs.org/~claudio
+-D: V4L, OV511 driver hacks
++D: V4L, OV511 and HDA-codec hacks
+ S: Conectiva S.A.
+-S: R. Tocantins 89
+-S: 80050-430 Curitiba PR
++S: Souza Naves 1250
++S: 80050-040 Curitiba PR
+ S: Brazil
+
+ N: Heinz Mauelshagen
+--- linux-2.6.22.1.orig/Documentation/sound/alsa/ALSA-Configuration.txt
++++ linux-2.6.22.1/Documentation/sound/alsa/ALSA-Configuration.txt
+@@ -467,7 +467,12 @@
+ above explicitly.
+
+ The power-management is supported.
+-
++
++ Module snd-cs5530
++ _________________
++
++ Module for Cyrix/NatSemi Geode 5530 chip.
++
+ Module snd-cs5535audio
+ ----------------------
+
+@@ -759,6 +764,7 @@
+
+ model - force the model name
+ position_fix - Fix DMA pointer (0 = auto, 1 = none, 2 = POSBUF, 3 = FIFO size)
++ probe_mask - Bitmask to probe codecs (default = -1, meaning all slots)
+ single_cmd - Use single immediate commands to communicate with
+ codecs (for debugging only)
+ enable_msi - Enable Message Signaled Interrupt (MSI) (default = off)
+@@ -803,6 +809,8 @@
+ hp-3013 HP machines (3013-variant)
+ fujitsu Fujitsu S7020
+ acer Acer TravelMate
++ will Will laptops (PB V7900)
++ replacer Replacer 672V
+ basic fixed pin assignment (old default model)
+ auto auto-config reading BIOS (default)
+
+@@ -811,16 +819,31 @@
+ hp-bpc HP xw4400/6400/8400/9400 laptops
+ hp-bpc-d7000 HP BPC D7000
+ benq Benq ED8
++ benq-t31 Benq T31
+ hippo Hippo (ATI) with jack detection, Sony UX-90s
+ hippo_1 Hippo (Benq) with jack detection
++ sony-assamd Sony ASSAMD
+ basic fixed pin assignment w/o SPDIF
+ auto auto-config reading BIOS (default)
+
++ ALC268
++ 3stack 3-stack model
++ auto auto-config reading BIOS (default)
++
++ ALC662
++ 3stack-dig 3-stack (2-channel) with SPDIF
++ 3stack-6ch 3-stack (6-channel)
++ 3stack-6ch-dig 3-stack (6-channel) with SPDIF
++ 6stack-dig 6-stack with SPDIF
++ lenovo-101e Lenovo laptop
++ auto auto-config reading BIOS (default)
++
+ ALC882/885
+ 3stack-dig 3-jack with SPDIF I/O
+ 6stack-dig 6-jack digital with SPDIF I/O
+ arima Arima W820Di1
+ macpro MacPro support
++ imac24 iMac 24'' with jack detection
+ w2jc ASUS W2JC
+ auto auto-config reading BIOS (default)
+
+@@ -832,9 +855,15 @@
+ 6stack-dig-demo 6-jack digital for Intel demo board
+ acer Acer laptops (Travelmate 3012WTMi, Aspire 5600, etc)
+ medion Medion Laptops
++ medion-md2 Medion MD2
+ targa-dig Targa/MSI
+ targa-2ch-dig Targs/MSI with 2-channel
+ laptop-eapd 3-jack with SPDIF I/O and EAPD (Clevo M540JE, M550JE)
++ lenovo-101e Lenovo 101E
++ lenovo-nb0763 Lenovo NB0763
++ lenovo-ms7195-dig Lenovo MS7195
++ 6stack-hp HP machines with 6stack (Nettle boards)
++ 3stack-hp HP machines with 3stack (Lucknow, Samba boards)
+ auto auto-config reading BIOS (default)
+
+ ALC861/660
+@@ -853,7 +882,9 @@
+ 3stack-dig 3-jack with SPDIF OUT
+ 6stack-dig 6-jack with SPDIF OUT
+ 3stack-660 3-jack (for ALC660VD)
++ 3stack-660-digout 3-jack with SPDIF OUT (for ALC660VD)
+ lenovo Lenovo 3000 C200
++ dallas Dallas laptops
+ auto auto-config reading BIOS (default)
+
+ CMI9880
+@@ -864,12 +895,26 @@
+ allout 5-jack in back, 2-jack in front, SPDIF out
+ auto auto-config reading BIOS (default)
+
++ AD1882
++ 3stack 3-stack mode (default)
++ 6stack 6-stack mode
++
++ AD1884
++ N/A
++
+ AD1981
+ basic 3-jack (default)
+ hp HP nx6320
+ thinkpad Lenovo Thinkpad T60/X60/Z60
+ toshiba Toshiba U205
+
++ AD1983
++ N/A
++
++ AD1984
++ basic default configuration
++ thinkpad Lenovo Thinkpad T61/X61
++
+ AD1986A
+ 6stack 6-jack, separate surrounds (default)
+ 3stack 3-stack, shared surrounds
+@@ -907,11 +952,18 @@
+ ref Reference board
+ 3stack D945 3stack
+ 5stack D945 5stack + SPDIF
+- macmini Intel Mac Mini
+- macbook Intel Mac Book
+- macbook-pro-v1 Intel Mac Book Pro 1st generation
+- macbook-pro Intel Mac Book Pro 2nd generation
+- imac-intel Intel iMac
++ dell Dell XPS M1210
++ intel-mac-v1 Intel Mac Type 1
++ intel-mac-v2 Intel Mac Type 2
++ intel-mac-v3 Intel Mac Type 3
++ intel-mac-v4 Intel Mac Type 4
++ intel-mac-v5 Intel Mac Type 5
++ macmini Intel Mac Mini (equivalent with type 3)
++ macbook Intel Mac Book (eq. type 5)
++ macbook-pro-v1 Intel Mac Book Pro 1st generation (eq. type 3)
++ macbook-pro Intel Mac Book Pro 2nd generation (eq. type 3)
++ imac-intel Intel iMac (eq. type 2)
++ imac-intel-20 Intel iMac (newer version) (eq. type 3)
+
+ STAC9202/9250/9251
+ ref Reference board, base config
+@@ -956,6 +1008,17 @@
+ from the irq. Remember this is a last resort, and should be
+ avoided as much as possible...
+
++ MORE NOTES ON "azx_get_response timeout" PROBLEMS:
++ On some hardwares, you may need to add a proper probe_mask option
++ to avoid the "azx_get_response timeout" problem above, instead.
++ This occurs when the access to non-existing or non-working codec slot
++ (likely a modem one) causes a stall of the communication via HD-audio
++ bus. You can see which codec slots are probed by enabling
++ CONFIG_SND_DEBUG_DETECT, or simply from the file name of the codec
++ proc files. Then limit the slots to probe by probe_mask option.
++ For example, probe_mask=1 means to probe only the first slot, and
++ probe_mask=4 means only the third slot.
++
+ The power-management is supported.
+
+ Module snd-hdsp
+--- linux-2.6.22.1.orig/Documentation/sound/alsa/Audiophile-Usb.txt
++++ linux-2.6.22.1/Documentation/sound/alsa/Audiophile-Usb.txt
+@@ -1,4 +1,4 @@
+- Guide to using M-Audio Audiophile USB with ALSA and Jack v1.3
++ Guide to using M-Audio Audiophile USB with ALSA and Jack v1.5
+ ========================================================
+
+ Thibault Le Meur <Thibault.LeMeur at supelec.fr>
+@@ -6,8 +6,19 @@
+ This document is a guide to using the M-Audio Audiophile USB (tm) device with
+ ALSA and JACK.
+
++History
++=======
++* v1.4 - Thibault Le Meur (2007-07-11)
++ - Added Low Endianness nature of 16bits-modes
++ found by Hakan Lennestal <Hakan.Lennestal at brfsodrahamn.se>
++ - Modifying document structure
++* v1.5 - Thibault Le Meur (2007-07-12)
++ - Added AC3/DTS passthru info
++
++
+ 1 - Audiophile USB Specs and correct usage
+ ==========================================
++
+ This part is a reminder of important facts about the functions and limitations
+ of the device.
+
+@@ -25,18 +36,18 @@
+ The internal DAC/ADC has the following characteristics:
+ * sample depth of 16 or 24 bits
+ * sample rate from 8kHz to 96kHz
+-* Two ports can't use different sample depths at the same time. Moreover, the
+-Audiophile USB documentation gives the following Warning: "Please exit any
+-audio application running before switching between bit depths"
++* Two interfaces can't use different sample depths at the same time.
++Moreover, the Audiophile USB documentation gives the following Warning:
++"Please exit any audio application running before switching between bit depths"
+
+ Due to the USB 1.1 bandwidth limitation, a limited number of interfaces can be
+ activated at the same time depending on the audio mode selected:
+- * 16-bit/48kHz ==> 4 channels in/4 channels out
++ * 16-bit/48kHz ==> 4 channels in + 4 channels out
+ - Ai+Ao+Di+Do
+- * 24-bit/48kHz ==> 4 channels in/2 channels out,
+- or 2 channels in/4 channels out
++ * 24-bit/48kHz ==> 4 channels in + 2 channels out,
++ or 2 channels in + 4 channels out
+ - Ai+Ao+Do or Ai+Di+Ao or Ai+Di+Do or Di+Ao+Do
+- * 24-bit/96kHz ==> 2 channels in, or 2 channels out (half duplex only)
++ * 24-bit/96kHz ==> 2 channels in _or_ 2 channels out (half duplex only)
+ - Ai or Ao or Di or Do
+
+ Important facts about the Digital interface:
+@@ -52,44 +63,56 @@
+ synchronization error (for instance sound played at an odd sample rate)
+
+
+-2 - Audiophile USB support in ALSA
+-==================================
++2 - Audiophile USB MIDI support in ALSA
++=======================================
+
+-2.1 - MIDI ports
+-----------------
+-The Audiophile USB MIDI ports will be automatically supported once the
++The Audiophile USB MIDI ports will be automatically supported once the
+ following modules have been loaded:
+ * snd-usb-audio
+ * snd-seq-midi
+
+ No additional setting is required.
+
+-2.2 - Audio ports
+------------------
++
++3 - Audiophile USB Audio support in ALSA
++========================================
+
+ Audio functions of the Audiophile USB device are handled by the snd-usb-audio
+ module. This module can work in a default mode (without any device-specific
+ parameter), or in an "advanced" mode with the device-specific parameter called
+ "device_setup".
+
+-2.2.1 - Default Alsa driver mode
+-
+-The default behavior of the snd-usb-audio driver is to parse the device
+-capabilities at startup and enable all functions inside the device (including
+-all ports at any supported sample rates and sample depths). This approach
+-has the advantage to let the driver easily switch from sample rates/depths
+-automatically according to the need of the application claiming the device.
++3.1 - Default Alsa driver mode
++------------------------------
+
+-In this case the Audiophile ports are mapped to alsa pcm devices in the
+-following way (I suppose the device's index is 1):
++The default behavior of the snd-usb-audio driver is to list the device
++capabilities at startup and activate the required mode when required
++by the applications: for instance if the user is recording in a
++24bit-depth-mode and immediately after wants to switch to a 16bit-depth mode,
++the snd-usb-audio module will reconfigure the device on the fly.
++
++This approach has the advantage to let the driver automatically switch from sample
++rates/depths automatically according to the user's needs. However, those who
++are using the device under windows know that this is not how the device is meant to
++work: under windows applications must be closed before using the m-audio control
++panel to switch the device working mode. Thus as we'll see in next section, this
++Default Alsa driver mode can lead to device misconfigurations.
++
++Let's get back to the Default Alsa driver mode for now. In this case the
++Audiophile interfaces are mapped to alsa pcm devices in the following
++way (I suppose the device's index is 1):
+ * hw:1,0 is Ao in playback and Di in capture
+ * hw:1,1 is Do in playback and Ai in capture
+ * hw:1,2 is Do in AC3/DTS passthrough mode
+
+-You must note as well that the device uses Big Endian byte encoding so that
+-supported audio format are S16_BE for 16-bit depth modes and S24_3BE for
+-24-bits depth mode. One exception is the hw:1,2 port which is Little Endian
+-compliant and thus uses S16_LE.
++In this mode, the device uses Big Endian byte-encoding so that
++supported audio format are S16_BE for 16-bit depth modes and S24_3BE for
++24-bits depth mode.
++
++One exception is the hw:1,2 port which was reported to be Little Endian
++compliant (supposedly supporting S16_LE) but processes in fact only S16_BE streams.
++This has been fixed in kernel 2.6.23 and above and now the hw:1,2 interface
++is reported to be big endian in this default driver mode.
+
+ Examples:
+ * playing a S24_3BE encoded raw file to the Ao port
+@@ -98,22 +121,26 @@
+ % arecord -D hw:1,1 -c2 -t raw -r48000 -fS24_3BE test.raw
+ * playing a S16_BE encoded raw file to the Do port
+ % aplay -D hw:1,1 -c2 -t raw -r48000 -fS16_BE test.raw
++ * playing an ac3 sample file to the Do port
++ % aplay -D hw:1,2 --channels=6 ac3_S16_BE_encoded_file.raw
+
+-If you're happy with the default Alsa driver setup and don't experience any
++If you're happy with the default Alsa driver mode and don't experience any
+ issue with this mode, then you can skip the following chapter.
+
+-2.2.2 - Advanced module setup
++3.2 - Advanced module setup
++---------------------------
+
+ Due to the hardware constraints described above, the device initialization made
+ by the Alsa driver in default mode may result in a corrupted state of the
+ device. For instance, a particularly annoying issue is that the sound captured
+-from the Ai port sounds distorted (as if boosted with an excessive high volume
+-gain).
++from the Ai interface sounds distorted (as if boosted with an excessive high
++volume gain).
+
+ For people having this problem, the snd-usb-audio module has a new module
+-parameter called "device_setup".
++parameter called "device_setup" (this parameter was introduced in kernel
++release 2.6.17)
+
+-2.2.2.1 - Initializing the working mode of the Audiophile USB
++3.2.1 - Initializing the working mode of the Audiophile USB
+
+ As far as the Audiophile USB device is concerned, this value let the user
+ specify:
+@@ -121,33 +148,57 @@
+ * the sample rate
+ * whether the Di port is used or not
+
+-Here is a list of supported device_setup values for this device:
+- * device_setup=0x00 (or omitted)
+- - Alsa driver default mode
+- - maintains backward compatibility with setups that do not use this
+- parameter by not introducing any change
+- - results sometimes in corrupted sound as described earlier
++When initialized with "device_setup=0x00", the snd-usb-audio module has
++the same behaviour as when the parameter is omitted (see paragraph "Default
++Alsa driver mode" above)
++
++Others modes are described in the following subsections.
++
++3.2.1.1 - 16-bit modes
++
++The two supported modes are:
++
+ * device_setup=0x01
+ - 16bits 48kHz mode with Di disabled
+ - Ai,Ao,Do can be used at the same time
+ - hw:1,0 is not available in capture mode
+ - hw:1,2 is not available
++
+ * device_setup=0x11
+ - 16bits 48kHz mode with Di enabled
+ - Ai,Ao,Di,Do can be used at the same time
+ - hw:1,0 is available in capture mode
+ - hw:1,2 is not available
++
++In this modes the device operates only at 16bits-modes. Before kernel 2.6.23,
++the devices where reported to be Big-Endian when in fact they were Little-Endian
++so that playing a file was a matter of using:
++ % aplay -D hw:1,1 -c2 -t raw -r48000 -fS16_BE test_S16_LE.raw
++where "test_S16_LE.raw" was in fact a little-endian sample file.
++
++Thanks to Hakan Lennestal (who discovered the Little-Endiannes of the device in
++these modes) a fix has been committed (expected in kernel 2.6.23) and
++Alsa now reports Little-Endian interfaces. Thus playing a file now is as simple as
++using:
++ % aplay -D hw:1,1 -c2 -t raw -r48000 -fS16_LE test_S16_LE.raw
++
++3.2.1.2 - 24-bit modes
++
++The three supported modes are:
++
+ * device_setup=0x09
+ - 24bits 48kHz mode with Di disabled
+ - Ai,Ao,Do can be used at the same time
+ - hw:1,0 is not available in capture mode
+ - hw:1,2 is not available
++
+ * device_setup=0x19
+ - 24bits 48kHz mode with Di enabled
+ - 3 ports from {Ai,Ao,Di,Do} can be used at the same time
+ - hw:1,0 is available in capture mode and an active digital source must be
+ connected to Di
+ - hw:1,2 is not available
++
+ * device_setup=0x0D or 0x10
+ - 24bits 96kHz mode
+ - Di is enabled by default for this mode but does not need to be connected
+@@ -155,34 +206,64 @@
+ - Only 1 port from {Ai,Ao,Di,Do} can be used at the same time
+ - hw:1,0 is available in captured mode
+ - hw:1,2 is not available
++
++In these modes the device is only Big-Endian compliant (see "Default Alsa driver
++mode" above for an aplay command example)
++
++3.2.1.3 - AC3 w/ DTS passthru mode
++
++Thanks to Hakan Lennestal, I now have a report saying that this mode works.
++
+ * device_setup=0x03
+ - 16bits 48kHz mode with only the Do port enabled
+- - AC3 with DTS passthru (not tested)
++ - AC3 with DTS passthru
+ - Caution with this setup the Do port is mapped to the pcm device hw:1,0
+
+-2.2.2.2 - Setting and switching configurations with the device_setup parameter
++The command line used to playback the AC3/DTS encoded .wav-files in this mode:
++ % aplay -D hw:1,0 --channels=6 ac3_S16_LE_encoded_file.raw
++
++3.2.2 - How to use the device_setup parameter
++----------------------------------------------
+
+ The parameter can be given:
++
+ * By manually probing the device (as root):
+ # modprobe -r snd-usb-audio
+ # modprobe snd-usb-audio index=1 device_setup=0x09
++
+ * Or while configuring the modules options in your modules configuration file
+ - For Fedora distributions, edit the /etc/modprobe.conf file:
+ alias snd-card-1 snd-usb-audio
+ options snd-usb-audio index=1 device_setup=0x09
+
+-IMPORTANT NOTE WHEN SWITCHING CONFIGURATION:
+--------------------------------------------
+- * You may need to _first_ initialize the module with the correct device_setup
+- parameter and _only_after_ turn on the Audiophile USB device
+- * This is especially true when switching the sample depth:
++CAUTION when initializaing the device
++-------------------------------------
++
++ * Correct initialization on the device requires that device_setup is given to
++ the module BEFORE the device is turned on. So, if you use the "manual probing"
++ method described above, take care to power-on the device AFTER this initialization.
++
++ * Failing to respect this will lead in a misconfiguration of the device. In this case
++ turn off the device, unproble the snd-usb-audio module, then probe it again with
++ correct device_setup parameter and then (and only then) turn on the device again.
++
++ * If you've correctly initialized the device in a valid mode and then want to switch
++ to another mode (possibly with another sample-depth), please use also the following
++ procedure:
+ - first turn off the device
+ - de-register the snd-usb-audio module (modprobe -r)
+ - change the device_setup parameter by changing the device_setup
+ option in /etc/modprobe.conf
+ - turn on the device
++ * A workaround for this last issue has been applied to kernel 2.6.23, but it may not
++ be enough to ensure the 'stability' of the device initialization.
+
+-2.2.2.3 - Audiophile USB's device_setup structure
++3.2.3 - Technical details for hackers
++-------------------------------------
++This section is for hackers, wanting to understand details about the device
++internals and how Alsa supports it.
++
++3.2.3.1 - Audiophile USB's device_setup structure
+
+ If you want to understand the device_setup magic numbers for the Audiophile
+ USB, you need some very basic understanding of binary computation. However,
+@@ -228,12 +309,12 @@
+ - choosing b2 will prepare all interfaces for 24bits/96kHz but you'll
+ only be able to use one at the same time
+
+-2.2.3 - USB implementation details for this device
++3.2.3.2 - USB implementation details for this device
+
+ You may safely skip this section if you're not interested in driver
+-development.
++hacking.
+
+-This section describes some internal aspects of the device and summarize the
++This section describes some internal aspects of the device and summarizes the
+ data I got by usb-snooping the windows and Linux drivers.
+
+ The M-Audio Audiophile USB has 7 USB Interfaces:
+@@ -293,43 +374,45 @@
+ "audiophile_skip_setting_quirk" in order to prevent AltSettings not
+ corresponding to device_setup from being registered in the driver.
+
+-3 - Audiophile USB and Jack support
++4 - Audiophile USB and Jack support
+ ===================================
+
+ This section deals with support of the Audiophile USB device in Jack.
+-The main issue regarding this support is that the device is Big Endian
+-compliant.
+
+-3.1 - Using the plug alsa plugin
+---------------------------------
++There are 2 main potential issues when using Jackd with the device:
++* support for Big-Endian devices in 24-bit modes
++* support for 4-in / 4-out channels
++
++4.1 - Direct support in Jackd
++-----------------------------
++
++Jack supports big endian devices only in recent versions (thanks to
++Andreas Steinmetz for his first big-endian patch). I can't remember
++extacly when this support was released into jackd, let's just say that
++with jackd version 0.103.0 it's almost ok (just a small bug is affecting
++16bits Big-Endian devices, but since you've read carefully the above
++paragraphs, you're now using kernel >= 2.6.23 and your 16bits devices
++are now Little Endians ;-) ).
+
+-Jack doesn't directly support big endian devices. Thus, one way to have support
+-for this device with Alsa is to use the Alsa "plug" converter.
++You can run jackd with the following command for playback with Ao and
++record with Ai:
++ % jackd -R -dalsa -Phw:1,0 -r48000 -p128 -n2 -D -Chw:1,1
++
++4.2 - Using Alsa plughw
++-----------------------
++If you don't have a recent Jackd installed, you can downgrade to using
++the Alsa "plug" converter.
+
+ For instance here is one way to run Jack with 2 playback channels on Ao and 2
+ capture channels from Ai:
+ % jackd -R -dalsa -dplughw:1 -r48000 -p256 -n2 -D -Cplughw:1,1
+
+-
+ However you may see the following warning message:
+ "You appear to be using the ALSA software "plug" layer, probably a result of
+ using the "default" ALSA device. This is less efficient than it could be.
+ Consider using a hardware device instead rather than using the plug layer."
+
+-3.2 - Patching alsa to use direct pcm device
+---------------------------------------------
+-A patch for Jack by Andreas Steinmetz adds support for Big Endian devices.
+-However it has not been included in the CVS tree.
+-
+-You can find it at the following URL:
+-http://sourceforge.net/tracker/index.php?func=detail&aid=1289682&group_id=39687&
+-atid=425939
+-
+-After having applied the patch you can run jackd with the following command
+-line:
+- % jackd -R -dalsa -Phw:1,0 -r48000 -p128 -n2 -D -Chw:1,1
+-
+-3.2 - Getting 2 input and/or output interfaces in Jack
++4.3 - Getting 2 input and/or output interfaces in Jack
+ ------------------------------------------------------
+
+ As you can see, starting the Jack server this way will only enable 1 stereo
+@@ -339,6 +422,7 @@
+ * Jack can only open one capture device and one playback device at a time
+ * The Audiophile USB is seen as 2 (or three) Alsa devices: hw:1,0, hw:1,1
+ (and optionally hw:1,2)
++
+ If you want to get Ai+Di and/or Ao+Do support with Jack, you would need to
+ combine the Alsa devices into one logical "complex" device.
+
+@@ -348,13 +432,11 @@
+ the Audiophile USB.
+
+ Enabling multiple Audiophile USB interfaces for Jackd will certainly require:
+-* patching Jack with the previously mentioned "Big Endian" patch
+-* patching Jackd with the MMAP_COMPLEX patch (see the ice1712 page)
+-* patching the alsa-lib/src/pcm/pcm_multi.c file (see the ice1712 page)
++* Making sure your Jackd version has the MMAP_COMPLEX patch (see the ice1712 page)
++* (maybe) patching the alsa-lib/src/pcm/pcm_multi.c file (see the ice1712 page)
+ * define a multi device (combination of hw:1,0 and hw:1,1) in your .asoundrc
+ file
+ * start jackd with this device
+
+-I had no success in testing this for now, but this may be due to my OS
+-configuration. If you have any success with this kind of setup, please
+-drop me an email.
++I had no success in testing this for now, if you have any success with this kind
++of setup, please drop me an email.
+--- linux-2.6.22.1.orig/Documentation/sound/alsa/OSS-Emulation.txt
++++ linux-2.6.22.1/Documentation/sound/alsa/OSS-Emulation.txt
+@@ -278,6 +278,21 @@
+ image.
+
+
++Duplex Streams
++==============
++
++Note that when attempting to use a single device file for playback and
++capture, the OSS API provides no way to set the format, sample rate or
++number of channels different in each direction. Thus
++ io_handle = open("device", O_RDWR)
++will only function correctly if the values are the same in each direction.
++
++To use different values in the two directions, use both
++ input_handle = open("device", O_RDONLY)
++ output_handle = open("device", O_WRONLY)
++and set the values for the corresponding handle.
++
++
+ Unsupported Features
+ ====================
+
+--- linux-2.6.22.1.orig/include/linux/i2c-id.h
++++ linux-2.6.22.1/include/linux/i2c-id.h
+@@ -115,9 +115,10 @@
+ #define I2C_DRIVERID_KS0127 86 /* Samsung ks0127 video decoder */
+ #define I2C_DRIVERID_TLV320AIC23B 87 /* TI TLV320AIC23B audio codec */
+ #define I2C_DRIVERID_ISL1208 88 /* Intersil ISL1208 RTC */
+-#define I2C_DRIVERID_WM8731 89 /* Wolfson WM8731 audio codec */
+-#define I2C_DRIVERID_WM8750 90 /* Wolfson WM8750 audio codec */
+-#define I2C_DRIVERID_WM8753 91 /* Wolfson WM8753 audio codec */
++#define I2C_DRIVERID_WM8731 89 /* Wolfson WM8731 audio codec */
++#define I2C_DRIVERID_WM8750 90 /* Wolfson WM8750 audio codec */
++#define I2C_DRIVERID_WM8753 91 /* Wolfson WM8753 audio codec */
++#define I2C_DRIVERID_LM4857 92 /* LM4857 Audio Amplifier */
+
+ #define I2C_DRIVERID_I2CDEV 900
+ #define I2C_DRIVERID_ARP 902 /* SMBus ARP Client */
+--- linux-2.6.22.1.orig/include/sound/ak4xxx-adda.h
++++ linux-2.6.22.1/include/sound/ak4xxx-adda.h
+@@ -43,6 +43,7 @@
+ struct snd_akm4xxx_dac_channel {
+ char *name; /* mixer volume name */
+ unsigned int num_channels;
++ char *switch_name; /* mixer switch*/
+ };
+
+ /* ADC labels and channels */
+--- linux-2.6.22.1.orig/include/sound/cs46xx.h
++++ linux-2.6.22.1/include/sound/cs46xx.h
+@@ -1723,6 +1723,10 @@
+ struct snd_cs46xx_pcm *playback_pcm;
+ unsigned int play_ctl;
+ #endif
++
++#ifdef CONFIG_PM
++ u32 *saved_regs;
++#endif
+ };
+
+ int snd_cs46xx_create(struct snd_card *card,
+--- linux-2.6.22.1.orig/include/sound/cs46xx_dsp_spos.h
++++ linux-2.6.22.1/include/sound/cs46xx_dsp_spos.h
+@@ -107,6 +107,7 @@
+ char scb_name[DSP_MAX_SCB_NAME];
+ u32 address;
+ int index;
++ u32 *data;
+
+ struct dsp_scb_descriptor * sub_list_ptr;
+ struct dsp_scb_descriptor * next_scb_ptr;
+@@ -127,6 +128,7 @@
+ int size;
+ u32 address;
+ int index;
++ u32 *data;
+ };
+
+ struct dsp_pcm_channel_descriptor {
+--- linux-2.6.22.1.orig/include/sound/emu10k1.h
++++ linux-2.6.22.1/include/sound/emu10k1.h
+@@ -1120,6 +1120,16 @@
+ /************************************************************************************************/
+ /* EMU1010m HANA Destinations */
+ /************************************************************************************************/
++/* 32-bit destinations of signal in the Hana FPGA. Destinations are either
++ * physical outputs of Hana, or outputs going to Alice2 (audigy) for capture
++ * - 16 x EMU_DST_ALICE2_EMU32_X.
++ */
++/* EMU32 = 32-bit serial channel between Alice2 (audigy) and Hana (FPGA) */
++/* EMU_DST_ALICE2_EMU32_X - data channels from Hana to Alice2 used for capture.
++ * Which data is fed into a EMU_DST_ALICE2_EMU32_X channel in Hana depends on
++ * setup of mixer control for each destination - see emumixer.c -
++ * snd_emu1010_output_enum_ctls[], snd_emu1010_input_enum_ctls[]
++ */
+ #define EMU_DST_ALICE2_EMU32_0 0x000f /* 16 EMU32 channels to Alice2 +0 to +0xf */
+ #define EMU_DST_ALICE2_EMU32_1 0x0000 /* 16 EMU32 channels to Alice2 +0 to +0xf */
+ #define EMU_DST_ALICE2_EMU32_2 0x0001 /* 16 EMU32 channels to Alice2 +0 to +0xf */
+@@ -1199,6 +1209,12 @@
+ /************************************************************************************************/
+ /* EMU1010m HANA Sources */
+ /************************************************************************************************/
++/* 32-bit sources of signal in the Hana FPGA. The sources are routed to
++ * destinations using mixer control for each destination - see emumixer.c
++ * Sources are either physical inputs of FPGA,
++ * or outputs from Alice (audigy) - 16 x EMU_SRC_ALICE_EMU32A +
++ * 16 x EMU_SRC_ALICE_EMU32B
++ */
+ #define EMU_SRC_SILENCE 0x0000 /* Silence */
+ #define EMU_SRC_DOCK_MIC_A1 0x0100 /* Audio Dock Mic A, 1st or 48kHz only */
+ #define EMU_SRC_DOCK_MIC_A2 0x0101 /* Audio Dock Mic A, 2nd or 96kHz */
+--- linux-2.6.22.1.orig/include/sound/sb.h
++++ linux-2.6.22.1/include/sound/sb.h
+@@ -38,6 +38,7 @@
+ SB_HW_ALS100, /* Avance Logic ALS100 chip */
+ SB_HW_ALS4000, /* Avance Logic ALS4000 chip */
+ SB_HW_DT019X, /* Diamond Tech. DT-019X / Avance Logic ALS-007 */
++ SB_HW_CS5530, /* Cyrix/NatSemi 5530 VSA1 */
+ };
+
+ #define SB_OPEN_PCM 0x01
+--- linux-2.6.22.1.orig/include/sound/version.h
++++ linux-2.6.22.1/include/sound/version.h
+@@ -1,3 +1,3 @@
+ /* include/version.h. Generated by alsa/ksync script. */
+ #define CONFIG_SND_VERSION "1.0.14"
+-#define CONFIG_SND_DATE " (Thu May 31 09:03:25 2007 UTC)"
++#define CONFIG_SND_DATE " (Fri Jul 20 09:12:58 2007 UTC)"
+--- linux-2.6.22.1.orig/include/sound/wavefront_fx.h
++++ /dev/null
+@@ -1,9 +0,0 @@
+-#ifndef __SOUND_WAVEFRONT_FX_H
+-#define __SOUND_WAVEFRONT_FX_H
+-
+-extern int snd_wavefront_fx_detect (snd_wavefront_t *);
+-extern void snd_wavefront_fx_ioctl (snd_synth_t *sdev,
+- unsigned int cmd,
+- unsigned long arg);
+-
+-#endif __SOUND_WAVEFRONT_FX_H
+--- linux-2.6.22.1.orig/sound/Kconfig
++++ linux-2.6.22.1/sound/Kconfig
+@@ -65,6 +65,8 @@
+
+ source "sound/mips/Kconfig"
+
++source "sound/sh/Kconfig"
++
+ # the following will depend on the order of config.
+ # here assuming USB is defined before ALSA
+ source "sound/usb/Kconfig"
+--- linux-2.6.22.1.orig/sound/Makefile
++++ linux-2.6.22.1/sound/Makefile
+@@ -5,7 +5,7 @@
+ obj-$(CONFIG_SOUND_PRIME) += sound_firmware.o
+ obj-$(CONFIG_SOUND_PRIME) += oss/
+ obj-$(CONFIG_DMASOUND) += oss/
+-obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/ soc/
++obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/ soc/
+ obj-$(CONFIG_SND_AOA) += aoa/
+
+ # This one must be compilable even if sound is configured out
+--- linux-2.6.22.1.orig/sound/aoa/codecs/snd-aoa-codec-onyx.c
++++ linux-2.6.22.1/sound/aoa/codecs/snd-aoa-codec-onyx.c
+@@ -661,7 +661,7 @@
+ .tag = 2,
+ },
+ #ifdef SNDRV_PCM_FMTBIT_COMPRESSED_16BE
+-Once alsa gets supports for this kind of thing we can add it...
++ /* Once alsa gets supports for this kind of thing we can add it... */
+ {
+ /* digital compressed output */
+ .formats = SNDRV_PCM_FMTBIT_COMPRESSED_16BE,
+@@ -713,7 +713,7 @@
+ if (substream->runtime->format == SNDRV_PCM_FMTBIT_COMPRESSED_16BE) {
+ /* mute and lock analog output */
+ onyx_read_register(onyx, ONYX_REG_DAC_CONTROL, &v);
+- if (onyx_write_register(onyx
++ if (onyx_write_register(onyx,
+ ONYX_REG_DAC_CONTROL,
+ v | ONYX_MUTE_RIGHT | ONYX_MUTE_LEFT))
+ goto out_unlock;
+--- linux-2.6.22.1.orig/sound/core/pcm_native.c
++++ linux-2.6.22.1/sound/core/pcm_native.c
+@@ -1487,7 +1487,7 @@
+
+ snd_pcm_stream_lock_irq(substream);
+ /* resume pause */
+- if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
++ if (substream->runtime->status->state == SNDRV_PCM_STATE_PAUSED)
+ snd_pcm_pause(substream, 0);
+
+ /* pre-start/stop - all running streams are changed to DRAINING state */
+--- linux-2.6.22.1.orig/sound/core/seq/seq_instr.c
++++ linux-2.6.22.1/sound/core/seq/seq_instr.c
+@@ -109,7 +109,7 @@
+ spin_lock_irqsave(&list->lock, flags);
+ while (instr->use) {
+ spin_unlock_irqrestore(&list->lock, flags);
+- schedule_timeout_interruptible(1);
++ schedule_timeout(1);
+ spin_lock_irqsave(&list->lock, flags);
+ }
+ spin_unlock_irqrestore(&list->lock, flags);
+@@ -199,7 +199,7 @@
+ instr = flist;
+ flist = instr->next;
+ while (instr->use)
+- schedule_timeout_interruptible(1);
++ schedule_timeout(1);
+ if (snd_seq_instr_free(instr, atomic)<0)
+ snd_printk(KERN_WARNING "instrument free problem\n");
+ instr = next;
+@@ -555,7 +555,7 @@
+ SNDRV_SEQ_INSTR_NOTIFY_REMOVE);
+ while (instr->use) {
+ spin_unlock_irqrestore(&list->lock, flags);
+- schedule_timeout_interruptible(1);
++ schedule_timeout(1);
+ spin_lock_irqsave(&list->lock, flags);
+ }
+ spin_unlock_irqrestore(&list->lock, flags);
+--- linux-2.6.22.1.orig/sound/core/timer.c
++++ linux-2.6.22.1/sound/core/timer.c
+@@ -1549,9 +1549,11 @@
+ int err = 0;
+
+ tu = file->private_data;
+- snd_assert(tu->timeri != NULL, return -ENXIO);
++ if (!tu->timeri)
++ return -EBADFD;
+ t = tu->timeri->timer;
+- snd_assert(t != NULL, return -ENXIO);
++ if (!t)
++ return -EBADFD;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (! info)
+@@ -1579,9 +1581,11 @@
+ int err;
+
+ tu = file->private_data;
+- snd_assert(tu->timeri != NULL, return -ENXIO);
++ if (!tu->timeri)
++ return -EBADFD;
+ t = tu->timeri->timer;
+- snd_assert(t != NULL, return -ENXIO);
++ if (!t)
++ return -EBADFD;
+ if (copy_from_user(¶ms, _params, sizeof(params)))
+ return -EFAULT;
+ if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE) && params.ticks < 1) {
+@@ -1675,7 +1679,8 @@
+ struct snd_timer_status status;
+
+ tu = file->private_data;
+- snd_assert(tu->timeri != NULL, return -ENXIO);
++ if (!tu->timeri)
++ return -EBADFD;
+ memset(&status, 0, sizeof(status));
+ status.tstamp = tu->tstamp;
+ status.resolution = snd_timer_resolution(tu->timeri);
+@@ -1695,7 +1700,8 @@
+ struct snd_timer_user *tu;
+
+ tu = file->private_data;
+- snd_assert(tu->timeri != NULL, return -ENXIO);
++ if (!tu->timeri)
++ return -EBADFD;
+ snd_timer_stop(tu->timeri);
+ tu->timeri->lost = 0;
+ tu->last_resolution = 0;
+@@ -1708,7 +1714,8 @@
+ struct snd_timer_user *tu;
+
+ tu = file->private_data;
+- snd_assert(tu->timeri != NULL, return -ENXIO);
++ if (!tu->timeri)
++ return -EBADFD;
+ return (err = snd_timer_stop(tu->timeri)) < 0 ? err : 0;
+ }
+
+@@ -1718,7 +1725,8 @@
+ struct snd_timer_user *tu;
+
+ tu = file->private_data;
+- snd_assert(tu->timeri != NULL, return -ENXIO);
++ if (!tu->timeri)
++ return -EBADFD;
+ tu->timeri->lost = 0;
+ return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0;
+ }
+@@ -1729,7 +1737,8 @@
+ struct snd_timer_user *tu;
+
+ tu = file->private_data;
+- snd_assert(tu->timeri != NULL, return -ENXIO);
++ if (!tu->timeri)
++ return -EBADFD;
+ return (err = snd_timer_pause(tu->timeri)) < 0 ? err : 0;
+ }
+
+--- linux-2.6.22.1.orig/sound/drivers/dummy.c
++++ linux-2.6.22.1/sound/drivers/dummy.c
+@@ -659,7 +659,7 @@
+ },
+ };
+
+-static void __init_or_module snd_dummy_unregister_all(void)
++static void snd_dummy_unregister_all(void)
+ {
+ int i;
+
+--- linux-2.6.22.1.orig/sound/drivers/mpu401/mpu401.c
++++ linux-2.6.22.1/sound/drivers/mpu401/mpu401.c
+@@ -228,7 +228,7 @@
+ static struct pnp_driver snd_mpu401_pnp_driver;
+ #endif
+
+-static void __init_or_module snd_mpu401_unregister_all(void)
++static void snd_mpu401_unregister_all(void)
+ {
+ int i;
+
+--- linux-2.6.22.1.orig/sound/drivers/portman2x4.c
++++ linux-2.6.22.1/sound/drivers/portman2x4.c
+@@ -833,7 +833,7 @@
+ /*********************************************************************
+ * module init stuff
+ *********************************************************************/
+-static void __init_or_module snd_portman_unregister_all(void)
++static void snd_portman_unregister_all(void)
+ {
+ int i;
+
+--- linux-2.6.22.1.orig/sound/drivers/serial-u16550.c
++++ linux-2.6.22.1/sound/drivers/serial-u16550.c
+@@ -998,7 +998,7 @@
+ },
+ };
+
+-static void __init_or_module snd_serial_unregister_all(void)
++static void snd_serial_unregister_all(void)
+ {
+ int i;
+
+--- linux-2.6.22.1.orig/sound/drivers/virmidi.c
++++ linux-2.6.22.1/sound/drivers/virmidi.c
+@@ -145,7 +145,7 @@
+ },
+ };
+
+-static void __init_or_module snd_virmidi_unregister_all(void)
++static void snd_virmidi_unregister_all(void)
+ {
+ int i;
+
+--- linux-2.6.22.1.orig/sound/i2c/other/ak4xxx-adda.c
++++ linux-2.6.22.1/sound/i2c/other/ak4xxx-adda.c
+@@ -481,8 +481,8 @@
+ int addr = AK_GET_ADDR(kcontrol->private_value);
+ int shift = AK_GET_SHIFT(kcontrol->private_value);
+ int invert = AK_GET_INVERT(kcontrol->private_value);
+- unsigned char val = snd_akm4xxx_get(ak, chip, addr);
+-
++ /* we observe the (1<<shift) bit only */
++ unsigned char val = snd_akm4xxx_get(ak, chip, addr) & (1<<shift);
+ if (invert)
+ val = ! val;
+ ucontrol->value.integer.value[0] = (val & (1<<shift)) != 0;
+@@ -585,6 +585,26 @@
+
+ mixer_ch = 0;
+ for (idx = 0; idx < ak->num_dacs; ) {
++ /* mute control for Revolution 7.1 - AK4381 */
++ if (ak->type == SND_AK4381
++ && ak->dac_info[mixer_ch].switch_name) {
++ memset(&knew, 0, sizeof(knew));
++ knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
++ knew.count = 1;
++ knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
++ knew.name = ak->dac_info[mixer_ch].switch_name;
++ knew.info = ak4xxx_switch_info;
++ knew.get = ak4xxx_switch_get;
++ knew.put = ak4xxx_switch_put;
++ knew.access = 0;
++ /* register 1, bit 0 (SMUTE): 0 = normal operation,
++ 1 = mute */
++ knew.private_value =
++ AK_COMPOSE(idx/2, 1, 0, 0) | AK_INVERT;
++ err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
++ if (err < 0)
++ return err;
++ }
+ memset(&knew, 0, sizeof(knew));
+ if (! ak->dac_info || ! ak->dac_info[mixer_ch].name) {
+ knew.name = "DAC Volume";
+--- linux-2.6.22.1.orig/sound/isa/Kconfig
++++ linux-2.6.22.1/sound/isa/Kconfig
+@@ -1,8 +1,5 @@
+ # ALSA ISA drivers
+
+-menu "ISA devices"
+- depends on SND!=n && ISA && ISA_DMA_API
+-
+ config SND_AD1848_LIB
+ tristate
+ select SND_PCM
+@@ -11,6 +8,22 @@
+ tristate
+ select SND_PCM
+
++config SND_SB_COMMON
++ tristate
++
++config SND_SB8_DSP
++ tristate
++ select SND_PCM
++ select SND_SB_COMMON
++
++config SND_SB16_DSP
++ tristate
++ select SND_PCM
++ select SND_SB_COMMON
++
++menu "ISA devices"
++ depends on SND!=n && ISA && ISA_DMA_API
++
+ config SND_ADLIB
+ tristate "AdLib FM card"
+ depends on SND
+@@ -55,7 +68,7 @@
+ select ISAPNP
+ select SND_OPL3_LIB
+ select SND_MPU401_UART
+- select SND_PCM
++ select SND_SB16_DSP
+ help
+ Say Y here to include support for soundcards based on Avance
+ Logic ALS100, ALS110, ALS120 and ALS200 chips.
+@@ -81,6 +94,7 @@
+ tristate "C-Media CMI8330"
+ depends on SND
+ select SND_AD1848_LIB
++ select SND_SB16_DSP
+ help
+ Say Y here to include support for soundcards based on the
+ C-Media CMI8330 chip.
+@@ -132,7 +146,7 @@
+ select ISAPNP
+ select SND_OPL3_LIB
+ select SND_MPU401_UART
+- select SND_PCM
++ select SND_SB16_DSP
+ help
+ Say Y here to include support for soundcards based on the
+ Diamond Technologies DT-019X or Avance Logic ALS-007 chips.
+@@ -145,7 +159,7 @@
+ depends on SND && PNP && ISA
+ select ISAPNP
+ select SND_MPU401_UART
+- select SND_PCM
++ select SND_SB8_DSP
+ help
+ Say Y here to include support for ESS AudioDrive ES968 chips.
+
+@@ -321,7 +335,7 @@
+ depends on SND
+ select SND_OPL3_LIB
+ select SND_RAWMIDI
+- select SND_PCM
++ select SND_SB8_DSP
+ help
+ Say Y here to include support for Creative Sound Blaster 1.0/
+ 2.0/Pro (8-bit) or 100% compatible soundcards.
+@@ -334,7 +348,7 @@
+ depends on SND
+ select SND_OPL3_LIB
+ select SND_MPU401_UART
+- select SND_PCM
++ select SND_SB16_DSP
+ help
+ Say Y here to include support for Sound Blaster 16 soundcards
+ (including the Plug and Play version).
+@@ -347,7 +361,7 @@
+ depends on SND
+ select SND_OPL3_LIB
+ select SND_MPU401_UART
+- select SND_PCM
++ select SND_SB16_DSP
+ help
+ Say Y here to include support for Sound Blaster AWE soundcards
+ (including the Plug and Play version).
+--- linux-2.6.22.1.orig/sound/isa/ad1848/ad1848_lib.c
++++ linux-2.6.22.1/sound/isa/ad1848/ad1848_lib.c
+@@ -245,7 +245,7 @@
+ snd_printk(KERN_ERR "mce_down - auto calibration time out (2)\n");
+ return;
+ }
+- time = schedule_timeout_interruptible(time);
++ time = schedule_timeout(time);
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ }
+ #if 0
+@@ -258,7 +258,7 @@
+ snd_printk(KERN_ERR "mce_down - auto calibration time out (3)\n");
+ return;
+ }
+- time = schedule_timeout_interruptible(time);
++ time = schedule_timeout(time);
+ spin_lock_irqsave(&chip->reg_lock, flags);
+ }
+ spin_unlock_irqrestore(&chip->reg_lock, flags);
+--- linux-2.6.22.1.orig/sound/isa/opl3sa2.c
++++ linux-2.6.22.1/sound/isa/opl3sa2.c
+@@ -164,6 +164,8 @@
+ { .id = "YMH0801", .devs = { { "YMH0021" } } },
+ /* NeoMagic MagicWave 3DX */
+ { .id = "NMX2200", .devs = { { "YMH2210" } } },
++ /* NeoMagic MagicWave 3D */
++ { .id = "NMX2200", .devs = { { "NMX2210" } } },
+ /* --- */
+ { .id = "" } /* end */
+ };
+--- linux-2.6.22.1.orig/sound/isa/opti9xx/opti92x-ad1848.c
++++ linux-2.6.22.1/sound/isa/opti9xx/opti92x-ad1848.c
+@@ -1927,10 +1927,12 @@
+ static int __devinit snd_opti9xx_isa_match(struct device *devptr,
+ unsigned int dev)
+ {
++#ifdef CONFIG_PNP
+ if (snd_opti9xx_pnp_is_probed)
+ return 0;
+ if (isapnp)
+ return 0;
++#endif
+ return 1;
+ }
+
+@@ -2096,6 +2098,7 @@
+ pnp_register_card_driver(&opti9xx_pnpc_driver);
+ if (snd_opti9xx_pnp_is_probed)
+ return 0;
++ pnp_unregister_card_driver(&opti9xx_pnpc_driver);
+ #endif
+ return isa_register_driver(&snd_opti9xx_driver, 1);
+ }
+--- linux-2.6.22.1.orig/sound/isa/sb/Makefile
++++ linux-2.6.22.1/sound/isa/sb/Makefile
+@@ -22,14 +22,13 @@
+ sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
+
+ # Toplevel Module Dependency
+-obj-$(CONFIG_SND_ALS100) += snd-sb16-dsp.o snd-sb-common.o
+-obj-$(CONFIG_SND_CMI8330) += snd-sb16-dsp.o snd-sb-common.o
+-obj-$(CONFIG_SND_DT019X) += snd-sb16-dsp.o snd-sb-common.o
+-obj-$(CONFIG_SND_SB8) += snd-sb8.o snd-sb8-dsp.o snd-sb-common.o
+-obj-$(CONFIG_SND_SB16) += snd-sb16.o snd-sb16-dsp.o snd-sb-common.o
+-obj-$(CONFIG_SND_SBAWE) += snd-sbawe.o snd-sb16-dsp.o snd-sb-common.o
+-obj-$(CONFIG_SND_ES968) += snd-es968.o snd-sb8-dsp.o snd-sb-common.o
+-obj-$(CONFIG_SND_ALS4000) += snd-sb-common.o
++obj-$(CONFIG_SND_SB_COMMON) += snd-sb-common.o
++obj-$(CONFIG_SND_SB16_DSP) += snd-sb16-dsp.o
++obj-$(CONFIG_SND_SB8_DSP) += snd-sb8-dsp.o
++obj-$(CONFIG_SND_SB8) += snd-sb8.o
++obj-$(CONFIG_SND_SB16) += snd-sb16.o
++obj-$(CONFIG_SND_SBAWE) += snd-sbawe.o
++obj-$(CONFIG_SND_ES968) += snd-es968.o
+ ifeq ($(CONFIG_SND_SB16_CSP),y)
+ obj-$(CONFIG_SND_SB16) += snd-sb16-csp.o
+ obj-$(CONFIG_SND_SBAWE) += snd-sb16-csp.o
+--- linux-2.6.22.1.orig/sound/isa/sb/sb16_main.c
++++ linux-2.6.22.1/sound/isa/sb/sb16_main.c
+@@ -563,6 +563,11 @@
+ __open_ok:
+ if (chip->hardware == SB_HW_ALS100)
+ runtime->hw.rate_max = 48000;
++ if (chip->hardware == SB_HW_CS5530) {
++ runtime->hw.buffer_bytes_max = 32 * 1024;
++ runtime->hw.periods_min = 2;
++ runtime->hw.rate_min = 44100;
++ }
+ if (chip->mode & SB_RATE_LOCK)
+ runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate;
+ chip->playback_substream = substream;
+@@ -633,6 +638,11 @@
+ __open_ok:
+ if (chip->hardware == SB_HW_ALS100)
+ runtime->hw.rate_max = 48000;
++ if (chip->hardware == SB_HW_CS5530) {
++ runtime->hw.buffer_bytes_max = 32 * 1024;
++ runtime->hw.periods_min = 2;
++ runtime->hw.rate_min = 44100;
++ }
+ if (chip->mode & SB_RATE_LOCK)
+ runtime->hw.rate_min = runtime->hw.rate_max = chip->locked_rate;
+ chip->capture_substream = substream;
+--- linux-2.6.22.1.orig/sound/isa/sb/sb_common.c
++++ linux-2.6.22.1/sound/isa/sb/sb_common.c
+@@ -128,7 +128,7 @@
+ minor = version & 0xff;
+ snd_printdd("SB [0x%lx]: DSP chip found, version = %i.%i\n",
+ chip->port, major, minor);
+-
++
+ switch (chip->hardware) {
+ case SB_HW_AUTO:
+ switch (major) {
+@@ -168,6 +168,9 @@
+ case SB_HW_DT019X:
+ str = "(DT019X/ALS007)";
+ break;
++ case SB_HW_CS5530:
++ str = "16 (CS5530)";
++ break;
+ default:
+ return -ENODEV;
+ }
+--- linux-2.6.22.1.orig/sound/isa/sb/sb_mixer.c
++++ linux-2.6.22.1/sound/isa/sb/sb_mixer.c
+@@ -821,6 +821,7 @@
+ break;
+ case SB_HW_16:
+ case SB_HW_ALS100:
++ case SB_HW_CS5530:
+ if ((err = snd_sbmixer_init(chip,
+ snd_sb16_controls,
+ ARRAY_SIZE(snd_sb16_controls),
+@@ -950,6 +951,7 @@
+ break;
+ case SB_HW_16:
+ case SB_HW_ALS100:
++ case SB_HW_CS5530:
+ save_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs));
+ break;
+ case SB_HW_ALS4000:
+@@ -975,6 +977,7 @@
+ break;
+ case SB_HW_16:
+ case SB_HW_ALS100:
++ case SB_HW_CS5530:
+ restore_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs));
+ break;
+ case SB_HW_ALS4000:
+--- linux-2.6.22.1.orig/sound/isa/sscape.c
++++ linux-2.6.22.1/sound/isa/sscape.c
+@@ -382,7 +382,7 @@
+ unsigned long flags;
+ unsigned char x;
+
+- schedule_timeout_interruptible(1);
++ schedule_timeout(1);
+
+ spin_lock_irqsave(&s->lock, flags);
+ x = inb(HOST_DATA_IO(s->io_base));
+@@ -409,7 +409,7 @@
+ unsigned long flags;
+ unsigned char x;
+
+- schedule_timeout_interruptible(1);
++ schedule_timeout(1);
+
+ spin_lock_irqsave(&s->lock, flags);
+ x = inb(HOST_DATA_IO(s->io_base));
+--- linux-2.6.22.1.orig/sound/isa/wavefront/wavefront_synth.c
++++ linux-2.6.22.1/sound/isa/wavefront/wavefront_synth.c
+@@ -1780,7 +1780,7 @@
+ outb (val,port);
+ spin_unlock_irq(&dev->irq_lock);
+ while (1) {
+- if ((timeout = schedule_timeout_interruptible(timeout)) == 0)
++ if ((timeout = schedule_timeout(timeout)) == 0)
+ return;
+ if (dev->irq_ok)
+ return;
+--- linux-2.6.22.1.orig/sound/pci/Kconfig
++++ linux-2.6.22.1/sound/pci/Kconfig
+@@ -33,6 +33,7 @@
+ select SND_OPL3_LIB
+ select SND_MPU401_UART
+ select SND_PCM
++ select SND_SB_COMMON
+ help
+ Say Y here to include support for soundcards based on Avance Logic
+ ALS4000 chips.
+@@ -215,6 +216,16 @@
+
+ This works better than the old code, so say Y.
+
++config SND_CS5530
++ tristate "CS5530 Audio"
++ depends on SND && ISA_DMA_API
++ select SND_SB16_DSP
++ help
++ Say Y here to include support for audio on Cyrix/NatSemi CS5530 chips.
++
++ To compile this driver as a module, choose M here: the module
++ will be called snd-cs5530.
++
+ config SND_CS5535AUDIO
+ tristate "CS5535/CS5536 Audio"
+ depends on SND && X86 && !X86_64
+--- linux-2.6.22.1.orig/sound/pci/Makefile
++++ linux-2.6.22.1/sound/pci/Makefile
+@@ -12,6 +12,7 @@
+ snd-bt87x-objs := bt87x.o
+ snd-cmipci-objs := cmipci.o
+ snd-cs4281-objs := cs4281.o
++snd-cs5530-objs := cs5530.o
+ snd-ens1370-objs := ens1370.o
+ snd-ens1371-objs := ens1371.o
+ snd-es1938-objs := es1938.o
+@@ -36,6 +37,7 @@
+ obj-$(CONFIG_SND_BT87X) += snd-bt87x.o
+ obj-$(CONFIG_SND_CMIPCI) += snd-cmipci.o
+ obj-$(CONFIG_SND_CS4281) += snd-cs4281.o
++obj-$(CONFIG_SND_CS5530) += snd-cs5530.o
+ obj-$(CONFIG_SND_ENS1370) += snd-ens1370.o
+ obj-$(CONFIG_SND_ENS1371) += snd-ens1371.o
+ obj-$(CONFIG_SND_ES1938) += snd-es1938.o
+--- linux-2.6.22.1.orig/sound/pci/ali5451/ali5451.c
++++ linux-2.6.22.1/sound/pci/ali5451/ali5451.c
+@@ -239,7 +239,7 @@
+
+
+ struct snd_ali {
+- unsigned long irq;
++ int irq;
+ unsigned long port;
+ unsigned char revision;
+
+@@ -731,8 +731,7 @@
+ return;
+ }
+
+- count = 0;
+- while (count++ <= 50000) {
++ for (count = 0; count <= 50000; count++) {
+ snd_ali_delay(codec, 6);
+ bval = inb(ALI_REG(codec,ALI_SPDIF_CTRL + 1));
+ R2 = bval & 0x1F;
+@@ -2343,7 +2342,7 @@
+ strcpy(card->driver, "ALI5451");
+ strcpy(card->shortname, "ALI 5451");
+
+- sprintf(card->longname, "%s at 0x%lx, irq %li",
++ sprintf(card->longname, "%s at 0x%lx, irq %i",
+ card->shortname, codec->port, codec->irq);
+
+ snd_ali_printk("register card.\n");
+--- linux-2.6.22.1.orig/sound/pci/als300.c
++++ linux-2.6.22.1/sound/pci/als300.c
+@@ -88,8 +88,8 @@
+ #define PLAYBACK_BLOCK_COUNTER 0x9A
+ #define RECORD_BLOCK_COUNTER 0x9B
+
+-#define DEBUG_CALLS 1
+-#define DEBUG_PLAY_REC 1
++#define DEBUG_CALLS 0
++#define DEBUG_PLAY_REC 0
+
+ #if DEBUG_CALLS
+ #define snd_als300_dbgcalls(format, args...) printk(format, ##args)
+@@ -733,7 +733,8 @@
+
+ snd_als300_init(chip);
+
+- if (snd_als300_ac97(chip) < 0) {
++ err = snd_als300_ac97(chip);
++ if (err < 0) {
+ snd_printk(KERN_WARNING "Could not create ac97\n");
+ snd_als300_free(chip);
+ return err;
+--- linux-2.6.22.1.orig/sound/pci/ca0106/ca0106_main.c
++++ linux-2.6.22.1/sound/pci/ca0106/ca0106_main.c
+@@ -168,6 +168,25 @@
+ #include "ca0106.h"
+
+ static struct snd_ca0106_details ca0106_chip_details[] = {
++ /* Sound Blaster X-Fi Extreme Audio. This does not have an AC97. 53SB079000000 */
++ /* It is really just a normal SB Live 24bit. */
++ /*
++ * CTRL:CA0111-WTLF
++ * ADC: WM8775SEDS
++ * DAC: CS4382-KQZ
++ */
++ /* Tested:
++ * Playback on front, rear, center/lfe speakers
++ * Capture from Mic in.
++ * Not-Tested:
++ * Capture from Line in.
++ * Playback to digital out.
++ */
++ { .serial = 0x10121102,
++ .name = "X-Fi Extreme Audio [SB0790]",
++ .gpio_type = 1,
++ .i2c_adc = 1 } ,
++ /* New Dell Sound Blaster Live! 7.1 24bit. This does not have an AC97. */
+ /* AudigyLS[SB0310] */
+ { .serial = 0x10021102,
+ .name = "AudigyLS [SB0310]",
+--- linux-2.6.22.1.orig/sound/pci/cs46xx/cs46xx_lib.c
++++ linux-2.6.22.1/sound/pci/cs46xx/cs46xx_lib.c
+@@ -2897,6 +2897,10 @@
+ }
+ #endif
+
++#ifdef CONFIG_PM
++ kfree(chip->saved_regs);
++#endif
++
+ pci_disable_device(chip->pci);
+ kfree(chip);
+ return 0;
+@@ -3140,6 +3144,23 @@
+ /*
+ * start and load DSP
+ */
++
++static void cs46xx_enable_stream_irqs(struct snd_cs46xx *chip)
++{
++ unsigned int tmp;
++
++ snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_IEV | HICR_CHGM);
++
++ tmp = snd_cs46xx_peek(chip, BA1_PFIE);
++ tmp &= ~0x0000f03f;
++ snd_cs46xx_poke(chip, BA1_PFIE, tmp); /* playback interrupt enable */
++
++ tmp = snd_cs46xx_peek(chip, BA1_CIE);
++ tmp &= ~0x0000003f;
++ tmp |= 0x00000001;
++ snd_cs46xx_poke(chip, BA1_CIE, tmp); /* capture interrupt enable */
++}
++
+ int __devinit snd_cs46xx_start_dsp(struct snd_cs46xx *chip)
+ {
+ unsigned int tmp;
+@@ -3214,19 +3235,7 @@
+
+ snd_cs46xx_proc_start(chip);
+
+- /*
+- * Enable interrupts on the part.
+- */
+- snd_cs46xx_pokeBA0(chip, BA0_HICR, HICR_IEV | HICR_CHGM);
+-
+- tmp = snd_cs46xx_peek(chip, BA1_PFIE);
+- tmp &= ~0x0000f03f;
+- snd_cs46xx_poke(chip, BA1_PFIE, tmp); /* playback interrupt enable */
+-
+- tmp = snd_cs46xx_peek(chip, BA1_CIE);
+- tmp &= ~0x0000003f;
+- tmp |= 0x00000001;
+- snd_cs46xx_poke(chip, BA1_CIE, tmp); /* capture interrupt enable */
++ cs46xx_enable_stream_irqs(chip);
+
+ #ifndef CONFIG_SND_CS46XX_NEW_DSP
+ /* set the attenuation to 0dB */
+@@ -3665,11 +3674,19 @@
+ * APM support
+ */
+ #ifdef CONFIG_PM
++static unsigned int saved_regs[] = {
++ BA0_ACOSV,
++ BA0_ASER_FADDR,
++ BA0_ASER_MASTER,
++ BA1_PVOL,
++ BA1_CVOL,
++};
++
+ int snd_cs46xx_suspend(struct pci_dev *pci, pm_message_t state)
+ {
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_cs46xx *chip = card->private_data;
+- int amp_saved;
++ int i, amp_saved;
+
+ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ chip->in_suspend = 1;
+@@ -3680,6 +3697,10 @@
+ snd_ac97_suspend(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]);
+ snd_ac97_suspend(chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]);
+
++ /* save some registers */
++ for (i = 0; i < ARRAY_SIZE(saved_regs); i++)
++ chip->saved_regs[i] = snd_cs46xx_peekBA0(chip, saved_regs[i]);
++
+ amp_saved = chip->amplifier;
+ /* turn off amp */
+ chip->amplifier_ctrl(chip, -chip->amplifier);
+@@ -3698,7 +3719,7 @@
+ {
+ struct snd_card *card = pci_get_drvdata(pci);
+ struct snd_cs46xx *chip = card->private_data;
+- int amp_saved;
++ int i, amp_saved;
+
+ pci_set_power_state(pci, PCI_D0);
+ pci_restore_state(pci);
+@@ -3716,6 +3737,16 @@
+
+ snd_cs46xx_chip_init(chip);
+
++ snd_cs46xx_reset(chip);
++#ifdef CONFIG_SND_CS46XX_NEW_DSP
++ cs46xx_dsp_resume(chip);
++ /* restore some registers */
++ for (i = 0; i < ARRAY_SIZE(saved_regs); i++)
++ snd_cs46xx_pokeBA0(chip, saved_regs[i], chip->saved_regs[i]);
++#else
++ snd_cs46xx_download_image(chip);
++#endif
++
+ #if 0
+ snd_cs46xx_codec_write(chip, BA0_AC97_GENERAL_PURPOSE,
+ chip->ac97_general_purpose);
+@@ -3730,6 +3761,13 @@
+ snd_ac97_resume(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]);
+ snd_ac97_resume(chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]);
+
++ /* reset playback/capture */
++ snd_cs46xx_set_play_sample_rate(chip, 8000);
++ snd_cs46xx_set_capture_sample_rate(chip, 8000);
++ snd_cs46xx_proc_start(chip);
++
++ cs46xx_enable_stream_irqs(chip);
++
+ if (amp_saved)
+ chip->amplifier_ctrl(chip, 1); /* turn amp on */
+ else
+@@ -3896,6 +3934,15 @@
+
+ snd_cs46xx_proc_init(card, chip);
+
++#ifdef CONFIG_PM
++ chip->saved_regs = kmalloc(sizeof(*chip->saved_regs) *
++ ARRAY_SIZE(saved_regs), GFP_KERNEL);
++ if (!chip->saved_regs) {
++ snd_cs46xx_free(chip);
++ return -ENOMEM;
++ }
++#endif
++
+ chip->active_ctrl(chip, -1); /* disable CLKRUN */
+
+ snd_card_set_dev(card, &pci->dev);
+--- linux-2.6.22.1.orig/sound/pci/cs46xx/cs46xx_lib.h
++++ linux-2.6.22.1/sound/pci/cs46xx/cs46xx_lib.h
+@@ -86,6 +86,9 @@
+ struct dsp_spos_instance *cs46xx_dsp_spos_create (struct snd_cs46xx * chip);
+ void cs46xx_dsp_spos_destroy (struct snd_cs46xx * chip);
+ int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * module);
++#ifdef CONFIG_PM
++int cs46xx_dsp_resume(struct snd_cs46xx * chip);
++#endif
+ struct dsp_symbol_entry *cs46xx_dsp_lookup_symbol (struct snd_cs46xx * chip, char * symbol_name,
+ int symbol_type);
+ #ifdef CONFIG_PROC_FS
+--- linux-2.6.22.1.orig/sound/pci/cs46xx/dsp_spos.c
++++ linux-2.6.22.1/sound/pci/cs46xx/dsp_spos.c
+@@ -306,13 +306,59 @@
+ mutex_unlock(&chip->spos_mutex);
+ }
+
++static int dsp_load_parameter(struct snd_cs46xx *chip,
++ struct dsp_segment_desc *parameter)
++{
++ u32 doffset, dsize;
++
++ if (!parameter) {
++ snd_printdd("dsp_spos: module got no parameter segment\n");
++ return 0;
++ }
++
++ doffset = (parameter->offset * 4 + DSP_PARAMETER_BYTE_OFFSET);
++ dsize = parameter->size * 4;
++
++ snd_printdd("dsp_spos: "
++ "downloading parameter data to chip (%08x-%08x)\n",
++ doffset,doffset + dsize);
++ if (snd_cs46xx_download (chip, parameter->data, doffset, dsize)) {
++ snd_printk(KERN_ERR "dsp_spos: "
++ "failed to download parameter data to DSP\n");
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static int dsp_load_sample(struct snd_cs46xx *chip,
++ struct dsp_segment_desc *sample)
++{
++ u32 doffset, dsize;
++
++ if (!sample) {
++ snd_printdd("dsp_spos: module got no sample segment\n");
++ return 0;
++ }
++
++ doffset = (sample->offset * 4 + DSP_SAMPLE_BYTE_OFFSET);
++ dsize = sample->size * 4;
++
++ snd_printdd("dsp_spos: downloading sample data to chip (%08x-%08x)\n",
++ doffset,doffset + dsize);
++
++ if (snd_cs46xx_download (chip,sample->data,doffset,dsize)) {
++ snd_printk(KERN_ERR "dsp_spos: failed to sample data to DSP\n");
++ return -EINVAL;
++ }
++ return 0;
++}
++
+ int cs46xx_dsp_load_module (struct snd_cs46xx * chip, struct dsp_module_desc * module)
+ {
+ struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+ struct dsp_segment_desc * code = get_segment_desc (module,SEGTYPE_SP_PROGRAM);
+- struct dsp_segment_desc * parameter = get_segment_desc (module,SEGTYPE_SP_PARAMETER);
+- struct dsp_segment_desc * sample = get_segment_desc (module,SEGTYPE_SP_SAMPLE);
+ u32 doffset, dsize;
++ int err;
+
+ if (ins->nmodules == DSP_MAX_MODULES - 1) {
+ snd_printk(KERN_ERR "dsp_spos: to many modules loaded into DSP\n");
+@@ -326,49 +372,20 @@
+ snd_cs46xx_clear_BA1(chip, DSP_PARAMETER_BYTE_OFFSET, DSP_PARAMETER_BYTE_SIZE);
+ }
+
+- if (parameter == NULL) {
+- snd_printdd("dsp_spos: module got no parameter segment\n");
+- } else {
+- if (ins->nmodules > 0) {
+- snd_printk(KERN_WARNING "dsp_spos: WARNING current parameter data may be overwriten!\n");
+- }
+-
+- doffset = (parameter->offset * 4 + DSP_PARAMETER_BYTE_OFFSET);
+- dsize = parameter->size * 4;
+-
+- snd_printdd("dsp_spos: downloading parameter data to chip (%08x-%08x)\n",
+- doffset,doffset + dsize);
+-
+- if (snd_cs46xx_download (chip, parameter->data, doffset, dsize)) {
+- snd_printk(KERN_ERR "dsp_spos: failed to download parameter data to DSP\n");
+- return -EINVAL;
+- }
+- }
++ err = dsp_load_parameter(chip, get_segment_desc(module,
++ SEGTYPE_SP_PARAMETER));
++ if (err < 0)
++ return err;
+
+ if (ins->nmodules == 0) {
+ snd_printdd("dsp_spos: clearing sample area\n");
+ snd_cs46xx_clear_BA1(chip, DSP_SAMPLE_BYTE_OFFSET, DSP_SAMPLE_BYTE_SIZE);
+ }
+
+- if (sample == NULL) {
+- snd_printdd("dsp_spos: module got no sample segment\n");
+- } else {
+- if (ins->nmodules > 0) {
+- snd_printk(KERN_WARNING "dsp_spos: WARNING current sample data may be overwriten\n");
+- }
+-
+- doffset = (sample->offset * 4 + DSP_SAMPLE_BYTE_OFFSET);
+- dsize = sample->size * 4;
+-
+- snd_printdd("dsp_spos: downloading sample data to chip (%08x-%08x)\n",
+- doffset,doffset + dsize);
+-
+- if (snd_cs46xx_download (chip,sample->data,doffset,dsize)) {
+- snd_printk(KERN_ERR "dsp_spos: failed to sample data to DSP\n");
+- return -EINVAL;
+- }
+- }
+-
++ err = dsp_load_sample(chip, get_segment_desc(module,
++ SEGTYPE_SP_SAMPLE));
++ if (err < 0)
++ return err;
+
+ if (ins->nmodules == 0) {
+ snd_printdd("dsp_spos: clearing code area\n");
+@@ -986,7 +1003,10 @@
+ return NULL;
+ }
+
+- strcpy(ins->tasks[ins->ntask].task_name,name);
++ if (name)
++ strcpy(ins->tasks[ins->ntask].task_name, name);
++ else
++ strcpy(ins->tasks[ins->ntask].task_name, "(NULL)");
+ ins->tasks[ins->ntask].address = dest;
+ ins->tasks[ins->ntask].size = size;
+
+@@ -995,7 +1015,8 @@
+ desc = (ins->tasks + ins->ntask);
+ ins->ntask++;
+
+- add_symbol (chip,name,dest,SYMBOL_PARAMETER);
++ if (name)
++ add_symbol (chip,name,dest,SYMBOL_PARAMETER);
+ return desc;
+ }
+
+@@ -1006,6 +1027,7 @@
+
+ desc = _map_scb (chip,name,dest);
+ if (desc) {
++ desc->data = scb_data;
+ _dsp_create_scb(chip,scb_data,dest);
+ } else {
+ snd_printk(KERN_ERR "dsp_spos: failed to map SCB\n");
+@@ -1023,6 +1045,7 @@
+
+ desc = _map_task_tree (chip,name,dest,size);
+ if (desc) {
++ desc->data = task_data;
+ _dsp_create_task_tree(chip,task_data,dest,size);
+ } else {
+ snd_printk(KERN_ERR "dsp_spos: failed to map TASK\n");
+@@ -1320,8 +1343,10 @@
+ 0x0000ffff
+ };
+
+- /* dirty hack ... */
+- _dsp_create_task_tree (chip,(u32 *)&mix2_ostream_spb,WRITE_BACK_SPB,2);
++ if (!cs46xx_dsp_create_task_tree(chip, NULL,
++ (u32 *)&mix2_ostream_spb,
++ WRITE_BACK_SPB, 2))
++ goto _fail_end;
+ }
+
+ /* input sample converter */
+@@ -1622,7 +1647,6 @@
+ return 0;
+ }
+
+-
+ static void cs46xx_dsp_disable_spdif_hw (struct snd_cs46xx *chip)
+ {
+ struct dsp_spos_instance * ins = chip->dsp_spos_instance;
+@@ -1894,3 +1918,61 @@
+
+ return 0;
+ }
++
++#ifdef CONFIG_PM
++int cs46xx_dsp_resume(struct snd_cs46xx * chip)
++{
++ struct dsp_spos_instance * ins = chip->dsp_spos_instance;
++ int i, err;
++
++ /* clear parameter, sample and code areas */
++ snd_cs46xx_clear_BA1(chip, DSP_PARAMETER_BYTE_OFFSET,
++ DSP_PARAMETER_BYTE_SIZE);
++ snd_cs46xx_clear_BA1(chip, DSP_SAMPLE_BYTE_OFFSET,
++ DSP_SAMPLE_BYTE_SIZE);
++ snd_cs46xx_clear_BA1(chip, DSP_CODE_BYTE_OFFSET, DSP_CODE_BYTE_SIZE);
++
++ for (i = 0; i < ins->nmodules; i++) {
++ struct dsp_module_desc *module = &ins->modules[i];
++ struct dsp_segment_desc *seg;
++ u32 doffset, dsize;
++
++ seg = get_segment_desc(module, SEGTYPE_SP_PARAMETER);
++ err = dsp_load_parameter(chip, seg);
++ if (err < 0)
++ return err;
++
++ seg = get_segment_desc(module, SEGTYPE_SP_SAMPLE);
++ err = dsp_load_sample(chip, seg);
++ if (err < 0)
++ return err;
++
++ seg = get_segment_desc(module, SEGTYPE_SP_PROGRAM);
++ if (!seg)
++ continue;
++
++ doffset = seg->offset * 4 + module->load_address * 4
++ + DSP_CODE_BYTE_OFFSET;
++ dsize = seg->size * 4;
++ err = snd_cs46xx_download(chip,
++ ins->code.data + module->load_address,
++ doffset, dsize);
++ if (err < 0)
++ return err;
++ }
++
++ for (i = 0; i < ins->ntask; i++) {
++ struct dsp_task_descriptor *t = &ins->tasks[i];
++ _dsp_create_task_tree(chip, t->data, t->address, t->size);
++ }
++
++ for (i = 0; i < ins->nscb; i++) {
++ struct dsp_scb_descriptor *s = &ins->scbs[i];
++ if (s->deleted)
++ continue;
++ _dsp_create_scb(chip, s->data, s->address);
++ }
++
++ return 0;
++}
++#endif
+--- /dev/null
++++ linux-2.6.22.1/sound/pci/cs5530.c
+@@ -0,0 +1,306 @@
++/*
++ * cs5530.c - Initialisation code for Cyrix/NatSemi VSA1 softaudio
++ *
++ * (C) Copyright 2007 Ash Willis <ashwillis at programmer.net>
++ * (C) Copyright 2003 Red Hat Inc <alan at redhat.com>
++ *
++ * This driver was ported (shamelessly ripped ;) from oss/kahlua.c but I did
++ * mess with it a bit. The chip seems to have to have trouble with full duplex
++ * mode. If we're recording in 8bit 8000kHz, say, and we then attempt to
++ * simultaneously play back audio at 16bit 44100kHz, the device actually plays
++ * back in the same format in which it is capturing. By forcing the chip to
++ * always play/capture in 16/44100, we can let alsa-lib convert the samples and
++ * that way we can hack up some full duplex audio.
++ *
++ * XpressAudio(tm) is used on the Cyrix MediaGX (now NatSemi Geode) systems.
++ * The older version (VSA1) provides fairly good soundblaster emulation
++ * although there are a couple of bugs: large DMA buffers break record,
++ * and the MPU event handling seems suspect. VSA2 allows the native driver
++ * to control the AC97 audio engine directly and requires a different driver.
++ *
++ * Thanks to National Semiconductor for providing the needed information
++ * on the XpressAudio(tm) internals.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2, or (at your option) any
++ * later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * TO DO:
++ * Investigate whether we can portably support Cognac (5520) in the
++ * same manner.
++ */
++
++#include <sound/driver.h>
++#include <linux/delay.h>
++#include <linux/moduleparam.h>
++#include <linux/pci.h>
++#include <sound/core.h>
++#include <sound/sb.h>
++#include <sound/initval.h>
++
++MODULE_AUTHOR("Ash Willis");
++MODULE_DESCRIPTION("CS5530 Audio");
++MODULE_LICENSE("GPL");
++
++static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
++static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
++static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
++
++struct snd_cs5530 {
++ struct snd_card *card;
++ struct pci_dev *pci;
++ struct snd_sb *sb;
++ unsigned long pci_base;
++};
++
++static struct pci_device_id snd_cs5530_ids[] = {
++ {PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_AUDIO, PCI_ANY_ID,
++ PCI_ANY_ID, 0, 0},
++ {0,}
++};
++
++MODULE_DEVICE_TABLE(pci, snd_cs5530_ids);
++
++static int snd_cs5530_free(struct snd_cs5530 *chip)
++{
++ pci_release_regions(chip->pci);
++ pci_disable_device(chip->pci);
++ kfree(chip);
++ return 0;
++}
++
++static int snd_cs5530_dev_free(struct snd_device *device)
++{
++ struct snd_cs5530 *chip = device->device_data;
++ return snd_cs5530_free(chip);
++}
++
++static void __devexit snd_cs5530_remove(struct pci_dev *pci)
++{
++ snd_card_free(pci_get_drvdata(pci));
++ pci_set_drvdata(pci, NULL);
++}
++
++static u8 __devinit snd_cs5530_mixer_read(unsigned long io, u8 reg)
++{
++ outb(reg, io + 4);
++ udelay(20);
++ reg = inb(io + 5);
++ udelay(20);
++ return reg;
++}
++
++static int __devinit snd_cs5530_create(struct snd_card *card,
++ struct pci_dev *pci,
++ struct snd_cs5530 **rchip)
++{
++ struct snd_cs5530 *chip;
++ unsigned long sb_base;
++ u8 irq, dma8, dma16 = 0;
++ u16 map;
++ void __iomem *mem;
++ int err;
++
++ static struct snd_device_ops ops = {
++ .dev_free = snd_cs5530_dev_free,
++ };
++ *rchip = NULL;
++
++ err = pci_enable_device(pci);
++ if (err < 0)
++ return err;
++
++ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
++ if (chip == NULL) {
++ pci_disable_device(pci);
++ return -ENOMEM;
++ }
++
++ chip->card = card;
++ chip->pci = pci;
++
++ err = pci_request_regions(pci, "CS5530");
++ if (err < 0) {
++ kfree(chip);
++ pci_disable_device(pci);
++ return err;
++ }
++ chip->pci_base = pci_resource_start(pci, 0);
++
++ mem = ioremap_nocache(chip->pci_base, pci_resource_len(pci, 0));
++ if (mem == NULL) {
++ kfree(chip);
++ pci_disable_device(pci);
++ return -EBUSY;
++ }
++
++ map = readw(mem + 0x18);
++ iounmap(mem);
++
++ /* Map bits
++ 0:1 * 0x20 + 0x200 = sb base
++ 2 sb enable
++ 3 adlib enable
++ 5 MPU enable 0x330
++ 6 MPU enable 0x300
++
++ The other bits may be used internally so must be masked */
++
++ sb_base = 0x220 + 0x20 * (map & 3);
++
++ if (map & (1<<2))
++ printk(KERN_INFO "CS5530: XpressAudio at 0x%lx\n", sb_base);
++ else {
++ printk(KERN_ERR "Could not find XpressAudio!\n");
++ snd_cs5530_free(chip);
++ return -ENODEV;
++ }
++
++ if (map & (1<<5))
++ printk(KERN_INFO "CS5530: MPU at 0x300\n");
++ else if (map & (1<<6))
++ printk(KERN_INFO "CS5530: MPU at 0x330\n");
++
++ irq = snd_cs5530_mixer_read(sb_base, 0x80) & 0x0F;
++ dma8 = snd_cs5530_mixer_read(sb_base, 0x81);
++
++ if (dma8 & 0x20)
++ dma16 = 5;
++ else if (dma8 & 0x40)
++ dma16 = 6;
++ else if (dma8 & 0x80)
++ dma16 = 7;
++ else {
++ printk(KERN_ERR "CS5530: No 16bit DMA enabled\n");
++ snd_cs5530_free(chip);
++ return -ENODEV;
++ }
++
++ if (dma8 & 0x01)
++ dma8 = 0;
++ else if (dma8 & 02)
++ dma8 = 1;
++ else if (dma8 & 0x08)
++ dma8 = 3;
++ else {
++ printk(KERN_ERR "CS5530: No 8bit DMA enabled\n");
++ snd_cs5530_free(chip);
++ return -ENODEV;
++ }
++
++ if (irq & 1)
++ irq = 9;
++ else if (irq & 2)
++ irq = 5;
++ else if (irq & 4)
++ irq = 7;
++ else if (irq & 8)
++ irq = 10;
++ else {
++ printk(KERN_ERR "CS5530: SoundBlaster IRQ not set\n");
++ snd_cs5530_free(chip);
++ return -ENODEV;
++ }
++
++ printk(KERN_INFO "CS5530: IRQ: %d DMA8: %d DMA16: %d\n", irq, dma8,
++ dma16);
++
++ err = snd_sbdsp_create(card, sb_base, irq, snd_sb16dsp_interrupt, dma8,
++ dma16, SB_HW_CS5530, &chip->sb);
++ if (err < 0) {
++ printk(KERN_ERR "CS5530: Could not create SoundBlaster\n");
++ snd_cs5530_free(chip);
++ return err;
++ }
++
++ err = snd_sb16dsp_pcm(chip->sb, 0, &chip->sb->pcm);
++ if (err < 0) {
++ printk(KERN_ERR "CS5530: Could not create PCM\n");
++ snd_cs5530_free(chip);
++ return err;
++ }
++
++ err = snd_sbmixer_new(chip->sb);
++ if (err < 0) {
++ printk(KERN_ERR "CS5530: Could not create Mixer\n");
++ snd_cs5530_free(chip);
++ return err;
++ }
++
++ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
++ if (err < 0) {
++ snd_cs5530_free(chip);
++ return err;
++ }
++
++ snd_card_set_dev(card, &pci->dev);
++ *rchip = chip;
++ return 0;
++}
++
++static int __devinit snd_cs5530_probe(struct pci_dev *pci,
++ const struct pci_device_id *pci_id)
++{
++ static int dev;
++ struct snd_card *card;
++ struct snd_cs5530 *chip = NULL;
++ int err;
++
++ if (dev >= SNDRV_CARDS)
++ return -ENODEV;
++ if (!enable[dev]) {
++ dev++;
++ return -ENOENT;
++ }
++
++ card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
++
++ if (card == NULL)
++ return -ENOMEM;
++
++ err = snd_cs5530_create(card, pci, &chip);
++ if (err < 0) {
++ snd_card_free(card);
++ return err;
++ }
++
++ strcpy(card->driver, "CS5530");
++ strcpy(card->shortname, "CS5530 Audio");
++ sprintf(card->longname, "%s at 0x%lx", card->shortname, chip->pci_base);
++
++ err = snd_card_register(card);
++ if (err < 0) {
++ snd_card_free(card);
++ return err;
++ }
++ pci_set_drvdata(pci, card);
++ dev++;
++ return 0;
++}
++
++static struct pci_driver driver = {
++ .name = "CS5530_Audio",
++ .id_table = snd_cs5530_ids,
++ .probe = snd_cs5530_probe,
++ .remove = __devexit_p(snd_cs5530_remove),
++};
++
++static int __init alsa_card_cs5530_init(void)
++{
++ return pci_register_driver(&driver);
++}
++
++static void __exit alsa_card_cs5530_exit(void)
++{
++ pci_unregister_driver(&driver);
++}
++
++module_init(alsa_card_cs5530_init)
++module_exit(alsa_card_cs5530_exit)
++
+--- linux-2.6.22.1.orig/sound/pci/emu10k1/emu10k1_main.c
++++ linux-2.6.22.1/sound/pci/emu10k1/emu10k1_main.c
+@@ -51,9 +51,15 @@
+
+ #define HANA_FILENAME "emu/hana.fw"
+ #define DOCK_FILENAME "emu/audio_dock.fw"
++#define EMU1010B_FILENAME "emu/emu1010b.fw"
++#define MICRO_DOCK_FILENAME "emu/micro_dock.fw"
++#define EMU1010_NOTEBOOK_FILENAME "emu/emu1010_notebook.fw"
+
+ MODULE_FIRMWARE(HANA_FILENAME);
+ MODULE_FIRMWARE(DOCK_FILENAME);
++MODULE_FIRMWARE(EMU1010B_FILENAME);
++MODULE_FIRMWARE(MICRO_DOCK_FILENAME);
++MODULE_FIRMWARE(EMU1010_NOTEBOOK_FILENAME);
+
+
+ /*************************************************************************
+@@ -660,10 +666,12 @@
+ return err;
+ }
+ snd_printk(KERN_INFO "firmware size=0x%zx\n", fw_entry->size);
++#if 0
+ if (fw_entry->size != 0x133a4) {
+ snd_printk(KERN_ERR "firmware: %s wrong size.\n",filename);
+ return -EINVAL;
+ }
++#endif
+
+ /* The FPGA is a Xilinx Spartan IIE XC2S50E */
+ /* GPIO7 -> FPGA PGMN
+@@ -694,6 +702,37 @@
+ return 0;
+ }
+
++/*
++ * EMU-1010 - details found out from this driver, official MS Win drivers,
++ * testing the card:
++ *
++ * Audigy2 (aka Alice2):
++ * ---------------------
++ * * communication over PCI
++ * * conversion of 32-bit data coming over EMU32 links from HANA FPGA
++ * to 2 x 16-bit, using internal DSP instructions
++ * * slave mode, clock supplied by HANA
++ * * linked to HANA using:
++ * 32 x 32-bit serial EMU32 output channels
++ * 16 x EMU32 input channels
++ * (?) x I2S I/O channels (?)
++ *
++ * FPGA (aka HANA):
++ * ---------------
++ * * provides all (?) physical inputs and outputs of the card
++ * (ADC, DAC, SPDIF I/O, ADAT I/O, etc.)
++ * * provides clock signal for the card and Alice2
++ * * two crystals - for 44.1kHz and 48kHz multiples
++ * * provides internal routing of signal sources to signal destinations
++ * * inputs/outputs to Alice2 - see above
++ *
++ * Current status of the driver:
++ * ----------------------------
++ * * only 44.1/48kHz supported (the MS Win driver supports up to 192 kHz)
++ * * PCM device nb. 2:
++ * 16 x 16-bit playback - snd_emu10k1_fx8010_playback_ops
++ * 16 x 32-bit capture - snd_emu10k1_capture_efx_ops
++ */
+ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 * emu)
+ {
+ unsigned int i;
+@@ -727,7 +766,7 @@
+ /* ID, should read & 0x7f = 0x55. (Bit 7 is the IRQ bit) */
+ snd_emu1010_fpga_read(emu, EMU_HANA_ID, ® );
+ snd_printdd("reg1=0x%x\n",reg);
+- if (reg == 0x55) {
++ if ((reg & 0x3f) == 0x15) {
+ /* FPGA netlist already present so clear it */
+ /* Return to programming mode */
+
+@@ -735,19 +774,32 @@
+ }
+ snd_emu1010_fpga_read(emu, EMU_HANA_ID, ® );
+ snd_printdd("reg2=0x%x\n",reg);
+- if (reg == 0x55) {
++ if ((reg & 0x3f) == 0x15) {
+ /* FPGA failed to return to programming mode */
++ snd_printk(KERN_INFO "emu1010: FPGA failed to return to programming mode\n");
+ return -ENODEV;
+ }
+ snd_printk(KERN_INFO "emu1010: EMU_HANA_ID=0x%x\n",reg);
+- if ((err = snd_emu1010_load_firmware(emu, HANA_FILENAME)) != 0) {
+- snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file %s failed\n", HANA_FILENAME);
+- return err;
++ if (emu->card_capabilities->emu1010 == 1) {
++ if ((err = snd_emu1010_load_firmware(emu, HANA_FILENAME)) != 0) {
++ snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file %s failed\n", HANA_FILENAME);
++ return err;
++ }
++ } else if (emu->card_capabilities->emu1010 == 2) {
++ if ((err = snd_emu1010_load_firmware(emu, EMU1010B_FILENAME)) != 0) {
++ snd_printk(KERN_INFO "emu1010: Loading Firmware file %s failed\n", EMU1010B_FILENAME);
++ return err;
++ }
++ } else if (emu->card_capabilities->emu1010 == 3) {
++ if ((err = snd_emu1010_load_firmware(emu, EMU1010_NOTEBOOK_FILENAME)) != 0) {
++ snd_printk(KERN_INFO "emu1010: Loading Firmware file %s failed\n", EMU1010_NOTEBOOK_FILENAME);
++ return err;
++ }
+ }
+
+ /* ID, should read & 0x7f = 0x55 when FPGA programmed. */
+ snd_emu1010_fpga_read(emu, EMU_HANA_ID, ® );
+- if (reg != 0x55) {
++ if ((reg & 0x3f) != 0x15) {
+ /* FPGA failed to be programmed */
+ snd_printk(KERN_INFO "emu1010: Loading Hana Firmware file failed, reg=0x%x\n", reg);
+ return -ENODEV;
+@@ -850,6 +902,27 @@
+ EMU_DST_ALICE2_EMU32_6, EMU_SRC_DOCK_ADC2_LEFT1);
+ snd_emu1010_fpga_link_dst_src_write(emu,
+ EMU_DST_ALICE2_EMU32_7, EMU_SRC_DOCK_ADC2_RIGHT1);
++ /* Pavel Hofman - setting defaults for 8 more capture channels
++ * Defaults only, users will set their own values anyways, let's
++ * just copy/paste.
++ */
++
++ snd_emu1010_fpga_link_dst_src_write(emu,
++ EMU_DST_ALICE2_EMU32_8, EMU_SRC_DOCK_MIC_A1);
++ snd_emu1010_fpga_link_dst_src_write(emu,
++ EMU_DST_ALICE2_EMU32_9, EMU_SRC_DOCK_MIC_B1);
++ snd_emu1010_fpga_link_dst_src_write(emu,
++ EMU_DST_ALICE2_EMU32_A, EMU_SRC_HAMOA_ADC_LEFT2);
++ snd_emu1010_fpga_link_dst_src_write(emu,
++ EMU_DST_ALICE2_EMU32_B, EMU_SRC_HAMOA_ADC_LEFT2);
++ snd_emu1010_fpga_link_dst_src_write(emu,
++ EMU_DST_ALICE2_EMU32_C, EMU_SRC_DOCK_ADC1_LEFT1);
++ snd_emu1010_fpga_link_dst_src_write(emu,
++ EMU_DST_ALICE2_EMU32_D, EMU_SRC_DOCK_ADC1_RIGHT1);
++ snd_emu1010_fpga_link_dst_src_write(emu,
++ EMU_DST_ALICE2_EMU32_E, EMU_SRC_DOCK_ADC2_LEFT1);
++ snd_emu1010_fpga_link_dst_src_write(emu,
++ EMU_DST_ALICE2_EMU32_F, EMU_SRC_DOCK_ADC2_RIGHT1);
+ #endif
+ #if 0
+ /* Original */
+@@ -943,16 +1016,27 @@
+ /* Return to Audio Dock programming mode */
+ snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware\n");
+ snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, EMU_HANA_FPGA_CONFIG_AUDIODOCK );
+- if ((err = snd_emu1010_load_firmware(emu, DOCK_FILENAME)) != 0) {
+- return err;
++ if (emu->card_capabilities->emu1010 == 1) {
++ if ((err = snd_emu1010_load_firmware(emu, DOCK_FILENAME)) != 0) {
++ return err;
++ }
++ } else if (emu->card_capabilities->emu1010 == 2) {
++ if ((err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME)) != 0) {
++ return err;
++ }
++ } else if (emu->card_capabilities->emu1010 == 3) {
++ if ((err = snd_emu1010_load_firmware(emu, MICRO_DOCK_FILENAME)) != 0) {
++ return err;
++ }
+ }
++
+ snd_emu1010_fpga_write(emu, EMU_HANA_FPGA_CONFIG, 0 );
+ snd_emu1010_fpga_read(emu, EMU_HANA_IRQ_STATUS, ® );
+ snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_IRQ_STATUS=0x%x\n",reg);
+ /* ID, should read & 0x7f = 0x55 when FPGA programmed. */
+ snd_emu1010_fpga_read(emu, EMU_HANA_ID, ® );
+ snd_printk(KERN_INFO "emu1010: EMU_HANA+DOCK_ID=0x%x\n",reg);
+- if (reg != 0x55) {
++ if ((reg & 0x3f) != 0x15) {
+ /* FPGA failed to be programmed */
+ snd_printk(KERN_INFO "emu1010: Loading Audio Dock Firmware file failed, reg=0x%x\n", reg);
+ return 0;
+@@ -1227,9 +1311,15 @@
+ .emu10k2_chip = 1,
+ .ca0108_chip = 1,
+ .ca_cardbus_chip = 1,
+- .spi_dac = 1,
+- .i2c_adc = 1,
+- .spk71 = 1} ,
++ .spk71 = 1 ,
++ .emu1010 = 3} ,
++ {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x40041102,
++ .driver = "Audigy2", .name = "E-mu 1010b PCI [MAEM????]",
++ .id = "EMU1010",
++ .emu10k2_chip = 1,
++ .ca0108_chip = 1,
++ .spk71 = 1 ,
++ .emu1010 = 2} ,
+ {.vendor = 0x1102, .device = 0x0008,
+ .driver = "Audigy2", .name = "Audigy 2 Value [Unknown]",
+ .id = "Audigy2",
+@@ -1665,12 +1755,13 @@
+ emu->fx8010.extout_mask = extout_mask;
+ emu->enable_ir = enable_ir;
+
++ if (emu->card_capabilities->ca_cardbus_chip) {
++ if ((err = snd_emu10k1_cardbus_init(emu)) < 0)
++ goto error;
++ }
+ if (emu->card_capabilities->ecard) {
+ if ((err = snd_emu10k1_ecard_init(emu)) < 0)
+ goto error;
+- } else if (emu->card_capabilities->ca_cardbus_chip) {
+- if ((err = snd_emu10k1_cardbus_init(emu)) < 0)
+- goto error;
+ } else if (emu->card_capabilities->emu1010) {
+ if ((err = snd_emu10k1_emu1010_init(emu)) < 0) {
+ snd_emu10k1_free(emu);
+@@ -1816,10 +1907,10 @@
+
+ void snd_emu10k1_resume_init(struct snd_emu10k1 *emu)
+ {
++ if (emu->card_capabilities->ca_cardbus_chip)
++ snd_emu10k1_cardbus_init(emu);
+ if (emu->card_capabilities->ecard)
+ snd_emu10k1_ecard_init(emu);
+- else if (emu->card_capabilities->ca_cardbus_chip)
+- snd_emu10k1_cardbus_init(emu);
+ else if (emu->card_capabilities->emu1010)
+ snd_emu10k1_emu1010_init(emu);
+ else
+--- linux-2.6.22.1.orig/sound/pci/emu10k1/emufx.c
++++ linux-2.6.22.1/sound/pci/emu10k1/emufx.c
+@@ -1123,6 +1123,11 @@
+ ctl->translation = EMU10K1_GPR_TRANSLATION_ONOFF;
+ }
+
++/*
++ * Used for emu1010 - conversion from 32-bit capture inputs from HANA
++ * to 2 x 16-bit registers in audigy - their values are read via DMA.
++ * Conversion is performed by Audigy DSP instructions of FX8010.
++ */
+ static int snd_emu10k1_audigy_dsp_convert_32_to_2x16(
+ struct snd_emu10k1_fx8010_code *icode,
+ u32 *ptr, int tmp, int bit_shifter16,
+@@ -1193,7 +1198,11 @@
+ snd_emu10k1_ptr_write(emu, A_DBG, 0, (emu->fx8010.dbg = 0) | A_DBG_SINGLE_STEP);
+
+ #if 1
+- /* PCM front Playback Volume (independent from stereo mix) */
++ /* PCM front Playback Volume (independent from stereo mix)
++ * playback = 0 + ( gpr * FXBUS_PCM_LEFT_FRONT >> 31)
++ * where gpr contains attenuation from corresponding mixer control
++ * (snd_emu10k1_init_stereo_control)
++ */
+ A_OP(icode, &ptr, iMAC0, A_GPR(playback), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT_FRONT));
+ A_OP(icode, &ptr, iMAC0, A_GPR(playback+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT_FRONT));
+ snd_emu10k1_init_stereo_control(&controls[nctl++], "PCM Front Playback Volume", gpr, 100);
+@@ -1549,7 +1558,7 @@
+
+ if (emu->card_capabilities->emu1010) {
+ snd_printk("EMU inputs on\n");
+- /* Capture 8 channels of S32_LE sound */
++ /* Capture 16 (originally 8) channels of S32_LE sound */
+
+ /* printk("emufx.c: gpr=0x%x, tmp=0x%x\n",gpr, tmp); */
+ /* For the EMU1010: How to get 32bit values from the DSP. High 16bits into L, low 16bits into R. */
+@@ -1560,6 +1569,11 @@
+ snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_P16VIN(0x0), A_FXBUS2(0) );
+ /* Right ADC in 1 of 2 */
+ gpr_map[gpr++] = 0x00000000;
++ /* Delaying by one sample: instead of copying the input
++ * value A_P16VIN to output A_FXBUS2 as in the first channel,
++ * we use an auxiliary register, delaying the value by one
++ * sample
++ */
+ snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(2) );
+ A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x1), A_C_00000000, A_C_00000000);
+ gpr_map[gpr++] = 0x00000000;
+@@ -1583,6 +1597,66 @@
+ gpr_map[gpr++] = 0x00000000;
+ snd_emu10k1_audigy_dsp_convert_32_to_2x16( icode, &ptr, tmp, bit_shifter16, A_GPR(gpr - 1), A_FXBUS2(0xe) );
+ A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x7), A_C_00000000, A_C_00000000);
++ /* Pavel Hofman - we still have voices, A_FXBUS2s, and
++ * A_P16VINs available -
++ * let's add 8 more capture channels - total of 16
++ */
++ gpr_map[gpr++] = 0x00000000;
++ snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
++ bit_shifter16,
++ A_GPR(gpr - 1),
++ A_FXBUS2(0x10));
++ A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x8),
++ A_C_00000000, A_C_00000000);
++ gpr_map[gpr++] = 0x00000000;
++ snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
++ bit_shifter16,
++ A_GPR(gpr - 1),
++ A_FXBUS2(0x12));
++ A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0x9),
++ A_C_00000000, A_C_00000000);
++ gpr_map[gpr++] = 0x00000000;
++ snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
++ bit_shifter16,
++ A_GPR(gpr - 1),
++ A_FXBUS2(0x14));
++ A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xa),
++ A_C_00000000, A_C_00000000);
++ gpr_map[gpr++] = 0x00000000;
++ snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
++ bit_shifter16,
++ A_GPR(gpr - 1),
++ A_FXBUS2(0x16));
++ A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xb),
++ A_C_00000000, A_C_00000000);
++ gpr_map[gpr++] = 0x00000000;
++ snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
++ bit_shifter16,
++ A_GPR(gpr - 1),
++ A_FXBUS2(0x18));
++ A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xc),
++ A_C_00000000, A_C_00000000);
++ gpr_map[gpr++] = 0x00000000;
++ snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
++ bit_shifter16,
++ A_GPR(gpr - 1),
++ A_FXBUS2(0x1a));
++ A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xd),
++ A_C_00000000, A_C_00000000);
++ gpr_map[gpr++] = 0x00000000;
++ snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
++ bit_shifter16,
++ A_GPR(gpr - 1),
++ A_FXBUS2(0x1c));
++ A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xe),
++ A_C_00000000, A_C_00000000);
++ gpr_map[gpr++] = 0x00000000;
++ snd_emu10k1_audigy_dsp_convert_32_to_2x16(icode, &ptr, tmp,
++ bit_shifter16,
++ A_GPR(gpr - 1),
++ A_FXBUS2(0x1e));
++ A_OP(icode, &ptr, iACC3, A_GPR(gpr - 1), A_P16VIN(0xf),
++ A_C_00000000, A_C_00000000);
+
+ #if 0
+ for (z = 4; z < 8; z++) {
+--- linux-2.6.22.1.orig/sound/pci/emu10k1/emumixer.c
++++ linux-2.6.22.1/sound/pci/emu10k1/emumixer.c
+@@ -77,6 +77,10 @@
+ return 0;
+ }
+
++/*
++ * Items labels in enum mixer controls assigning source data to
++ * each destination
++ */
+ static char *emu1010_src_texts[] = {
+ "Silence",
+ "Dock Mic A",
+@@ -133,6 +137,9 @@
+ "DSP 31",
+ };
+
++/*
++ * List of data sources available for each destination
++ */
+ static unsigned int emu1010_src_regs[] = {
+ EMU_SRC_SILENCE,/* 0 */
+ EMU_SRC_DOCK_MIC_A1, /* 1 */
+@@ -189,6 +196,10 @@
+ EMU_SRC_ALICE_EMU32B+0xf, /* 52 */
+ };
+
++/*
++ * Data destinations - physical EMU outputs.
++ * Each destination has an enum mixer control to choose a data source
++ */
+ static unsigned int emu1010_output_dst[] = {
+ EMU_DST_DOCK_DAC1_LEFT1, /* 0 */
+ EMU_DST_DOCK_DAC1_RIGHT1, /* 1 */
+@@ -216,6 +227,11 @@
+ EMU_DST_HANA_ADAT+7, /* 23 */
+ };
+
++/*
++ * Data destinations - HANA outputs going to Alice2 (audigy) for
++ * capture (EMU32 + I2S links)
++ * Each destination has an enum mixer control to choose a data source
++ */
+ static unsigned int emu1010_input_dst[] = {
+ EMU_DST_ALICE2_EMU32_0,
+ EMU_DST_ALICE2_EMU32_1,
+--- linux-2.6.22.1.orig/sound/pci/emu10k1/emupcm.c
++++ linux-2.6.22.1/sound/pci/emu10k1/emupcm.c
+@@ -1233,24 +1233,26 @@
+ runtime->hw.rate_min = runtime->hw.rate_max = 48000;
+ spin_lock_irq(&emu->reg_lock);
+ if (emu->card_capabilities->emu1010) {
+- /* TODO
++ /* Nb. of channels has been increased to 16 */
++ /* TODO
+ * SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE
+ * SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+ * SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+ * SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000
+ * rate_min = 44100,
+ * rate_max = 192000,
+- * channels_min = 8,
+- * channels_max = 8,
++ * channels_min = 16,
++ * channels_max = 16,
+ * Need to add mixer control to fix sample rate
+ *
+- * There are 16 mono channels of 16bits each.
++ * There are 32 mono channels of 16bits each.
+ * 24bit Audio uses 2x channels over 16bit
+ * 96kHz uses 2x channels over 48kHz
+ * 192kHz uses 4x channels over 48kHz
+- * So, for 48kHz 24bit, one has 8 channels
+- * for 96kHz 24bit, one has 4 channels
+- * for 192kHz 24bit, one has 2 channels
++ * So, for 48kHz 24bit, one has 16 channels
++ * for 96kHz 24bit, one has 8 channels
++ * for 192kHz 24bit, one has 4 channels
++ *
+ */
+ #if 1
+ switch (emu->emu1010.internal_clock) {
+@@ -1258,13 +1260,15 @@
+ /* For 44.1kHz */
+ runtime->hw.rates = SNDRV_PCM_RATE_44100;
+ runtime->hw.rate_min = runtime->hw.rate_max = 44100;
+- runtime->hw.channels_min = runtime->hw.channels_max = 8;
++ runtime->hw.channels_min =
++ runtime->hw.channels_max = 16;
+ break;
+ case 1:
+ /* For 48kHz */
+ runtime->hw.rates = SNDRV_PCM_RATE_48000;
+ runtime->hw.rate_min = runtime->hw.rate_max = 48000;
+- runtime->hw.channels_min = runtime->hw.channels_max = 8;
++ runtime->hw.channels_min =
++ runtime->hw.channels_max = 16;
+ break;
+ };
+ #endif
+@@ -1282,7 +1286,7 @@
+ #endif
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
+ /* efx_voices_mask[0] is expected to be zero
+- * efx_voices_mask[1] is expected to have 16bits set
++ * efx_voices_mask[1] is expected to have 32bits set
+ */
+ } else {
+ runtime->hw.channels_min = runtime->hw.channels_max = 0;
+@@ -1787,11 +1791,24 @@
+ /* emu->efx_voices_mask[0] = FXWC_DEFAULTROUTE_C | FXWC_DEFAULTROUTE_A; */
+ if (emu->audigy) {
+ emu->efx_voices_mask[0] = 0;
+- emu->efx_voices_mask[1] = 0xffff;
++ if (emu->card_capabilities->emu1010)
++ /* Pavel Hofman - 32 voices will be used for
++ * capture (write mode) -
++ * each bit = corresponding voice
++ */
++ emu->efx_voices_mask[1] = 0xffffffff;
++ else
++ emu->efx_voices_mask[1] = 0xffff;
+ } else {
+ emu->efx_voices_mask[0] = 0xffff0000;
+ emu->efx_voices_mask[1] = 0;
+ }
++ /* For emu1010, the control has to set 32 upper bits (voices)
++ * out of the 64 bits (voices) to true for the 16-channels capture
++ * to work correctly. Correct A_FXWC2 initial value (0xffffffff)
++ * is already defined but the snd_emu10k1_pcm_efx_voices_mask
++ * control can override this register's value.
++ */
+ kctl = snd_ctl_new1(&snd_emu10k1_pcm_efx_voices_mask, emu);
+ if (!kctl)
+ return -ENOMEM;
+--- linux-2.6.22.1.orig/sound/pci/ens1370.c
++++ linux-2.6.22.1/sound/pci/ens1370.c
+@@ -1607,8 +1607,8 @@
+ unsigned char rev; /* revision */
+ };
+
+-static int __devinit es1371_quirk_lookup(struct ensoniq *ensoniq,
+- struct es1371_quirk *list)
++static int es1371_quirk_lookup(struct ensoniq *ensoniq,
++ struct es1371_quirk *list)
+ {
+ while (list->vid != (unsigned short)PCI_ANY_ID) {
+ if (ensoniq->pci->vendor == list->vid &&
+--- linux-2.6.22.1.orig/sound/pci/hda/hda_intel.c
++++ linux-2.6.22.1/sound/pci/hda/hda_intel.c
+@@ -341,6 +341,9 @@
+ unsigned int single_cmd :1;
+ unsigned int polling_mode :1;
+ unsigned int msi :1;
++
++ /* for debugging */
++ unsigned int last_cmd; /* last issued command (to sync) */
+ };
+
+ /* driver types */
+@@ -466,18 +469,10 @@
+ }
+
+ /* send a command */
+-static int azx_corb_send_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
+- unsigned int verb, unsigned int para)
++static int azx_corb_send_cmd(struct hda_codec *codec, u32 val)
+ {
+ struct azx *chip = codec->bus->private_data;
+ unsigned int wp;
+- u32 val;
+-
+- val = (u32)(codec->addr & 0x0f) << 28;
+- val |= (u32)direct << 27;
+- val |= (u32)nid << 20;
+- val |= verb << 8;
+- val |= para;
+
+ /* add command to corb */
+ wp = azx_readb(chip, CORBWP);
+@@ -538,12 +533,12 @@
+ }
+ if (! chip->rirb.cmds)
+ return chip->rirb.res; /* the last value */
+- schedule_timeout_interruptible(1);
++ schedule_timeout(1);
+ } while (time_after_eq(timeout, jiffies));
+
+ if (chip->msi) {
+ snd_printk(KERN_WARNING "hda_intel: No response from codec, "
+- "disabling MSI...\n");
++ "disabling MSI: last cmd=0x%08x\n", chip->last_cmd);
+ free_irq(chip->irq, chip);
+ chip->irq = -1;
+ pci_disable_msi(chip->pci);
+@@ -555,13 +550,15 @@
+
+ if (!chip->polling_mode) {
+ snd_printk(KERN_WARNING "hda_intel: azx_get_response timeout, "
+- "switching to polling mode...\n");
++ "switching to polling mode: last cmd=0x%08x\n",
++ chip->last_cmd);
+ chip->polling_mode = 1;
+ goto again;
+ }
+
+ snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, "
+- "switching to single_cmd mode...\n");
++ "switching to single_cmd mode: last cmd=0x%08x\n",
++ chip->last_cmd);
+ chip->rirb.rp = azx_readb(chip, RIRBWP);
+ chip->rirb.cmds = 0;
+ /* switch to single_cmd mode */
+@@ -581,20 +578,11 @@
+ */
+
+ /* send a command */
+-static int azx_single_send_cmd(struct hda_codec *codec, hda_nid_t nid,
+- int direct, unsigned int verb,
+- unsigned int para)
++static int azx_single_send_cmd(struct hda_codec *codec, u32 val)
+ {
+ struct azx *chip = codec->bus->private_data;
+- u32 val;
+ int timeout = 50;
+
+- val = (u32)(codec->addr & 0x0f) << 28;
+- val |= (u32)direct << 27;
+- val |= (u32)nid << 20;
+- val |= verb << 8;
+- val |= para;
+-
+ while (timeout--) {
+ /* check ICB busy bit */
+ if (! (azx_readw(chip, IRS) & ICH6_IRS_BUSY)) {
+@@ -639,10 +627,19 @@
+ unsigned int para)
+ {
+ struct azx *chip = codec->bus->private_data;
++ u32 val;
++
++ val = (u32)(codec->addr & 0x0f) << 28;
++ val |= (u32)direct << 27;
++ val |= (u32)nid << 20;
++ val |= verb << 8;
++ val |= para;
++ chip->last_cmd = val;
++
+ if (chip->single_cmd)
+- return azx_single_send_cmd(codec, nid, direct, verb, para);
++ return azx_single_send_cmd(codec, val);
+ else
+- return azx_corb_send_cmd(codec, nid, direct, verb, para);
++ return azx_corb_send_cmd(codec, val);
+ }
+
+ /* get a response */
+@@ -1788,6 +1785,12 @@
+ { 0x10de, 0x044b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP65 */
+ { 0x10de, 0x055c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP67 */
+ { 0x10de, 0x055d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP67 */
++ { 0x10de, 0x07fc, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP73 */
++ { 0x10de, 0x07fd, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP73 */
++ { 0x10de, 0x0774, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */
++ { 0x10de, 0x0775, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */
++ { 0x10de, 0x0776, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */
++ { 0x10de, 0x0777, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_NVIDIA }, /* NVIDIA MCP77 */
+ { 0, }
+ };
+ MODULE_DEVICE_TABLE(pci, azx_ids);
+--- linux-2.6.22.1.orig/sound/pci/hda/hda_proc.c
++++ linux-2.6.22.1/sound/pci/hda/hda_proc.c
+@@ -250,6 +250,12 @@
+ snd_iprintf(buffer, "Vendor Id: 0x%x\n", codec->vendor_id);
+ snd_iprintf(buffer, "Subsystem Id: 0x%x\n", codec->subsystem_id);
+ snd_iprintf(buffer, "Revision Id: 0x%x\n", codec->revision_id);
++
++ if (codec->mfg)
++ snd_iprintf(buffer, "Modem Function Group: 0x%x\n", codec->mfg);
++ else
++ snd_iprintf(buffer, "No Modem Function Group found\n");
++
+ if (! codec->afg)
+ return;
+ snd_iprintf(buffer, "Default PCM:\n");
+--- linux-2.6.22.1.orig/sound/pci/hda/patch_analog.c
++++ linux-2.6.22.1/sound/pci/hda/patch_analog.c
+@@ -1,7 +1,8 @@
+ /*
+- * HD audio interface patch for AD1981HD, AD1983, AD1986A, AD1988
++ * HD audio interface patch for AD1882, AD1884, AD1981HD, AD1983, AD1984,
++ * AD1986A, AD1988
+ *
+- * Copyright (c) 2005 Takashi Iwai <tiwai at suse.de>
++ * Copyright (c) 2005-2007 Takashi Iwai <tiwai at 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
+@@ -61,7 +62,7 @@
+ int num_channel_mode;
+
+ /* PCM information */
+- struct hda_pcm pcm_rec[2]; /* used in alc_build_pcms() */
++ struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */
+
+ struct mutex amp_mutex; /* PCM volume/mute control mutex */
+ unsigned int spdif_route;
+@@ -2775,11 +2776,634 @@
+
+
+ /*
++ * AD1884 / AD1984
++ *
++ * port-B - front line/mic-in
++ * port-E - aux in/out
++ * port-F - aux in/out
++ * port-C - rear line/mic-in
++ * port-D - rear line/hp-out
++ * port-A - front line/hp-out
++ *
++ * AD1984 = AD1884 + two digital mic-ins
++ *
++ * FIXME:
++ * For simplicity, we share the single DAC for both HP and line-outs
++ * right now. The inidividual playbacks could be easily implemented,
++ * but no build-up framework is given, so far.
++ */
++
++static hda_nid_t ad1884_dac_nids[1] = {
++ 0x04,
++};
++
++static hda_nid_t ad1884_adc_nids[2] = {
++ 0x08, 0x09,
++};
++
++static hda_nid_t ad1884_capsrc_nids[2] = {
++ 0x0c, 0x0d,
++};
++
++#define AD1884_SPDIF_OUT 0x02
++
++static struct hda_input_mux ad1884_capture_source = {
++ .num_items = 4,
++ .items = {
++ { "Front Mic", 0x0 },
++ { "Mic", 0x1 },
++ { "CD", 0x2 },
++ { "Mix", 0x3 },
++ },
++};
++
++static struct snd_kcontrol_new ad1884_base_mixers[] = {
++ HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
++ /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
++ HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
++ HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
++ HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
++ HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
++ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
++ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
++ HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
++ HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
++ HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
++ HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
++ /*
++ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x20, 0x03, HDA_INPUT),
++ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x20, 0x03, HDA_INPUT),
++ HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0x10, 0x0, HDA_OUTPUT),
++ HDA_CODEC_MUTE("Digital Beep Playback Switch", 0x10, 0x0, HDA_OUTPUT),
++ */
++ HDA_CODEC_VOLUME("Mic Boost", 0x15, 0x0, HDA_INPUT),
++ HDA_CODEC_VOLUME("Front Mic Boost", 0x14, 0x0, HDA_INPUT),
++ HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
++ HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
++ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
++ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
++ {
++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++ /* The multiple "Capture Source" controls confuse alsamixer
++ * So call somewhat different..
++ * FIXME: the controls appear in the "playback" view!
++ */
++ /* .name = "Capture Source", */
++ .name = "Input Source",
++ .count = 2,
++ .info = ad198x_mux_enum_info,
++ .get = ad198x_mux_enum_get,
++ .put = ad198x_mux_enum_put,
++ },
++ /* SPDIF controls */
++ HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
++ {
++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
++ /* identical with ad1983 */
++ .info = ad1983_spdif_route_info,
++ .get = ad1983_spdif_route_get,
++ .put = ad1983_spdif_route_put,
++ },
++ { } /* end */
++};
++
++static struct snd_kcontrol_new ad1984_dmic_mixers[] = {
++ HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
++ HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
++ HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
++ HDA_INPUT),
++ HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
++ HDA_INPUT),
++ { } /* end */
++};
++
++/*
++ * initialization verbs
++ */
++static struct hda_verb ad1884_init_verbs[] = {
++ /* DACs; mute as default */
++ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
++ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
++ /* Port-A (HP) mixer */
++ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
++ /* Port-A pin */
++ {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
++ {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++ /* HP selector - select DAC2 */
++ {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
++ /* Port-D (Line-out) mixer */
++ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++ {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
++ /* Port-D pin */
++ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
++ {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++ /* Mono-out mixer */
++ {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++ {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
++ /* Mono-out pin */
++ {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
++ {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++ /* Mono selector */
++ {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
++ /* Port-B (front mic) pin */
++ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
++ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++ /* Port-C (rear mic) pin */
++ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
++ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++ /* Analog mixer; mute as default */
++ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
++ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
++ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
++ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
++ /* Analog Mix output amp */
++ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
++ /* SPDIF output selector */
++ {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
++ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
++ { } /* end */
++};
++
++static int patch_ad1884(struct hda_codec *codec)
++{
++ struct ad198x_spec *spec;
++
++ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
++ if (spec == NULL)
++ return -ENOMEM;
++
++ mutex_init(&spec->amp_mutex);
++ codec->spec = spec;
++
++ spec->multiout.max_channels = 2;
++ spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
++ spec->multiout.dac_nids = ad1884_dac_nids;
++ spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
++ spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
++ spec->adc_nids = ad1884_adc_nids;
++ spec->capsrc_nids = ad1884_capsrc_nids;
++ spec->input_mux = &ad1884_capture_source;
++ spec->num_mixers = 1;
++ spec->mixers[0] = ad1884_base_mixers;
++ spec->num_init_verbs = 1;
++ spec->init_verbs[0] = ad1884_init_verbs;
++ spec->spdif_route = 0;
++
++ codec->patch_ops = ad198x_patch_ops;
++
++ return 0;
++}
++
++/*
++ * Lenovo Thinkpad T61/X61
++ */
++static struct hda_input_mux ad1984_thinkpad_capture_source = {
++ .num_items = 3,
++ .items = {
++ { "Mic", 0x0 },
++ { "Internal Mic", 0x1 },
++ { "Mix", 0x3 },
++ },
++};
++
++static struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
++ HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
++ /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
++ HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
++ HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
++ HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
++ HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
++ HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
++ HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
++ HDA_CODEC_VOLUME("Docking Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
++ HDA_CODEC_MUTE("Docking Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
++ HDA_CODEC_VOLUME("Mic Boost", 0x14, 0x0, HDA_INPUT),
++ HDA_CODEC_VOLUME("Internal Mic Boost", 0x15, 0x0, HDA_INPUT),
++ HDA_CODEC_VOLUME("Docking Mic Boost", 0x25, 0x0, HDA_OUTPUT),
++ HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
++ HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
++ HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
++ HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
++ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
++ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
++ {
++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++ /* The multiple "Capture Source" controls confuse alsamixer
++ * So call somewhat different..
++ * FIXME: the controls appear in the "playback" view!
++ */
++ /* .name = "Capture Source", */
++ .name = "Input Source",
++ .count = 2,
++ .info = ad198x_mux_enum_info,
++ .get = ad198x_mux_enum_get,
++ .put = ad198x_mux_enum_put,
++ },
++ { } /* end */
++};
++
++/* additional verbs */
++static struct hda_verb ad1984_thinkpad_init_verbs[] = {
++ /* Port-E (docking station mic) pin */
++ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
++ {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++ /* docking mic boost */
++ {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++ /* Analog mixer - docking mic; mute as default */
++ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
++ /* enable EAPD bit */
++ {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
++ { } /* end */
++};
++
++/* Digial MIC ADC NID 0x05 + 0x06 */
++static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
++ struct hda_codec *codec,
++ unsigned int stream_tag,
++ unsigned int format,
++ struct snd_pcm_substream *substream)
++{
++ snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
++ stream_tag, 0, format);
++ return 0;
++}
++
++static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
++ struct hda_codec *codec,
++ struct snd_pcm_substream *substream)
++{
++ snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
++ 0, 0, 0);
++ return 0;
++}
++
++static struct hda_pcm_stream ad1984_pcm_dmic_capture = {
++ .substreams = 2,
++ .channels_min = 2,
++ .channels_max = 2,
++ .nid = 0x05,
++ .ops = {
++ .prepare = ad1984_pcm_dmic_prepare,
++ .cleanup = ad1984_pcm_dmic_cleanup
++ },
++};
++
++static int ad1984_build_pcms(struct hda_codec *codec)
++{
++ struct ad198x_spec *spec = codec->spec;
++ struct hda_pcm *info;
++ int err;
++
++ err = ad198x_build_pcms(codec);
++ if (err < 0)
++ return err;
++
++ info = spec->pcm_rec + codec->num_pcms;
++ codec->num_pcms++;
++ info->name = "AD1984 Digital Mic";
++ info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
++ return 0;
++}
++
++/* models */
++enum {
++ AD1984_BASIC,
++ AD1984_THINKPAD,
++ AD1984_MODELS
++};
++
++static const char *ad1984_models[AD1984_MODELS] = {
++ [AD1984_BASIC] = "basic",
++ [AD1984_THINKPAD] = "thinkpad",
++};
++
++static struct snd_pci_quirk ad1984_cfg_tbl[] = {
++ /* Lenovo Thinkpad T61/X61 */
++ SND_PCI_QUIRK(0x17aa, 0, "Lenovo Thinkpad", AD1984_THINKPAD),
++ {}
++};
++
++static int patch_ad1984(struct hda_codec *codec)
++{
++ struct ad198x_spec *spec;
++ int board_config, err;
++
++ err = patch_ad1884(codec);
++ if (err < 0)
++ return err;
++ spec = codec->spec;
++ board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
++ ad1984_models, ad1984_cfg_tbl);
++ switch (board_config) {
++ case AD1984_BASIC:
++ /* additional digital mics */
++ spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
++ codec->patch_ops.build_pcms = ad1984_build_pcms;
++ break;
++ case AD1984_THINKPAD:
++ spec->multiout.dig_out_nid = 0;
++ spec->input_mux = &ad1984_thinkpad_capture_source;
++ spec->mixers[0] = ad1984_thinkpad_mixers;
++ spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
++ break;
++ }
++ return 0;
++}
++
++
++/*
++ * AD1882
++ *
++ * port-A - front hp-out
++ * port-B - front mic-in
++ * port-C - rear line-in, shared surr-out (3stack)
++ * port-D - rear line-out
++ * port-E - rear mic-in, shared clfe-out (3stack)
++ * port-F - rear surr-out (6stack)
++ * port-G - rear clfe-out (6stack)
++ */
++
++static hda_nid_t ad1882_dac_nids[3] = {
++ 0x04, 0x03, 0x05
++};
++
++static hda_nid_t ad1882_adc_nids[2] = {
++ 0x08, 0x09,
++};
++
++static hda_nid_t ad1882_capsrc_nids[2] = {
++ 0x0c, 0x0d,
++};
++
++#define AD1882_SPDIF_OUT 0x02
++
++/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
++static struct hda_input_mux ad1882_capture_source = {
++ .num_items = 5,
++ .items = {
++ { "Front Mic", 0x1 },
++ { "Mic", 0x4 },
++ { "Line", 0x2 },
++ { "CD", 0x3 },
++ { "Mix", 0x7 },
++ },
++};
++
++static struct snd_kcontrol_new ad1882_base_mixers[] = {
++ HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
++ HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
++ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
++ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
++ HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
++ HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
++ HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
++ HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
++ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
++ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
++ HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
++ HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
++ HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
++ HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
++ HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
++ HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
++ HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x07, HDA_INPUT),
++ HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x07, HDA_INPUT),
++ HDA_CODEC_VOLUME("Mic Boost", 0x3c, 0x0, HDA_OUTPUT),
++ HDA_CODEC_VOLUME("Front Mic Boost", 0x39, 0x0, HDA_OUTPUT),
++ HDA_CODEC_VOLUME("Line-In Boost", 0x3a, 0x0, HDA_OUTPUT),
++ HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
++ HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
++ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
++ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
++ {
++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++ /* The multiple "Capture Source" controls confuse alsamixer
++ * So call somewhat different..
++ * FIXME: the controls appear in the "playback" view!
++ */
++ /* .name = "Capture Source", */
++ .name = "Input Source",
++ .count = 2,
++ .info = ad198x_mux_enum_info,
++ .get = ad198x_mux_enum_get,
++ .put = ad198x_mux_enum_put,
++ },
++ /* SPDIF controls */
++ HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
++ {
++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++ .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
++ /* identical with ad1983 */
++ .info = ad1983_spdif_route_info,
++ .get = ad1983_spdif_route_get,
++ .put = ad1983_spdif_route_put,
++ },
++ { } /* end */
++};
++
++static struct snd_kcontrol_new ad1882_3stack_mixers[] = {
++ HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
++ HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
++ HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
++ {
++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++ .name = "Channel Mode",
++ .info = ad198x_ch_mode_info,
++ .get = ad198x_ch_mode_get,
++ .put = ad198x_ch_mode_put,
++ },
++ { } /* end */
++};
++
++static struct snd_kcontrol_new ad1882_6stack_mixers[] = {
++ HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
++ HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
++ HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
++ { } /* end */
++};
++
++static struct hda_verb ad1882_ch2_init[] = {
++ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
++ {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
++ {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
++ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
++ {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
++ {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
++ { } /* end */
++};
++
++static struct hda_verb ad1882_ch4_init[] = {
++ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
++ {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++ {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
++ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
++ {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
++ {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
++ { } /* end */
++};
++
++static struct hda_verb ad1882_ch6_init[] = {
++ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
++ {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++ {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
++ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
++ {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++ {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
++ { } /* end */
++};
++
++static struct hda_channel_mode ad1882_modes[3] = {
++ { 2, ad1882_ch2_init },
++ { 4, ad1882_ch4_init },
++ { 6, ad1882_ch6_init },
++};
++
++/*
++ * initialization verbs
++ */
++static struct hda_verb ad1882_init_verbs[] = {
++ /* DACs; mute as default */
++ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
++ {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
++ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
++ /* Port-A (HP) mixer */
++ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++ {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
++ /* Port-A pin */
++ {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
++ {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++ /* HP selector - select DAC2 */
++ {0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
++ /* Port-D (Line-out) mixer */
++ {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++ {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
++ /* Port-D pin */
++ {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
++ {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++ /* Mono-out mixer */
++ {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++ {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
++ /* Mono-out pin */
++ {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
++ {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++ /* Port-B (front mic) pin */
++ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
++ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++ {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
++ /* Port-C (line-in) pin */
++ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
++ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++ {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
++ /* Port-C mixer - mute as input */
++ {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
++ {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
++ /* Port-E (mic-in) pin */
++ {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
++ {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++ {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
++ /* Port-E mixer - mute as input */
++ {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
++ {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
++ /* Port-F (surround) */
++ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
++ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++ /* Port-G (CLFE) */
++ {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
++ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++ /* Analog mixer; mute as default */
++ /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
++ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
++ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
++ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
++ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
++ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
++ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
++ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
++ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
++ /* Analog Mix output amp */
++ {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
++ /* SPDIF output selector */
++ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
++ {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
++ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
++ { } /* end */
++};
++
++/* models */
++enum {
++ AD1882_3STACK,
++ AD1882_6STACK,
++ AD1882_MODELS
++};
++
++static const char *ad1882_models[AD1986A_MODELS] = {
++ [AD1882_3STACK] = "3stack",
++ [AD1882_6STACK] = "6stack",
++};
++
++
++static int patch_ad1882(struct hda_codec *codec)
++{
++ struct ad198x_spec *spec;
++ int board_config;
++
++ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
++ if (spec == NULL)
++ return -ENOMEM;
++
++ mutex_init(&spec->amp_mutex);
++ codec->spec = spec;
++
++ spec->multiout.max_channels = 6;
++ spec->multiout.num_dacs = 3;
++ spec->multiout.dac_nids = ad1882_dac_nids;
++ spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
++ spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
++ spec->adc_nids = ad1882_adc_nids;
++ spec->capsrc_nids = ad1882_capsrc_nids;
++ spec->input_mux = &ad1882_capture_source;
++ spec->num_mixers = 1;
++ spec->mixers[0] = ad1882_base_mixers;
++ spec->num_init_verbs = 1;
++ spec->init_verbs[0] = ad1882_init_verbs;
++ spec->spdif_route = 0;
++
++ codec->patch_ops = ad198x_patch_ops;
++
++ /* override some parameters */
++ board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
++ ad1882_models, NULL);
++ switch (board_config) {
++ default:
++ case AD1882_3STACK:
++ spec->num_mixers = 2;
++ spec->mixers[1] = ad1882_3stack_mixers;
++ spec->channel_mode = ad1882_modes;
++ spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
++ spec->need_dac_fix = 1;
++ spec->multiout.max_channels = 2;
++ spec->multiout.num_dacs = 1;
++ break;
++ case AD1882_6STACK:
++ spec->num_mixers = 2;
++ spec->mixers[1] = ad1882_6stack_mixers;
++ break;
++ }
++ return 0;
++}
++
++
++/*
+ * patch entries
+ */
+ struct hda_codec_preset snd_hda_preset_analog[] = {
++ { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
++ { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
+ { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
+ { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
++ { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1984 },
+ { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
+ { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
+ { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
+--- linux-2.6.22.1.orig/sound/pci/hda/patch_atihdmi.c
++++ linux-2.6.22.1/sound/pci/hda/patch_atihdmi.c
+@@ -172,6 +172,7 @@
+ */
+ struct hda_codec_preset snd_hda_preset_atihdmi[] = {
+ { .id = 0x1002793c, .name = "ATI RS600 HDMI", .patch = patch_atihdmi },
++ { .id = 0x10027919, .name = "ATI RS600 HDMI", .patch = patch_atihdmi },
+ { .id = 0x1002791a, .name = "ATI RS690/780 HDMI", .patch = patch_atihdmi },
+ { .id = 0x1002aa01, .name = "ATI R600 HDMI", .patch = patch_atihdmi },
+ {} /* terminator */
+--- linux-2.6.22.1.orig/sound/pci/hda/patch_conexant.c
++++ linux-2.6.22.1/sound/pci/hda/patch_conexant.c
+@@ -801,7 +801,9 @@
+ SND_PCI_QUIRK(0x103c, 0x30b2, "HP DV Series", CXT5045_LAPTOP),
+ SND_PCI_QUIRK(0x103c, 0x30b5, "HP DV2120", CXT5045_LAPTOP),
+ SND_PCI_QUIRK(0x103c, 0x30cd, "HP DV Series", CXT5045_LAPTOP),
++ SND_PCI_QUIRK(0x103c, 0x30d9, "HP Spartan", CXT5045_LAPTOP),
+ SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_FUJITSU),
++ SND_PCI_QUIRK(0x1734, 0x10cb, "Fujitsu Si3515", CXT5045_LAPTOP),
+ SND_PCI_QUIRK(0x8086, 0x2111, "Conexant Reference board", CXT5045_LAPTOP),
+ {}
+ };
+--- linux-2.6.22.1.orig/sound/pci/hda/patch_realtek.c
++++ linux-2.6.22.1/sound/pci/hda/patch_realtek.c
+@@ -94,10 +94,18 @@
+ ALC262_HP_BPC_D7000_WF,
+ ALC262_BENQ_ED8,
+ ALC262_SONY_ASSAMD,
++ ALC262_BENQ_T31,
+ ALC262_AUTO,
+ ALC262_MODEL_LAST /* last tag */
+ };
+
++/* ALC268 models */
++enum {
++ ALC268_3ST,
++ ALC268_AUTO,
++ ALC268_MODEL_LAST /* last tag */
++};
++
+ /* ALC861 models */
+ enum {
+ ALC861_3ST,
+@@ -115,6 +123,7 @@
+ /* ALC861-VD models */
+ enum {
+ ALC660VD_3ST,
++ ALC660VD_3ST_DIG,
+ ALC861VD_3ST,
+ ALC861VD_3ST_DIG,
+ ALC861VD_6ST_DIG,
+@@ -144,6 +153,7 @@
+ ALC882_TARGA,
+ ALC882_ASUS_A7J,
+ ALC885_MACPRO,
++ ALC885_IMAC24,
+ ALC882_AUTO,
+ ALC882_MODEL_LAST,
+ };
+@@ -163,6 +173,8 @@
+ ALC883_LENOVO_101E_2ch,
+ ALC883_LENOVO_NB0763,
+ ALC888_LENOVO_MS7195_DIG,
++ ALC888_6ST_HP,
++ ALC888_3ST_HP,
+ ALC883_AUTO,
+ ALC883_MODEL_LAST,
+ };
+@@ -713,6 +725,38 @@
+ }
+
+ /*
++ * Fix-up pin default configurations
++ */
++
++struct alc_pincfg {
++ hda_nid_t nid;
++ u32 val;
++};
++
++static void alc_fix_pincfg(struct hda_codec *codec,
++ const struct snd_pci_quirk *quirk,
++ const struct alc_pincfg **pinfix)
++{
++ const struct alc_pincfg *cfg;
++
++ quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk);
++ if (!quirk)
++ return;
++
++ cfg = pinfix[quirk->value];
++ for (; cfg->nid; cfg++) {
++ int i;
++ u32 val = cfg->val;
++ for (i = 0; i < 4; i++) {
++ snd_hda_codec_write(codec, cfg->nid, 0,
++ AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 + i,
++ val & 0xff);
++ val >>= 8;
++ }
++ }
++}
++
++/*
+ * ALC880 3-stack model
+ *
+ * DAC: Front = 0x02 (0x0c), Surr = 0x05 (0x0f), CLFE = 0x04 (0x0e)
+@@ -1878,31 +1922,53 @@
+ * Pin assignment:
+ * Speaker-out: 0x14
+ * Mic-In: 0x18
+- * Built-in Mic-In: 0x19 (?)
+- * HP-Out: 0x1b
++ * Built-in Mic-In: 0x19
++ * Line-In: 0x1b
++ * HP-Out: 0x1a
+ * SPDIF-Out: 0x1e
+ */
+
+-/* seems analog CD is not working */
+ static struct hda_input_mux alc880_lg_lw_capture_source = {
+- .num_items = 2,
++ .num_items = 3,
+ .items = {
+ { "Mic", 0x0 },
+ { "Internal Mic", 0x1 },
++ { "Line In", 0x2 },
+ },
+ };
+
++#define alc880_lg_lw_modes alc880_threestack_modes
++
+ static struct snd_kcontrol_new alc880_lg_lw_mixer[] = {
+- HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+- HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT),
++ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
++ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
++ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
++ HDA_BIND_MUTE("Surround Playback Switch", 0x0f, 2, HDA_INPUT),
++ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x0, HDA_OUTPUT),
++ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT),
++ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 2, HDA_INPUT),
++ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 2, HDA_INPUT),
++ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
++ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+ HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+ HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
++ {
++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++ .name = "Channel Mode",
++ .info = alc_ch_mode_info,
++ .get = alc_ch_mode_get,
++ .put = alc_ch_mode_put,
++ },
+ { } /* end */
+ };
+
+ static struct hda_verb alc880_lg_lw_init_verbs[] = {
++ {0x13, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
++ {0x10, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */
++ {0x12, AC_VERB_SET_CONNECT_SEL, 0x03}, /* line/surround */
++
+ /* set capture source to mic-in */
+ {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+@@ -1912,7 +1978,6 @@
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ /* HP-out */
+- {0x13, AC_VERB_SET_CONNECT_SEL, 0x00},
+ {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+ {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+ /* mic-in to input */
+@@ -2856,11 +2921,11 @@
+ .mixers = { alc880_lg_lw_mixer },
+ .init_verbs = { alc880_volume_init_verbs,
+ alc880_lg_lw_init_verbs },
+- .num_dacs = 1,
++ .num_dacs = ARRAY_SIZE(alc880_dac_nids),
+ .dac_nids = alc880_dac_nids,
+ .dig_out_nid = ALC880_DIGOUT_NID,
+- .num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
+- .channel_mode = alc880_2_jack_modes,
++ .num_channel_mode = ARRAY_SIZE(alc880_lg_lw_modes),
++ .channel_mode = alc880_lg_lw_modes,
+ .input_mux = &alc880_lg_lw_capture_source,
+ .unsol_event = alc880_lg_lw_unsol_event,
+ .init_hook = alc880_lg_lw_automute,
+@@ -5054,6 +5119,60 @@
+ { }
+ };
+
++/* iMac 24 mixer. */
++static struct snd_kcontrol_new alc885_imac24_mixer[] = {
++ HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
++ HDA_CODEC_MUTE("Master Playback Switch", 0x0c, 0x00, HDA_INPUT),
++ { } /* end */
++};
++
++/* iMac 24 init verbs. */
++static struct hda_verb alc885_imac24_init_verbs[] = {
++ /* Internal speakers: output 0 (0x0c) */
++ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
++ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
++ {0x18, AC_VERB_SET_CONNECT_SEL, 0x00},
++ /* Internal speakers: output 0 (0x0c) */
++ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
++ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
++ {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
++ /* Headphone: output 0 (0x0c) */
++ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
++ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
++ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
++ {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
++ /* Front Mic: input vref at 80% */
++ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
++ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++ { }
++};
++
++/* Toggle speaker-output according to the hp-jack state */
++static void alc885_imac24_automute(struct hda_codec *codec)
++{
++ unsigned int present;
++
++ present = snd_hda_codec_read(codec, 0x14, 0,
++ AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
++ snd_hda_codec_amp_update(codec, 0x18, 0, HDA_OUTPUT, 0,
++ 0x80, present ? 0x80 : 0);
++ snd_hda_codec_amp_update(codec, 0x18, 1, HDA_OUTPUT, 0,
++ 0x80, present ? 0x80 : 0);
++ snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0,
++ 0x80, present ? 0x80 : 0);
++ snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0,
++ 0x80, present ? 0x80 : 0);
++}
++
++/* Processes unsolicited events. */
++static void alc885_imac24_unsol_event(struct hda_codec *codec,
++ unsigned int res)
++{
++ /* Headphone insertion or removal. */
++ if ((res >> 26) == ALC880_HP_EVENT)
++ alc885_imac24_automute(codec);
++}
++
+ static struct hda_verb alc882_targa_verbs[] = {
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+ {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+@@ -5274,6 +5393,7 @@
+ [ALC882_ARIMA] = "arima",
+ [ALC882_W2JC] = "w2jc",
+ [ALC885_MACPRO] = "macpro",
++ [ALC885_IMAC24] = "imac24",
+ [ALC882_AUTO] = "auto",
+ };
+
+@@ -5284,6 +5404,7 @@
+ SND_PCI_QUIRK(0x1462, 0x28fb, "Targa T8", ALC882_TARGA), /* MSI-1049 T8 */
+ SND_PCI_QUIRK(0x161f, 0x2054, "Arima W820", ALC882_ARIMA),
+ SND_PCI_QUIRK(0x1043, 0x060d, "Asus A7J", ALC882_ASUS_A7J),
++ SND_PCI_QUIRK(0x1043, 0x817f, "Asus P5LD2", ALC882_6ST_DIG),
+ SND_PCI_QUIRK(0x1043, 0x81d8, "Asus P5WD", ALC882_6ST_DIG),
+ SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_W2JC),
+ {}
+@@ -5345,6 +5466,19 @@
+ .channel_mode = alc882_ch_modes,
+ .input_mux = &alc882_capture_source,
+ },
++ [ALC885_IMAC24] = {
++ .mixers = { alc885_imac24_mixer },
++ .init_verbs = { alc885_imac24_init_verbs },
++ .num_dacs = ARRAY_SIZE(alc882_dac_nids),
++ .dac_nids = alc882_dac_nids,
++ .dig_out_nid = ALC882_DIGOUT_NID,
++ .dig_in_nid = ALC882_DIGIN_NID,
++ .num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
++ .channel_mode = alc882_ch_modes,
++ .input_mux = &alc882_capture_source,
++ .unsol_event = alc885_imac24_unsol_event,
++ .init_hook = alc885_imac24_automute,
++ },
+ [ALC882_TARGA] = {
+ .mixers = { alc882_targa_mixer, alc882_chmode_mixer,
+ alc882_capture_mixer },
+@@ -5379,6 +5513,29 @@
+
+
+ /*
++ * Pin config fixes
++ */
++enum {
++ PINFIX_ABIT_AW9D_MAX
++};
++
++static struct alc_pincfg alc882_abit_aw9d_pinfix[] = {
++ { 0x15, 0x01080104 }, /* side */
++ { 0x16, 0x01011012 }, /* rear */
++ { 0x17, 0x01016011 }, /* clfe */
++ { }
++};
++
++static const struct alc_pincfg *alc882_pin_fixes[] = {
++ [PINFIX_ABIT_AW9D_MAX] = alc882_abit_aw9d_pinfix,
++};
++
++static struct snd_pci_quirk alc882_pinfix_tbl[] = {
++ SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", PINFIX_ABIT_AW9D_MAX),
++ {}
++};
++
++/*
+ * BIOS auto configuration
+ */
+ static void alc882_auto_set_output_and_unmute(struct hda_codec *codec,
+@@ -5494,6 +5651,9 @@
+ case 0x106b0c00: /* Mac Pro */
+ board_config = ALC885_MACPRO;
+ break;
++ case 0x106b1000: /* iMac 24 */
++ board_config = ALC885_IMAC24;
++ break;
+ default:
+ printk(KERN_INFO "hda_codec: Unknown model for ALC882, "
+ "trying auto-probe from BIOS...\n");
+@@ -5501,6 +5661,8 @@
+ }
+ }
+
++ alc_fix_pincfg(codec, alc882_pinfix_tbl, alc882_pin_fixes);
++
+ if (board_config == ALC882_AUTO) {
+ /* automatic parse from the BIOS config */
+ err = alc882_parse_auto_config(codec);
+@@ -5518,7 +5680,7 @@
+ if (board_config != ALC882_AUTO)
+ setup_preset(spec, &alc882_presets[board_config]);
+
+- if (board_config == ALC885_MACPRO) {
++ if (board_config == ALC885_MACPRO || board_config == ALC885_IMAC24) {
+ alc882_gpio_mute(codec, 0, 0);
+ alc882_gpio_mute(codec, 1, 0);
+ }
+@@ -5995,6 +6157,84 @@
+ { } /* end */
+ };
+
++static struct snd_kcontrol_new alc888_6st_hp_mixer[] = {
++ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
++ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
++ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0e, 0x0, HDA_OUTPUT),
++ HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT),
++ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
++ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT),
++ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT),
++ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT),
++ HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
++ HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
++ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
++ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
++ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
++ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
++ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
++ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
++ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
++ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
++ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
++ HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
++ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
++ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
++ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
++ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
++ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
++ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
++ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
++ {
++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++ /* .name = "Capture Source", */
++ .name = "Input Source",
++ .count = 2,
++ .info = alc883_mux_enum_info,
++ .get = alc883_mux_enum_get,
++ .put = alc883_mux_enum_put,
++ },
++ { } /* end */
++};
++
++static struct snd_kcontrol_new alc888_3st_hp_mixer[] = {
++ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
++ HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
++ HDA_CODEC_VOLUME("Surround Playback Volume", 0x0e, 0x0, HDA_OUTPUT),
++ HDA_BIND_MUTE("Surround Playback Switch", 0x0e, 2, HDA_INPUT),
++ HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0d, 1, 0x0, HDA_OUTPUT),
++ HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0d, 2, 0x0, HDA_OUTPUT),
++ HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0d, 1, 2, HDA_INPUT),
++ HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT),
++ HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
++ HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
++ HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
++ HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
++ HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
++ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
++ HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
++ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
++ HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
++ HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
++ HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
++ HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT),
++ HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT),
++ HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
++ HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
++ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x09, 0x0, HDA_INPUT),
++ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x09, 0x0, HDA_INPUT),
++ {
++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++ /* .name = "Capture Source", */
++ .name = "Input Source",
++ .count = 2,
++ .info = alc883_mux_enum_info,
++ .get = alc883_mux_enum_get,
++ .put = alc883_mux_enum_put,
++ },
++ { } /* end */
++};
++
+ static struct snd_kcontrol_new alc883_chmode_mixer[] = {
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+@@ -6126,6 +6366,42 @@
+ { } /* end */
+ };
+
++static struct hda_verb alc888_6st_hp_verbs[] = {
++ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */
++ {0x15, AC_VERB_SET_CONNECT_SEL, 0x02}, /* Rear : output 2 (0x0e) */
++ {0x16, AC_VERB_SET_CONNECT_SEL, 0x01}, /* CLFE : output 1 (0x0d) */
++ {0x17, AC_VERB_SET_CONNECT_SEL, 0x03}, /* Side : output 3 (0x0f) */
++ { }
++};
++
++static struct hda_verb alc888_3st_hp_verbs[] = {
++ {0x14, AC_VERB_SET_CONNECT_SEL, 0x00}, /* Front: output 0 (0x0c) */
++ {0x18, AC_VERB_SET_CONNECT_SEL, 0x01}, /* Rear : output 1 (0x0d) */
++ {0x16, AC_VERB_SET_CONNECT_SEL, 0x02}, /* CLFE : output 2 (0x0e) */
++ { }
++};
++
++static struct hda_verb alc888_3st_hp_2ch_init[] = {
++ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
++ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
++ { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
++ { 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
++ { }
++};
++
++static struct hda_verb alc888_3st_hp_6ch_init[] = {
++ { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
++ { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
++ { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
++ { 0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
++ { }
++};
++
++static struct hda_channel_mode alc888_3st_hp_modes[2] = {
++ { 2, alc888_3st_hp_2ch_init },
++ { 6, alc888_3st_hp_6ch_init },
++};
++
+ /* toggle front-jack and RCA according to the hp-jack state */
+ static void alc888_lenovo_ms7195_front_automute(struct hda_codec *codec)
+ {
+@@ -6368,11 +6644,14 @@
+ [ALC883_LENOVO_101E_2ch] = "lenovo-101e",
+ [ALC883_LENOVO_NB0763] = "lenovo-nb0763",
+ [ALC888_LENOVO_MS7195_DIG] = "lenovo-ms7195-dig",
++ [ALC888_6ST_HP] = "6stack-hp",
++ [ALC888_3ST_HP] = "3stack-hp",
+ [ALC883_AUTO] = "auto",
+ };
+
+ static struct snd_pci_quirk alc883_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x1019, 0x6668, "ECS", ALC883_3ST_6ch_DIG),
++ SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavillion", ALC883_6ST_DIG),
+ SND_PCI_QUIRK(0x108e, 0x534d, NULL, ALC883_3ST_6ch),
+ SND_PCI_QUIRK(0x1558, 0, "Clevo laptop", ALC883_LAPTOP_EAPD),
+ SND_PCI_QUIRK(0x105b, 0x6668, "Foxconn", ALC883_6ST_DIG),
+@@ -6381,6 +6660,8 @@
+ SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG),
+ SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG),
+ SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG),
++ SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG),
++ SND_PCI_QUIRK(0x1462, 0x0349, "MSI", ALC883_TARGA_2ch_DIG),
+ SND_PCI_QUIRK(0x1462, 0x0579, "MSI", ALC883_TARGA_2ch_DIG),
+ SND_PCI_QUIRK(0x1462, 0x3729, "MSI S420", ALC883_TARGA_DIG),
+ SND_PCI_QUIRK(0x1462, 0x3ef9, "MSI", ALC883_TARGA_DIG),
+@@ -6400,6 +6681,9 @@
+ SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo 101e", ALC883_LENOVO_101E_2ch),
+ SND_PCI_QUIRK(0x17aa, 0x3bfd, "Lenovo NB0763", ALC883_LENOVO_NB0763),
+ SND_PCI_QUIRK(0x17aa, 0x2085, "Lenovo NB0763", ALC883_LENOVO_NB0763),
++ SND_PCI_QUIRK(0x103c, 0x2a61, "HP Nettle", ALC888_6ST_HP),
++ SND_PCI_QUIRK(0x103c, 0x2a60, "HP Lucknow", ALC888_3ST_HP),
++ SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP),
+ SND_PCI_QUIRK(0x17c0, 0x4071, "MEDION MD2", ALC883_MEDION_MD2),
+ {}
+ };
+@@ -6584,6 +6868,31 @@
+ .unsol_event = alc883_lenovo_ms7195_unsol_event,
+ .init_hook = alc888_lenovo_ms7195_front_automute,
+ },
++ [ALC888_6ST_HP] = {
++ .mixers = { alc888_6st_hp_mixer, alc883_chmode_mixer },
++ .init_verbs = { alc883_init_verbs, alc888_6st_hp_verbs },
++ .num_dacs = ARRAY_SIZE(alc883_dac_nids),
++ .dac_nids = alc883_dac_nids,
++ .dig_out_nid = ALC883_DIGOUT_NID,
++ .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
++ .adc_nids = alc883_adc_nids,
++ .dig_in_nid = ALC883_DIGIN_NID,
++ .num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
++ .channel_mode = alc883_sixstack_modes,
++ .input_mux = &alc883_capture_source,
++ },
++ [ALC888_3ST_HP] = {
++ .mixers = { alc888_3st_hp_mixer, alc883_chmode_mixer },
++ .init_verbs = { alc883_init_verbs, alc888_3st_hp_verbs },
++ .num_dacs = ARRAY_SIZE(alc883_dac_nids),
++ .dac_nids = alc883_dac_nids,
++ .num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
++ .adc_nids = alc883_adc_nids,
++ .num_channel_mode = ARRAY_SIZE(alc888_3st_hp_modes),
++ .channel_mode = alc888_3st_hp_modes,
++ .need_dac_fix = 1,
++ .input_mux = &alc883_capture_source,
++ },
+ };
+
+
+@@ -6857,7 +7166,16 @@
+ { } /* end */
+ };
+
+-
++static struct snd_kcontrol_new alc262_benq_t31_mixer[] = {
++ HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
++ HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
++ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
++ HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
++ HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
++ HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
++ HDA_CODEC_MUTE("ATAPI Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
++ { } /* end */
++};
+
+ #define alc262_capture_mixer alc882_capture_mixer
+ #define alc262_capture_alt_mixer alc882_capture_alt_mixer
+@@ -7189,6 +7507,15 @@
+ {}
+ };
+
++static struct hda_verb alc262_benq_t31_EAPD_verbs[] = {
++ {0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
++ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
++
++ {0x20, AC_VERB_SET_COEF_INDEX, 0x07},
++ {0x20, AC_VERB_SET_PROC_COEF, 0x3050},
++ {}
++};
++
+ /* add playback controls from the parsed DAC table */
+ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec,
+ const struct auto_pin_cfg *cfg)
+@@ -7584,7 +7911,8 @@
+ [ALC262_HP_BPC] = "hp-bpc",
+ [ALC262_HP_BPC_D7000_WL]= "hp-bpc-d7000",
+ [ALC262_BENQ_ED8] = "benq",
+- [ALC262_BENQ_ED8] = "sony-assamd",
++ [ALC262_BENQ_T31] = "benq-t31",
++ [ALC262_SONY_ASSAMD] = "sony-assamd",
+ [ALC262_AUTO] = "auto",
+ };
+
+@@ -7592,8 +7920,12 @@
+ SND_PCI_QUIRK(0x1002, 0x437b, "Hippo", ALC262_HIPPO),
+ SND_PCI_QUIRK(0x103c, 0x12fe, "HP xw9400", ALC262_HP_BPC),
+ SND_PCI_QUIRK(0x103c, 0x280c, "HP xw4400", ALC262_HP_BPC),
++ SND_PCI_QUIRK(0x103c, 0x12ff, "HP xw4550", ALC262_HP_BPC),
++ SND_PCI_QUIRK(0x103c, 0x1308, "HP xw4600", ALC262_HP_BPC),
+ SND_PCI_QUIRK(0x103c, 0x3014, "HP xw6400", ALC262_HP_BPC),
++ SND_PCI_QUIRK(0x103c, 0x1307, "HP xw6600", ALC262_HP_BPC),
+ SND_PCI_QUIRK(0x103c, 0x3015, "HP xw8400", ALC262_HP_BPC),
++ SND_PCI_QUIRK(0x103c, 0x1306, "HP xw8600", ALC262_HP_BPC),
+ SND_PCI_QUIRK(0x103c, 0x2800, "HP D7000", ALC262_HP_BPC_D7000_WL),
+ SND_PCI_QUIRK(0x103c, 0x2802, "HP D7000", ALC262_HP_BPC_D7000_WL),
+ SND_PCI_QUIRK(0x103c, 0x2804, "HP D7000", ALC262_HP_BPC_D7000_WL),
+@@ -7606,6 +7938,7 @@
+ SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FUJITSU),
+ SND_PCI_QUIRK(0x17ff, 0x058f, "Benq Hippo", ALC262_HIPPO_1),
+ SND_PCI_QUIRK(0x17ff, 0x0560, "Benq ED8", ALC262_BENQ_ED8),
++ SND_PCI_QUIRK(0x17ff, 0x058d, "Benq T31-16", ALC262_BENQ_T31),
+ SND_PCI_QUIRK(0x104d, 0x9015, "Sony 0x9015", ALC262_SONY_ASSAMD),
+ SND_PCI_QUIRK(0x104d, 0x900e, "Sony ASSAMD", ALC262_SONY_ASSAMD),
+ SND_PCI_QUIRK(0x104d, 0x1f00, "Sony ASSAMD", ALC262_SONY_ASSAMD),
+@@ -7710,6 +8043,17 @@
+ .channel_mode = alc262_modes,
+ .input_mux = &alc262_capture_source,
+ .unsol_event = alc262_hippo_unsol_event,
++ },
++ [ALC262_BENQ_T31] = {
++ .mixers = { alc262_benq_t31_mixer },
++ .init_verbs = { alc262_init_verbs, alc262_benq_t31_EAPD_verbs, alc262_hippo_unsol_verbs },
++ .num_dacs = ARRAY_SIZE(alc262_dac_nids),
++ .dac_nids = alc262_dac_nids,
++ .hp_nid = 0x03,
++ .num_channel_mode = ARRAY_SIZE(alc262_modes),
++ .channel_mode = alc262_modes,
++ .input_mux = &alc262_capture_source,
++ .unsol_event = alc262_hippo_unsol_event,
+ },
+ };
+
+@@ -7800,31 +8144,540 @@
+ }
+
+ /*
+- * ALC861 channel source setting (2/6 channel selection for 3-stack)
++ * ALC268 channel source setting (2 channel)
+ */
++#define ALC268_DIGOUT_NID ALC880_DIGOUT_NID
++#define alc268_modes alc260_modes
++
++static hda_nid_t alc268_dac_nids[2] = {
++ /* front, hp */
++ 0x02, 0x03
++};
+
+-/*
+- * set the path ways for 2 channel output
+- * need to set the codec line out and mic 1 pin widgets to inputs
+- */
+-static struct hda_verb alc861_threestack_ch2_init[] = {
+- /* set pin widget 1Ah (line in) for input */
+- { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
+- /* set pin widget 18h (mic1/2) for input, for mic also enable
+- * the vref
+- */
+- { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
++static hda_nid_t alc268_adc_nids[2] = {
++ /* ADC0-1 */
++ 0x08, 0x07
++};
+
+- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c },
+-#if 0
+- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/
+- { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, /*line-in*/
+-#endif
+- { } /* end */
++static hda_nid_t alc268_adc_nids_alt[1] = {
++ /* ADC0 */
++ 0x08
+ };
+-/*
+- * 6ch mode
+- * need to set the codec line out and mic 1 pin widgets to outputs
++
++static struct snd_kcontrol_new alc268_base_mixer[] = {
++ /* output mixer control */
++ HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT),
++ HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
++ HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT),
++ HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
++ { }
++};
++
++/*
++ * generic initialization of ADC, input mixers and output mixers
++ */
++static struct hda_verb alc268_base_init_verbs[] = {
++ /* Unmute DAC0-1 and set vol = 0 */
++ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
++ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
++ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
++ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
++
++ /*
++ * Set up output mixers (0x0c - 0x0e)
++ */
++ /* set vol=0 to output mixers */
++ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
++ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
++ {0x0e, AC_VERB_SET_CONNECT_SEL, 0x00},
++
++ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++
++ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
++ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0},
++ {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
++ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
++ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
++ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
++ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
++ {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
++
++ {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++ {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++ {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++ {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
++
++ /* FIXME: use matrix-type input source selection */
++ /* Mixer elements: 0x18, 19, 1a, 1c, 14, 15, 0b */
++ /* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
++ /* Input mixer2 */
++ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
++ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
++ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
++ {0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
++
++ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
++ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8))},
++ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
++ {0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
++ { }
++};
++
++/*
++ * generic initialization of ADC, input mixers and output mixers
++ */
++static struct hda_verb alc268_volume_init_verbs[] = {
++ /* set output DAC */
++ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
++ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++ {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
++
++ {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
++ {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24},
++ {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
++ {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
++ {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
++
++ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
++ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++ {0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
++ {0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++ {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
++
++ {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++ {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++ {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++ {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
++
++ /* set PCBEEP vol = 0 */
++ {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, (0xb000 | (0x00 << 8))},
++
++ { }
++};
++
++#define alc268_mux_enum_info alc_mux_enum_info
++#define alc268_mux_enum_get alc_mux_enum_get
++
++static int alc268_mux_enum_put(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
++ struct alc_spec *spec = codec->spec;
++ const struct hda_input_mux *imux = spec->input_mux;
++ unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
++ static hda_nid_t capture_mixers[3] = { 0x23, 0x24 };
++ hda_nid_t nid = capture_mixers[adc_idx];
++ unsigned int *cur_val = &spec->cur_mux[adc_idx];
++ unsigned int i, idx;
++
++ idx = ucontrol->value.enumerated.item[0];
++ if (idx >= imux->num_items)
++ idx = imux->num_items - 1;
++ if (*cur_val == idx && !codec->in_resume)
++ return 0;
++ for (i = 0; i < imux->num_items; i++) {
++ unsigned int v = (i == idx) ? 0x7000 : 0x7080;
++ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
++ v | (imux->items[i].index << 8));
++ snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL,
++ idx );
++ }
++ *cur_val = idx;
++ return 1;
++}
++
++static struct snd_kcontrol_new alc268_capture_alt_mixer[] = {
++ HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT),
++ HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT),
++ {
++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++ /* The multiple "Capture Source" controls confuse alsamixer
++ * So call somewhat different..
++ * FIXME: the controls appear in the "playback" view!
++ */
++ /* .name = "Capture Source", */
++ .name = "Input Source",
++ .count = 1,
++ .info = alc268_mux_enum_info,
++ .get = alc268_mux_enum_get,
++ .put = alc268_mux_enum_put,
++ },
++ { } /* end */
++};
++
++static struct snd_kcontrol_new alc268_capture_mixer[] = {
++ HDA_CODEC_VOLUME("Capture Volume", 0x23, 0x0, HDA_OUTPUT),
++ HDA_CODEC_MUTE("Capture Switch", 0x23, 0x0, HDA_OUTPUT),
++ HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x24, 0x0, HDA_OUTPUT),
++ HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x24, 0x0, HDA_OUTPUT),
++ {
++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++ /* The multiple "Capture Source" controls confuse alsamixer
++ * So call somewhat different..
++ * FIXME: the controls appear in the "playback" view!
++ */
++ /* .name = "Capture Source", */
++ .name = "Input Source",
++ .count = 2,
++ .info = alc268_mux_enum_info,
++ .get = alc268_mux_enum_get,
++ .put = alc268_mux_enum_put,
++ },
++ { } /* end */
++};
++
++static struct hda_input_mux alc268_capture_source = {
++ .num_items = 4,
++ .items = {
++ { "Mic", 0x0 },
++ { "Front Mic", 0x1 },
++ { "Line", 0x2 },
++ { "CD", 0x3 },
++ },
++};
++
++/* create input playback/capture controls for the given pin */
++static int alc268_new_analog_output(struct alc_spec *spec, hda_nid_t nid,
++ const char *ctlname, int idx)
++{
++ char name[32];
++ int err;
++
++ sprintf(name, "%s Playback Volume", ctlname);
++ if (nid == 0x14) {
++ err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
++ HDA_COMPOSE_AMP_VAL(0x02, 3, idx,
++ HDA_OUTPUT));
++ if (err < 0)
++ return err;
++ } else if (nid == 0x15) {
++ err = add_control(spec, ALC_CTL_WIDGET_VOL, name,
++ HDA_COMPOSE_AMP_VAL(0x03, 3, idx,
++ HDA_OUTPUT));
++ if (err < 0)
++ return err;
++ } else
++ return -1;
++ sprintf(name, "%s Playback Switch", ctlname);
++ err = add_control(spec, ALC_CTL_WIDGET_MUTE, name,
++ HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_OUTPUT));
++ if (err < 0)
++ return err;
++ return 0;
++}
++
++/* add playback controls from the parsed DAC table */
++static int alc268_auto_create_multi_out_ctls(struct alc_spec *spec,
++ const struct auto_pin_cfg *cfg)
++{
++ hda_nid_t nid;
++ int err;
++
++ spec->multiout.num_dacs = 2; /* only use one dac */
++ spec->multiout.dac_nids = spec->private_dac_nids;
++ spec->multiout.dac_nids[0] = 2;
++ spec->multiout.dac_nids[1] = 3;
++
++ nid = cfg->line_out_pins[0];
++ if (nid)
++ alc268_new_analog_output(spec, nid, "Front", 0);
++
++ nid = cfg->speaker_pins[0];
++ if (nid == 0x1d) {
++ err = add_control(spec, ALC_CTL_WIDGET_VOL,
++ "Speaker Playback Volume",
++ HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT));
++ if (err < 0)
++ return err;
++ }
++ nid = cfg->hp_pins[0];
++ if (nid)
++ alc268_new_analog_output(spec, nid, "Headphone", 0);
++
++ nid = cfg->line_out_pins[1] | cfg->line_out_pins[2];
++ if (nid == 0x16) {
++ err = add_control(spec, ALC_CTL_WIDGET_MUTE,
++ "Mono Playback Switch",
++ HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_INPUT));
++ if (err < 0)
++ return err;
++ }
++ return 0;
++}
++
++/* create playback/capture controls for input pins */
++static int alc268_auto_create_analog_input_ctls(struct alc_spec *spec,
++ const struct auto_pin_cfg *cfg)
++{
++ struct hda_input_mux *imux = &spec->private_imux;
++ int i, idx1;
++
++ for (i = 0; i < AUTO_PIN_LAST; i++) {
++ switch(cfg->input_pins[i]) {
++ case 0x18:
++ idx1 = 0; /* Mic 1 */
++ break;
++ case 0x19:
++ idx1 = 1; /* Mic 2 */
++ break;
++ case 0x1a:
++ idx1 = 2; /* Line In */
++ break;
++ case 0x1c:
++ idx1 = 3; /* CD */
++ break;
++ default:
++ continue;
++ }
++ imux->items[imux->num_items].label = auto_pin_cfg_labels[i];
++ imux->items[imux->num_items].index = idx1;
++ imux->num_items++;
++ }
++ return 0;
++}
++
++static void alc268_auto_init_mono_speaker_out(struct hda_codec *codec)
++{
++ struct alc_spec *spec = codec->spec;
++ hda_nid_t speaker_nid = spec->autocfg.speaker_pins[0];
++ hda_nid_t hp_nid = spec->autocfg.hp_pins[0];
++ hda_nid_t line_nid = spec->autocfg.line_out_pins[0];
++ unsigned int dac_vol1, dac_vol2;
++
++ if (speaker_nid) {
++ snd_hda_codec_write(codec, speaker_nid, 0,
++ AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
++ snd_hda_codec_write(codec, 0x0f, 0,
++ AC_VERB_SET_AMP_GAIN_MUTE,
++ AMP_IN_UNMUTE(1));
++ snd_hda_codec_write(codec, 0x10, 0,
++ AC_VERB_SET_AMP_GAIN_MUTE,
++ AMP_IN_UNMUTE(1));
++ } else {
++ snd_hda_codec_write(codec, 0x0f, 0,
++ AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1));
++ snd_hda_codec_write(codec, 0x10, 0,
++ AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1));
++ }
++
++ dac_vol1 = dac_vol2 = 0xb000 | 0x40; /* set max volume */
++ if (line_nid == 0x14)
++ dac_vol2 = AMP_OUT_ZERO;
++ else if (line_nid == 0x15)
++ dac_vol1 = AMP_OUT_ZERO;
++ if (hp_nid == 0x14)
++ dac_vol2 = AMP_OUT_ZERO;
++ else if (hp_nid == 0x15)
++ dac_vol1 = AMP_OUT_ZERO;
++ if (line_nid != 0x16 || hp_nid != 0x16 ||
++ spec->autocfg.line_out_pins[1] != 0x16 ||
++ spec->autocfg.line_out_pins[2] != 0x16)
++ dac_vol1 = dac_vol2 = AMP_OUT_ZERO;
++
++ snd_hda_codec_write(codec, 0x02, 0,
++ AC_VERB_SET_AMP_GAIN_MUTE, dac_vol1);
++ snd_hda_codec_write(codec, 0x03, 0,
++ AC_VERB_SET_AMP_GAIN_MUTE, dac_vol2);
++}
++
++/* pcm configuration: identiacal with ALC880 */
++#define alc268_pcm_analog_playback alc880_pcm_analog_playback
++#define alc268_pcm_analog_capture alc880_pcm_analog_capture
++#define alc268_pcm_digital_playback alc880_pcm_digital_playback
++
++/*
++ * BIOS auto configuration
++ */
++static int alc268_parse_auto_config(struct hda_codec *codec)
++{
++ struct alc_spec *spec = codec->spec;
++ int err;
++ static hda_nid_t alc268_ignore[] = { 0 };
++
++ err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
++ alc268_ignore);
++ if (err < 0)
++ return err;
++ if (!spec->autocfg.line_outs)
++ return 0; /* can't find valid BIOS pin config */
++
++ err = alc268_auto_create_multi_out_ctls(spec, &spec->autocfg);
++ if (err < 0)
++ return err;
++ err = alc268_auto_create_analog_input_ctls(spec, &spec->autocfg);
++ if (err < 0)
++ return err;
++
++ spec->multiout.max_channels = 2;
++
++ /* digital only support output */
++ if (spec->autocfg.dig_out_pin)
++ spec->multiout.dig_out_nid = ALC268_DIGOUT_NID;
++
++ if (spec->kctl_alloc)
++ spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
++
++ spec->init_verbs[spec->num_init_verbs++] = alc268_volume_init_verbs;
++ spec->num_mux_defs = 1;
++ spec->input_mux = &spec->private_imux;
++
++ return 1;
++}
++
++#define alc268_auto_init_multi_out alc882_auto_init_multi_out
++#define alc268_auto_init_hp_out alc882_auto_init_hp_out
++#define alc268_auto_init_analog_input alc882_auto_init_analog_input
++
++/* init callback for auto-configuration model -- overriding the default init */
++static void alc268_auto_init(struct hda_codec *codec)
++{
++ alc268_auto_init_multi_out(codec);
++ alc268_auto_init_hp_out(codec);
++ alc268_auto_init_mono_speaker_out(codec);
++ alc268_auto_init_analog_input(codec);
++}
++
++/*
++ * configuration and preset
++ */
++static const char *alc268_models[ALC268_MODEL_LAST] = {
++ [ALC268_3ST] = "3stack",
++ [ALC268_AUTO] = "auto",
++};
++
++static struct snd_pci_quirk alc268_cfg_tbl[] = {
++ SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST),
++ {}
++};
++
++static struct alc_config_preset alc268_presets[] = {
++ [ALC268_3ST] = {
++ .mixers = { alc268_base_mixer, alc268_capture_alt_mixer },
++ .init_verbs = { alc268_base_init_verbs },
++ .num_dacs = ARRAY_SIZE(alc268_dac_nids),
++ .dac_nids = alc268_dac_nids,
++ .num_adc_nids = ARRAY_SIZE(alc268_adc_nids_alt),
++ .adc_nids = alc268_adc_nids_alt,
++ .hp_nid = 0x03,
++ .dig_out_nid = ALC268_DIGOUT_NID,
++ .num_channel_mode = ARRAY_SIZE(alc268_modes),
++ .channel_mode = alc268_modes,
++ .input_mux = &alc268_capture_source,
++ },
++};
++
++static int patch_alc268(struct hda_codec *codec)
++{
++ struct alc_spec *spec;
++ int board_config;
++ int err;
++
++ spec = kcalloc(1, sizeof(*spec), GFP_KERNEL);
++ if (spec == NULL)
++ return -ENOMEM;
++
++ codec->spec = spec;
++
++ board_config = snd_hda_check_board_config(codec, ALC268_MODEL_LAST,
++ alc268_models,
++ alc268_cfg_tbl);
++
++ if (board_config < 0 || board_config >= ALC268_MODEL_LAST) {
++ printk(KERN_INFO "hda_codec: Unknown model for ALC268, "
++ "trying auto-probe from BIOS...\n");
++ board_config = ALC268_AUTO;
++ }
++
++ if (board_config == ALC268_AUTO) {
++ /* automatic parse from the BIOS config */
++ err = alc268_parse_auto_config(codec);
++ if (err < 0) {
++ alc_free(codec);
++ return err;
++ } else if (!err) {
++ printk(KERN_INFO
++ "hda_codec: Cannot set up configuration "
++ "from BIOS. Using base mode...\n");
++ board_config = ALC268_3ST;
++ }
++ }
++
++ if (board_config != ALC268_AUTO)
++ setup_preset(spec, &alc268_presets[board_config]);
++
++ spec->stream_name_analog = "ALC268 Analog";
++ spec->stream_analog_playback = &alc268_pcm_analog_playback;
++ spec->stream_analog_capture = &alc268_pcm_analog_capture;
++
++ spec->stream_name_digital = "ALC268 Digital";
++ spec->stream_digital_playback = &alc268_pcm_digital_playback;
++
++ if (board_config == ALC268_AUTO) {
++ if (!spec->adc_nids && spec->input_mux) {
++ /* check whether NID 0x07 is valid */
++ unsigned int wcap = get_wcaps(codec, 0x07);
++
++ /* get type */
++ wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
++ if (wcap != AC_WID_AUD_IN) {
++ spec->adc_nids = alc268_adc_nids_alt;
++ spec->num_adc_nids =
++ ARRAY_SIZE(alc268_adc_nids_alt);
++ spec->mixers[spec->num_mixers] =
++ alc268_capture_alt_mixer;
++ spec->num_mixers++;
++ } else {
++ spec->adc_nids = alc268_adc_nids;
++ spec->num_adc_nids =
++ ARRAY_SIZE(alc268_adc_nids);
++ spec->mixers[spec->num_mixers] =
++ alc268_capture_mixer;
++ spec->num_mixers++;
++ }
++ }
++ }
++ codec->patch_ops = alc_patch_ops;
++ if (board_config == ALC268_AUTO)
++ spec->init_hook = alc268_auto_init;
++
++ return 0;
++}
++
++/*
++ * ALC861 channel source setting (2/6 channel selection for 3-stack)
++ */
++
++/*
++ * set the path ways for 2 channel output
++ * need to set the codec line out and mic 1 pin widgets to inputs
++ */
++static struct hda_verb alc861_threestack_ch2_init[] = {
++ /* set pin widget 1Ah (line in) for input */
++ { 0x0c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
++ /* set pin widget 18h (mic1/2) for input, for mic also enable
++ * the vref
++ */
++ { 0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
++
++ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb00c },
++#if 0
++ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x01 << 8)) }, /*mic*/
++ { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8)) }, /*line-in*/
++#endif
++ { } /* end */
++};
++/*
++ * 6ch mode
++ * need to set the codec line out and mic 1 pin widgets to outputs
+ */
+ static struct hda_verb alc861_threestack_ch6_init[] = {
+ /* set pin widget 1Ah (line in) for output (Back Surround)*/
+@@ -8767,13 +9620,21 @@
+ SND_PCI_QUIRK(0x1043, 0x1335, "ASUS F2/3", ALC861_ASUS_LAPTOP),
+ SND_PCI_QUIRK(0x1043, 0x1338, "ASUS F2/3", ALC861_ASUS_LAPTOP),
+ SND_PCI_QUIRK(0x1043, 0x13d7, "ASUS A9rp", ALC861_ASUS_LAPTOP),
++ SND_PCI_QUIRK(0x1584, 0x9075, "Airis Praxis N1212", ALC861_ASUS_LAPTOP),
+ SND_PCI_QUIRK(0x1043, 0x1393, "ASUS", ALC861_ASUS),
++ SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS P1-AH2", ALC861_3ST_DIG),
+ SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba", ALC861_TOSHIBA),
+- SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba", ALC861_TOSHIBA),
++ /* FIXME: the entry below breaks Toshiba A100 (model=auto works!)
++ * Any other models that need this preset?
++ */
++ /* SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba", ALC861_TOSHIBA), */
+ SND_PCI_QUIRK(0x1584, 0x9072, "Uniwill m31", ALC861_UNIWILL_M31),
++ SND_PCI_QUIRK(0x1584, 0x9075, "Uniwill", ALC861_UNIWILL_M31),
+ SND_PCI_QUIRK(0x1584, 0x2b01, "Uniwill X40AIx", ALC861_UNIWILL_M31),
+ SND_PCI_QUIRK(0x1849, 0x0660, "Asrock 939SLI32", ALC660_3ST),
+ SND_PCI_QUIRK(0x8086, 0xd600, "Intel", ALC861_3ST),
++ SND_PCI_QUIRK(0x1462, 0x7254, "HP dx2200 (MSI MS-7254)", ALC861_3ST),
++ SND_PCI_QUIRK(0x1462, 0x7297, "HP dx2250 (MSI MS-7297)", ALC861_3ST),
+ {}
+ };
+
+@@ -9464,6 +10325,7 @@
+ */
+ static const char *alc861vd_models[ALC861VD_MODEL_LAST] = {
+ [ALC660VD_3ST] = "3stack-660",
++ [ALC660VD_3ST_DIG]= "3stack-660-digout",
+ [ALC861VD_3ST] = "3stack",
+ [ALC861VD_3ST_DIG] = "3stack-digout",
+ [ALC861VD_6ST_DIG] = "6stack-digout",
+@@ -9475,7 +10337,7 @@
+ static struct snd_pci_quirk alc861vd_cfg_tbl[] = {
+ SND_PCI_QUIRK(0x1043, 0x12e2, "Asus z35m", ALC660VD_3ST),
+ SND_PCI_QUIRK(0x1043, 0x1339, "Asus G1", ALC660VD_3ST),
+- SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660VD_3ST),
++ SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS", ALC660VD_3ST_DIG),
+ SND_PCI_QUIRK(0x10de, 0x03f0, "Realtek ALC660 demo", ALC660VD_3ST),
+ SND_PCI_QUIRK(0x1019, 0xa88d, "Realtek ALC660 demo", ALC660VD_3ST),
+
+@@ -9483,6 +10345,7 @@
+ SND_PCI_QUIRK(0x1179, 0xff01, "DALLAS", ALC861VD_DALLAS),
+ SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo 3000 C200", ALC861VD_LENOVO),
+ SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo", ALC861VD_LENOVO),
++ SND_PCI_QUIRK(0x1179, 0xff00, "Toshiba A135", ALC861VD_LENOVO),
+ {}
+ };
+
+@@ -9499,6 +10362,19 @@
+ .channel_mode = alc861vd_3stack_2ch_modes,
+ .input_mux = &alc861vd_capture_source,
+ },
++ [ALC660VD_3ST_DIG] = {
++ .mixers = { alc861vd_3st_mixer },
++ .init_verbs = { alc861vd_volume_init_verbs,
++ alc861vd_3stack_init_verbs },
++ .num_dacs = ARRAY_SIZE(alc660vd_dac_nids),
++ .dac_nids = alc660vd_dac_nids,
++ .dig_out_nid = ALC861VD_DIGOUT_NID,
++ .num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids),
++ .adc_nids = alc861vd_adc_nids,
++ .num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
++ .channel_mode = alc861vd_3stack_2ch_modes,
++ .input_mux = &alc861vd_capture_source,
++ },
+ [ALC861VD_3ST] = {
+ .mixers = { alc861vd_3st_mixer },
+ .init_verbs = { alc861vd_volume_init_verbs,
+@@ -10420,7 +11296,7 @@
+ for (i = 0; i < cfg->line_outs; i++) {
+ if (!spec->multiout.dac_nids[i])
+ continue;
+- nid = alc880_idx_to_dac(i);
++ nid = alc880_idx_to_mixer(i);
+ if (i == 2) {
+ /* Center/LFE */
+ err = add_control(spec, ALC_CTL_WIDGET_VOL,
+@@ -10643,14 +11519,10 @@
+ spec->num_mux_defs = 1;
+ spec->input_mux = &spec->private_imux;
+
+- if (err < 0)
+- return err;
+- else if (err > 0)
+- /* hack - override the init verbs */
+- spec->init_verbs[0] = alc662_auto_init_verbs;
++ spec->init_verbs[spec->num_init_verbs++] = alc662_auto_init_verbs;
+ spec->mixers[spec->num_mixers] = alc662_capture_mixer;
+ spec->num_mixers++;
+- return err;
++ return 1;
+ }
+
+ /* additional initialization for auto-configuration model */
+@@ -10687,7 +11559,7 @@
+ if (err < 0) {
+ alc_free(codec);
+ return err;
+- } else if (err) {
++ } else if (!err) {
+ printk(KERN_INFO
+ "hda_codec: Cannot set up configuration "
+ "from BIOS. Using base mode...\n");
+@@ -10724,6 +11596,7 @@
+ struct hda_codec_preset snd_hda_preset_realtek[] = {
+ { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 },
+ { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 },
++ { .id = 0x10ec0268, .name = "ALC268", .patch = patch_alc268 },
+ { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
+ .patch = patch_alc861 },
+ { .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd },
+--- linux-2.6.22.1.orig/sound/pci/hda/patch_si3054.c
++++ linux-2.6.22.1/sound/pci/hda/patch_si3054.c
+@@ -304,8 +304,12 @@
+ { .id = 0x10573055, .name = "Si3054", .patch = patch_si3054 },
+ { .id = 0x10573057, .name = "Si3054", .patch = patch_si3054 },
+ { .id = 0x10573155, .name = "Si3054", .patch = patch_si3054 },
++ /* VIA HDA on Clevo m540 */
++ { .id = 0x11063288, .name = "Si3054", .patch = patch_si3054 },
+ /* Asus A8J Modem (SM56) */
+ { .id = 0x15433155, .name = "Si3054", .patch = patch_si3054 },
++ /* LG LW20 modem */
++ { .id = 0x18540018, .name = "Si3054", .patch = patch_si3054 },
+ {}
+ };
+
+--- linux-2.6.22.1.orig/sound/pci/hda/patch_sigmatel.c
++++ linux-2.6.22.1/sound/pci/hda/patch_sigmatel.c
+@@ -44,6 +44,7 @@
+
+ enum {
+ STAC_9205_REF,
++ STAC_M43xx,
+ STAC_9205_MODELS
+ };
+
+@@ -59,11 +60,19 @@
+ STAC_D945_REF,
+ STAC_D945GTP3,
+ STAC_D945GTP5,
++ STAC_922X_DELL,
++ STAC_INTEL_MAC_V1,
++ STAC_INTEL_MAC_V2,
++ STAC_INTEL_MAC_V3,
++ STAC_INTEL_MAC_V4,
++ STAC_INTEL_MAC_V5,
++ /* for backward compitability */
+ STAC_MACMINI,
+ STAC_MACBOOK,
+ STAC_MACBOOK_PRO_V1,
+ STAC_MACBOOK_PRO_V2,
+ STAC_IMAC_INTEL,
++ STAC_IMAC_INTEL_20,
+ STAC_922X_MODELS
+ };
+
+@@ -210,7 +219,6 @@
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x0f, 0x14, 0x16, 0x17, 0x18,
+ 0x21, 0x22,
+-
+ };
+
+ static int stac92xx_dmux_enum_info(struct snd_kcontrol *kcontrol,
+@@ -326,8 +334,6 @@
+ };
+
+ static struct snd_kcontrol_new stac925x_mixer[] = {
+- HDA_CODEC_VOLUME("Master Playback Volume", 0xe, 0, HDA_OUTPUT),
+- HDA_CODEC_MUTE("Master Playback Switch", 0xe, 0, HDA_OUTPUT),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Input Source",
+@@ -549,44 +555,78 @@
+ 0x02a19320, 0x40000100,
+ };
+
+-static unsigned int macbook_pro_v1_pin_configs[10] = {
+- 0x0321e230, 0x03a1e020, 0x9017e110, 0x01014010,
+- 0x01a19021, 0x0381e021, 0x1345e240, 0x13c5e22e,
+- 0x02a19320, 0x400000fb
++static unsigned int intel_mac_v1_pin_configs[10] = {
++ 0x0121e21f, 0x400000ff, 0x9017e110, 0x400000fd,
++ 0x400000fe, 0x0181e020, 0x1145e030, 0x11c5e240,
++ 0x400000fc, 0x400000fb,
++};
++
++static unsigned int intel_mac_v2_pin_configs[10] = {
++ 0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd,
++ 0x400000fe, 0x0181e020, 0x1145e230, 0x500000fa,
++ 0x400000fc, 0x400000fb,
++};
++
++static unsigned int intel_mac_v3_pin_configs[10] = {
++ 0x0121e21f, 0x90a7012e, 0x9017e110, 0x400000fd,
++ 0x400000fe, 0x0181e020, 0x1145e230, 0x11c5e240,
++ 0x400000fc, 0x400000fb,
+ };
+
+-static unsigned int macbook_pro_v2_pin_configs[10] = {
+- 0x0221401f, 0x90a70120, 0x01813024, 0x01014010,
+- 0x400000fd, 0x01016011, 0x1345e240, 0x13c5e22e,
++static unsigned int intel_mac_v4_pin_configs[10] = {
++ 0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f,
++ 0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240,
+ 0x400000fc, 0x400000fb,
+ };
+
+-static unsigned int imac_intel_pin_configs[10] = {
+- 0x0121e230, 0x90a70120, 0x9017e110, 0x400000fe,
+- 0x400000fd, 0x0181e021, 0x1145e040, 0x400000fa,
++static unsigned int intel_mac_v5_pin_configs[10] = {
++ 0x0321e21f, 0x03a1e02e, 0x9017e110, 0x9017e11f,
++ 0x400000fe, 0x0381e020, 0x1345e230, 0x13c5e240,
+ 0x400000fc, 0x400000fb,
+ };
+
++static unsigned int stac922x_dell_pin_configs[10] = {
++ 0x0221121e, 0x408103ff, 0x02a1123e, 0x90100310,
++ 0x408003f1, 0x0221122f, 0x03451340, 0x40c003f2,
++ 0x50a003f3, 0x405003f4
++};
++
+ static unsigned int *stac922x_brd_tbl[STAC_922X_MODELS] = {
+ [STAC_D945_REF] = ref922x_pin_configs,
+ [STAC_D945GTP3] = d945gtp3_pin_configs,
+ [STAC_D945GTP5] = d945gtp5_pin_configs,
+- [STAC_MACMINI] = macbook_pro_v1_pin_configs,
+- [STAC_MACBOOK] = macbook_pro_v1_pin_configs,
+- [STAC_MACBOOK_PRO_V1] = macbook_pro_v1_pin_configs,
+- [STAC_MACBOOK_PRO_V2] = macbook_pro_v2_pin_configs,
+- [STAC_IMAC_INTEL] = imac_intel_pin_configs,
++ [STAC_922X_DELL] = stac922x_dell_pin_configs,
++ [STAC_INTEL_MAC_V1] = intel_mac_v1_pin_configs,
++ [STAC_INTEL_MAC_V2] = intel_mac_v2_pin_configs,
++ [STAC_INTEL_MAC_V3] = intel_mac_v3_pin_configs,
++ [STAC_INTEL_MAC_V4] = intel_mac_v4_pin_configs,
++ [STAC_INTEL_MAC_V5] = intel_mac_v5_pin_configs,
++ /* for backward compitability */
++ [STAC_MACMINI] = intel_mac_v3_pin_configs,
++ [STAC_MACBOOK] = intel_mac_v5_pin_configs,
++ [STAC_MACBOOK_PRO_V1] = intel_mac_v3_pin_configs,
++ [STAC_MACBOOK_PRO_V2] = intel_mac_v3_pin_configs,
++ [STAC_IMAC_INTEL] = intel_mac_v2_pin_configs,
++ [STAC_IMAC_INTEL_20] = intel_mac_v3_pin_configs,
+ };
+
+ static const char *stac922x_models[STAC_922X_MODELS] = {
+ [STAC_D945_REF] = "ref",
+ [STAC_D945GTP5] = "5stack",
+ [STAC_D945GTP3] = "3stack",
++ [STAC_922X_DELL] = "dell",
++ [STAC_INTEL_MAC_V1] = "intel-mac-v1",
++ [STAC_INTEL_MAC_V2] = "intel-mac-v2",
++ [STAC_INTEL_MAC_V3] = "intel-mac-v3",
++ [STAC_INTEL_MAC_V4] = "intel-mac-v4",
++ [STAC_INTEL_MAC_V5] = "intel-mac-v5",
++ /* for backward compitability */
+ [STAC_MACMINI] = "macmini",
+ [STAC_MACBOOK] = "macbook",
+ [STAC_MACBOOK_PRO_V1] = "macbook-pro-v1",
+ [STAC_MACBOOK_PRO_V2] = "macbook-pro",
+ [STAC_IMAC_INTEL] = "imac-intel",
++ [STAC_IMAC_INTEL_20] = "imac-intel-20",
+ };
+
+ static struct snd_pci_quirk stac922x_cfg_tbl[] = {
+@@ -649,7 +689,10 @@
+ /* other systems */
+ /* Apple Mac Mini (early 2006) */
+ SND_PCI_QUIRK(0x8384, 0x7680,
+- "Mac Mini", STAC_MACMINI),
++ "Mac Mini", STAC_INTEL_MAC_V3),
++ /* Dell */
++ SND_PCI_QUIRK(0x1028, 0x01d7, "Dell XPS M1210", STAC_922X_DELL),
++
+ {} /* terminator */
+ };
+
+@@ -730,7 +773,8 @@
+ };
+
+ static unsigned int *stac9205_brd_tbl[STAC_9205_MODELS] = {
+- ref9205_pin_configs,
++ [STAC_REF] = ref9205_pin_configs,
++ [STAC_M43xx] = NULL,
+ };
+
+ static const char *stac9205_models[STAC_9205_MODELS] = {
+@@ -741,6 +785,10 @@
+ /* SigmaTel reference board */
+ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
+ "DFI LanParty", STAC_9205_REF),
++ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x01f8,
++ "Dell Precision", STAC_M43xx),
++ SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x01ff,
++ "Dell Precision", STAC_M43xx),
+ {} /* terminator */
+ };
+
+@@ -770,33 +818,56 @@
+ return 0;
+ }
+
++static void stac92xx_set_config_reg(struct hda_codec *codec,
++ hda_nid_t pin_nid, unsigned int pin_config)
++{
++ int i;
++ snd_hda_codec_write(codec, pin_nid, 0,
++ AC_VERB_SET_CONFIG_DEFAULT_BYTES_0,
++ pin_config & 0x000000ff);
++ snd_hda_codec_write(codec, pin_nid, 0,
++ AC_VERB_SET_CONFIG_DEFAULT_BYTES_1,
++ (pin_config & 0x0000ff00) >> 8);
++ snd_hda_codec_write(codec, pin_nid, 0,
++ AC_VERB_SET_CONFIG_DEFAULT_BYTES_2,
++ (pin_config & 0x00ff0000) >> 16);
++ snd_hda_codec_write(codec, pin_nid, 0,
++ AC_VERB_SET_CONFIG_DEFAULT_BYTES_3,
++ pin_config >> 24);
++ i = snd_hda_codec_read(codec, pin_nid, 0,
++ AC_VERB_GET_CONFIG_DEFAULT,
++ 0x00);
++ snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x pin config %8.8x\n",
++ pin_nid, i);
++}
++
+ static void stac92xx_set_config_regs(struct hda_codec *codec)
+ {
+ int i;
+ struct sigmatel_spec *spec = codec->spec;
+- unsigned int pin_cfg;
+
+- if (! spec->pin_nids || ! spec->pin_configs)
+- return;
++ if (!spec->pin_configs)
++ return;
+
+- for (i = 0; i < spec->num_pins; i++) {
+- snd_hda_codec_write(codec, spec->pin_nids[i], 0,
+- AC_VERB_SET_CONFIG_DEFAULT_BYTES_0,
+- spec->pin_configs[i] & 0x000000ff);
+- snd_hda_codec_write(codec, spec->pin_nids[i], 0,
+- AC_VERB_SET_CONFIG_DEFAULT_BYTES_1,
+- (spec->pin_configs[i] & 0x0000ff00) >> 8);
+- snd_hda_codec_write(codec, spec->pin_nids[i], 0,
+- AC_VERB_SET_CONFIG_DEFAULT_BYTES_2,
+- (spec->pin_configs[i] & 0x00ff0000) >> 16);
+- snd_hda_codec_write(codec, spec->pin_nids[i], 0,
+- AC_VERB_SET_CONFIG_DEFAULT_BYTES_3,
+- spec->pin_configs[i] >> 24);
+- pin_cfg = snd_hda_codec_read(codec, spec->pin_nids[i], 0,
+- AC_VERB_GET_CONFIG_DEFAULT,
+- 0x00);
+- snd_printdd(KERN_INFO "hda_codec: pin nid %2.2x pin config %8.8x\n", spec->pin_nids[i], pin_cfg);
+- }
++ for (i = 0; i < spec->num_pins; i++)
++ stac92xx_set_config_reg(codec, spec->pin_nids[i],
++ spec->pin_configs[i]);
++}
++
++static void stac92xx_enable_gpio_mask(struct hda_codec *codec,
++ int gpio_mask, int gpio_data)
++{
++ /* Configure GPIOx as output */
++ snd_hda_codec_write(codec, codec->afg, 0,
++ AC_VERB_SET_GPIO_DIRECTION, gpio_mask);
++ /* Configure GPIOx as CMOS */
++ snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0x00000000);
++ /* Assert GPIOx */
++ snd_hda_codec_write(codec, codec->afg, 0,
++ AC_VERB_SET_GPIO_DATA, gpio_data);
++ /* Enable GPIOx */
++ snd_hda_codec_write(codec, codec->afg, 0,
++ AC_VERB_SET_GPIO_MASK, gpio_mask);
+ }
+
+ /*
+@@ -1168,7 +1239,7 @@
+ * and 9202/925x. For those, dac_nids[] must be hard-coded.
+ */
+ static int stac92xx_auto_fill_dac_nids(struct hda_codec *codec,
+- const struct auto_pin_cfg *cfg)
++ struct auto_pin_cfg *cfg)
+ {
+ struct sigmatel_spec *spec = codec->spec;
+ int i, j, conn_len = 0;
+@@ -1193,6 +1264,13 @@
+ }
+
+ if (j == conn_len) {
++ if (spec->multiout.num_dacs > 0) {
++ /* we have already working output pins,
++ * so let's drop the broken ones again
++ */
++ cfg->line_outs = spec->multiout.num_dacs;
++ break;
++ }
+ /* error out, no available DAC found */
+ snd_printk(KERN_ERR
+ "%s: No available DAC for pin 0x%x\n",
+@@ -1334,7 +1412,15 @@
+ continue;
+ add_spec_dacs(spec, nid);
+ }
+-
++ for (i = 0; i < cfg->line_outs; i++) {
++ nid = snd_hda_codec_read(codec, cfg->line_out_pins[i], 0,
++ AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
++ if (check_in_dac_nids(spec, nid))
++ nid = 0;
++ if (! nid)
++ continue;
++ add_spec_dacs(spec, nid);
++ }
+ for (i = old_num_dacs; i < spec->multiout.num_dacs; i++) {
+ static const char *pfxs[] = {
+ "Speaker", "External Speaker", "Speaker2",
+@@ -1891,7 +1977,7 @@
+ return -ENOMEM;
+
+ codec->spec = spec;
+- spec->num_pins = 8;
++ spec->num_pins = ARRAY_SIZE(stac9200_pin_nids);
+ spec->pin_nids = stac9200_pin_nids;
+ spec->board_config = snd_hda_check_board_config(codec, STAC_9200_MODELS,
+ stac9200_models,
+@@ -1941,7 +2027,7 @@
+ return -ENOMEM;
+
+ codec->spec = spec;
+- spec->num_pins = 8;
++ spec->num_pins = ARRAY_SIZE(stac925x_pin_nids);
+ spec->pin_nids = stac925x_pin_nids;
+ spec->board_config = snd_hda_check_board_config(codec, STAC_925x_MODELS,
+ stac925x_models,
+@@ -2013,29 +2099,41 @@
+ return -ENOMEM;
+
+ codec->spec = spec;
+- spec->num_pins = 10;
++ spec->num_pins = ARRAY_SIZE(stac922x_pin_nids);
+ spec->pin_nids = stac922x_pin_nids;
+ spec->board_config = snd_hda_check_board_config(codec, STAC_922X_MODELS,
+ stac922x_models,
+ stac922x_cfg_tbl);
+- if (spec->board_config == STAC_MACMINI) {
++ if (spec->board_config == STAC_INTEL_MAC_V3) {
+ spec->gpio_mute = 1;
+ /* Intel Macs have all same PCI SSID, so we need to check
+ * codec SSID to distinguish the exact models
+ */
+ printk(KERN_INFO "hda_codec: STAC922x, Apple subsys_id=%x\n", codec->subsystem_id);
+ switch (codec->subsystem_id) {
+- case 0x106b0a00: /* MacBook First generatoin */
+- spec->board_config = STAC_MACBOOK;
++
++ case 0x106b0800:
++ spec->board_config = STAC_INTEL_MAC_V1;
++ break;
++ case 0x106b0600:
++ case 0x106b0700:
++ spec->board_config = STAC_INTEL_MAC_V2;
+ break;
+- case 0x106b0200: /* MacBook Pro first generation */
+- spec->board_config = STAC_MACBOOK_PRO_V1;
++ case 0x106b0e00:
++ case 0x106b0f00:
++ case 0x106b1600:
++ case 0x106b1700:
++ case 0x106b0200:
++ case 0x106b1e00:
++ spec->board_config = STAC_INTEL_MAC_V3;
+ break;
+- case 0x106b1e00: /* MacBook Pro second generation */
+- spec->board_config = STAC_MACBOOK_PRO_V2;
++ case 0x106b1a00:
++ case 0x00000100:
++ spec->board_config = STAC_INTEL_MAC_V4;
+ break;
+- case 0x106b0700: /* Intel-based iMac */
+- spec->board_config = STAC_IMAC_INTEL;
++ case 0x106b0a00:
++ case 0x106b2200:
++ spec->board_config = STAC_INTEL_MAC_V5;
+ break;
+ }
+ }
+@@ -2082,6 +2180,13 @@
+
+ codec->patch_ops = stac92xx_patch_ops;
+
++ /* Fix Mux capture level; max to 2 */
++ snd_hda_override_amp_caps(codec, 0x12, HDA_OUTPUT,
++ (0 << AC_AMPCAP_OFFSET_SHIFT) |
++ (2 << AC_AMPCAP_NUM_STEPS_SHIFT) |
++ (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) |
++ (0 << AC_AMPCAP_MUTE_SHIFT));
++
+ return 0;
+ }
+
+@@ -2095,7 +2200,7 @@
+ return -ENOMEM;
+
+ codec->spec = spec;
+- spec->num_pins = 14;
++ spec->num_pins = ARRAY_SIZE(stac927x_pin_nids);
+ spec->pin_nids = stac927x_pin_nids;
+ spec->board_config = snd_hda_check_board_config(codec, STAC_927X_MODELS,
+ stac927x_models,
+@@ -2141,7 +2246,9 @@
+ }
+
+ spec->multiout.dac_nids = spec->dac_nids;
+-
++ /* GPIO0 High = Enable EAPD */
++ stac92xx_enable_gpio_mask(codec, 0x00000001, 0x00000001);
++
+ err = stac92xx_parse_auto_config(codec, 0x1e, 0x20);
+ if (!err) {
+ if (spec->board_config < 0) {
+@@ -2159,27 +2266,20 @@
+
+ codec->patch_ops = stac92xx_patch_ops;
+
+- /* Fix Mux capture level; max to 2 */
+- snd_hda_override_amp_caps(codec, 0x12, HDA_OUTPUT,
+- (0 << AC_AMPCAP_OFFSET_SHIFT) |
+- (2 << AC_AMPCAP_NUM_STEPS_SHIFT) |
+- (0x27 << AC_AMPCAP_STEP_SIZE_SHIFT) |
+- (0 << AC_AMPCAP_MUTE_SHIFT));
+-
+ return 0;
+ }
+
+ static int patch_stac9205(struct hda_codec *codec)
+ {
+ struct sigmatel_spec *spec;
+- int err;
++ int err, gpio_mask, gpio_data;
+
+ spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+ if (spec == NULL)
+ return -ENOMEM;
+
+ codec->spec = spec;
+- spec->num_pins = 14;
++ spec->num_pins = ARRAY_SIZE(stac9205_pin_nids);
+ spec->pin_nids = stac9205_pin_nids;
+ spec->board_config = snd_hda_check_board_config(codec, STAC_9205_MODELS,
+ stac9205_models,
+@@ -2209,19 +2309,21 @@
+ spec->mixer = stac9205_mixer;
+
+ spec->multiout.dac_nids = spec->dac_nids;
++
++ if (spec->board_config == STAC_M43xx) {
++ /* Enable SPDIF in/out */
++ stac92xx_set_config_reg(codec, 0x1f, 0x01441030);
++ stac92xx_set_config_reg(codec, 0x20, 0x1c410030);
++
++ gpio_mask = 0x00000007; /* GPIO0-2 */
++ /* GPIO0 High = EAPD, GPIO1 Low = DRM,
++ * GPIO2 High = Headphone Mute
++ */
++ gpio_data = 0x00000005;
++ } else
++ gpio_mask = gpio_data = 0x00000001; /* GPIO0 High = EAPD */
+
+- /* Configure GPIO0 as EAPD output */
+- snd_hda_codec_write(codec, codec->afg, 0,
+- AC_VERB_SET_GPIO_DIRECTION, 0x00000001);
+- /* Configure GPIO0 as CMOS */
+- snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0x00000000);
+- /* Assert GPIO0 high */
+- snd_hda_codec_write(codec, codec->afg, 0,
+- AC_VERB_SET_GPIO_DATA, 0x00000001);
+- /* Enable GPIO0 */
+- snd_hda_codec_write(codec, codec->afg, 0,
+- AC_VERB_SET_GPIO_MASK, 0x00000001);
+-
++ stac92xx_enable_gpio_mask(codec, gpio_mask, gpio_data);
+ err = stac92xx_parse_auto_config(codec, 0x1f, 0x20);
+ if (!err) {
+ if (spec->board_config < 0) {
+@@ -2256,8 +2358,8 @@
+ .num_items = 2,
+ .items = {
+ /* { "HP", 0x0 }, */
+- { "Line", 0x1 },
+- { "Mic", 0x2 },
++ { "Mic Jack", 0x1 },
++ { "Internal Mic", 0x2 },
+ { "PCM", 0x3 },
+ }
+ };
+@@ -2268,7 +2370,7 @@
+ {0x0d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? (<- 0x2) */
+ {0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */
+- {0x15, AC_VERB_SET_CONNECT_SEL, 0x2}, /* mic-sel: 0a,0d,14,02 */
++ {0x15, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mic-sel: 0a,0d,14,02 */
+ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */
+ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */
+ {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* capture sw/vol -> 0x8 */
+@@ -2284,7 +2386,7 @@
+ {0x0e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, /* CD */
+ /* {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },*/ /* Optical Out */
+ {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, /* Mic? */
+- {0x15, AC_VERB_SET_CONNECT_SEL, 0x2}, /* mic-sel: 0a,0d,14,02 */
++ {0x15, AC_VERB_SET_CONNECT_SEL, 0x1}, /* mic-sel: 0a,0d,14,02 */
+ {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* HP */
+ {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Speaker */
+ /* {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},*/ /* Optical Out */
+--- linux-2.6.22.1.orig/sound/pci/ice1712/revo.c
++++ linux-2.6.22.1/sound/pci/ice1712/revo.c
+@@ -186,7 +186,12 @@
+ #define AK_DAC(xname,xch) { .name = xname, .num_channels = xch }
+
+ static const struct snd_akm4xxx_dac_channel revo71_front[] = {
+- AK_DAC("PCM Playback Volume", 2)
++ {
++ .name = "PCM Playback Volume",
++ .num_channels = 2,
++ /* front channels DAC supports muting */
++ .switch_name = "PCM Playback Switch",
++ },
+ };
+
+ static const struct snd_akm4xxx_dac_channel revo71_surround[] = {
+--- linux-2.6.22.1.orig/sound/pci/nm256/nm256.c
++++ linux-2.6.22.1/sound/pci/nm256/nm256.c
+@@ -1533,7 +1533,8 @@
+ printk(KERN_ERR " force the driver to load by "
+ "passing in the module parameter\n");
+ printk(KERN_ERR " force_ac97=1\n");
+- printk(KERN_ERR " or try sb16 or cs423x drivers instead.\n");
++ printk(KERN_ERR " or try sb16, opl3sa2, or "
++ "cs423x drivers instead.\n");
+ err = -ENXIO;
+ goto __error;
+ }
+--- linux-2.6.22.1.orig/sound/pci/rme9652/rme9652.c
++++ linux-2.6.22.1/sound/pci/rme9652/rme9652.c
+@@ -406,7 +406,7 @@
+ } else if (!frag)
+ return 0;
+ offset -= rme9652->max_jitter;
+- if (offset < 0)
++ if ((int)offset < 0)
+ offset += period_size * 2;
+ } else {
+ if (offset > period_size + rme9652->max_jitter) {
+--- linux-2.6.22.1.orig/sound/pci/via82xx.c
++++ linux-2.6.22.1/sound/pci/via82xx.c
+@@ -2098,7 +2098,7 @@
+ pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval);
+ if (pval & VIA_ACLINK_C00_READY) /* primary codec ready */
+ break;
+- schedule_timeout_uninterruptible(1);
++ schedule_timeout(1);
+ } while (time_before(jiffies, end_time));
+
+ if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY)
+@@ -2117,7 +2117,7 @@
+ chip->ac97_secondary = 1;
+ goto __ac97_ok2;
+ }
+- schedule_timeout_interruptible(1);
++ schedule_timeout(1);
+ } while (time_before(jiffies, end_time));
+ /* This is ok, the most of motherboards have only one codec */
+
+--- linux-2.6.22.1.orig/sound/pci/via82xx_modem.c
++++ linux-2.6.22.1/sound/pci/via82xx_modem.c
+@@ -983,7 +983,7 @@
+ pci_read_config_byte(chip->pci, VIA_ACLINK_STAT, &pval);
+ if (pval & VIA_ACLINK_C00_READY) /* primary codec ready */
+ break;
+- schedule_timeout_uninterruptible(1);
++ schedule_timeout(1);
+ } while (time_before(jiffies, end_time));
+
+ if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY)
+@@ -1001,7 +1001,7 @@
+ chip->ac97_secondary = 1;
+ goto __ac97_ok2;
+ }
+- schedule_timeout_interruptible(1);
++ schedule_timeout(1);
+ } while (time_before(jiffies, end_time));
+ /* This is ok, the most of motherboards have only one codec */
+
+--- linux-2.6.22.1.orig/sound/ppc/Kconfig
++++ linux-2.6.22.1/sound/ppc/Kconfig
+@@ -33,3 +33,23 @@
+ option.
+
+ endmenu
++
++menu "ALSA PowerPC devices"
++ depends on SND!=n && ( PPC64 || PPC32 )
++
++config SND_PS3
++ tristate "PS3 Audio support"
++ depends on SND && PS3_PS3AV
++ select SND_PCM
++ default m
++ help
++ Say Y here to include support for audio on the PS3
++
++ To compile this driver as a module, choose M here: the module
++ will be called snd_ps3.
++
++config SND_PS3_DEFAULT_START_DELAY
++ int "Startup delay time in ms"
++ depends on SND_PS3
++ default "2000"
++endmenu
+--- linux-2.6.22.1.orig/sound/ppc/Makefile
++++ linux-2.6.22.1/sound/ppc/Makefile
+@@ -6,4 +6,5 @@
+ snd-powermac-objs := powermac.o pmac.o awacs.o burgundy.o daca.o tumbler.o keywest.o beep.o
+
+ # Toplevel Module Dependency
+-obj-$(CONFIG_SND_POWERMAC) += snd-powermac.o
++obj-$(CONFIG_SND_POWERMAC) += snd-powermac.o
++obj-$(CONFIG_SND_PS3) += snd_ps3.o
+--- /dev/null
++++ linux-2.6.22.1/sound/ppc/snd_ps3.c
+@@ -0,0 +1,1125 @@
++/*
++ * Audio support for PS3
++ * Copyright (C) 2007 Sony Computer Entertainment Inc.
++ * All rights reserved.
++ * Copyright 2006, 2007 Sony Corporation
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; version 2 of the Licence.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/io.h>
++#include <linux/interrupt.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/initval.h>
++#include <sound/pcm.h>
++#include <sound/asound.h>
++#include <sound/memalloc.h>
++#include <sound/pcm_params.h>
++#include <sound/control.h>
++#include <linux/dmapool.h>
++#include <linux/dma-mapping.h>
++#include <asm/firmware.h>
++#include <linux/io.h>
++#include <asm/dma.h>
++#include <asm/lv1call.h>
++#include <asm/ps3.h>
++#include <asm/ps3av.h>
++
++#include "snd_ps3_reg.h"
++#include "snd_ps3.h"
++
++MODULE_LICENSE("GPL v2");
++MODULE_DESCRIPTION("PS3 sound driver");
++MODULE_AUTHOR("Sony Computer Entertainment Inc.");
++
++/* module entries */
++static int __init snd_ps3_init(void);
++static void __exit snd_ps3_exit(void);
++
++/* ALSA snd driver ops */
++static int snd_ps3_pcm_open(struct snd_pcm_substream *substream);
++static int snd_ps3_pcm_close(struct snd_pcm_substream *substream);
++static int snd_ps3_pcm_prepare(struct snd_pcm_substream *substream);
++static int snd_ps3_pcm_trigger(struct snd_pcm_substream *substream,
++ int cmd);
++static snd_pcm_uframes_t snd_ps3_pcm_pointer(struct snd_pcm_substream
++ *substream);
++static int snd_ps3_pcm_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *hw_params);
++static int snd_ps3_pcm_hw_free(struct snd_pcm_substream *substream);
++
++
++/* ps3_system_bus_driver entries */
++static int __init snd_ps3_driver_probe(struct ps3_system_bus_device *dev);
++static int snd_ps3_driver_remove(struct ps3_system_bus_device *dev);
++
++/* address setup */
++static int snd_ps3_map_mmio(void);
++static void snd_ps3_unmap_mmio(void);
++static int snd_ps3_allocate_irq(void);
++static void snd_ps3_free_irq(void);
++static void snd_ps3_audio_set_base_addr(uint64_t ioaddr_start);
++
++/* interrupt handler */
++static irqreturn_t snd_ps3_interrupt(int irq, void *dev_id);
++
++
++/* set sampling rate/format */
++static int snd_ps3_set_avsetting(struct snd_pcm_substream *substream);
++/* take effect parameter change */
++static int snd_ps3_change_avsetting(struct snd_ps3_card_info *card);
++/* initialize avsetting and take it effect */
++static int snd_ps3_init_avsetting(struct snd_ps3_card_info *card);
++/* setup dma */
++static int snd_ps3_program_dma(struct snd_ps3_card_info *card,
++ enum snd_ps3_dma_filltype filltype);
++static void snd_ps3_wait_for_dma_stop(struct snd_ps3_card_info *card);
++
++static dma_addr_t v_to_bus(struct snd_ps3_card_info *, void *vaddr, int ch);
++
++
++module_init(snd_ps3_init);
++module_exit(snd_ps3_exit);
++
++/*
++ * global
++ */
++static struct snd_ps3_card_info the_card;
++
++static int snd_ps3_start_delay = CONFIG_SND_PS3_DEFAULT_START_DELAY;
++
++module_param_named(start_delay, snd_ps3_start_delay, uint, 0644);
++MODULE_PARM_DESC(start_delay, "time to insert silent data in milisec");
++
++static int index = SNDRV_DEFAULT_IDX1;
++static char *id = SNDRV_DEFAULT_STR1;
++
++module_param(index, int, 0444);
++MODULE_PARM_DESC(index, "Index value for PS3 soundchip.");
++module_param(id, charp, 0444);
++MODULE_PARM_DESC(id, "ID string for PS3 soundchip.");
++
++
++/*
++ * PS3 audio register access
++ */
++static inline u32 read_reg(unsigned int reg)
++{
++ return in_be32(the_card.mapped_mmio_vaddr + reg);
++}
++static inline void write_reg(unsigned int reg, u32 val)
++{
++ out_be32(the_card.mapped_mmio_vaddr + reg, val);
++}
++static inline void update_reg(unsigned int reg, u32 or_val)
++{
++ u32 newval = read_reg(reg) | or_val;
++ write_reg(reg, newval);
++}
++static inline void update_mask_reg(unsigned int reg, u32 mask, u32 or_val)
++{
++ u32 newval = (read_reg(reg) & mask) | or_val;
++ write_reg(reg, newval);
++}
++
++/*
++ * ALSA defs
++ */
++const static struct snd_pcm_hardware snd_ps3_pcm_hw = {
++ .info = (SNDRV_PCM_INFO_MMAP |
++ SNDRV_PCM_INFO_NONINTERLEAVED |
++ SNDRV_PCM_INFO_MMAP_VALID),
++ .formats = (SNDRV_PCM_FMTBIT_S16_BE |
++ SNDRV_PCM_FMTBIT_S24_BE),
++ .rates = (SNDRV_PCM_RATE_44100 |
++ SNDRV_PCM_RATE_48000 |
++ SNDRV_PCM_RATE_88200 |
++ SNDRV_PCM_RATE_96000),
++ .rate_min = 44100,
++ .rate_max = 96000,
++
++ .channels_min = 2, /* stereo only */
++ .channels_max = 2,
++
++ .buffer_bytes_max = PS3_AUDIO_FIFO_SIZE * 64,
++
++ /* interrupt by four stages */
++ .period_bytes_min = PS3_AUDIO_FIFO_STAGE_SIZE * 4,
++ .period_bytes_max = PS3_AUDIO_FIFO_STAGE_SIZE * 4,
++
++ .periods_min = 16,
++ .periods_max = 32, /* buffer_size_max/ period_bytes_max */
++
++ .fifo_size = PS3_AUDIO_FIFO_SIZE
++};
++
++static struct snd_pcm_ops snd_ps3_pcm_spdif_ops =
++{
++ .open = snd_ps3_pcm_open,
++ .close = snd_ps3_pcm_close,
++ .prepare = snd_ps3_pcm_prepare,
++ .ioctl = snd_pcm_lib_ioctl,
++ .trigger = snd_ps3_pcm_trigger,
++ .pointer = snd_ps3_pcm_pointer,
++ .hw_params = snd_ps3_pcm_hw_params,
++ .hw_free = snd_ps3_pcm_hw_free
++};
++
++static int snd_ps3_verify_dma_stop(struct snd_ps3_card_info *card,
++ int count, int force_stop)
++{
++ int dma_ch, done, retries, stop_forced = 0;
++ uint32_t status;
++
++ for (dma_ch = 0; dma_ch < 8; dma_ch ++) {
++ retries = count;
++ do {
++ status = read_reg(PS3_AUDIO_KICK(dma_ch)) &
++ PS3_AUDIO_KICK_STATUS_MASK;
++ switch (status) {
++ case PS3_AUDIO_KICK_STATUS_DONE:
++ case PS3_AUDIO_KICK_STATUS_NOTIFY:
++ case PS3_AUDIO_KICK_STATUS_CLEAR:
++ case PS3_AUDIO_KICK_STATUS_ERROR:
++ done = 1;
++ break;
++ default:
++ done = 0;
++ udelay(10);
++ }
++ } while (!done && --retries);
++ if (!retries && force_stop) {
++ pr_info("%s: DMA ch %d is not stopped.",
++ __func__, dma_ch);
++ /* last resort. force to stop dma.
++ * NOTE: this cause DMA done interrupts
++ */
++ update_reg(PS3_AUDIO_CONFIG, PS3_AUDIO_CONFIG_CLEAR);
++ stop_forced = 1;
++ }
++ }
++ return stop_forced;
++}
++
++/*
++ * wait for all dma is done.
++ * NOTE: caller should reset card->running before call.
++ * If not, the interrupt handler will re-start DMA,
++ * then DMA is never stopped.
++ */
++static void snd_ps3_wait_for_dma_stop(struct snd_ps3_card_info *card)
++{
++ int stop_forced;
++ /*
++ * wait for the last dma is done
++ */
++
++ /*
++ * expected maximum DMA done time is 5.7ms + something (DMA itself).
++ * 5.7ms is from 16bit/sample 2ch 44.1Khz; the time next
++ * DMA kick event would occur.
++ */
++ stop_forced = snd_ps3_verify_dma_stop(card, 700, 1);
++
++ /*
++ * clear outstanding interrupts.
++ */
++ update_reg(PS3_AUDIO_INTR_0, 0);
++ update_reg(PS3_AUDIO_AX_IS, 0);
++
++ /*
++ *revert CLEAR bit since it will not reset automatically after DMA stop
++ */
++ if (stop_forced)
++ update_mask_reg(PS3_AUDIO_CONFIG, ~PS3_AUDIO_CONFIG_CLEAR, 0);
++ /* ensure the hardware sees changes */
++ wmb();
++}
++
++static void snd_ps3_kick_dma(struct snd_ps3_card_info *card)
++{
++
++ update_reg(PS3_AUDIO_KICK(0), PS3_AUDIO_KICK_REQUEST);
++ /* ensure the hardware sees the change */
++ wmb();
++}
++
++/*
++ * convert virtual addr to ioif bus addr.
++ */
++static dma_addr_t v_to_bus(struct snd_ps3_card_info *card,
++ void * paddr,
++ int ch)
++{
++ return card->dma_start_bus_addr[ch] +
++ (paddr - card->dma_start_vaddr[ch]);
++};
++
++
++/*
++ * increment ring buffer pointer.
++ * NOTE: caller must hold write spinlock
++ */
++static void snd_ps3_bump_buffer(struct snd_ps3_card_info *card,
++ enum snd_ps3_ch ch, size_t byte_count,
++ int stage)
++{
++ if (!stage)
++ card->dma_last_transfer_vaddr[ch] =
++ card->dma_next_transfer_vaddr[ch];
++ card->dma_next_transfer_vaddr[ch] += byte_count;
++ if ((card->dma_start_vaddr[ch] + (card->dma_buffer_size / 2)) <=
++ card->dma_next_transfer_vaddr[ch]) {
++ card->dma_next_transfer_vaddr[ch] = card->dma_start_vaddr[ch];
++ }
++}
++/*
++ * setup dmac to send data to audio and attenuate samples on the ring buffer
++ */
++static int snd_ps3_program_dma(struct snd_ps3_card_info *card,
++ enum snd_ps3_dma_filltype filltype)
++{
++ /* this dmac does not support over 4G */
++ uint32_t dma_addr;
++ int fill_stages, dma_ch, stage;
++ enum snd_ps3_ch ch;
++ uint32_t ch0_kick_event = 0; /* initialize to mute gcc */
++ void *start_vaddr;
++ unsigned long irqsave;
++ int silent = 0;
++
++ switch (filltype) {
++ case SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL:
++ silent = 1;
++ /* intentionally fall thru */
++ case SND_PS3_DMA_FILLTYPE_FIRSTFILL:
++ ch0_kick_event = PS3_AUDIO_KICK_EVENT_ALWAYS;
++ break;
++
++ case SND_PS3_DMA_FILLTYPE_SILENT_RUNNING:
++ silent = 1;
++ /* intentionally fall thru */
++ case SND_PS3_DMA_FILLTYPE_RUNNING:
++ ch0_kick_event = PS3_AUDIO_KICK_EVENT_SERIALOUT0_EMPTY;
++ break;
++ }
++
++ snd_ps3_verify_dma_stop(card, 700, 0);
++ fill_stages = 4;
++ spin_lock_irqsave(&card->dma_lock, irqsave);
++ for (ch = 0; ch < 2; ch++) {
++ start_vaddr = card->dma_next_transfer_vaddr[0];
++ for (stage = 0; stage < fill_stages; stage ++) {
++ dma_ch = stage * 2 + ch;
++ if (silent)
++ dma_addr = card->null_buffer_start_dma_addr;
++ else
++ dma_addr =
++ v_to_bus(card,
++ card->dma_next_transfer_vaddr[ch],
++ ch);
++
++ write_reg(PS3_AUDIO_SOURCE(dma_ch),
++ (PS3_AUDIO_SOURCE_TARGET_SYSTEM_MEMORY |
++ dma_addr));
++
++ /* dst: fixed to 3wire#0 */
++ if (ch == 0)
++ write_reg(PS3_AUDIO_DEST(dma_ch),
++ (PS3_AUDIO_DEST_TARGET_AUDIOFIFO |
++ PS3_AUDIO_AO_3W_LDATA(0)));
++ else
++ write_reg(PS3_AUDIO_DEST(dma_ch),
++ (PS3_AUDIO_DEST_TARGET_AUDIOFIFO |
++ PS3_AUDIO_AO_3W_RDATA(0)));
++
++ /* count always 1 DMA block (1/2 stage = 128 bytes) */
++ write_reg(PS3_AUDIO_DMASIZE(dma_ch), 0);
++ /* bump pointer if needed */
++ if (!silent)
++ snd_ps3_bump_buffer(card, ch,
++ PS3_AUDIO_DMAC_BLOCK_SIZE,
++ stage);
++
++ /* kick event */
++ if (dma_ch == 0)
++ write_reg(PS3_AUDIO_KICK(dma_ch),
++ ch0_kick_event);
++ else
++ write_reg(PS3_AUDIO_KICK(dma_ch),
++ PS3_AUDIO_KICK_EVENT_AUDIO_DMA(dma_ch
++ - 1) |
++ PS3_AUDIO_KICK_REQUEST);
++ }
++ }
++ /* ensure the hardware sees the change */
++ wmb();
++ spin_unlock_irqrestore(&card->dma_lock, irqsave);
++
++ return 0;
++}
++
++/*
++ * audio mute on/off
++ * mute_on : 0 output enabled
++ * 1 mute
++ */
++static int snd_ps3_mute(int mute_on)
++{
++ return ps3av_audio_mute(mute_on);
++}
++
++/*
++ * PCM operators
++ */
++static int snd_ps3_pcm_open(struct snd_pcm_substream *substream)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream);
++ int pcm_index;
++
++ pcm_index = substream->pcm->device;
++ /* to retrieve substream/runtime in interrupt handler */
++ card->substream = substream;
++
++ runtime->hw = snd_ps3_pcm_hw;
++
++ card->start_delay = snd_ps3_start_delay;
++
++ /* mute off */
++ snd_ps3_mute(0); /* this function sleep */
++
++ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
++ PS3_AUDIO_FIFO_STAGE_SIZE * 4 * 2);
++ return 0;
++};
++
++static int snd_ps3_pcm_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *hw_params)
++{
++ size_t size;
++
++ /* alloc transport buffer */
++ size = params_buffer_bytes(hw_params);
++ snd_pcm_lib_malloc_pages(substream, size);
++ return 0;
++};
++
++static int snd_ps3_delay_to_bytes(struct snd_pcm_substream *substream,
++ unsigned int delay_ms)
++{
++ int ret;
++ int rate ;
++
++ rate = substream->runtime->rate;
++ ret = snd_pcm_format_size(substream->runtime->format,
++ rate * delay_ms / 1000)
++ * substream->runtime->channels;
++
++ pr_debug(KERN_ERR "%s: time=%d rate=%d bytes=%ld, frames=%d, ret=%d\n",
++ __func__,
++ delay_ms,
++ rate,
++ snd_pcm_format_size(substream->runtime->format, rate),
++ rate * delay_ms / 1000,
++ ret);
++
++ return ret;
++};
++
++static int snd_ps3_pcm_prepare(struct snd_pcm_substream *substream)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream);
++ unsigned long irqsave;
++
++ if (!snd_ps3_set_avsetting(substream)) {
++ /* some parameter changed */
++ write_reg(PS3_AUDIO_AX_IE,
++ PS3_AUDIO_AX_IE_ASOBEIE(0) |
++ PS3_AUDIO_AX_IE_ASOBUIE(0));
++ /*
++ * let SPDIF device re-lock with SPDIF signal,
++ * start with some silence
++ */
++ card->silent = snd_ps3_delay_to_bytes(substream,
++ card->start_delay) /
++ (PS3_AUDIO_FIFO_STAGE_SIZE * 4); /* every 4 times */
++ }
++
++ /* restart ring buffer pointer */
++ spin_lock_irqsave(&card->dma_lock, irqsave);
++ {
++ card->dma_buffer_size = runtime->dma_bytes;
++
++ card->dma_last_transfer_vaddr[SND_PS3_CH_L] =
++ card->dma_next_transfer_vaddr[SND_PS3_CH_L] =
++ card->dma_start_vaddr[SND_PS3_CH_L] =
++ runtime->dma_area;
++ card->dma_start_bus_addr[SND_PS3_CH_L] = runtime->dma_addr;
++
++ card->dma_last_transfer_vaddr[SND_PS3_CH_R] =
++ card->dma_next_transfer_vaddr[SND_PS3_CH_R] =
++ card->dma_start_vaddr[SND_PS3_CH_R] =
++ runtime->dma_area + (runtime->dma_bytes / 2);
++ card->dma_start_bus_addr[SND_PS3_CH_R] =
++ runtime->dma_addr + (runtime->dma_bytes / 2);
++
++ pr_debug("%s: vaddr=%p bus=%#lx\n", __func__,
++ card->dma_start_vaddr[SND_PS3_CH_L],
++ card->dma_start_bus_addr[SND_PS3_CH_L]);
++
++ }
++ spin_unlock_irqrestore(&card->dma_lock, irqsave);
++
++ /* ensure the hardware sees the change */
++ mb();
++
++ return 0;
++};
++
++static int snd_ps3_pcm_trigger(struct snd_pcm_substream *substream,
++ int cmd)
++{
++ struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream);
++ int ret = 0;
++
++ switch (cmd) {
++ case SNDRV_PCM_TRIGGER_START:
++ /* clear outstanding interrupts */
++ update_reg(PS3_AUDIO_AX_IS, 0);
++
++ spin_lock(&card->dma_lock);
++ {
++ card->running = 1;
++ }
++ spin_unlock(&card->dma_lock);
++
++ snd_ps3_program_dma(card,
++ SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
++ snd_ps3_kick_dma(card);
++ while (read_reg(PS3_AUDIO_KICK(7)) &
++ PS3_AUDIO_KICK_STATUS_MASK) {
++ udelay(1);
++ }
++ snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_RUNNING);
++ snd_ps3_kick_dma(card);
++ break;
++
++ case SNDRV_PCM_TRIGGER_STOP:
++ spin_lock(&card->dma_lock);
++ {
++ card->running = 0;
++ }
++ spin_unlock(&card->dma_lock);
++ snd_ps3_wait_for_dma_stop(card);
++ break;
++ default:
++ break;
++
++ }
++
++ return ret;
++};
++
++/*
++ * report current pointer
++ */
++static snd_pcm_uframes_t snd_ps3_pcm_pointer(
++ struct snd_pcm_substream *substream)
++{
++ struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream);
++ size_t bytes;
++ snd_pcm_uframes_t ret;
++
++ spin_lock(&card->dma_lock);
++ {
++ bytes = (size_t)(card->dma_last_transfer_vaddr[SND_PS3_CH_L] -
++ card->dma_start_vaddr[SND_PS3_CH_L]);
++ }
++ spin_unlock(&card->dma_lock);
++
++ ret = bytes_to_frames(substream->runtime, bytes * 2);
++
++ return ret;
++};
++
++static int snd_ps3_pcm_hw_free(struct snd_pcm_substream *substream)
++{
++ int ret;
++ ret = snd_pcm_lib_free_pages(substream);
++ return ret;
++};
++
++static int snd_ps3_pcm_close(struct snd_pcm_substream *substream)
++{
++ /* mute on */
++ snd_ps3_mute(1);
++ return 0;
++};
++
++static void snd_ps3_audio_fixup(struct snd_ps3_card_info *card)
++{
++ /*
++ * avsetting driver seems to never change the followings
++ * so, init them here once
++ */
++
++ /* no dma interrupt needed */
++ write_reg(PS3_AUDIO_INTR_EN_0, 0);
++
++ /* use every 4 buffer empty interrupt */
++ update_mask_reg(PS3_AUDIO_AX_IC,
++ PS3_AUDIO_AX_IC_AASOIMD_MASK,
++ PS3_AUDIO_AX_IC_AASOIMD_EVERY4);
++
++ /* enable 3wire clocks */
++ update_mask_reg(PS3_AUDIO_AO_3WMCTRL,
++ ~(PS3_AUDIO_AO_3WMCTRL_ASOBCLKD_DISABLED |
++ PS3_AUDIO_AO_3WMCTRL_ASOLRCKD_DISABLED),
++ 0);
++ update_reg(PS3_AUDIO_AO_3WMCTRL,
++ PS3_AUDIO_AO_3WMCTRL_ASOPLRCK_DEFAULT);
++}
++
++/*
++ * av setting
++ * NOTE: calling this function may generate audio interrupt.
++ */
++static int snd_ps3_change_avsetting(struct snd_ps3_card_info *card)
++{
++ int ret, retries, i;
++ pr_debug("%s: start\n", __func__);
++
++ ret = ps3av_set_audio_mode(card->avs.avs_audio_ch,
++ card->avs.avs_audio_rate,
++ card->avs.avs_audio_width,
++ card->avs.avs_audio_format,
++ card->avs.avs_audio_source);
++ /*
++ * Reset the following unwanted settings:
++ */
++
++ /* disable all 3wire buffers */
++ update_mask_reg(PS3_AUDIO_AO_3WMCTRL,
++ ~(PS3_AUDIO_AO_3WMCTRL_ASOEN(0) |
++ PS3_AUDIO_AO_3WMCTRL_ASOEN(1) |
++ PS3_AUDIO_AO_3WMCTRL_ASOEN(2) |
++ PS3_AUDIO_AO_3WMCTRL_ASOEN(3)),
++ 0);
++ wmb(); /* ensure the hardware sees the change */
++ /* wait for actually stopped */
++ retries = 1000;
++ while ((read_reg(PS3_AUDIO_AO_3WMCTRL) &
++ (PS3_AUDIO_AO_3WMCTRL_ASORUN(0) |
++ PS3_AUDIO_AO_3WMCTRL_ASORUN(1) |
++ PS3_AUDIO_AO_3WMCTRL_ASORUN(2) |
++ PS3_AUDIO_AO_3WMCTRL_ASORUN(3))) &&
++ --retries) {
++ udelay(1);
++ }
++
++ /* reset buffer pointer */
++ for (i = 0; i < 4; i++) {
++ update_reg(PS3_AUDIO_AO_3WCTRL(i),
++ PS3_AUDIO_AO_3WCTRL_ASOBRST_RESET);
++ udelay(10);
++ }
++ wmb(); /* ensure the hardware actually start resetting */
++
++ /* enable 3wire#0 buffer */
++ update_reg(PS3_AUDIO_AO_3WMCTRL, PS3_AUDIO_AO_3WMCTRL_ASOEN(0));
++
++
++ /* In 24bit mode,ALSA inserts a zero byte at first byte of per sample */
++ update_mask_reg(PS3_AUDIO_AO_3WCTRL(0),
++ ~PS3_AUDIO_AO_3WCTRL_ASODF,
++ PS3_AUDIO_AO_3WCTRL_ASODF_LSB);
++ update_mask_reg(PS3_AUDIO_AO_SPDCTRL(0),
++ ~PS3_AUDIO_AO_SPDCTRL_SPODF,
++ PS3_AUDIO_AO_SPDCTRL_SPODF_LSB);
++ /* ensure all the setting above is written back to register */
++ wmb();
++ /* avsetting driver altered AX_IE, caller must reset it if you want */
++ pr_debug("%s: end\n", __func__);
++ return ret;
++}
++
++static int snd_ps3_init_avsetting(struct snd_ps3_card_info *card)
++{
++ int ret;
++ pr_debug("%s: start\n", __func__);
++ card->avs.avs_audio_ch = PS3AV_CMD_AUDIO_NUM_OF_CH_2;
++ card->avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_48K;
++ card->avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16;
++ card->avs.avs_audio_format = PS3AV_CMD_AUDIO_FORMAT_PCM;
++ card->avs.avs_audio_source = PS3AV_CMD_AUDIO_SOURCE_SERIAL;
++
++ ret = snd_ps3_change_avsetting(card);
++
++ snd_ps3_audio_fixup(card);
++
++ /* to start to generate SPDIF signal, fill data */
++ snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
++ snd_ps3_kick_dma(card);
++ pr_debug("%s: end\n", __func__);
++ return ret;
++}
++
++/*
++ * set sampling rate according to the substream
++ */
++static int snd_ps3_set_avsetting(struct snd_pcm_substream *substream)
++{
++ struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream);
++ struct snd_ps3_avsetting_info avs;
++
++ avs = card->avs;
++
++ pr_debug("%s: called freq=%d width=%d\n", __func__,
++ substream->runtime->rate,
++ snd_pcm_format_width(substream->runtime->format));
++
++ pr_debug("%s: before freq=%d width=%d\n", __func__,
++ card->avs.avs_audio_rate, card->avs.avs_audio_width);
++
++ /* sample rate */
++ switch (substream->runtime->rate) {
++ case 44100:
++ avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_44K;
++ break;
++ case 48000:
++ avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_48K;
++ break;
++ case 88200:
++ avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_88K;
++ break;
++ case 96000:
++ avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_96K;
++ break;
++ default:
++ pr_info("%s: invalid rate %d\n", __func__,
++ substream->runtime->rate);
++ return 1;
++ }
++
++ /* width */
++ switch (snd_pcm_format_width(substream->runtime->format)) {
++ case 16:
++ avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16;
++ break;
++ case 24:
++ avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_24;
++ break;
++ default:
++ pr_info("%s: invalid width %d\n", __func__,
++ snd_pcm_format_width(substream->runtime->format));
++ return 1;
++ }
++
++ if ((card->avs.avs_audio_width != avs.avs_audio_width) ||
++ (card->avs.avs_audio_rate != avs.avs_audio_rate)) {
++ card->avs = avs;
++ snd_ps3_change_avsetting(card);
++
++ pr_debug("%s: after freq=%d width=%d\n", __func__,
++ card->avs.avs_audio_rate, card->avs.avs_audio_width);
++
++ return 0;
++ } else
++ return 1;
++}
++
++
++
++static int snd_ps3_map_mmio(void)
++{
++ the_card.mapped_mmio_vaddr =
++ ioremap(the_card.ps3_dev->m_region->bus_addr,
++ the_card.ps3_dev->m_region->len);
++
++ if (!the_card.mapped_mmio_vaddr) {
++ pr_info("%s: ioremap 0 failed p=%#lx l=%#lx \n",
++ __func__, the_card.ps3_dev->m_region->lpar_addr,
++ the_card.ps3_dev->m_region->len);
++ return -ENXIO;
++ }
++
++ return 0;
++};
++
++static void snd_ps3_unmap_mmio(void)
++{
++ iounmap(the_card.mapped_mmio_vaddr);
++ the_card.mapped_mmio_vaddr = NULL;
++}
++
++static int snd_ps3_allocate_irq(void)
++{
++ int ret;
++ u64 lpar_addr, lpar_size;
++ u64 __iomem *mapped;
++
++ /* FIXME: move this to device_init (H/W probe) */
++
++ /* get irq outlet */
++ ret = lv1_gpu_device_map(1, &lpar_addr, &lpar_size);
++ if (ret) {
++ pr_info("%s: device map 1 failed %d\n", __func__,
++ ret);
++ return -ENXIO;
++ }
++
++ mapped = ioremap(lpar_addr, lpar_size);
++ if (!mapped) {
++ pr_info("%s: ioremap 1 failed \n", __func__);
++ return -ENXIO;
++ }
++
++ the_card.audio_irq_outlet = in_be64(mapped);
++
++ iounmap(mapped);
++ ret = lv1_gpu_device_unmap(1);
++ if (ret)
++ pr_info("%s: unmap 1 failed\n", __func__);
++
++ /* irq */
++ ret = ps3_irq_plug_setup(PS3_BINDING_CPU_ANY,
++ the_card.audio_irq_outlet,
++ &the_card.irq_no);
++ if (ret) {
++ pr_info("%s:ps3_alloc_irq failed (%d)\n", __func__, ret);
++ return ret;
++ }
++
++ ret = request_irq(the_card.irq_no, snd_ps3_interrupt, IRQF_DISABLED,
++ SND_PS3_DRIVER_NAME, &the_card);
++ if (ret) {
++ pr_info("%s: request_irq failed (%d)\n", __func__, ret);
++ goto cleanup_irq;
++ }
++
++ return 0;
++
++ cleanup_irq:
++ ps3_irq_plug_destroy(the_card.irq_no);
++ return ret;
++};
++
++static void snd_ps3_free_irq(void)
++{
++ free_irq(the_card.irq_no, &the_card);
++ ps3_irq_plug_destroy(the_card.irq_no);
++}
++
++static void snd_ps3_audio_set_base_addr(uint64_t ioaddr_start)
++{
++ uint64_t val;
++ int ret;
++
++ val = (ioaddr_start & (0x0fUL << 32)) >> (32 - 20) |
++ (0x03UL << 24) |
++ (0x0fUL << 12) |
++ (PS3_AUDIO_IOID);
++
++ ret = lv1_gpu_attribute(0x100, 0x007, val, 0, 0);
++ if (ret)
++ pr_info("%s: gpu_attribute failed %d\n", __func__,
++ ret);
++}
++
++static int __init snd_ps3_driver_probe(struct ps3_system_bus_device *dev)
++{
++ int ret;
++ u64 lpar_addr, lpar_size;
++
++ BUG_ON(!firmware_has_feature(FW_FEATURE_PS3_LV1));
++ BUG_ON(dev->match_id != PS3_MATCH_ID_SOUND);
++
++ the_card.ps3_dev = dev;
++
++ ret = ps3_open_hv_device(dev);
++
++ if (ret)
++ return -ENXIO;
++
++ /* setup MMIO */
++ ret = lv1_gpu_device_map(2, &lpar_addr, &lpar_size);
++ if (ret) {
++ pr_info("%s: device map 2 failed %d\n", __func__, ret);
++ goto clean_open;
++ }
++ ps3_mmio_region_init(dev, dev->m_region, lpar_addr, lpar_size,
++ PAGE_SHIFT);
++
++ ret = snd_ps3_map_mmio();
++ if (ret)
++ goto clean_dev_map;
++
++ /* setup DMA area */
++ ps3_dma_region_init(dev, dev->d_region,
++ PAGE_SHIFT, /* use system page size */
++ 0, /* dma type; not used */
++ NULL,
++ _ALIGN_UP(SND_PS3_DMA_REGION_SIZE, PAGE_SIZE));
++ dev->d_region->ioid = PS3_AUDIO_IOID;
++
++ ret = ps3_dma_region_create(dev->d_region);
++ if (ret) {
++ pr_info("%s: region_create\n", __func__);
++ goto clean_mmio;
++ }
++
++ snd_ps3_audio_set_base_addr(dev->d_region->bus_addr);
++
++ /* CONFIG_SND_PS3_DEFAULT_START_DELAY */
++ the_card.start_delay = snd_ps3_start_delay;
++
++ /* irq */
++ if (snd_ps3_allocate_irq()) {
++ ret = -ENXIO;
++ goto clean_dma_region;
++ }
++
++ /* create card instance */
++ the_card.card = snd_card_new(index, id, THIS_MODULE, 0);
++ if (!the_card.card) {
++ ret = -ENXIO;
++ goto clean_irq;
++ }
++
++ strcpy(the_card.card->driver, "PS3");
++ strcpy(the_card.card->shortname, "PS3");
++ strcpy(the_card.card->longname, "PS3 sound");
++ /* create PCM devices instance */
++ /* NOTE:this driver works assuming pcm:substream = 1:1 */
++ ret = snd_pcm_new(the_card.card,
++ "SPDIF",
++ 0, /* instance index, will be stored pcm.device*/
++ 1, /* output substream */
++ 0, /* input substream */
++ &(the_card.pcm));
++ if (ret)
++ goto clean_card;
++
++ the_card.pcm->private_data = &the_card;
++ strcpy(the_card.pcm->name, "SPDIF");
++
++ /* set pcm ops */
++ snd_pcm_set_ops(the_card.pcm, SNDRV_PCM_STREAM_PLAYBACK,
++ &snd_ps3_pcm_spdif_ops);
++
++ the_card.pcm->info_flags = SNDRV_PCM_INFO_NONINTERLEAVED;
++ /* pre-alloc PCM DMA buffer*/
++ ret = snd_pcm_lib_preallocate_pages_for_all(the_card.pcm,
++ SNDRV_DMA_TYPE_DEV,
++ &dev->core,
++ SND_PS3_PCM_PREALLOC_SIZE,
++ SND_PS3_PCM_PREALLOC_SIZE);
++ if (ret < 0) {
++ pr_info("%s: prealloc failed\n", __func__);
++ goto clean_card;
++ }
++
++ /*
++ * allocate null buffer
++ * its size should be lager than PS3_AUDIO_FIFO_STAGE_SIZE * 2
++ * PAGE_SIZE is enogh
++ */
++ if (!(the_card.null_buffer_start_vaddr =
++ dma_alloc_coherent(&the_card.ps3_dev->core,
++ PAGE_SIZE,
++ &the_card.null_buffer_start_dma_addr,
++ GFP_KERNEL))) {
++ pr_info("%s: nullbuffer alloc failed\n", __func__);
++ goto clean_preallocate;
++ }
++ pr_debug("%s: null vaddr=%p dma=%#lx\n", __func__,
++ the_card.null_buffer_start_vaddr,
++ the_card.null_buffer_start_dma_addr);
++ /* set default sample rate/word width */
++ snd_ps3_init_avsetting(&the_card);
++
++ /* register the card */
++ ret = snd_card_register(the_card.card);
++ if (ret < 0)
++ goto clean_dma_map;
++
++ pr_info("%s started. start_delay=%dms\n",
++ the_card.card->longname, the_card.start_delay);
++ return 0;
++
++clean_dma_map:
++ dma_free_coherent(&the_card.ps3_dev->core,
++ PAGE_SIZE,
++ the_card.null_buffer_start_vaddr,
++ the_card.null_buffer_start_dma_addr);
++clean_preallocate:
++ snd_pcm_lib_preallocate_free_for_all(the_card.pcm);
++clean_card:
++ snd_card_free(the_card.card);
++clean_irq:
++ snd_ps3_free_irq();
++clean_dma_region:
++ ps3_dma_region_free(dev->d_region);
++clean_mmio:
++ snd_ps3_unmap_mmio();
++clean_dev_map:
++ lv1_gpu_device_unmap(2);
++clean_open:
++ ps3_close_hv_device(dev);
++ /*
++ * there is no destructor function to pcm.
++ * midlayer automatically releases if the card removed
++ */
++ return ret;
++}; /* snd_ps3_probe */
++
++/* called when module removal */
++static int snd_ps3_driver_remove(struct ps3_system_bus_device *dev)
++{
++ int ret;
++ pr_info("%s:start id=%d\n", __func__, dev->match_id);
++ if (dev->match_id != PS3_MATCH_ID_SOUND)
++ return -ENXIO;
++
++ /*
++ * ctl and preallocate buffer will be freed in
++ * snd_card_free
++ */
++ ret = snd_card_free(the_card.card);
++ if (ret)
++ pr_info("%s: ctl freecard=%d\n", __func__, ret);
++
++ dma_free_coherent(&dev->core,
++ PAGE_SIZE,
++ the_card.null_buffer_start_vaddr,
++ the_card.null_buffer_start_dma_addr);
++
++ ps3_dma_region_free(dev->d_region);
++
++ snd_ps3_free_irq();
++ snd_ps3_unmap_mmio();
++
++ lv1_gpu_device_unmap(2);
++ ps3_close_hv_device(dev);
++ pr_info("%s:end id=%d\n", __func__, dev->match_id);
++ return 0;
++} /* snd_ps3_remove */
++
++static struct ps3_system_bus_driver snd_ps3_bus_driver_info = {
++ .match_id = PS3_MATCH_ID_SOUND,
++ .probe = snd_ps3_driver_probe,
++ .remove = snd_ps3_driver_remove,
++ .shutdown = snd_ps3_driver_remove,
++ .core = {
++ .name = SND_PS3_DRIVER_NAME,
++ .owner = THIS_MODULE,
++ },
++};
++
++
++/*
++ * Interrupt handler
++ */
++static irqreturn_t snd_ps3_interrupt(int irq, void *dev_id)
++{
++
++ uint32_t port_intr;
++ int underflow_occured = 0;
++ struct snd_ps3_card_info *card = dev_id;
++
++ if (!card->running) {
++ update_reg(PS3_AUDIO_AX_IS, 0);
++ update_reg(PS3_AUDIO_INTR_0, 0);
++ return IRQ_HANDLED;
++ }
++
++ port_intr = read_reg(PS3_AUDIO_AX_IS);
++ /*
++ *serial buffer empty detected (every 4 times),
++ *program next dma and kick it
++ */
++ if (port_intr & PS3_AUDIO_AX_IE_ASOBEIE(0)) {
++ write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBEIE(0));
++ if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) {
++ write_reg(PS3_AUDIO_AX_IS, port_intr);
++ underflow_occured = 1;
++ }
++ if (card->silent) {
++ /* we are still in silent time */
++ snd_ps3_program_dma(card,
++ (underflow_occured) ?
++ SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL :
++ SND_PS3_DMA_FILLTYPE_SILENT_RUNNING);
++ snd_ps3_kick_dma(card);
++ card->silent --;
++ } else {
++ snd_ps3_program_dma(card,
++ (underflow_occured) ?
++ SND_PS3_DMA_FILLTYPE_FIRSTFILL :
++ SND_PS3_DMA_FILLTYPE_RUNNING);
++ snd_ps3_kick_dma(card);
++ snd_pcm_period_elapsed(card->substream);
++ }
++ } else if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) {
++ write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBUIE(0));
++ /*
++ * serial out underflow, but buffer empty not detected.
++ * in this case, fill fifo with 0 to recover. After
++ * filling dummy data, serial automatically start to
++ * consume them and then will generate normal buffer
++ * empty interrupts.
++ * If both buffer underflow and buffer empty are occured,
++ * it is better to do nomal data transfer than empty one
++ */
++ snd_ps3_program_dma(card,
++ SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
++ snd_ps3_kick_dma(card);
++ snd_ps3_program_dma(card,
++ SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
++ snd_ps3_kick_dma(card);
++ }
++ /* clear interrupt cause */
++ return IRQ_HANDLED;
++};
++
++/*
++ * module/subsystem initialize/terminate
++ */
++static int __init snd_ps3_init(void)
++{
++ int ret;
++
++ if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
++ return -ENXIO;
++
++ memset(&the_card, 0, sizeof(the_card));
++ spin_lock_init(&the_card.dma_lock);
++
++ /* register systembus DRIVER, this calls our probe() func */
++ ret = ps3_system_bus_driver_register(&snd_ps3_bus_driver_info);
++
++ return ret;
++}
++
++static void __exit snd_ps3_exit(void)
++{
++ ps3_system_bus_driver_unregister(&snd_ps3_bus_driver_info);
++}
++
++MODULE_ALIAS(PS3_MODULE_ALIAS_SOUND);
+--- /dev/null
++++ linux-2.6.22.1/sound/ppc/snd_ps3.h
+@@ -0,0 +1,135 @@
++/*
++ * Audio support for PS3
++ * Copyright (C) 2007 Sony Computer Entertainment Inc.
++ * All rights reserved.
++ * Copyright 2006, 2007 Sony Corporation
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; version 2 of the Licence.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++#if !defined(_SND_PS3_H_)
++#define _SND_PS3_H_
++
++#include <linux/irqreturn.h>
++
++#define SND_PS3_DRIVER_NAME "snd_ps3"
++
++enum snd_ps3_out_channel {
++ SND_PS3_OUT_SPDIF_0,
++ SND_PS3_OUT_SPDIF_1,
++ SND_PS3_OUT_SERIAL_0,
++ SND_PS3_OUT_DEVS
++};
++
++enum snd_ps3_dma_filltype {
++ SND_PS3_DMA_FILLTYPE_FIRSTFILL,
++ SND_PS3_DMA_FILLTYPE_RUNNING,
++ SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL,
++ SND_PS3_DMA_FILLTYPE_SILENT_RUNNING
++};
++
++enum snd_ps3_ch {
++ SND_PS3_CH_L = 0,
++ SND_PS3_CH_R = 1,
++ SND_PS3_CH_MAX = 2
++};
++
++struct snd_ps3_avsetting_info {
++ uint32_t avs_audio_ch; /* fixed */
++ uint32_t avs_audio_rate;
++ uint32_t avs_audio_width;
++ uint32_t avs_audio_format; /* fixed */
++ uint32_t avs_audio_source; /* fixed */
++};
++/*
++ * PS3 audio 'card' instance
++ * there should be only ONE hardware.
++ */
++struct snd_ps3_card_info {
++ struct ps3_system_bus_device *ps3_dev;
++ struct snd_card *card;
++
++ struct snd_pcm *pcm;
++ struct snd_pcm_substream *substream;
++
++ /* hvc info */
++ u64 audio_lpar_addr;
++ u64 audio_lpar_size;
++
++ /* registers */
++ void __iomem *mapped_mmio_vaddr;
++
++ /* irq */
++ u64 audio_irq_outlet;
++ unsigned int irq_no;
++
++ /* remember avsetting */
++ struct snd_ps3_avsetting_info avs;
++
++ /* dma buffer management */
++ spinlock_t dma_lock;
++ /* dma_lock start */
++ void * dma_start_vaddr[2]; /* 0 for L, 1 for R */
++ dma_addr_t dma_start_bus_addr[2];
++ size_t dma_buffer_size;
++ void * dma_last_transfer_vaddr[2];
++ void * dma_next_transfer_vaddr[2];
++ int silent;
++ /* dma_lock end */
++
++ int running;
++
++ /* null buffer */
++ void *null_buffer_start_vaddr;
++ dma_addr_t null_buffer_start_dma_addr;
++
++ /* start delay */
++ unsigned int start_delay;
++
++};
++
++
++/* PS3 audio DMAC block size in bytes */
++#define PS3_AUDIO_DMAC_BLOCK_SIZE (128)
++/* one stage (stereo) of audio FIFO in bytes */
++#define PS3_AUDIO_FIFO_STAGE_SIZE (256)
++/* how many stages the fifo have */
++#define PS3_AUDIO_FIFO_STAGE_COUNT (8)
++/* fifo size 128 bytes * 8 stages * stereo (2ch) */
++#define PS3_AUDIO_FIFO_SIZE \
++ (PS3_AUDIO_FIFO_STAGE_SIZE * PS3_AUDIO_FIFO_STAGE_COUNT)
++
++/* PS3 audio DMAC max block count in one dma shot = 128 (0x80) blocks*/
++#define PS3_AUDIO_DMAC_MAX_BLOCKS (PS3_AUDIO_DMASIZE_BLOCKS_MASK + 1)
++
++#define PS3_AUDIO_NORMAL_DMA_START_CH (0)
++#define PS3_AUDIO_NORMAL_DMA_COUNT (8)
++#define PS3_AUDIO_NULL_DMA_START_CH \
++ (PS3_AUDIO_NORMAL_DMA_START_CH + PS3_AUDIO_NORMAL_DMA_COUNT)
++#define PS3_AUDIO_NULL_DMA_COUNT (2)
++
++#define SND_PS3_MAX_VOL (0x0F)
++#define SND_PS3_MIN_VOL (0x00)
++#define SND_PS3_MIN_ATT SND_PS3_MIN_VOL
++#define SND_PS3_MAX_ATT SND_PS3_MAX_VOL
++
++#define SND_PS3_PCM_PREALLOC_SIZE \
++ (PS3_AUDIO_DMAC_BLOCK_SIZE * PS3_AUDIO_DMAC_MAX_BLOCKS * 4)
++
++#define SND_PS3_DMA_REGION_SIZE \
++ (SND_PS3_PCM_PREALLOC_SIZE + PAGE_SIZE)
++
++#define PS3_AUDIO_IOID (1UL)
++
++#endif /* _SND_PS3_H_ */
+--- /dev/null
++++ linux-2.6.22.1/sound/ppc/snd_ps3_reg.h
+@@ -0,0 +1,891 @@
++/*
++ * Audio support for PS3
++ * Copyright (C) 2007 Sony Computer Entertainment Inc.
++ * Copyright 2006, 2007 Sony Corporation
++ * All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License
++ * as published by the Free Software Foundation; version 2 of the License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++/*
++ * interrupt / configure registers
++ */
++
++#define PS3_AUDIO_INTR_0 (0x00000100)
++#define PS3_AUDIO_INTR_EN_0 (0x00000140)
++#define PS3_AUDIO_CONFIG (0x00000200)
++
++/*
++ * DMAC registers
++ * n:0..9
++ */
++#define PS3_AUDIO_DMAC_REGBASE(x) (0x0000210 + 0x20 * (x))
++
++#define PS3_AUDIO_KICK(n) (PS3_AUDIO_DMAC_REGBASE(n) + 0x00)
++#define PS3_AUDIO_SOURCE(n) (PS3_AUDIO_DMAC_REGBASE(n) + 0x04)
++#define PS3_AUDIO_DEST(n) (PS3_AUDIO_DMAC_REGBASE(n) + 0x08)
++#define PS3_AUDIO_DMASIZE(n) (PS3_AUDIO_DMAC_REGBASE(n) + 0x0C)
++
++/*
++ * mute control
++ */
++#define PS3_AUDIO_AX_MCTRL (0x00004000)
++#define PS3_AUDIO_AX_ISBP (0x00004004)
++#define PS3_AUDIO_AX_AOBP (0x00004008)
++#define PS3_AUDIO_AX_IC (0x00004010)
++#define PS3_AUDIO_AX_IE (0x00004014)
++#define PS3_AUDIO_AX_IS (0x00004018)
++
++/*
++ * three wire serial
++ * n:0..3
++ */
++#define PS3_AUDIO_AO_MCTRL (0x00006000)
++#define PS3_AUDIO_AO_3WMCTRL (0x00006004)
++
++#define PS3_AUDIO_AO_3WCTRL(n) (0x00006200 + 0x200 * (n))
++
++/*
++ * S/PDIF
++ * n:0..1
++ * x:0..11
++ * y:0..5
++ */
++#define PS3_AUDIO_AO_SPD_REGBASE(n) (0x00007200 + 0x200 * (n))
++
++#define PS3_AUDIO_AO_SPDCTRL(n) \
++ (PS3_AUDIO_AO_SPD_REGBASE(n) + 0x00)
++#define PS3_AUDIO_AO_SPDUB(n, x) \
++ (PS3_AUDIO_AO_SPD_REGBASE(n) + 0x04 + 0x04 * (x))
++#define PS3_AUDIO_AO_SPDCS(n, y) \
++ (PS3_AUDIO_AO_SPD_REGBASE(n) + 0x34 + 0x04 * (y))
++
++
++/*
++ PS3_AUDIO_INTR_0 register tells an interrupt handler which audio
++ DMA channel triggered the interrupt. The interrupt status for a channel
++ can be cleared by writing a '1' to the corresponding bit. A new interrupt
++ cannot be generated until the previous interrupt has been cleared.
++
++ Note that the status reported by PS3_AUDIO_INTR_0 is independent of the
++ value of PS3_AUDIO_INTR_EN_0.
++
++ 31 24 23 16 15 8 7 0
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++ |0 0 0 0 0 0 0 0 0 0 0 0 0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C| INTR_0
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++*/
++#define PS3_AUDIO_INTR_0_CHAN(n) (1 << ((n) * 2))
++#define PS3_AUDIO_INTR_0_CHAN9 PS3_AUDIO_INTR_0_CHAN(9)
++#define PS3_AUDIO_INTR_0_CHAN8 PS3_AUDIO_INTR_0_CHAN(8)
++#define PS3_AUDIO_INTR_0_CHAN7 PS3_AUDIO_INTR_0_CHAN(7)
++#define PS3_AUDIO_INTR_0_CHAN6 PS3_AUDIO_INTR_0_CHAN(6)
++#define PS3_AUDIO_INTR_0_CHAN5 PS3_AUDIO_INTR_0_CHAN(5)
++#define PS3_AUDIO_INTR_0_CHAN4 PS3_AUDIO_INTR_0_CHAN(4)
++#define PS3_AUDIO_INTR_0_CHAN3 PS3_AUDIO_INTR_0_CHAN(3)
++#define PS3_AUDIO_INTR_0_CHAN2 PS3_AUDIO_INTR_0_CHAN(2)
++#define PS3_AUDIO_INTR_0_CHAN1 PS3_AUDIO_INTR_0_CHAN(1)
++#define PS3_AUDIO_INTR_0_CHAN0 PS3_AUDIO_INTR_0_CHAN(0)
++
++/*
++ The PS3_AUDIO_INTR_EN_0 register specifies which DMA channels can generate
++ an interrupt to the PU. Each bit of PS3_AUDIO_INTR_EN_0 is ANDed with the
++ corresponding bit in PS3_AUDIO_INTR_0. The resulting bits are OR'd together
++ to generate the Audio interrupt.
++
++ 31 24 23 16 15 8 7 0
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++ |0 0 0 0 0 0 0 0 0 0 0 0 0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C|0|C| INTR_EN_0
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++
++ Bit assignments are same as PS3_AUDIO_INTR_0
++*/
++
++/*
++ PS3_AUDIO_CONFIG
++ 31 24 23 16 15 8 7 0
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++ |0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 C|0 0 0 0 0 0 0 0| CONFIG
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++
++*/
++
++/* The CLEAR field cancels all pending transfers, and stops any running DMA
++ transfers. Any interrupts associated with the canceled transfers
++ will occur as if the transfer had finished.
++ Since this bit is designed to recover from DMA related issues
++ which are caused by unpredictable situations, it is prefered to wait
++ for normal DMA transfer end without using this bit.
++*/
++#define PS3_AUDIO_CONFIG_CLEAR (1 << 8) /* RWIVF */
++
++/*
++ PS3_AUDIO_AX_MCTRL: Audio Port Mute Control Register
++
++ 31 24 23 16 15 8 7 0
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++ |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|A|A|A|0 0 0 0 0 0 0|S|S|A|A|A|A| AX_MCTRL
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++*/
++
++/* 3 Wire Audio Serial Output Channel Mutes (0..3) */
++#define PS3_AUDIO_AX_MCTRL_ASOMT(n) (1 << (3 - (n))) /* RWIVF */
++#define PS3_AUDIO_AX_MCTRL_ASO3MT (1 << 0) /* RWIVF */
++#define PS3_AUDIO_AX_MCTRL_ASO2MT (1 << 1) /* RWIVF */
++#define PS3_AUDIO_AX_MCTRL_ASO1MT (1 << 2) /* RWIVF */
++#define PS3_AUDIO_AX_MCTRL_ASO0MT (1 << 3) /* RWIVF */
++
++/* S/PDIF mutes (0,1)*/
++#define PS3_AUDIO_AX_MCTRL_SPOMT(n) (1 << (5 - (n))) /* RWIVF */
++#define PS3_AUDIO_AX_MCTRL_SPO1MT (1 << 4) /* RWIVF */
++#define PS3_AUDIO_AX_MCTRL_SPO0MT (1 << 5) /* RWIVF */
++
++/* All 3 Wire Serial Outputs Mute */
++#define PS3_AUDIO_AX_MCTRL_AASOMT (1 << 13) /* RWIVF */
++
++/* All S/PDIF Mute */
++#define PS3_AUDIO_AX_MCTRL_ASPOMT (1 << 14) /* RWIVF */
++
++/* All Audio Outputs Mute */
++#define PS3_AUDIO_AX_MCTRL_AAOMT (1 << 15) /* RWIVF */
++
++/*
++ S/PDIF Outputs Buffer Read/Write Pointer Register
++
++ 31 24 23 16 15 8 7 0
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++ |0 0 0 0 0 0 0 0|0|SPO0B|0|SPO1B|0 0 0 0 0 0 0 0|0|SPO0B|0|SPO1B| AX_ISBP
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++
++*/
++/*
++ S/PDIF Output Channel Read Buffer Numbers
++ Buffer number is value of field.
++ Indicates current read access buffer ID from Audio Data
++ Transfer controller of S/PDIF Output
++*/
++
++#define PS3_AUDIO_AX_ISBP_SPOBRN_MASK(n) (0x7 << 4 * (1 - (n))) /* R-IUF */
++#define PS3_AUDIO_AX_ISBP_SPO1BRN_MASK (0x7 << 0) /* R-IUF */
++#define PS3_AUDIO_AX_ISBP_SPO0BRN_MASK (0x7 << 4) /* R-IUF */
++
++/*
++S/PDIF Output Channel Buffer Write Numbers
++Indicates current write access buffer ID from bus master.
++*/
++#define PS3_AUDIO_AX_ISBP_SPOBWN_MASK(n) (0x7 << 4 * (5 - (n))) /* R-IUF */
++#define PS3_AUDIO_AX_ISBP_SPO1BWN_MASK (0x7 << 16) /* R-IUF */
++#define PS3_AUDIO_AX_ISBP_SPO0BWN_MASK (0x7 << 20) /* R-IUF */
++
++/*
++ 3 Wire Audio Serial Outputs Buffer Read/Write
++ Pointer Register
++ Buffer number is value of field
++
++ 31 24 23 16 15 8 7 0
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++ |0|ASO0B|0|ASO1B|0|ASO2B|0|ASO3B|0|ASO0B|0|ASO1B|0|ASO2B|0|ASO3B| AX_AOBP
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++*/
++
++/*
++3 Wire Audio Serial Output Channel Buffer Read Numbers
++Indicates current read access buffer Id from Audio Data Transfer
++Controller of 3 Wire Audio Serial Output Channels
++*/
++#define PS3_AUDIO_AX_AOBP_ASOBRN_MASK(n) (0x7 << 4 * (3 - (n))) /* R-IUF */
++
++#define PS3_AUDIO_AX_AOBP_ASO3BRN_MASK (0x7 << 0) /* R-IUF */
++#define PS3_AUDIO_AX_AOBP_ASO2BRN_MASK (0x7 << 4) /* R-IUF */
++#define PS3_AUDIO_AX_AOBP_ASO1BRN_MASK (0x7 << 8) /* R-IUF */
++#define PS3_AUDIO_AX_AOBP_ASO0BRN_MASK (0x7 << 12) /* R-IUF */
++
++/*
++3 Wire Audio Serial Output Channel Buffer Write Numbers
++Indicates current write access buffer ID from bus master.
++*/
++#define PS3_AUDIO_AX_AOBP_ASOBWN_MASK(n) (0x7 << 4 * (7 - (n))) /* R-IUF */
++
++#define PS3_AUDIO_AX_AOBP_ASO3BWN_MASK (0x7 << 16) /* R-IUF */
++#define PS3_AUDIO_AX_AOBP_ASO2BWN_MASK (0x7 << 20) /* R-IUF */
++#define PS3_AUDIO_AX_AOBP_ASO1BWN_MASK (0x7 << 24) /* R-IUF */
++#define PS3_AUDIO_AX_AOBP_ASO0BWN_MASK (0x7 << 28) /* R-IUF */
++
++
++
++/*
++Audio Port Interrupt Condition Register
++For the fields in this register, the following values apply:
++0 = Interrupt is generated every interrupt event.
++1 = Interrupt is generated every 2 interrupt events.
++2 = Interrupt is generated every 4 interrupt events.
++3 = Reserved
++
++
++ 31 24 23 16 15 8 7 0
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++ |0 0 0 0 0 0 0 0|0 0|SPO|0 0|SPO|0 0|AAS|0 0 0 0 0 0 0 0 0 0 0 0| AX_IC
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++*/
++/*
++All 3-Wire Audio Serial Outputs Interrupt Mode
++Configures the Interrupt and Signal Notification
++condition of all 3-wire Audio Serial Outputs.
++*/
++#define PS3_AUDIO_AX_IC_AASOIMD_MASK (0x3 << 12) /* RWIVF */
++#define PS3_AUDIO_AX_IC_AASOIMD_EVERY1 (0x0 << 12) /* RWI-V */
++#define PS3_AUDIO_AX_IC_AASOIMD_EVERY2 (0x1 << 12) /* RW--V */
++#define PS3_AUDIO_AX_IC_AASOIMD_EVERY4 (0x2 << 12) /* RW--V */
++
++/*
++S/PDIF Output Channel Interrupt Modes
++Configures the Interrupt and signal Notification
++conditions of S/PDIF output channels.
++*/
++#define PS3_AUDIO_AX_IC_SPO1IMD_MASK (0x3 << 16) /* RWIVF */
++#define PS3_AUDIO_AX_IC_SPO1IMD_EVERY1 (0x0 << 16) /* RWI-V */
++#define PS3_AUDIO_AX_IC_SPO1IMD_EVERY2 (0x1 << 16) /* RW--V */
++#define PS3_AUDIO_AX_IC_SPO1IMD_EVERY4 (0x2 << 16) /* RW--V */
++
++#define PS3_AUDIO_AX_IC_SPO0IMD_MASK (0x3 << 20) /* RWIVF */
++#define PS3_AUDIO_AX_IC_SPO0IMD_EVERY1 (0x0 << 20) /* RWI-V */
++#define PS3_AUDIO_AX_IC_SPO0IMD_EVERY2 (0x1 << 20) /* RW--V */
++#define PS3_AUDIO_AX_IC_SPO0IMD_EVERY4 (0x2 << 20) /* RW--V */
++
++/*
++Audio Port interrupt Enable Register
++Configures whether to enable or disable each Interrupt Generation.
++
++
++ 31 24 23 16 15 8 7 0
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++ |0 0 0 0 0 0 0 0|S|S|0 0|A|A|A|A|0 0 0 0|S|S|0 0|S|S|0 0|A|A|A|A| AX_IE
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++
++*/
++
++/*
++3 Wire Audio Serial Output Channel Buffer Underflow
++Interrupt Enables
++Select enable/disable of Buffer Underflow Interrupts for
++3-Wire Audio Serial Output Channels
++DISABLED=Interrupt generation disabled.
++*/
++#define PS3_AUDIO_AX_IE_ASOBUIE(n) (1 << (3 - (n))) /* RWIVF */
++#define PS3_AUDIO_AX_IE_ASO3BUIE (1 << 0) /* RWIVF */
++#define PS3_AUDIO_AX_IE_ASO2BUIE (1 << 1) /* RWIVF */
++#define PS3_AUDIO_AX_IE_ASO1BUIE (1 << 2) /* RWIVF */
++#define PS3_AUDIO_AX_IE_ASO0BUIE (1 << 3) /* RWIVF */
++
++/* S/PDIF Output Channel Buffer Underflow Interrupt Enables */
++
++#define PS3_AUDIO_AX_IE_SPOBUIE(n) (1 << (7 - (n))) /* RWIVF */
++#define PS3_AUDIO_AX_IE_SPO1BUIE (1 << 6) /* RWIVF */
++#define PS3_AUDIO_AX_IE_SPO0BUIE (1 << 7) /* RWIVF */
++
++/* S/PDIF Output Channel One Block Transfer Completion Interrupt Enables */
++
++#define PS3_AUDIO_AX_IE_SPOBTCIE(n) (1 << (11 - (n))) /* RWIVF */
++#define PS3_AUDIO_AX_IE_SPO1BTCIE (1 << 10) /* RWIVF */
++#define PS3_AUDIO_AX_IE_SPO0BTCIE (1 << 11) /* RWIVF */
++
++/* 3-Wire Audio Serial Output Channel Buffer Empty Interrupt Enables */
++
++#define PS3_AUDIO_AX_IE_ASOBEIE(n) (1 << (19 - (n))) /* RWIVF */
++#define PS3_AUDIO_AX_IE_ASO3BEIE (1 << 16) /* RWIVF */
++#define PS3_AUDIO_AX_IE_ASO2BEIE (1 << 17) /* RWIVF */
++#define PS3_AUDIO_AX_IE_ASO1BEIE (1 << 18) /* RWIVF */
++#define PS3_AUDIO_AX_IE_ASO0BEIE (1 << 19) /* RWIVF */
++
++/* S/PDIF Output Channel Buffer Empty Interrupt Enables */
++
++#define PS3_AUDIO_AX_IE_SPOBEIE(n) (1 << (23 - (n))) /* RWIVF */
++#define PS3_AUDIO_AX_IE_SPO1BEIE (1 << 22) /* RWIVF */
++#define PS3_AUDIO_AX_IE_SPO0BEIE (1 << 23) /* RWIVF */
++
++/*
++Audio Port Interrupt Status Register
++Indicates Interrupt status, which interrupt has occured, and can clear
++each interrupt in this register.
++Writing 1b to a field containing 1b clears field and de-asserts interrupt.
++Writing 0b to a field has no effect.
++Field vaules are the following:
++0 - Interrupt hasn't occured.
++1 - Interrupt has occured.
++
++
++ 31 24 23 16 15 8 7 0
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++ |0 0 0 0 0 0 0 0|S|S|0 0|A|A|A|A|0 0 0 0|S|S|0 0|S|S|0 0|A|A|A|A| AX_IS
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++
++ Bit assignment are same as AX_IE
++*/
++
++/*
++Audio Output Master Control Register
++Configures Master Clock and other master Audio Output Settings
++
++
++ 31 24 23 16 15 8 7 0
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++ |0|SCKSE|0|SCKSE| MR0 | MR1 |MCL|MCL|0 0 0 0|0 0 0 0 0 0 0 0| AO_MCTRL
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++*/
++
++/*
++MCLK Output Control
++Controls mclko[1] output.
++0 - Disable output (fixed at High)
++1 - Output clock produced by clock selected
++with scksel1 by mr1
++2 - Reserved
++3 - Reserved
++*/
++
++#define PS3_AUDIO_AO_MCTRL_MCLKC1_MASK (0x3 << 12) /* RWIVF */
++#define PS3_AUDIO_AO_MCTRL_MCLKC1_DISABLED (0x0 << 12) /* RWI-V */
++#define PS3_AUDIO_AO_MCTRL_MCLKC1_ENABLED (0x1 << 12) /* RW--V */
++#define PS3_AUDIO_AO_MCTRL_MCLKC1_RESVD2 (0x2 << 12) /* RW--V */
++#define PS3_AUDIO_AO_MCTRL_MCLKC1_RESVD3 (0x3 << 12) /* RW--V */
++
++/*
++MCLK Output Control
++Controls mclko[0] output.
++0 - Disable output (fixed at High)
++1 - Output clock produced by clock selected
++with SCKSEL0 by MR0
++2 - Reserved
++3 - Reserved
++*/
++#define PS3_AUDIO_AO_MCTRL_MCLKC0_MASK (0x3 << 14) /* RWIVF */
++#define PS3_AUDIO_AO_MCTRL_MCLKC0_DISABLED (0x0 << 14) /* RWI-V */
++#define PS3_AUDIO_AO_MCTRL_MCLKC0_ENABLED (0x1 << 14) /* RW--V */
++#define PS3_AUDIO_AO_MCTRL_MCLKC0_RESVD2 (0x2 << 14) /* RW--V */
++#define PS3_AUDIO_AO_MCTRL_MCLKC0_RESVD3 (0x3 << 14) /* RW--V */
++/*
++Master Clock Rate 1
++Sets the divide ration of Master Clock1 (clock output from
++mclko[1] for the input clock selected by scksel1.
++*/
++#define PS3_AUDIO_AO_MCTRL_MR1_MASK (0xf << 16)
++#define PS3_AUDIO_AO_MCTRL_MR1_DEFAULT (0x0 << 16) /* RWI-V */
++/*
++Master Clock Rate 0
++Sets the divide ratio of Master Clock0 (clock output from
++mclko[0] for the input clock selected by scksel0).
++*/
++#define PS3_AUDIO_AO_MCTRL_MR0_MASK (0xf << 20) /* RWIVF */
++#define PS3_AUDIO_AO_MCTRL_MR0_DEFAULT (0x0 << 20) /* RWI-V */
++/*
++System Clock Select 0/1
++Selects the system clock to be used as Master Clock 0/1
++Input the system clock that is appropriate for the sampling
++rate.
++*/
++#define PS3_AUDIO_AO_MCTRL_SCKSEL1_MASK (0x7 << 24) /* RWIVF */
++#define PS3_AUDIO_AO_MCTRL_SCKSEL1_DEFAULT (0x2 << 24) /* RWI-V */
++
++#define PS3_AUDIO_AO_MCTRL_SCKSEL0_MASK (0x7 << 28) /* RWIVF */
++#define PS3_AUDIO_AO_MCTRL_SCKSEL0_DEFAULT (0x2 << 28) /* RWI-V */
++
++
++/*
++3-Wire Audio Output Master Control Register
++Configures clock, 3-Wire Audio Serial Output Enable, and
++other 3-Wire Audio Serial Output Master Settings
++
++
++ 31 24 23 16 15 8 7 0
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++ |A|A|A|A|0 0 0|A| ASOSR |0 0 0 0|A|A|A|A|A|A|0|1|0 0 0 0 0 0 0 0| AO_3WMCTRL
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++*/
++
++
++/*
++LRCKO Polarity
++0 - Reserved
++1 - default
++*/
++#define PS3_AUDIO_AO_3WMCTRL_ASOPLRCK (1 << 8) /* RWIVF */
++#define PS3_AUDIO_AO_3WMCTRL_ASOPLRCK_DEFAULT (1 << 8) /* RW--V */
++
++/* LRCK Output Disable */
++
++#define PS3_AUDIO_AO_3WMCTRL_ASOLRCKD (1 << 10) /* RWIVF */
++#define PS3_AUDIO_AO_3WMCTRL_ASOLRCKD_ENABLED (0 << 10) /* RW--V */
++#define PS3_AUDIO_AO_3WMCTRL_ASOLRCKD_DISABLED (1 << 10) /* RWI-V */
++
++/* Bit Clock Output Disable */
++
++#define PS3_AUDIO_AO_3WMCTRL_ASOBCLKD (1 << 11) /* RWIVF */
++#define PS3_AUDIO_AO_3WMCTRL_ASOBCLKD_ENABLED (0 << 11) /* RW--V */
++#define PS3_AUDIO_AO_3WMCTRL_ASOBCLKD_DISABLED (1 << 11) /* RWI-V */
++
++/*
++3-Wire Audio Serial Output Channel 0-3 Operational
++Status. Each bit becomes 1 after each 3-Wire Audio
++Serial Output Channel N is in action by setting 1 to
++asoen.
++Each bit becomes 0 after each 3-Wire Audio Serial Output
++Channel N is out of action by setting 0 to asoen.
++*/
++#define PS3_AUDIO_AO_3WMCTRL_ASORUN(n) (1 << (15 - (n))) /* R-IVF */
++#define PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(n) (0 << (15 - (n))) /* R-I-V */
++#define PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(n) (1 << (15 - (n))) /* R---V */
++#define PS3_AUDIO_AO_3WMCTRL_ASORUN0 \
++ PS3_AUDIO_AO_3WMCTRL_ASORUN(0)
++#define PS3_AUDIO_AO_3WMCTRL_ASORUN0_STOPPED \
++ PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(0)
++#define PS3_AUDIO_AO_3WMCTRL_ASORUN0_RUNNING \
++ PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(0)
++#define PS3_AUDIO_AO_3WMCTRL_ASORUN1 \
++ PS3_AUDIO_AO_3WMCTRL_ASORUN(1)
++#define PS3_AUDIO_AO_3WMCTRL_ASORUN1_STOPPED \
++ PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(1)
++#define PS3_AUDIO_AO_3WMCTRL_ASORUN1_RUNNING \
++ PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(1)
++#define PS3_AUDIO_AO_3WMCTRL_ASORUN2 \
++ PS3_AUDIO_AO_3WMCTRL_ASORUN(2)
++#define PS3_AUDIO_AO_3WMCTRL_ASORUN2_STOPPED \
++ PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(2)
++#define PS3_AUDIO_AO_3WMCTRL_ASORUN2_RUNNING \
++ PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(2)
++#define PS3_AUDIO_AO_3WMCTRL_ASORUN3 \
++ PS3_AUDIO_AO_3WMCTRL_ASORUN(3)
++#define PS3_AUDIO_AO_3WMCTRL_ASORUN3_STOPPED \
++ PS3_AUDIO_AO_3WMCTRL_ASORUN_STOPPED(3)
++#define PS3_AUDIO_AO_3WMCTRL_ASORUN3_RUNNING \
++ PS3_AUDIO_AO_3WMCTRL_ASORUN_RUNNING(3)
++
++/*
++Sampling Rate
++Specifies the divide ratio of the bit clock (clock output
++from bclko) used by the 3-wire Audio Output Clock, whcih
++is applied to the master clock selected by mcksel.
++Data output is synchronized with this clock.
++*/
++#define PS3_AUDIO_AO_3WMCTRL_ASOSR_MASK (0xf << 20) /* RWIVF */
++#define PS3_AUDIO_AO_3WMCTRL_ASOSR_DIV2 (0x1 << 20) /* RWI-V */
++#define PS3_AUDIO_AO_3WMCTRL_ASOSR_DIV4 (0x2 << 20) /* RW--V */
++#define PS3_AUDIO_AO_3WMCTRL_ASOSR_DIV8 (0x4 << 20) /* RW--V */
++#define PS3_AUDIO_AO_3WMCTRL_ASOSR_DIV12 (0x6 << 20) /* RW--V */
++
++/*
++Master Clock Select
++0 - Master Clock 0
++1 - Master Clock 1
++*/
++#define PS3_AUDIO_AO_3WMCTRL_ASOMCKSEL (1 << 24) /* RWIVF */
++#define PS3_AUDIO_AO_3WMCTRL_ASOMCKSEL_CLK0 (0 << 24) /* RWI-V */
++#define PS3_AUDIO_AO_3WMCTRL_ASOMCKSEL_CLK1 (1 << 24) /* RW--V */
++
++/*
++Enables and disables 4ch 3-Wire Audio Serial Output
++operation. Each Bit from 0 to 3 corresponds to an
++output channel, which means that each output channel
++can be enabled or disabled individually. When
++multiple channels are enabled at the same time, output
++operations are performed in synchronization.
++Bit 0 - Output Channel 0 (SDOUT[0])
++Bit 1 - Output Channel 1 (SDOUT[1])
++Bit 2 - Output Channel 2 (SDOUT[2])
++Bit 3 - Output Channel 3 (SDOUT[3])
++*/
++#define PS3_AUDIO_AO_3WMCTRL_ASOEN(n) (1 << (31 - (n))) /* RWIVF */
++#define PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(n) (0 << (31 - (n))) /* RWI-V */
++#define PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(n) (1 << (31 - (n))) /* RW--V */
++
++#define PS3_AUDIO_AO_3WMCTRL_ASOEN0 \
++ PS3_AUDIO_AO_3WMCTRL_ASOEN(0) /* RWIVF */
++#define PS3_AUDIO_AO_3WMCTRL_ASOEN0_DISABLED \
++ PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(0) /* RWI-V */
++#define PS3_AUDIO_AO_3WMCTRL_ASOEN0_ENABLED \
++ PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(0) /* RW--V */
++#define PS3_AUDIO_A1_3WMCTRL_ASOEN0 \
++ PS3_AUDIO_AO_3WMCTRL_ASOEN(1) /* RWIVF */
++#define PS3_AUDIO_A1_3WMCTRL_ASOEN0_DISABLED \
++ PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(1) /* RWI-V */
++#define PS3_AUDIO_A1_3WMCTRL_ASOEN0_ENABLED \
++ PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(1) /* RW--V */
++#define PS3_AUDIO_A2_3WMCTRL_ASOEN0 \
++ PS3_AUDIO_AO_3WMCTRL_ASOEN(2) /* RWIVF */
++#define PS3_AUDIO_A2_3WMCTRL_ASOEN0_DISABLED \
++ PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(2) /* RWI-V */
++#define PS3_AUDIO_A2_3WMCTRL_ASOEN0_ENABLED \
++ PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(2) /* RW--V */
++#define PS3_AUDIO_A3_3WMCTRL_ASOEN0 \
++ PS3_AUDIO_AO_3WMCTRL_ASOEN(3) /* RWIVF */
++#define PS3_AUDIO_A3_3WMCTRL_ASOEN0_DISABLED \
++ PS3_AUDIO_AO_3WMCTRL_ASOEN_DISABLED(3) /* RWI-V */
++#define PS3_AUDIO_A3_3WMCTRL_ASOEN0_ENABLED \
++ PS3_AUDIO_AO_3WMCTRL_ASOEN_ENABLED(3) /* RW--V */
++
++/*
++3-Wire Audio Serial output Channel 0-3 Control Register
++Configures settings for 3-Wire Serial Audio Output Channel 0-3
++
++
++ 31 24 23 16 15 8 7 0
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++ |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|A|0 0 0 0|A|0|ASO|0 0 0|0|0|0|0|0| AO_3WCTRL
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++
++*/
++/*
++Data Bit Mode
++Specifies the number of data bits
++0 - 16 bits
++1 - reserved
++2 - 20 bits
++3 - 24 bits
++*/
++#define PS3_AUDIO_AO_3WCTRL_ASODB_MASK (0x3 << 8) /* RWIVF */
++#define PS3_AUDIO_AO_3WCTRL_ASODB_16BIT (0x0 << 8) /* RWI-V */
++#define PS3_AUDIO_AO_3WCTRL_ASODB_RESVD (0x1 << 8) /* RWI-V */
++#define PS3_AUDIO_AO_3WCTRL_ASODB_20BIT (0x2 << 8) /* RW--V */
++#define PS3_AUDIO_AO_3WCTRL_ASODB_24BIT (0x3 << 8) /* RW--V */
++/*
++Data Format Mode
++Specifies the data format where (LSB side or MSB) the data(in 20 bit
++or 24 bit resolution mode) is put in a 32 bit field.
++0 - Data put on LSB side
++1 - Data put on MSB side
++*/
++#define PS3_AUDIO_AO_3WCTRL_ASODF (1 << 11) /* RWIVF */
++#define PS3_AUDIO_AO_3WCTRL_ASODF_LSB (0 << 11) /* RWI-V */
++#define PS3_AUDIO_AO_3WCTRL_ASODF_MSB (1 << 11) /* RW--V */
++/*
++Buffer Reset
++Performs buffer reset. Writing 1 to this bit initializes the
++corresponding 3-Wire Audio Output buffers(both L and R).
++*/
++#define PS3_AUDIO_AO_3WCTRL_ASOBRST (1 << 16) /* CWIVF */
++#define PS3_AUDIO_AO_3WCTRL_ASOBRST_IDLE (0 << 16) /* -WI-V */
++#define PS3_AUDIO_AO_3WCTRL_ASOBRST_RESET (1 << 16) /* -W--T */
++
++/*
++S/PDIF Audio Output Channel 0/1 Control Register
++Configures settings for S/PDIF Audio Output Channel 0/1.
++
++ 31 24 23 16 15 8 7 0
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++ |S|0 0 0|S|0 0|S| SPOSR |0 0|SPO|0 0 0 0|S|0|SPO|0 0 0 0 0 0 0|S| AO_SPDCTRL
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++*/
++/*
++Buffer reset. Writing 1 to this bit initializes the
++corresponding S/PDIF output buffer pointer.
++*/
++#define PS3_AUDIO_AO_SPDCTRL_SPOBRST (1 << 0) /* CWIVF */
++#define PS3_AUDIO_AO_SPDCTRL_SPOBRST_IDLE (0 << 0) /* -WI-V */
++#define PS3_AUDIO_AO_SPDCTRL_SPOBRST_RESET (1 << 0) /* -W--T */
++
++/*
++Data Bit Mode
++Specifies number of data bits
++0 - 16 bits
++1 - Reserved
++2 - 20 bits
++3 - 24 bits
++*/
++#define PS3_AUDIO_AO_SPDCTRL_SPODB_MASK (0x3 << 8) /* RWIVF */
++#define PS3_AUDIO_AO_SPDCTRL_SPODB_16BIT (0x0 << 8) /* RWI-V */
++#define PS3_AUDIO_AO_SPDCTRL_SPODB_RESVD (0x1 << 8) /* RW--V */
++#define PS3_AUDIO_AO_SPDCTRL_SPODB_20BIT (0x2 << 8) /* RW--V */
++#define PS3_AUDIO_AO_SPDCTRL_SPODB_24BIT (0x3 << 8) /* RW--V */
++/*
++Data format Mode
++Specifies the data format, where (LSB side or MSB)
++the data(in 20 or 24 bit resolution) is put in the
++32 bit field.
++0 - LSB Side
++1 - MSB Side
++*/
++#define PS3_AUDIO_AO_SPDCTRL_SPODF (1 << 11) /* RWIVF */
++#define PS3_AUDIO_AO_SPDCTRL_SPODF_LSB (0 << 11) /* RWI-V */
++#define PS3_AUDIO_AO_SPDCTRL_SPODF_MSB (1 << 11) /* RW--V */
++/*
++Source Select
++Specifies the source of the S/PDIF output. When 0, output
++operation is controlled by 3wen[0] of AO_3WMCTRL register.
++The SR must have the same setting as the a0_3wmctrl reg.
++0 - 3-Wire Audio OUT Ch0 Buffer
++1 - S/PDIF buffer
++*/
++#define PS3_AUDIO_AO_SPDCTRL_SPOSS_MASK (0x3 << 16) /* RWIVF */
++#define PS3_AUDIO_AO_SPDCTRL_SPOSS_3WEN (0x0 << 16) /* RWI-V */
++#define PS3_AUDIO_AO_SPDCTRL_SPOSS_SPDIF (0x1 << 16) /* RW--V */
++/*
++Sampling Rate
++Specifies the divide ratio of the bit clock (clock output
++from bclko) used by the S/PDIF Output Clock, which
++is applied to the master clock selected by mcksel.
++*/
++#define PS3_AUDIO_AO_SPDCTRL_SPOSR (0xf << 20) /* RWIVF */
++#define PS3_AUDIO_AO_SPDCTRL_SPOSR_DIV2 (0x1 << 20) /* RWI-V */
++#define PS3_AUDIO_AO_SPDCTRL_SPOSR_DIV4 (0x2 << 20) /* RW--V */
++#define PS3_AUDIO_AO_SPDCTRL_SPOSR_DIV8 (0x4 << 20) /* RW--V */
++#define PS3_AUDIO_AO_SPDCTRL_SPOSR_DIV12 (0x6 << 20) /* RW--V */
++/*
++Master Clock Select
++0 - Master Clock 0
++1 - Master Clock 1
++*/
++#define PS3_AUDIO_AO_SPDCTRL_SPOMCKSEL (1 << 24) /* RWIVF */
++#define PS3_AUDIO_AO_SPDCTRL_SPOMCKSEL_CLK0 (0 << 24) /* RWI-V */
++#define PS3_AUDIO_AO_SPDCTRL_SPOMCKSEL_CLK1 (1 << 24) /* RW--V */
++
++/*
++S/PDIF Output Channel Operational Status
++This bit becomes 1 after S/PDIF Output Channel is in
++action by setting 1 to spoen. This bit becomes 0
++after S/PDIF Output Channel is out of action by setting
++0 to spoen.
++*/
++#define PS3_AUDIO_AO_SPDCTRL_SPORUN (1 << 27) /* R-IVF */
++#define PS3_AUDIO_AO_SPDCTRL_SPORUN_STOPPED (0 << 27) /* R-I-V */
++#define PS3_AUDIO_AO_SPDCTRL_SPORUN_RUNNING (1 << 27) /* R---V */
++
++/*
++S/PDIF Audio Output Channel Output Enable
++Enables and disables output operation. This bit is used
++only when sposs = 1
++*/
++#define PS3_AUDIO_AO_SPDCTRL_SPOEN (1 << 31) /* RWIVF */
++#define PS3_AUDIO_AO_SPDCTRL_SPOEN_DISABLED (0 << 31) /* RWI-V */
++#define PS3_AUDIO_AO_SPDCTRL_SPOEN_ENABLED (1 << 31) /* RW--V */
++
++/*
++S/PDIF Audio Output Channel Channel Status
++Setting Registers.
++Configures channel status bit settings for each block
++(192 bits).
++Output is performed from the MSB(AO_SPDCS0 register bit 31).
++The same value is added for subframes within the same frame.
++ 31 24 23 16 15 8 7 0
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++ | SPOCS | AO_SPDCS
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++
++S/PDIF Audio Output Channel User Bit Setting
++Configures user bit settings for each block (384 bits).
++Output is performed from the MSB(ao_spdub0 register bit 31).
++
++
++ 31 24 23 16 15 8 7 0
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++ | SPOUB | AO_SPDUB
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++*/
++/*****************************************************************************
++ *
++ * DMAC register
++ *
++ *****************************************************************************/
++/*
++The PS3_AUDIO_KICK register is used to initiate a DMA transfer and monitor
++its status
++
++ 31 24 23 16 15 8 7 0
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++ |0 0 0 0 0|STATU|0 0 0| EVENT |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|R| KICK
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++*/
++/*
++The REQUEST field is written to ACTIVE to initiate a DMA request when EVENT
++occurs.
++It will return to the DONE state when the request is completed.
++The registers for a DMA channel should only be written if REQUEST is IDLE.
++*/
++
++#define PS3_AUDIO_KICK_REQUEST (1 << 0) /* RWIVF */
++#define PS3_AUDIO_KICK_REQUEST_IDLE (0 << 0) /* RWI-V */
++#define PS3_AUDIO_KICK_REQUEST_ACTIVE (1 << 0) /* -W--T */
++
++/*
++ *The EVENT field is used to set the event in which
++ *the DMA request becomes active.
++ */
++#define PS3_AUDIO_KICK_EVENT_MASK (0x1f << 16) /* RWIVF */
++#define PS3_AUDIO_KICK_EVENT_ALWAYS (0x00 << 16) /* RWI-V */
++#define PS3_AUDIO_KICK_EVENT_SERIALOUT0_EMPTY (0x01 << 16) /* RW--V */
++#define PS3_AUDIO_KICK_EVENT_SERIALOUT0_UNDERFLOW (0x02 << 16) /* RW--V */
++#define PS3_AUDIO_KICK_EVENT_SERIALOUT1_EMPTY (0x03 << 16) /* RW--V */
++#define PS3_AUDIO_KICK_EVENT_SERIALOUT1_UNDERFLOW (0x04 << 16) /* RW--V */
++#define PS3_AUDIO_KICK_EVENT_SERIALOUT2_EMPTY (0x05 << 16) /* RW--V */
++#define PS3_AUDIO_KICK_EVENT_SERIALOUT2_UNDERFLOW (0x06 << 16) /* RW--V */
++#define PS3_AUDIO_KICK_EVENT_SERIALOUT3_EMPTY (0x07 << 16) /* RW--V */
++#define PS3_AUDIO_KICK_EVENT_SERIALOUT3_UNDERFLOW (0x08 << 16) /* RW--V */
++#define PS3_AUDIO_KICK_EVENT_SPDIF0_BLOCKTRANSFERCOMPLETE \
++ (0x09 << 16) /* RW--V */
++#define PS3_AUDIO_KICK_EVENT_SPDIF0_UNDERFLOW (0x0A << 16) /* RW--V */
++#define PS3_AUDIO_KICK_EVENT_SPDIF0_EMPTY (0x0B << 16) /* RW--V */
++#define PS3_AUDIO_KICK_EVENT_SPDIF1_BLOCKTRANSFERCOMPLETE \
++ (0x0C << 16) /* RW--V */
++#define PS3_AUDIO_KICK_EVENT_SPDIF1_UNDERFLOW (0x0D << 16) /* RW--V */
++#define PS3_AUDIO_KICK_EVENT_SPDIF1_EMPTY (0x0E << 16) /* RW--V */
++
++#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA(n) \
++ ((0x13 + (n)) << 16) /* RW--V */
++#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA0 (0x13 << 16) /* RW--V */
++#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA1 (0x14 << 16) /* RW--V */
++#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA2 (0x15 << 16) /* RW--V */
++#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA3 (0x16 << 16) /* RW--V */
++#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA4 (0x17 << 16) /* RW--V */
++#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA5 (0x18 << 16) /* RW--V */
++#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA6 (0x19 << 16) /* RW--V */
++#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA7 (0x1A << 16) /* RW--V */
++#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA8 (0x1B << 16) /* RW--V */
++#define PS3_AUDIO_KICK_EVENT_AUDIO_DMA9 (0x1C << 16) /* RW--V */
++
++/*
++The STATUS field can be used to monitor the progress of a DMA request.
++DONE indicates the previous request has completed.
++EVENT indicates that the DMA engine is waiting for the EVENT to occur.
++PENDING indicates that the DMA engine has not started processing this
++request, but the EVENT has occured.
++DMA indicates that the data transfer is in progress.
++NOTIFY indicates that the notifier signalling end of transfer is being written.
++CLEAR indicated that the previous transfer was cleared.
++ERROR indicates the previous transfer requested an unsupported
++source/destination combination.
++*/
++
++#define PS3_AUDIO_KICK_STATUS_MASK (0x7 << 24) /* R-IVF */
++#define PS3_AUDIO_KICK_STATUS_DONE (0x0 << 24) /* R-I-V */
++#define PS3_AUDIO_KICK_STATUS_EVENT (0x1 << 24) /* R---V */
++#define PS3_AUDIO_KICK_STATUS_PENDING (0x2 << 24) /* R---V */
++#define PS3_AUDIO_KICK_STATUS_DMA (0x3 << 24) /* R---V */
++#define PS3_AUDIO_KICK_STATUS_NOTIFY (0x4 << 24) /* R---V */
++#define PS3_AUDIO_KICK_STATUS_CLEAR (0x5 << 24) /* R---V */
++#define PS3_AUDIO_KICK_STATUS_ERROR (0x6 << 24) /* R---V */
++
++/*
++The PS3_AUDIO_SOURCE register specifies the source address for transfers.
++
++
++ 31 24 23 16 15 8 7 0
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++ | START |0 0 0 0 0|TAR| SOURCE
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++*/
++
++/*
++The Audio DMA engine uses 128-byte transfers, thus the address must be aligned
++to a 128 byte boundary. The low seven bits are assumed to be 0.
++*/
++
++#define PS3_AUDIO_SOURCE_START_MASK (0x01FFFFFF << 7) /* RWIUF */
++
++/*
++The TARGET field specifies the memory space containing the source address.
++*/
++
++#define PS3_AUDIO_SOURCE_TARGET_MASK (3 << 0) /* RWIVF */
++#define PS3_AUDIO_SOURCE_TARGET_SYSTEM_MEMORY (2 << 0) /* RW--V */
++
++/*
++The PS3_AUDIO_DEST register specifies the destination address for transfers.
++
++
++ 31 24 23 16 15 8 7 0
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++ | START |0 0 0 0 0|TAR| DEST
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++*/
++
++/*
++The Audio DMA engine uses 128-byte transfers, thus the address must be aligned
++to a 128 byte boundary. The low seven bits are assumed to be 0.
++*/
++
++#define PS3_AUDIO_DEST_START_MASK (0x01FFFFFF << 7) /* RWIUF */
++
++/*
++The TARGET field specifies the memory space containing the destination address
++AUDIOFIFO = Audio WriteData FIFO,
++*/
++
++#define PS3_AUDIO_DEST_TARGET_MASK (3 << 0) /* RWIVF */
++#define PS3_AUDIO_DEST_TARGET_AUDIOFIFO (1 << 0) /* RW--V */
++
++/*
++PS3_AUDIO_DMASIZE specifies the number of 128-byte blocks + 1 to transfer.
++So a value of 0 means 128-bytes will get transfered.
++
++
++ 31 24 23 16 15 8 7 0
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++ |0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0| BLOCKS | DMASIZE
++ +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
++*/
++
++
++#define PS3_AUDIO_DMASIZE_BLOCKS_MASK (0x7f << 0) /* RWIUF */
++
++/*
++ * source/destination address for internal fifos
++ */
++#define PS3_AUDIO_AO_3W_LDATA(n) (0x1000 + (0x100 * (n)))
++#define PS3_AUDIO_AO_3W_RDATA(n) (0x1080 + (0x100 * (n)))
++
++#define PS3_AUDIO_AO_SPD_DATA(n) (0x2000 + (0x400 * (n)))
++
++
++/*
++ * field attiribute
++ *
++ * Read
++ * ' ' = Other Information
++ * '-' = Field is part of a write-only register
++ * 'C' = Value read is always the same, constant value line follows (C)
++ * 'R' = Value is read
++ *
++ * Write
++ * ' ' = Other Information
++ * '-' = Must not be written (D), value ignored when written (R,A,F)
++ * 'W' = Can be written
++ *
++ * Internal State
++ * ' ' = Other Information
++ * '-' = No internal state
++ * 'X' = Internal state, initial value is unknown
++ * 'I' = Internal state, initial value is known and follows (I)
++ *
++ * Declaration/Size
++ * ' ' = Other Information
++ * '-' = Does Not Apply
++ * 'V' = Type is void
++ * 'U' = Type is unsigned integer
++ * 'S' = Type is signed integer
++ * 'F' = Type is IEEE floating point
++ * '1' = Byte size (008)
++ * '2' = Short size (016)
++ * '3' = Three byte size (024)
++ * '4' = Word size (032)
++ * '8' = Double size (064)
++ *
++ * Define Indicator
++ * ' ' = Other Information
++ * 'D' = Device
++ * 'M' = Memory
++ * 'R' = Register
++ * 'A' = Array of Registers
++ * 'F' = Field
++ * 'V' = Value
++ * 'T' = Task
++ */
++
+--- /dev/null
++++ linux-2.6.22.1/sound/sh/Kconfig
+@@ -0,0 +1,14 @@
++# ALSA SH drivers
++
++menu "SUPERH devices"
++ depends on SND!=n && SUPERH
++
++config SND_AICA
++ tristate "Dreamcast Yamaha AICA sound"
++ depends on SH_DREAMCAST && SND
++ select SND_PCM
++ help
++ ALSA Sound driver for the SEGA Dreamcast console.
++
++endmenu
++
+--- /dev/null
++++ linux-2.6.22.1/sound/sh/Makefile
+@@ -0,0 +1,8 @@
++#
++# Makefile for ALSA
++#
++
++snd-aica-objs := aica.o
++
++# Toplevel Module Dependency
++obj-$(CONFIG_SND_AICA) += snd-aica.o
+--- /dev/null
++++ linux-2.6.22.1/sound/sh/aica.c
+@@ -0,0 +1,665 @@
++/*
++* This code is licenced under
++* the General Public Licence
++* version 2
++*
++* Copyright Adrian McMenamin 2005, 2006, 2007
++* <adrian at mcmen.demon.co.uk>
++* Requires firmware (BSD licenced) available from:
++* http://linuxdc.cvs.sourceforge.net/linuxdc/linux-sh-dc/sound/oss/aica/firmware/
++* or the maintainer
++*
++* This program is free software; you can redistribute it and/or modify
++* it under the terms of version 2 of the GNU General Public License as published by
++* the Free Software Foundation.
++*
++* This program is distributed in the hope that it will be useful,
++* but WITHOUT ANY WARRANTY; without even the implied warranty of
++* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++* GNU General Public License for more details.
++*
++* You should have received a copy of the GNU General Public License
++* along with this program; if not, write to the Free Software
++* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++*
++*/
++
++#include <linux/init.h>
++#include <linux/jiffies.h>
++#include <linux/slab.h>
++#include <linux/time.h>
++#include <linux/wait.h>
++#include <linux/moduleparam.h>
++#include <linux/platform_device.h>
++#include <linux/firmware.h>
++#include <linux/timer.h>
++#include <linux/delay.h>
++#include <linux/workqueue.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/control.h>
++#include <sound/pcm.h>
++#include <sound/initval.h>
++#include <sound/info.h>
++#include <asm/io.h>
++#include <asm/dma.h>
++#include <asm/dreamcast/sysasic.h>
++#include "aica.h"
++
++MODULE_AUTHOR("Adrian McMenamin <adrian at mcmen.demon.co.uk>");
++MODULE_DESCRIPTION("Dreamcast AICA sound (pcm) driver");
++MODULE_LICENSE("GPL");
++MODULE_SUPPORTED_DEVICE("{{Yamaha/SEGA, AICA}}");
++
++/* module parameters */
++#define CARD_NAME "AICA"
++static int index = -1;
++static char *id;
++static int enable = 1;
++module_param(index, int, 0444);
++MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
++module_param(id, charp, 0444);
++MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
++module_param(enable, bool, 0644);
++MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
++
++/* Use workqueue */
++static struct workqueue_struct *aica_queue;
++
++/* Simple platform device */
++static struct platform_device *pd;
++static struct resource aica_memory_space[2] = {
++ {
++ .name = "AICA ARM CONTROL",
++ .start = ARM_RESET_REGISTER,
++ .flags = IORESOURCE_MEM,
++ .end = ARM_RESET_REGISTER + 3,
++ },
++ {
++ .name = "AICA Sound RAM",
++ .start = SPU_MEMORY_BASE,
++ .flags = IORESOURCE_MEM,
++ .end = SPU_MEMORY_BASE + 0x200000 - 1,
++ },
++};
++
++/* SPU specific functions */
++/* spu_write_wait - wait for G2-SH FIFO to clear */
++static void spu_write_wait(void)
++{
++ int time_count;
++ time_count = 0;
++ while (1) {
++ if (!(readl(G2_FIFO) & 0x11))
++ break;
++ /* To ensure hardware failure doesn't wedge kernel */
++ time_count++;
++ if (time_count > 0x10000) {
++ snd_printk
++ ("WARNING: G2 FIFO appears to be blocked.\n");
++ break;
++ }
++ }
++}
++
++/* spu_memset - write to memory in SPU address space */
++static void spu_memset(u32 toi, u32 what, int length)
++{
++ int i;
++ snd_assert(length % 4 == 0, return);
++ for (i = 0; i < length; i++) {
++ if (!(i % 8))
++ spu_write_wait();
++ writel(what, toi + SPU_MEMORY_BASE);
++ toi++;
++ }
++}
++
++/* spu_memload - write to SPU address space */
++static void spu_memload(u32 toi, void *from, int length)
++{
++ u32 *froml = from;
++ u32 __iomem *to = (u32 __iomem *) (SPU_MEMORY_BASE + toi);
++ int i;
++ u32 val;
++ length = DIV_ROUND_UP(length, 4);
++ spu_write_wait();
++ for (i = 0; i < length; i++) {
++ if (!(i % 8))
++ spu_write_wait();
++ val = *froml;
++ writel(val, to);
++ froml++;
++ to++;
++ }
++}
++
++/* spu_disable - set spu registers to stop sound output */
++static void spu_disable(void)
++{
++ int i;
++ u32 regval;
++ spu_write_wait();
++ regval = readl(ARM_RESET_REGISTER);
++ regval |= 1;
++ spu_write_wait();
++ writel(regval, ARM_RESET_REGISTER);
++ for (i = 0; i < 64; i++) {
++ spu_write_wait();
++ regval = readl(SPU_REGISTER_BASE + (i * 0x80));
++ regval = (regval & ~0x4000) | 0x8000;
++ spu_write_wait();
++ writel(regval, SPU_REGISTER_BASE + (i * 0x80));
++ }
++}
++
++/* spu_enable - set spu registers to enable sound output */
++static void spu_enable(void)
++{
++ u32 regval = readl(ARM_RESET_REGISTER);
++ regval &= ~1;
++ spu_write_wait();
++ writel(regval, ARM_RESET_REGISTER);
++}
++
++/*
++ * Halt the sound processor, clear the memory,
++ * load some default ARM7 code, and then restart ARM7
++*/
++static void spu_reset(void)
++{
++ spu_disable();
++ spu_memset(0, 0, 0x200000 / 4);
++ /* Put ARM7 in endless loop */
++ ctrl_outl(0xea000002, SPU_MEMORY_BASE);
++ spu_enable();
++}
++
++/* aica_chn_start - write to spu to start playback */
++static void aica_chn_start(void)
++{
++ spu_write_wait();
++ writel(AICA_CMD_KICK | AICA_CMD_START, (u32 *) AICA_CONTROL_POINT);
++}
++
++/* aica_chn_halt - write to spu to halt playback */
++static void aica_chn_halt(void)
++{
++ spu_write_wait();
++ writel(AICA_CMD_KICK | AICA_CMD_STOP, (u32 *) AICA_CONTROL_POINT);
++}
++
++/* ALSA code below */
++static struct snd_pcm_hardware snd_pcm_aica_playback_hw = {
++ .info = (SNDRV_PCM_INFO_NONINTERLEAVED),
++ .formats =
++ (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |
++ SNDRV_PCM_FMTBIT_IMA_ADPCM),
++ .rates = SNDRV_PCM_RATE_8000_48000,
++ .rate_min = 8000,
++ .rate_max = 48000,
++ .channels_min = 1,
++ .channels_max = 2,
++ .buffer_bytes_max = AICA_BUFFER_SIZE,
++ .period_bytes_min = AICA_PERIOD_SIZE,
++ .period_bytes_max = AICA_PERIOD_SIZE,
++ .periods_min = AICA_PERIOD_NUMBER,
++ .periods_max = AICA_PERIOD_NUMBER,
++};
++
++static int aica_dma_transfer(int channels, int buffer_size,
++ struct snd_pcm_substream *substream)
++{
++ int q, err, period_offset;
++ struct snd_card_aica *dreamcastcard;
++ struct snd_pcm_runtime *runtime;
++ err = 0;
++ dreamcastcard = substream->pcm->private_data;
++ period_offset = dreamcastcard->clicks;
++ period_offset %= (AICA_PERIOD_NUMBER / channels);
++ runtime = substream->runtime;
++ for (q = 0; q < channels; q++) {
++ err = dma_xfer(AICA_DMA_CHANNEL,
++ (unsigned long) (runtime->dma_area +
++ (AICA_BUFFER_SIZE * q) /
++ channels +
++ AICA_PERIOD_SIZE *
++ period_offset),
++ AICA_CHANNEL0_OFFSET + q * CHANNEL_OFFSET +
++ AICA_PERIOD_SIZE * period_offset,
++ buffer_size / channels, AICA_DMA_MODE);
++ if (unlikely(err < 0))
++ break;
++ dma_wait_for_completion(AICA_DMA_CHANNEL);
++ }
++ return err;
++}
++
++static void startup_aica(struct snd_card_aica *dreamcastcard)
++{
++ spu_memload(AICA_CHANNEL0_CONTROL_OFFSET,
++ dreamcastcard->channel, sizeof(struct aica_channel));
++ aica_chn_start();
++}
++
++static void run_spu_dma(struct work_struct *work)
++{
++ int buffer_size;
++ struct snd_pcm_runtime *runtime;
++ struct snd_card_aica *dreamcastcard;
++ dreamcastcard =
++ container_of(work, struct snd_card_aica, spu_dma_work);
++ runtime = dreamcastcard->substream->runtime;
++ if (unlikely(dreamcastcard->dma_check == 0)) {
++ buffer_size =
++ frames_to_bytes(runtime, runtime->buffer_size);
++ if (runtime->channels > 1)
++ dreamcastcard->channel->flags |= 0x01;
++ aica_dma_transfer(runtime->channels, buffer_size,
++ dreamcastcard->substream);
++ startup_aica(dreamcastcard);
++ dreamcastcard->clicks =
++ buffer_size / (AICA_PERIOD_SIZE * runtime->channels);
++ return;
++ } else {
++ aica_dma_transfer(runtime->channels,
++ AICA_PERIOD_SIZE * runtime->channels,
++ dreamcastcard->substream);
++ snd_pcm_period_elapsed(dreamcastcard->substream);
++ dreamcastcard->clicks++;
++ if (unlikely(dreamcastcard->clicks >= AICA_PERIOD_NUMBER))
++ dreamcastcard->clicks %= AICA_PERIOD_NUMBER;
++ mod_timer(&dreamcastcard->timer, jiffies + 1);
++ }
++}
++
++static void aica_period_elapsed(unsigned long timer_var)
++{
++ /*timer function - so cannot sleep */
++ int play_period;
++ struct snd_pcm_runtime *runtime;
++ struct snd_pcm_substream *substream;
++ struct snd_card_aica *dreamcastcard;
++ substream = (struct snd_pcm_substream *) timer_var;
++ runtime = substream->runtime;
++ dreamcastcard = substream->pcm->private_data;
++ /* Have we played out an additional period? */
++ play_period =
++ frames_to_bytes(runtime,
++ readl
++ (AICA_CONTROL_CHANNEL_SAMPLE_NUMBER)) /
++ AICA_PERIOD_SIZE;
++ if (play_period == dreamcastcard->current_period) {
++ /* reschedule the timer */
++ mod_timer(&(dreamcastcard->timer), jiffies + 1);
++ return;
++ }
++ if (runtime->channels > 1)
++ dreamcastcard->current_period = play_period;
++ if (unlikely(dreamcastcard->dma_check == 0))
++ dreamcastcard->dma_check = 1;
++ queue_work(aica_queue, &(dreamcastcard->spu_dma_work));
++}
++
++static void spu_begin_dma(struct snd_pcm_substream *substream)
++{
++ struct snd_card_aica *dreamcastcard;
++ struct snd_pcm_runtime *runtime;
++ runtime = substream->runtime;
++ dreamcastcard = substream->pcm->private_data;
++ /*get the queue to do the work */
++ queue_work(aica_queue, &(dreamcastcard->spu_dma_work));
++ /* Timer may already be running */
++ if (unlikely(dreamcastcard->timer.data)) {
++ mod_timer(&dreamcastcard->timer, jiffies + 4);
++ return;
++ }
++ init_timer(&(dreamcastcard->timer));
++ dreamcastcard->timer.data = (unsigned long) substream;
++ dreamcastcard->timer.function = aica_period_elapsed;
++ dreamcastcard->timer.expires = jiffies + 4;
++ add_timer(&(dreamcastcard->timer));
++}
++
++static int snd_aicapcm_pcm_open(struct snd_pcm_substream
++ *substream)
++{
++ struct snd_pcm_runtime *runtime;
++ struct aica_channel *channel;
++ struct snd_card_aica *dreamcastcard;
++ if (!enable)
++ return -ENOENT;
++ dreamcastcard = substream->pcm->private_data;
++ channel = kmalloc(sizeof(struct aica_channel), GFP_KERNEL);
++ if (!channel)
++ return -ENOMEM;
++ /* set defaults for channel */
++ channel->sfmt = SM_8BIT;
++ channel->cmd = AICA_CMD_START;
++ channel->vol = dreamcastcard->master_volume;
++ channel->pan = 0x80;
++ channel->pos = 0;
++ channel->flags = 0; /* default to mono */
++ dreamcastcard->channel = channel;
++ runtime = substream->runtime;
++ runtime->hw = snd_pcm_aica_playback_hw;
++ spu_enable();
++ dreamcastcard->clicks = 0;
++ dreamcastcard->current_period = 0;
++ dreamcastcard->dma_check = 0;
++ return 0;
++}
++
++static int snd_aicapcm_pcm_close(struct snd_pcm_substream
++ *substream)
++{
++ struct snd_card_aica *dreamcastcard = substream->pcm->private_data;
++ flush_workqueue(aica_queue);
++ if (dreamcastcard->timer.data)
++ del_timer(&dreamcastcard->timer);
++ kfree(dreamcastcard->channel);
++ spu_disable();
++ return 0;
++}
++
++static int snd_aicapcm_pcm_hw_free(struct snd_pcm_substream
++ *substream)
++{
++ /* Free the DMA buffer */
++ return snd_pcm_lib_free_pages(substream);
++}
++
++static int snd_aicapcm_pcm_hw_params(struct snd_pcm_substream
++ *substream, struct snd_pcm_hw_params
++ *hw_params)
++{
++ /* Allocate a DMA buffer using ALSA built-ins */
++ return
++ snd_pcm_lib_malloc_pages(substream,
++ params_buffer_bytes(hw_params));
++}
++
++static int snd_aicapcm_pcm_prepare(struct snd_pcm_substream
++ *substream)
++{
++ struct snd_card_aica *dreamcastcard = substream->pcm->private_data;
++ if ((substream->runtime)->format == SNDRV_PCM_FORMAT_S16_LE)
++ dreamcastcard->channel->sfmt = SM_16BIT;
++ dreamcastcard->channel->freq = substream->runtime->rate;
++ dreamcastcard->substream = substream;
++ return 0;
++}
++
++static int snd_aicapcm_pcm_trigger(struct snd_pcm_substream
++ *substream, int cmd)
++{
++ switch (cmd) {
++ case SNDRV_PCM_TRIGGER_START:
++ spu_begin_dma(substream);
++ break;
++ case SNDRV_PCM_TRIGGER_STOP:
++ aica_chn_halt();
++ break;
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static unsigned long snd_aicapcm_pcm_pointer(struct snd_pcm_substream
++ *substream)
++{
++ return readl(AICA_CONTROL_CHANNEL_SAMPLE_NUMBER);
++}
++
++static struct snd_pcm_ops snd_aicapcm_playback_ops = {
++ .open = snd_aicapcm_pcm_open,
++ .close = snd_aicapcm_pcm_close,
++ .ioctl = snd_pcm_lib_ioctl,
++ .hw_params = snd_aicapcm_pcm_hw_params,
++ .hw_free = snd_aicapcm_pcm_hw_free,
++ .prepare = snd_aicapcm_pcm_prepare,
++ .trigger = snd_aicapcm_pcm_trigger,
++ .pointer = snd_aicapcm_pcm_pointer,
++};
++
++/* TO DO: set up to handle more than one pcm instance */
++static int __init snd_aicapcmchip(struct snd_card_aica
++ *dreamcastcard, int pcm_index)
++{
++ struct snd_pcm *pcm;
++ int err;
++ /* AICA has no capture ability */
++ err =
++ snd_pcm_new(dreamcastcard->card, "AICA PCM", pcm_index, 1, 0,
++ &pcm);
++ if (unlikely(err < 0))
++ return err;
++ pcm->private_data = dreamcastcard;
++ strcpy(pcm->name, "AICA PCM");
++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
++ &snd_aicapcm_playback_ops);
++ /* Allocate the DMA buffers */
++ err =
++ snd_pcm_lib_preallocate_pages_for_all(pcm,
++ SNDRV_DMA_TYPE_CONTINUOUS,
++ snd_dma_continuous_data
++ (GFP_KERNEL),
++ AICA_BUFFER_SIZE,
++ AICA_BUFFER_SIZE);
++ return err;
++}
++
++/* Mixer controls */
++static int aica_pcmswitch_info(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_info *uinfo)
++{
++ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
++ uinfo->count = 1;
++ uinfo->value.integer.min = 0;
++ uinfo->value.integer.max = 1;
++ return 0;
++}
++
++static int aica_pcmswitch_get(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ ucontrol->value.integer.value[0] = 1; /* TO DO: Fix me */
++ return 0;
++}
++
++static int aica_pcmswitch_put(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ if (ucontrol->value.integer.value[0] == 1)
++ return 0; /* TO DO: Fix me */
++ else
++ aica_chn_halt();
++ return 0;
++}
++
++static int aica_pcmvolume_info(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_info *uinfo)
++{
++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
++ uinfo->count = 1;
++ uinfo->value.integer.min = 0;
++ uinfo->value.integer.max = 0xFF;
++ return 0;
++}
++
++static int aica_pcmvolume_get(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ struct snd_card_aica *dreamcastcard;
++ dreamcastcard = kcontrol->private_data;
++ if (unlikely(!dreamcastcard->channel))
++ return -ETXTBSY; /* we've not yet been set up */
++ ucontrol->value.integer.value[0] = dreamcastcard->channel->vol;
++ return 0;
++}
++
++static int aica_pcmvolume_put(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ struct snd_card_aica *dreamcastcard;
++ dreamcastcard = kcontrol->private_data;
++ if (unlikely(!dreamcastcard->channel))
++ return -ETXTBSY;
++ if (unlikely(dreamcastcard->channel->vol ==
++ ucontrol->value.integer.value[0]))
++ return 0;
++ dreamcastcard->channel->vol = ucontrol->value.integer.value[0];
++ dreamcastcard->master_volume = ucontrol->value.integer.value[0];
++ spu_memload(AICA_CHANNEL0_CONTROL_OFFSET,
++ dreamcastcard->channel, sizeof(struct aica_channel));
++ return 1;
++}
++
++static struct snd_kcontrol_new snd_aica_pcmswitch_control __devinitdata = {
++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++ .name = "PCM Playback Switch",
++ .index = 0,
++ .info = aica_pcmswitch_info,
++ .get = aica_pcmswitch_get,
++ .put = aica_pcmswitch_put
++};
++
++static struct snd_kcontrol_new snd_aica_pcmvolume_control __devinitdata = {
++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
++ .name = "PCM Playback Volume",
++ .index = 0,
++ .info = aica_pcmvolume_info,
++ .get = aica_pcmvolume_get,
++ .put = aica_pcmvolume_put
++};
++
++static int load_aica_firmware(void)
++{
++ int err;
++ const struct firmware *fw_entry;
++ spu_reset();
++ err = request_firmware(&fw_entry, "aica_firmware.bin", &pd->dev);
++ if (unlikely(err))
++ return err;
++ /* write firware into memory */
++ spu_disable();
++ spu_memload(0, fw_entry->data, fw_entry->size);
++ spu_enable();
++ release_firmware(fw_entry);
++ return err;
++}
++
++static int __devinit add_aicamixer_controls(struct snd_card_aica
++ *dreamcastcard)
++{
++ int err;
++ err = snd_ctl_add
++ (dreamcastcard->card,
++ snd_ctl_new1(&snd_aica_pcmvolume_control, dreamcastcard));
++ if (unlikely(err < 0))
++ return err;
++ err = snd_ctl_add
++ (dreamcastcard->card,
++ snd_ctl_new1(&snd_aica_pcmswitch_control, dreamcastcard));
++ if (unlikely(err < 0))
++ return err;
++ return 0;
++}
++
++static int snd_aica_remove(struct platform_device *devptr)
++{
++ struct snd_card_aica *dreamcastcard;
++ dreamcastcard = platform_get_drvdata(devptr);
++ if (unlikely(!dreamcastcard))
++ return -ENODEV;
++ snd_card_free(dreamcastcard->card);
++ kfree(dreamcastcard);
++ platform_set_drvdata(devptr, NULL);
++ return 0;
++}
++
++static int __init snd_aica_probe(struct platform_device *devptr)
++{
++ int err;
++ struct snd_card_aica *dreamcastcard;
++ dreamcastcard = kmalloc(sizeof(struct snd_card_aica), GFP_KERNEL);
++ if (unlikely(!dreamcastcard))
++ return -ENOMEM;
++ dreamcastcard->card =
++ snd_card_new(index, SND_AICA_DRIVER, THIS_MODULE, 0);
++ if (unlikely(!dreamcastcard->card)) {
++ kfree(dreamcastcard);
++ return -ENODEV;
++ }
++ strcpy(dreamcastcard->card->driver, "snd_aica");
++ strcpy(dreamcastcard->card->shortname, SND_AICA_DRIVER);
++ strcpy(dreamcastcard->card->longname,
++ "Yamaha AICA Super Intelligent Sound Processor for SEGA Dreamcast");
++ /* Prepare to use the queue */
++ INIT_WORK(&(dreamcastcard->spu_dma_work), run_spu_dma);
++ /* Load the PCM 'chip' */
++ err = snd_aicapcmchip(dreamcastcard, 0);
++ if (unlikely(err < 0))
++ goto freedreamcast;
++ snd_card_set_dev(dreamcastcard->card, &devptr->dev);
++ dreamcastcard->timer.data = 0;
++ dreamcastcard->channel = NULL;
++ /* Add basic controls */
++ err = add_aicamixer_controls(dreamcastcard);
++ if (unlikely(err < 0))
++ goto freedreamcast;
++ /* Register the card with ALSA subsystem */
++ err = snd_card_register(dreamcastcard->card);
++ if (unlikely(err < 0))
++ goto freedreamcast;
++ platform_set_drvdata(devptr, dreamcastcard);
++ aica_queue = create_workqueue(CARD_NAME);
++ if (unlikely(!aica_queue))
++ goto freedreamcast;
++ snd_printk
++ ("ALSA Driver for Yamaha AICA Super Intelligent Sound Processor\n");
++ return 0;
++ freedreamcast:
++ snd_card_free(dreamcastcard->card);
++ kfree(dreamcastcard);
++ return err;
++}
++
++static struct platform_driver snd_aica_driver = {
++ .probe = snd_aica_probe,
++ .remove = snd_aica_remove,
++ .driver = {
++ .name = SND_AICA_DRIVER},
++};
++
++static int __init aica_init(void)
++{
++ int err;
++ err = platform_driver_register(&snd_aica_driver);
++ if (unlikely(err < 0))
++ return err;
++ pd = platform_device_register_simple(SND_AICA_DRIVER, -1,
++ aica_memory_space, 2);
++ if (unlikely(IS_ERR(pd))) {
++ platform_driver_unregister(&snd_aica_driver);
++ return PTR_ERR(pd);
++ }
++ /* Load the firmware */
++ return load_aica_firmware();
++}
++
++static void __exit aica_exit(void)
++{
++ /* Destroy the aica kernel thread *
++ * being extra cautious to check if it exists*/
++ if (likely(aica_queue))
++ destroy_workqueue(aica_queue);
++ platform_device_unregister(pd);
++ platform_driver_unregister(&snd_aica_driver);
++ /* Kill any sound still playing and reset ARM7 to safe state */
++ spu_reset();
++}
++
++module_init(aica_init);
++module_exit(aica_exit);
+--- /dev/null
++++ linux-2.6.22.1/sound/sh/aica.h
+@@ -0,0 +1,81 @@
++/* aica.h
++ * Header file for ALSA driver for
++ * Sega Dreamcast Yamaha AICA sound
++ * Copyright Adrian McMenamin
++ * <adrian at mcmen.demon.co.uk>
++ * 2006
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of version 2 of the GNU General Public License as published by
++ * the Free Software Foundation.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++/* SPU memory and register constants etc */
++#define G2_FIFO 0xa05f688c
++#define SPU_MEMORY_BASE 0xA0800000
++#define ARM_RESET_REGISTER 0xA0702C00
++#define SPU_REGISTER_BASE 0xA0700000
++
++/* AICA channels stuff */
++#define AICA_CONTROL_POINT 0xA0810000
++#define AICA_CONTROL_CHANNEL_SAMPLE_NUMBER 0xA0810008
++#define AICA_CHANNEL0_CONTROL_OFFSET 0x10004
++
++/* Command values */
++#define AICA_CMD_KICK 0x80000000
++#define AICA_CMD_NONE 0
++#define AICA_CMD_START 1
++#define AICA_CMD_STOP 2
++#define AICA_CMD_VOL 3
++
++/* Sound modes */
++#define SM_8BIT 1
++#define SM_16BIT 0
++#define SM_ADPCM 2
++
++/* Buffer and period size */
++#define AICA_BUFFER_SIZE 0x8000
++#define AICA_PERIOD_SIZE 0x800
++#define AICA_PERIOD_NUMBER 16
++
++#define AICA_CHANNEL0_OFFSET 0x11000
++#define AICA_CHANNEL1_OFFSET 0x21000
++#define CHANNEL_OFFSET 0x10000
++
++#define AICA_DMA_CHANNEL 0
++#define AICA_DMA_MODE 5
++
++#define SND_AICA_DRIVER "AICA"
++
++struct aica_channel {
++ uint32_t cmd; /* Command ID */
++ uint32_t pos; /* Sample position */
++ uint32_t length; /* Sample length */
++ uint32_t freq; /* Frequency */
++ uint32_t vol; /* Volume 0-255 */
++ uint32_t pan; /* Pan 0-255 */
++ uint32_t sfmt; /* Sound format */
++ uint32_t flags; /* Bit flags */
++};
++
++struct snd_card_aica {
++ struct work_struct spu_dma_work;
++ struct snd_card *card;
++ struct aica_channel *channel;
++ struct snd_pcm_substream *substream;
++ int clicks;
++ int current_period;
++ struct timer_list timer;
++ int master_volume;
++ int dma_check;
++};
+--- linux-2.6.22.1.orig/sound/soc/Kconfig
++++ linux-2.6.22.1/sound/soc/Kconfig
+@@ -27,6 +27,7 @@
+ source "sound/soc/at91/Kconfig"
+ source "sound/soc/pxa/Kconfig"
+ source "sound/soc/s3c24xx/Kconfig"
++source "sound/soc/sh/Kconfig"
+
+ # Supported codecs
+ source "sound/soc/codecs/Kconfig"
+--- linux-2.6.22.1.orig/sound/soc/Makefile
++++ linux-2.6.22.1/sound/soc/Makefile
+@@ -1,4 +1,4 @@
+ snd-soc-core-objs := soc-core.o soc-dapm.o
+
+ obj-$(CONFIG_SND_SOC) += snd-soc-core.o
+-obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/ s3c24xx/
++obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/ s3c24xx/ sh/
+--- linux-2.6.22.1.orig/sound/soc/s3c24xx/Kconfig
++++ linux-2.6.22.1/sound/soc/s3c24xx/Kconfig
+@@ -1,6 +1,7 @@
+ config SND_S3C24XX_SOC
+ tristate "SoC Audio for the Samsung S3C24XX chips"
+ depends on ARCH_S3C2410 && SND_SOC
++ select SND_PCM
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the S3C24XX AC97, I2S or SSP interface. You will also need
+@@ -8,3 +9,29 @@
+
+ config SND_S3C24XX_SOC_I2S
+ tristate
++
++config SND_S3C2443_SOC_AC97
++ tristate
++ select AC97_BUS
++ select SND_AC97_CODEC
++ select SND_SOC_AC97_BUS
++
++config SND_S3C24XX_SOC_NEO1973_WM8753
++ tristate "SoC I2S Audio support for NEO1973 - WM8753"
++ depends on SND_S3C24XX_SOC && MACH_GTA01
++ select SND_S3C24XX_SOC_I2S
++ select SND_SOC_WM8753
++ help
++ Say Y if you want to add support for SoC audio on smdk2440
++ with the WM8753.
++
++config SND_S3C24XX_SOC_SMDK2443_WM9710
++ tristate "SoC AC97 Audio support for SMDK2443 - WM9710"
++ depends on SND_S3C24XX_SOC && MACH_SMDK2443
++ select SND_S3C2443_SOC_AC97
++ select SND_SOC_AC97_CODEC
++ help
++ Say Y if you want to add support for SoC audio on smdk2443
++ with the WM9710.
++
++
+--- linux-2.6.22.1.orig/sound/soc/s3c24xx/Makefile
++++ linux-2.6.22.1/sound/soc/s3c24xx/Makefile
+@@ -1,6 +1,15 @@
+ # S3c24XX Platform Support
+ snd-soc-s3c24xx-objs := s3c24xx-pcm.o
+ snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o
++snd-soc-s3c2443-ac97-objs := s3c2443-ac97.o
+
+ obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o
+ obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o
++obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.o
++
++# S3C24XX Machine Support
++snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o
++snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o
++
++obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
++obj-$(CONFIG_SND_S3C24XX_SOC_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o
+--- /dev/null
++++ linux-2.6.22.1/sound/soc/s3c24xx/lm4857.h
+@@ -0,0 +1,32 @@
++/*
++ * lm4857.h -- ALSA Soc Audio Layer
++ *
++ * Copyright 2007 Wolfson Microelectronics PLC.
++ * Author: Graeme Gregory
++ * graeme.gregory at wolfsonmicro.com or linux at wolfsonmicro.com
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ * Revision history
++ * 18th Jun 2007 Initial version.
++ */
++
++#ifndef LM4857_H_
++#define LM4857_H_
++
++/* The register offsets in the cache array */
++#define LM4857_MVOL 0
++#define LM4857_LVOL 1
++#define LM4857_RVOL 2
++#define LM4857_CTRL 3
++
++/* the shifts required to set these bits */
++#define LM4857_3D 5
++#define LM4857_WAKEUP 5
++#define LM4857_EPGAIN 4
++
++#endif /*LM4857_H_*/
++
+--- /dev/null
++++ linux-2.6.22.1/sound/soc/s3c24xx/neo1973_wm8753.c
+@@ -0,0 +1,670 @@
++/*
++ * neo1973_wm8753.c -- SoC audio for Neo1973
++ *
++ * Copyright 2007 Wolfson Microelectronics PLC.
++ * Author: Graeme Gregory
++ * graeme.gregory at wolfsonmicro.com or linux at wolfsonmicro.com
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ * Revision history
++ * 20th Jan 2007 Initial version.
++ * 05th Feb 2007 Rename all to Neo1973
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/timer.h>
++#include <linux/interrupt.h>
++#include <linux/platform_device.h>
++#include <linux/i2c.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++
++#include <asm/mach-types.h>
++#include <asm/hardware/scoop.h>
++#include <asm/arch/regs-iis.h>
++#include <asm/arch/regs-clock.h>
++#include <asm/arch/regs-gpio.h>
++#include <asm/hardware.h>
++#include <asm/arch/audio.h>
++#include <asm/io.h>
++#include <asm/arch/spi-gpio.h>
++#include "../codecs/wm8753.h"
++#include "lm4857.h"
++#include "s3c24xx-pcm.h"
++#include "s3c24xx-i2s.h"
++
++/* define the scenarios */
++#define NEO_AUDIO_OFF 0
++#define NEO_GSM_CALL_AUDIO_HANDSET 1
++#define NEO_GSM_CALL_AUDIO_HEADSET 2
++#define NEO_GSM_CALL_AUDIO_BLUETOOTH 3
++#define NEO_STEREO_TO_SPEAKERS 4
++#define NEO_STEREO_TO_HEADPHONES 5
++#define NEO_CAPTURE_HANDSET 6
++#define NEO_CAPTURE_HEADSET 7
++#define NEO_CAPTURE_BLUETOOTH 8
++
++static struct snd_soc_machine neo1973;
++static struct i2c_client *i2c;
++
++static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
++ unsigned int pll_out = 0, bclk = 0;
++ int ret = 0;
++ unsigned long iis_clkrate;
++
++ iis_clkrate = s3c24xx_i2s_get_clockrate();
++
++ switch (params_rate(params)) {
++ case 8000:
++ case 16000:
++ pll_out = 12288000;
++ break;
++ case 48000:
++ bclk = WM8753_BCLK_DIV_4;
++ pll_out = 12288000;
++ break;
++ case 96000:
++ bclk = WM8753_BCLK_DIV_2;
++ pll_out = 12288000;
++ break;
++ case 11025:
++ bclk = WM8753_BCLK_DIV_16;
++ pll_out = 11289600;
++ break;
++ case 22050:
++ bclk = WM8753_BCLK_DIV_8;
++ pll_out = 11289600;
++ break;
++ case 44100:
++ bclk = WM8753_BCLK_DIV_4;
++ pll_out = 11289600;
++ break;
++ case 88200:
++ bclk = WM8753_BCLK_DIV_2;
++ pll_out = 11289600;
++ break;
++ }
++
++ /* set codec DAI configuration */
++ ret = codec_dai->dai_ops.set_fmt(codec_dai,
++ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
++ SND_SOC_DAIFMT_CBM_CFM);
++ if (ret < 0)
++ return ret;
++
++ /* set cpu DAI configuration */
++ ret = cpu_dai->dai_ops.set_fmt(cpu_dai,
++ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
++ SND_SOC_DAIFMT_CBM_CFM);
++ if (ret < 0)
++ return ret;
++
++ /* set the codec system clock for DAC and ADC */
++ ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_MCLK, pll_out,
++ SND_SOC_CLOCK_IN);
++ if (ret < 0)
++ return ret;
++
++ /* set MCLK division for sample rate */
++ ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
++ S3C2410_IISMOD_32FS );
++ if (ret < 0)
++ return ret;
++
++ /* set codec BCLK division for sample rate */
++ ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_BCLKDIV, bclk);
++ if (ret < 0)
++ return ret;
++
++ /* set prescaler division for sample rate */
++ ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
++ S3C24XX_PRESCALE(4,4));
++ if (ret < 0)
++ return ret;
++
++ /* codec PLL input is PCLK/4 */
++ ret = codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1,
++ iis_clkrate / 4, pll_out);
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
++
++ /* disable the PLL */
++ return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1, 0, 0);
++}
++
++/*
++ * Neo1973 WM8753 HiFi DAI opserations.
++ */
++static struct snd_soc_ops neo1973_hifi_ops = {
++ .hw_params = neo1973_hifi_hw_params,
++ .hw_free = neo1973_hifi_hw_free,
++};
++
++static int neo1973_voice_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
++ unsigned int pcmdiv = 0;
++ int ret = 0;
++ unsigned long iis_clkrate;
++
++ iis_clkrate = s3c24xx_i2s_get_clockrate();
++
++ if (params_rate(params) != 8000)
++ return -EINVAL;
++ if (params_channels(params) != 1)
++ return -EINVAL;
++
++ pcmdiv = WM8753_PCM_DIV_6; /* 2.048 MHz */
++
++ /* todo: gg check mode (DSP_B) against CSR datasheet */
++ /* set codec DAI configuration */
++ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B |
++ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
++ if (ret < 0)
++ return ret;
++
++ /* set the codec system clock for DAC and ADC */
++ ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_PCMCLK, 12288000,
++ SND_SOC_CLOCK_IN);
++ if (ret < 0)
++ return ret;
++
++ /* set codec PCM division for sample rate */
++ ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv);
++ if (ret < 0)
++ return ret;
++
++ /* configue and enable PLL for 12.288MHz output */
++ ret = codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL2,
++ iis_clkrate / 4, 12288000);
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++static int neo1973_voice_hw_free(struct snd_pcm_substream *substream)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
++
++ /* disable the PLL */
++ return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL2, 0, 0);
++}
++
++static struct snd_soc_ops neo1973_voice_ops = {
++ .hw_params = neo1973_voice_hw_params,
++ .hw_free = neo1973_voice_hw_free,
++};
++
++static int neo1973_scenario = 0;
++
++static int neo1973_get_scenario(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ ucontrol->value.integer.value[0] = neo1973_scenario;
++ return 0;
++}
++
++static int set_scenario_endpoints(struct snd_soc_codec *codec, int scenario)
++{
++ switch(neo1973_scenario) {
++ case NEO_AUDIO_OFF:
++ snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
++ snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
++ snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
++ break;
++ case NEO_GSM_CALL_AUDIO_HANDSET:
++ snd_soc_dapm_set_endpoint(codec, "Audio Out", 1);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line In", 1);
++ snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
++ snd_soc_dapm_set_endpoint(codec, "Call Mic", 1);
++ break;
++ case NEO_GSM_CALL_AUDIO_HEADSET:
++ snd_soc_dapm_set_endpoint(codec, "Audio Out", 1);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line In", 1);
++ snd_soc_dapm_set_endpoint(codec, "Headset Mic", 1);
++ snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
++ break;
++ case NEO_GSM_CALL_AUDIO_BLUETOOTH:
++ snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line In", 1);
++ snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
++ snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
++ break;
++ case NEO_STEREO_TO_SPEAKERS:
++ snd_soc_dapm_set_endpoint(codec, "Audio Out", 1);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
++ snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
++ snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
++ break;
++ case NEO_STEREO_TO_HEADPHONES:
++ snd_soc_dapm_set_endpoint(codec, "Audio Out", 1);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
++ snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
++ snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
++ break;
++ case NEO_CAPTURE_HANDSET:
++ snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
++ snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
++ snd_soc_dapm_set_endpoint(codec, "Call Mic", 1);
++ break;
++ case NEO_CAPTURE_HEADSET:
++ snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
++ snd_soc_dapm_set_endpoint(codec, "Headset Mic", 1);
++ snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
++ break;
++ case NEO_CAPTURE_BLUETOOTH:
++ snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
++ snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
++ snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
++ break;
++ default:
++ snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
++ snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
++ snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
++ snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
++ }
++
++ snd_soc_dapm_sync_endpoints(codec);
++
++ return 0;
++}
++
++static int neo1973_set_scenario(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
++
++ if (neo1973_scenario == ucontrol->value.integer.value[0])
++ return 0;
++
++ neo1973_scenario = ucontrol->value.integer.value[0];
++ set_scenario_endpoints(codec, neo1973_scenario);
++ return 1;
++}
++
++static u8 lm4857_regs[4] = {0x00, 0x40, 0x80, 0xC0};
++
++static void lm4857_write_regs(void)
++{
++ if (i2c_master_send(i2c, lm4857_regs, 4) != 4)
++ printk(KERN_ERR "lm4857: i2c write failed\n");
++}
++
++static int lm4857_get_reg(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ int reg=kcontrol->private_value & 0xFF;
++ int shift = (kcontrol->private_value >> 8) & 0x0F;
++ int mask = (kcontrol->private_value >> 16) & 0xFF;
++
++ ucontrol->value.integer.value[0] = (lm4857_regs[reg] >> shift) & mask;
++ return 0;
++}
++
++static int lm4857_set_reg(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ int reg = kcontrol->private_value & 0xFF;
++ int shift = (kcontrol->private_value >> 8) & 0x0F;
++ int mask = (kcontrol->private_value >> 16) & 0xFF;
++
++ if (((lm4857_regs[reg] >> shift ) & mask) ==
++ ucontrol->value.integer.value[0])
++ return 0;
++
++ lm4857_regs[reg] &= ~ (mask << shift);
++ lm4857_regs[reg] |= ucontrol->value.integer.value[0] << shift;
++ lm4857_write_regs();
++ return 1;
++}
++
++static int lm4857_get_mode(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ u8 value = lm4857_regs[LM4857_CTRL] & 0x0F;
++
++ if (value)
++ value -= 5;
++
++ ucontrol->value.integer.value[0] = value;
++ return 0;
++}
++
++static int lm4857_set_mode(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ u8 value = ucontrol->value.integer.value[0];
++
++ if (value)
++ value += 5;
++
++ if ((lm4857_regs[LM4857_CTRL] & 0x0F) == value)
++ return 0;
++
++ lm4857_regs[LM4857_CTRL] &= 0xF0;
++ lm4857_regs[LM4857_CTRL] |= value;
++ lm4857_write_regs();
++ return 1;
++}
++
++static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = {
++ SND_SOC_DAPM_LINE("Audio Out", NULL),
++ SND_SOC_DAPM_LINE("GSM Line Out", NULL),
++ SND_SOC_DAPM_LINE("GSM Line In", NULL),
++ SND_SOC_DAPM_MIC("Headset Mic", NULL),
++ SND_SOC_DAPM_MIC("Call Mic", NULL),
++};
++
++
++/* example machine audio_mapnections */
++static const char* audio_map[][3] = {
++
++ /* Connections to the lm4857 amp */
++ {"Audio Out", NULL, "LOUT1"},
++ {"Audio Out", NULL, "ROUT1"},
++
++ /* Connections to the GSM Module */
++ {"GSM Line Out", NULL, "MONO1"},
++ {"GSM Line Out", NULL, "MONO2"},
++ {"RXP", NULL, "GSM Line In"},
++ {"RXN", NULL, "GSM Line In"},
++
++ /* Connections to Headset */
++ {"MIC1", NULL, "Mic Bias"},
++ {"Mic Bias", NULL, "Headset Mic"},
++
++ /* Call Mic */
++ {"MIC2", NULL, "Mic Bias"},
++ {"MIC2N", NULL, "Mic Bias"},
++ {"Mic Bias", NULL, "Call Mic"},
++
++ /* Connect the ALC pins */
++ {"ACIN", NULL, "ACOP"},
++
++ {NULL, NULL, NULL},
++};
++
++static const char *lm4857_mode[] = {
++ "Off",
++ "Call Speaker",
++ "Stereo Speakers",
++ "Stereo Speakers + Headphones",
++ "Headphones"
++};
++
++static const struct soc_enum lm4857_mode_enum[] = {
++ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lm4857_mode), lm4857_mode),
++};
++
++static const char *neo_scenarios[] = {
++ "Off",
++ "GSM Handset",
++ "GSM Headset",
++ "GSM Bluetooth",
++ "Speakers",
++ "Headphones",
++ "Capture Handset",
++ "Capture Headset",
++ "Capture Bluetooth"
++};
++
++static const struct soc_enum neo_scenario_enum[] = {
++ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(neo_scenarios),neo_scenarios),
++};
++
++static const struct snd_kcontrol_new wm8753_neo1973_controls[] = {
++ SOC_SINGLE_EXT("Amp Left Playback Volume", LM4857_LVOL, 0, 31, 0,
++ lm4857_get_reg, lm4857_set_reg),
++ SOC_SINGLE_EXT("Amp Right Playback Volume", LM4857_RVOL, 0, 31, 0,
++ lm4857_get_reg, lm4857_set_reg),
++ SOC_SINGLE_EXT("Amp Mono Playback Volume", LM4857_MVOL, 0, 31, 0,
++ lm4857_get_reg, lm4857_set_reg),
++ SOC_ENUM_EXT("Amp Mode", lm4857_mode_enum[0],
++ lm4857_get_mode, lm4857_set_mode),
++ SOC_ENUM_EXT("Neo Mode", neo_scenario_enum[0],
++ neo1973_get_scenario, neo1973_set_scenario),
++ SOC_SINGLE_EXT("Amp Spk 3D Playback Switch", LM4857_LVOL, 5, 1, 0,
++ lm4857_get_reg, lm4857_set_reg),
++ SOC_SINGLE_EXT("Amp HP 3d Playback Switch", LM4857_RVOL, 5, 1, 0,
++ lm4857_get_reg, lm4857_set_reg),
++ SOC_SINGLE_EXT("Amp Fast Wakeup Playback Switch", LM4857_CTRL, 5, 1, 0,
++ lm4857_get_reg, lm4857_set_reg),
++ SOC_SINGLE_EXT("Amp Earpiece 6dB Playback Switch", LM4857_CTRL, 4, 1, 0,
++ lm4857_get_reg, lm4857_set_reg),
++};
++
++/*
++ * This is an example machine initialisation for a wm8753 connected to a
++ * neo1973 II. It is missing logic to detect hp/mic insertions and logic
++ * to re-route the audio in such an event.
++ */
++static int neo1973_wm8753_init(struct snd_soc_codec *codec)
++{
++ int i, err;
++
++ /* set up NC codec pins */
++ snd_soc_dapm_set_endpoint(codec, "LOUT2", 0);
++ snd_soc_dapm_set_endpoint(codec, "ROUT2", 0);
++ snd_soc_dapm_set_endpoint(codec, "OUT3", 0);
++ snd_soc_dapm_set_endpoint(codec, "OUT4", 0);
++ snd_soc_dapm_set_endpoint(codec, "LINE1", 0);
++ snd_soc_dapm_set_endpoint(codec, "LINE2", 0);
++
++
++ /* set endpoints to default mode */
++ set_scenario_endpoints(codec, NEO_AUDIO_OFF);
++
++ /* Add neo1973 specific widgets */
++ for (i = 0; i < ARRAY_SIZE(wm8753_dapm_widgets); i++)
++ snd_soc_dapm_new_control(codec, &wm8753_dapm_widgets[i]);
++
++ /* add neo1973 specific controls */
++ for (i = 0; i < ARRAY_SIZE(wm8753_neo1973_controls); i++) {
++ err = snd_ctl_add(codec->card,
++ snd_soc_cnew(&wm8753_neo1973_controls[i],
++ codec, NULL));
++ if (err < 0)
++ return err;
++ }
++
++ /* set up neo1973 specific audio path audio_mapnects */
++ for (i = 0; audio_map[i][0] != NULL; i++) {
++ snd_soc_dapm_connect_input(codec, audio_map[i][0],
++ audio_map[i][1], audio_map[i][2]);
++ }
++
++ snd_soc_dapm_sync_endpoints(codec);
++ return 0;
++}
++
++/*
++ * BT Codec DAI
++ */
++static struct snd_soc_cpu_dai bt_dai =
++{ .name = "Bluetooth",
++ .id = 0,
++ .type = SND_SOC_DAI_PCM,
++ .playback = {
++ .channels_min = 1,
++ .channels_max = 1,
++ .rates = SNDRV_PCM_RATE_8000,
++ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
++ .capture = {
++ .channels_min = 1,
++ .channels_max = 1,
++ .rates = SNDRV_PCM_RATE_8000,
++ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
++};
++
++static struct snd_soc_dai_link neo1973_dai[] = {
++{ /* Hifi Playback - for similatious use with voice below */
++ .name = "WM8753",
++ .stream_name = "WM8753 HiFi",
++ .cpu_dai = &s3c24xx_i2s_dai,
++ .codec_dai = &wm8753_dai[WM8753_DAI_HIFI],
++ .init = neo1973_wm8753_init,
++ .ops = &neo1973_hifi_ops,
++},
++{ /* Voice via BT */
++ .name = "Bluetooth",
++ .stream_name = "Voice",
++ .cpu_dai = &bt_dai,
++ .codec_dai = &wm8753_dai[WM8753_DAI_VOICE],
++ .ops = &neo1973_voice_ops,
++},
++};
++
++static struct snd_soc_machine neo1973 = {
++ .name = "neo1973",
++ .dai_link = neo1973_dai,
++ .num_links = ARRAY_SIZE(neo1973_dai),
++};
++
++static struct wm8753_setup_data neo1973_wm8753_setup = {
++ .i2c_address = 0x1a,
++};
++
++static struct snd_soc_device neo1973_snd_devdata = {
++ .machine = &neo1973,
++ .platform = &s3c24xx_soc_platform,
++ .codec_dev = &soc_codec_dev_wm8753,
++ .codec_data = &neo1973_wm8753_setup,
++};
++
++static struct i2c_client client_template;
++
++static unsigned short normal_i2c[] = { 0x7C, I2C_CLIENT_END };
++
++/* Magic definition of all other variables and things */
++I2C_CLIENT_INSMOD;
++
++static int lm4857_amp_probe(struct i2c_adapter *adap, int addr, int kind)
++{
++ int ret;
++
++ client_template.adapter = adap;
++ client_template.addr = addr;
++
++ i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL);
++ if (i2c == NULL)
++ return -ENOMEM;
++
++ ret = i2c_attach_client(i2c);
++ if (ret < 0) {
++ printk(KERN_ERR "LM4857 failed to attach at addr %x\n", addr);
++ goto exit_err;
++ }
++
++ lm4857_write_regs();
++ return ret;
++
++exit_err:
++ kfree(i2c);
++ return ret;
++}
++
++static int lm4857_i2c_detach(struct i2c_client *client)
++{
++ i2c_detach_client(client);
++ kfree(client);
++ return 0;
++}
++
++static int lm4857_i2c_attach(struct i2c_adapter *adap)
++{
++ return i2c_probe(adap, &addr_data, lm4857_amp_probe);
++}
++
++/* corgi i2c codec control layer */
++static struct i2c_driver lm4857_i2c_driver = {
++ .driver = {
++ .name = "LM4857 I2C Amp",
++ .owner = THIS_MODULE,
++ },
++ .id = I2C_DRIVERID_LM4857,
++ .attach_adapter = lm4857_i2c_attach,
++ .detach_client = lm4857_i2c_detach,
++ .command = NULL,
++};
++
++static struct i2c_client client_template = {
++ .name = "LM4857",
++ .driver = &lm4857_i2c_driver,
++};
++
++static struct platform_device *neo1973_snd_device;
++
++static int __init neo1973_init(void)
++{
++ int ret;
++
++ neo1973_snd_device = platform_device_alloc("soc-audio", -1);
++ if (!neo1973_snd_device)
++ return -ENOMEM;
++
++ platform_set_drvdata(neo1973_snd_device, &neo1973_snd_devdata);
++ neo1973_snd_devdata.dev = &neo1973_snd_device->dev;
++ ret = platform_device_add(neo1973_snd_device);
++
++ if (ret)
++ platform_device_put(neo1973_snd_device);
++
++ ret = i2c_add_driver(&lm4857_i2c_driver);
++ if (ret != 0)
++ printk(KERN_ERR "can't add i2c driver");
++
++ return ret;
++}
++
++static void __exit neo1973_exit(void)
++{
++ platform_device_unregister(neo1973_snd_device);
++}
++
++module_init(neo1973_init);
++module_exit(neo1973_exit);
++
++/* Module information */
++MODULE_AUTHOR("Graeme Gregory, graeme.gregory at wolfsonmicro.com, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.6.22.1/sound/soc/s3c24xx/s3c2443-ac97.c
+@@ -0,0 +1,401 @@
++/*
++ * s3c2443-ac97.c -- ALSA Soc Audio Layer
++ *
++ * (c) 2007 Wolfson Microelectronics PLC.
++ * Graeme Gregory graeme.gregory at wolfsonmicro.com or linux at wolfsonmicro.com
++ *
++ * Copyright (C) 2005, Sean Choi <sh428.choi at samsung.com>
++ * All rights reserved.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * Revision history
++ * 21st Mar 2007 Initial Version
++ */
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/interrupt.h>
++#include <linux/wait.h>
++#include <linux/delay.h>
++#include <linux/clk.h>
++
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/ac97_codec.h>
++#include <sound/initval.h>
++#include <sound/soc.h>
++
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/arch/regs-ac97.h>
++#include <asm/arch/regs-gpio.h>
++#include <asm/arch/regs-clock.h>
++#include <asm/arch/audio.h>
++#include <asm/dma.h>
++#include <asm/arch/dma.h>
++
++#include "s3c24xx-pcm.h"
++#include "s3c24xx-ac97.h"
++
++struct s3c24xx_ac97_info {
++ void __iomem *regs;
++ struct clk *ac97_clk;
++};
++static struct s3c24xx_ac97_info s3c24xx_ac97;
++
++DECLARE_COMPLETION(ac97_completion);
++static u32 codec_ready;
++static DECLARE_MUTEX(ac97_mutex);
++
++static unsigned short s3c2443_ac97_read(struct snd_ac97 *ac97,
++ unsigned short reg)
++{
++ u32 ac_glbctrl;
++ u32 ac_codec_cmd;
++ u32 stat, addr, data;
++
++ down(&ac97_mutex);
++
++ codec_ready = S3C_AC97_GLBSTAT_CODECREADY;
++ ac_codec_cmd = readl(s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
++ ac_codec_cmd = S3C_AC97_CODEC_CMD_READ | AC_CMD_ADDR(reg);
++ writel(ac_codec_cmd, s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
++
++ udelay(50);
++
++ ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
++ ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
++ writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
++
++ wait_for_completion(&ac97_completion);
++
++ stat = readl(s3c24xx_ac97.regs + S3C_AC97_STAT);
++ addr = (stat >> 16) & 0x7f;
++ data = (stat & 0xffff);
++
++ if (addr != reg)
++ printk(KERN_ERR "s3c24xx-ac97: req addr = %02x,"
++ " rep addr = %02x\n", reg, addr);
++
++ up(&ac97_mutex);
++
++ return (unsigned short)data;
++}
++
++static void s3c2443_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
++ unsigned short val)
++{
++ u32 ac_glbctrl;
++ u32 ac_codec_cmd;
++
++ down(&ac97_mutex);
++
++ codec_ready = S3C_AC97_GLBSTAT_CODECREADY;
++ ac_codec_cmd = readl(s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
++ ac_codec_cmd = AC_CMD_ADDR(reg) | AC_CMD_DATA(val);
++ writel(ac_codec_cmd, s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
++
++ udelay(50);
++
++ ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
++ ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
++ writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
++
++ wait_for_completion(&ac97_completion);
++
++ ac_codec_cmd = readl(s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
++ ac_codec_cmd |= S3C_AC97_CODEC_CMD_READ;
++ writel(ac_codec_cmd, s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
++
++ up(&ac97_mutex);
++
++}
++
++static void s3c2443_ac97_warm_reset(struct snd_ac97 *ac97)
++{
++ u32 ac_glbctrl;
++
++ ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
++ ac_glbctrl = S3C_AC97_GLBCTRL_WARMRESET;
++ writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
++ msleep(1);
++
++ ac_glbctrl = 0;
++ writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
++ msleep(1);
++}
++
++static void s3c2443_ac97_cold_reset(struct snd_ac97 *ac97)
++{
++ u32 ac_glbctrl;
++
++ ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
++ ac_glbctrl = S3C_AC97_GLBCTRL_COLDRESET;
++ writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
++ msleep(1);
++
++ ac_glbctrl = 0;
++ writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
++ msleep(1);
++
++ ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
++ ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
++ writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
++ msleep(1);
++
++ ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE;
++ writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
++ msleep(1);
++
++ ac_glbctrl |= S3C_AC97_GLBCTRL_PCMOUTTM_DMA |
++ S3C_AC97_GLBCTRL_PCMINTM_DMA | S3C_AC97_GLBCTRL_MICINTM_DMA;
++ writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
++}
++
++static irqreturn_t s3c2443_ac97_irq(int irq, void *dev_id)
++{
++ int status;
++ u32 ac_glbctrl;
++
++ status = readl(s3c24xx_ac97.regs + S3C_AC97_GLBSTAT) & codec_ready;
++
++ if (status) {
++ ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
++ ac_glbctrl &= ~S3C_AC97_GLBCTRL_CODECREADYIE;
++ writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
++ complete(&ac97_completion);
++ }
++ return IRQ_HANDLED;
++}
++
++struct snd_ac97_bus_ops soc_ac97_ops = {
++ .read = s3c2443_ac97_read,
++ .write = s3c2443_ac97_write,
++ .warm_reset = s3c2443_ac97_warm_reset,
++ .reset = s3c2443_ac97_cold_reset,
++};
++
++static struct s3c2410_dma_client s3c2443_dma_client_out = {
++ .name = "AC97 PCM Stereo out"
++};
++
++static struct s3c2410_dma_client s3c2443_dma_client_in = {
++ .name = "AC97 PCM Stereo in"
++};
++
++static struct s3c2410_dma_client s3c2443_dma_client_micin = {
++ .name = "AC97 Mic Mono in"
++};
++
++static struct s3c24xx_pcm_dma_params s3c2443_ac97_pcm_stereo_out = {
++ .client = &s3c2443_dma_client_out,
++ .channel = DMACH_PCM_OUT,
++ .dma_addr = S3C2440_PA_AC97 + S3C_AC97_PCM_DATA,
++ .dma_size = 4,
++};
++
++static struct s3c24xx_pcm_dma_params s3c2443_ac97_pcm_stereo_in = {
++ .client = &s3c2443_dma_client_in,
++ .channel = DMACH_PCM_IN,
++ .dma_addr = S3C2440_PA_AC97 + S3C_AC97_PCM_DATA,
++ .dma_size = 4,
++};
++
++static struct s3c24xx_pcm_dma_params s3c2443_ac97_mic_mono_in = {
++ .client = &s3c2443_dma_client_micin,
++ .channel = DMACH_MIC_IN,
++ .dma_addr = S3C2440_PA_AC97 + S3C_AC97_MIC_DATA,
++ .dma_size = 4,
++};
++
++static int s3c2443_ac97_probe(struct platform_device *pdev)
++{
++ int ret;
++ u32 ac_glbctrl;
++
++ s3c24xx_ac97.regs = ioremap(S3C2440_PA_AC97, 0x100);
++ if (s3c24xx_ac97.regs == NULL)
++ return -ENXIO;
++
++ s3c24xx_ac97.ac97_clk = clk_get(&pdev->dev, "ac97");
++ if (s3c24xx_ac97.ac97_clk == NULL) {
++ printk(KERN_ERR "s3c2443-ac97 failed to get ac97_clock\n");
++ iounmap(s3c24xx_ac97.regs);
++ return -ENODEV;
++ }
++ clk_enable(s3c24xx_ac97.ac97_clk);
++
++ s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2443_GPE0_AC_nRESET);
++ s3c2410_gpio_cfgpin(S3C2410_GPE1, S3C2443_GPE1_AC_SYNC);
++ s3c2410_gpio_cfgpin(S3C2410_GPE2, S3C2443_GPE2_AC_BITCLK);
++ s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2443_GPE3_AC_SDI);
++ s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2443_GPE4_AC_SDO);
++
++ ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
++ ac_glbctrl = S3C_AC97_GLBCTRL_COLDRESET;
++ writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
++ msleep(1);
++
++ ac_glbctrl = 0;
++ writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
++ msleep(1);
++
++ ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
++ ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
++ writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
++ msleep(1);
++
++ ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE;
++ writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
++
++ ret = request_irq(IRQ_S3C2443_AC97, s3c2443_ac97_irq,
++ IRQF_DISABLED, "AC97", NULL);
++ if (ret < 0) {
++ printk(KERN_ERR "s3c24xx-ac97: interrupt request failed.\n");
++ clk_disable(s3c24xx_ac97.ac97_clk);
++ clk_put(s3c24xx_ac97.ac97_clk);
++ iounmap(s3c24xx_ac97.regs);
++ }
++ return ret;
++}
++
++static void s3c2443_ac97_remove(struct platform_device *pdev)
++{
++ free_irq(IRQ_S3C2443_AC97, NULL);
++ clk_disable(s3c24xx_ac97.ac97_clk);
++ clk_put(s3c24xx_ac97.ac97_clk);
++ iounmap(s3c24xx_ac97.regs);
++}
++
++static int s3c2443_ac97_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
++
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++ cpu_dai->dma_data = &s3c2443_ac97_pcm_stereo_out;
++ else
++ cpu_dai->dma_data = &s3c2443_ac97_pcm_stereo_in;
++
++ return 0;
++}
++
++static int s3c2443_ac97_trigger(struct snd_pcm_substream *substream, int cmd)
++{
++ u32 ac_glbctrl;
++
++ ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
++ switch(cmd) {
++ case SNDRV_PCM_TRIGGER_START:
++ case SNDRV_PCM_TRIGGER_RESUME:
++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
++ ac_glbctrl |= S3C_AC97_GLBCTRL_PCMINTM_DMA;
++ else
++ ac_glbctrl |= S3C_AC97_GLBCTRL_PCMOUTTM_DMA;
++ break;
++ case SNDRV_PCM_TRIGGER_STOP:
++ case SNDRV_PCM_TRIGGER_SUSPEND:
++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
++ ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMINTM_MASK;
++ else
++ ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMOUTTM_MASK;
++ break;
++ }
++ writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
++
++ return 0;
++}
++
++static int s3c2443_ac97_hw_mic_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
++
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++ return -ENODEV;
++ else
++ cpu_dai->dma_data = &s3c2443_ac97_mic_mono_in;
++
++ return 0;
++}
++
++static int s3c2443_ac97_mic_trigger(struct snd_pcm_substream *substream,
++ int cmd)
++{
++ u32 ac_glbctrl;
++
++ ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
++ switch(cmd) {
++ case SNDRV_PCM_TRIGGER_START:
++ case SNDRV_PCM_TRIGGER_RESUME:
++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
++ ac_glbctrl |= S3C_AC97_GLBCTRL_PCMINTM_DMA;
++ break;
++ case SNDRV_PCM_TRIGGER_STOP:
++ case SNDRV_PCM_TRIGGER_SUSPEND:
++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++ ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMINTM_MASK;
++ }
++ writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
++
++ return 0;
++}
++
++#define s3c2443_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
++ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
++ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
++
++struct snd_soc_cpu_dai s3c2443_ac97_dai[] = {
++{
++ .name = "s3c2443-ac97",
++ .id = 0,
++ .type = SND_SOC_DAI_AC97,
++ .probe = s3c2443_ac97_probe,
++ .remove = s3c2443_ac97_remove,
++ .playback = {
++ .stream_name = "AC97 Playback",
++ .channels_min = 2,
++ .channels_max = 2,
++ .rates = s3c2443_AC97_RATES,
++ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
++ .capture = {
++ .stream_name = "AC97 Capture",
++ .channels_min = 2,
++ .channels_max = 2,
++ .rates = s3c2443_AC97_RATES,
++ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
++ .ops = {
++ .hw_params = s3c2443_ac97_hw_params,
++ .trigger = s3c2443_ac97_trigger},
++},
++{
++ .name = "pxa2xx-ac97-mic",
++ .id = 1,
++ .type = SND_SOC_DAI_AC97,
++ .capture = {
++ .stream_name = "AC97 Mic Capture",
++ .channels_min = 1,
++ .channels_max = 1,
++ .rates = s3c2443_AC97_RATES,
++ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
++ .ops = {
++ .hw_params = s3c2443_ac97_hw_mic_params,
++ .trigger = s3c2443_ac97_mic_trigger,},
++},
++};
++
++EXPORT_SYMBOL_GPL(s3c2443_ac97_dai);
++EXPORT_SYMBOL_GPL(soc_ac97_ops);
++
++MODULE_AUTHOR("Graeme Gregory");
++MODULE_DESCRIPTION("AC97 driver for the Samsung s3c2443 chip");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.6.22.1/sound/soc/s3c24xx/s3c24xx-ac97.h
+@@ -0,0 +1,25 @@
++/*
++ * s3c24xx-ac97.c -- ALSA Soc Audio Layer
++ *
++ * (c) 2007 Wolfson Microelectronics PLC.
++ * Author: Graeme Gregory
++ * graeme.gregory at wolfsonmicro.com or linux at wolfsonmicro.com
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ * Revision history
++ * 10th Nov 2006 Initial version.
++ */
++
++#ifndef S3C24XXAC97_H_
++#define S3C24XXAC97_H_
++
++#define AC_CMD_ADDR(x) (x << 16)
++#define AC_CMD_DATA(x) (x & 0xffff)
++
++extern struct snd_soc_cpu_dai s3c2443_ac97_dai[];
++
++#endif /*S3C24XXAC97_H_*/
+--- linux-2.6.22.1.orig/sound/soc/s3c24xx/s3c24xx-i2s.c
++++ linux-2.6.22.1/sound/soc/s3c24xx/s3c24xx-i2s.c
+@@ -344,11 +344,11 @@
+ DBG("Entered %s\n", __FUNCTION__);
+
+ switch (div_id) {
+- case S3C24XX_DIV_MCLK:
++ case S3C24XX_DIV_BCLK:
+ reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~S3C2410_IISMOD_FS_MASK;
+ writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
+ break;
+- case S3C24XX_DIV_BCLK:
++ case S3C24XX_DIV_MCLK:
+ reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~(S3C2410_IISMOD_384FS);
+ writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
+ break;
+--- /dev/null
++++ linux-2.6.22.1/sound/soc/s3c24xx/smdk2443_wm9710.c
+@@ -0,0 +1,85 @@
++/*
++ * smdk2443_wm9710.c -- SoC audio for smdk2443
++ *
++ * Copyright 2007 Wolfson Microelectronics PLC.
++ * Author: Graeme Gregory
++ * graeme.gregory at wolfsonmicro.com or linux at wolfsonmicro.com
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ * Revision history
++ * 8th Mar 2007 Initial version.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/device.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++
++#include "../codecs/ac97.h"
++#include "s3c24xx-pcm.h"
++#include "s3c24xx-ac97.h"
++
++static struct snd_soc_machine smdk2443;
++
++static struct snd_soc_dai_link smdk2443_dai[] = {
++{
++ .name = "AC97",
++ .stream_name = "AC97 HiFi",
++ .cpu_dai = &s3c2443_ac97_dai[0],
++ .codec_dai = &ac97_dai,
++},
++};
++
++static struct snd_soc_machine smdk2443 = {
++ .name = "SMDK2443",
++ .dai_link = smdk2443_dai,
++ .num_links = ARRAY_SIZE(smdk2443_dai),
++};
++
++static struct snd_soc_device smdk2443_snd_ac97_devdata = {
++ .machine = &smdk2443,
++ .platform = &s3c24xx_soc_platform,
++ .codec_dev = &soc_codec_dev_ac97,
++};
++
++static struct platform_device *smdk2443_snd_ac97_device;
++
++static int __init smdk2443_init(void)
++{
++ int ret;
++
++ smdk2443_snd_ac97_device = platform_device_alloc("soc-audio", -1);
++ if (!smdk2443_snd_ac97_device)
++ return -ENOMEM;
++
++ platform_set_drvdata(smdk2443_snd_ac97_device,
++ &smdk2443_snd_ac97_devdata);
++ smdk2443_snd_ac97_devdata.dev = &smdk2443_snd_ac97_device->dev;
++ ret = platform_device_add(smdk2443_snd_ac97_device);
++
++ if (ret)
++ platform_device_put(smdk2443_snd_ac97_device);
++
++ return ret;
++}
++
++static void __exit smdk2443_exit(void)
++{
++ platform_device_unregister(smdk2443_snd_ac97_device);
++}
++
++module_init(smdk2443_init);
++module_exit(smdk2443_exit);
++
++/* Module information */
++MODULE_AUTHOR("Graeme Gregory, graeme.gregory at wolfsonmicro.com, www.wolfsonmicro.com");
++MODULE_DESCRIPTION("ALSA SoC WM9710 SMDK2443");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ linux-2.6.22.1/sound/soc/sh/Kconfig
+@@ -0,0 +1,38 @@
++menu "SoC Audio support for SuperH"
++
++config SND_SOC_PCM_SH7760
++ tristate "SoC Audio support for Renesas SH7760"
++ depends on CPU_SUBTYPE_SH7760 && SND_SOC && SH_DMABRG
++ help
++ Enable this option for SH7760 AC97/I2S audio support.
++
++
++##
++## Audio unit modules
++##
++
++config SND_SOC_SH4_HAC
++ select AC97_BUS
++ select SND_SOC_AC97_BUS
++ select SND_AC97_CODEC
++ tristate
++
++config SND_SOC_SH4_SSI
++ tristate
++
++
++
++##
++## Boards
++##
++
++config SND_SH7760_AC97
++ tristate "SH7760 AC97 sound support"
++ depends on CPU_SUBTYPE_SH7760 && SND_SOC_PCM_SH7760
++ select SND_SOC_SH4_HAC
++ select SND_SOC_AC97_CODEC
++ help
++ This option enables generic sound support for the first
++ AC97 unit of the SH7760.
++
++endmenu
+--- /dev/null
++++ linux-2.6.22.1/sound/soc/sh/Makefile
+@@ -0,0 +1,14 @@
++## DMA engines
++snd-soc-dma-sh7760-objs := dma-sh7760.o
++obj-$(CONFIG_SND_SOC_PCM_SH7760) += snd-soc-dma-sh7760.o
++
++## audio units found on some SH-4
++snd-soc-hac-objs := hac.o
++snd-soc-ssi-objs := ssi.o
++obj-$(CONFIG_SND_SOC_SH4_HAC) += snd-soc-hac.o
++obj-$(CONFIG_SND_SOC_SH4_SSI) += snd-soc-ssi.o
++
++## boards
++snd-soc-sh7760-ac97-objs := sh7760-ac97.o
++
++obj-$(CONFIG_SND_SH7760_AC97) += snd-soc-sh7760-ac97.o
+--- /dev/null
++++ linux-2.6.22.1/sound/soc/sh/dma-sh7760.c
+@@ -0,0 +1,354 @@
++/*
++ * SH7760 ("camelot") DMABRG audio DMA unit support
++ *
++ * Copyright (C) 2007 Manuel Lauss <mano at roarinelk.homelinux.net>
++ * licensed under the terms outlined in the file COPYING at the root
++ * of the linux kernel sources.
++ *
++ * The SH7760 DMABRG provides 4 dma channels (2x rec, 2x play), which
++ * trigger an interrupt when one half of the programmed transfer size
++ * has been xmitted.
++ *
++ * FIXME: little-endian only for now
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/dma-mapping.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <asm/dmabrg.h>
++
++
++/* registers and bits */
++#define BRGATXSAR 0x00
++#define BRGARXDAR 0x04
++#define BRGATXTCR 0x08
++#define BRGARXTCR 0x0C
++#define BRGACR 0x10
++#define BRGATXTCNT 0x14
++#define BRGARXTCNT 0x18
++
++#define ACR_RAR (1 << 18)
++#define ACR_RDS (1 << 17)
++#define ACR_RDE (1 << 16)
++#define ACR_TAR (1 << 2)
++#define ACR_TDS (1 << 1)
++#define ACR_TDE (1 << 0)
++
++/* receiver/transmitter data alignment */
++#define ACR_RAM_NONE (0 << 24)
++#define ACR_RAM_4BYTE (1 << 24)
++#define ACR_RAM_2WORD (2 << 24)
++#define ACR_TAM_NONE (0 << 8)
++#define ACR_TAM_4BYTE (1 << 8)
++#define ACR_TAM_2WORD (2 << 8)
++
++
++struct camelot_pcm {
++ unsigned long mmio; /* DMABRG audio channel control reg MMIO */
++ unsigned int txid; /* ID of first DMABRG IRQ for this unit */
++
++ struct snd_pcm_substream *tx_ss;
++ unsigned long tx_period_size;
++ unsigned int tx_period;
++
++ struct snd_pcm_substream *rx_ss;
++ unsigned long rx_period_size;
++ unsigned int rx_period;
++
++} cam_pcm_data[2] = {
++ {
++ .mmio = 0xFE3C0040,
++ .txid = DMABRGIRQ_A0TXF,
++ },
++ {
++ .mmio = 0xFE3C0060,
++ .txid = DMABRGIRQ_A1TXF,
++ },
++};
++
++#define BRGREG(x) (*(unsigned long *)(cam->mmio + (x)))
++
++/*
++ * set a minimum of 16kb per period, to avoid interrupt-"storm" and
++ * resulting skipping. In general, the bigger the minimum size, the
++ * better for overall system performance. (The SH7760 is a puny CPU
++ * with a slow SDRAM interface and poor internal bus bandwidth,
++ * *especially* when the LCDC is active). The minimum for the DMAC
++ * is 8 bytes; 16kbytes are enough to get skip-free playback of a
++ * 44kHz/16bit/stereo MP3 on a lightly loaded system, and maintain
++ * reasonable responsiveness in MPlayer.
++ */
++#define DMABRG_PERIOD_MIN 16 * 1024
++#define DMABRG_PERIOD_MAX 0x03fffffc
++#define DMABRG_PREALLOC_BUFFER 32 * 1024
++#define DMABRG_PREALLOC_BUFFER_MAX 32 * 1024
++
++/* support everything the SSI supports */
++#define DMABRG_RATES \
++ SNDRV_PCM_RATE_8000_192000
++
++#define DMABRG_FMTS \
++ (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | \
++ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | \
++ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE | \
++ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3LE | \
++ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE)
++
++static struct snd_pcm_hardware camelot_pcm_hardware = {
++ .info = (SNDRV_PCM_INFO_MMAP |
++ SNDRV_PCM_INFO_INTERLEAVED |
++ SNDRV_PCM_INFO_BLOCK_TRANSFER |
++ SNDRV_PCM_INFO_MMAP_VALID),
++ .formats = DMABRG_FMTS,
++ .rates = DMABRG_RATES,
++ .rate_min = 8000,
++ .rate_max = 192000,
++ .channels_min = 2,
++ .channels_max = 8, /* max of the SSI */
++ .buffer_bytes_max = DMABRG_PERIOD_MAX,
++ .period_bytes_min = DMABRG_PERIOD_MIN,
++ .period_bytes_max = DMABRG_PERIOD_MAX / 2,
++ .periods_min = 2,
++ .periods_max = 2,
++ .fifo_size = 128,
++};
++
++static void camelot_txdma(void *data)
++{
++ struct camelot_pcm *cam = data;
++ cam->tx_period ^= 1;
++ snd_pcm_period_elapsed(cam->tx_ss);
++}
++
++static void camelot_rxdma(void *data)
++{
++ struct camelot_pcm *cam = data;
++ cam->rx_period ^= 1;
++ snd_pcm_period_elapsed(cam->rx_ss);
++}
++
++static int camelot_pcm_open(struct snd_pcm_substream *substream)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id];
++ int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
++ int ret, dmairq;
++
++ snd_soc_set_runtime_hwparams(substream, &camelot_pcm_hardware);
++
++ /* DMABRG buffer half/full events */
++ dmairq = (recv) ? cam->txid + 2 : cam->txid;
++ if (recv) {
++ cam->rx_ss = substream;
++ ret = dmabrg_request_irq(dmairq, camelot_rxdma, cam);
++ if (unlikely(ret)) {
++ pr_debug("audio unit %d irqs already taken!\n",
++ rtd->dai->cpu_dai->id);
++ return -EBUSY;
++ }
++ (void)dmabrg_request_irq(dmairq + 1,camelot_rxdma, cam);
++ } else {
++ cam->tx_ss = substream;
++ ret = dmabrg_request_irq(dmairq, camelot_txdma, cam);
++ if (unlikely(ret)) {
++ pr_debug("audio unit %d irqs already taken!\n",
++ rtd->dai->cpu_dai->id);
++ return -EBUSY;
++ }
++ (void)dmabrg_request_irq(dmairq + 1, camelot_txdma, cam);
++ }
++ return 0;
++}
++
++static int camelot_pcm_close(struct snd_pcm_substream *substream)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id];
++ int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
++ int dmairq;
++
++ dmairq = (recv) ? cam->txid + 2 : cam->txid;
++
++ if (recv)
++ cam->rx_ss = NULL;
++ else
++ cam->tx_ss = NULL;
++
++ dmabrg_free_irq(dmairq + 1);
++ dmabrg_free_irq(dmairq);
++
++ return 0;
++}
++
++static int camelot_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *hw_params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id];
++ int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
++ int ret;
++
++ ret = snd_pcm_lib_malloc_pages(substream,
++ params_buffer_bytes(hw_params));
++ if (ret < 0)
++ return ret;
++
++ if (recv) {
++ cam->rx_period_size = params_period_bytes(hw_params);
++ cam->rx_period = 0;
++ } else {
++ cam->tx_period_size = params_period_bytes(hw_params);
++ cam->tx_period = 0;
++ }
++ return 0;
++}
++
++static int camelot_hw_free(struct snd_pcm_substream *substream)
++{
++ return snd_pcm_lib_free_pages(substream);
++}
++
++static int camelot_prepare(struct snd_pcm_substream *substream)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id];
++
++ pr_debug("PCM data: addr 0x%08ulx len %d\n",
++ (u32)runtime->dma_addr, runtime->dma_bytes);
++
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
++ BRGREG(BRGATXSAR) = (unsigned long)runtime->dma_area;
++ BRGREG(BRGATXTCR) = runtime->dma_bytes;
++ } else {
++ BRGREG(BRGARXDAR) = (unsigned long)runtime->dma_area;
++ BRGREG(BRGARXTCR) = runtime->dma_bytes;
++ }
++
++ return 0;
++}
++
++static inline void dmabrg_play_dma_start(struct camelot_pcm *cam)
++{
++ unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
++ /* start DMABRG engine: XFER start, auto-addr-reload */
++ BRGREG(BRGACR) = acr | ACR_TDE | ACR_TAR | ACR_TAM_2WORD;
++}
++
++static inline void dmabrg_play_dma_stop(struct camelot_pcm *cam)
++{
++ unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
++ /* forcibly terminate data transmission */
++ BRGREG(BRGACR) = acr | ACR_TDS;
++}
++
++static inline void dmabrg_rec_dma_start(struct camelot_pcm *cam)
++{
++ unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
++ /* start DMABRG engine: recv start, auto-reload */
++ BRGREG(BRGACR) = acr | ACR_RDE | ACR_RAR | ACR_RAM_2WORD;
++}
++
++static inline void dmabrg_rec_dma_stop(struct camelot_pcm *cam)
++{
++ unsigned long acr = BRGREG(BRGACR) & ~(ACR_TDS | ACR_RDS);
++ /* forcibly terminate data receiver */
++ BRGREG(BRGACR) = acr | ACR_RDS;
++}
++
++static int camelot_trigger(struct snd_pcm_substream *substream, int cmd)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id];
++ int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
++
++ switch (cmd) {
++ case SNDRV_PCM_TRIGGER_START:
++ if (recv)
++ dmabrg_rec_dma_start(cam);
++ else
++ dmabrg_play_dma_start(cam);
++ break;
++ case SNDRV_PCM_TRIGGER_STOP:
++ if (recv)
++ dmabrg_rec_dma_stop(cam);
++ else
++ dmabrg_play_dma_stop(cam);
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static snd_pcm_uframes_t camelot_pos(struct snd_pcm_substream *substream)
++{
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct camelot_pcm *cam = &cam_pcm_data[rtd->dai->cpu_dai->id];
++ int recv = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0:1;
++ unsigned long pos;
++
++ /* cannot use the DMABRG pointer register: under load, by the
++ * time ALSA comes around to read the register, it is already
++ * far ahead (or worse, already done with the fragment) of the
++ * position at the time the IRQ was triggered, which results in
++ * fast-playback sound in my test application (ScummVM)
++ */
++ if (recv)
++ pos = cam->rx_period ? cam->rx_period_size : 0;
++ else
++ pos = cam->tx_period ? cam->tx_period_size : 0;
++
++ return bytes_to_frames(runtime, pos);
++}
++
++static struct snd_pcm_ops camelot_pcm_ops = {
++ .open = camelot_pcm_open,
++ .close = camelot_pcm_close,
++ .ioctl = snd_pcm_lib_ioctl,
++ .hw_params = camelot_hw_params,
++ .hw_free = camelot_hw_free,
++ .prepare = camelot_prepare,
++ .trigger = camelot_trigger,
++ .pointer = camelot_pos,
++};
++
++static void camelot_pcm_free(struct snd_pcm *pcm)
++{
++ snd_pcm_lib_preallocate_free_for_all(pcm);
++}
++
++static int camelot_pcm_new(struct snd_card *card,
++ struct snd_soc_codec_dai *dai,
++ struct snd_pcm *pcm)
++{
++ /* dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel
++ * in MMAP mode (i.e. aplay -M)
++ */
++ snd_pcm_lib_preallocate_pages_for_all(pcm,
++ SNDRV_DMA_TYPE_CONTINUOUS,
++ snd_dma_continuous_data(GFP_KERNEL),
++ DMABRG_PREALLOC_BUFFER, DMABRG_PREALLOC_BUFFER_MAX);
++
++ return 0;
++}
++
++struct snd_soc_platform sh7760_soc_platform = {
++ .name = "sh7760-pcm",
++ .pcm_ops = &camelot_pcm_ops,
++ .pcm_new = camelot_pcm_new,
++ .pcm_free = camelot_pcm_free,
++};
++EXPORT_SYMBOL_GPL(sh7760_soc_platform);
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("SH7760 Audio DMA (DMABRG) driver");
++MODULE_AUTHOR("Manuel Lauss <mano at roarinelk.homelinux.net>");
+--- /dev/null
++++ linux-2.6.22.1/sound/soc/sh/hac.c
+@@ -0,0 +1,322 @@
++/*
++ * Hitachi Audio Controller (AC97) support for SH7760/SH7780
++ *
++ * Copyright (c) 2007 Manuel Lauss <mano at roarinelk.homelinux.net>
++ * licensed under the terms outlined in the file COPYING at the root
++ * of the linux kernel sources.
++ *
++ * dont forget to set IPSEL/OMSEL register bits (in your board code) to
++ * enable HAC output pins!
++ */
++
++/* BIG FAT FIXME: although the SH7760 has 2 independent AC97 units, only
++ * the FIRST can be used since ASoC does not pass any information to the
++ * ac97_read/write() functions regarding WHICH unit to use. You'll have
++ * to edit the code a bit to use the other AC97 unit. --mlau
++ */
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/interrupt.h>
++#include <linux/wait.h>
++#include <linux/delay.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/ac97_codec.h>
++#include <sound/initval.h>
++#include <sound/soc.h>
++
++/* regs and bits */
++#define HACCR 0x08
++#define HACCSAR 0x20
++#define HACCSDR 0x24
++#define HACPCML 0x28
++#define HACPCMR 0x2C
++#define HACTIER 0x50
++#define HACTSR 0x54
++#define HACRIER 0x58
++#define HACRSR 0x5C
++#define HACACR 0x60
++
++#define CR_CR (1 << 15) /* "codec-ready" indicator */
++#define CR_CDRT (1 << 11) /* cold reset */
++#define CR_WMRT (1 << 10) /* warm reset */
++#define CR_B9 (1 << 9) /* the mysterious "bit 9" */
++#define CR_ST (1 << 5) /* AC97 link start bit */
++
++#define CSAR_RD (1 << 19) /* AC97 data read bit */
++#define CSAR_WR (0)
++
++#define TSR_CMDAMT (1 << 31)
++#define TSR_CMDDMT (1 << 30)
++
++#define RSR_STARY (1 << 22)
++#define RSR_STDRY (1 << 21)
++
++#define ACR_DMARX16 (1 << 30)
++#define ACR_DMATX16 (1 << 29)
++#define ACR_TX12ATOM (1 << 26)
++#define ACR_DMARX20 ((1 << 24) | (1 << 22))
++#define ACR_DMATX20 ((1 << 23) | (1 << 21))
++
++#define CSDR_SHIFT 4
++#define CSDR_MASK (0xffff << CSDR_SHIFT)
++#define CSAR_SHIFT 12
++#define CSAR_MASK (0x7f << CSAR_SHIFT)
++
++#define AC97_WRITE_RETRY 1
++#define AC97_READ_RETRY 5
++
++/* manual-suggested AC97 codec access timeouts (us) */
++#define TMO_E1 500 /* 21 < E1 < 1000 */
++#define TMO_E2 13 /* 13 < E2 */
++#define TMO_E3 21 /* 21 < E3 */
++#define TMO_E4 500 /* 21 < E4 < 1000 */
++
++struct hac_priv {
++ unsigned long mmio; /* HAC base address */
++} hac_cpu_data[] = {
++#if defined(CONFIG_CPU_SUBTYPE_SH7760)
++ {
++ .mmio = 0xFE240000,
++ },
++ {
++ .mmio = 0xFE250000,
++ },
++#elif defined(CONFIG_CPU_SUBTYPE_SH7780)
++ {
++ .mmio = 0xFFE40000,
++ },
++#else
++#error "Unsupported SuperH SoC"
++#endif
++};
++
++#define HACREG(reg) (*(unsigned long *)(hac->mmio + (reg)))
++
++/*
++ * AC97 read/write flow as outlined in the SH7760 manual (pages 903-906)
++ */
++static int hac_get_codec_data(struct hac_priv *hac, unsigned short r,
++ unsigned short *v)
++{
++ unsigned int to1, to2, i;
++ unsigned short adr;
++
++ for (i = 0; i < AC97_READ_RETRY; ++i) {
++ *v = 0;
++ /* wait for HAC to receive something from the codec */
++ for (to1 = TMO_E4;
++ to1 && !(HACREG(HACRSR) & RSR_STARY);
++ --to1)
++ udelay(1);
++ for (to2 = TMO_E4;
++ to2 && !(HACREG(HACRSR) & RSR_STDRY);
++ --to2)
++ udelay(1);
++
++ if (!to1 && !to2)
++ return 0; /* codec comm is down */
++
++ adr = ((HACREG(HACCSAR) & CSAR_MASK) >> CSAR_SHIFT);
++ *v = ((HACREG(HACCSDR) & CSDR_MASK) >> CSDR_SHIFT);
++
++ HACREG(HACRSR) &= ~(RSR_STDRY | RSR_STARY);
++
++ if (r == adr)
++ break;
++
++ /* manual says: wait at least 21 usec before retrying */
++ udelay(21);
++ }
++ HACREG(HACRSR) &= ~(RSR_STDRY | RSR_STARY);
++ return (i < AC97_READ_RETRY);
++}
++
++static unsigned short hac_read_codec_aux(struct hac_priv *hac,
++ unsigned short reg)
++{
++ unsigned short val;
++ unsigned int i, to;
++
++ for (i = 0; i < AC97_READ_RETRY; i++) {
++ /* send_read_request */
++ local_irq_disable();
++ HACREG(HACTSR) &= ~(TSR_CMDAMT);
++ HACREG(HACCSAR) = (reg << CSAR_SHIFT) | CSAR_RD;
++ local_irq_enable();
++
++ for (to = TMO_E3;
++ to && !(HACREG(HACTSR) & TSR_CMDAMT);
++ --to)
++ udelay(1);
++
++ HACREG(HACTSR) &= ~TSR_CMDAMT;
++ val = 0;
++ if (hac_get_codec_data(hac, reg, &val) != 0)
++ break;
++ }
++
++ if (i == AC97_READ_RETRY)
++ return ~0;
++
++ return val;
++}
++
++static void hac_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
++ unsigned short val)
++{
++ int unit_id = 0 /* ac97->private_data */;
++ struct hac_priv *hac = &hac_cpu_data[unit_id];
++ unsigned int i, to;
++ /* write_codec_aux */
++ for (i = 0; i < AC97_WRITE_RETRY; i++) {
++ /* send_write_request */
++ local_irq_disable();
++ HACREG(HACTSR) &= ~(TSR_CMDDMT | TSR_CMDAMT);
++ HACREG(HACCSDR) = (val << CSDR_SHIFT);
++ HACREG(HACCSAR) = (reg << CSAR_SHIFT) & (~CSAR_RD);
++ local_irq_enable();
++
++ /* poll-wait for CMDAMT and CMDDMT */
++ for (to = TMO_E1;
++ to && !(HACREG(HACTSR) & (TSR_CMDAMT|TSR_CMDDMT));
++ --to)
++ udelay(1);
++
++ HACREG(HACTSR) &= ~(TSR_CMDAMT | TSR_CMDDMT);
++ if (to)
++ break;
++ /* timeout, try again */
++ }
++}
++
++static unsigned short hac_ac97_read(struct snd_ac97 *ac97,
++ unsigned short reg)
++{
++ int unit_id = 0 /* ac97->private_data */;
++ struct hac_priv *hac = &hac_cpu_data[unit_id];
++ return hac_read_codec_aux(hac, reg);
++}
++
++static void hac_ac97_warmrst(struct snd_ac97 *ac97)
++{
++ int unit_id = 0 /* ac97->private_data */;
++ struct hac_priv *hac = &hac_cpu_data[unit_id];
++ unsigned int tmo;
++
++ HACREG(HACCR) = CR_WMRT | CR_ST | CR_B9;
++ msleep(10);
++ HACREG(HACCR) = CR_ST | CR_B9;
++ for (tmo = 1000; (tmo > 0) && !(HACREG(HACCR) & CR_CR); tmo--)
++ udelay(1);
++
++ if (!tmo)
++ printk(KERN_INFO "hac: reset: AC97 link down!\n");
++ /* settings this bit lets us have a conversation with codec */
++ HACREG(HACACR) |= ACR_TX12ATOM;
++}
++
++static void hac_ac97_coldrst(struct snd_ac97 *ac97)
++{
++ int unit_id = 0 /* ac97->private_data */;
++ struct hac_priv *hac;
++ hac = &hac_cpu_data[unit_id];
++
++ HACREG(HACCR) = 0;
++ HACREG(HACCR) = CR_CDRT | CR_ST | CR_B9;
++ msleep(10);
++ hac_ac97_warmrst(ac97);
++}
++
++struct snd_ac97_bus_ops soc_ac97_ops = {
++ .read = hac_ac97_read,
++ .write = hac_ac97_write,
++ .reset = hac_ac97_coldrst,
++ .warm_reset = hac_ac97_warmrst,
++};
++EXPORT_SYMBOL_GPL(soc_ac97_ops);
++
++static int hac_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct hac_priv *hac = &hac_cpu_data[rtd->dai->cpu_dai->id];
++ int d = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1;
++
++ switch (params->msbits) {
++ case 16:
++ HACREG(HACACR) |= d ? ACR_DMARX16 : ACR_DMATX16;
++ HACREG(HACACR) &= d ? ~ACR_DMARX20 : ~ACR_DMATX20;
++ break;
++ case 20:
++ HACREG(HACACR) &= d ? ~ACR_DMARX16 : ~ACR_DMATX16;
++ HACREG(HACACR) |= d ? ACR_DMARX20 : ACR_DMATX20;
++ break;
++ default:
++ pr_debug("hac: invalid depth %d bit\n", params->msbits);
++ return -EINVAL;
++ break;
++ }
++
++ return 0;
++}
++
++#define AC97_RATES \
++ SNDRV_PCM_RATE_8000_192000
++
++#define AC97_FMTS \
++ SNDRV_PCM_FMTBIT_S16_LE
++
++struct snd_soc_cpu_dai sh4_hac_dai[] = {
++{
++ .name = "HAC0",
++ .id = 0,
++ .type = SND_SOC_DAI_AC97,
++ .playback = {
++ .rates = AC97_RATES,
++ .formats = AC97_FMTS,
++ .channels_min = 2,
++ .channels_max = 2,
++ },
++ .capture = {
++ .rates = AC97_RATES,
++ .formats = AC97_FMTS,
++ .channels_min = 2,
++ .channels_max = 2,
++ },
++ .ops = {
++ .hw_params = hac_hw_params,
++ },
++},
++#ifdef CONFIG_CPU_SUBTYPE_SH7760
++{
++ .name = "HAC1",
++ .id = 1,
++ .type = SND_SOC_DAI_AC97,
++ .playback = {
++ .rates = AC97_RATES,
++ .formats = AC97_FMTS,
++ .channels_min = 2,
++ .channels_max = 2,
++ },
++ .capture = {
++ .rates = AC97_RATES,
++ .formats = AC97_FMTS,
++ .channels_min = 2,
++ .channels_max = 2,
++ },
++ .ops = {
++ .hw_params = hac_hw_params,
++ },
++
++},
++#endif
++};
++EXPORT_SYMBOL_GPL(sh4_hac_dai);
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("SuperH onchip HAC (AC97) audio driver");
++MODULE_AUTHOR("Manuel Lauss <mano at roarinelk.homelinux.net>");
+--- /dev/null
++++ linux-2.6.22.1/sound/soc/sh/sh7760-ac97.c
+@@ -0,0 +1,92 @@
++/*
++ * Generic AC97 sound support for SH7760
++ *
++ * (c) 2007 Manuel Lauss
++ *
++ * Licensed under the GPLv2.
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/platform_device.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/soc.h>
++#include <sound/soc-dapm.h>
++#include <asm/io.h>
++
++#include "../codecs/ac97.h"
++
++#define IPSEL 0xFE400034
++
++/* platform specific structs can be declared here */
++extern struct snd_soc_cpu_dai sh4_hac_dai[2];
++extern struct snd_soc_platform sh7760_soc_platform;
++
++static int machine_init(struct snd_soc_codec *codec)
++{
++ snd_soc_dapm_sync_endpoints(codec);
++ return 0;
++}
++
++static struct snd_soc_dai_link sh7760_ac97_dai = {
++ .name = "AC97",
++ .stream_name = "AC97 HiFi",
++ .cpu_dai = &sh4_hac_dai[0], /* HAC0 */
++ .codec_dai = &ac97_dai,
++ .init = machine_init,
++ .ops = NULL,
++};
++
++static struct snd_soc_machine sh7760_ac97_soc_machine = {
++ .name = "SH7760 AC97",
++ .dai_link = &sh7760_ac97_dai,
++ .num_links = 1,
++};
++
++static struct snd_soc_device sh7760_ac97_snd_devdata = {
++ .machine = &sh7760_ac97_soc_machine,
++ .platform = &sh7760_soc_platform,
++ .codec_dev = &soc_codec_dev_ac97,
++};
++
++static struct platform_device *sh7760_ac97_snd_device;
++
++static int __init sh7760_ac97_init(void)
++{
++ int ret;
++ unsigned short ipsel;
++
++ /* enable both AC97 controllers in pinmux reg */
++ ipsel = ctrl_inw(IPSEL);
++ ctrl_outw(ipsel | (3 << 10), IPSEL);
++
++ ret = -ENOMEM;
++ sh7760_ac97_snd_device = platform_device_alloc("soc-audio", -1);
++ if (!sh7760_ac97_snd_device)
++ goto out;
++
++ platform_set_drvdata(sh7760_ac97_snd_device,
++ &sh7760_ac97_snd_devdata);
++ sh7760_ac97_snd_devdata.dev = &sh7760_ac97_snd_device->dev;
++ ret = platform_device_add(sh7760_ac97_snd_device);
++
++ if (ret)
++ platform_device_put(sh7760_ac97_snd_device);
++
++out:
++ return ret;
++}
++
++static void __exit sh7760_ac97_exit(void)
++{
++ platform_device_unregister(sh7760_ac97_snd_device);
++}
++
++module_init(sh7760_ac97_init);
++module_exit(sh7760_ac97_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Generic SH7760 AC97 sound machine");
++MODULE_AUTHOR("Manuel Lauss <mano at roarinelk.homelinux.net>");
+--- /dev/null
++++ linux-2.6.22.1/sound/soc/sh/ssi.c
+@@ -0,0 +1,400 @@
++/*
++ * Serial Sound Interface (I2S) support for SH7760/SH7780
++ *
++ * Copyright (c) 2007 Manuel Lauss <mano at roarinelk.homelinux.net>
++ *
++ * licensed under the terms outlined in the file COPYING at the root
++ * of the linux kernel sources.
++ *
++ * dont forget to set IPSEL/OMSEL register bits (in your board code) to
++ * enable SSI output pins!
++ */
++
++/*
++ * LIMITATIONS:
++ * The SSI unit has only one physical data line, so full duplex is
++ * impossible. This can be remedied on the SH7760 by using the
++ * other SSI unit for recording; however the SH7780 has only 1 SSI
++ * unit, and its pins are shared with the AC97 unit, among others.
++ *
++ * FEATURES:
++ * The SSI features "compressed mode": in this mode it continuously
++ * streams PCM data over the I2S lines and uses LRCK as a handshake
++ * signal. Can be used to send compressed data (AC3/DTS) to a DSP.
++ * The number of bits sent over the wire in a frame can be adjusted
++ * and can be independent from the actual sample bit depth. This is
++ * useful to support TDM mode codecs like the AD1939 which have a
++ * fixed TDM slot size, regardless of sample resolution.
++ */
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/initval.h>
++#include <sound/soc.h>
++#include <asm/io.h>
++
++#define SSICR 0x00
++#define SSISR 0x04
++
++#define CR_DMAEN (1 << 28)
++#define CR_CHNL_SHIFT 22
++#define CR_CHNL_MASK (3 << CR_CHNL_SHIFT)
++#define CR_DWL_SHIFT 19
++#define CR_DWL_MASK (7 << CR_DWL_SHIFT)
++#define CR_SWL_SHIFT 16
++#define CR_SWL_MASK (7 << CR_SWL_SHIFT)
++#define CR_SCK_MASTER (1 << 15) /* bitclock master bit */
++#define CR_SWS_MASTER (1 << 14) /* wordselect master bit */
++#define CR_SCKP (1 << 13) /* I2Sclock polarity */
++#define CR_SWSP (1 << 12) /* LRCK polarity */
++#define CR_SPDP (1 << 11)
++#define CR_SDTA (1 << 10) /* i2s alignment (msb/lsb) */
++#define CR_PDTA (1 << 9) /* fifo data alignment */
++#define CR_DEL (1 << 8) /* delay data by 1 i2sclk */
++#define CR_BREN (1 << 7) /* clock gating in burst mode */
++#define CR_CKDIV_SHIFT 4
++#define CR_CKDIV_MASK (7 << CR_CKDIV_SHIFT) /* bitclock divider */
++#define CR_MUTE (1 << 3) /* SSI mute */
++#define CR_CPEN (1 << 2) /* compressed mode */
++#define CR_TRMD (1 << 1) /* transmit/receive select */
++#define CR_EN (1 << 0) /* enable SSI */
++
++#define SSIREG(reg) (*(unsigned long *)(ssi->mmio + (reg)))
++
++struct ssi_priv {
++ unsigned long mmio;
++ unsigned long sysclk;
++ int inuse;
++} ssi_cpu_data[] = {
++#if defined(CONFIG_CPU_SUBTYPE_SH7760)
++ {
++ .mmio = 0xFE680000,
++ },
++ {
++ .mmio = 0xFE690000,
++ },
++#elif defined(CONFIG_CPU_SUBTYPE_SH7780)
++ {
++ .mmio = 0xFFE70000,
++ },
++#else
++#error "Unsupported SuperH SoC"
++#endif
++};
++
++/*
++ * track usage of the SSI; it is simplex-only so prevent attempts of
++ * concurrent playback + capture. FIXME: any locking required?
++ */
++static int ssi_startup(struct snd_pcm_substream *substream)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id];
++ if (ssi->inuse) {
++ pr_debug("ssi: already in use!\n");
++ return -EBUSY;
++ } else
++ ssi->inuse = 1;
++ return 0;
++}
++
++static void ssi_shutdown(struct snd_pcm_substream *substream)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id];
++
++ ssi->inuse = 0;
++}
++
++static int ssi_trigger(struct snd_pcm_substream *substream, int cmd)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id];
++
++ switch (cmd) {
++ case SNDRV_PCM_TRIGGER_START:
++ SSIREG(SSICR) |= CR_DMAEN | CR_EN;
++ break;
++ case SNDRV_PCM_TRIGGER_STOP:
++ SSIREG(SSICR) &= ~(CR_DMAEN | CR_EN);
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static int ssi_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params)
++{
++ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct ssi_priv *ssi = &ssi_cpu_data[rtd->dai->cpu_dai->id];
++ unsigned long ssicr = SSIREG(SSICR);
++ unsigned int bits, channels, swl, recv, i;
++
++ channels = params_channels(params);
++ bits = params->msbits;
++ recv = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 0 : 1;
++
++ pr_debug("ssi_hw_params() enter\nssicr was %08lx\n", ssicr);
++ pr_debug("bits: %d channels: %d\n", bits, channels);
++
++ ssicr &= ~(CR_TRMD | CR_CHNL_MASK | CR_DWL_MASK | CR_PDTA |
++ CR_SWL_MASK);
++
++ /* direction (send/receive) */
++ if (!recv)
++ ssicr |= CR_TRMD; /* transmit */
++
++ /* channels */
++ if ((channels < 2) || (channels > 8) || (channels & 1)) {
++ pr_debug("ssi: invalid number of channels\n");
++ return -EINVAL;
++ }
++ ssicr |= ((channels >> 1) - 1) << CR_CHNL_SHIFT;
++
++ /* DATA WORD LENGTH (DWL): databits in audio sample */
++ i = 0;
++ switch (bits) {
++ case 32: ++i;
++ case 24: ++i;
++ case 22: ++i;
++ case 20: ++i;
++ case 18: ++i;
++ case 16: ++i;
++ ssicr |= i << CR_DWL_SHIFT;
++ case 8: break;
++ default:
++ pr_debug("ssi: invalid sample width\n");
++ return -EINVAL;
++ }
++
++ /*
++ * SYSTEM WORD LENGTH: size in bits of half a frame over the I2S
++ * wires. This is usually bits_per_sample x channels/2; i.e. in
++ * Stereo mode the SWL equals DWL. SWL can be bigger than the
++ * product of (channels_per_slot x samplebits), e.g. for codecs
++ * like the AD1939 which only accept 32bit wide TDM slots. For
++ * "standard" I2S operation we set SWL = chans / 2 * DWL here.
++ * Waiting for ASoC to get TDM support ;-)
++ */
++ if ((bits > 16) && (bits <= 24)) {
++ bits = 24; /* these are padded by the SSI */
++ /*ssicr |= CR_PDTA;*/ /* cpu/data endianness ? */
++ }
++ i = 0;
++ swl = (bits * channels) / 2;
++ switch (swl) {
++ case 256: ++i;
++ case 128: ++i;
++ case 64: ++i;
++ case 48: ++i;
++ case 32: ++i;
++ case 16: ++i;
++ ssicr |= i << CR_SWL_SHIFT;
++ case 8: break;
++ default:
++ pr_debug("ssi: invalid system word length computed\n");
++ return -EINVAL;
++ }
++
++ SSIREG(SSICR) = ssicr;
++
++ pr_debug("ssi_hw_params() leave\nssicr is now %08lx\n", ssicr);
++ return 0;
++}
++
++static int ssi_set_sysclk(struct snd_soc_cpu_dai *cpu_dai, int clk_id,
++ unsigned int freq, int dir)
++{
++ struct ssi_priv *ssi = &ssi_cpu_data[cpu_dai->id];
++
++ ssi->sysclk = freq;
++
++ return 0;
++}
++
++/*
++ * This divider is used to generate the SSI_SCK (I2S bitclock) from the
++ * clock at the HAC_BIT_CLK ("oversampling clock") pin.
++ */
++static int ssi_set_clkdiv(struct snd_soc_cpu_dai *dai, int did, int div)
++{
++ struct ssi_priv *ssi = &ssi_cpu_data[dai->id];
++ unsigned long ssicr;
++ int i;
++
++ i = 0;
++ ssicr = SSIREG(SSICR) & ~CR_CKDIV_MASK;
++ switch (div) {
++ case 16: ++i;
++ case 8: ++i;
++ case 4: ++i;
++ case 2: ++i;
++ SSIREG(SSICR) = ssicr | (i << CR_CKDIV_SHIFT);
++ case 1: break;
++ default:
++ pr_debug("ssi: invalid sck divider %d\n", div);
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static int ssi_set_fmt(struct snd_soc_cpu_dai *dai, unsigned int fmt)
++{
++ struct ssi_priv *ssi = &ssi_cpu_data[dai->id];
++ unsigned long ssicr = SSIREG(SSICR);
++
++ pr_debug("ssi_set_fmt()\nssicr was 0x%08lx\n", ssicr);
++
++ ssicr &= ~(CR_DEL | CR_PDTA | CR_BREN | CR_SWSP | CR_SCKP |
++ CR_SWS_MASTER | CR_SCK_MASTER);
++
++ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
++ case SND_SOC_DAIFMT_I2S:
++ break;
++ case SND_SOC_DAIFMT_RIGHT_J:
++ ssicr |= CR_DEL | CR_PDTA;
++ break;
++ case SND_SOC_DAIFMT_LEFT_J:
++ ssicr |= CR_DEL;
++ break;
++ default:
++ pr_debug("ssi: unsupported format\n");
++ return -EINVAL;
++ }
++
++ switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
++ case SND_SOC_DAIFMT_CONT:
++ break;
++ case SND_SOC_DAIFMT_GATED:
++ ssicr |= CR_BREN;
++ break;
++ }
++
++ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
++ case SND_SOC_DAIFMT_NB_NF:
++ ssicr |= CR_SCKP; /* sample data at low clkedge */
++ break;
++ case SND_SOC_DAIFMT_NB_IF:
++ ssicr |= CR_SCKP | CR_SWSP;
++ break;
++ case SND_SOC_DAIFMT_IB_NF:
++ break;
++ case SND_SOC_DAIFMT_IB_IF:
++ ssicr |= CR_SWSP; /* word select starts low */
++ break;
++ default:
++ pr_debug("ssi: invalid inversion\n");
++ return -EINVAL;
++ }
++
++ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
++ case SND_SOC_DAIFMT_CBM_CFM:
++ break;
++ case SND_SOC_DAIFMT_CBS_CFM:
++ ssicr |= CR_SCK_MASTER;
++ break;
++ case SND_SOC_DAIFMT_CBM_CFS:
++ ssicr |= CR_SWS_MASTER;
++ break;
++ case SND_SOC_DAIFMT_CBS_CFS:
++ ssicr |= CR_SWS_MASTER | CR_SCK_MASTER;
++ break;
++ default:
++ pr_debug("ssi: invalid master/slave configuration\n");
++ return -EINVAL;
++ }
++
++ SSIREG(SSICR) = ssicr;
++ pr_debug("ssi_set_fmt() leave\nssicr is now 0x%08lx\n", ssicr);
++
++ return 0;
++}
++
++/* the SSI depends on an external clocksource (at HAC_BIT_CLK) even in
++ * Master mode, so really this is board specific; the SSI can do any
++ * rate with the right bitclk and divider settings.
++ */
++#define SSI_RATES \
++ SNDRV_PCM_RATE_8000_192000
++
++/* the SSI can do 8-32 bit samples, with 8 possible channels */
++#define SSI_FMTS \
++ (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | \
++ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | \
++ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE | \
++ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3LE | \
++ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE)
++
++struct snd_soc_cpu_dai sh4_ssi_dai[] = {
++{
++ .name = "SSI0",
++ .id = 0,
++ .type = SND_SOC_DAI_I2S,
++ .playback = {
++ .rates = SSI_RATES,
++ .formats = SSI_FMTS,
++ .channels_min = 2,
++ .channels_max = 8,
++ },
++ .capture = {
++ .rates = SSI_RATES,
++ .formats = SSI_FMTS,
++ .channels_min = 2,
++ .channels_max = 8,
++ },
++ .ops = {
++ .startup = ssi_startup,
++ .shutdown = ssi_shutdown,
++ .trigger = ssi_trigger,
++ .hw_params = ssi_hw_params,
++ },
++ .dai_ops = {
++ .set_sysclk = ssi_set_sysclk,
++ .set_clkdiv = ssi_set_clkdiv,
++ .set_fmt = ssi_set_fmt,
++ },
++},
++#ifdef CONFIG_CPU_SUBTYPE_SH7760
++{
++ .name = "SSI1",
++ .id = 1,
++ .type = SND_SOC_DAI_I2S,
++ .playback = {
++ .rates = SSI_RATES,
++ .formats = SSI_FMTS,
++ .channels_min = 2,
++ .channels_max = 8,
++ },
++ .capture = {
++ .rates = SSI_RATES,
++ .formats = SSI_FMTS,
++ .channels_min = 2,
++ .channels_max = 8,
++ },
++ .ops = {
++ .startup = ssi_startup,
++ .shutdown = ssi_shutdown,
++ .trigger = ssi_trigger,
++ .hw_params = ssi_hw_params,
++ },
++ .dai_ops = {
++ .set_sysclk = ssi_set_sysclk,
++ .set_clkdiv = ssi_set_clkdiv,
++ .set_fmt = ssi_set_fmt,
++ },
++},
++#endif
++};
++EXPORT_SYMBOL_GPL(sh4_ssi_dai);
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("SuperH onchip SSI (I2S) audio driver");
++MODULE_AUTHOR("Manuel Lauss <mano at roarinelk.homelinux.net>");
+--- linux-2.6.22.1.orig/sound/usb/usbaudio.c
++++ linux-2.6.22.1/sound/usb/usbaudio.c
+@@ -2350,7 +2350,9 @@
+ return 1;
+ break;
+ case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
+- return 1;
++ if (device_setup[chip->index] == 0x00 ||
++ fp->altsetting==1 || fp->altsetting==2 || fp->altsetting==3)
++ return 1;
+ }
+ return 0;
+ }
+@@ -2530,7 +2532,18 @@
+ * but we give normal PCM format to get the existing
+ * apps working...
+ */
+- pcm_format = SNDRV_PCM_FORMAT_S16_LE;
++ switch (chip->usb_id) {
++
++ case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
++ if (device_setup[chip->index] == 0x00 &&
++ fp->altsetting == 6)
++ pcm_format = SNDRV_PCM_FORMAT_S16_BE;
++ else
++ pcm_format = SNDRV_PCM_FORMAT_S16_LE;
++ break;
++ default:
++ pcm_format = SNDRV_PCM_FORMAT_S16_LE;
++ }
+ } else {
+ pcm_format = parse_audio_format_i_type(chip, fp, format, fmt);
+ if (pcm_format < 0)
+@@ -3251,6 +3264,11 @@
+ static int audiophile_skip_setting_quirk(struct snd_usb_audio *chip,
+ int iface, int altno)
+ {
++ /* Reset ALL ifaces to 0 altsetting.
++ * Call it for every possible altsetting of every interface.
++ */
++ usb_set_interface(chip->dev, iface, 0);
++
+ if (device_setup[chip->index] & AUDIOPHILE_SET) {
+ if ((device_setup[chip->index] & AUDIOPHILE_SET_DTS)
+ && altno != 6)
+--- linux-2.6.22.1.orig/sound/usb/usbquirks.h
++++ linux-2.6.22.1/sound/usb/usbquirks.h
+@@ -57,6 +57,24 @@
+ USB_DEVICE_ID_MATCH_INT_CLASS |
+ USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+ .idVendor = 0x046d,
++ .idProduct = 0x08ae,
++ .bInterfaceClass = USB_CLASS_AUDIO,
++ .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL
++},
++{
++ .match_flags = USB_DEVICE_ID_MATCH_DEVICE |
++ USB_DEVICE_ID_MATCH_INT_CLASS |
++ USB_DEVICE_ID_MATCH_INT_SUBCLASS,
++ .idVendor = 0x046d,
++ .idProduct = 0x08c6,
++ .bInterfaceClass = USB_CLASS_AUDIO,
++ .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL
++},
++{
++ .match_flags = USB_DEVICE_ID_MATCH_DEVICE |
++ USB_DEVICE_ID_MATCH_INT_CLASS |
++ USB_DEVICE_ID_MATCH_INT_SUBCLASS,
++ .idVendor = 0x046d,
+ .idProduct = 0x08f0,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL
+@@ -1051,7 +1069,15 @@
+ .type = QUIRK_MIDI_STANDARD_INTERFACE
+ }
+ },
+- /* TODO: add Roland EXR support */
++{
++ USB_DEVICE(0x0582, 0x0060),
++ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
++ .vendor_name = "Roland",
++ .product_name = "EXR Series",
++ .ifnum = 0,
++ .type = QUIRK_MIDI_STANDARD_INTERFACE
++ }
++},
+ {
+ /* has ID 0x0067 when not in "Advanced Driver" mode */
+ USB_DEVICE(0x0582, 0x0065),
+@@ -1094,6 +1120,19 @@
+ }
+ }
+ },
++{
++ USB_DEVICE(0x582, 0x00a6),
++ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
++ .vendor_name = "Roland",
++ .product_name = "Juno-G",
++ .ifnum = 0,
++ .type = QUIRK_MIDI_FIXED_ENDPOINT,
++ .data = & (const struct snd_usb_midi_endpoint_info) {
++ .out_cables = 0x0001,
++ .in_cables = 0x0001
++ }
++ }
++},
+ { /*
+ * This quirk is for the "Advanced" modes of the Edirol UA-25.
+ * If the switch is not in an advanced setting, the UA-25 has
+@@ -1230,6 +1269,37 @@
+ }
+ },
+ /* TODO: add Edirol MD-P1 support */
++{
++ /* Roland SH-201 */
++ USB_DEVICE(0x0582, 0x00ad),
++ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
++ .vendor_name = "Roland",
++ .product_name = "SH-201",
++ .ifnum = QUIRK_ANY_INTERFACE,
++ .type = QUIRK_COMPOSITE,
++ .data = (const struct snd_usb_audio_quirk[]) {
++ {
++ .ifnum = 0,
++ .type = QUIRK_AUDIO_STANDARD_INTERFACE
++ },
++ {
++ .ifnum = 1,
++ .type = QUIRK_AUDIO_STANDARD_INTERFACE
++ },
++ {
++ .ifnum = 2,
++ .type = QUIRK_MIDI_FIXED_ENDPOINT,
++ .data = & (const struct snd_usb_midi_endpoint_info) {
++ .out_cables = 0x0001,
++ .in_cables = 0x0001
++ }
++ },
++ {
++ .ifnum = -1
++ }
++ }
++ }
++},
+
+ /* Guillemot devices */
+ {
+--- linux-2.6.22.1.orig/sound/usb/usx2y/usbusx2yaudio.c
++++ linux-2.6.22.1/sound/usb/usx2y/usbusx2yaudio.c
+@@ -935,10 +935,9 @@
+ */
+ static void usX2Y_audio_stream_free(struct snd_usX2Y_substream **usX2Y_substream)
+ {
+- if (NULL != usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK]) {
+- kfree(usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK]);
+- usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK] = NULL;
+- }
++ kfree(usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK]);
++ usX2Y_substream[SNDRV_PCM_STREAM_PLAYBACK] = NULL;
++
+ kfree(usX2Y_substream[SNDRV_PCM_STREAM_CAPTURE]);
+ usX2Y_substream[SNDRV_PCM_STREAM_CAPTURE] = NULL;
+ }
Deleted: branches/src/target/kernel/2.6.22.x/patches/asoc-asm_hardware_h.patch
===================================================================
--- branches/src/target/kernel/2.6.22.x/patches/asoc-asm_hardware_h.patch 2007-07-26 14:40:11 UTC (rev 2397)
+++ branches/src/target/kernel/2.6.22.x/patches/asoc-asm_hardware_h.patch 2007-07-26 15:24:28 UTC (rev 2398)
@@ -1,17 +0,0 @@
-Don't include <asm/arch/hardware.h>, but <asm/hardware.h>
-
-Signed-off-by: Harald Welte <laforge at openmko.org>
-
-Index: linux-2.6.20/sound/soc/s3c24xx/neo1973_wm8753.c
-===================================================================
---- linux-2.6.20.orig/sound/soc/s3c24xx/neo1973_wm8753.c 2007-02-15 16:27:53.000000000 +0100
-+++ linux-2.6.20/sound/soc/s3c24xx/neo1973_wm8753.c 2007-02-15 16:28:02.000000000 +0100
-@@ -33,7 +33,7 @@
- #include <asm/arch/regs-iis.h>
- #include <asm/arch/regs-clock.h>
- #include <asm/arch/regs-gpio.h>
--#include <asm/arch/hardware.h>
-+#include <asm/hardware.h>
- #include <asm/arch/audio.h>
- #include <asm/io.h>
- #include <asm/arch/spi-gpio.h>
Added: branches/src/target/kernel/2.6.22.x/patches/asoc-kconfig-fix.patch
===================================================================
--- branches/src/target/kernel/2.6.22.x/patches/asoc-kconfig-fix.patch 2007-07-26 14:40:11 UTC (rev 2397)
+++ branches/src/target/kernel/2.6.22.x/patches/asoc-kconfig-fix.patch 2007-07-26 15:24:28 UTC (rev 2398)
@@ -0,0 +1,17 @@
+---
+ sound/soc/s3c24xx/Kconfig | 2 1 + 1 - 0 !
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+Index: linux-2.6/sound/soc/s3c24xx/Kconfig
+===================================================================
+--- linux-2.6.orig/sound/soc/s3c24xx/Kconfig 2007-07-23 10:15:13.000000000 +0200
++++ linux-2.6/sound/soc/s3c24xx/Kconfig 2007-07-23 10:18:07.000000000 +0200
+@@ -18,7 +18,7 @@ config SND_S3C2443_SOC_AC97
+
+ config SND_S3C24XX_SOC_NEO1973_WM8753
+ tristate "SoC I2S Audio support for NEO1973 - WM8753"
+- depends on SND_S3C24XX_SOC && MACH_GTA01
++ depends on SND_S3C24XX_SOC && MACH_NEO1973_GTA01
+ select SND_S3C24XX_SOC_I2S
+ select SND_SOC_WM8753
+ help
Deleted: branches/src/target/kernel/2.6.22.x/patches/asoc.patch
===================================================================
--- branches/src/target/kernel/2.6.22.x/patches/asoc.patch 2007-07-26 14:40:11 UTC (rev 2397)
+++ branches/src/target/kernel/2.6.22.x/patches/asoc.patch 2007-07-26 15:24:28 UTC (rev 2398)
@@ -1,24271 +0,0 @@
-Index: linux-2.6.21-moko/sound/soc/codecs/ak4535.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/codecs/ak4535.c
-@@ -0,0 +1,697 @@
-+/*
-+ * ak4535.c -- AK4535 ALSA Soc Audio driver
-+ *
-+ * Copyright 2005 Openedhand Ltd.
-+ *
-+ * Author: Richard Purdie <richard at openedhand.com>
-+ *
-+ * Based on wm8753.c by Liam Girdwood
-+ *
-+ * 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.
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/moduleparam.h>
-+#include <linux/init.h>
-+#include <linux/delay.h>
-+#include <linux/pm.h>
-+#include <linux/i2c.h>
-+#include <linux/platform_device.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/pcm_params.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+#include <sound/initval.h>
-+
-+#include "ak4535.h"
-+
-+#define AUDIO_NAME "ak4535"
-+#define AK4535_VERSION "0.3"
-+
-+struct snd_soc_codec_device soc_codec_dev_ak4535;
-+
-+/* codec private data */
-+struct ak4535_priv {
-+ unsigned int sysclk;
-+};
-+
-+/*
-+ * ak4535 register cache
-+ */
-+static const u16 ak4535_reg[AK4535_CACHEREGNUM] = {
-+ 0x0000, 0x0080, 0x0000, 0x0003,
-+ 0x0002, 0x0000, 0x0011, 0x0001,
-+ 0x0000, 0x0040, 0x0036, 0x0010,
-+ 0x0000, 0x0000, 0x0057, 0x0000,
-+};
-+
-+/*
-+ * read ak4535 register cache
-+ */
-+static inline unsigned int ak4535_read_reg_cache(struct snd_soc_codec *codec,
-+ unsigned int reg)
-+{
-+ u16 *cache = codec->reg_cache;
-+ if (reg >= AK4535_CACHEREGNUM)
-+ return -1;
-+ return cache[reg];
-+}
-+
-+/*
-+ * write ak4535 register cache
-+ */
-+static inline void ak4535_write_reg_cache(struct snd_soc_codec *codec,
-+ u16 reg, unsigned int value)
-+{
-+ u16 *cache = codec->reg_cache;
-+ if (reg >= AK4535_CACHEREGNUM)
-+ return;
-+ cache[reg] = value;
-+}
-+
-+/*
-+ * write to the AK4535 register space
-+ */
-+static int ak4535_write(struct snd_soc_codec *codec, unsigned int reg,
-+ unsigned int value)
-+{
-+ u8 data[2];
-+
-+ /* data is
-+ * D15..D8 AK4535 register offset
-+ * D7...D0 register data
-+ */
-+ data[0] = reg & 0xff;
-+ data[1] = value & 0xff;
-+
-+ ak4535_write_reg_cache (codec, reg, value);
-+ if (codec->hw_write(codec->control_data, data, 2) == 2)
-+ return 0;
-+ else
-+ return -EIO;
-+}
-+
-+static const char *ak4535_mono_gain[] = {"+6dB", "-17dB"};
-+static const char *ak4535_mono_out[] = {"(L + R)/2", "Hi-Z"};
-+static const char *ak4535_hp_out[] = {"Stereo", "Mono"};
-+static const char *ak4535_deemp[] = {"44.1kHz", "Off", "48kHz", "32kHz"};
-+static const char *ak4535_mic_select[] = {"Internal", "External"};
-+
-+static const struct soc_enum ak4535_enum[] = {
-+ SOC_ENUM_SINGLE(AK4535_SIG1, 7, 2, ak4535_mono_gain),
-+ SOC_ENUM_SINGLE(AK4535_SIG1, 6, 2, ak4535_mono_out),
-+ SOC_ENUM_SINGLE(AK4535_MODE2, 2, 2, ak4535_hp_out),
-+ SOC_ENUM_SINGLE(AK4535_DAC, 0, 4, ak4535_deemp),
-+ SOC_ENUM_SINGLE(AK4535_MIC, 1, 2, ak4535_mic_select),
-+};
-+
-+static const struct snd_kcontrol_new ak4535_snd_controls[] = {
-+ SOC_SINGLE("ALC2 Switch", AK4535_SIG1, 1, 1, 0),
-+ SOC_ENUM("Mono 1 Output", ak4535_enum[1]),
-+ SOC_ENUM("Mono 1 Gain", ak4535_enum[0]),
-+ SOC_ENUM("Headphone Output", ak4535_enum[2]),
-+ SOC_ENUM("Playback Deemphasis", ak4535_enum[3]),
-+ SOC_SINGLE("Bass Volume", AK4535_DAC, 2, 3, 0),
-+ SOC_SINGLE("Mic Boost (+20dB) Switch", AK4535_MIC, 0, 1, 0),
-+ SOC_ENUM("Mic Select", ak4535_enum[4]),
-+ SOC_SINGLE("ALC Operation Time", AK4535_TIMER, 0, 3, 0),
-+ SOC_SINGLE("ALC Recovery Time", AK4535_TIMER, 2, 3, 0),
-+ SOC_SINGLE("ALC ZC Time", AK4535_TIMER, 4, 3, 0),
-+ SOC_SINGLE("ALC 1 Switch", AK4535_ALC1, 5, 1, 0),
-+ SOC_SINGLE("ALC 2 Switch", AK4535_ALC1, 6, 1, 0),
-+ SOC_SINGLE("ALC Volume", AK4535_ALC2, 0, 127, 0),
-+ SOC_SINGLE("Capture Volume", AK4535_PGA, 0, 127, 0),
-+ SOC_SINGLE("Left Playback Volume", AK4535_LATT, 0, 127, 1),
-+ SOC_SINGLE("Right Playback Volume", AK4535_RATT, 0, 127, 1),
-+ SOC_SINGLE("AUX Bypass Volume", AK4535_VOL, 0, 15, 0),
-+ SOC_SINGLE("Mic Sidetone Volume", AK4535_VOL, 4, 7, 0),
-+};
-+
-+/* add non dapm controls */
-+static int ak4535_add_controls(struct snd_soc_codec *codec)
-+{
-+ int err, i;
-+
-+ for (i = 0; i < ARRAY_SIZE(ak4535_snd_controls); i++) {
-+ err = snd_ctl_add(codec->card,
-+ snd_soc_cnew(&ak4535_snd_controls[i],codec, NULL));
-+ if (err < 0)
-+ return err;
-+ }
-+
-+ return 0;
-+}
-+
-+/* Mono 1 Mixer */
-+static const struct snd_kcontrol_new ak4535_mono1_mixer_controls[] = {
-+ SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4535_SIG1, 4, 1, 0),
-+ SOC_DAPM_SINGLE("Mono Playback Switch", AK4535_SIG1, 5, 1, 0),
-+};
-+
-+/* Stereo Mixer */
-+static const struct snd_kcontrol_new ak4535_stereo_mixer_controls[] = {
-+ SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4535_SIG2, 4, 1, 0),
-+ SOC_DAPM_SINGLE("Playback Switch", AK4535_SIG2, 7, 1, 0),
-+ SOC_DAPM_SINGLE("Aux Bypass Switch", AK4535_SIG2, 5, 1, 0),
-+};
-+
-+/* Input Mixer */
-+static const struct snd_kcontrol_new ak4535_input_mixer_controls[] = {
-+ SOC_DAPM_SINGLE("Mic Capture Switch", AK4535_MIC, 2, 1, 0),
-+ SOC_DAPM_SINGLE("Aux Capture Switch", AK4535_MIC, 5, 1, 0),
-+};
-+
-+/* Input mux */
-+static const struct snd_kcontrol_new ak4535_input_mux_control =
-+ SOC_DAPM_ENUM("Input Select", ak4535_enum[0]);
-+
-+/* HP L switch */
-+static const struct snd_kcontrol_new ak4535_hpl_control =
-+ SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 1, 1, 1);
-+
-+/* HP R switch */
-+static const struct snd_kcontrol_new ak4535_hpr_control =
-+ SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 0, 1, 1);
-+
-+/* Speaker switch */
-+static const struct snd_kcontrol_new ak4535_spk_control =
-+ SOC_DAPM_SINGLE("Switch", AK4535_MODE2, 0, 0, 0);
-+
-+/* mono 2 switch */
-+static const struct snd_kcontrol_new ak4535_mono2_control =
-+ SOC_DAPM_SINGLE("Switch", AK4535_SIG1, 0, 1, 0);
-+
-+/* Line out switch */
-+static const struct snd_kcontrol_new ak4535_line_control =
-+ SOC_DAPM_SINGLE("Switch", AK4535_SIG2, 6, 1, 0);
-+
-+/* ak4535 dapm widgets */
-+static const struct snd_soc_dapm_widget ak4535_dapm_widgets[] = {
-+ SND_SOC_DAPM_MIXER("Stereo Mixer", SND_SOC_NOPM, 0, 0,
-+ &ak4535_stereo_mixer_controls[0],
-+ ARRAY_SIZE(ak4535_stereo_mixer_controls)),
-+ SND_SOC_DAPM_MIXER("Mono1 Mixer", SND_SOC_NOPM, 0, 0,
-+ &ak4535_mono1_mixer_controls[0],
-+ ARRAY_SIZE(ak4535_mono1_mixer_controls)),
-+ SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0,
-+ &ak4535_input_mixer_controls[0],
-+ ARRAY_SIZE(ak4535_mono1_mixer_controls)),
-+ SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
-+ &ak4535_input_mux_control),
-+ SND_SOC_DAPM_DAC("DAC", "Playback", AK4535_PM2, 0, 0),
-+ SND_SOC_DAPM_SWITCH("Mono 2 Enable", SND_SOC_NOPM, 0, 0,
-+ &ak4535_mono2_control),
-+ SND_SOC_DAPM_SWITCH("Speaker Enable", SND_SOC_NOPM, 0, 0,
-+ &ak4535_spk_control),
-+ SND_SOC_DAPM_SWITCH("Line Out Enable", SND_SOC_NOPM, 0, 0,
-+ &ak4535_line_control),
-+ SND_SOC_DAPM_SWITCH("Left HP Enable", SND_SOC_NOPM, 0, 0,
-+ &ak4535_hpl_control),
-+ SND_SOC_DAPM_SWITCH("Right HP Enable", SND_SOC_NOPM, 0, 0,
-+ &ak4535_hpr_control),
-+ SND_SOC_DAPM_OUTPUT("LOUT"),
-+ SND_SOC_DAPM_OUTPUT("HPL"),
-+ SND_SOC_DAPM_OUTPUT("ROUT"),
-+ SND_SOC_DAPM_OUTPUT("HPR"),
-+ SND_SOC_DAPM_OUTPUT("SPP"),
-+ SND_SOC_DAPM_OUTPUT("SPN"),
-+ SND_SOC_DAPM_OUTPUT("MOUT1"),
-+ SND_SOC_DAPM_OUTPUT("MOUT2"),
-+ SND_SOC_DAPM_OUTPUT("MICOUT"),
-+ SND_SOC_DAPM_ADC("ADC", "Capture", AK4535_PM1, 0, 1),
-+ SND_SOC_DAPM_PGA("Spk Amp", AK4535_PM2, 3, 0, NULL, 0),
-+ SND_SOC_DAPM_PGA("HP R Amp", AK4535_PM2, 1, 0, NULL, 0),
-+ SND_SOC_DAPM_PGA("HP L Amp", AK4535_PM2, 2, 0, NULL, 0),
-+ SND_SOC_DAPM_PGA("Mic", AK4535_PM1, 1, 0, NULL, 0),
-+ SND_SOC_DAPM_PGA("Line Out", AK4535_PM1, 4, 0, NULL, 0),
-+ SND_SOC_DAPM_PGA("Mono Out", AK4535_PM1, 3, 0, NULL, 0),
-+ SND_SOC_DAPM_PGA("AUX In", AK4535_PM1, 2, 0, NULL, 0),
-+
-+ SND_SOC_DAPM_MICBIAS("Mic Int Bias", AK4535_MIC, 3, 0),
-+ SND_SOC_DAPM_MICBIAS("Mic Ext Bias", AK4535_MIC, 4, 0),
-+ SND_SOC_DAPM_INPUT("MICIN"),
-+ SND_SOC_DAPM_INPUT("MICEXT"),
-+ SND_SOC_DAPM_INPUT("AUX"),
-+ SND_SOC_DAPM_INPUT("MIN"),
-+ SND_SOC_DAPM_INPUT("AIN"),
-+};
-+
-+static const char *audio_map[][3] = {
-+ /*stereo mixer */
-+ {"Stereo Mixer", "Playback Switch", "DAC"},
-+ {"Stereo Mixer", "Mic Sidetone Switch", "Mic"},
-+ {"Stereo Mixer", "Aux Bypass Switch", "AUX In"},
-+
-+ /* mono1 mixer */
-+ {"Mono1 Mixer", "Mic Sidetone Switch", "Mic"},
-+ {"Mono1 Mixer", "Mono Playback Switch", "DAC"},
-+
-+ /* mono2 mixer */
-+ {"Mono2 Mixer", "Mono Playback Switch", "Stereo Mixer"},
-+
-+ /* Mic */
-+ {"AIN", NULL, "Mic"},
-+ {"Input Mux", "Internal", "Mic Int Bias"},
-+ {"Input Mux", "External", "Mic Ext Bias"},
-+ {"Mic Int Bias", NULL, "MICIN"},
-+ {"Mic Ext Bias", NULL, "MICEXT"},
-+ {"MICOUT", NULL, "Input Mux"},
-+
-+ /* line out */
-+ {"LOUT", "Switch", "Line"},
-+ {"ROUT", "Switch", "Line Out Enable"},
-+ {"Line Out Enable", NULL, "Line Out"},
-+ {"Line Out", NULL, "Stereo Mixer"},
-+
-+ /* mono1 out */
-+ {"MOUT1", NULL, "Mono Out"},
-+ {"Mono Out", NULL, "Mono Mixer"},
-+
-+ /* left HP */
-+ {"HPL", "Switch", "Left HP Enable"},
-+ {"Left HP Enable", NULL, "HP L Amp"},
-+ {"HP L Amp", NULL, "Stereo Mixer"},
-+
-+ /* right HP */
-+ {"HPR", "Switch", "Right HP Enable"},
-+ {"Right HP Enable", NULL, "HP R Amp"},
-+ {"HP R Amp", NULL, "Stereo Mixer"},
-+
-+ /* speaker */
-+ {"SPP", "Switch", "Speaker Enable"},
-+ {"SPN", "Switch", "Speaker Enable"},
-+ {"Speaker Enable", NULL, "Spk Amp"},
-+ {"Spk Amp", NULL, "MIN"},
-+
-+ /* mono 2 */
-+ {"MOUT2", "Switch", "Mono 2 Enable"},
-+ {"Mono 2 Enable", NULL, "Stereo Mixer"},
-+
-+ /* Aux In */
-+ {"Aux In", NULL, "AUX"},
-+
-+ /* ADC */
-+ {"ADC", NULL, "Input Mixer"},
-+ {"Input Mixer", "Mic Capture Switch", "Mic"},
-+ {"Input Mixer", "Aux Capture Switch", "Aux In"},
-+
-+ /* terminator */
-+ {NULL, NULL, NULL},
-+};
-+
-+static int ak4535_add_widgets(struct snd_soc_codec *codec)
-+{
-+ int i;
-+
-+ for(i = 0; i < ARRAY_SIZE(ak4535_dapm_widgets); i++) {
-+ snd_soc_dapm_new_control(codec, &ak4535_dapm_widgets[i]);
-+ }
-+
-+ /* set up audio path audio_mapnects */
-+ for(i = 0; audio_map[i][0] != NULL; i++) {
-+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
-+ audio_map[i][1], audio_map[i][2]);
-+ }
-+
-+ snd_soc_dapm_new_widgets(codec);
-+ return 0;
-+}
-+
-+static int ak4535_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
-+ int clk_id, unsigned int freq, int dir)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ struct ak4535_priv *ak4535 = codec->private_data;
-+
-+ ak4535->sysclk = freq;
-+ return 0;
-+}
-+
-+static int ak4535_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_device *socdev = rtd->socdev;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ struct ak4535_priv *ak4535 = codec->private_data;
-+ u8 mode2 = ak4535_read_reg_cache(codec, AK4535_MODE2) & ~(0x3 << 5);
-+ int rate = params_rate(params), fs = 256;
-+
-+ if (rate)
-+ fs = ak4535->sysclk / rate;
-+
-+ /* set fs */
-+ switch (fs) {
-+ case 1024:
-+ mode2 |= (0x2 << 5);
-+ break;
-+ case 512:
-+ mode2 |= (0x1 << 5);
-+ break;
-+ case 256:
-+ break;
-+ }
-+
-+ /* set rate */
-+ ak4535_write(codec, AK4535_MODE2, mode2);
-+ return 0;
-+}
-+
-+static int ak4535_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
-+ unsigned int fmt)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u8 mode1 = 0;
-+
-+ /* interface format */
-+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
-+ case SND_SOC_DAIFMT_I2S:
-+ mode1 = 0x0002;
-+ break;
-+ case SND_SOC_DAIFMT_LEFT_J:
-+ mode1 = 0x0001;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ /* use 32 fs for BCLK to save power */
-+ mode1 |= 0x4;
-+
-+ ak4535_write(codec, AK4535_MODE1, mode1);
-+ return 0;
-+}
-+
-+static int ak4535_mute(struct snd_soc_codec_dai *dai, int mute)
-+{
-+ struct snd_soc_codec *codec = dai->codec;
-+ u16 mute_reg = ak4535_read_reg_cache(codec, AK4535_DAC) & 0xffdf;
-+ if (mute)
-+ ak4535_write(codec, AK4535_DAC, mute_reg);
-+ else
-+ ak4535_write(codec, AK4535_DAC, mute_reg | 0x20);
-+ return 0;
-+}
-+
-+static int ak4535_dapm_event(struct snd_soc_codec *codec, int event)
-+{
-+ switch (event) {
-+ case SNDRV_CTL_POWER_D0: /* full On */
-+ /* vref/mid, clk and osc on, dac unmute, active */
-+ case SNDRV_CTL_POWER_D1: /* partial On */
-+ case SNDRV_CTL_POWER_D2: /* partial On */
-+ break;
-+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
-+ /* everything off except vref/vmid, dac mute, inactive */
-+ ak4535_write(codec, AK4535_PM1, 0x80);
-+ ak4535_write(codec, AK4535_PM2, 0x0);
-+ break;
-+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
-+ /* everything off, inactive */
-+ ak4535_write(codec, AK4535_PM1, 0x0);
-+ ak4535_write(codec, AK4535_PM2, 0x80);
-+ break;
-+ }
-+ codec->dapm_state = event;
-+ return 0;
-+}
-+
-+#define AK4535_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
-+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
-+ SNDRV_PCM_RATE_48000)
-+
-+struct snd_soc_codec_dai ak4535_dai = {
-+ .name = "AK4535",
-+ .playback = {
-+ .stream_name = "Playback",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = AK4535_RATES,
-+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
-+ .capture = {
-+ .stream_name = "Capture",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = AK4535_RATES,
-+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
-+ .ops = {
-+ .hw_params = ak4535_hw_params,
-+ },
-+ .dai_ops = {
-+ .set_fmt = ak4535_set_dai_fmt,
-+ .digital_mute = ak4535_mute,
-+ .set_sysclk = ak4535_set_dai_sysclk,
-+ },
-+};
-+EXPORT_SYMBOL_GPL(ak4535_dai);
-+
-+static int ak4535_suspend(struct platform_device *pdev, pm_message_t state)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+
-+ ak4535_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
-+ return 0;
-+}
-+
-+static int ak4535_resume(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+ int i;
-+ u8 data[2];
-+ u16 *cache = codec->reg_cache;
-+
-+ /* Sync reg_cache with the hardware */
-+ for (i = 0; i < ARRAY_SIZE(ak4535_reg); i++) {
-+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
-+ data[1] = cache[i] & 0x00ff;
-+ codec->hw_write(codec->control_data, data, 2);
-+ }
-+ ak4535_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
-+ ak4535_dapm_event(codec, codec->suspend_dapm_state);
-+ return 0;
-+}
-+
-+/*
-+ * initialise the AK4535 driver
-+ * register the mixer and dsp interfaces with the kernel
-+ */
-+static int ak4535_init(struct snd_soc_device *socdev)
-+{
-+ struct snd_soc_codec *codec = socdev->codec;
-+ int ret = 0;
-+
-+ codec->name = "AK4535";
-+ codec->owner = THIS_MODULE;
-+ codec->read = ak4535_read_reg_cache;
-+ codec->write = ak4535_write;
-+ codec->dapm_event = ak4535_dapm_event;
-+ codec->dai = &ak4535_dai;
-+ codec->num_dai = 1;
-+ codec->reg_cache_size = ARRAY_SIZE(ak4535_reg);
-+ codec->reg_cache =
-+ kzalloc(sizeof(u16) * ARRAY_SIZE(ak4535_reg), GFP_KERNEL);
-+ if (codec->reg_cache == NULL)
-+ return -ENOMEM;
-+ memcpy(codec->reg_cache, ak4535_reg,
-+ sizeof(u16) * ARRAY_SIZE(ak4535_reg));
-+ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(ak4535_reg);
-+
-+ /* register pcms */
-+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
-+ if (ret < 0) {
-+ printk(KERN_ERR "ak4535: failed to create pcms\n");
-+ goto pcm_err;
-+ }
-+
-+ /* power on device */
-+ ak4535_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
-+
-+ ak4535_add_controls(codec);
-+ ak4535_add_widgets(codec);
-+ ret = snd_soc_register_card(socdev);
-+ if (ret < 0) {
-+ printk(KERN_ERR "ak4535: failed to register card\n");
-+ goto card_err;
-+ }
-+
-+ return ret;
-+
-+card_err:
-+ snd_soc_free_pcms(socdev);
-+ snd_soc_dapm_free(socdev);
-+pcm_err:
-+ kfree(codec->reg_cache);
-+
-+ return ret;
-+}
-+
-+static struct snd_soc_device *ak4535_socdev;
-+
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+
-+#define I2C_DRIVERID_AK4535 0xfefe /* liam - need a proper id */
-+
-+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
-+
-+/* Magic definition of all other variables and things */
-+I2C_CLIENT_INSMOD;
-+
-+static struct i2c_driver ak4535_i2c_driver;
-+static struct i2c_client client_template;
-+
-+/* If the i2c layer weren't so broken, we could pass this kind of data
-+ around */
-+static int ak4535_codec_probe(struct i2c_adapter *adap, int addr, int kind)
-+{
-+ struct snd_soc_device *socdev = ak4535_socdev;
-+ struct ak4535_setup_data *setup = socdev->codec_data;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ struct i2c_client *i2c;
-+ int ret;
-+
-+ if (addr != setup->i2c_address)
-+ return -ENODEV;
-+
-+ client_template.adapter = adap;
-+ client_template.addr = addr;
-+
-+ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
-+ if (i2c == NULL){
-+ kfree(codec);
-+ return -ENOMEM;
-+ }
-+ memcpy(i2c, &client_template, sizeof(struct i2c_client));
-+ i2c_set_clientdata(i2c, codec);
-+ codec->control_data = i2c;
-+
-+ ret = i2c_attach_client(i2c);
-+ if (ret < 0) {
-+ printk(KERN_ERR "failed to attach codec at addr %x\n", addr);
-+ goto err;
-+ }
-+
-+ ret = ak4535_init(socdev);
-+ if (ret < 0) {
-+ printk(KERN_ERR "failed to initialise AK4535\n");
-+ goto err;
-+ }
-+ return ret;
-+
-+err:
-+ kfree(codec);
-+ kfree(i2c);
-+ return ret;
-+}
-+
-+static int ak4535_i2c_detach(struct i2c_client *client)
-+{
-+ struct snd_soc_codec* codec = i2c_get_clientdata(client);
-+ i2c_detach_client(client);
-+ kfree(codec->reg_cache);
-+ kfree(client);
-+ return 0;
-+}
-+
-+static int ak4535_i2c_attach(struct i2c_adapter *adap)
-+{
-+ return i2c_probe(adap, &addr_data, ak4535_codec_probe);
-+}
-+
-+/* corgi i2c codec control layer */
-+static struct i2c_driver ak4535_i2c_driver = {
-+ .driver = {
-+ .name = "AK4535 I2C Codec",
-+ .owner = THIS_MODULE,
-+ },
-+ .id = I2C_DRIVERID_AK4535,
-+ .attach_adapter = ak4535_i2c_attach,
-+ .detach_client = ak4535_i2c_detach,
-+ .command = NULL,
-+};
-+
-+static struct i2c_client client_template = {
-+ .name = "AK4535",
-+ .driver = &ak4535_i2c_driver,
-+};
-+#endif
-+
-+static int ak4535_probe(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct ak4535_setup_data *setup;
-+ struct snd_soc_codec* codec;
-+ struct ak4535_priv *ak4535;
-+ int ret = 0;
-+
-+ printk(KERN_INFO "AK4535 Audio Codec %s", AK4535_VERSION);
-+
-+ setup = socdev->codec_data;
-+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
-+ if (codec == NULL)
-+ return -ENOMEM;
-+
-+ ak4535 = kzalloc(sizeof(struct ak4535_priv), GFP_KERNEL);
-+ if (ak4535 == NULL) {
-+ kfree(codec);
-+ return -ENOMEM;
-+ }
-+
-+ codec->private_data = ak4535;
-+ socdev->codec = codec;
-+ mutex_init(&codec->mutex);
-+ INIT_LIST_HEAD(&codec->dapm_widgets);
-+ INIT_LIST_HEAD(&codec->dapm_paths);
-+
-+ ak4535_socdev = socdev;
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+ if (setup->i2c_address) {
-+ normal_i2c[0] = setup->i2c_address;
-+ codec->hw_write = (hw_write_t)i2c_master_send;
-+ ret = i2c_add_driver(&ak4535_i2c_driver);
-+ if (ret != 0)
-+ printk(KERN_ERR "can't add i2c driver");
-+ }
-+#else
-+ /* Add other interfaces here */
-+#endif
-+ return ret;
-+}
-+
-+/* power down chip */
-+static int ak4535_remove(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec* codec = socdev->codec;
-+
-+ if (codec->control_data)
-+ ak4535_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
-+
-+ snd_soc_free_pcms(socdev);
-+ snd_soc_dapm_free(socdev);
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+ i2c_del_driver(&ak4535_i2c_driver);
-+#endif
-+ kfree(codec->private_data);
-+ kfree(codec);
-+
-+ return 0;
-+}
-+
-+struct snd_soc_codec_device soc_codec_dev_ak4535 = {
-+ .probe = ak4535_probe,
-+ .remove = ak4535_remove,
-+ .suspend = ak4535_suspend,
-+ .resume = ak4535_resume,
-+};
-+
-+EXPORT_SYMBOL_GPL(soc_codec_dev_ak4535);
-+
-+MODULE_DESCRIPTION("Soc AK4535 driver");
-+MODULE_AUTHOR("Richard Purdie");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/codecs/ak4535.h
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/codecs/ak4535.h
-@@ -0,0 +1,46 @@
-+/*
-+ * ak4535.h -- AK4535 Soc Audio driver
-+ *
-+ * Copyright 2005 Openedhand Ltd.
-+ *
-+ * Author: Richard Purdie <richard at openedhand.com>
-+ *
-+ * Based on wm8753.h
-+ *
-+ * 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 _AK4535_H
-+#define _AK4535_H
-+
-+/* AK4535 register space */
-+
-+#define AK4535_PM1 0x0
-+#define AK4535_PM2 0x1
-+#define AK4535_SIG1 0x2
-+#define AK4535_SIG2 0x3
-+#define AK4535_MODE1 0x4
-+#define AK4535_MODE2 0x5
-+#define AK4535_DAC 0x6
-+#define AK4535_MIC 0x7
-+#define AK4535_TIMER 0x8
-+#define AK4535_ALC1 0x9
-+#define AK4535_ALC2 0xa
-+#define AK4535_PGA 0xb
-+#define AK4535_LATT 0xc
-+#define AK4535_RATT 0xd
-+#define AK4535_VOL 0xe
-+#define AK4535_STATUS 0xf
-+
-+#define AK4535_CACHEREGNUM 0x10
-+
-+struct ak4535_setup_data {
-+ unsigned short i2c_address;
-+};
-+
-+extern struct snd_soc_codec_dai ak4535_dai;
-+extern struct snd_soc_codec_device soc_codec_dev_ak4535;
-+
-+#endif
-Index: linux-2.6.21-moko/sound/soc/codecs/uda1380.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/codecs/uda1380.c
-@@ -0,0 +1,745 @@
-+/*
-+ * uda1380.c - Philips UDA1380 ALSA SoC audio driver
-+ *
-+ * 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.
-+ *
-+ * Modified by Richard Purdie <richard at openedhand.com> to fit into SoC
-+ * codec model.
-+ *
-+ * Copyright (c) 2005 Giorgio Padrin <giorgio at mandarinlogiq.org>
-+ * Copyright 2005 Openedhand Ltd.
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/init.h>
-+#include <linux/types.h>
-+#include <linux/string.h>
-+#include <linux/slab.h>
-+#include <linux/errno.h>
-+#include <linux/ioctl.h>
-+#include <linux/delay.h>
-+#include <linux/i2c.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/control.h>
-+#include <sound/initval.h>
-+#include <sound/info.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+
-+#include "uda1380.h"
-+
-+#define UDA1380_VERSION "0.5"
-+#define AUDIO_NAME "uda1380"
-+/*
-+ * Debug
-+ */
-+
-+#define UDA1380_DEBUG 0
-+
-+#ifdef UDA1380_DEBUG
-+#define dbg(format, arg...) \
-+ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
-+#else
-+#define dbg(format, arg...) do {} while (0)
-+#endif
-+
-+/*
-+ * uda1380 register cache
-+ */
-+static const u16 uda1380_reg[UDA1380_CACHEREGNUM] = {
-+ 0x0502, 0x0000, 0x0000, 0x3f3f,
-+ 0x0202, 0x0000, 0x0000, 0x0000,
-+ 0x0000, 0x0000, 0x0000, 0x0000,
-+ 0x0000, 0x0000, 0x0000, 0x0000,
-+ 0x0000, 0xff00, 0x0000, 0x4800,
-+ 0x0000, 0x0000, 0x0000, 0x0000,
-+ 0x0000, 0x0000, 0x0000, 0x0000,
-+ 0x0000, 0x0000, 0x0000, 0x0000,
-+ 0x0000, 0x8000, 0x0002, 0x0000,
-+};
-+
-+/*
-+ * read uda1380 register cache
-+ */
-+static inline unsigned int uda1380_read_reg_cache(struct snd_soc_codec *codec,
-+ unsigned int reg)
-+{
-+ u16 *cache = codec->reg_cache;
-+ if (reg == UDA1380_RESET)
-+ return 0;
-+ if (reg >= UDA1380_CACHEREGNUM)
-+ return -1;
-+ return cache[reg];
-+}
-+
-+/*
-+ * write uda1380 register cache
-+ */
-+static inline void uda1380_write_reg_cache(struct snd_soc_codec *codec,
-+ u16 reg, unsigned int value)
-+{
-+ u16 *cache = codec->reg_cache;
-+ if (reg >= UDA1380_CACHEREGNUM)
-+ return;
-+ cache[reg] = value;
-+}
-+
-+/*
-+ * write to the UDA1380 register space
-+ */
-+static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg,
-+ unsigned int value)
-+{
-+ u8 data[3];
-+
-+ /* data is
-+ * data[0] is register offset
-+ * data[1] is MS byte
-+ * data[2] is LS byte
-+ */
-+ data[0] = reg;
-+ data[1] = (value & 0xff00) >> 8;
-+ data[2] = value & 0x00ff;
-+
-+ uda1380_write_reg_cache (codec, reg, value);
-+
-+ /* the interpolator & decimator regs must only be written when the
-+ * codec DAI is active.
-+ */
-+ if (!codec->active && (reg >= UDA1380_MVOL))
-+ return 0;
-+ dbg("uda hw write %x val %x\n", reg, value);
-+ if (codec->hw_write(codec->control_data, data, 3) == 3) {
-+ unsigned int val;
-+ i2c_master_send(codec->control_data, data, 1);
-+ i2c_master_recv(codec->control_data, data, 2);
-+ val = (data[0]<<8) | data[1];
-+ if (val != value) {
-+ dbg("READ BACK VAL %x\n", (data[0]<<8) | data[1]);
-+ return -EIO;
-+ }
-+ return 0;
-+ } else
-+ return -EIO;
-+}
-+
-+#define uda1380_reset(c) uda1380_write(c, UDA1380_RESET, 0)
-+
-+/* declarations of ALSA reg_elem_REAL controls */
-+static const char *uda1380_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz",
-+ "96kHz"};
-+static const char *uda1380_input_sel[] = {"Line", "Mic"};
-+static const char *uda1380_output_sel[] = {"Direct", "Mixer"};
-+static const char *uda1380_spf_mode[] = {"Flat", "Minimum1", "Minimum2",
-+ "Maximum"};
-+
-+static const struct soc_enum uda1380_enum[] = {
-+ SOC_ENUM_DOUBLE(UDA1380_DEEMP, 0, 8, 5, uda1380_deemp),
-+ SOC_ENUM_SINGLE(UDA1380_ADC, 3, 2, uda1380_input_sel),
-+ SOC_ENUM_SINGLE(UDA1380_MODE, 14, 4, uda1380_spf_mode),
-+ SOC_ENUM_SINGLE(UDA1380_PM, 7, 2, uda1380_output_sel), /* R02_EN_AVC */
-+};
-+
-+static const struct snd_kcontrol_new uda1380_snd_controls[] = {
-+ SOC_DOUBLE("Playback Volume", UDA1380_MVOL, 0, 8, 255, 1),
-+ SOC_DOUBLE("Mixer Volume", UDA1380_MIXVOL, 0, 8, 255, 1),
-+ SOC_ENUM("Sound Processing Filter Mode", uda1380_enum[2]),
-+ SOC_DOUBLE("Treble Volume", UDA1380_MODE, 4, 12, 3, 0),
-+ SOC_DOUBLE("Bass Volume", UDA1380_MODE, 0, 8, 15, 0),
-+ SOC_ENUM("Playback De-emphasis", uda1380_enum[0]),
-+ SOC_DOUBLE("Capture Volume", UDA1380_DEC, 0, 8, 127, 0),
-+ SOC_DOUBLE("Line Capture Volume", UDA1380_PGA, 0, 8, 15, 0),
-+ SOC_SINGLE("Mic Capture Volume", UDA1380_PGA, 8, 11, 0),
-+ SOC_DOUBLE("Playback Switch", UDA1380_DEEMP, 3, 11, 1, 1),
-+ SOC_SINGLE("Capture Switch", UDA1380_PGA, 15, 1, 0),
-+ SOC_SINGLE("AGC Timing", UDA1380_AGC, 8, 7, 0),
-+ SOC_SINGLE("AGC Target level", UDA1380_AGC, 2, 3, 1),
-+ SOC_SINGLE("AGC Switch", UDA1380_AGC, 0, 1, 0),
-+ SOC_SINGLE("Silence", UDA1380_MIXER, 7, 1, 0),
-+ SOC_SINGLE("Silence Detection", UDA1380_MIXER, 6, 1, 0),
-+};
-+
-+/* add non dapm controls */
-+static int uda1380_add_controls(struct snd_soc_codec *codec)
-+{
-+ int err, i;
-+
-+ for (i = 0; i < ARRAY_SIZE(uda1380_snd_controls); i++) {
-+ err = snd_ctl_add(codec->card,
-+ snd_soc_cnew(&uda1380_snd_controls[i],codec, NULL));
-+ if (err < 0)
-+ return err;
-+ }
-+
-+ return 0;
-+}
-+
-+/* Input mux */
-+static const struct snd_kcontrol_new uda1380_input_mux_control =
-+ SOC_DAPM_ENUM("Input Select", uda1380_enum[1]);
-+
-+/* Output mux */
-+static const struct snd_kcontrol_new uda1380_output_mux_control =
-+ SOC_DAPM_ENUM("Output Select", uda1380_enum[3]);
-+
-+static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = {
-+ SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
-+ &uda1380_input_mux_control),
-+ SND_SOC_DAPM_MUX("Output Mux", SND_SOC_NOPM, 0, 0,
-+ &uda1380_output_mux_control),
-+ SND_SOC_DAPM_PGA("Left PGA", UDA1380_PM, 3, 0, NULL, 0),
-+ SND_SOC_DAPM_PGA("Right PGA", UDA1380_PM, 1, 0, NULL, 0),
-+ SND_SOC_DAPM_PGA("Mic LNA", UDA1380_PM, 4, 0, NULL, 0),
-+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", UDA1380_PM, 2, 0),
-+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", UDA1380_PM, 0, 0),
-+ SND_SOC_DAPM_INPUT("VINM"),
-+ SND_SOC_DAPM_INPUT("VINL"),
-+ SND_SOC_DAPM_INPUT("VINR"),
-+ SND_SOC_DAPM_MIXER("Analog Mixer", UDA1380_PM, 6, 0, NULL, 0),
-+ SND_SOC_DAPM_OUTPUT("VOUTLHP"),
-+ SND_SOC_DAPM_OUTPUT("VOUTRHP"),
-+ SND_SOC_DAPM_OUTPUT("VOUTL"),
-+ SND_SOC_DAPM_OUTPUT("VOUTR"),
-+ SND_SOC_DAPM_DAC("DAC", "Playback", UDA1380_PM, 10, 0),
-+ SND_SOC_DAPM_PGA("HeadPhone Driver", UDA1380_PM, 13, 0, NULL, 0),
-+};
-+
-+static const char *audio_map[][3] = {
-+
-+ /* output mux */
-+ {"HeadPhone Driver", NULL, "Output Mux"},
-+ {"VOUTR", NULL, "Output Mux"},
-+ {"VOUTL", NULL, "Output Mux"},
-+
-+ {"Analog Mixer", NULL, "VINR"},
-+ {"Analog Mixer", NULL, "VINL"},
-+ {"Analog Mixer", NULL, "DAC"},
-+
-+ {"Output Mux", "Direct", "DAC"},
-+ {"Output Mux", "Mixer", "Analog Mixer"},
-+
-+ /* headphone driver */
-+ {"VOUTLHP", NULL, "HeadPhone Driver"},
-+ {"VOUTRHP", NULL, "HeadPhone Driver"},
-+
-+ /* input mux */
-+ {"Left ADC", NULL, "Input Mux"},
-+ {"Input Mux", "Mic", "Mic LNA"},
-+ {"Input Mux", "Line", "Left PGA"},
-+
-+ /* right input */
-+ {"Right ADC", NULL, "Right PGA"},
-+
-+ /* inputs */
-+ {"Mic LNA", NULL, "VINM"},
-+ {"Left PGA", NULL, "VINL"},
-+ {"Right PGA", NULL, "VINR"},
-+
-+ /* terminator */
-+ {NULL, NULL, NULL},
-+};
-+
-+static int uda1380_add_widgets(struct snd_soc_codec *codec)
-+{
-+ int i;
-+
-+ for (i = 0; i < ARRAY_SIZE(uda1380_dapm_widgets); i++)
-+ snd_soc_dapm_new_control(codec, &uda1380_dapm_widgets[i]);
-+
-+ /* set up audio path interconnects */
-+ for (i = 0; audio_map[i][0] != NULL; i++)
-+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
-+ audio_map[i][1], audio_map[i][2]);
-+
-+ snd_soc_dapm_new_widgets(codec);
-+ return 0;
-+}
-+
-+static int uda1380_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
-+ unsigned int fmt)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ int iface;
-+ /* set up DAI based upon fmt */
-+
-+ iface = uda1380_read_reg_cache (codec, UDA1380_IFACE);
-+ iface &= ~(R01_SFORI_MASK | R01_SIM | R01_SFORO_MASK);
-+
-+ /* FIXME: how to select I2S for DATAO and MSB for DATAI correctly? */
-+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
-+ case SND_SOC_DAIFMT_I2S:
-+ iface |= R01_SFORI_I2S | R01_SFORO_I2S;
-+ break;
-+ case SND_SOC_DAIFMT_LSB:
-+ iface |= R01_SFORI_LSB16 | R01_SFORO_I2S;
-+ break;
-+ case SND_SOC_DAIFMT_MSB:
-+ iface |= R01_SFORI_MSB | R01_SFORO_I2S;
-+ }
-+
-+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBM_CFM)
-+ iface |= R01_SIM;
-+
-+ uda1380_write(codec, UDA1380_IFACE, iface);
-+
-+ return 0;
-+}
-+
-+/*
-+ * Flush reg cache
-+ * We can only write the interpolator and decimator registers
-+ * when the DAI is being clocked by the CPU DAI. It's up to the
-+ * machine and cpu DAI driver to do this before we are called.
-+ */
-+static int uda1380_pcm_prepare(struct snd_pcm_substream *substream)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_device *socdev = rtd->socdev;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ int reg, reg_start, reg_end, clk;
-+
-+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-+ reg_start = UDA1380_MVOL;
-+ reg_end = UDA1380_MIXER;
-+ } else {
-+ reg_start = UDA1380_DEC;
-+ reg_end = UDA1380_AGC;
-+ }
-+
-+ /* FIXME disable DAC_CLK */
-+ clk = uda1380_read_reg_cache (codec, 00);
-+ uda1380_write(codec, UDA1380_CLK, clk & ~R00_DAC_CLK);
-+
-+ for (reg = reg_start; reg <= reg_end; reg++ ) {
-+ dbg("reg %x val %x\n",reg, uda1380_read_reg_cache (codec, reg));
-+ uda1380_write(codec, reg, uda1380_read_reg_cache (codec, reg));
-+ }
-+
-+ /* FIXME enable DAC_CLK */
-+ uda1380_write(codec, UDA1380_CLK, clk | R00_DAC_CLK);
-+
-+ return 0;
-+}
-+
-+static int uda1380_pcm_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_device *socdev = rtd->socdev;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
-+
-+ /* set WSPLL power and divider if running from this clock */
-+ if (clk & R00_DAC_CLK) {
-+ int rate = params_rate(params);
-+ u16 pm = uda1380_read_reg_cache(codec, UDA1380_PM);
-+ clk &= ~0x3; /* clear SEL_LOOP_DIV */
-+ switch (rate) {
-+ case 6250 ... 12500:
-+ clk |= 0x0;
-+ break;
-+ case 12501 ... 25000:
-+ clk |= 0x1;
-+ break;
-+ case 25001 ... 50000:
-+ clk |= 0x2;
-+ break;
-+ case 50001 ... 100000:
-+ clk |= 0x3;
-+ break;
-+ }
-+ uda1380_write(codec, UDA1380_PM, R02_PON_PLL | pm);
-+ }
-+
-+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-+ clk |= R00_EN_DAC | R00_EN_INT;
-+ else
-+ clk |= R00_EN_ADC | R00_EN_DEC;
-+
-+ uda1380_write(codec, UDA1380_CLK, clk);
-+ return 0;
-+}
-+
-+static void uda1380_pcm_shutdown(struct snd_pcm_substream *substream)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_device *socdev = rtd->socdev;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
-+
-+ /* shut down WSPLL power if running from this clock */
-+ if (clk & R00_DAC_CLK) {
-+ u16 pm = uda1380_read_reg_cache(codec, UDA1380_PM);
-+ uda1380_write(codec, UDA1380_PM, ~R02_PON_PLL & pm);
-+ }
-+
-+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-+ clk &= ~(R00_EN_DAC | R00_EN_INT);
-+ else
-+ clk &= ~(R00_EN_ADC | R00_EN_DEC);
-+
-+ uda1380_write(codec, UDA1380_CLK, clk);
-+}
-+
-+static int uda1380_mute(struct snd_soc_codec_dai *codec_dai, int mute)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 mute_reg = uda1380_read_reg_cache(codec, UDA1380_DEEMP) & 0xbfff;
-+
-+ /* FIXME: mute(codec,0) is called when the magician clock is already
-+ * set to WSPLL, but for some unknown reason writing to interpolator
-+ * registers works only when clocked by SYSCLK */
-+ u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
-+ uda1380_write(codec, UDA1380_CLK, ~R00_DAC_CLK & clk);
-+ if (mute)
-+ uda1380_write(codec, UDA1380_DEEMP, mute_reg | 0x4000);
-+ else
-+ uda1380_write(codec, UDA1380_DEEMP, mute_reg);
-+ uda1380_write(codec, UDA1380_CLK, clk);
-+ return 0;
-+}
-+
-+static int uda1380_dapm_event(struct snd_soc_codec *codec, int event)
-+{
-+ int pm = uda1380_read_reg_cache(codec, UDA1380_PM);
-+
-+ switch (event) {
-+ case SNDRV_CTL_POWER_D0: /* full On */
-+ case SNDRV_CTL_POWER_D1: /* partial On */
-+ case SNDRV_CTL_POWER_D2: /* partial On */
-+ /* enable internal bias */
-+ uda1380_write(codec, UDA1380_PM, R02_PON_BIAS | pm);
-+ break;
-+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
-+ /* everything off except internal bias */
-+ uda1380_write(codec, UDA1380_PM, R02_PON_BIAS);
-+ break;
-+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
-+ /* everything off, inactive */
-+ uda1380_write(codec, UDA1380_PM, 0x0);
-+ break;
-+ }
-+ codec->dapm_state = event;
-+ return 0;
-+}
-+
-+#define UDA1380_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
-+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
-+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
-+
-+struct snd_soc_codec_dai uda1380_dai[] = {
-+{
-+ .name = "UDA1380",
-+ .playback = {
-+ .stream_name = "Playback",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = UDA1380_RATES,
-+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
-+ .capture = {
-+ .stream_name = "Capture",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = UDA1380_RATES,
-+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
-+ .ops = {
-+ .hw_params = uda1380_pcm_hw_params,
-+ .shutdown = uda1380_pcm_shutdown,
-+ .prepare = uda1380_pcm_prepare,
-+ },
-+ .dai_ops = {
-+ .digital_mute = uda1380_mute,
-+ .set_fmt = uda1380_set_dai_fmt,
-+ },
-+},
-+{/* playback only - dual interface */
-+ .name = "UDA1380",
-+ .playback = {
-+ .stream_name = "Playback",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = UDA1380_RATES,
-+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
-+ },
-+ .ops = {
-+ .hw_params = uda1380_pcm_hw_params,
-+ .shutdown = uda1380_pcm_shutdown,
-+ .prepare = uda1380_pcm_prepare,
-+ },
-+ .dai_ops = {
-+ .digital_mute = uda1380_mute,
-+ .set_fmt = uda1380_set_dai_fmt,
-+ },
-+},
-+{ /* capture only - dual interface*/
-+ .name = "UDA1380",
-+ .capture = {
-+ .stream_name = "Capture",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = UDA1380_RATES,
-+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
-+ },
-+ .ops = {
-+ .hw_params = uda1380_pcm_hw_params,
-+ .shutdown = uda1380_pcm_shutdown,
-+ .prepare = uda1380_pcm_prepare,
-+ },
-+ .dai_ops = {
-+ .set_fmt = uda1380_set_dai_fmt,
-+ },
-+},
-+};
-+EXPORT_SYMBOL_GPL(uda1380_dai);
-+
-+static int uda1380_suspend(struct platform_device *pdev, pm_message_t state)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+
-+ uda1380_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
-+ return 0;
-+}
-+
-+static int uda1380_resume(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+ int i;
-+ u8 data[2];
-+ u16 *cache = codec->reg_cache;
-+
-+ /* Sync reg_cache with the hardware */
-+ for (i = 0; i < ARRAY_SIZE(uda1380_reg); i++) {
-+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
-+ data[1] = cache[i] & 0x00ff;
-+ codec->hw_write(codec->control_data, data, 2);
-+ }
-+ uda1380_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
-+ uda1380_dapm_event(codec, codec->suspend_dapm_state);
-+ return 0;
-+}
-+
-+/*
-+ * initialise the UDA1380 driver
-+ * register the mixer and dsp interfaces with the kernel
-+ */
-+static int uda1380_init(struct snd_soc_device *socdev, int dac_clk)
-+{
-+ struct snd_soc_codec *codec = socdev->codec;
-+ int ret = 0;
-+
-+ codec->name = "UDA1380";
-+ codec->owner = THIS_MODULE;
-+ codec->read = uda1380_read_reg_cache;
-+ codec->write = uda1380_write;
-+ codec->dapm_event = uda1380_dapm_event;
-+ codec->dai = uda1380_dai;
-+ codec->num_dai = ARRAY_SIZE(uda1380_dai);
-+ codec->reg_cache_size = ARRAY_SIZE(uda1380_reg);
-+ codec->reg_cache =
-+ kcalloc(ARRAY_SIZE(uda1380_reg), sizeof(u16), GFP_KERNEL);
-+ if (codec->reg_cache == NULL)
-+ return -ENOMEM;
-+ memcpy(codec->reg_cache, uda1380_reg,
-+ sizeof(u16) * ARRAY_SIZE(uda1380_reg));
-+ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(uda1380_reg);
-+ uda1380_reset(codec);
-+
-+ /* register pcms */
-+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
-+ if(ret < 0) {
-+ printk(KERN_ERR "uda1380: failed to create pcms\n");
-+ goto pcm_err;
-+ }
-+
-+ /* power on device */
-+ uda1380_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
-+ /* set clock input */
-+ switch (dac_clk) {
-+ case UDA1380_DAC_CLK_SYSCLK:
-+ uda1380_write(codec, UDA1380_CLK, 0);
-+ break;
-+ case UDA1380_DAC_CLK_WSPLL:
-+ uda1380_write(codec, UDA1380_CLK, R00_DAC_CLK);
-+ break;
-+ }
-+
-+ /* uda1380 init */
-+ uda1380_add_controls(codec);
-+ uda1380_add_widgets(codec);
-+ ret = snd_soc_register_card(socdev);
-+ if (ret < 0) {
-+ printk(KERN_ERR "uda1380: failed to register card\n");
-+ goto card_err;
-+ }
-+
-+ return ret;
-+
-+card_err:
-+ snd_soc_free_pcms(socdev);
-+ snd_soc_dapm_free(socdev);
-+pcm_err:
-+ kfree(codec->reg_cache);
-+ return ret;
-+}
-+
-+static struct snd_soc_device *uda1380_socdev;
-+
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+
-+#define I2C_DRIVERID_UDA1380 0xfefe /* liam - need a proper id */
-+
-+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
-+
-+/* Magic definition of all other variables and things */
-+I2C_CLIENT_INSMOD;
-+
-+static struct i2c_driver uda1380_i2c_driver;
-+static struct i2c_client client_template;
-+
-+/* If the i2c layer weren't so broken, we could pass this kind of data
-+ around */
-+
-+static int uda1380_codec_probe(struct i2c_adapter *adap, int addr, int kind)
-+{
-+ struct snd_soc_device *socdev = uda1380_socdev;
-+ struct uda1380_setup_data *setup = socdev->codec_data;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ struct i2c_client *i2c;
-+ int ret;
-+
-+ if (addr != setup->i2c_address)
-+ return -ENODEV;
-+
-+ client_template.adapter = adap;
-+ client_template.addr = addr;
-+
-+ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
-+ if (i2c == NULL){
-+ kfree(codec);
-+ return -ENOMEM;
-+ }
-+ memcpy(i2c, &client_template, sizeof(struct i2c_client));
-+ i2c_set_clientdata(i2c, codec);
-+ codec->control_data = i2c;
-+
-+ ret = i2c_attach_client(i2c);
-+ if (ret < 0) {
-+ printk(KERN_ERR "failed to attach codec at addr %x\n", addr);
-+ goto err;
-+ }
-+
-+ ret = uda1380_init(socdev, setup->dac_clk);
-+ if (ret < 0) {
-+ printk(KERN_ERR "failed to initialise UDA1380\n");
-+ goto err;
-+ }
-+ return ret;
-+
-+err:
-+ kfree(codec);
-+ kfree(i2c);
-+ return ret;
-+}
-+
-+static int uda1380_i2c_detach(struct i2c_client *client)
-+{
-+ struct snd_soc_codec* codec = i2c_get_clientdata(client);
-+ i2c_detach_client(client);
-+ kfree(codec->reg_cache);
-+ kfree(client);
-+ return 0;
-+}
-+
-+static int uda1380_i2c_attach(struct i2c_adapter *adap)
-+{
-+ return i2c_probe(adap, &addr_data, uda1380_codec_probe);
-+}
-+
-+/* corgi i2c codec control layer */
-+static struct i2c_driver uda1380_i2c_driver = {
-+ .driver = {
-+ .name = "UDA1380 I2C Codec",
-+ .owner = THIS_MODULE,
-+ },
-+ .id = I2C_DRIVERID_UDA1380,
-+ .attach_adapter = uda1380_i2c_attach,
-+ .detach_client = uda1380_i2c_detach,
-+ .command = NULL,
-+};
-+
-+static struct i2c_client client_template = {
-+ .name = "UDA1380",
-+ .driver = &uda1380_i2c_driver,
-+};
-+#endif
-+
-+static int uda1380_probe(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct uda1380_setup_data *setup;
-+ struct snd_soc_codec* codec;
-+ int ret = 0;
-+
-+ printk(KERN_INFO "UDA1380 Audio Codec %s", UDA1380_VERSION);
-+
-+ setup = socdev->codec_data;
-+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
-+ if (codec == NULL)
-+ return -ENOMEM;
-+
-+ socdev->codec = codec;
-+ mutex_init(&codec->mutex);
-+ INIT_LIST_HEAD(&codec->dapm_widgets);
-+ INIT_LIST_HEAD(&codec->dapm_paths);
-+
-+ uda1380_socdev = socdev;
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+ if (setup->i2c_address) {
-+ normal_i2c[0] = setup->i2c_address;
-+ codec->hw_write = (hw_write_t)i2c_master_send;
-+ ret = i2c_add_driver(&uda1380_i2c_driver);
-+ if (ret != 0)
-+ printk(KERN_ERR "can't add i2c driver");
-+ }
-+#else
-+ /* Add other interfaces here */
-+#endif
-+ return ret;
-+}
-+
-+/* power down chip */
-+static int uda1380_remove(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec* codec = socdev->codec;
-+
-+ if (codec->control_data)
-+ uda1380_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
-+
-+ snd_soc_free_pcms(socdev);
-+ snd_soc_dapm_free(socdev);
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+ i2c_del_driver(&uda1380_i2c_driver);
-+#endif
-+ kfree(codec);
-+
-+ return 0;
-+}
-+
-+struct snd_soc_codec_device soc_codec_dev_uda1380 = {
-+ .probe = uda1380_probe,
-+ .remove = uda1380_remove,
-+ .suspend = uda1380_suspend,
-+ .resume = uda1380_resume,
-+};
-+
-+EXPORT_SYMBOL_GPL(soc_codec_dev_uda1380);
-+
-+MODULE_AUTHOR("Giorgio Padrin");
-+MODULE_DESCRIPTION("Audio support for codec Philips UDA1380");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/codecs/uda1380.h
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/codecs/uda1380.h
-@@ -0,0 +1,89 @@
-+/*
-+ * Audio support for Philips UDA1380
-+ *
-+ * 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.
-+ *
-+ * Copyright (c) 2005 Giorgio Padrin <giorgio at mandarinlogiq.org>
-+ */
-+
-+#ifndef _UDA1380_H
-+#define _UDA1380_H
-+
-+#define UDA1380_CLK 0x00
-+#define UDA1380_IFACE 0x01
-+#define UDA1380_PM 0x02
-+#define UDA1380_AMIX 0x03
-+#define UDA1380_HP 0x04
-+#define UDA1380_MVOL 0x10
-+#define UDA1380_MIXVOL 0x11
-+#define UDA1380_MODE 0x12
-+#define UDA1380_DEEMP 0x13
-+#define UDA1380_MIXER 0x14
-+#define UDA1380_INTSTAT 0x18
-+#define UDA1380_DEC 0x20
-+#define UDA1380_PGA 0x21
-+#define UDA1380_ADC 0x22
-+#define UDA1380_AGC 0x23
-+#define UDA1380_DECSTAT 0x28
-+#define UDA1380_RESET 0x7f
-+
-+#define UDA1380_CACHEREGNUM 0x24
-+
-+/* Register flags */
-+#define R00_EN_ADC 0x0800
-+#define R00_EN_DEC 0x0400
-+#define R00_EN_DAC 0x0200
-+#define R00_EN_INT 0x0100
-+#define R00_DAC_CLK 0x0010
-+#define R01_SFORI_I2S 0x0000
-+#define R01_SFORI_LSB16 0x0100
-+#define R01_SFORI_LSB18 0x0200
-+#define R01_SFORI_LSB20 0x0300
-+#define R01_SFORI_MSB 0x0500
-+#define R01_SFORI_MASK 0x0700
-+#define R01_SFORO_I2S 0x0000
-+#define R01_SFORO_LSB16 0x0001
-+#define R01_SFORO_LSB18 0x0002
-+#define R01_SFORO_LSB20 0x0003
-+#define R01_SFORO_LSB24 0x0004
-+#define R01_SFORO_MSB 0x0005
-+#define R01_SFORO_MASK 0x0007
-+#define R01_SEL_SOURCE 0x0040
-+#define R01_SIM 0x0010
-+#define R02_PON_PLL 0x8000
-+#define R02_PON_HP 0x2000
-+#define R02_PON_DAC 0x0400
-+#define R02_PON_BIAS 0x0100
-+#define R02_EN_AVC 0x0080
-+#define R02_PON_AVC 0x0040
-+#define R02_PON_LNA 0x0010
-+#define R02_PON_PGAL 0x0008
-+#define R02_PON_ADCL 0x0004
-+#define R02_PON_PGAR 0x0002
-+#define R02_PON_ADCR 0x0001
-+#define R13_MTM 0x4000
-+#define R14_SILENCE 0x0080
-+#define R14_SDET_ON 0x0040
-+#define R21_MT_ADC 0x8000
-+#define R22_SEL_LNA 0x0008
-+#define R22_SEL_MIC 0x0004
-+#define R22_SKIP_DCFIL 0x0002
-+#define R23_AGC_EN 0x0001
-+
-+struct uda1380_setup_data {
-+ unsigned short i2c_address;
-+ int dac_clk;
-+#define UDA1380_DAC_CLK_SYSCLK 0
-+#define UDA1380_DAC_CLK_WSPLL 1
-+};
-+
-+#define UDA1380_DAI_DUPLEX 0 /* playback and capture on single DAI */
-+#define UDA1380_DAI_PLAYBACK 1 /* playback DAI */
-+#define UDA1380_DAI_CAPTURE 2 /* capture DAI */
-+
-+extern struct snd_soc_codec_dai uda1380_dai[3];
-+extern struct snd_soc_codec_device soc_codec_dev_uda1380;
-+
-+#endif /* _UDA1380_H */
-Index: linux-2.6.21-moko/sound/soc/codecs/wm8753.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/codecs/wm8753.c
-@@ -0,0 +1,1782 @@
-+/*
-+ * wm8753.c -- WM8753 ALSA Soc Audio driver
-+ *
-+ * Copyright 2003 Wolfson Microelectronics PLC.
-+ * Author: Liam Girdwood
-+ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or (at your
-+ * option) any later version.
-+ *
-+ * Notes:
-+ * The WM8753 is a low power, high quality stereo codec with integrated PCM
-+ * codec designed for portable digital telephony applications.
-+ *
-+ * Dual DAI:-
-+ *
-+ * This driver support 2 DAI PCM's. This makes the default PCM available for
-+ * HiFi audio (e.g. MP3, ogg) playback/capture and the other PCM available for
-+ * voice.
-+ *
-+ * Please note that the voice PCM can be connected directly to a Bluetooth
-+ * codec or GSM modem and thus cannot be read or written to, although it is
-+ * available to be configured with snd_hw_params(), etc and kcontrols in the
-+ * normal alsa manner.
-+ *
-+ * Fast DAI switching:-
-+ *
-+ * The driver can now fast switch between the DAI configurations via a
-+ * an alsa kcontrol. This allows the PCM to remain open.
-+ *
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/moduleparam.h>
-+#include <linux/version.h>
-+#include <linux/kernel.h>
-+#include <linux/init.h>
-+#include <linux/delay.h>
-+#include <linux/pm.h>
-+#include <linux/i2c.h>
-+#include <linux/platform_device.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/pcm_params.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+#include <sound/initval.h>
-+#include <asm/div64.h>
-+
-+#include "wm8753.h"
-+
-+#define AUDIO_NAME "wm8753"
-+#define WM8753_VERSION "0.16"
-+
-+/*
-+ * Debug
-+ */
-+
-+#define WM8753_DEBUG 0
-+
-+#ifdef WM8753_DEBUG
-+#define dbg(format, arg...) \
-+ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
-+#else
-+#define dbg(format, arg...) do {} while (0)
-+#endif
-+#define err(format, arg...) \
-+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
-+#define info(format, arg...) \
-+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
-+#define warn(format, arg...) \
-+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
-+
-+static int caps_charge = 2000;
-+module_param(caps_charge, int, 0);
-+MODULE_PARM_DESC(caps_charge, "WM8753 cap charge time (msecs)");
-+
-+static void wm8753_set_dai_mode(struct snd_soc_codec *codec,
-+ unsigned int mode);
-+
-+/* codec private data */
-+struct wm8753_priv {
-+ unsigned int sysclk;
-+ unsigned int pcmclk;
-+};
-+
-+/*
-+ * wm8753 register cache
-+ * We can't read the WM8753 register space when we
-+ * are using 2 wire for device control, so we cache them instead.
-+ */
-+static const u16 wm8753_reg[] = {
-+ 0x0008, 0x0000, 0x000a, 0x000a,
-+ 0x0033, 0x0000, 0x0007, 0x00ff,
-+ 0x00ff, 0x000f, 0x000f, 0x007b,
-+ 0x0000, 0x0032, 0x0000, 0x00c3,
-+ 0x00c3, 0x00c0, 0x0000, 0x0000,
-+ 0x0000, 0x0000, 0x0000, 0x0000,
-+ 0x0000, 0x0000, 0x0000, 0x0000,
-+ 0x0000, 0x0000, 0x0000, 0x0055,
-+ 0x0005, 0x0050, 0x0055, 0x0050,
-+ 0x0055, 0x0050, 0x0055, 0x0079,
-+ 0x0079, 0x0079, 0x0079, 0x0079,
-+ 0x0000, 0x0000, 0x0000, 0x0000,
-+ 0x0097, 0x0097, 0x0000, 0x0004,
-+ 0x0000, 0x0083, 0x0024, 0x01ba,
-+ 0x0000, 0x0083, 0x0024, 0x01ba,
-+ 0x0000, 0x0000
-+};
-+
-+/*
-+ * read wm8753 register cache
-+ */
-+static inline unsigned int wm8753_read_reg_cache(struct snd_soc_codec *codec,
-+ unsigned int reg)
-+{
-+ u16 *cache = codec->reg_cache;
-+ if (reg < 1 || reg > (ARRAY_SIZE(wm8753_reg) + 1))
-+ return -1;
-+ return cache[reg - 1];
-+}
-+
-+/*
-+ * write wm8753 register cache
-+ */
-+static inline void wm8753_write_reg_cache(struct snd_soc_codec *codec,
-+ unsigned int reg, unsigned int value)
-+{
-+ u16 *cache = codec->reg_cache;
-+ if (reg < 1 || reg > 0x3f)
-+ return;
-+ cache[reg - 1] = value;
-+}
-+
-+/*
-+ * write to the WM8753 register space
-+ */
-+static int wm8753_write(struct snd_soc_codec *codec, unsigned int reg,
-+ unsigned int value)
-+{
-+ u8 data[2];
-+
-+ /* data is
-+ * D15..D9 WM8753 register offset
-+ * D8...D0 register data
-+ */
-+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
-+ data[1] = value & 0x00ff;
-+
-+ wm8753_write_reg_cache (codec, reg, value);
-+ if (codec->hw_write(codec->control_data, data, 2) == 2)
-+ return 0;
-+ else
-+ return -EIO;
-+}
-+
-+#define wm8753_reset(c) wm8753_write(c, WM8753_RESET, 0)
-+
-+/*
-+ * WM8753 Controls
-+ */
-+static const char *wm8753_base[] = {"Linear Control", "Adaptive Boost"};
-+static const char *wm8753_base_filter[] =
-+ {"130Hz @ 48kHz", "200Hz @ 48kHz", "100Hz @ 16kHz", "400Hz @ 48kHz",
-+ "100Hz @ 8kHz", "200Hz @ 8kHz"};
-+static const char *wm8753_treble[] = {"8kHz", "4kHz"};
-+static const char *wm8753_alc_func[] = {"Off", "Right", "Left", "Stereo"};
-+static const char *wm8753_ng_type[] = {"Constant PGA Gain", "Mute ADC Output"};
-+static const char *wm8753_3d_func[] = {"Capture", "Playback"};
-+static const char *wm8753_3d_uc[] = {"2.2kHz", "1.5kHz"};
-+static const char *wm8753_3d_lc[] = {"200Hz", "500Hz"};
-+static const char *wm8753_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz"};
-+static const char *wm8753_mono_mix[] = {"Stereo", "Left", "Right", "Mono"};
-+static const char *wm8753_dac_phase[] = {"Non Inverted", "Inverted"};
-+static const char *wm8753_line_mix[] = {"Line 1 + 2", "Line 1 - 2",
-+ "Line 1", "Line 2"};
-+static const char *wm8753_mono_mux[] = {"Line Mix", "Rx Mix"};
-+static const char *wm8753_right_mux[] = {"Line 2", "Rx Mix"};
-+static const char *wm8753_left_mux[] = {"Line 1", "Rx Mix"};
-+static const char *wm8753_rxmsel[] = {"RXP - RXN", "RXP + RXN", "RXP", "RXN"};
-+static const char *wm8753_sidetone_mux[] = {"Left PGA", "Mic 1", "Mic 2",
-+ "Right PGA"};
-+static const char *wm8753_mono2_src[] = {"Inverted Mono 1", "Left", "Right",
-+ "Left + Right"};
-+static const char *wm8753_out3[] = {"VREF", "ROUT2", "Left + Right"};
-+static const char *wm8753_out4[] = {"VREF", "Capture ST", "LOUT2"};
-+static const char *wm8753_radcsel[] = {"PGA", "Line or RXP-RXN", "Sidetone"};
-+static const char *wm8753_ladcsel[] = {"PGA", "Line or RXP-RXN", "Line"};
-+static const char *wm8753_mono_adc[] = {"Stereo", "Analogue Mix Left",
-+ "Analogue Mix Right", "Digital Mono Mix"};
-+static const char *wm8753_adc_hp[] = {"3.4Hz @ 48kHz", "82Hz @ 16k",
-+ "82Hz @ 8kHz", "170Hz @ 8kHz"};
-+static const char *wm8753_adc_filter[] = {"HiFi", "Voice"};
-+static const char *wm8753_mic_sel[] = {"Mic 1", "Mic 2", "Mic 3"};
-+static const char *wm8753_dai_mode[] = {"DAI 0", "DAI 1", "DAI 2", "DAI 3"};
-+static const char *wm8753_dat_sel[] = {"Stereo", "Left ADC", "Right ADC",
-+ "Channel Swap"};
-+
-+static const struct soc_enum wm8753_enum[] = {
-+SOC_ENUM_SINGLE(WM8753_BASS, 7, 2, wm8753_base), // 0
-+SOC_ENUM_SINGLE(WM8753_BASS, 4, 6, wm8753_base_filter), // 1
-+SOC_ENUM_SINGLE(WM8753_TREBLE, 6, 2, wm8753_treble), // 2
-+SOC_ENUM_SINGLE(WM8753_ALC1, 7, 4, wm8753_alc_func), // 3
-+SOC_ENUM_SINGLE(WM8753_NGATE, 1, 2, wm8753_ng_type), // 4
-+SOC_ENUM_SINGLE(WM8753_3D, 7, 2, wm8753_3d_func), // 5
-+SOC_ENUM_SINGLE(WM8753_3D, 6, 2, wm8753_3d_uc), // 6
-+SOC_ENUM_SINGLE(WM8753_3D, 5, 2, wm8753_3d_lc), // 7
-+SOC_ENUM_SINGLE(WM8753_DAC, 1, 4, wm8753_deemp), // 8
-+SOC_ENUM_SINGLE(WM8753_DAC, 4, 4, wm8753_mono_mix), // 9
-+SOC_ENUM_SINGLE(WM8753_DAC, 6, 2, wm8753_dac_phase), // 10
-+SOC_ENUM_SINGLE(WM8753_INCTL1, 3, 4, wm8753_line_mix), // 11
-+SOC_ENUM_SINGLE(WM8753_INCTL1, 2, 2, wm8753_mono_mux), // 12
-+SOC_ENUM_SINGLE(WM8753_INCTL1, 1, 2, wm8753_right_mux), // 13
-+SOC_ENUM_SINGLE(WM8753_INCTL1, 0, 2, wm8753_left_mux), // 14
-+SOC_ENUM_SINGLE(WM8753_INCTL2, 6, 4, wm8753_rxmsel), // 15
-+SOC_ENUM_SINGLE(WM8753_INCTL2, 4, 4, wm8753_sidetone_mux),// 16
-+SOC_ENUM_SINGLE(WM8753_OUTCTL, 7, 4, wm8753_mono2_src), // 17
-+SOC_ENUM_SINGLE(WM8753_OUTCTL, 0, 3, wm8753_out3), // 18
-+SOC_ENUM_SINGLE(WM8753_ADCTL2, 7, 3, wm8753_out4), // 19
-+SOC_ENUM_SINGLE(WM8753_ADCIN, 2, 3, wm8753_radcsel), // 20
-+SOC_ENUM_SINGLE(WM8753_ADCIN, 0, 3, wm8753_ladcsel), // 21
-+SOC_ENUM_SINGLE(WM8753_ADCIN, 4, 4, wm8753_mono_adc), // 22
-+SOC_ENUM_SINGLE(WM8753_ADC, 2, 4, wm8753_adc_hp), // 23
-+SOC_ENUM_SINGLE(WM8753_ADC, 4, 2, wm8753_adc_filter), // 24
-+SOC_ENUM_SINGLE(WM8753_MICBIAS, 6, 3, wm8753_mic_sel), // 25
-+SOC_ENUM_SINGLE(WM8753_IOCTL, 2, 4, wm8753_dai_mode), // 26
-+SOC_ENUM_SINGLE(WM8753_ADC, 7, 4, wm8753_dat_sel), // 27
-+};
-+
-+
-+static int wm8753_get_dai(struct snd_kcontrol *kcontrol,
-+ struct snd_ctl_elem_value *ucontrol)
-+{
-+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-+ int mode = wm8753_read_reg_cache(codec, WM8753_IOCTL);
-+
-+ ucontrol->value.integer.value[0] = (mode & 0xc) >> 2;
-+ return 0;
-+}
-+
-+static int wm8753_set_dai(struct snd_kcontrol *kcontrol,
-+ struct snd_ctl_elem_value *ucontrol)
-+{
-+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-+ int mode = wm8753_read_reg_cache(codec, WM8753_IOCTL);
-+
-+ if (((mode &0xc) >> 2) == ucontrol->value.integer.value[0])
-+ return 0;
-+
-+ mode &= 0xfff3;
-+ mode |= (ucontrol->value.integer.value[0] << 2);
-+
-+ wm8753_write(codec, WM8753_IOCTL, mode);
-+ wm8753_set_dai_mode(codec, ucontrol->value.integer.value[0]);
-+ return 1;
-+}
-+
-+static const struct snd_kcontrol_new wm8753_snd_controls[] = {
-+SOC_DOUBLE_R("PCM Volume", WM8753_LDAC, WM8753_RDAC, 0, 255, 0),
-+
-+SOC_DOUBLE_R("ADC Capture Volume", WM8753_LADC, WM8753_RADC, 0, 255, 0),
-+
-+SOC_DOUBLE_R("Headphone Playback Volume", WM8753_LOUT1V, WM8753_ROUT1V, 0, 127, 0),
-+SOC_DOUBLE_R("Speaker Playback Volume", WM8753_LOUT2V, WM8753_ROUT2V, 0, 127, 0),
-+
-+SOC_SINGLE("Mono Playback Volume", WM8753_MOUTV, 0, 127, 0),
-+
-+SOC_DOUBLE_R("Bypass Playback Volume", WM8753_LOUTM1, WM8753_ROUTM1, 4, 7, 1),
-+SOC_DOUBLE_R("Sidetone Playback Volume", WM8753_LOUTM2, WM8753_ROUTM2, 4, 7, 1),
-+SOC_DOUBLE_R("Voice Playback Volume", WM8753_LOUTM2, WM8753_ROUTM2, 0, 7, 1),
-+
-+SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8753_LOUT1V, WM8753_ROUT1V, 7, 1, 0),
-+SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8753_LOUT2V, WM8753_ROUT2V, 7, 1, 0),
-+
-+SOC_SINGLE("Mono Bypass Playback Volume", WM8753_MOUTM1, 4, 7, 1),
-+SOC_SINGLE("Mono Sidetone Playback Volume", WM8753_MOUTM2, 4, 7, 1),
-+SOC_SINGLE("Mono Voice Playback Volume", WM8753_MOUTM2, 4, 7, 1),
-+SOC_SINGLE("Mono Playback ZC Switch", WM8753_MOUTV, 7, 1, 0),
-+
-+SOC_ENUM("Bass Boost", wm8753_enum[0]),
-+SOC_ENUM("Bass Filter", wm8753_enum[1]),
-+SOC_SINGLE("Bass Volume", WM8753_BASS, 0, 15, 1),
-+
-+SOC_SINGLE("Treble Volume", WM8753_TREBLE, 0, 15, 1),
-+SOC_ENUM("Treble Cut-off", wm8753_enum[2]),
-+
-+SOC_DOUBLE("Sidetone Capture Volume", WM8753_RECMIX1, 0, 4, 7, 1),
-+SOC_SINGLE("Voice Sidetone Capture Volume", WM8753_RECMIX2, 0, 7, 1),
-+
-+SOC_DOUBLE_R("Capture Volume", WM8753_LINVOL, WM8753_RINVOL, 0, 63, 0),
-+SOC_DOUBLE_R("Capture ZC Switch", WM8753_LINVOL, WM8753_RINVOL, 6, 1, 0),
-+SOC_DOUBLE_R("Capture Switch", WM8753_LINVOL, WM8753_RINVOL, 7, 1, 1),
-+
-+SOC_ENUM("Capture Filter Select", wm8753_enum[23]),
-+SOC_ENUM("Capture Filter Cut-off", wm8753_enum[24]),
-+SOC_SINGLE("Capture Filter Switch", WM8753_ADC, 0, 1, 1),
-+
-+SOC_SINGLE("ALC Capture Target Volume", WM8753_ALC1, 0, 7, 0),
-+SOC_SINGLE("ALC Capture Max Volume", WM8753_ALC1, 4, 7, 0),
-+SOC_ENUM("ALC Capture Function", wm8753_enum[3]),
-+SOC_SINGLE("ALC Capture ZC Switch", WM8753_ALC2, 8, 1, 0),
-+SOC_SINGLE("ALC Capture Hold Time", WM8753_ALC2, 0, 15, 1),
-+SOC_SINGLE("ALC Capture Decay Time", WM8753_ALC3, 4, 15, 1),
-+SOC_SINGLE("ALC Capture Attack Time", WM8753_ALC3, 0, 15, 0),
-+SOC_SINGLE("ALC Capture NG Threshold", WM8753_NGATE, 3, 31, 0),
-+SOC_ENUM("ALC Capture NG Type", wm8753_enum[4]),
-+SOC_SINGLE("ALC Capture NG Switch", WM8753_NGATE, 0, 1, 0),
-+
-+SOC_ENUM("3D Function", wm8753_enum[5]),
-+SOC_ENUM("3D Upper Cut-off", wm8753_enum[6]),
-+SOC_ENUM("3D Lower Cut-off", wm8753_enum[7]),
-+SOC_SINGLE("3D Volume", WM8753_3D, 1, 15, 0),
-+SOC_SINGLE("3D Switch", WM8753_3D, 0, 1, 0),
-+
-+SOC_SINGLE("Capture 6dB Attenuate", WM8753_ADCTL1, 2, 1, 0),
-+SOC_SINGLE("Playback 6dB Attenuate", WM8753_ADCTL1, 1, 1, 0),
-+
-+SOC_ENUM("De-emphasis", wm8753_enum[8]),
-+SOC_ENUM("Playback Mono Mix", wm8753_enum[9]),
-+SOC_ENUM("Playback Phase", wm8753_enum[10]),
-+
-+SOC_SINGLE("Mic2 Capture Volume", WM8753_INCTL1, 7, 3, 0),
-+SOC_SINGLE("Mic1 Capture Volume", WM8753_INCTL1, 5, 3, 0),
-+
-+SOC_ENUM_EXT("DAI Mode", wm8753_enum[26], wm8753_get_dai, wm8753_set_dai),
-+
-+SOC_ENUM("ADC Data Select", wm8753_enum[27]),
-+};
-+
-+/* add non dapm controls */
-+static int wm8753_add_controls(struct snd_soc_codec *codec)
-+{
-+ int err, i;
-+
-+ for (i = 0; i < ARRAY_SIZE(wm8753_snd_controls); i++) {
-+ err = snd_ctl_add(codec->card,
-+ snd_soc_cnew(&wm8753_snd_controls[i],codec, NULL));
-+ if (err < 0)
-+ return err;
-+ }
-+ return 0;
-+}
-+
-+/*
-+ * _DAPM_ Controls
-+ */
-+
-+/* Left Mixer */
-+static const struct snd_kcontrol_new wm8753_left_mixer_controls[] = {
-+SOC_DAPM_SINGLE("Voice Playback Switch", WM8753_LOUTM2, 8, 1, 0),
-+SOC_DAPM_SINGLE("Sidetone Playback Switch", WM8753_LOUTM2, 7, 1, 0),
-+SOC_DAPM_SINGLE("Left Playback Switch", WM8753_LOUTM1, 8, 1, 0),
-+SOC_DAPM_SINGLE("Bypass Playback Switch", WM8753_LOUTM1, 7, 1, 0),
-+};
-+
-+/* Right mixer */
-+static const struct snd_kcontrol_new wm8753_right_mixer_controls[] = {
-+SOC_DAPM_SINGLE("Voice Playback Switch", WM8753_ROUTM2, 8, 1, 0),
-+SOC_DAPM_SINGLE("Sidetone Playback Switch", WM8753_ROUTM2, 7, 1, 0),
-+SOC_DAPM_SINGLE("Right Playback Switch", WM8753_ROUTM1, 8, 1, 0),
-+SOC_DAPM_SINGLE("Bypass Playback Switch", WM8753_ROUTM1, 7, 1, 0),
-+};
-+
-+/* Mono mixer */
-+static const struct snd_kcontrol_new wm8753_mono_mixer_controls[] = {
-+SOC_DAPM_SINGLE("Left Playback Switch", WM8753_MOUTM1, 8, 1, 0),
-+SOC_DAPM_SINGLE("Right Playback Switch", WM8753_MOUTM2, 8, 1, 0),
-+SOC_DAPM_SINGLE("Voice Playback Switch", WM8753_MOUTM2, 3, 1, 0),
-+SOC_DAPM_SINGLE("Sidetone Playback Switch", WM8753_MOUTM2, 7, 1, 0),
-+SOC_DAPM_SINGLE("Bypass Playback Switch", WM8753_MOUTM1, 7, 1, 0),
-+};
-+
-+/* Mono 2 Mux */
-+static const struct snd_kcontrol_new wm8753_mono2_controls =
-+SOC_DAPM_ENUM("Route", wm8753_enum[17]);
-+
-+/* Out 3 Mux */
-+static const struct snd_kcontrol_new wm8753_out3_controls =
-+SOC_DAPM_ENUM("Route", wm8753_enum[18]);
-+
-+/* Out 4 Mux */
-+static const struct snd_kcontrol_new wm8753_out4_controls =
-+SOC_DAPM_ENUM("Route", wm8753_enum[19]);
-+
-+/* ADC Mono Mix */
-+static const struct snd_kcontrol_new wm8753_adc_mono_controls =
-+SOC_DAPM_ENUM("Route", wm8753_enum[22]);
-+
-+/* Record mixer */
-+static const struct snd_kcontrol_new wm8753_record_mixer_controls[] = {
-+SOC_DAPM_SINGLE("Voice Capture Switch", WM8753_RECMIX2, 3, 1, 0),
-+SOC_DAPM_SINGLE("Left Capture Switch", WM8753_RECMIX1, 3, 1, 0),
-+SOC_DAPM_SINGLE("Right Capture Switch", WM8753_RECMIX1, 7, 1, 0),
-+};
-+
-+/* Left ADC mux */
-+static const struct snd_kcontrol_new wm8753_adc_left_controls =
-+SOC_DAPM_ENUM("Route", wm8753_enum[21]);
-+
-+/* Right ADC mux */
-+static const struct snd_kcontrol_new wm8753_adc_right_controls =
-+SOC_DAPM_ENUM("Route", wm8753_enum[20]);
-+
-+/* MIC mux */
-+static const struct snd_kcontrol_new wm8753_mic_mux_controls =
-+SOC_DAPM_ENUM("Route", wm8753_enum[16]);
-+
-+/* ALC mixer */
-+static const struct snd_kcontrol_new wm8753_alc_mixer_controls[] = {
-+SOC_DAPM_SINGLE("Line Capture Switch", WM8753_INCTL2, 3, 1, 0),
-+SOC_DAPM_SINGLE("Mic2 Capture Switch", WM8753_INCTL2, 2, 1, 0),
-+SOC_DAPM_SINGLE("Mic1 Capture Switch", WM8753_INCTL2, 1, 1, 0),
-+SOC_DAPM_SINGLE("Rx Capture Switch", WM8753_INCTL2, 0, 1, 0),
-+};
-+
-+/* Left Line mux */
-+static const struct snd_kcontrol_new wm8753_line_left_controls =
-+SOC_DAPM_ENUM("Route", wm8753_enum[14]);
-+
-+/* Right Line mux */
-+static const struct snd_kcontrol_new wm8753_line_right_controls =
-+SOC_DAPM_ENUM("Route", wm8753_enum[13]);
-+
-+/* Mono Line mux */
-+static const struct snd_kcontrol_new wm8753_line_mono_controls =
-+SOC_DAPM_ENUM("Route", wm8753_enum[12]);
-+
-+/* Line mux and mixer */
-+static const struct snd_kcontrol_new wm8753_line_mux_mix_controls =
-+SOC_DAPM_ENUM("Route", wm8753_enum[11]);
-+
-+/* Rx mux and mixer */
-+static const struct snd_kcontrol_new wm8753_rx_mux_mix_controls =
-+SOC_DAPM_ENUM("Route", wm8753_enum[15]);
-+
-+/* Mic Selector Mux */
-+static const struct snd_kcontrol_new wm8753_mic_sel_mux_controls =
-+SOC_DAPM_ENUM("Route", wm8753_enum[25]);
-+
-+static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = {
-+SND_SOC_DAPM_MICBIAS("Mic Bias", WM8753_PWR1, 5, 0),
-+SND_SOC_DAPM_MIXER("Left Mixer", WM8753_PWR4, 0, 0,
-+ &wm8753_left_mixer_controls[0], ARRAY_SIZE(wm8753_left_mixer_controls)),
-+SND_SOC_DAPM_PGA("Left Out 1", WM8753_PWR3, 8, 0, NULL, 0),
-+SND_SOC_DAPM_PGA("Left Out 2", WM8753_PWR3, 6, 0, NULL, 0),
-+SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", WM8753_PWR1, 3, 0),
-+SND_SOC_DAPM_OUTPUT("LOUT1"),
-+SND_SOC_DAPM_OUTPUT("LOUT2"),
-+SND_SOC_DAPM_MIXER("Right Mixer", WM8753_PWR4, 1, 0,
-+ &wm8753_right_mixer_controls[0], ARRAY_SIZE(wm8753_right_mixer_controls)),
-+SND_SOC_DAPM_PGA("Right Out 1", WM8753_PWR3, 7, 0, NULL, 0),
-+SND_SOC_DAPM_PGA("Right Out 2", WM8753_PWR3, 5, 0, NULL, 0),
-+SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", WM8753_PWR1, 2, 0),
-+SND_SOC_DAPM_OUTPUT("ROUT1"),
-+SND_SOC_DAPM_OUTPUT("ROUT2"),
-+SND_SOC_DAPM_MIXER("Mono Mixer", WM8753_PWR4, 2, 0,
-+ &wm8753_mono_mixer_controls[0], ARRAY_SIZE(wm8753_mono_mixer_controls)),
-+SND_SOC_DAPM_PGA("Mono Out 1", WM8753_PWR3, 2, 0, NULL, 0),
-+SND_SOC_DAPM_PGA("Mono Out 2", WM8753_PWR3, 1, 0, NULL, 0),
-+SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback", WM8753_PWR1, 4, 0),
-+SND_SOC_DAPM_OUTPUT("MONO1"),
-+SND_SOC_DAPM_MUX("Mono 2 Mux", SND_SOC_NOPM, 0, 0, &wm8753_mono2_controls),
-+SND_SOC_DAPM_OUTPUT("MONO2"),
-+SND_SOC_DAPM_MIXER("Out3 Left + Right", -1, 0, 0, NULL, 0),
-+SND_SOC_DAPM_MUX("Out3 Mux", SND_SOC_NOPM, 0, 0, &wm8753_out3_controls),
-+SND_SOC_DAPM_PGA("Out 3", WM8753_PWR3, 4, 0, NULL, 0),
-+SND_SOC_DAPM_OUTPUT("OUT3"),
-+SND_SOC_DAPM_MUX("Out4 Mux", SND_SOC_NOPM, 0, 0, &wm8753_out4_controls),
-+SND_SOC_DAPM_PGA("Out 4", WM8753_PWR3, 3, 0, NULL, 0),
-+SND_SOC_DAPM_OUTPUT("OUT4"),
-+SND_SOC_DAPM_MIXER("Playback Mixer", WM8753_PWR4, 3, 0,
-+ &wm8753_record_mixer_controls[0],
-+ ARRAY_SIZE(wm8753_record_mixer_controls)),
-+SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8753_PWR2, 3, 0),
-+SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8753_PWR2, 2, 0),
-+SND_SOC_DAPM_MUX("Capture Left Mixer", SND_SOC_NOPM, 0, 0,
-+ &wm8753_adc_mono_controls),
-+SND_SOC_DAPM_MUX("Capture Right Mixer", SND_SOC_NOPM, 0, 0,
-+ &wm8753_adc_mono_controls),
-+SND_SOC_DAPM_MUX("Capture Left Mux", SND_SOC_NOPM, 0, 0,
-+ &wm8753_adc_left_controls),
-+SND_SOC_DAPM_MUX("Capture Right Mux", SND_SOC_NOPM, 0, 0,
-+ &wm8753_adc_right_controls),
-+SND_SOC_DAPM_MUX("Mic Sidetone Mux", SND_SOC_NOPM, 0, 0,
-+ &wm8753_mic_mux_controls),
-+SND_SOC_DAPM_PGA("Left Capture Volume", WM8753_PWR2, 5, 0, NULL, 0),
-+SND_SOC_DAPM_PGA("Right Capture Volume", WM8753_PWR2, 4, 0, NULL, 0),
-+SND_SOC_DAPM_MIXER("ALC Mixer", WM8753_PWR2, 6, 0,
-+ &wm8753_alc_mixer_controls[0], ARRAY_SIZE(wm8753_alc_mixer_controls)),
-+SND_SOC_DAPM_MUX("Line Left Mux", SND_SOC_NOPM, 0, 0,
-+ &wm8753_line_left_controls),
-+SND_SOC_DAPM_MUX("Line Right Mux", SND_SOC_NOPM, 0, 0,
-+ &wm8753_line_right_controls),
-+SND_SOC_DAPM_MUX("Line Mono Mux", SND_SOC_NOPM, 0, 0,
-+ &wm8753_line_mono_controls),
-+SND_SOC_DAPM_MUX("Line Mixer", WM8753_PWR2, 0, 0,
-+ &wm8753_line_mux_mix_controls),
-+SND_SOC_DAPM_MUX("Rx Mixer", WM8753_PWR2, 1, 0,
-+ &wm8753_rx_mux_mix_controls),
-+SND_SOC_DAPM_PGA("Mic 1 Volume", WM8753_PWR2, 8, 0, NULL, 0),
-+SND_SOC_DAPM_PGA("Mic 2 Volume", WM8753_PWR2, 7, 0, NULL, 0),
-+SND_SOC_DAPM_MUX("Mic Selection Mux", SND_SOC_NOPM, 0, 0,
-+ &wm8753_mic_sel_mux_controls),
-+SND_SOC_DAPM_INPUT("LINE1"),
-+SND_SOC_DAPM_INPUT("LINE2"),
-+SND_SOC_DAPM_INPUT("RXP"),
-+SND_SOC_DAPM_INPUT("RXN"),
-+SND_SOC_DAPM_INPUT("ACIN"),
-+SND_SOC_DAPM_OUTPUT("ACOP"),
-+SND_SOC_DAPM_INPUT("MIC1N"),
-+SND_SOC_DAPM_INPUT("MIC1"),
-+SND_SOC_DAPM_INPUT("MIC2N"),
-+SND_SOC_DAPM_INPUT("MIC2"),
-+SND_SOC_DAPM_VMID("VREF"),
-+};
-+
-+static const char *audio_map[][3] = {
-+ /* left mixer */
-+ {"Left Mixer", "Left Playback Switch", "Left DAC"},
-+ {"Left Mixer", "Voice Playback Switch", "Voice DAC"},
-+ {"Left Mixer", "Sidetone Playback Switch", "Mic Sidetone Mux"},
-+ {"Left Mixer", "Bypass Playback Switch", "Line Left Mux"},
-+
-+ /* right mixer */
-+ {"Right Mixer", "Right Playback Switch", "Right DAC"},
-+ {"Right Mixer", "Voice Playback Switch", "Voice DAC"},
-+ {"Right Mixer", "Sidetone Playback Switch", "Mic Sidetone Mux"},
-+ {"Right Mixer", "Bypass Playback Switch", "Line Right Mux"},
-+
-+ /* mono mixer */
-+ {"Mono Mixer", "Voice Playback Switch", "Voice DAC"},
-+ {"Mono Mixer", "Left Playback Switch", "Left DAC"},
-+ {"Mono Mixer", "Right Playback Switch", "Right DAC"},
-+ {"Mono Mixer", "Sidetone Playback Switch", "Mic Sidetone Mux"},
-+ {"Mono Mixer", "Bypass Playback Switch", "Line Mono Mux"},
-+
-+ /* left out */
-+ {"Left Out 1", NULL, "Left Mixer"},
-+ {"Left Out 2", NULL, "Left Mixer"},
-+ {"LOUT1", NULL, "Left Out 1"},
-+ {"LOUT2", NULL, "Left Out 2"},
-+
-+ /* right out */
-+ {"Right Out 1", NULL, "Right Mixer"},
-+ {"Right Out 2", NULL, "Right Mixer"},
-+ {"ROUT1", NULL, "Right Out 1"},
-+ {"ROUT2", NULL, "Right Out 2"},
-+
-+ /* mono 1 out */
-+ {"Mono Out 1", NULL, "Mono Mixer"},
-+ {"MONO1", NULL, "Mono Out 1"},
-+
-+ /* mono 2 out */
-+ {"Mono 2 Mux", "Left + Right", "Out3 Left + Right"},
-+ {"Mono 2 Mux", "Inverted Mono 1", "MONO1"},
-+ {"Mono 2 Mux", "Left", "Left Mixer"},
-+ {"Mono 2 Mux", "Right", "Right Mixer"},
-+ {"Mono Out 2", NULL, "Mono 2 Mux"},
-+ {"MONO2", NULL, "Mono Out 2"},
-+
-+ /* out 3 */
-+ {"Out3 Left + Right", NULL, "Left Mixer"},
-+ {"Out3 Left + Right", NULL, "Right Mixer"},
-+ {"Out3 Mux", "VREF", "VREF"},
-+ {"Out3 Mux", "Left + Right", "Out3 Left + Right"},
-+ {"Out3 Mux", "ROUT2", "ROUT2"},
-+ {"Out 3", NULL, "Out3 Mux"},
-+ {"OUT3", NULL, "Out 3"},
-+
-+ /* out 4 */
-+ {"Out4 Mux", "VREF", "VREF"},
-+ {"Out4 Mux", "Capture ST", "Capture ST Mixer"},
-+ {"Out4 Mux", "LOUT2", "LOUT2"},
-+ {"Out 4", NULL, "Out4 Mux"},
-+ {"OUT4", NULL, "Out 4"},
-+
-+ /* record mixer */
-+ {"Playback Mixer", "Left Capture Switch", "Left Mixer"},
-+ {"Playback Mixer", "Voice Capture Switch", "Mono Mixer"},
-+ {"Playback Mixer", "Right Capture Switch", "Right Mixer"},
-+
-+ /* Mic/SideTone Mux */
-+ {"Mic Sidetone Mux", "Left PGA", "Left Capture Volume"},
-+ {"Mic Sidetone Mux", "Right PGA", "Right Capture Volume"},
-+ {"Mic Sidetone Mux", "Mic 1", "Mic 1 Volume"},
-+ {"Mic Sidetone Mux", "Mic 2", "Mic 2 Volume"},
-+
-+ /* Capture Left Mux */
-+ {"Capture Left Mux", "PGA", "Left Capture Volume"},
-+ {"Capture Left Mux", "Line or RXP-RXN", "Line Left Mux"},
-+ {"Capture Left Mux", "Line", "LINE1"},
-+
-+ /* Capture Right Mux */
-+ {"Capture Right Mux", "PGA", "Right Capture Volume"},
-+ {"Capture Right Mux", "Line or RXP-RXN", "Line Right Mux"},
-+ {"Capture Right Mux", "Sidetone", "Capture ST Mixer"},
-+
-+ /* Mono Capture mixer-mux */
-+ {"Capture Right Mixer", "Stereo", "Capture Right Mux"},
-+ {"Capture Left Mixer", "Analogue Mix Left", "Capture Left Mux"},
-+ {"Capture Left Mixer", "Analogue Mix Left", "Capture Right Mux"},
-+ {"Capture Right Mixer", "Analogue Mix Right", "Capture Left Mux"},
-+ {"Capture Right Mixer", "Analogue Mix Right", "Capture Right Mux"},
-+ {"Capture Left Mixer", "Digital Mono Mix", "Capture Left Mux"},
-+ {"Capture Left Mixer", "Digital Mono Mix", "Capture Right Mux"},
-+ {"Capture Right Mixer", "Digital Mono Mix", "Capture Left Mux"},
-+ {"Capture Right Mixer", "Digital Mono Mix", "Capture Right Mux"},
-+
-+ /* ADC */
-+ {"Left ADC", NULL, "Capture Left Mixer"},
-+ {"Right ADC", NULL, "Capture Right Mixer"},
-+
-+ /* Left Capture Volume */
-+ {"Left Capture Volume", NULL, "ACIN"},
-+
-+ /* Right Capture Volume */
-+ {"Right Capture Volume", NULL, "Mic 2 Volume"},
-+
-+ /* ALC Mixer */
-+ {"ALC Mixer", "Line Capture Switch", "Line Mixer"},
-+ {"ALC Mixer", "Mic2 Capture Switch", "Mic 2 Volume"},
-+ {"ALC Mixer", "Mic1 Capture Switch", "Mic 1 Volume"},
-+ {"ALC Mixer", "Rx Capture Switch", "Rx Mixer"},
-+
-+ /* Line Left Mux */
-+ {"Line Left Mux", "Line 1", "LINE1"},
-+ {"Line Left Mux", "Rx Mix", "Rx Mixer"},
-+
-+ /* Line Right Mux */
-+ {"Line Right Mux", "Line 2", "LINE2"},
-+ {"Line Right Mux", "Rx Mix", "Rx Mixer"},
-+
-+ /* Line Mono Mux */
-+ {"Line Mono Mux", "Line Mix", "Line Mixer"},
-+ {"Line Mono Mux", "Rx Mix", "Rx Mixer"},
-+
-+ /* Line Mixer/Mux */
-+ {"Line Mixer", "Line 1 + 2", "LINE1"},
-+ {"Line Mixer", "Line 1 - 2", "LINE1"},
-+ {"Line Mixer", "Line 1 + 2", "LINE2"},
-+ {"Line Mixer", "Line 1 - 2", "LINE2"},
-+ {"Line Mixer", "Line 1", "LINE1"},
-+ {"Line Mixer", "Line 2", "LINE2"},
-+
-+ /* Rx Mixer/Mux */
-+ {"Rx Mixer", "RXP - RXN", "RXP"},
-+ {"Rx Mixer", "RXP + RXN", "RXP"},
-+ {"Rx Mixer", "RXP - RXN", "RXN"},
-+ {"Rx Mixer", "RXP + RXN", "RXN"},
-+ {"Rx Mixer", "RXP", "RXP"},
-+ {"Rx Mixer", "RXN", "RXN"},
-+
-+ /* Mic 1 Volume */
-+ {"Mic 1 Volume", NULL, "MIC1N"},
-+ {"Mic 1 Volume", NULL, "Mic Selection Mux"},
-+
-+ /* Mic 2 Volume */
-+ {"Mic 2 Volume", NULL, "MIC2N"},
-+ {"Mic 2 Volume", NULL, "MIC2"},
-+
-+ /* Mic Selector Mux */
-+ {"Mic Selection Mux", "Mic 1", "MIC1"},
-+ {"Mic Selection Mux", "Mic 2", "MIC2N"},
-+ {"Mic Selection Mux", "Mic 3", "MIC2"},
-+
-+ /* ACOP */
-+ {"ACOP", NULL, "ALC Mixer"},
-+
-+ /* terminator */
-+ {NULL, NULL, NULL},
-+};
-+
-+static int wm8753_add_widgets(struct snd_soc_codec *codec)
-+{
-+ int i;
-+
-+ for (i = 0; i < ARRAY_SIZE(wm8753_dapm_widgets); i++)
-+ snd_soc_dapm_new_control(codec, &wm8753_dapm_widgets[i]);
-+
-+ /* set up the WM8753 audio map */
-+ for (i = 0; audio_map[i][0] != NULL; i++) {
-+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
-+ audio_map[i][1], audio_map[i][2]);
-+ }
-+
-+ snd_soc_dapm_new_widgets(codec);
-+ return 0;
-+}
-+
-+/* PLL divisors */
-+struct _pll_div {
-+ u32 div2:1;
-+ u32 n:4;
-+ u32 k:24;
-+};
-+
-+/* The size in bits of the pll divide multiplied by 10
-+ * to allow rounding later */
-+#define FIXED_PLL_SIZE ((1 << 22) * 10)
-+
-+static void pll_factors(struct _pll_div *pll_div, unsigned int target,
-+ unsigned int source)
-+{
-+ unsigned long long Kpart;
-+ unsigned int K, Ndiv, Nmod;
-+
-+ Ndiv = target / source;
-+ if (Ndiv < 6) {
-+ source >>= 1;
-+ pll_div->div2 = 1;
-+ Ndiv = target / source;
-+ } else
-+ pll_div->div2 = 0;
-+
-+ if ((Ndiv < 6) || (Ndiv > 12))
-+ printk(KERN_WARNING
-+ "WM8753 N value outwith recommended range! N = %d\n",Ndiv);
-+
-+ pll_div->n = Ndiv;
-+ Nmod = target % source;
-+ Kpart = FIXED_PLL_SIZE * (long long)Nmod;
-+
-+ do_div(Kpart, source);
-+
-+ K = Kpart & 0xFFFFFFFF;
-+
-+ /* Check if we need to round */
-+ if ((K % 10) >= 5)
-+ K += 5;
-+
-+ /* Move down to proper range now rounding is done */
-+ K /= 10;
-+
-+ pll_div->k = K;
-+}
-+
-+static int wm8753_set_dai_pll(struct snd_soc_codec_dai *codec_dai,
-+ int pll_id, unsigned int freq_in, unsigned int freq_out)
-+{
-+ u16 reg, enable;
-+ int offset;
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+
-+ if (pll_id < WM8753_PLL1 || pll_id > WM8753_PLL2)
-+ return -ENODEV;
-+
-+ if (pll_id == WM8753_PLL1) {
-+ offset = 0;
-+ enable = 0x10;
-+ reg = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0xffef;
-+ } else {
-+ offset = 4;
-+ enable = 0x8;
-+ reg = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0xfff7;
-+ }
-+
-+ if (!freq_in || !freq_out) {
-+ /* disable PLL */
-+ wm8753_write(codec, WM8753_PLL1CTL1 + offset, 0x0026);
-+ wm8753_write(codec, WM8753_CLOCK, reg);
-+ return 0;
-+ } else {
-+
-+ u16 value = 0;
-+ struct _pll_div pll_div;
-+
-+ pll_factors(&pll_div, freq_out * 8, freq_in);
-+
-+ /* set up N and K PLL divisor ratios */
-+ /* bits 8:5 = PLL_N, bits 3:0 = PLL_K[21:18] */
-+ value = (pll_div.n << 5) + ((pll_div.k & 0x3c0000) >> 18);
-+ wm8753_write(codec, WM8753_PLL1CTL2 + offset, value);
-+
-+ /* bits 8:0 = PLL_K[17:9] */
-+ value = (pll_div.k & 0x03fe00) >> 9;
-+ wm8753_write(codec, WM8753_PLL1CTL3 + offset, value);
-+
-+ /* bits 8:0 = PLL_K[8:0] */
-+ value = pll_div.k & 0x0001ff;
-+ wm8753_write(codec, WM8753_PLL1CTL4 + offset, value);
-+
-+ /* set PLL as input and enable */
-+ wm8753_write(codec, WM8753_PLL1CTL1 + offset, 0x0027 |
-+ (pll_div.div2 << 3));
-+ wm8753_write(codec, WM8753_CLOCK, reg | enable);
-+ }
-+ return 0;
-+}
-+
-+struct _coeff_div {
-+ u32 mclk;
-+ u32 rate;
-+ u8 sr:5;
-+ u8 usb:1;
-+};
-+
-+/* codec hifi mclk (after PLL) clock divider coefficients */
-+static const struct _coeff_div coeff_div[] = {
-+ /* 8k */
-+ {12288000, 8000, 0x6, 0x0},
-+ {11289600, 8000, 0x16, 0x0},
-+ {18432000, 8000, 0x7, 0x0},
-+ {16934400, 8000, 0x17, 0x0},
-+ {12000000, 8000, 0x6, 0x1},
-+
-+ /* 11.025k */
-+ {11289600, 11025, 0x18, 0x0},
-+ {16934400, 11025, 0x19, 0x0},
-+ {12000000, 11025, 0x19, 0x1},
-+
-+ /* 16k */
-+ {12288000, 16000, 0xa, 0x0},
-+ {18432000, 16000, 0xb, 0x0},
-+ {12000000, 16000, 0xa, 0x1},
-+
-+ /* 22.05k */
-+ {11289600, 22050, 0x1a, 0x0},
-+ {16934400, 22050, 0x1b, 0x0},
-+ {12000000, 22050, 0x1b, 0x1},
-+
-+ /* 32k */
-+ {12288000, 32000, 0xc, 0x0},
-+ {18432000, 32000, 0xd, 0x0},
-+ {12000000, 32000, 0xa, 0x1},
-+
-+ /* 44.1k */
-+ {11289600, 44100, 0x10, 0x0},
-+ {16934400, 44100, 0x11, 0x0},
-+ {12000000, 44100, 0x11, 0x1},
-+
-+ /* 48k */
-+ {12288000, 48000, 0x0, 0x0},
-+ {18432000, 48000, 0x1, 0x0},
-+ {12000000, 48000, 0x0, 0x1},
-+
-+ /* 88.2k */
-+ {11289600, 88200, 0x1e, 0x0},
-+ {16934400, 88200, 0x1f, 0x0},
-+ {12000000, 88200, 0x1f, 0x1},
-+
-+ /* 96k */
-+ {12288000, 96000, 0xe, 0x0},
-+ {18432000, 96000, 0xf, 0x0},
-+ {12000000, 96000, 0xe, 0x1},
-+};
-+
-+static int get_coeff(int mclk, int rate)
-+{
-+ int i;
-+
-+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
-+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
-+ return i;
-+ }
-+ return -EINVAL;
-+}
-+
-+/*
-+ * Clock after PLL and dividers
-+ */
-+static int wm8753_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
-+ int clk_id, unsigned int freq, int dir)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ struct wm8753_priv *wm8753 = codec->private_data;
-+
-+ switch (freq) {
-+ case 11289600:
-+ case 12000000:
-+ case 12288000:
-+ case 16934400:
-+ case 18432000:
-+ if (clk_id == WM8753_MCLK) {
-+ wm8753->sysclk = freq;
-+ return 0;
-+ } else if (clk_id == WM8753_PCMCLK) {
-+ wm8753->pcmclk = freq;
-+ return 0;
-+ }
-+ break;
-+ }
-+ return -EINVAL;
-+}
-+
-+/*
-+ * Set's ADC and Voice DAC format.
-+ */
-+static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
-+ unsigned int fmt)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 voice = wm8753_read_reg_cache(codec, WM8753_PCM) & 0x01ec;
-+
-+ /* interface format */
-+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
-+ case SND_SOC_DAIFMT_I2S:
-+ voice |= 0x0002;
-+ break;
-+ case SND_SOC_DAIFMT_RIGHT_J:
-+ break;
-+ case SND_SOC_DAIFMT_LEFT_J:
-+ voice |= 0x0001;
-+ break;
-+ case SND_SOC_DAIFMT_DSP_A:
-+ voice |= 0x0003;
-+ break;
-+ case SND_SOC_DAIFMT_DSP_B:
-+ voice |= 0x0013;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ wm8753_write(codec, WM8753_PCM, voice);
-+ return 0;
-+}
-+
-+/*
-+ * Set PCM DAI bit size and sample rate.
-+ */
-+static int wm8753_pcm_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_device *socdev = rtd->socdev;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ struct wm8753_priv *wm8753 = codec->private_data;
-+ u16 voice = wm8753_read_reg_cache(codec, WM8753_PCM) & 0x01f3;
-+ u16 srate = wm8753_read_reg_cache(codec, WM8753_SRATE1) & 0x017f;
-+
-+ /* bit size */
-+ switch (params_format(params)) {
-+ case SNDRV_PCM_FORMAT_S16_LE:
-+ break;
-+ case SNDRV_PCM_FORMAT_S20_3LE:
-+ voice |= 0x0004;
-+ break;
-+ case SNDRV_PCM_FORMAT_S24_LE:
-+ voice |= 0x0008;
-+ break;
-+ case SNDRV_PCM_FORMAT_S32_LE:
-+ voice |= 0x000c;
-+ break;
-+ }
-+
-+ /* sample rate */
-+ if (params_rate(params) * 384 == wm8753->pcmclk)
-+ srate |= 0x80;
-+ wm8753_write(codec, WM8753_SRATE1, srate);
-+
-+ wm8753_write(codec, WM8753_PCM, voice);
-+ return 0;
-+}
-+
-+/*
-+ * Set's PCM dai fmt and BCLK.
-+ */
-+static int wm8753_pcm_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
-+ unsigned int fmt)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 voice, ioctl;
-+
-+ voice = wm8753_read_reg_cache(codec, WM8753_PCM) & 0x011f;
-+ ioctl = wm8753_read_reg_cache(codec, WM8753_IOCTL) & 0x015d;
-+
-+ /* set master/slave audio interface */
-+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
-+ case SND_SOC_DAIFMT_CBS_CFS:
-+ break;
-+ case SND_SOC_DAIFMT_CBM_CFM:
-+ ioctl |= 0x2;
-+ case SND_SOC_DAIFMT_CBM_CFS:
-+ voice |= 0x0040;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ /* clock inversion */
-+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
-+ case SND_SOC_DAIFMT_DSP_A:
-+ case SND_SOC_DAIFMT_DSP_B:
-+ /* frame inversion not valid for DSP modes */
-+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
-+ case SND_SOC_DAIFMT_NB_NF:
-+ break;
-+ case SND_SOC_DAIFMT_IB_NF:
-+ voice |= 0x0080;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+ break;
-+ case SND_SOC_DAIFMT_I2S:
-+ case SND_SOC_DAIFMT_RIGHT_J:
-+ case SND_SOC_DAIFMT_LEFT_J:
-+ voice &= ~0x0010;
-+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
-+ case SND_SOC_DAIFMT_NB_NF:
-+ break;
-+ case SND_SOC_DAIFMT_IB_IF:
-+ voice |= 0x0090;
-+ break;
-+ case SND_SOC_DAIFMT_IB_NF:
-+ voice |= 0x0080;
-+ break;
-+ case SND_SOC_DAIFMT_NB_IF:
-+ voice |= 0x0010;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ wm8753_write(codec, WM8753_PCM, voice);
-+ wm8753_write(codec, WM8753_IOCTL, ioctl);
-+ return 0;
-+}
-+
-+static int wm8753_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
-+ int div_id, int div)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 reg;
-+
-+ switch (div_id) {
-+ case WM8753_PCMDIV:
-+ reg = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0x003f;
-+ wm8753_write(codec, WM8753_CLOCK, reg | div);
-+ break;
-+ case WM8753_BCLKDIV:
-+ reg = wm8753_read_reg_cache(codec, WM8753_SRATE2) & 0x01c7;
-+ wm8753_write(codec, WM8753_SRATE2, reg | div);
-+ break;
-+ case WM8753_VXCLKDIV:
-+ reg = wm8753_read_reg_cache(codec, WM8753_SRATE2) & 0x003f;
-+ wm8753_write(codec, WM8753_SRATE2, reg | div);
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+ return 0;
-+}
-+
-+/*
-+ * Set's HiFi DAC format.
-+ */
-+static int wm8753_hdac_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
-+ unsigned int fmt)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 hifi = wm8753_read_reg_cache(codec, WM8753_HIFI) & 0x01e0;
-+
-+ /* interface format */
-+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
-+ case SND_SOC_DAIFMT_I2S:
-+ hifi |= 0x0002;
-+ break;
-+ case SND_SOC_DAIFMT_RIGHT_J:
-+ break;
-+ case SND_SOC_DAIFMT_LEFT_J:
-+ hifi |= 0x0001;
-+ break;
-+ case SND_SOC_DAIFMT_DSP_A:
-+ hifi |= 0x0003;
-+ break;
-+ case SND_SOC_DAIFMT_DSP_B:
-+ hifi |= 0x0013;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ wm8753_write(codec, WM8753_HIFI, hifi);
-+ return 0;
-+}
-+
-+/*
-+ * Set's I2S DAI format.
-+ */
-+static int wm8753_i2s_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
-+ unsigned int fmt)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 ioctl, hifi;
-+
-+ hifi = wm8753_read_reg_cache(codec, WM8753_HIFI) & 0x011f;
-+ ioctl = wm8753_read_reg_cache(codec, WM8753_IOCTL) & 0x00ae;
-+
-+ /* set master/slave audio interface */
-+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
-+ case SND_SOC_DAIFMT_CBS_CFS:
-+ break;
-+ case SND_SOC_DAIFMT_CBM_CFM:
-+ ioctl |= 0x1;
-+ case SND_SOC_DAIFMT_CBM_CFS:
-+ hifi |= 0x0040;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ /* clock inversion */
-+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
-+ case SND_SOC_DAIFMT_DSP_A:
-+ case SND_SOC_DAIFMT_DSP_B:
-+ /* frame inversion not valid for DSP modes */
-+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
-+ case SND_SOC_DAIFMT_NB_NF:
-+ break;
-+ case SND_SOC_DAIFMT_IB_NF:
-+ hifi |= 0x0080;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+ break;
-+ case SND_SOC_DAIFMT_I2S:
-+ case SND_SOC_DAIFMT_RIGHT_J:
-+ case SND_SOC_DAIFMT_LEFT_J:
-+ hifi &= ~0x0010;
-+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
-+ case SND_SOC_DAIFMT_NB_NF:
-+ break;
-+ case SND_SOC_DAIFMT_IB_IF:
-+ hifi |= 0x0090;
-+ break;
-+ case SND_SOC_DAIFMT_IB_NF:
-+ hifi |= 0x0080;
-+ break;
-+ case SND_SOC_DAIFMT_NB_IF:
-+ hifi |= 0x0010;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ wm8753_write(codec, WM8753_HIFI, hifi);
-+ wm8753_write(codec, WM8753_IOCTL, ioctl);
-+ return 0;
-+}
-+
-+/*
-+ * Set PCM DAI bit size and sample rate.
-+ */
-+static int wm8753_i2s_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_device *socdev = rtd->socdev;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ struct wm8753_priv *wm8753 = codec->private_data;
-+ u16 srate = wm8753_read_reg_cache(codec, WM8753_SRATE1) & 0x01c0;
-+ u16 hifi = wm8753_read_reg_cache(codec, WM8753_HIFI) & 0x01f3;
-+ int coeff;
-+
-+ /* is digital filter coefficient valid ? */
-+ coeff = get_coeff(wm8753->sysclk, params_rate(params));
-+ if (coeff < 0) {
-+ printk(KERN_ERR "wm8753 invalid MCLK or rate\n");
-+ return coeff;
-+ }
-+ wm8753_write(codec, WM8753_SRATE1, srate | (coeff_div[coeff].sr << 1) |
-+ coeff_div[coeff].usb);
-+
-+ /* bit size */
-+ switch (params_format(params)) {
-+ case SNDRV_PCM_FORMAT_S16_LE:
-+ break;
-+ case SNDRV_PCM_FORMAT_S20_3LE:
-+ hifi |= 0x0004;
-+ break;
-+ case SNDRV_PCM_FORMAT_S24_LE:
-+ hifi |= 0x0008;
-+ break;
-+ case SNDRV_PCM_FORMAT_S32_LE:
-+ hifi |= 0x000c;
-+ break;
-+ }
-+
-+ wm8753_write(codec, WM8753_HIFI, hifi);
-+ return 0;
-+}
-+
-+static int wm8753_mode1v_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
-+ unsigned int fmt)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 clock;
-+
-+ /* set clk source as pcmclk */
-+ clock = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0xfffb;
-+ wm8753_write(codec, WM8753_CLOCK, clock);
-+
-+ if (wm8753_vdac_adc_set_dai_fmt(codec_dai, fmt) < 0)
-+ return -EINVAL;
-+ return wm8753_pcm_set_dai_fmt(codec_dai, fmt);
-+}
-+
-+static int wm8753_mode1h_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
-+ unsigned int fmt)
-+{
-+ if (wm8753_hdac_set_dai_fmt(codec_dai, fmt) < 0)
-+ return -EINVAL;
-+ return wm8753_i2s_set_dai_fmt(codec_dai, fmt);
-+}
-+
-+static int wm8753_mode2_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
-+ unsigned int fmt)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 clock;
-+
-+ /* set clk source as pcmclk */
-+ clock = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0xfffb;
-+ wm8753_write(codec, WM8753_CLOCK, clock);
-+
-+ if (wm8753_vdac_adc_set_dai_fmt(codec_dai, fmt) < 0)
-+ return -EINVAL;
-+ return wm8753_i2s_set_dai_fmt(codec_dai, fmt);
-+}
-+
-+static int wm8753_mode3_4_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
-+ unsigned int fmt)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 clock;
-+
-+ /* set clk source as mclk */
-+ clock = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0xfffb;
-+ wm8753_write(codec, WM8753_CLOCK, clock | 0x4);
-+
-+ if (wm8753_hdac_set_dai_fmt(codec_dai, fmt) < 0)
-+ return -EINVAL;
-+ if (wm8753_vdac_adc_set_dai_fmt(codec_dai, fmt) < 0)
-+ return -EINVAL;
-+ return wm8753_i2s_set_dai_fmt(codec_dai, fmt);
-+}
-+
-+static int wm8753_mute(struct snd_soc_codec_dai *dai, int mute)
-+{
-+ struct snd_soc_codec *codec = dai->codec;
-+ u16 mute_reg = wm8753_read_reg_cache(codec, WM8753_DAC) & 0xfff7;
-+
-+ /* the digital mute covers the HiFi and Voice DAC's on the WM8753.
-+ * make sure we check if they are not both active when we mute */
-+ if (mute && dai->id == 1) {
-+ if (!wm8753_dai[WM8753_DAI_VOICE].playback.active ||
-+ !wm8753_dai[WM8753_DAI_HIFI].playback.active)
-+ wm8753_write(codec, WM8753_DAC, mute_reg | 0x8);
-+ } else {
-+ if (mute)
-+ wm8753_write(codec, WM8753_DAC, mute_reg | 0x8);
-+ else
-+ wm8753_write(codec, WM8753_DAC, mute_reg);
-+ }
-+
-+ return 0;
-+}
-+
-+static int wm8753_dapm_event(struct snd_soc_codec *codec, int event)
-+{
-+ u16 pwr_reg = wm8753_read_reg_cache(codec, WM8753_PWR1) & 0xfe3e;
-+
-+ switch (event) {
-+ case SNDRV_CTL_POWER_D0: /* full On */
-+ /* set vmid to 50k and unmute dac */
-+ wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x00c0);
-+ break;
-+ case SNDRV_CTL_POWER_D1: /* partial On */
-+ case SNDRV_CTL_POWER_D2: /* partial On */
-+ /* set vmid to 5k for quick power up */
-+ wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x01c1);
-+ break;
-+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
-+ /* mute dac and set vmid to 500k, enable VREF */
-+ wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x0141);
-+ break;
-+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
-+ wm8753_write(codec, WM8753_PWR1, 0x0001);
-+ break;
-+ }
-+ codec->dapm_state = event;
-+ return 0;
-+}
-+
-+#define WM8753_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
-+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
-+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
-+
-+#define WM8753_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
-+ SNDRV_PCM_FMTBIT_S24_LE)
-+
-+/*
-+ * The WM8753 supports upto 4 different and mutually exclusive DAI
-+ * configurations. This gives 2 PCM's available for use, hifi and voice.
-+ * NOTE: The Voice PCM cannot play or capture audio to the CPU as it's DAI
-+ * is connected between the wm8753 and a BT codec or GSM modem.
-+ *
-+ * 1. Voice over PCM DAI - HIFI DAC over HIFI DAI
-+ * 2. Voice over HIFI DAI - HIFI disabled
-+ * 3. Voice disabled - HIFI over HIFI
-+ * 4. Voice disabled - HIFI over HIFI, uses voice DAI LRC for capture
-+ */
-+static const struct snd_soc_codec_dai wm8753_all_dai[] = {
-+/* DAI HiFi mode 1 */
-+{ .name = "WM8753 HiFi",
-+ .id = 1,
-+ .playback = {
-+ .stream_name = "HiFi Playback",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = WM8753_RATES,
-+ .formats = WM8753_FORMATS,},
-+ .capture = { /* dummy for fast DAI switching */
-+ .stream_name = "Capture",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = WM8753_RATES,
-+ .formats = WM8753_FORMATS,},
-+ .ops = {
-+ .hw_params = wm8753_i2s_hw_params,},
-+ .dai_ops = {
-+ .digital_mute = wm8753_mute,
-+ .set_fmt = wm8753_mode1h_set_dai_fmt,
-+ .set_clkdiv = wm8753_set_dai_clkdiv,
-+ .set_pll = wm8753_set_dai_pll,
-+ .set_sysclk = wm8753_set_dai_sysclk,
-+ },
-+},
-+/* DAI Voice mode 1 */
-+{ .name = "WM8753 Voice",
-+ .id = 1,
-+ .playback = {
-+ .stream_name = "Voice Playback",
-+ .channels_min = 1,
-+ .channels_max = 1,
-+ .rates = WM8753_RATES,
-+ .formats = WM8753_FORMATS,},
-+ .capture = {
-+ .stream_name = "Capture",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = WM8753_RATES,
-+ .formats = WM8753_FORMATS,},
-+ .ops = {
-+ .hw_params = wm8753_pcm_hw_params,},
-+ .dai_ops = {
-+ .digital_mute = wm8753_mute,
-+ .set_fmt = wm8753_mode1v_set_dai_fmt,
-+ .set_clkdiv = wm8753_set_dai_clkdiv,
-+ .set_pll = wm8753_set_dai_pll,
-+ .set_sysclk = wm8753_set_dai_sysclk,
-+ },
-+},
-+/* DAI HiFi mode 2 - dummy */
-+{ .name = "WM8753 HiFi",
-+ .id = 2,
-+},
-+/* DAI Voice mode 2 */
-+{ .name = "WM8753 Voice",
-+ .id = 2,
-+ .playback = {
-+ .stream_name = "Voice Playback",
-+ .channels_min = 1,
-+ .channels_max = 1,
-+ .rates = WM8753_RATES,
-+ .formats = WM8753_FORMATS,},
-+ .capture = {
-+ .stream_name = "Capture",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = WM8753_RATES,
-+ .formats = WM8753_FORMATS,},
-+ .ops = {
-+ .hw_params = wm8753_pcm_hw_params,},
-+ .dai_ops = {
-+ .digital_mute = wm8753_mute,
-+ .set_fmt = wm8753_mode2_set_dai_fmt,
-+ .set_clkdiv = wm8753_set_dai_clkdiv,
-+ .set_pll = wm8753_set_dai_pll,
-+ .set_sysclk = wm8753_set_dai_sysclk,
-+ },
-+},
-+/* DAI HiFi mode 3 */
-+{ .name = "WM8753 HiFi",
-+ .id = 3,
-+ .playback = {
-+ .stream_name = "HiFi Playback",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = WM8753_RATES,
-+ .formats = WM8753_FORMATS,},
-+ .capture = {
-+ .stream_name = "Capture",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = WM8753_RATES,
-+ .formats = WM8753_FORMATS,},
-+ .ops = {
-+ .hw_params = wm8753_i2s_hw_params,},
-+ .dai_ops = {
-+ .digital_mute = wm8753_mute,
-+ .set_fmt = wm8753_mode3_4_set_dai_fmt,
-+ .set_clkdiv = wm8753_set_dai_clkdiv,
-+ .set_pll = wm8753_set_dai_pll,
-+ .set_sysclk = wm8753_set_dai_sysclk,
-+ },
-+},
-+/* DAI Voice mode 3 - dummy */
-+{ .name = "WM8753 Voice",
-+ .id = 3,
-+},
-+/* DAI HiFi mode 4 */
-+{ .name = "WM8753 HiFi",
-+ .id = 4,
-+ .playback = {
-+ .stream_name = "HiFi Playback",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = WM8753_RATES,
-+ .formats = WM8753_FORMATS,},
-+ .capture = {
-+ .stream_name = "Capture",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = WM8753_RATES,
-+ .formats = WM8753_FORMATS,},
-+ .ops = {
-+ .hw_params = wm8753_i2s_hw_params,},
-+ .dai_ops = {
-+ .digital_mute = wm8753_mute,
-+ .set_fmt = wm8753_mode3_4_set_dai_fmt,
-+ .set_clkdiv = wm8753_set_dai_clkdiv,
-+ .set_pll = wm8753_set_dai_pll,
-+ .set_sysclk = wm8753_set_dai_sysclk,
-+ },
-+},
-+/* DAI Voice mode 4 - dummy */
-+{ .name = "WM8753 Voice",
-+ .id = 4,
-+},
-+};
-+
-+struct snd_soc_codec_dai wm8753_dai[2];
-+EXPORT_SYMBOL_GPL(wm8753_dai);
-+
-+static void wm8753_set_dai_mode(struct snd_soc_codec *codec, unsigned int mode)
-+{
-+ if (mode < 4) {
-+ wm8753_dai[0] = wm8753_all_dai[mode << 1];
-+ wm8753_dai[1] = wm8753_all_dai[(mode << 1) + 1];
-+ }
-+ wm8753_dai[0].codec = codec;
-+ wm8753_dai[1].codec = codec;
-+}
-+
-+static void wm8753_work(struct work_struct *work)
-+{
-+ struct snd_soc_codec *codec =
-+ container_of(work, struct snd_soc_codec, delayed_work.work);
-+ wm8753_dapm_event(codec, codec->dapm_state);
-+}
-+
-+static int wm8753_suspend(struct platform_device *pdev, pm_message_t state)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+
-+ wm8753_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
-+ return 0;
-+}
-+
-+static int wm8753_resume(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+ int i;
-+ u8 data[2];
-+ u16 *cache = codec->reg_cache;
-+
-+ /* Sync reg_cache with the hardware */
-+ for (i = 0; i < ARRAY_SIZE(wm8753_reg); i++) {
-+ if (i + 1 == WM8753_RESET)
-+ continue;
-+ data[0] = ((i + 1) << 1) | ((cache[i] >> 8) & 0x0001);
-+ data[1] = cache[i] & 0x00ff;
-+ codec->hw_write(codec->control_data, data, 2);
-+ }
-+
-+ wm8753_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
-+
-+ /* charge wm8753 caps */
-+ if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) {
-+ wm8753_dapm_event(codec, SNDRV_CTL_POWER_D2);
-+ codec->dapm_state = SNDRV_CTL_POWER_D0;
-+ schedule_delayed_work(&codec->delayed_work,
-+ msecs_to_jiffies(caps_charge));
-+ }
-+
-+ return 0;
-+}
-+
-+/*
-+ * initialise the WM8753 driver
-+ * register the mixer and dsp interfaces with the kernel
-+ */
-+static int wm8753_init(struct snd_soc_device *socdev)
-+{
-+ struct snd_soc_codec *codec = socdev->codec;
-+ int reg, ret = 0;
-+
-+ codec->name = "WM8753";
-+ codec->owner = THIS_MODULE;
-+ codec->read = wm8753_read_reg_cache;
-+ codec->write = wm8753_write;
-+ codec->dapm_event = wm8753_dapm_event;
-+ codec->dai = wm8753_dai;
-+ codec->num_dai = 2;
-+ codec->reg_cache_size = ARRAY_SIZE(wm8753_reg);
-+
-+ codec->reg_cache =
-+ kzalloc(sizeof(u16) * ARRAY_SIZE(wm8753_reg), GFP_KERNEL);
-+ if (codec->reg_cache == NULL)
-+ return -ENOMEM;
-+ memcpy(codec->reg_cache, wm8753_reg,
-+ sizeof(u16) * ARRAY_SIZE(wm8753_reg));
-+ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8753_reg);
-+ wm8753_set_dai_mode(codec, 0);
-+
-+ wm8753_reset(codec);
-+
-+ /* register pcms */
-+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
-+ if (ret < 0) {
-+ printk(KERN_ERR "wm8753: failed to create pcms\n");
-+ goto pcm_err;
-+ }
-+
-+ /* charge output caps */
-+ wm8753_dapm_event(codec, SNDRV_CTL_POWER_D2);
-+ codec->dapm_state = SNDRV_CTL_POWER_D3hot;
-+ schedule_delayed_work(&codec->delayed_work,
-+ msecs_to_jiffies(caps_charge));
-+
-+ /* set the update bits */
-+ reg = wm8753_read_reg_cache(codec, WM8753_LDAC);
-+ wm8753_write(codec, WM8753_LDAC, reg | 0x0100);
-+ reg = wm8753_read_reg_cache(codec, WM8753_RDAC);
-+ wm8753_write(codec, WM8753_RDAC, reg | 0x0100);
-+ reg = wm8753_read_reg_cache(codec, WM8753_LOUT1V);
-+ wm8753_write(codec, WM8753_LOUT1V, reg | 0x0100);
-+ reg = wm8753_read_reg_cache(codec, WM8753_ROUT1V);
-+ wm8753_write(codec, WM8753_ROUT1V, reg | 0x0100);
-+ reg = wm8753_read_reg_cache(codec, WM8753_LOUT2V);
-+ wm8753_write(codec, WM8753_LOUT2V, reg | 0x0100);
-+ reg = wm8753_read_reg_cache(codec, WM8753_ROUT2V);
-+ wm8753_write(codec, WM8753_ROUT2V, reg | 0x0100);
-+ reg = wm8753_read_reg_cache(codec, WM8753_LINVOL);
-+ wm8753_write(codec, WM8753_LINVOL, reg | 0x0100);
-+ reg = wm8753_read_reg_cache(codec, WM8753_RINVOL);
-+ wm8753_write(codec, WM8753_RINVOL, reg | 0x0100);
-+
-+ wm8753_add_controls(codec);
-+ wm8753_add_widgets(codec);
-+ ret = snd_soc_register_card(socdev);
-+ if (ret < 0) {
-+ printk(KERN_ERR "wm8753: failed to register card\n");
-+ goto card_err;
-+ }
-+ return ret;
-+
-+card_err:
-+ snd_soc_free_pcms(socdev);
-+ snd_soc_dapm_free(socdev);
-+pcm_err:
-+ kfree(codec->reg_cache);
-+ return ret;
-+}
-+
-+/* If the i2c layer weren't so broken, we could pass this kind of data
-+ around */
-+static struct snd_soc_device *wm8753_socdev;
-+
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+
-+/*
-+ * WM8753 2 wire address is determined by GPIO5
-+ * state during powerup.
-+ * low = 0x1a
-+ * high = 0x1b
-+ */
-+#define I2C_DRIVERID_WM8753 0xfefe /* liam - need a proper id */
-+
-+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
-+
-+/* Magic definition of all other variables and things */
-+I2C_CLIENT_INSMOD;
-+
-+static struct i2c_driver wm8753_i2c_driver;
-+static struct i2c_client client_template;
-+
-+static int wm8753_codec_probe(struct i2c_adapter *adap, int addr, int kind)
-+{
-+ struct snd_soc_device *socdev = wm8753_socdev;
-+ struct wm8753_setup_data *setup = socdev->codec_data;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ struct i2c_client *i2c;
-+ int ret;
-+
-+ if (addr != setup->i2c_address)
-+ return -ENODEV;
-+
-+ client_template.adapter = adap;
-+ client_template.addr = addr;
-+
-+ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
-+ if (i2c == NULL){
-+ kfree(codec);
-+ return -ENOMEM;
-+ }
-+ memcpy(i2c, &client_template, sizeof(struct i2c_client));
-+ i2c_set_clientdata(i2c, codec);
-+ codec->control_data = i2c;
-+
-+ ret = i2c_attach_client(i2c);
-+ if (ret < 0) {
-+ err("failed to attach codec at addr %x\n", addr);
-+ goto err;
-+ }
-+
-+ ret = wm8753_init(socdev);
-+ if (ret < 0) {
-+ err("failed to initialise WM8753\n");
-+ goto err;
-+ }
-+
-+ return ret;
-+
-+err:
-+ kfree(codec);
-+ kfree(i2c);
-+ return ret;
-+}
-+
-+static int wm8753_i2c_detach(struct i2c_client *client)
-+{
-+ struct snd_soc_codec *codec = i2c_get_clientdata(client);
-+ i2c_detach_client(client);
-+ kfree(codec->reg_cache);
-+ kfree(client);
-+ return 0;
-+}
-+
-+static int wm8753_i2c_attach(struct i2c_adapter *adap)
-+{
-+ return i2c_probe(adap, &addr_data, wm8753_codec_probe);
-+}
-+
-+/* corgi i2c codec control layer */
-+static struct i2c_driver wm8753_i2c_driver = {
-+ .driver = {
-+ .name = "WM8753 I2C Codec",
-+ .owner = THIS_MODULE,
-+ },
-+ .id = I2C_DRIVERID_WM8753,
-+ .attach_adapter = wm8753_i2c_attach,
-+ .detach_client = wm8753_i2c_detach,
-+ .command = NULL,
-+};
-+
-+static struct i2c_client client_template = {
-+ .name = "WM8753",
-+ .driver = &wm8753_i2c_driver,
-+};
-+#endif
-+
-+static int wm8753_probe(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct wm8753_setup_data *setup;
-+ struct snd_soc_codec *codec;
-+ struct wm8753_priv *wm8753;
-+ int ret = 0;
-+
-+ info("WM8753 Audio Codec %s", WM8753_VERSION);
-+
-+ setup = socdev->codec_data;
-+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
-+ if (codec == NULL)
-+ return -ENOMEM;
-+
-+ wm8753 = kzalloc(sizeof(struct wm8753_priv), GFP_KERNEL);
-+ if (wm8753 == NULL) {
-+ kfree(codec);
-+ return -ENOMEM;
-+ }
-+
-+ codec->private_data = wm8753;
-+ socdev->codec = codec;
-+ mutex_init(&codec->mutex);
-+ INIT_LIST_HEAD(&codec->dapm_widgets);
-+ INIT_LIST_HEAD(&codec->dapm_paths);
-+ wm8753_socdev = socdev;
-+ INIT_DELAYED_WORK(&codec->delayed_work, wm8753_work);
-+
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+ if (setup->i2c_address) {
-+ normal_i2c[0] = setup->i2c_address;
-+ codec->hw_write = (hw_write_t)i2c_master_send;
-+ ret = i2c_add_driver(&wm8753_i2c_driver);
-+ if (ret != 0)
-+ printk(KERN_ERR "can't add i2c driver");
-+ }
-+#else
-+ /* Add other interfaces here */
-+#endif
-+ return ret;
-+}
-+
-+/*
-+ * This function forces any delayed work to be queued and run.
-+ */
-+static int run_delayed_work(struct delayed_work *dwork)
-+{
-+ int ret;
-+
-+ /* cancel any work waiting to be queued. */
-+ ret = cancel_delayed_work(dwork);
-+
-+ /* if there was any work waiting then we run it now and
-+ * wait for it's completion */
-+ if (ret) {
-+ schedule_delayed_work(dwork, 0);
-+ flush_scheduled_work();
-+ }
-+ return ret;
-+}
-+
-+/* power down chip */
-+static int wm8753_remove(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+
-+ if (codec->control_data)
-+ wm8753_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
-+ run_delayed_work(&codec->delayed_work);
-+ snd_soc_free_pcms(socdev);
-+ snd_soc_dapm_free(socdev);
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+ i2c_del_driver(&wm8753_i2c_driver);
-+#endif
-+ kfree(codec->private_data);
-+ kfree(codec);
-+
-+ return 0;
-+}
-+
-+struct snd_soc_codec_device soc_codec_dev_wm8753 = {
-+ .probe = wm8753_probe,
-+ .remove = wm8753_remove,
-+ .suspend = wm8753_suspend,
-+ .resume = wm8753_resume,
-+};
-+
-+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8753);
-+
-+MODULE_DESCRIPTION("ASoC WM8753 driver");
-+MODULE_AUTHOR("Liam Girdwood");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/codecs/wm8753.h
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/codecs/wm8753.h
-@@ -0,0 +1,126 @@
-+/*
-+ * wm8753.h -- audio driver for WM8753
-+ *
-+ * Copyright 2003 Wolfson Microelectronics PLC.
-+ * Author: Liam Girdwood
-+ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or (at your
-+ * option) any later version.
-+ *
-+ */
-+
-+#ifndef _WM8753_H
-+#define _WM8753_H
-+
-+/* WM8753 register space */
-+
-+#define WM8753_DAC 0x01
-+#define WM8753_ADC 0x02
-+#define WM8753_PCM 0x03
-+#define WM8753_HIFI 0x04
-+#define WM8753_IOCTL 0x05
-+#define WM8753_SRATE1 0x06
-+#define WM8753_SRATE2 0x07
-+#define WM8753_LDAC 0x08
-+#define WM8753_RDAC 0x09
-+#define WM8753_BASS 0x0a
-+#define WM8753_TREBLE 0x0b
-+#define WM8753_ALC1 0x0c
-+#define WM8753_ALC2 0x0d
-+#define WM8753_ALC3 0x0e
-+#define WM8753_NGATE 0x0f
-+#define WM8753_LADC 0x10
-+#define WM8753_RADC 0x11
-+#define WM8753_ADCTL1 0x12
-+#define WM8753_3D 0x13
-+#define WM8753_PWR1 0x14
-+#define WM8753_PWR2 0x15
-+#define WM8753_PWR3 0x16
-+#define WM8753_PWR4 0x17
-+#define WM8753_ID 0x18
-+#define WM8753_INTPOL 0x19
-+#define WM8753_INTEN 0x1a
-+#define WM8753_GPIO1 0x1b
-+#define WM8753_GPIO2 0x1c
-+#define WM8753_RESET 0x1f
-+#define WM8753_RECMIX1 0x20
-+#define WM8753_RECMIX2 0x21
-+#define WM8753_LOUTM1 0x22
-+#define WM8753_LOUTM2 0x23
-+#define WM8753_ROUTM1 0x24
-+#define WM8753_ROUTM2 0x25
-+#define WM8753_MOUTM1 0x26
-+#define WM8753_MOUTM2 0x27
-+#define WM8753_LOUT1V 0x28
-+#define WM8753_ROUT1V 0x29
-+#define WM8753_LOUT2V 0x2a
-+#define WM8753_ROUT2V 0x2b
-+#define WM8753_MOUTV 0x2c
-+#define WM8753_OUTCTL 0x2d
-+#define WM8753_ADCIN 0x2e
-+#define WM8753_INCTL1 0x2f
-+#define WM8753_INCTL2 0x30
-+#define WM8753_LINVOL 0x31
-+#define WM8753_RINVOL 0x32
-+#define WM8753_MICBIAS 0x33
-+#define WM8753_CLOCK 0x34
-+#define WM8753_PLL1CTL1 0x35
-+#define WM8753_PLL1CTL2 0x36
-+#define WM8753_PLL1CTL3 0x37
-+#define WM8753_PLL1CTL4 0x38
-+#define WM8753_PLL2CTL1 0x39
-+#define WM8753_PLL2CTL2 0x3a
-+#define WM8753_PLL2CTL3 0x3b
-+#define WM8753_PLL2CTL4 0x3c
-+#define WM8753_BIASCTL 0x3d
-+#define WM8753_ADCTL2 0x3f
-+
-+struct wm8753_setup_data {
-+ unsigned short i2c_address;
-+};
-+
-+#define WM8753_PLL1 0
-+#define WM8753_PLL2 1
-+
-+/* clock inputs */
-+#define WM8753_MCLK 0
-+#define WM8753_PCMCLK 1
-+
-+/* clock divider id's */
-+#define WM8753_PCMDIV 0
-+#define WM8753_BCLKDIV 1
-+#define WM8753_VXCLKDIV 2
-+
-+/* PCM clock dividers */
-+#define WM8753_PCM_DIV_1 (0 << 6)
-+#define WM8753_PCM_DIV_3 (2 << 6)
-+#define WM8753_PCM_DIV_5_5 (3 << 6)
-+#define WM8753_PCM_DIV_2 (4 << 6)
-+#define WM8753_PCM_DIV_4 (5 << 6)
-+#define WM8753_PCM_DIV_6 (6 << 6)
-+#define WM8753_PCM_DIV_8 (7 << 6)
-+
-+/* BCLK clock dividers */
-+#define WM8753_BCLK_DIV_1 (0 << 3)
-+#define WM8753_BCLK_DIV_2 (1 << 3)
-+#define WM8753_BCLK_DIV_4 (2 << 3)
-+#define WM8753_BCLK_DIV_8 (3 << 3)
-+#define WM8753_BCLK_DIV_16 (4 << 3)
-+
-+/* VXCLK clock dividers */
-+#define WM8753_VXCLK_DIV_1 (0 << 6)
-+#define WM8753_VXCLK_DIV_2 (1 << 6)
-+#define WM8753_VXCLK_DIV_4 (2 << 6)
-+#define WM8753_VXCLK_DIV_8 (3 << 6)
-+#define WM8753_VXCLK_DIV_16 (4 << 6)
-+
-+#define WM8753_DAI_HIFI 0
-+#define WM8753_DAI_VOICE 1
-+
-+extern struct snd_soc_codec_dai wm8753_dai[2];
-+extern struct snd_soc_codec_device soc_codec_dev_wm8753;
-+
-+#endif
-Index: linux-2.6.21-moko/sound/soc/codecs/wm8772.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/codecs/wm8772.c
-@@ -0,0 +1,603 @@
-+/*
-+ * wm8772.c -- WM8772 ALSA Soc Audio driver
-+ *
-+ * Copyright 2005 Wolfson Microelectronics PLC.
-+ * Author: Liam Girdwood
-+ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or (at your
-+ * option) any later version.
-+ *
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/moduleparam.h>
-+#include <linux/version.h>
-+#include <linux/kernel.h>
-+#include <linux/init.h>
-+#include <linux/delay.h>
-+#include <linux/pm.h>
-+#include <linux/i2c.h>
-+
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/pcm_params.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+#include <sound/initval.h>
-+
-+#include "wm8772.h"
-+
-+#define AUDIO_NAME "WM8772"
-+#define WM8772_VERSION "0.4"
-+
-+/* codec private data */
-+struct wm8772_priv {
-+ unsigned int adcclk;
-+ unsigned int dacclk;
-+};
-+
-+/*
-+ * wm8772 register cache
-+ * We can't read the WM8772 register space when we
-+ * are using 2 wire for device control, so we cache them instead.
-+ */
-+static const u16 wm8772_reg[] = {
-+ 0x00ff, 0x00ff, 0x0120, 0x0000, /* 0 */
-+ 0x00ff, 0x00ff, 0x00ff, 0x00ff, /* 4 */
-+ 0x00ff, 0x0000, 0x0080, 0x0040, /* 8 */
-+ 0x0000
-+};
-+
-+/*
-+ * read wm8772 register cache
-+ */
-+static inline unsigned int wm8772_read_reg_cache(struct snd_soc_codec * codec,
-+ unsigned int reg)
-+{
-+ u16 *cache = codec->reg_cache;
-+ if (reg > WM8772_CACHE_REGNUM)
-+ return -1;
-+ return cache[reg];
-+}
-+
-+/*
-+ * write wm8772 register cache
-+ */
-+static inline void wm8772_write_reg_cache(struct snd_soc_codec * codec,
-+ unsigned int reg, unsigned int value)
-+{
-+ u16 *cache = codec->reg_cache;
-+ if (reg > WM8772_CACHE_REGNUM)
-+ return;
-+ cache[reg] = value;
-+}
-+
-+static int wm8772_write(struct snd_soc_codec * codec, unsigned int reg,
-+ unsigned int value)
-+{
-+ u8 data[2];
-+
-+ /* data is
-+ * D15..D9 WM8772 register offset
-+ * D8...D0 register data
-+ */
-+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
-+ data[1] = value & 0x00ff;
-+
-+ wm8772_write_reg_cache (codec, reg, value);
-+ if (codec->hw_write(codec->control_data, data, 2) == 2)
-+ return 0;
-+ else
-+ return -1;
-+}
-+
-+#define wm8772_reset(c) wm8772_write(c, WM8772_RESET, 0)
-+
-+/*
-+ * WM8772 Controls
-+ */
-+static const char *wm8772_zero_flag[] = {"All Ch", "Ch 1", "Ch 2", "Ch3"};
-+
-+static const struct soc_enum wm8772_enum[] = {
-+SOC_ENUM_SINGLE(WM8772_DACCTRL, 0, 4, wm8772_zero_flag),
-+};
-+
-+static const struct snd_kcontrol_new wm8772_snd_controls[] = {
-+
-+SOC_SINGLE("Left1 Playback Volume", WM8772_LDAC1VOL, 0, 255, 0),
-+SOC_SINGLE("Left2 Playback Volume", WM8772_LDAC2VOL, 0, 255, 0),
-+SOC_SINGLE("Left3 Playback Volume", WM8772_LDAC3VOL, 0, 255, 0),
-+SOC_SINGLE("Right1 Playback Volume", WM8772_RDAC1VOL, 0, 255, 0),
-+SOC_SINGLE("Right1 Playback Volume", WM8772_RDAC2VOL, 0, 255, 0),
-+SOC_SINGLE("Right1 Playback Volume", WM8772_RDAC3VOL, 0, 255, 0),
-+SOC_SINGLE("Master Playback Volume", WM8772_MDACVOL, 0, 255, 0),
-+
-+SOC_SINGLE("Playback Switch", WM8772_DACCH, 0, 1, 0),
-+SOC_SINGLE("Capture Switch", WM8772_ADCCTRL, 2, 1, 0),
-+
-+SOC_SINGLE("Demp1 Playback Switch", WM8772_DACCTRL, 6, 1, 0),
-+SOC_SINGLE("Demp2 Playback Switch", WM8772_DACCTRL, 7, 1, 0),
-+SOC_SINGLE("Demp3 Playback Switch", WM8772_DACCTRL, 8, 1, 0),
-+
-+SOC_SINGLE("Phase Invert 1 Switch", WM8772_IFACE, 6, 1, 0),
-+SOC_SINGLE("Phase Invert 2 Switch", WM8772_IFACE, 7, 1, 0),
-+SOC_SINGLE("Phase Invert 3 Switch", WM8772_IFACE, 8, 1, 0),
-+
-+SOC_SINGLE("Playback ZC Switch", WM8772_DACCTRL, 0, 1, 0),
-+
-+SOC_SINGLE("Capture High Pass Switch", WM8772_ADCCTRL, 3, 1, 0),
-+};
-+
-+/* add non dapm controls */
-+static int wm8772_add_controls(struct snd_soc_codec *codec)
-+{
-+ int err, i;
-+
-+ for (i = 0; i < ARRAY_SIZE(wm8772_snd_controls); i++) {
-+ err = snd_ctl_add(codec->card,
-+ snd_soc_cnew(&wm8772_snd_controls[i],codec, NULL));
-+ if (err < 0)
-+ return err;
-+ }
-+ return 0;
-+}
-+
-+static int wm8772_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
-+ int clk_id, unsigned int freq, int dir)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ struct wm8772_priv *wm8772 = codec->private_data;
-+
-+ switch (freq) {
-+ case 4096000:
-+ case 5644800:
-+ case 6144000:
-+ case 8192000:
-+ case 8467000:
-+ case 9216000:
-+ case 11289600:
-+ case 12000000:
-+ case 12288000:
-+ case 16934400:
-+ case 18432000:
-+ case 22579200:
-+ case 24576000:
-+ case 33868800:
-+ case 36864000:
-+ if (clk_id == WM8772_DACCLK) {
-+ wm8772->dacclk = freq;
-+ return 0;
-+ } else if (clk_id == WM8772_ADCCLK) {
-+ wm8772->adcclk = freq;
-+ return 0;
-+ }
-+ }
-+ return -EINVAL;
-+}
-+
-+static int wm8772_set_dac_dai_fmt(struct snd_soc_codec_dai *codec_dai,
-+ unsigned int fmt)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 diface = wm8772_read_reg_cache(codec, WM8772_IFACE) & 0x1f0;
-+ u16 diface_ctrl = wm8772_read_reg_cache(codec, WM8772_DACRATE) & 0x1ef;
-+
-+ /* set master/slave audio interface */
-+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
-+ case SND_SOC_DAIFMT_CBM_CFM:
-+ diface_ctrl |= 0x0010;
-+ break;
-+ case SND_SOC_DAIFMT_CBS_CFS:
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ /* interface format */
-+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
-+ case SND_SOC_DAIFMT_I2S:
-+ diface |= 0x0002;
-+ break;
-+ case SND_SOC_DAIFMT_RIGHT_J:
-+ break;
-+ case SND_SOC_DAIFMT_LEFT_J:
-+ diface |= 0x0001;
-+ break;
-+ case SND_SOC_DAIFMT_DSP_A:
-+ diface |= 0x0003;
-+ break;
-+ case SND_SOC_DAIFMT_DSP_B:
-+ diface |= 0x0007;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ /* clock inversion */
-+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
-+ case SND_SOC_DAIFMT_NB_NF:
-+ break;
-+ case SND_SOC_DAIFMT_IB_NF:
-+ diface |= 0x0008;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ wm8772_write(codec, WM8772_DACRATE, diface_ctrl);
-+ wm8772_write(codec, WM8772_IFACE, diface);
-+ return 0;
-+}
-+
-+static int wm8772_set_adc_dai_fmt(struct snd_soc_codec_dai *codec_dai,
-+ unsigned int fmt)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 aiface = 0;
-+ u16 aiface_ctrl = wm8772_read_reg_cache(codec, WM8772_ADCCTRL) & 0x1cf;
-+
-+ /* set master/slave audio interface */
-+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
-+ case SND_SOC_DAIFMT_CBM_CFM:
-+ aiface |= 0x0010;
-+ break;
-+ case SND_SOC_DAIFMT_CBS_CFS:
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ /* interface format */
-+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
-+ case SND_SOC_DAIFMT_I2S:
-+ aiface |= 0x0002;
-+ break;
-+ case SND_SOC_DAIFMT_RIGHT_J:
-+ break;
-+ case SND_SOC_DAIFMT_LEFT_J:
-+ aiface |= 0x0001;
-+ break;
-+ case SND_SOC_DAIFMT_DSP_A:
-+ aiface |= 0x0003;
-+ break;
-+ case SND_SOC_DAIFMT_DSP_B:
-+ aiface |= 0x0003;
-+ aiface_ctrl |= 0x0010;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ /* clock inversion */
-+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
-+ case SND_SOC_DAIFMT_NB_NF:
-+ break;
-+ case SND_SOC_DAIFMT_IB_NF:
-+ aiface_ctrl |= 0x0020;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ wm8772_write(codec, WM8772_ADCCTRL, aiface_ctrl);
-+ wm8772_write(codec, WM8772_ADCRATE, aiface);
-+ return 0;
-+}
-+
-+static int wm8772_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_device *socdev = rtd->socdev;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ struct wm8772_priv *wm8772 = codec->private_data;
-+
-+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-+
-+ u16 diface = wm8772_read_reg_cache(codec, WM8772_IFACE) & 0x1cf;
-+ u16 diface_ctrl = wm8772_read_reg_cache(codec, WM8772_DACRATE) & 0x3f;
-+
-+ /* bit size */
-+ switch (params_format(params)) {
-+ case SNDRV_PCM_FORMAT_S16_LE:
-+ break;
-+ case SNDRV_PCM_FORMAT_S20_3LE:
-+ diface |= 0x0010;
-+ break;
-+ case SNDRV_PCM_FORMAT_S24_3LE:
-+ diface |= 0x0020;
-+ break;
-+ case SNDRV_PCM_FORMAT_S32_LE:
-+ diface |= 0x0030;
-+ break;
-+ }
-+
-+ /* set rate */
-+ switch (wm8772->dacclk / params_rate(params)) {
-+ case 768:
-+ diface_ctrl |= (0x5 << 6);
-+ break;
-+ case 512:
-+ diface_ctrl |= (0x4 << 6);
-+ break;
-+ case 384:
-+ diface_ctrl |= (0x3 << 6);
-+ break;
-+ case 256:
-+ diface_ctrl |= (0x2 << 6);
-+ break;
-+ case 192:
-+ diface_ctrl |= (0x1 << 6);
-+ break;
-+ }
-+
-+ wm8772_write(codec, WM8772_DACRATE, diface_ctrl);
-+ wm8772_write(codec, WM8772_IFACE, diface);
-+
-+ } else {
-+
-+ u16 aiface = wm8772_read_reg_cache(codec, WM8772_ADCRATE) & 0x113;
-+
-+ /* bit size */
-+ switch (params_format(params)) {
-+ case SNDRV_PCM_FORMAT_S16_LE:
-+ break;
-+ case SNDRV_PCM_FORMAT_S20_3LE:
-+ aiface |= 0x0004;
-+ break;
-+ case SNDRV_PCM_FORMAT_S24_LE:
-+ aiface |= 0x0008;
-+ break;
-+ case SNDRV_PCM_FORMAT_S32_LE:
-+ aiface |= 0x000c;
-+ break;
-+ }
-+
-+ /* set rate */
-+ switch (wm8772->adcclk / params_rate(params)) {
-+ case 768:
-+ aiface |= (0x5 << 5);
-+ break;
-+ case 512:
-+ aiface |= (0x4 << 5);
-+ break;
-+ case 384:
-+ aiface |= (0x3 << 5);
-+ break;
-+ case 256:
-+ aiface |= (0x2 << 5);
-+ break;
-+ }
-+
-+ wm8772_write(codec, WM8772_ADCRATE, aiface);
-+ }
-+
-+ return 0;
-+}
-+
-+static int wm8772_dapm_event(struct snd_soc_codec *codec, int event)
-+{
-+ u16 master = wm8772_read_reg_cache(codec, WM8772_DACRATE) & 0xffe0;
-+
-+ switch (event) {
-+ case SNDRV_CTL_POWER_D0: /* full On */
-+ /* vref/mid, clk and osc on, dac unmute, active */
-+ wm8772_write(codec, WM8772_DACRATE, master);
-+ break;
-+ case SNDRV_CTL_POWER_D1: /* partial On */
-+ case SNDRV_CTL_POWER_D2: /* partial On */
-+ break;
-+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
-+ /* everything off except vref/vmid, dac mute, inactive */
-+ wm8772_write(codec, WM8772_DACRATE, master | 0x0f);
-+ break;
-+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
-+ /* everything off, dac mute, inactive */
-+ wm8772_write(codec, WM8772_DACRATE, master | 0x1f);
-+ break;
-+ }
-+ codec->dapm_state = event;
-+ return 0;
-+}
-+
-+struct snd_soc_codec_dai wm8772_dai[] = {
-+{
-+ .name = "WM8772",
-+ .playback = {
-+ .stream_name = "Playback",
-+ .channels_min = 2,
-+ .channels_max = 6,
-+ },
-+ .ops = {
-+ .hw_params = wm8772_hw_params,
-+ },
-+ .dai_ops = {
-+ .set_fmt = wm8772_set_dac_dai_fmt,
-+ .set_sysclk = wm8772_set_dai_sysclk,
-+ },
-+},
-+{
-+ .name = "WM8772",
-+ .capture = {
-+ .stream_name = "Capture",
-+ .channels_min = 2,
-+ .channels_max = 2,
-+ },
-+ .ops = {
-+ .hw_params = wm8772_hw_params,
-+ },
-+ .dai_ops = {
-+ .set_fmt = wm8772_set_adc_dai_fmt,
-+ .set_sysclk = wm8772_set_dai_sysclk,
-+ },
-+},
-+};
-+EXPORT_SYMBOL_GPL(wm8772_dai);
-+
-+static int wm8772_suspend(struct platform_device *pdev, pm_message_t state)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+
-+ wm8772_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
-+ return 0;
-+}
-+
-+static int wm8772_resume(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+ int i;
-+ u8 data[2];
-+ u16 *cache = codec->reg_cache;
-+
-+ /* Sync reg_cache with the hardware */
-+ for (i = 0; i < ARRAY_SIZE(wm8772_reg); i++) {
-+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
-+ data[1] = cache[i] & 0x00ff;
-+ codec->hw_write(codec->control_data, data, 2);
-+ }
-+ wm8772_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
-+ wm8772_dapm_event(codec, codec->suspend_dapm_state);
-+ return 0;
-+}
-+
-+/*
-+ * initialise the WM8772 driver
-+ * register the mixer and dsp interfaces with the kernel
-+ */
-+static int wm8772_init(struct snd_soc_device *socdev)
-+{
-+ struct snd_soc_codec *codec = socdev->codec;
-+ int reg, ret = 0;
-+
-+ codec->name = "WM8772";
-+ codec->owner = THIS_MODULE;
-+ codec->read = wm8772_read_reg_cache;
-+ codec->write = wm8772_write;
-+ codec->dapm_event = wm8772_dapm_event;
-+ codec->dai = wm8772_dai;
-+ codec->num_dai = 1;
-+ codec->reg_cache_size = ARRAY_SIZE(wm8772_reg);
-+ codec->reg_cache =
-+ kzalloc(sizeof(u16) * ARRAY_SIZE(wm8772_reg), GFP_KERNEL);
-+ if (codec->reg_cache == NULL)
-+ return -ENOMEM;
-+ memcpy(codec->reg_cache, wm8772_reg,
-+ sizeof(u16) * ARRAY_SIZE(wm8772_reg));
-+ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8772_reg);
-+
-+ wm8772_reset(codec);
-+
-+ /* register pcms */
-+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
-+ if(ret < 0) {
-+ printk(KERN_ERR "wm8772: failed to create pcms\n");
-+ goto pcm_err;
-+ }
-+
-+ /* power on device */
-+ wm8772_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
-+
-+ /* set the update bits */
-+ reg = wm8772_read_reg_cache(codec, WM8772_MDACVOL);
-+ wm8772_write(codec, WM8772_MDACVOL, reg | 0x0100);
-+ reg = wm8772_read_reg_cache(codec, WM8772_LDAC1VOL);
-+ wm8772_write(codec, WM8772_LDAC1VOL, reg | 0x0100);
-+ reg = wm8772_read_reg_cache(codec, WM8772_LDAC2VOL);
-+ wm8772_write(codec, WM8772_LDAC2VOL, reg | 0x0100);
-+ reg = wm8772_read_reg_cache(codec, WM8772_LDAC3VOL);
-+ wm8772_write(codec, WM8772_LDAC3VOL, reg | 0x0100);
-+ reg = wm8772_read_reg_cache(codec, WM8772_RDAC1VOL);
-+ wm8772_write(codec, WM8772_RDAC1VOL, reg | 0x0100);
-+ reg = wm8772_read_reg_cache(codec, WM8772_RDAC2VOL);
-+ wm8772_write(codec, WM8772_RDAC2VOL, reg | 0x0100);
-+ reg = wm8772_read_reg_cache(codec, WM8772_RDAC3VOL);
-+ wm8772_write(codec, WM8772_RDAC3VOL, reg | 0x0100);
-+
-+ wm8772_add_controls(codec);
-+ ret = snd_soc_register_card(socdev);
-+ if (ret < 0) {
-+ printk(KERN_ERR "wm8772: failed to register card\n");
-+ goto card_err;
-+ }
-+ return ret;
-+
-+card_err:
-+ snd_soc_free_pcms(socdev);
-+ snd_soc_dapm_free(socdev);
-+pcm_err:
-+ kfree(codec->reg_cache);
-+ return ret;
-+}
-+
-+static struct snd_soc_device *wm8772_socdev;
-+
-+static int wm8772_probe(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct wm8772_setup_data *setup;
-+ struct snd_soc_codec *codec;
-+ struct wm8772_priv *wm8772;
-+ int ret = 0;
-+
-+ printk(KERN_INFO "WM8772 Audio Codec %s", WM8772_VERSION);
-+
-+ setup = socdev->codec_data;
-+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
-+ if (codec == NULL)
-+ return -ENOMEM;
-+
-+ wm8772 = kzalloc(sizeof(struct wm8772_priv), GFP_KERNEL);
-+ if (wm8772 == NULL) {
-+ kfree(codec);
-+ return -ENOMEM;
-+ }
-+
-+ codec->private_data = wm8772;
-+ socdev->codec = codec;
-+ mutex_init(&codec->mutex);
-+ INIT_LIST_HEAD(&codec->dapm_widgets);
-+ INIT_LIST_HEAD(&codec->dapm_paths);
-+
-+ wm8772_socdev = socdev;
-+
-+ /* Add other interfaces here */
-+#warning do SPI device probe here and then call wm8772_init()
-+
-+ return ret;
-+}
-+
-+/* power down chip */
-+static int wm8772_remove(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+
-+ if (codec->control_data)
-+ wm8772_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
-+
-+ snd_soc_free_pcms(socdev);
-+ kfree(codec->private_data);
-+ kfree(codec->reg_cache);
-+ kfree(codec);
-+
-+ return 0;
-+}
-+
-+struct snd_soc_codec_device soc_codec_dev_wm8772 = {
-+ .probe = wm8772_probe,
-+ .remove = wm8772_remove,
-+ .suspend = wm8772_suspend,
-+ .resume = wm8772_resume,
-+};
-+
-+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8772);
-+
-+MODULE_DESCRIPTION("ASoC WM8772 driver");
-+MODULE_AUTHOR("Liam Girdwood");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/codecs/wm8772.h
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/codecs/wm8772.h
-@@ -0,0 +1,46 @@
-+/*
-+ * wm8772.h -- audio driver for WM8772
-+ *
-+ * Copyright 2005 Wolfson Microelectronics PLC.
-+ * Author: Liam Girdwood
-+ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or (at your
-+ * option) any later version.
-+ *
-+ */
-+
-+#ifndef _WM8772_H
-+#define _WM8772_H
-+
-+/* WM8772 register space */
-+
-+#define WM8772_LDAC1VOL 0x00
-+#define WM8772_RDAC1VOL 0x01
-+#define WM8772_DACCH 0x02
-+#define WM8772_IFACE 0x03
-+#define WM8772_LDAC2VOL 0x04
-+#define WM8772_RDAC2VOL 0x05
-+#define WM8772_LDAC3VOL 0x06
-+#define WM8772_RDAC3VOL 0x07
-+#define WM8772_MDACVOL 0x08
-+#define WM8772_DACCTRL 0x09
-+#define WM8772_DACRATE 0x0a
-+#define WM8772_ADCRATE 0x0b
-+#define WM8772_ADCCTRL 0x0c
-+#define WM8772_RESET 0x1f
-+
-+#define WM8772_CACHE_REGNUM 10
-+
-+#define WM8772_DACCLK 0
-+#define WM8772_ADCCLK 1
-+
-+#define WM8753_DAI_DAC 0
-+#define WM8753_DAI_ADC 1
-+
-+extern struct snd_soc_codec_dai wm8772_dai[2];
-+extern struct snd_soc_codec_device soc_codec_dev_wm8772;
-+
-+#endif
-Index: linux-2.6.21-moko/sound/soc/codecs/wm8971.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/codecs/wm8971.c
-@@ -0,0 +1,971 @@
-+/*
-+ * wm8971.c -- WM8971 ALSA SoC Audio driver
-+ *
-+ * Copyright 2005 Lab126, Inc.
-+ *
-+ * Author: Kenneth Kiraly <kiraly at lab126.com>
-+ *
-+ * Based on wm8753.c by Liam Girdwood
-+ *
-+ * 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.
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/moduleparam.h>
-+#include <linux/init.h>
-+#include <linux/delay.h>
-+#include <linux/pm.h>
-+#include <linux/i2c.h>
-+#include <linux/platform_device.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/pcm_params.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+#include <sound/initval.h>
-+
-+#include "wm8971.h"
-+
-+#define AUDIO_NAME "wm8971"
-+#define WM8971_VERSION "0.9"
-+
-+#undef WM8971_DEBUG
-+
-+#ifdef WM8971_DEBUG
-+#define dbg(format, arg...) \
-+ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
-+#else
-+#define dbg(format, arg...) do {} while (0)
-+#endif
-+#define err(format, arg...) \
-+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
-+#define info(format, arg...) \
-+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
-+#define warn(format, arg...) \
-+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
-+
-+#define WM8971_REG_COUNT 43
-+
-+static struct workqueue_struct *wm8971_workq = NULL;
-+
-+/* codec private data */
-+struct wm8971_priv {
-+ unsigned int sysclk;
-+};
-+
-+/*
-+ * wm8971 register cache
-+ * We can't read the WM8971 register space when we
-+ * are using 2 wire for device control, so we cache them instead.
-+ */
-+static const u16 wm8971_reg[] = {
-+ 0x0097, 0x0097, 0x0079, 0x0079, /* 0 */
-+ 0x0000, 0x0008, 0x0000, 0x000a, /* 4 */
-+ 0x0000, 0x0000, 0x00ff, 0x00ff, /* 8 */
-+ 0x000f, 0x000f, 0x0000, 0x0000, /* 12 */
-+ 0x0000, 0x007b, 0x0000, 0x0032, /* 16 */
-+ 0x0000, 0x00c3, 0x00c3, 0x00c0, /* 20 */
-+ 0x0000, 0x0000, 0x0000, 0x0000, /* 24 */
-+ 0x0000, 0x0000, 0x0000, 0x0000, /* 28 */
-+ 0x0000, 0x0000, 0x0050, 0x0050, /* 32 */
-+ 0x0050, 0x0050, 0x0050, 0x0050, /* 36 */
-+ 0x0079, 0x0079, 0x0079, /* 40 */
-+};
-+
-+static inline unsigned int wm8971_read_reg_cache(struct snd_soc_codec *codec,
-+ unsigned int reg)
-+{
-+ u16 *cache = codec->reg_cache;
-+ if (reg < WM8971_REG_COUNT)
-+ return cache[reg];
-+
-+ return -1;
-+}
-+
-+static inline void wm8971_write_reg_cache(struct snd_soc_codec *codec,
-+ unsigned int reg, unsigned int value)
-+{
-+ u16 *cache = codec->reg_cache;
-+ if (reg < WM8971_REG_COUNT)
-+ cache[reg] = value;
-+}
-+
-+static int wm8971_write(struct snd_soc_codec *codec, unsigned int reg,
-+ unsigned int value)
-+{
-+ u8 data[2];
-+
-+ /* data is
-+ * D15..D9 WM8753 register offset
-+ * D8...D0 register data
-+ */
-+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
-+ data[1] = value & 0x00ff;
-+
-+ wm8971_write_reg_cache (codec, reg, value);
-+ if (codec->hw_write(codec->control_data, data, 2) == 2)
-+ return 0;
-+ else
-+ return -EIO;
-+}
-+
-+#define wm8971_reset(c) wm8971_write(c, WM8971_RESET, 0)
-+
-+/* WM8971 Controls */
-+static const char *wm8971_bass[] = { "Linear Control", "Adaptive Boost" };
-+static const char *wm8971_bass_filter[] = { "130Hz @ 48kHz",
-+ "200Hz @ 48kHz" };
-+static const char *wm8971_treble[] = { "8kHz", "4kHz" };
-+static const char *wm8971_alc_func[] = { "Off", "Right", "Left", "Stereo" };
-+static const char *wm8971_ng_type[] = { "Constant PGA Gain",
-+ "Mute ADC Output" };
-+static const char *wm8971_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" };
-+static const char *wm8971_mono_mux[] = {"Stereo", "Mono (Left)",
-+ "Mono (Right)", "Digital Mono"};
-+static const char *wm8971_dac_phase[] = { "Non Inverted", "Inverted" };
-+static const char *wm8971_lline_mux[] = {"Line", "NC", "NC", "PGA",
-+ "Differential"};
-+static const char *wm8971_rline_mux[] = {"Line", "Mic", "NC", "PGA",
-+ "Differential"};
-+static const char *wm8971_lpga_sel[] = {"Line", "NC", "NC", "Differential"};
-+static const char *wm8971_rpga_sel[] = {"Line", "Mic", "NC", "Differential"};
-+static const char *wm8971_adcpol[] = {"Normal", "L Invert", "R Invert",
-+ "L + R Invert"};
-+
-+static const struct soc_enum wm8971_enum[] = {
-+ SOC_ENUM_SINGLE(WM8971_BASS, 7, 2, wm8971_bass), /* 0 */
-+ SOC_ENUM_SINGLE(WM8971_BASS, 6, 2, wm8971_bass_filter),
-+ SOC_ENUM_SINGLE(WM8971_TREBLE, 6, 2, wm8971_treble),
-+ SOC_ENUM_SINGLE(WM8971_ALC1, 7, 4, wm8971_alc_func),
-+ SOC_ENUM_SINGLE(WM8971_NGATE, 1, 2, wm8971_ng_type), /* 4 */
-+ SOC_ENUM_SINGLE(WM8971_ADCDAC, 1, 4, wm8971_deemp),
-+ SOC_ENUM_SINGLE(WM8971_ADCTL1, 4, 4, wm8971_mono_mux),
-+ SOC_ENUM_SINGLE(WM8971_ADCTL1, 1, 2, wm8971_dac_phase),
-+ SOC_ENUM_SINGLE(WM8971_LOUTM1, 0, 5, wm8971_lline_mux), /* 8 */
-+ SOC_ENUM_SINGLE(WM8971_ROUTM1, 0, 5, wm8971_rline_mux),
-+ SOC_ENUM_SINGLE(WM8971_LADCIN, 6, 4, wm8971_lpga_sel),
-+ SOC_ENUM_SINGLE(WM8971_RADCIN, 6, 4, wm8971_rpga_sel),
-+ SOC_ENUM_SINGLE(WM8971_ADCDAC, 5, 4, wm8971_adcpol), /* 12 */
-+ SOC_ENUM_SINGLE(WM8971_ADCIN, 6, 4, wm8971_mono_mux),
-+};
-+
-+static const struct snd_kcontrol_new wm8971_snd_controls[] = {
-+ SOC_DOUBLE_R("Capture Volume", WM8971_LINVOL, WM8971_RINVOL, 0, 63, 0),
-+ SOC_DOUBLE_R("Capture ZC Switch", WM8971_LINVOL, WM8971_RINVOL, 6, 1, 0),
-+ SOC_DOUBLE_R("Capture Switch", WM8971_LINVOL, WM8971_RINVOL, 7, 1, 1),
-+
-+ SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8971_LOUT1V,
-+ WM8971_ROUT1V, 7, 1, 0),
-+ SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8971_LOUT2V,
-+ WM8971_ROUT2V, 7, 1, 0),
-+ SOC_SINGLE("Mono Playback ZC Switch", WM8971_MOUTV, 7, 1, 0),
-+
-+ SOC_DOUBLE_R("PCM Volume", WM8971_LDAC, WM8971_RDAC, 0, 255, 0),
-+
-+ SOC_DOUBLE_R("Bypass Left Playback Volume", WM8971_LOUTM1,
-+ WM8971_LOUTM2, 4, 7, 1),
-+ SOC_DOUBLE_R("Bypass Right Playback Volume", WM8971_ROUTM1,
-+ WM8971_ROUTM2, 4, 7, 1),
-+ SOC_DOUBLE_R("Bypass Mono Playback Volume", WM8971_MOUTM1,
-+ WM8971_MOUTM2, 4, 7, 1),
-+
-+ SOC_DOUBLE_R("Headphone Playback Volume", WM8971_LOUT1V,
-+ WM8971_ROUT1V, 0, 127, 0),
-+ SOC_DOUBLE_R("Speaker Playback Volume", WM8971_LOUT2V,
-+ WM8971_ROUT2V, 0, 127, 0),
-+
-+ SOC_ENUM("Bass Boost", wm8971_enum[0]),
-+ SOC_ENUM("Bass Filter", wm8971_enum[1]),
-+ SOC_SINGLE("Bass Volume", WM8971_BASS, 0, 7, 1),
-+
-+ SOC_SINGLE("Treble Volume", WM8971_TREBLE, 0, 7, 0),
-+ SOC_ENUM("Treble Cut-off", wm8971_enum[2]),
-+
-+ SOC_SINGLE("Capture Filter Switch", WM8971_ADCDAC, 0, 1, 1),
-+
-+ SOC_SINGLE("ALC Target Volume", WM8971_ALC1, 0, 7, 0),
-+ SOC_SINGLE("ALC Max Volume", WM8971_ALC1, 4, 7, 0),
-+
-+ SOC_SINGLE("ALC Capture Target Volume", WM8971_ALC1, 0, 7, 0),
-+ SOC_SINGLE("ALC Capture Max Volume", WM8971_ALC1, 4, 7, 0),
-+ SOC_ENUM("ALC Capture Function", wm8971_enum[3]),
-+ SOC_SINGLE("ALC Capture ZC Switch", WM8971_ALC2, 7, 1, 0),
-+ SOC_SINGLE("ALC Capture Hold Time", WM8971_ALC2, 0, 15, 0),
-+ SOC_SINGLE("ALC Capture Decay Time", WM8971_ALC3, 4, 15, 0),
-+ SOC_SINGLE("ALC Capture Attack Time", WM8971_ALC3, 0, 15, 0),
-+ SOC_SINGLE("ALC Capture NG Threshold", WM8971_NGATE, 3, 31, 0),
-+ SOC_ENUM("ALC Capture NG Type", wm8971_enum[4]),
-+ SOC_SINGLE("ALC Capture NG Switch", WM8971_NGATE, 0, 1, 0),
-+
-+ SOC_SINGLE("Capture 6dB Attenuate", WM8971_ADCDAC, 8, 1, 0),
-+ SOC_SINGLE("Playback 6dB Attenuate", WM8971_ADCDAC, 7, 1, 0),
-+
-+ SOC_ENUM("Playback De-emphasis", wm8971_enum[5]),
-+ SOC_ENUM("Playback Function", wm8971_enum[6]),
-+ SOC_ENUM("Playback Phase", wm8971_enum[7]),
-+
-+ SOC_DOUBLE_R("Mic Boost", WM8971_LADCIN, WM8971_RADCIN, 4, 3, 0),
-+};
-+
-+/* add non-DAPM controls */
-+static int wm8971_add_controls(struct snd_soc_codec *codec)
-+{
-+ int err, i;
-+
-+ for (i = 0; i < ARRAY_SIZE(wm8971_snd_controls); i++) {
-+ err = snd_ctl_add(codec->card,
-+ snd_soc_cnew(&wm8971_snd_controls[i],codec, NULL));
-+ if (err < 0)
-+ return err;
-+ }
-+ return 0;
-+}
-+
-+/*
-+ * DAPM Controls
-+ */
-+
-+/* Left Mixer */
-+static const struct snd_kcontrol_new wm8971_left_mixer_controls[] = {
-+SOC_DAPM_SINGLE("Playback Switch", WM8971_LOUTM1, 8, 1, 0),
-+SOC_DAPM_SINGLE("Left Bypass Switch", WM8971_LOUTM1, 7, 1, 0),
-+SOC_DAPM_SINGLE("Right Playback Switch", WM8971_LOUTM2, 8, 1, 0),
-+SOC_DAPM_SINGLE("Right Bypass Switch", WM8971_LOUTM2, 7, 1, 0),
-+};
-+
-+/* Right Mixer */
-+static const struct snd_kcontrol_new wm8971_right_mixer_controls[] = {
-+SOC_DAPM_SINGLE("Left Playback Switch", WM8971_ROUTM1, 8, 1, 0),
-+SOC_DAPM_SINGLE("Left Bypass Switch", WM8971_ROUTM1, 7, 1, 0),
-+SOC_DAPM_SINGLE("Playback Switch", WM8971_ROUTM2, 8, 1, 0),
-+SOC_DAPM_SINGLE("Right Bypass Switch", WM8971_ROUTM2, 7, 1, 0),
-+};
-+
-+/* Mono Mixer */
-+static const struct snd_kcontrol_new wm8971_mono_mixer_controls[] = {
-+SOC_DAPM_SINGLE("Left Playback Switch", WM8971_MOUTM1, 8, 1, 0),
-+SOC_DAPM_SINGLE("Left Bypass Switch", WM8971_MOUTM1, 7, 1, 0),
-+SOC_DAPM_SINGLE("Right Playback Switch", WM8971_MOUTM2, 8, 1, 0),
-+SOC_DAPM_SINGLE("Right Bypass Switch", WM8971_MOUTM2, 7, 1, 0),
-+};
-+
-+/* Left Line Mux */
-+static const struct snd_kcontrol_new wm8971_left_line_controls =
-+SOC_DAPM_ENUM("Route", wm8971_enum[8]);
-+
-+/* Right Line Mux */
-+static const struct snd_kcontrol_new wm8971_right_line_controls =
-+SOC_DAPM_ENUM("Route", wm8971_enum[9]);
-+
-+/* Left PGA Mux */
-+static const struct snd_kcontrol_new wm8971_left_pga_controls =
-+SOC_DAPM_ENUM("Route", wm8971_enum[10]);
-+
-+/* Right PGA Mux */
-+static const struct snd_kcontrol_new wm8971_right_pga_controls =
-+SOC_DAPM_ENUM("Route", wm8971_enum[11]);
-+
-+/* Mono ADC Mux */
-+static const struct snd_kcontrol_new wm8971_monomux_controls =
-+SOC_DAPM_ENUM("Route", wm8971_enum[13]);
-+
-+static const struct snd_soc_dapm_widget wm8971_dapm_widgets[] = {
-+ SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
-+ &wm8971_left_mixer_controls[0],
-+ ARRAY_SIZE(wm8971_left_mixer_controls)),
-+ SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
-+ &wm8971_right_mixer_controls[0],
-+ ARRAY_SIZE(wm8971_right_mixer_controls)),
-+ SND_SOC_DAPM_MIXER("Mono Mixer", WM8971_PWR2, 2, 0,
-+ &wm8971_mono_mixer_controls[0],
-+ ARRAY_SIZE(wm8971_mono_mixer_controls)),
-+
-+ SND_SOC_DAPM_PGA("Right Out 2", WM8971_PWR2, 3, 0, NULL, 0),
-+ SND_SOC_DAPM_PGA("Left Out 2", WM8971_PWR2, 4, 0, NULL, 0),
-+ SND_SOC_DAPM_PGA("Right Out 1", WM8971_PWR2, 5, 0, NULL, 0),
-+ SND_SOC_DAPM_PGA("Left Out 1", WM8971_PWR2, 6, 0, NULL, 0),
-+ SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8971_PWR2, 7, 0),
-+ SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8971_PWR2, 8, 0),
-+ SND_SOC_DAPM_PGA("Mono Out 1", WM8971_PWR2, 2, 0, NULL, 0),
-+
-+ SND_SOC_DAPM_MICBIAS("Mic Bias", WM8971_PWR1, 1, 0),
-+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8971_PWR1, 2, 0),
-+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8971_PWR1, 3, 0),
-+
-+ SND_SOC_DAPM_MUX("Left PGA Mux", WM8971_PWR1, 5, 0,
-+ &wm8971_left_pga_controls),
-+ SND_SOC_DAPM_MUX("Right PGA Mux", WM8971_PWR1, 4, 0,
-+ &wm8971_right_pga_controls),
-+ SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0,
-+ &wm8971_left_line_controls),
-+ SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0,
-+ &wm8971_right_line_controls),
-+
-+ SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0,
-+ &wm8971_monomux_controls),
-+ SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0,
-+ &wm8971_monomux_controls),
-+
-+ SND_SOC_DAPM_OUTPUT("LOUT1"),
-+ SND_SOC_DAPM_OUTPUT("ROUT1"),
-+ SND_SOC_DAPM_OUTPUT("LOUT2"),
-+ SND_SOC_DAPM_OUTPUT("ROUT2"),
-+ SND_SOC_DAPM_OUTPUT("MONO"),
-+
-+ SND_SOC_DAPM_INPUT("LINPUT1"),
-+ SND_SOC_DAPM_INPUT("RINPUT1"),
-+ SND_SOC_DAPM_INPUT("MIC"),
-+};
-+
-+static const char *audio_map[][3] = {
-+ /* left mixer */
-+ {"Left Mixer", "Playback Switch", "Left DAC"},
-+ {"Left Mixer", "Left Bypass Switch", "Left Line Mux"},
-+ {"Left Mixer", "Right Playback Switch", "Right DAC"},
-+ {"Left Mixer", "Right Bypass Switch", "Right Line Mux"},
-+
-+ /* right mixer */
-+ {"Right Mixer", "Left Playback Switch", "Left DAC"},
-+ {"Right Mixer", "Left Bypass Switch", "Left Line Mux"},
-+ {"Right Mixer", "Playback Switch", "Right DAC"},
-+ {"Right Mixer", "Right Bypass Switch", "Right Line Mux"},
-+
-+ /* left out 1 */
-+ {"Left Out 1", NULL, "Left Mixer"},
-+ {"LOUT1", NULL, "Left Out 1"},
-+
-+ /* left out 2 */
-+ {"Left Out 2", NULL, "Left Mixer"},
-+ {"LOUT2", NULL, "Left Out 2"},
-+
-+ /* right out 1 */
-+ {"Right Out 1", NULL, "Right Mixer"},
-+ {"ROUT1", NULL, "Right Out 1"},
-+
-+ /* right out 2 */
-+ {"Right Out 2", NULL, "Right Mixer"},
-+ {"ROUT2", NULL, "Right Out 2"},
-+
-+ /* mono mixer */
-+ {"Mono Mixer", "Left Playback Switch", "Left DAC"},
-+ {"Mono Mixer", "Left Bypass Switch", "Left Line Mux"},
-+ {"Mono Mixer", "Right Playback Switch", "Right DAC"},
-+ {"Mono Mixer", "Right Bypass Switch", "Right Line Mux"},
-+
-+ /* mono out */
-+ {"Mono Out", NULL, "Mono Mixer"},
-+ {"MONO1", NULL, "Mono Out"},
-+
-+ /* Left Line Mux */
-+ {"Left Line Mux", "Line", "LINPUT1"},
-+ {"Left Line Mux", "PGA", "Left PGA Mux"},
-+ {"Left Line Mux", "Differential", "Differential Mux"},
-+
-+ /* Right Line Mux */
-+ {"Right Line Mux", "Line", "RINPUT1"},
-+ {"Right Line Mux", "Mic", "MIC"},
-+ {"Right Line Mux", "PGA", "Right PGA Mux"},
-+ {"Right Line Mux", "Differential", "Differential Mux"},
-+
-+ /* Left PGA Mux */
-+ {"Left PGA Mux", "Line", "LINPUT1"},
-+ {"Left PGA Mux", "Differential", "Differential Mux"},
-+
-+ /* Right PGA Mux */
-+ {"Right PGA Mux", "Line", "RINPUT1"},
-+ {"Right PGA Mux", "Differential", "Differential Mux"},
-+
-+ /* Differential Mux */
-+ {"Differential Mux", "Line", "LINPUT1"},
-+ {"Differential Mux", "Line", "RINPUT1"},
-+
-+ /* Left ADC Mux */
-+ {"Left ADC Mux", "Stereo", "Left PGA Mux"},
-+ {"Left ADC Mux", "Mono (Left)", "Left PGA Mux"},
-+ {"Left ADC Mux", "Digital Mono", "Left PGA Mux"},
-+
-+ /* Right ADC Mux */
-+ {"Right ADC Mux", "Stereo", "Right PGA Mux"},
-+ {"Right ADC Mux", "Mono (Right)", "Right PGA Mux"},
-+ {"Right ADC Mux", "Digital Mono", "Right PGA Mux"},
-+
-+ /* ADC */
-+ {"Left ADC", NULL, "Left ADC Mux"},
-+ {"Right ADC", NULL, "Right ADC Mux"},
-+
-+ /* terminator */
-+ {NULL, NULL, NULL},
-+};
-+
-+static int wm8971_add_widgets(struct snd_soc_codec *codec)
-+{
-+ int i;
-+
-+ for(i = 0; i < ARRAY_SIZE(wm8971_dapm_widgets); i++) {
-+ snd_soc_dapm_new_control(codec, &wm8971_dapm_widgets[i]);
-+ }
-+
-+ /* set up audio path audio_mapnects */
-+ for(i = 0; audio_map[i][0] != NULL; i++) {
-+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
-+ audio_map[i][1], audio_map[i][2]);
-+ }
-+
-+ snd_soc_dapm_new_widgets(codec);
-+ return 0;
-+}
-+
-+struct _coeff_div {
-+ u32 mclk;
-+ u32 rate;
-+ u16 fs;
-+ u8 sr:5;
-+ u8 usb:1;
-+};
-+
-+/* codec hifi mclk clock divider coefficients */
-+static const struct _coeff_div coeff_div[] = {
-+ /* 8k */
-+ {12288000, 8000, 1536, 0x6, 0x0},
-+ {11289600, 8000, 1408, 0x16, 0x0},
-+ {18432000, 8000, 2304, 0x7, 0x0},
-+ {16934400, 8000, 2112, 0x17, 0x0},
-+ {12000000, 8000, 1500, 0x6, 0x1},
-+
-+ /* 11.025k */
-+ {11289600, 11025, 1024, 0x18, 0x0},
-+ {16934400, 11025, 1536, 0x19, 0x0},
-+ {12000000, 11025, 1088, 0x19, 0x1},
-+
-+ /* 16k */
-+ {12288000, 16000, 768, 0xa, 0x0},
-+ {18432000, 16000, 1152, 0xb, 0x0},
-+ {12000000, 16000, 750, 0xa, 0x1},
-+
-+ /* 22.05k */
-+ {11289600, 22050, 512, 0x1a, 0x0},
-+ {16934400, 22050, 768, 0x1b, 0x0},
-+ {12000000, 22050, 544, 0x1b, 0x1},
-+
-+ /* 32k */
-+ {12288000, 32000, 384, 0xc, 0x0},
-+ {18432000, 32000, 576, 0xd, 0x0},
-+ {12000000, 32000, 375, 0xa, 0x1},
-+
-+ /* 44.1k */
-+ {11289600, 44100, 256, 0x10, 0x0},
-+ {16934400, 44100, 384, 0x11, 0x0},
-+ {12000000, 44100, 272, 0x11, 0x1},
-+
-+ /* 48k */
-+ {12288000, 48000, 256, 0x0, 0x0},
-+ {18432000, 48000, 384, 0x1, 0x0},
-+ {12000000, 48000, 250, 0x0, 0x1},
-+
-+ /* 88.2k */
-+ {11289600, 88200, 128, 0x1e, 0x0},
-+ {16934400, 88200, 192, 0x1f, 0x0},
-+ {12000000, 88200, 136, 0x1f, 0x1},
-+
-+ /* 96k */
-+ {12288000, 96000, 128, 0xe, 0x0},
-+ {18432000, 96000, 192, 0xf, 0x0},
-+ {12000000, 96000, 125, 0xe, 0x1},
-+};
-+
-+static int get_coeff(int mclk, int rate)
-+{
-+ int i;
-+
-+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
-+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
-+ return i;
-+ }
-+ return -EINVAL;
-+}
-+
-+static int wm8971_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
-+ int clk_id, unsigned int freq, int dir)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ struct wm8971_priv *wm8971 = codec->private_data;
-+
-+ switch (freq) {
-+ case 11289600:
-+ case 12000000:
-+ case 12288000:
-+ case 16934400:
-+ case 18432000:
-+ wm8971->sysclk = freq;
-+ return 0;
-+ }
-+ return -EINVAL;
-+}
-+
-+static int wm8971_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
-+ unsigned int fmt)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 iface = 0;
-+
-+ /* set master/slave audio interface */
-+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
-+ case SND_SOC_DAIFMT_CBM_CFM:
-+ iface = 0x0040;
-+ break;
-+ case SND_SOC_DAIFMT_CBS_CFS:
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ /* interface format */
-+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
-+ case SND_SOC_DAIFMT_I2S:
-+ iface |= 0x0002;
-+ break;
-+ case SND_SOC_DAIFMT_RIGHT_J:
-+ break;
-+ case SND_SOC_DAIFMT_LEFT_J:
-+ iface |= 0x0001;
-+ break;
-+ case SND_SOC_DAIFMT_DSP_A:
-+ iface |= 0x0003;
-+ break;
-+ case SND_SOC_DAIFMT_DSP_B:
-+ iface |= 0x0013;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ /* clock inversion */
-+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
-+ case SND_SOC_DAIFMT_NB_NF:
-+ break;
-+ case SND_SOC_DAIFMT_IB_IF:
-+ iface |= 0x0090;
-+ break;
-+ case SND_SOC_DAIFMT_IB_NF:
-+ iface |= 0x0080;
-+ break;
-+ case SND_SOC_DAIFMT_NB_IF:
-+ iface |= 0x0010;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ wm8971_write(codec, WM8971_IFACE, iface);
-+ return 0;
-+}
-+
-+static int wm8971_pcm_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_device *socdev = rtd->socdev;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ struct wm8971_priv *wm8971 = codec->private_data;
-+ u16 iface = wm8971_read_reg_cache(codec, WM8971_IFACE) & 0x1f3;
-+ u16 srate = wm8971_read_reg_cache(codec, WM8971_SRATE) & 0x1c0;
-+ int coeff = get_coeff(wm8971->sysclk, params_rate(params));
-+
-+ /* bit size */
-+ switch (params_format(params)) {
-+ case SNDRV_PCM_FORMAT_S16_LE:
-+ break;
-+ case SNDRV_PCM_FORMAT_S20_3LE:
-+ iface |= 0x0004;
-+ break;
-+ case SNDRV_PCM_FORMAT_S24_LE:
-+ iface |= 0x0008;
-+ break;
-+ case SNDRV_PCM_FORMAT_S32_LE:
-+ iface |= 0x000c;
-+ break;
-+ }
-+
-+ /* set iface & srate */
-+ wm8971_write(codec, WM8971_IFACE, iface);
-+ if (coeff >= 0)
-+ wm8971_write(codec, WM8971_SRATE, srate |
-+ (coeff_div[coeff].sr << 1) | coeff_div[coeff].usb);
-+
-+ return 0;
-+}
-+
-+static int wm8971_mute(struct snd_soc_codec_dai *dai, int mute)
-+{
-+ struct snd_soc_codec *codec = dai->codec;
-+ u16 mute_reg = wm8971_read_reg_cache(codec, WM8971_ADCDAC) & 0xfff7;
-+
-+ if (mute)
-+ wm8971_write(codec, WM8971_ADCDAC, mute_reg | 0x8);
-+ else
-+ wm8971_write(codec, WM8971_ADCDAC, mute_reg);
-+ return 0;
-+}
-+
-+static int wm8971_dapm_event(struct snd_soc_codec *codec, int event)
-+{
-+ u16 pwr_reg = wm8971_read_reg_cache(codec, WM8971_PWR1) & 0xfe3e;
-+
-+ switch (event) {
-+ case SNDRV_CTL_POWER_D0: /* full On */
-+ /* set vmid to 50k and unmute dac */
-+ wm8971_write(codec, WM8971_PWR1, pwr_reg | 0x00c1);
-+ break;
-+ case SNDRV_CTL_POWER_D1: /* partial On */
-+ case SNDRV_CTL_POWER_D2: /* partial On */
-+ /* set vmid to 5k for quick power up */
-+ wm8971_write(codec, WM8971_PWR1, pwr_reg | 0x01c0);
-+ break;
-+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
-+ /* mute dac and set vmid to 500k, enable VREF */
-+ wm8971_write(codec, WM8971_PWR1, pwr_reg | 0x0140);
-+ break;
-+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
-+ wm8971_write(codec, WM8971_PWR1, 0x0001);
-+ break;
-+ }
-+ codec->dapm_state = event;
-+ return 0;
-+}
-+
-+#define WM8971_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
-+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
-+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
-+
-+#define WM8971_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
-+ SNDRV_PCM_FMTBIT_S24_LE)
-+
-+struct snd_soc_codec_dai wm8971_dai = {
-+ .name = "WM8971",
-+ .playback = {
-+ .stream_name = "Playback",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = WM8971_RATES,
-+ .formats = WM8971_FORMATS,},
-+ .capture = {
-+ .stream_name = "Capture",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = WM8971_RATES,
-+ .formats = WM8971_FORMATS,},
-+ .ops = {
-+ .hw_params = wm8971_pcm_hw_params,
-+ },
-+ .dai_ops = {
-+ .digital_mute = wm8971_mute,
-+ .set_fmt = wm8971_set_dai_fmt,
-+ .set_sysclk = wm8971_set_dai_sysclk,
-+ },
-+};
-+EXPORT_SYMBOL_GPL(wm8971_dai);
-+
-+static void wm8971_work(struct work_struct *work)
-+{
-+ struct snd_soc_codec *codec =
-+ container_of(work, struct snd_soc_codec, delayed_work.work);
-+ wm8971_dapm_event(codec, codec->dapm_state);
-+}
-+
-+static int wm8971_suspend(struct platform_device *pdev, pm_message_t state)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+
-+ wm8971_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
-+ return 0;
-+}
-+
-+static int wm8971_resume(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+ int i;
-+ u8 data[2];
-+ u16 *cache = codec->reg_cache;
-+
-+ /* Sync reg_cache with the hardware */
-+ for (i = 0; i < ARRAY_SIZE(wm8971_reg); i++) {
-+ if (i + 1 == WM8971_RESET)
-+ continue;
-+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
-+ data[1] = cache[i] & 0x00ff;
-+ codec->hw_write(codec->control_data, data, 2);
-+ }
-+
-+ wm8971_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
-+
-+ /* charge wm8971 caps */
-+ if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) {
-+ wm8971_dapm_event(codec, SNDRV_CTL_POWER_D2);
-+ codec->dapm_state = SNDRV_CTL_POWER_D0;
-+ queue_delayed_work(wm8971_workq, &codec->delayed_work,
-+ msecs_to_jiffies(1000));
-+ }
-+
-+ return 0;
-+}
-+
-+static int wm8971_init(struct snd_soc_device *socdev)
-+{
-+ struct snd_soc_codec *codec = socdev->codec;
-+ int reg, ret = 0;
-+
-+ codec->name = "WM8971";
-+ codec->owner = THIS_MODULE;
-+ codec->read = wm8971_read_reg_cache;
-+ codec->write = wm8971_write;
-+ codec->dapm_event = wm8971_dapm_event;
-+ codec->dai = &wm8971_dai;
-+ codec->reg_cache_size = ARRAY_SIZE(wm8971_reg);
-+ codec->num_dai = 1;
-+ codec->reg_cache =
-+ kzalloc(sizeof(u16) * ARRAY_SIZE(wm8971_reg), GFP_KERNEL);
-+ if (codec->reg_cache == NULL)
-+ return -ENOMEM;
-+ memcpy(codec->reg_cache, wm8971_reg,
-+ sizeof(u16) * ARRAY_SIZE(wm8971_reg));
-+ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8971_reg);
-+
-+ wm8971_reset(codec);
-+
-+ /* register pcms */
-+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
-+ if (ret < 0) {
-+ printk(KERN_ERR "wm8971: failed to create pcms\n");
-+ goto pcm_err;
-+ }
-+
-+ /* charge output caps */
-+ wm8971_dapm_event(codec, SNDRV_CTL_POWER_D2);
-+ codec->dapm_state = SNDRV_CTL_POWER_D3hot;
-+ queue_delayed_work(wm8971_workq, &codec->delayed_work,
-+ msecs_to_jiffies(1000));
-+
-+ /* set the update bits */
-+ reg = wm8971_read_reg_cache(codec, WM8971_LDAC);
-+ wm8971_write(codec, WM8971_LDAC, reg | 0x0100);
-+ reg = wm8971_read_reg_cache(codec, WM8971_RDAC);
-+ wm8971_write(codec, WM8971_RDAC, reg | 0x0100);
-+
-+ reg = wm8971_read_reg_cache(codec, WM8971_LOUT1V);
-+ wm8971_write(codec, WM8971_LOUT1V, reg | 0x0100);
-+ reg = wm8971_read_reg_cache(codec, WM8971_ROUT1V);
-+ wm8971_write(codec, WM8971_ROUT1V, reg | 0x0100);
-+
-+ reg = wm8971_read_reg_cache(codec, WM8971_LOUT2V);
-+ wm8971_write(codec, WM8971_LOUT2V, reg | 0x0100);
-+ reg = wm8971_read_reg_cache(codec, WM8971_ROUT2V);
-+ wm8971_write(codec, WM8971_ROUT2V, reg | 0x0100);
-+
-+ reg = wm8971_read_reg_cache(codec, WM8971_LINVOL);
-+ wm8971_write(codec, WM8971_LINVOL, reg | 0x0100);
-+ reg = wm8971_read_reg_cache(codec, WM8971_RINVOL);
-+ wm8971_write(codec, WM8971_RINVOL, reg | 0x0100);
-+
-+ wm8971_add_controls(codec);
-+ wm8971_add_widgets(codec);
-+ ret = snd_soc_register_card(socdev);
-+ if (ret < 0) {
-+ printk(KERN_ERR "wm8971: failed to register card\n");
-+ goto card_err;
-+ }
-+ return ret;
-+
-+card_err:
-+ snd_soc_free_pcms(socdev);
-+ snd_soc_dapm_free(socdev);
-+pcm_err:
-+ kfree(codec->reg_cache);
-+ return ret;
-+}
-+
-+/* If the i2c layer weren't so broken, we could pass this kind of data
-+ around */
-+static struct snd_soc_device *wm8971_socdev;
-+
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+
-+/*
-+ * WM8731 2 wire address is determined by GPIO5
-+ * state during powerup.
-+ * low = 0x1a
-+ * high = 0x1b
-+ */
-+#define I2C_DRIVERID_WM8971 0xfefe /* liam - need a proper id */
-+
-+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
-+
-+/* Magic definition of all other variables and things */
-+I2C_CLIENT_INSMOD;
-+
-+static struct i2c_driver wm8971_i2c_driver;
-+static struct i2c_client client_template;
-+
-+static int wm8971_codec_probe(struct i2c_adapter *adap, int addr, int kind)
-+{
-+ struct snd_soc_device *socdev = wm8971_socdev;
-+ struct wm8971_setup_data *setup = socdev->codec_data;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ struct i2c_client *i2c;
-+ int ret;
-+
-+ if (addr != setup->i2c_address)
-+ return -ENODEV;
-+
-+ client_template.adapter = adap;
-+ client_template.addr = addr;
-+
-+ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
-+ if (i2c == NULL) {
-+ kfree(codec);
-+ return -ENOMEM;
-+ }
-+ memcpy(i2c, &client_template, sizeof(struct i2c_client));
-+
-+ i2c_set_clientdata(i2c, codec);
-+
-+ codec->control_data = i2c;
-+
-+ ret = i2c_attach_client(i2c);
-+ if (ret < 0) {
-+ err("failed to attach codec at addr %x\n", addr);
-+ goto err;
-+ }
-+
-+ ret = wm8971_init(socdev);
-+ if (ret < 0) {
-+ err("failed to initialise WM8971\n");
-+ goto err;
-+ }
-+ return ret;
-+
-+err:
-+ kfree(codec);
-+ kfree(i2c);
-+ return ret;
-+}
-+
-+static int wm8971_i2c_detach(struct i2c_client *client)
-+{
-+ struct snd_soc_codec* codec = i2c_get_clientdata(client);
-+ i2c_detach_client(client);
-+ kfree(codec->reg_cache);
-+ kfree(client);
-+ return 0;
-+}
-+
-+static int wm8971_i2c_attach(struct i2c_adapter *adap)
-+{
-+ return i2c_probe(adap, &addr_data, wm8971_codec_probe);
-+}
-+
-+/* corgi i2c codec control layer */
-+static struct i2c_driver wm8971_i2c_driver = {
-+ .driver = {
-+ .name = "WM8971 I2C Codec",
-+ .owner = THIS_MODULE,
-+ },
-+ .id = I2C_DRIVERID_WM8971,
-+ .attach_adapter = wm8971_i2c_attach,
-+ .detach_client = wm8971_i2c_detach,
-+ .command = NULL,
-+};
-+
-+static struct i2c_client client_template = {
-+ .name = "WM8971",
-+ .driver = &wm8971_i2c_driver,
-+};
-+#endif
-+
-+static int wm8971_probe(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct wm8971_setup_data *setup;
-+ struct snd_soc_codec *codec;
-+ struct wm8971_priv *wm8971;
-+ int ret = 0;
-+
-+ info("WM8971 Audio Codec %s", WM8971_VERSION);
-+
-+ setup = socdev->codec_data;
-+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
-+ if (codec == NULL)
-+ return -ENOMEM;
-+
-+ wm8971 = kzalloc(sizeof(struct wm8971_priv), GFP_KERNEL);
-+ if (wm8971 == NULL) {
-+ kfree(codec);
-+ return -ENOMEM;
-+ }
-+
-+ codec->private_data = wm8971;
-+ socdev->codec = codec;
-+ mutex_init(&codec->mutex);
-+ INIT_LIST_HEAD(&codec->dapm_widgets);
-+ INIT_LIST_HEAD(&codec->dapm_paths);
-+ wm8971_socdev = socdev;
-+
-+ INIT_DELAYED_WORK(&codec->delayed_work, wm8971_work);
-+ wm8971_workq = create_workqueue("wm8971");
-+ if (wm8971_workq == NULL) {
-+ kfree(codec);
-+ return -ENOMEM;
-+ }
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+ if (setup->i2c_address) {
-+ normal_i2c[0] = setup->i2c_address;
-+ codec->hw_write = (hw_write_t)i2c_master_send;
-+ ret = i2c_add_driver(&wm8971_i2c_driver);
-+ if (ret != 0)
-+ printk(KERN_ERR "can't add i2c driver");
-+ }
-+#else
-+ /* Add other interfaces here */
-+#endif
-+
-+ return ret;
-+}
-+
-+/* power down chip */
-+static int wm8971_remove(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+
-+ if (codec->control_data)
-+ wm8971_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
-+ if (wm8971_workq)
-+ destroy_workqueue(wm8971_workq);
-+ snd_soc_free_pcms(socdev);
-+ snd_soc_dapm_free(socdev);
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+ i2c_del_driver(&wm8971_i2c_driver);
-+#endif
-+ kfree(codec->private_data);
-+ kfree(codec);
-+
-+ return 0;
-+}
-+
-+struct snd_soc_codec_device soc_codec_dev_wm8971 = {
-+ .probe = wm8971_probe,
-+ .remove = wm8971_remove,
-+ .suspend = wm8971_suspend,
-+ .resume = wm8971_resume,
-+};
-+
-+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8971);
-+
-+MODULE_DESCRIPTION("ASoC WM8971 driver");
-+MODULE_AUTHOR("Lab126");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/codecs/wm8971.h
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/codecs/wm8971.h
-@@ -0,0 +1,63 @@
-+/*
-+ * wm8971.h -- audio driver for WM8971
-+ *
-+ * Copyright 2005 Lab126, Inc.
-+ *
-+ * Author: Kenneth Kiraly <kiraly at lab126.com>
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or (at your
-+ * option) any later version.
-+ *
-+ */
-+
-+#ifndef _WM8971_H
-+#define _WM8971_H
-+
-+#define WM8971_LINVOL 0x00
-+#define WM8971_RINVOL 0x01
-+#define WM8971_LOUT1V 0x02
-+#define WM8971_ROUT1V 0x03
-+#define WM8971_ADCDAC 0x05
-+#define WM8971_IFACE 0x07
-+#define WM8971_SRATE 0x08
-+#define WM8971_LDAC 0x0a
-+#define WM8971_RDAC 0x0b
-+#define WM8971_BASS 0x0c
-+#define WM8971_TREBLE 0x0d
-+#define WM8971_RESET 0x0f
-+#define WM8971_ALC1 0x11
-+#define WM8971_ALC2 0x12
-+#define WM8971_ALC3 0x13
-+#define WM8971_NGATE 0x14
-+#define WM8971_LADC 0x15
-+#define WM8971_RADC 0x16
-+#define WM8971_ADCTL1 0x17
-+#define WM8971_ADCTL2 0x18
-+#define WM8971_PWR1 0x19
-+#define WM8971_PWR2 0x1a
-+#define WM8971_ADCTL3 0x1b
-+#define WM8971_ADCIN 0x1f
-+#define WM8971_LADCIN 0x20
-+#define WM8971_RADCIN 0x21
-+#define WM8971_LOUTM1 0x22
-+#define WM8971_LOUTM2 0x23
-+#define WM8971_ROUTM1 0x24
-+#define WM8971_ROUTM2 0x25
-+#define WM8971_MOUTM1 0x26
-+#define WM8971_MOUTM2 0x27
-+#define WM8971_LOUT2V 0x28
-+#define WM8971_ROUT2V 0x29
-+#define WM8971_MOUTV 0x2A
-+
-+#define WM8971_SYSCLK 0
-+
-+struct wm8971_setup_data {
-+ unsigned short i2c_address;
-+};
-+
-+extern struct snd_soc_codec_dai wm8971_dai;
-+extern struct snd_soc_codec_device soc_codec_dev_wm8971;
-+
-+#endif
-Index: linux-2.6.21-moko/sound/soc/codecs/wm8974.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/codecs/wm8974.c
-@@ -0,0 +1,873 @@
-+/*
-+ * wm8974.c -- WM8974 ALSA Soc Audio driver
-+ *
-+ * Copyright 2006 Wolfson Microelectronics PLC.
-+ *
-+ * Author: Liam Girdwood <liam.girdwood at wolfsonmicro.com>
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License version 2 as
-+ * published by the Free Software Foundation.
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/moduleparam.h>
-+#include <linux/version.h>
-+#include <linux/kernel.h>
-+#include <linux/init.h>
-+#include <linux/delay.h>
-+#include <linux/pm.h>
-+#include <linux/i2c.h>
-+#include <linux/platform_device.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/pcm_params.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+#include <sound/initval.h>
-+
-+#include "wm8974.h"
-+
-+#define AUDIO_NAME "wm8974"
-+#define WM8974_VERSION "0.6"
-+
-+/*
-+ * Debug
-+ */
-+
-+#define WM8974_DEBUG 0
-+
-+#ifdef WM8974_DEBUG
-+#define dbg(format, arg...) \
-+ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
-+#else
-+#define dbg(format, arg...) do {} while (0)
-+#endif
-+#define err(format, arg...) \
-+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
-+#define info(format, arg...) \
-+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
-+#define warn(format, arg...) \
-+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
-+
-+struct snd_soc_codec_device soc_codec_dev_wm8974;
-+
-+/*
-+ * wm8974 register cache
-+ * We can't read the WM8974 register space when we are
-+ * using 2 wire for device control, so we cache them instead.
-+ */
-+static const u16 wm8974_reg[WM8974_CACHEREGNUM] = {
-+ 0x0000, 0x0000, 0x0000, 0x0000,
-+ 0x0050, 0x0000, 0x0140, 0x0000,
-+ 0x0000, 0x0000, 0x0000, 0x00ff,
-+ 0x0000, 0x0000, 0x0100, 0x00ff,
-+ 0x0000, 0x0000, 0x012c, 0x002c,
-+ 0x002c, 0x002c, 0x002c, 0x0000,
-+ 0x0032, 0x0000, 0x0000, 0x0000,
-+ 0x0000, 0x0000, 0x0000, 0x0000,
-+ 0x0038, 0x000b, 0x0032, 0x0000,
-+ 0x0008, 0x000c, 0x0093, 0x00e9,
-+ 0x0000, 0x0000, 0x0000, 0x0000,
-+ 0x0003, 0x0010, 0x0000, 0x0000,
-+ 0x0000, 0x0002, 0x0000, 0x0000,
-+ 0x0000, 0x0000, 0x0039, 0x0000,
-+ 0x0000,
-+};
-+
-+/*
-+ * read wm8974 register cache
-+ */
-+static inline unsigned int wm8974_read_reg_cache(struct snd_soc_codec * codec,
-+ unsigned int reg)
-+{
-+ u16 *cache = codec->reg_cache;
-+ if (reg == WM8974_RESET)
-+ return 0;
-+ if (reg >= WM8974_CACHEREGNUM)
-+ return -1;
-+ return cache[reg];
-+}
-+
-+/*
-+ * write wm8974 register cache
-+ */
-+static inline void wm8974_write_reg_cache(struct snd_soc_codec *codec,
-+ u16 reg, unsigned int value)
-+{
-+ u16 *cache = codec->reg_cache;
-+ if (reg >= WM8974_CACHEREGNUM)
-+ return;
-+ cache[reg] = value;
-+}
-+
-+/*
-+ * write to the WM8974 register space
-+ */
-+static int wm8974_write(struct snd_soc_codec *codec, unsigned int reg,
-+ unsigned int value)
-+{
-+ u8 data[2];
-+
-+ /* data is
-+ * D15..D9 WM8974 register offset
-+ * D8...D0 register data
-+ */
-+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
-+ data[1] = value & 0x00ff;
-+
-+ wm8974_write_reg_cache (codec, reg, value);
-+ if (codec->hw_write(codec->control_data, data, 2) == 2)
-+ return 0;
-+ else
-+ return -EIO;
-+}
-+
-+#define wm8974_reset(c) wm8974_write(c, WM8974_RESET, 0)
-+
-+static const char *wm8974_companding[] = {"Off", "NC", "u-law", "A-law" };
-+static const char *wm8974_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
-+static const char *wm8974_eqmode[] = {"Capture", "Playback" };
-+static const char *wm8974_bw[] = {"Narrow", "Wide" };
-+static const char *wm8974_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" };
-+static const char *wm8974_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" };
-+static const char *wm8974_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" };
-+static const char *wm8974_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" };
-+static const char *wm8974_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" };
-+static const char *wm8974_alc[] = {"ALC", "Limiter" };
-+
-+static const struct soc_enum wm8974_enum[] = {
-+ SOC_ENUM_SINGLE(WM8974_COMP, 1, 4, wm8974_companding), /* adc */
-+ SOC_ENUM_SINGLE(WM8974_COMP, 3, 4, wm8974_companding), /* dac */
-+ SOC_ENUM_SINGLE(WM8974_DAC, 4, 4, wm8974_deemp),
-+ SOC_ENUM_SINGLE(WM8974_EQ1, 8, 2, wm8974_eqmode),
-+
-+ SOC_ENUM_SINGLE(WM8974_EQ1, 5, 4, wm8974_eq1),
-+ SOC_ENUM_SINGLE(WM8974_EQ2, 8, 2, wm8974_bw),
-+ SOC_ENUM_SINGLE(WM8974_EQ2, 5, 4, wm8974_eq2),
-+ SOC_ENUM_SINGLE(WM8974_EQ3, 8, 2, wm8974_bw),
-+
-+ SOC_ENUM_SINGLE(WM8974_EQ3, 5, 4, wm8974_eq3),
-+ SOC_ENUM_SINGLE(WM8974_EQ4, 8, 2, wm8974_bw),
-+ SOC_ENUM_SINGLE(WM8974_EQ4, 5, 4, wm8974_eq4),
-+ SOC_ENUM_SINGLE(WM8974_EQ5, 8, 2, wm8974_bw),
-+
-+ SOC_ENUM_SINGLE(WM8974_EQ5, 5, 4, wm8974_eq5),
-+ SOC_ENUM_SINGLE(WM8974_ALC3, 8, 2, wm8974_alc),
-+};
-+
-+static const struct snd_kcontrol_new wm8974_snd_controls[] = {
-+
-+SOC_SINGLE("Digital Loopback Switch", WM8974_COMP, 0, 1, 0),
-+
-+SOC_ENUM("DAC Companding", wm8974_enum[1]),
-+SOC_ENUM("ADC Companding", wm8974_enum[0]),
-+
-+SOC_ENUM("Playback De-emphasis", wm8974_enum[2]),
-+SOC_SINGLE("DAC Inversion Switch", WM8974_DAC, 0, 1, 0),
-+
-+SOC_SINGLE("PCM Volume", WM8974_DACVOL, 0, 127, 0),
-+
-+SOC_SINGLE("High Pass Filter Switch", WM8974_ADC, 8, 1, 0),
-+SOC_SINGLE("High Pass Cut Off", WM8974_ADC, 4, 7, 0),
-+SOC_SINGLE("ADC Inversion Switch", WM8974_COMP, 0, 1, 0),
-+
-+SOC_SINGLE("Capture Volume", WM8974_ADCVOL, 0, 127, 0),
-+
-+SOC_ENUM("Equaliser Function", wm8974_enum[3]),
-+SOC_ENUM("EQ1 Cut Off", wm8974_enum[4]),
-+SOC_SINGLE("EQ1 Volume", WM8974_EQ1, 0, 31, 1),
-+
-+SOC_ENUM("Equaliser EQ2 Bandwith", wm8974_enum[5]),
-+SOC_ENUM("EQ2 Cut Off", wm8974_enum[6]),
-+SOC_SINGLE("EQ2 Volume", WM8974_EQ2, 0, 31, 1),
-+
-+SOC_ENUM("Equaliser EQ3 Bandwith", wm8974_enum[7]),
-+SOC_ENUM("EQ3 Cut Off", wm8974_enum[8]),
-+SOC_SINGLE("EQ3 Volume", WM8974_EQ3, 0, 31, 1),
-+
-+SOC_ENUM("Equaliser EQ4 Bandwith", wm8974_enum[9]),
-+SOC_ENUM("EQ4 Cut Off", wm8974_enum[10]),
-+SOC_SINGLE("EQ4 Volume", WM8974_EQ4, 0, 31, 1),
-+
-+SOC_ENUM("Equaliser EQ5 Bandwith", wm8974_enum[11]),
-+SOC_ENUM("EQ5 Cut Off", wm8974_enum[12]),
-+SOC_SINGLE("EQ5 Volume", WM8974_EQ5, 0, 31, 1),
-+
-+SOC_SINGLE("DAC Playback Limiter Switch", WM8974_DACLIM1, 8, 1, 0),
-+SOC_SINGLE("DAC Playback Limiter Decay", WM8974_DACLIM1, 4, 15, 0),
-+SOC_SINGLE("DAC Playback Limiter Attack", WM8974_DACLIM1, 0, 15, 0),
-+
-+SOC_SINGLE("DAC Playback Limiter Threshold", WM8974_DACLIM2, 4, 7, 0),
-+SOC_SINGLE("DAC Playback Limiter Boost", WM8974_DACLIM2, 0, 15, 0),
-+
-+SOC_SINGLE("ALC Enable Switch", WM8974_ALC1, 8, 1, 0),
-+SOC_SINGLE("ALC Capture Max Gain", WM8974_ALC1, 3, 7, 0),
-+SOC_SINGLE("ALC Capture Min Gain", WM8974_ALC1, 0, 7, 0),
-+
-+SOC_SINGLE("ALC Capture ZC Switch", WM8974_ALC2, 8, 1, 0),
-+SOC_SINGLE("ALC Capture Hold", WM8974_ALC2, 4, 7, 0),
-+SOC_SINGLE("ALC Capture Target", WM8974_ALC2, 0, 15, 0),
-+
-+SOC_ENUM("ALC Capture Mode", wm8974_enum[13]),
-+SOC_SINGLE("ALC Capture Decay", WM8974_ALC3, 4, 15, 0),
-+SOC_SINGLE("ALC Capture Attack", WM8974_ALC3, 0, 15, 0),
-+
-+SOC_SINGLE("ALC Capture Noise Gate Switch", WM8974_NGATE, 3, 1, 0),
-+SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8974_NGATE, 0, 7, 0),
-+
-+SOC_SINGLE("Capture PGA ZC Switch", WM8974_INPPGA, 7, 1, 0),
-+SOC_SINGLE("Capture PGA Volume", WM8974_INPPGA, 0, 63, 0),
-+
-+SOC_SINGLE("Speaker Playback ZC Switch", WM8974_SPKVOL, 7, 1, 0),
-+SOC_SINGLE("Speaker Playback Switch", WM8974_SPKVOL, 6, 1, 1),
-+SOC_SINGLE("Speaker Playback Volume", WM8974_SPKVOL, 0, 63, 0),
-+
-+SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST, 8, 1, 0),
-+SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 0),
-+};
-+
-+/* add non dapm controls */
-+static int wm8974_add_controls(struct snd_soc_codec *codec)
-+{
-+ int err, i;
-+
-+ for (i = 0; i < ARRAY_SIZE(wm8974_snd_controls); i++) {
-+ err = snd_ctl_add(codec->card,
-+ snd_soc_cnew(&wm8974_snd_controls[i],codec, NULL));
-+ if (err < 0)
-+ return err;
-+ }
-+
-+ return 0;
-+}
-+
-+/* Speaker Output Mixer */
-+static const struct snd_kcontrol_new wm8974_speaker_mixer_controls[] = {
-+SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_SPKMIX, 1, 1, 0),
-+SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_SPKMIX, 5, 1, 0),
-+SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 1),
-+};
-+
-+/* Mono Output Mixer */
-+static const struct snd_kcontrol_new wm8974_mono_mixer_controls[] = {
-+SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_MONOMIX, 1, 1, 0),
-+SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_MONOMIX, 2, 1, 0),
-+SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_MONOMIX, 0, 1, 1),
-+};
-+
-+/* AUX Input boost vol */
-+static const struct snd_kcontrol_new wm8974_aux_boost_controls =
-+SOC_DAPM_SINGLE("Aux Volume", WM8974_ADCBOOST, 0, 7, 0);
-+
-+/* Mic Input boost vol */
-+static const struct snd_kcontrol_new wm8974_mic_boost_controls =
-+SOC_DAPM_SINGLE("Mic Volume", WM8974_ADCBOOST, 4, 7, 0);
-+
-+/* Capture boost switch */
-+static const struct snd_kcontrol_new wm8974_capture_boost_controls =
-+SOC_DAPM_SINGLE("Capture Boost Switch", WM8974_INPPGA, 6, 1, 0);
-+
-+/* Aux In to PGA */
-+static const struct snd_kcontrol_new wm8974_aux_capture_boost_controls =
-+SOC_DAPM_SINGLE("Aux Capture Boost Switch", WM8974_INPPGA, 2, 1, 0);
-+
-+/* Mic P In to PGA */
-+static const struct snd_kcontrol_new wm8974_micp_capture_boost_controls =
-+SOC_DAPM_SINGLE("Mic P Capture Boost Switch", WM8974_INPPGA, 0, 1, 0);
-+
-+/* Mic N In to PGA */
-+static const struct snd_kcontrol_new wm8974_micn_capture_boost_controls =
-+SOC_DAPM_SINGLE("Mic N Capture Boost Switch", WM8974_INPPGA, 1, 1, 0);
-+
-+static const struct snd_soc_dapm_widget wm8974_dapm_widgets[] = {
-+SND_SOC_DAPM_MIXER("Speaker Mixer", WM8974_POWER3, 2, 0,
-+ &wm8974_speaker_mixer_controls[0],
-+ ARRAY_SIZE(wm8974_speaker_mixer_controls)),
-+SND_SOC_DAPM_MIXER("Mono Mixer", WM8974_POWER3, 3, 0,
-+ &wm8974_mono_mixer_controls[0],
-+ ARRAY_SIZE(wm8974_mono_mixer_controls)),
-+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8974_POWER3, 0, 0),
-+SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8974_POWER3, 0, 0),
-+SND_SOC_DAPM_PGA("Aux Input", WM8974_POWER1, 6, 0, NULL, 0),
-+SND_SOC_DAPM_PGA("SpkN Out", WM8974_POWER3, 5, 0, NULL, 0),
-+SND_SOC_DAPM_PGA("SpkP Out", WM8974_POWER3, 6, 0, NULL, 0),
-+SND_SOC_DAPM_PGA("Mono Out", WM8974_POWER3, 7, 0, NULL, 0),
-+SND_SOC_DAPM_PGA("Mic PGA", WM8974_POWER2, 2, 0, NULL, 0),
-+
-+SND_SOC_DAPM_PGA("Aux Boost", SND_SOC_NOPM, 0, 0,
-+ &wm8974_aux_boost_controls, 1),
-+SND_SOC_DAPM_PGA("Mic Boost", SND_SOC_NOPM, 0, 0,
-+ &wm8974_mic_boost_controls, 1),
-+SND_SOC_DAPM_SWITCH("Capture Boost", SND_SOC_NOPM, 0, 0,
-+ &wm8974_capture_boost_controls),
-+
-+SND_SOC_DAPM_MIXER("Boost Mixer", WM8974_POWER2, 4, 0, NULL, 0),
-+
-+SND_SOC_DAPM_MICBIAS("Mic Bias", WM8974_POWER1, 4, 0),
-+
-+SND_SOC_DAPM_INPUT("MICN"),
-+SND_SOC_DAPM_INPUT("MICP"),
-+SND_SOC_DAPM_INPUT("AUX"),
-+SND_SOC_DAPM_OUTPUT("MONOOUT"),
-+SND_SOC_DAPM_OUTPUT("SPKOUTP"),
-+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
-+};
-+
-+static const char *audio_map[][3] = {
-+ /* Mono output mixer */
-+ {"Mono Mixer", "PCM Playback Switch", "DAC"},
-+ {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
-+ {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
-+
-+ /* Speaker output mixer */
-+ {"Speaker Mixer", "PCM Playback Switch", "DAC"},
-+ {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
-+ {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
-+
-+ /* Outputs */
-+ {"Mono Out", NULL, "Mono Mixer"},
-+ {"MONOOUT", NULL, "Mono Out"},
-+ {"SpkN Out", NULL, "Speaker Mixer"},
-+ {"SpkP Out", NULL, "Speaker Mixer"},
-+ {"SPKOUTN", NULL, "SpkN Out"},
-+ {"SPKOUTP", NULL, "SpkP Out"},
-+
-+ /* Boost Mixer */
-+ {"Boost Mixer", NULL, "ADC"},
-+ {"Capture Boost Switch", "Aux Capture Boost Switch", "AUX"},
-+ {"Aux Boost", "Aux Volume", "Boost Mixer"},
-+ {"Capture Boost", "Capture Switch", "Boost Mixer"},
-+ {"Mic Boost", "Mic Volume", "Boost Mixer"},
-+
-+ /* Inputs */
-+ {"MICP", NULL, "Mic Boost"},
-+ {"MICN", NULL, "Mic PGA"},
-+ {"Mic PGA", NULL, "Capture Boost"},
-+ {"AUX", NULL, "Aux Input"},
-+
-+ /* terminator */
-+ {NULL, NULL, NULL},
-+};
-+
-+static int wm8974_add_widgets(struct snd_soc_codec *codec)
-+{
-+ int i;
-+
-+ for(i = 0; i < ARRAY_SIZE(wm8974_dapm_widgets); i++) {
-+ snd_soc_dapm_new_control(codec, &wm8974_dapm_widgets[i]);
-+ }
-+
-+ /* set up audio path audio_mapnects */
-+ for(i = 0; audio_map[i][0] != NULL; i++) {
-+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
-+ audio_map[i][1], audio_map[i][2]);
-+ }
-+
-+ snd_soc_dapm_new_widgets(codec);
-+ return 0;
-+}
-+
-+struct pll_ {
-+ unsigned int in_hz, out_hz;
-+ unsigned int pre:4; /* prescale - 1 */
-+ unsigned int n:4;
-+ unsigned int k;
-+};
-+
-+struct pll_ pll[] = {
-+ {12000000, 11289600, 0, 7, 0x86c220},
-+ {12000000, 12288000, 0, 8, 0x3126e8},
-+ {13000000, 11289600, 0, 6, 0xf28bd4},
-+ {13000000, 12288000, 0, 7, 0x8fd525},
-+ {12288000, 11289600, 0, 7, 0x59999a},
-+ {11289600, 12288000, 0, 8, 0x80dee9},
-+ /* liam - add more entries */
-+};
-+
-+static int wm8974_set_dai_pll(struct snd_soc_codec_dai *codec_dai,
-+ int pll_id, unsigned int freq_in, unsigned int freq_out)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ int i;
-+ u16 reg;
-+
-+ if(freq_in == 0 || freq_out == 0) {
-+ reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
-+ wm8974_write(codec, WM8974_POWER1, reg & 0x1df);
-+ return 0;
-+ }
-+
-+ for(i = 0; i < ARRAY_SIZE(pll); i++) {
-+ if (freq_in == pll[i].in_hz && freq_out == pll[i].out_hz) {
-+ wm8974_write(codec, WM8974_PLLN, (pll[i].pre << 4) | pll[i].n);
-+ wm8974_write(codec, WM8974_PLLK1, pll[i].k >> 18);
-+ wm8974_write(codec, WM8974_PLLK1, (pll[i].k >> 9) && 0x1ff);
-+ wm8974_write(codec, WM8974_PLLK1, pll[i].k && 0x1ff);
-+ reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
-+ wm8974_write(codec, WM8974_POWER1, reg | 0x020);
-+ return 0;
-+ }
-+ }
-+ return -EINVAL;
-+}
-+
-+/*
-+ * Configure WM8974 clock dividers.
-+ */
-+static int wm8974_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
-+ int div_id, int div)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 reg;
-+
-+ switch (div_id) {
-+ case WM8974_OPCLKDIV:
-+ reg = wm8974_read_reg_cache(codec, WM8974_GPIO & 0x1cf);
-+ wm8974_write(codec, WM8974_GPIO, reg | div);
-+ break;
-+ case WM8974_MCLKDIV:
-+ reg = wm8974_read_reg_cache(codec, WM8974_CLOCK & 0x1f);
-+ wm8974_write(codec, WM8974_CLOCK, reg | div);
-+ break;
-+ case WM8974_ADCCLK:
-+ reg = wm8974_read_reg_cache(codec, WM8974_ADC & 0x1f7);
-+ wm8974_write(codec, WM8974_ADC, reg | div);
-+ break;
-+ case WM8974_DACCLK:
-+ reg = wm8974_read_reg_cache(codec, WM8974_DAC & 0x1f7);
-+ wm8974_write(codec, WM8974_DAC, reg | div);
-+ break;
-+ case WM8974_BCLKDIV:
-+ reg = wm8974_read_reg_cache(codec, WM8974_CLOCK & 0x1e3);
-+ wm8974_write(codec, WM8974_CLOCK, reg | div);
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ return 0;
-+}
-+
-+static int wm8974_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
-+ unsigned int fmt)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 iface = 0;
-+ u16 clk = wm8974_read_reg_cache(codec, WM8974_CLOCK) & 0x1fe;
-+
-+ /* set master/slave audio interface */
-+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
-+ case SND_SOC_DAIFMT_CBM_CFM:
-+ clk |= 0x0001;
-+ break;
-+ case SND_SOC_DAIFMT_CBS_CFS:
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ /* interface format */
-+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
-+ case SND_SOC_DAIFMT_I2S:
-+ iface |= 0x0010;
-+ break;
-+ case SND_SOC_DAIFMT_RIGHT_J:
-+ break;
-+ case SND_SOC_DAIFMT_LEFT_J:
-+ iface |= 0x0008;
-+ break;
-+ case SND_SOC_DAIFMT_DSP_A:
-+ iface |= 0x00018;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ /* clock inversion */
-+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
-+ case SND_SOC_DAIFMT_NB_NF:
-+ break;
-+ case SND_SOC_DAIFMT_IB_IF:
-+ iface |= 0x0180;
-+ break;
-+ case SND_SOC_DAIFMT_IB_NF:
-+ iface |= 0x0100;
-+ break;
-+ case SND_SOC_DAIFMT_NB_IF:
-+ iface |= 0x0080;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ wm8974_write(codec, WM8974_IFACE, iface);
-+ wm8974_write(codec, WM8974_CLOCK, clk);
-+ return 0;
-+}
-+
-+static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_device *socdev = rtd->socdev;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ u16 iface = wm8974_read_reg_cache(codec, WM8974_ADD) & 0x19f;
-+ u16 adn = wm8974_read_reg_cache(codec, WM8974_ADD) & 0x1f1;
-+
-+ /* bit size */
-+ switch (params_format(params)) {
-+ case SNDRV_PCM_FORMAT_S16_LE:
-+ break;
-+ case SNDRV_PCM_FORMAT_S20_3LE:
-+ iface |= 0x0020;
-+ break;
-+ case SNDRV_PCM_FORMAT_S24_LE:
-+ iface |= 0x0040;
-+ break;
-+ case SNDRV_PCM_FORMAT_S32_LE:
-+ iface |= 0x0060;
-+ break;
-+ }
-+
-+ /* filter coefficient */
-+ switch (params_rate(params)) {
-+ case SNDRV_PCM_RATE_8000:
-+ adn |= 0x5 << 1;
-+ break;
-+ case SNDRV_PCM_RATE_11025:
-+ adn |= 0x4 << 1;
-+ break;
-+ case SNDRV_PCM_RATE_16000:
-+ adn |= 0x3 << 1;
-+ break;
-+ case SNDRV_PCM_RATE_22050:
-+ adn |= 0x2 << 1;
-+ break;
-+ case SNDRV_PCM_RATE_32000:
-+ adn |= 0x1 << 1;
-+ break;
-+ case SNDRV_PCM_RATE_44100:
-+ break;
-+ }
-+
-+ wm8974_write(codec, WM8974_IFACE, iface);
-+ wm8974_write(codec, WM8974_ADD, adn);
-+ return 0;
-+}
-+
-+static int wm8974_mute(struct snd_soc_codec_dai *dai, int mute)
-+{
-+ struct snd_soc_codec *codec = dai->codec;
-+ u16 mute_reg = wm8974_read_reg_cache(codec, WM8974_DAC) & 0xffbf;
-+
-+ if(mute)
-+ wm8974_write(codec, WM8974_DAC, mute_reg | 0x40);
-+ else
-+ wm8974_write(codec, WM8974_DAC, mute_reg);
-+ return 0;
-+}
-+
-+/* liam need to make this lower power with dapm */
-+static int wm8974_dapm_event(struct snd_soc_codec *codec, int event)
-+{
-+
-+ switch (event) {
-+ case SNDRV_CTL_POWER_D0: /* full On */
-+ /* vref/mid, clk and osc on, dac unmute, active */
-+ wm8974_write(codec, WM8974_POWER1, 0x1ff);
-+ wm8974_write(codec, WM8974_POWER2, 0x1ff);
-+ wm8974_write(codec, WM8974_POWER3, 0x1ff);
-+ break;
-+ case SNDRV_CTL_POWER_D1: /* partial On */
-+ case SNDRV_CTL_POWER_D2: /* partial On */
-+ break;
-+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
-+ /* everything off except vref/vmid, dac mute, inactive */
-+
-+ break;
-+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
-+ /* everything off, dac mute, inactive */
-+ wm8974_write(codec, WM8974_POWER1, 0x0);
-+ wm8974_write(codec, WM8974_POWER2, 0x0);
-+ wm8974_write(codec, WM8974_POWER3, 0x0);
-+ break;
-+ }
-+ codec->dapm_state = event;
-+ return 0;
-+}
-+
-+#define WM8974_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
-+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
-+ SNDRV_PCM_RATE_48000)
-+
-+#define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
-+ SNDRV_PCM_FMTBIT_S24_LE)
-+
-+struct snd_soc_codec_dai wm8974_dai = {
-+ .name = "WM8974 HiFi",
-+ .playback = {
-+ .stream_name = "Playback",
-+ .channels_min = 1,
-+ .channels_max = 1,
-+ .rates = WM8974_RATES,
-+ .formats = WM8974_FORMATS,},
-+ .capture = {
-+ .stream_name = "Capture",
-+ .channels_min = 1,
-+ .channels_max = 1,
-+ .rates = WM8974_RATES,
-+ .formats = WM8974_FORMATS,},
-+ .ops = {
-+ .hw_params = wm8974_pcm_hw_params,
-+ },
-+ .dai_ops = {
-+ .digital_mute = wm8974_mute,
-+ .set_fmt = wm8974_set_dai_fmt,
-+ .set_clkdiv = wm8974_set_dai_clkdiv,
-+ .set_pll = wm8974_set_dai_pll,
-+ },
-+};
-+EXPORT_SYMBOL_GPL(wm8974_dai);
-+
-+static int wm8974_suspend(struct platform_device *pdev, pm_message_t state)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+
-+ wm8974_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
-+ return 0;
-+}
-+
-+static int wm8974_resume(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+ int i;
-+ u8 data[2];
-+ u16 *cache = codec->reg_cache;
-+
-+ /* Sync reg_cache with the hardware */
-+ for (i = 0; i < ARRAY_SIZE(wm8974_reg); i++) {
-+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
-+ data[1] = cache[i] & 0x00ff;
-+ codec->hw_write(codec->control_data, data, 2);
-+ }
-+ wm8974_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
-+ wm8974_dapm_event(codec, codec->suspend_dapm_state);
-+ return 0;
-+}
-+
-+/*
-+ * initialise the WM8974 driver
-+ * register the mixer and dsp interfaces with the kernel
-+ */
-+static int wm8974_init(struct snd_soc_device *socdev)
-+{
-+ struct snd_soc_codec *codec = socdev->codec;
-+ int ret = 0;
-+
-+ codec->name = "WM8974";
-+ codec->owner = THIS_MODULE;
-+ codec->read = wm8974_read_reg_cache;
-+ codec->write = wm8974_write;
-+ codec->dapm_event = wm8974_dapm_event;
-+ codec->dai = &wm8974_dai;
-+ codec->num_dai = 1;
-+ codec->reg_cache_size = ARRAY_SIZE(wm8974_reg);
-+ codec->reg_cache =
-+ kzalloc(sizeof(u16) * ARRAY_SIZE(wm8974_reg), GFP_KERNEL);
-+ if (codec->reg_cache == NULL)
-+ return -ENOMEM;
-+ memcpy(codec->reg_cache, wm8974_reg,
-+ sizeof(u16) * ARRAY_SIZE(wm8974_reg));
-+ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8974_reg);
-+
-+ wm8974_reset(codec);
-+
-+ /* register pcms */
-+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
-+ if(ret < 0) {
-+ printk(KERN_ERR "wm8974: failed to create pcms\n");
-+ goto pcm_err;
-+ }
-+
-+ /* power on device */
-+ wm8974_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
-+ wm8974_add_controls(codec);
-+ wm8974_add_widgets(codec);
-+ ret = snd_soc_register_card(socdev);
-+ if (ret < 0) {
-+ printk(KERN_ERR "wm8974: failed to register card\n");
-+ goto card_err;
-+ }
-+ return ret;
-+
-+card_err:
-+ snd_soc_free_pcms(socdev);
-+ snd_soc_dapm_free(socdev);
-+pcm_err:
-+ kfree(codec->reg_cache);
-+ return ret;
-+}
-+
-+static struct snd_soc_device *wm8974_socdev;
-+
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+
-+/*
-+ * WM8974 2 wire address is 0x1a
-+ */
-+#define I2C_DRIVERID_WM8974 0xfefe /* liam - need a proper id */
-+
-+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
-+
-+/* Magic definition of all other variables and things */
-+I2C_CLIENT_INSMOD;
-+
-+static struct i2c_driver wm8974_i2c_driver;
-+static struct i2c_client client_template;
-+
-+/* If the i2c layer weren't so broken, we could pass this kind of data
-+ around */
-+
-+static int wm8974_codec_probe(struct i2c_adapter *adap, int addr, int kind)
-+{
-+ struct snd_soc_device *socdev = wm8974_socdev;
-+ struct wm8974_setup_data *setup = socdev->codec_data;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ struct i2c_client *i2c;
-+ int ret;
-+
-+ if (addr != setup->i2c_address)
-+ return -ENODEV;
-+
-+ client_template.adapter = adap;
-+ client_template.addr = addr;
-+
-+ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
-+ if (i2c == NULL) {
-+ kfree(codec);
-+ return -ENOMEM;
-+ }
-+ memcpy(i2c, &client_template, sizeof(struct i2c_client));
-+ i2c_set_clientdata(i2c, codec);
-+ codec->control_data = i2c;
-+
-+ ret = i2c_attach_client(i2c);
-+ if(ret < 0) {
-+ err("failed to attach codec at addr %x\n", addr);
-+ goto err;
-+ }
-+
-+ ret = wm8974_init(socdev);
-+ if(ret < 0) {
-+ err("failed to initialise WM8974\n");
-+ goto err;
-+ }
-+ return ret;
-+
-+err:
-+ kfree(codec);
-+ kfree(i2c);
-+ return ret;
-+}
-+
-+static int wm8974_i2c_detach(struct i2c_client *client)
-+{
-+ struct snd_soc_codec *codec = i2c_get_clientdata(client);
-+ i2c_detach_client(client);
-+ kfree(codec->reg_cache);
-+ kfree(client);
-+ return 0;
-+}
-+
-+static int wm8974_i2c_attach(struct i2c_adapter *adap)
-+{
-+ return i2c_probe(adap, &addr_data, wm8974_codec_probe);
-+}
-+
-+/* corgi i2c codec control layer */
-+static struct i2c_driver wm8974_i2c_driver = {
-+ .driver = {
-+ .name = "WM8974 I2C Codec",
-+ .owner = THIS_MODULE,
-+ },
-+ .id = I2C_DRIVERID_WM8974,
-+ .attach_adapter = wm8974_i2c_attach,
-+ .detach_client = wm8974_i2c_detach,
-+ .command = NULL,
-+};
-+
-+static struct i2c_client client_template = {
-+ .name = "WM8974",
-+ .driver = &wm8974_i2c_driver,
-+};
-+#endif
-+
-+static int wm8974_probe(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct wm8974_setup_data *setup;
-+ struct snd_soc_codec *codec;
-+ int ret = 0;
-+
-+ info("WM8974 Audio Codec %s", WM8974_VERSION);
-+
-+ setup = socdev->codec_data;
-+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
-+ if (codec == NULL)
-+ return -ENOMEM;
-+
-+ socdev->codec = codec;
-+ mutex_init(&codec->mutex);
-+ INIT_LIST_HEAD(&codec->dapm_widgets);
-+ INIT_LIST_HEAD(&codec->dapm_paths);
-+
-+ wm8974_socdev = socdev;
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+ if (setup->i2c_address) {
-+ normal_i2c[0] = setup->i2c_address;
-+ codec->hw_write = (hw_write_t)i2c_master_send;
-+ ret = i2c_add_driver(&wm8974_i2c_driver);
-+ if (ret != 0)
-+ printk(KERN_ERR "can't add i2c driver");
-+ }
-+#else
-+ /* Add other interfaces here */
-+#endif
-+ return ret;
-+}
-+
-+/* power down chip */
-+static int wm8974_remove(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+
-+ if (codec->control_data)
-+ wm8974_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
-+
-+ snd_soc_free_pcms(socdev);
-+ snd_soc_dapm_free(socdev);
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+ i2c_del_driver(&wm8974_i2c_driver);
-+#endif
-+ kfree(codec);
-+
-+ return 0;
-+}
-+
-+struct snd_soc_codec_device soc_codec_dev_wm8974 = {
-+ .probe = wm8974_probe,
-+ .remove = wm8974_remove,
-+ .suspend = wm8974_suspend,
-+ .resume = wm8974_resume,
-+};
-+
-+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8974);
-+
-+MODULE_DESCRIPTION("ASoC WM8974 driver");
-+MODULE_AUTHOR("Liam Girdwood");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/codecs/wm8974.h
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/codecs/wm8974.h
-@@ -0,0 +1,104 @@
-+/*
-+ * wm8974.h -- WM8974 Soc Audio driver
-+ *
-+ * 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 _WM8974_H
-+#define _WM8974_H
-+
-+/* WM8974 register space */
-+
-+#define WM8974_RESET 0x0
-+#define WM8974_POWER1 0x1
-+#define WM8974_POWER2 0x2
-+#define WM8974_POWER3 0x3
-+#define WM8974_IFACE 0x4
-+#define WM8974_COMP 0x5
-+#define WM8974_CLOCK 0x6
-+#define WM8974_ADD 0x7
-+#define WM8974_GPIO 0x8
-+#define WM8974_DAC 0xa
-+#define WM8974_DACVOL 0xb
-+#define WM8974_ADC 0xe
-+#define WM8974_ADCVOL 0xf
-+#define WM8974_EQ1 0x12
-+#define WM8974_EQ2 0x13
-+#define WM8974_EQ3 0x14
-+#define WM8974_EQ4 0x15
-+#define WM8974_EQ5 0x16
-+#define WM8974_DACLIM1 0x18
-+#define WM8974_DACLIM2 0x19
-+#define WM8974_NOTCH1 0x1b
-+#define WM8974_NOTCH2 0x1c
-+#define WM8974_NOTCH3 0x1d
-+#define WM8974_NOTCH4 0x1e
-+#define WM8974_ALC1 0x20
-+#define WM8974_ALC2 0x21
-+#define WM8974_ALC3 0x22
-+#define WM8974_NGATE 0x23
-+#define WM8974_PLLN 0x24
-+#define WM8974_PLLK1 0x25
-+#define WM8974_PLLK2 0x26
-+#define WM8974_PLLK3 0x27
-+#define WM8974_ATTEN 0x28
-+#define WM8974_INPUT 0x2c
-+#define WM8974_INPPGA 0x2d
-+#define WM8974_ADCBOOST 0x2f
-+#define WM8974_OUTPUT 0x31
-+#define WM8974_SPKMIX 0x32
-+#define WM8974_SPKVOL 0x36
-+#define WM8974_MONOMIX 0x38
-+
-+#define WM8974_CACHEREGNUM 57
-+
-+/* Clock divider Id's */
-+#define WM8974_OPCLKDIV 0
-+#define WM8974_MCLKDIV 1
-+#define WM8974_ADCCLK 2
-+#define WM8974_DACCLK 3
-+#define WM8974_BCLKDIV 4
-+
-+/* DAC clock dividers */
-+#define WM8974_DACCLK_F2 (1 << 3)
-+#define WM8974_DACCLK_F4 (0 << 3)
-+
-+/* ADC clock dividers */
-+#define WM8974_ADCCLK_F2 (1 << 3)
-+#define WM8974_ADCCLK_F4 (0 << 3)
-+
-+/* PLL Out dividers */
-+#define WM8974_OPCLKDIV_1 (0 << 4)
-+#define WM8974_OPCLKDIV_2 (1 << 4)
-+#define WM8974_OPCLKDIV_3 (2 << 4)
-+#define WM8974_OPCLKDIV_4 (3 << 4)
-+
-+/* BCLK clock dividers */
-+#define WM8974_BCLKDIV_1 (0 << 2)
-+#define WM8974_BCLKDIV_2 (1 << 2)
-+#define WM8974_BCLKDIV_4 (2 << 2)
-+#define WM8974_BCLKDIV_8 (3 << 2)
-+#define WM8974_BCLKDIV_16 (4 << 2)
-+#define WM8974_BCLKDIV_32 (5 << 2)
-+
-+/* MCLK clock dividers */
-+#define WM8974_MCLKDIV_1 (0 << 5)
-+#define WM8974_MCLKDIV_1_5 (1 << 5)
-+#define WM8974_MCLKDIV_2 (2 << 5)
-+#define WM8974_MCLKDIV_3 (3 << 5)
-+#define WM8974_MCLKDIV_4 (4 << 5)
-+#define WM8974_MCLKDIV_6 (5 << 5)
-+#define WM8974_MCLKDIV_8 (6 << 5)
-+#define WM8974_MCLKDIV_12 (7 << 5)
-+
-+
-+struct wm8974_setup_data {
-+ unsigned short i2c_address;
-+};
-+
-+extern struct snd_soc_codec_dai wm8974_dai;
-+extern struct snd_soc_codec_device soc_codec_dev_wm8974;
-+
-+#endif
-Index: linux-2.6.21-moko/sound/soc/codecs/wm9713.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/codecs/wm9713.c
-@@ -0,0 +1,1220 @@
-+/*
-+ * wm9713.c -- ALSA Soc WM9713 codec support
-+ *
-+ * Copyright 2006 Wolfson Microelectronics PLC.
-+ * Author: Liam Girdwood
-+ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or (at your
-+ * option) any later version.
-+ *
-+ * Revision history
-+ * 4th Feb 2006 Initial version.
-+ *
-+ * Features:-
-+ *
-+ * o Support for AC97 Codec, Voice DAC and Aux DAC
-+ * o Support for DAPM
-+ */
-+
-+#include <linux/init.h>
-+#include <linux/module.h>
-+#include <linux/device.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/ac97_codec.h>
-+#include <sound/initval.h>
-+#include <sound/pcm_params.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+
-+#include "wm9713.h"
-+
-+#define WM9713_VERSION "0.12"
-+
-+struct wm9713_priv {
-+ u32 pll_in; /* PLL input frequency */
-+ u32 pll_out; /* PLL output frequency */
-+};
-+
-+static unsigned int ac97_read(struct snd_soc_codec *codec,
-+ unsigned int reg);
-+static int ac97_write(struct snd_soc_codec *codec,
-+ unsigned int reg, unsigned int val);
-+
-+/*
-+ * WM9713 register cache
-+ * Reg 0x3c bit 15 is used by touch driver.
-+ */
-+static const u16 wm9713_reg[] = {
-+ 0x6174, 0x8080, 0x8080, 0x8080, // 6
-+ 0xc880, 0xe808, 0xe808, 0x0808, // e
-+ 0x00da, 0x8000, 0xd600, 0xaaa0, // 16
-+ 0xaaa0, 0xaaa0, 0x0000, 0x0000, // 1e
-+ 0x0f0f, 0x0040, 0x0000, 0x7f00, // 26
-+ 0x0405, 0x0410, 0xbb80, 0xbb80, // 2e
-+ 0x0000, 0xbb80, 0x0000, 0x4523, // 36
-+ 0x0000, 0x2000, 0x7eff, 0xffff, // 3e
-+ 0x0000, 0x0000, 0x0080, 0x0000, // 46
-+ 0x0000, 0x0000, 0xfffe, 0xffff, // 4e
-+ 0x0000, 0x0000, 0x0000, 0xfffe, // 56
-+ 0x4000, 0x0000, 0x0000, 0x0000, // 5e
-+ 0xb032, 0x3e00, 0x0000, 0x0000, // 66
-+ 0x0000, 0x0000, 0x0000, 0x0000, // 6e
-+ 0x0000, 0x0000, 0x0000, 0x0006, // 76
-+ 0x0001, 0x0000, 0x574d, 0x4c13, // 7e
-+ 0x0000, 0x0000, 0x0000 // virtual hp & mic mixers
-+};
-+
-+/* virtual HP mixers regs */
-+#define HPL_MIXER 0x80
-+#define HPR_MIXER 0x82
-+#define MICB_MUX 0x82
-+
-+static const char *wm9713_mic_mixer[] = {"Stereo", "Mic 1", "Mic 2", "Mute"};
-+static const char *wm9713_rec_mux[] = {"Stereo", "Left", "Right", "Mute"};
-+static const char *wm9713_rec_src[] =
-+ {"Mic 1", "Mic 2", "Line", "Mono In", "Headphone", "Speaker",
-+ "Mono Out", "Zh"};
-+static const char *wm9713_rec_gain[] = {"+1.5dB Steps", "+0.75dB Steps"};
-+static const char *wm9713_alc_select[] = {"None", "Left", "Right", "Stereo"};
-+static const char *wm9713_mono_pga[] = {"Vmid", "Zh", "Mono", "Inv",
-+ "Mono Vmid", "Inv Vmid"};
-+static const char *wm9713_spk_pga[] =
-+ {"Vmid", "Zh", "Headphone", "Speaker", "Inv", "Headphone Vmid",
-+ "Speaker Vmid", "Inv Vmid"};
-+static const char *wm9713_hp_pga[] = {"Vmid", "Zh", "Headphone",
-+ "Headphone Vmid"};
-+static const char *wm9713_out3_pga[] = {"Vmid", "Zh", "Inv 1", "Inv 1 Vmid"};
-+static const char *wm9713_out4_pga[] = {"Vmid", "Zh", "Inv 2", "Inv 2 Vmid"};
-+static const char *wm9713_dac_inv[] =
-+ {"Off", "Mono", "Speaker", "Left Headphone", "Right Headphone",
-+ "Headphone Mono", "NC", "Vmid"};
-+static const char *wm9713_bass[] = {"Linear Control", "Adaptive Boost"};
-+static const char *wm9713_ng_type[] = {"Constant Gain", "Mute"};
-+static const char *wm9713_mic_select[] = {"Mic 1", "Mic 2 A", "Mic 2 B"};
-+static const char *wm9713_micb_select[] = {"MPB", "MPA"};
-+
-+static const struct soc_enum wm9713_enum[] = {
-+SOC_ENUM_SINGLE(AC97_LINE, 3, 4, wm9713_mic_mixer), /* record mic mixer 0 */
-+SOC_ENUM_SINGLE(AC97_VIDEO, 14, 4, wm9713_rec_mux), /* record mux hp 1 */
-+SOC_ENUM_SINGLE(AC97_VIDEO, 9, 4, wm9713_rec_mux), /* record mux mono 2 */
-+SOC_ENUM_SINGLE(AC97_VIDEO, 3, 8, wm9713_rec_src), /* record mux left 3 */
-+SOC_ENUM_SINGLE(AC97_VIDEO, 0, 8, wm9713_rec_src), /* record mux right 4*/
-+SOC_ENUM_DOUBLE(AC97_CD, 14, 6, 2, wm9713_rec_gain), /* record step size 5 */
-+SOC_ENUM_SINGLE(AC97_PCI_SVID, 14, 4, wm9713_alc_select), /* alc source select 6*/
-+SOC_ENUM_SINGLE(AC97_REC_GAIN, 14, 4, wm9713_mono_pga), /* mono input select 7 */
-+SOC_ENUM_SINGLE(AC97_REC_GAIN, 11, 8, wm9713_spk_pga), /* speaker left input select 8 */
-+SOC_ENUM_SINGLE(AC97_REC_GAIN, 8, 8, wm9713_spk_pga), /* speaker right input select 9 */
-+SOC_ENUM_SINGLE(AC97_REC_GAIN, 6, 3, wm9713_hp_pga), /* headphone left input 10 */
-+SOC_ENUM_SINGLE(AC97_REC_GAIN, 4, 3, wm9713_hp_pga), /* headphone right input 11 */
-+SOC_ENUM_SINGLE(AC97_REC_GAIN, 2, 4, wm9713_out3_pga), /* out 3 source 12 */
-+SOC_ENUM_SINGLE(AC97_REC_GAIN, 0, 4, wm9713_out4_pga), /* out 4 source 13 */
-+SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 13, 8, wm9713_dac_inv), /* dac invert 1 14 */
-+SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 10, 8, wm9713_dac_inv), /* dac invert 2 15 */
-+SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, wm9713_bass), /* bass control 16 */
-+SOC_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9713_ng_type), /* noise gate type 17 */
-+SOC_ENUM_SINGLE(AC97_3D_CONTROL, 12, 3, wm9713_mic_select), /* mic selection 18 */
-+SOC_ENUM_SINGLE(MICB_MUX, 0, 2, wm9713_micb_select), /* mic selection 19 */
-+};
-+
-+static const struct snd_kcontrol_new wm9713_snd_ac97_controls[] = {
-+SOC_DOUBLE("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1),
-+SOC_DOUBLE("Speaker Playback Switch", AC97_MASTER, 15, 7, 1, 1),
-+SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1),
-+SOC_DOUBLE("Headphone Playback Switch", AC97_HEADPHONE,15, 7, 1, 1),
-+SOC_DOUBLE("Line In Volume", AC97_PC_BEEP, 8, 0, 31, 1),
-+SOC_DOUBLE("PCM Playback Volume", AC97_PHONE, 8, 0, 31, 1),
-+SOC_SINGLE("Mic 1 Volume", AC97_MIC, 8, 31, 1),
-+SOC_SINGLE("Mic 2 Volume", AC97_MIC, 0, 31, 1),
-+
-+SOC_SINGLE("Mic Boost (+20dB) Switch", AC97_LINE, 5, 1, 0),
-+SOC_SINGLE("Mic Headphone Mixer Volume", AC97_LINE, 0, 7, 1),
-+
-+SOC_SINGLE("Capture Switch", AC97_CD, 15, 1, 1),
-+SOC_ENUM("Capture Volume Steps", wm9713_enum[5]),
-+SOC_DOUBLE("Capture Volume", AC97_CD, 8, 0, 31, 0),
-+SOC_SINGLE("Capture ZC Switch", AC97_CD, 7, 1, 0),
-+
-+SOC_SINGLE("Capture to Headphone Volume", AC97_VIDEO, 11, 7, 1),
-+SOC_SINGLE("Capture to Mono Boost (+20dB) Switch", AC97_VIDEO, 8, 1, 0),
-+SOC_SINGLE("Capture ADC Boost (+20dB) Switch", AC97_VIDEO, 6, 1, 0),
-+
-+SOC_SINGLE("ALC Target Volume", AC97_CODEC_CLASS_REV, 12, 15, 0),
-+SOC_SINGLE("ALC Hold Time", AC97_CODEC_CLASS_REV, 8, 15, 0),
-+SOC_SINGLE("ALC Decay Time ", AC97_CODEC_CLASS_REV, 4, 15, 0),
-+SOC_SINGLE("ALC Attack Time", AC97_CODEC_CLASS_REV, 0, 15, 0),
-+SOC_ENUM("ALC Function", wm9713_enum[6]),
-+SOC_SINGLE("ALC Max Volume", AC97_PCI_SVID, 11, 7, 0),
-+SOC_SINGLE("ALC ZC Timeout", AC97_PCI_SVID, 9, 3, 0),
-+SOC_SINGLE("ALC ZC Switch", AC97_PCI_SVID, 8, 1, 0),
-+SOC_SINGLE("ALC NG Switch", AC97_PCI_SVID, 7, 1, 0),
-+SOC_ENUM("ALC NG Type", wm9713_enum[17]),
-+SOC_SINGLE("ALC NG Threshold", AC97_PCI_SVID, 0, 31, 0),
-+
-+SOC_DOUBLE("Speaker Playback ZC Switch", AC97_MASTER, 14, 6, 1, 0),
-+SOC_DOUBLE("Headphone Playback ZC Switch", AC97_HEADPHONE, 14, 6, 1, 0),
-+
-+SOC_SINGLE("Out4 Playback Switch", AC97_MASTER_MONO, 15, 1, 1),
-+SOC_SINGLE("Out4 Playback ZC Switch", AC97_MASTER_MONO, 14, 1, 0),
-+SOC_SINGLE("Out4 Playback Volume", AC97_MASTER_MONO, 8, 63, 1),
-+
-+SOC_SINGLE("Out3 Playback Switch", AC97_MASTER_MONO, 7, 1, 1),
-+SOC_SINGLE("Out3 Playback ZC Switch", AC97_MASTER_MONO, 6, 1, 0),
-+SOC_SINGLE("Out3 Playback Volume", AC97_MASTER_MONO, 0, 63, 1),
-+
-+SOC_SINGLE("Mono Capture Volume", AC97_MASTER_TONE, 8, 31, 1),
-+SOC_SINGLE("Mono Playback Switch", AC97_MASTER_TONE, 7, 1, 1),
-+SOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_TONE, 6, 1, 0),
-+SOC_SINGLE("Mono Playback Volume", AC97_MASTER_TONE, 0, 31, 1),
-+
-+SOC_SINGLE("PC Beep Playback Headphone Volume", AC97_AUX, 12, 7, 1),
-+SOC_SINGLE("PC Beep Playback Speaker Volume", AC97_AUX, 8, 7, 1),
-+SOC_SINGLE("PC Beep Playback Mono Volume", AC97_AUX, 4, 7, 1),
-+
-+SOC_SINGLE("Voice Playback Headphone Volume", AC97_PCM, 12, 7, 1),
-+SOC_SINGLE("Voice Playback Master Volume", AC97_PCM, 8, 7, 1),
-+SOC_SINGLE("Voice Playback Mono Volume", AC97_PCM, 4, 7, 1),
-+
-+SOC_SINGLE("Aux Playback Headphone Volume", AC97_REC_SEL, 12, 7, 1),
-+SOC_SINGLE("Aux Playback Master Volume", AC97_REC_SEL, 8, 7, 1),
-+SOC_SINGLE("Aux Playback Mono Volume", AC97_REC_SEL, 4, 7, 1),
-+
-+SOC_ENUM("Bass Control", wm9713_enum[16]),
-+SOC_SINGLE("Bass Cut-off Switch", AC97_GENERAL_PURPOSE, 12, 1, 1),
-+SOC_SINGLE("Tone Cut-off Switch", AC97_GENERAL_PURPOSE, 4, 1, 1),
-+SOC_SINGLE("Playback Attenuate (-6dB) Switch", AC97_GENERAL_PURPOSE, 6, 1, 0),
-+SOC_SINGLE("Bass Volume", AC97_GENERAL_PURPOSE, 8, 15, 1),
-+SOC_SINGLE("Tone Volume", AC97_GENERAL_PURPOSE, 0, 15, 1),
-+
-+SOC_SINGLE("3D Upper Cut-off Switch", AC97_REC_GAIN_MIC, 5, 1, 0),
-+SOC_SINGLE("3D Lower Cut-off Switch", AC97_REC_GAIN_MIC, 4, 1, 0),
-+SOC_SINGLE("3D Depth", AC97_REC_GAIN_MIC, 0, 15, 1),
-+};
-+
-+/* add non dapm controls */
-+static int wm9713_add_controls(struct snd_soc_codec *codec)
-+{
-+ int err, i;
-+
-+ for (i = 0; i < ARRAY_SIZE(wm9713_snd_ac97_controls); i++) {
-+ err = snd_ctl_add(codec->card,
-+ snd_soc_cnew(&wm9713_snd_ac97_controls[i],codec, NULL));
-+ if (err < 0)
-+ return err;
-+ }
-+ return 0;
-+}
-+
-+/* We have to create a fake left and right HP mixers because
-+ * the codec only has a single control that is shared by both channels.
-+ * This makes it impossible to determine the audio path using the current
-+ * register map, thus we add a new (virtual) register to help determine the
-+ * audio route within the device.
-+ */
-+static int mixer_event (struct snd_soc_dapm_widget *w, int event)
-+{
-+ u16 l, r, beep, tone, phone, rec, pcm, aux;
-+
-+ l = ac97_read(w->codec, HPL_MIXER);
-+ r = ac97_read(w->codec, HPR_MIXER);
-+ beep = ac97_read(w->codec, AC97_PC_BEEP);
-+ tone = ac97_read(w->codec, AC97_MASTER_TONE);
-+ phone = ac97_read(w->codec, AC97_PHONE);
-+ rec = ac97_read(w->codec, AC97_REC_SEL);
-+ pcm = ac97_read(w->codec, AC97_PCM);
-+ aux = ac97_read(w->codec, AC97_AUX);
-+
-+ if (event & SND_SOC_DAPM_PRE_REG)
-+ return 0;
-+ if (l & 0x1 || r & 0x1)
-+ ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff);
-+ else
-+ ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000);
-+
-+ if (l & 0x2 || r & 0x2)
-+ ac97_write(w->codec, AC97_MASTER_TONE, tone & 0x7fff);
-+ else
-+ ac97_write(w->codec, AC97_MASTER_TONE, tone | 0x8000);
-+
-+ if (l & 0x4 || r & 0x4)
-+ ac97_write(w->codec, AC97_PHONE, phone & 0x7fff);
-+ else
-+ ac97_write(w->codec, AC97_PHONE, phone | 0x8000);
-+
-+ if (l & 0x8 || r & 0x8)
-+ ac97_write(w->codec, AC97_REC_SEL, rec & 0x7fff);
-+ else
-+ ac97_write(w->codec, AC97_REC_SEL, rec | 0x8000);
-+
-+ if (l & 0x10 || r & 0x10)
-+ ac97_write(w->codec, AC97_PCM, pcm & 0x7fff);
-+ else
-+ ac97_write(w->codec, AC97_PCM, pcm | 0x8000);
-+
-+ if (l & 0x20 || r & 0x20)
-+ ac97_write(w->codec, AC97_AUX, aux & 0x7fff);
-+ else
-+ ac97_write(w->codec, AC97_AUX, aux | 0x8000);
-+
-+ return 0;
-+}
-+
-+/* Left Headphone Mixers */
-+static const struct snd_kcontrol_new wm9713_hpl_mixer_controls[] = {
-+SOC_DAPM_SINGLE("PC Beep Playback Switch", HPL_MIXER, 5, 1, 0),
-+SOC_DAPM_SINGLE("Voice Playback Switch", HPL_MIXER, 4, 1, 0),
-+SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 3, 1, 0),
-+SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 2, 1, 0),
-+SOC_DAPM_SINGLE("MonoIn Playback Switch", HPL_MIXER, 1, 1, 0),
-+SOC_DAPM_SINGLE("Bypass Playback Switch", HPL_MIXER, 0, 1, 0),
-+};
-+
-+/* Right Headphone Mixers */
-+static const struct snd_kcontrol_new wm9713_hpr_mixer_controls[] = {
-+SOC_DAPM_SINGLE("PC Beep Playback Switch", HPR_MIXER, 5, 1, 0),
-+SOC_DAPM_SINGLE("Voice Playback Switch", HPR_MIXER, 4, 1, 0),
-+SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 3, 1, 0),
-+SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 2, 1, 0),
-+SOC_DAPM_SINGLE("MonoIn Playback Switch", HPR_MIXER, 1, 1, 0),
-+SOC_DAPM_SINGLE("Bypass Playback Switch", HPR_MIXER, 0, 1, 0),
-+};
-+
-+/* headphone capture mux */
-+static const struct snd_kcontrol_new wm9713_hp_rec_mux_controls =
-+SOC_DAPM_ENUM("Route", wm9713_enum[1]);
-+
-+/* headphone mic mux */
-+static const struct snd_kcontrol_new wm9713_hp_mic_mux_controls =
-+SOC_DAPM_ENUM("Route", wm9713_enum[0]);
-+
-+/* Speaker Mixer */
-+static const struct snd_kcontrol_new wm9713_speaker_mixer_controls[] = {
-+SOC_DAPM_SINGLE("PC Beep Playback Switch", AC97_AUX, 11, 1, 1),
-+SOC_DAPM_SINGLE("Voice Playback Switch", AC97_PCM, 11, 1, 1),
-+SOC_DAPM_SINGLE("Aux Playback Switch", AC97_REC_SEL, 11, 1, 1),
-+SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PHONE, 14, 1, 1),
-+SOC_DAPM_SINGLE("MonoIn Playback Switch", AC97_MASTER_TONE, 14, 1, 1),
-+SOC_DAPM_SINGLE("Bypass Playback Switch", AC97_PC_BEEP, 14, 1, 1),
-+};
-+
-+/* Mono Mixer */
-+static const struct snd_kcontrol_new wm9713_mono_mixer_controls[] = {
-+SOC_DAPM_SINGLE("PC Beep Playback Switch", AC97_AUX, 7, 1, 1),
-+SOC_DAPM_SINGLE("Voice Playback Switch", AC97_PCM, 7, 1, 1),
-+SOC_DAPM_SINGLE("Aux Playback Switch", AC97_REC_SEL, 7, 1, 1),
-+SOC_DAPM_SINGLE("PCM Playback Switch", AC97_PHONE, 13, 1, 1),
-+SOC_DAPM_SINGLE("MonoIn Playback Switch", AC97_MASTER_TONE, 13, 1, 1),
-+SOC_DAPM_SINGLE("Bypass Playback Switch", AC97_PC_BEEP, 13, 1, 1),
-+SOC_DAPM_SINGLE("Mic 1 Sidetone Switch", AC97_LINE, 7, 1, 1),
-+SOC_DAPM_SINGLE("Mic 2 Sidetone Switch", AC97_LINE, 6, 1, 1),
-+};
-+
-+/* mono mic mux */
-+static const struct snd_kcontrol_new wm9713_mono_mic_mux_controls =
-+SOC_DAPM_ENUM("Route", wm9713_enum[2]);
-+
-+/* mono output mux */
-+static const struct snd_kcontrol_new wm9713_mono_mux_controls =
-+SOC_DAPM_ENUM("Route", wm9713_enum[7]);
-+
-+/* speaker left output mux */
-+static const struct snd_kcontrol_new wm9713_hp_spkl_mux_controls =
-+SOC_DAPM_ENUM("Route", wm9713_enum[8]);
-+
-+/* speaker right output mux */
-+static const struct snd_kcontrol_new wm9713_hp_spkr_mux_controls =
-+SOC_DAPM_ENUM("Route", wm9713_enum[9]);
-+
-+/* headphone left output mux */
-+static const struct snd_kcontrol_new wm9713_hpl_out_mux_controls =
-+SOC_DAPM_ENUM("Route", wm9713_enum[10]);
-+
-+/* headphone right output mux */
-+static const struct snd_kcontrol_new wm9713_hpr_out_mux_controls =
-+SOC_DAPM_ENUM("Route", wm9713_enum[11]);
-+
-+/* Out3 mux */
-+static const struct snd_kcontrol_new wm9713_out3_mux_controls =
-+SOC_DAPM_ENUM("Route", wm9713_enum[12]);
-+
-+/* Out4 mux */
-+static const struct snd_kcontrol_new wm9713_out4_mux_controls =
-+SOC_DAPM_ENUM("Route", wm9713_enum[13]);
-+
-+/* DAC inv mux 1 */
-+static const struct snd_kcontrol_new wm9713_dac_inv1_mux_controls =
-+SOC_DAPM_ENUM("Route", wm9713_enum[14]);
-+
-+/* DAC inv mux 2 */
-+static const struct snd_kcontrol_new wm9713_dac_inv2_mux_controls =
-+SOC_DAPM_ENUM("Route", wm9713_enum[15]);
-+
-+/* Capture source left */
-+static const struct snd_kcontrol_new wm9713_rec_srcl_mux_controls =
-+SOC_DAPM_ENUM("Route", wm9713_enum[3]);
-+
-+/* Capture source right */
-+static const struct snd_kcontrol_new wm9713_rec_srcr_mux_controls =
-+SOC_DAPM_ENUM("Route", wm9713_enum[4]);
-+
-+/* mic source */
-+static const struct snd_kcontrol_new wm9713_mic_sel_mux_controls =
-+SOC_DAPM_ENUM("Route", wm9713_enum[18]);
-+
-+/* mic source B virtual control */
-+static const struct snd_kcontrol_new wm9713_micb_sel_mux_controls =
-+SOC_DAPM_ENUM("Route", wm9713_enum[19]);
-+
-+static const struct snd_soc_dapm_widget wm9713_dapm_widgets[] = {
-+SND_SOC_DAPM_MUX("Capture Headphone Mux", SND_SOC_NOPM, 0, 0,
-+ &wm9713_hp_rec_mux_controls),
-+SND_SOC_DAPM_MUX("Sidetone Mux", SND_SOC_NOPM, 0, 0,
-+ &wm9713_hp_mic_mux_controls),
-+SND_SOC_DAPM_MUX("Capture Mono Mux", SND_SOC_NOPM, 0, 0,
-+ &wm9713_mono_mic_mux_controls),
-+SND_SOC_DAPM_MUX("Mono Out Mux", SND_SOC_NOPM, 0, 0,
-+ &wm9713_mono_mux_controls),
-+SND_SOC_DAPM_MUX("Left Speaker Out Mux", SND_SOC_NOPM, 0, 0,
-+ &wm9713_hp_spkl_mux_controls),
-+SND_SOC_DAPM_MUX("Right Speaker Out Mux", SND_SOC_NOPM, 0, 0,
-+ &wm9713_hp_spkr_mux_controls),
-+SND_SOC_DAPM_MUX("Left Headphone Out Mux", SND_SOC_NOPM, 0, 0,
-+ &wm9713_hpl_out_mux_controls),
-+SND_SOC_DAPM_MUX("Right Headphone Out Mux", SND_SOC_NOPM, 0, 0,
-+ &wm9713_hpr_out_mux_controls),
-+SND_SOC_DAPM_MUX("Out 3 Mux", SND_SOC_NOPM, 0, 0,
-+ &wm9713_out3_mux_controls),
-+SND_SOC_DAPM_MUX("Out 4 Mux", SND_SOC_NOPM, 0, 0,
-+ &wm9713_out4_mux_controls),
-+SND_SOC_DAPM_MUX("DAC Inv Mux 1", SND_SOC_NOPM, 0, 0,
-+ &wm9713_dac_inv1_mux_controls),
-+SND_SOC_DAPM_MUX("DAC Inv Mux 2", SND_SOC_NOPM, 0, 0,
-+ &wm9713_dac_inv2_mux_controls),
-+SND_SOC_DAPM_MUX("Left Capture Source", SND_SOC_NOPM, 0, 0,
-+ &wm9713_rec_srcl_mux_controls),
-+SND_SOC_DAPM_MUX("Right Capture Source", SND_SOC_NOPM, 0, 0,
-+ &wm9713_rec_srcr_mux_controls),
-+SND_SOC_DAPM_MUX("Mic A Source", SND_SOC_NOPM, 0, 0,
-+ &wm9713_mic_sel_mux_controls ),
-+SND_SOC_DAPM_MUX("Mic B Source", SND_SOC_NOPM, 0, 0,
-+ &wm9713_micb_sel_mux_controls ),
-+SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_EXTENDED_MID, 3, 1,
-+ &wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls),
-+ mixer_event, SND_SOC_DAPM_POST_REG),
-+SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_EXTENDED_MID, 2, 1,
-+ &wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls),
-+ mixer_event, SND_SOC_DAPM_POST_REG),
-+SND_SOC_DAPM_MIXER("Mono Mixer", AC97_EXTENDED_MID, 0, 1,
-+ &wm9713_mono_mixer_controls[0], ARRAY_SIZE(wm9713_mono_mixer_controls)),
-+SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_EXTENDED_MID, 1, 1,
-+ &wm9713_speaker_mixer_controls[0],
-+ ARRAY_SIZE(wm9713_speaker_mixer_controls)),
-+SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", AC97_EXTENDED_MID, 7, 1),
-+SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", AC97_EXTENDED_MID, 6, 1),
-+SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
-+SND_SOC_DAPM_MIXER("HP Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
-+SND_SOC_DAPM_MIXER("Capture Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
-+SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback", AC97_EXTENDED_MID, 12, 1),
-+SND_SOC_DAPM_DAC("Aux DAC", "Aux Playback", AC97_EXTENDED_MID, 11, 1),
-+SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture", AC97_EXTENDED_MID, 5, 1),
-+SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture", AC97_EXTENDED_MID, 4, 1),
-+SND_SOC_DAPM_PGA("Left Headphone", AC97_EXTENDED_MSTATUS, 10, 1, NULL, 0),
-+SND_SOC_DAPM_PGA("Right Headphone", AC97_EXTENDED_MSTATUS, 9, 1, NULL, 0),
-+SND_SOC_DAPM_PGA("Left Speaker", AC97_EXTENDED_MSTATUS, 8, 1, NULL, 0),
-+SND_SOC_DAPM_PGA("Right Speaker", AC97_EXTENDED_MSTATUS, 7, 1, NULL, 0),
-+SND_SOC_DAPM_PGA("Out 3", AC97_EXTENDED_MSTATUS, 11, 1, NULL, 0),
-+SND_SOC_DAPM_PGA("Out 4", AC97_EXTENDED_MSTATUS, 12, 1, NULL, 0),
-+SND_SOC_DAPM_PGA("Mono Out", AC97_EXTENDED_MSTATUS, 13, 1, NULL, 0),
-+SND_SOC_DAPM_PGA("Left Line In", AC97_EXTENDED_MSTATUS, 6, 1, NULL, 0),
-+SND_SOC_DAPM_PGA("Right Line In", AC97_EXTENDED_MSTATUS, 5, 1, NULL, 0),
-+SND_SOC_DAPM_PGA("Mono In", AC97_EXTENDED_MSTATUS, 4, 1, NULL, 0),
-+SND_SOC_DAPM_PGA("Mic A PGA", AC97_EXTENDED_MSTATUS, 3, 1, NULL, 0),
-+SND_SOC_DAPM_PGA("Mic B PGA", AC97_EXTENDED_MSTATUS, 2, 1, NULL, 0),
-+SND_SOC_DAPM_PGA("Mic A Pre Amp", AC97_EXTENDED_MSTATUS, 1, 1, NULL, 0),
-+SND_SOC_DAPM_PGA("Mic B Pre Amp", AC97_EXTENDED_MSTATUS, 0, 1, NULL, 0),
-+SND_SOC_DAPM_MICBIAS("Mic Bias", AC97_EXTENDED_MSTATUS, 14, 1),
-+SND_SOC_DAPM_OUTPUT("MONO"),
-+SND_SOC_DAPM_OUTPUT("HPL"),
-+SND_SOC_DAPM_OUTPUT("HPR"),
-+SND_SOC_DAPM_OUTPUT("SPKL"),
-+SND_SOC_DAPM_OUTPUT("SPKR"),
-+SND_SOC_DAPM_OUTPUT("OUT3"),
-+SND_SOC_DAPM_OUTPUT("OUT4"),
-+SND_SOC_DAPM_INPUT("LINEL"),
-+SND_SOC_DAPM_INPUT("LINER"),
-+SND_SOC_DAPM_INPUT("MONOIN"),
-+SND_SOC_DAPM_INPUT("PCBEEP"),
-+SND_SOC_DAPM_INPUT("MIC1"),
-+SND_SOC_DAPM_INPUT("MIC2A"),
-+SND_SOC_DAPM_INPUT("MIC2B"),
-+SND_SOC_DAPM_VMID("VMID"),
-+};
-+
-+static const char *audio_map[][3] = {
-+ /* left HP mixer */
-+ {"Left HP Mixer", "PC Beep Playback Switch", "PCBEEP"},
-+ {"Left HP Mixer", "Voice Playback Switch", "Voice DAC"},
-+ {"Left HP Mixer", "Aux Playback Switch", "Aux DAC"},
-+ {"Left HP Mixer", "Bypass Playback Switch", "Left Line In"},
-+ {"Left HP Mixer", "PCM Playback Switch", "Left DAC"},
-+ {"Left HP Mixer", "MonoIn Playback Switch", "Mono In"},
-+ {"Left HP Mixer", NULL, "Capture Headphone Mux"},
-+
-+ /* right HP mixer */
-+ {"Right HP Mixer", "PC Beep Playback Switch", "PCBEEP"},
-+ {"Right HP Mixer", "Voice Playback Switch", "Voice DAC"},
-+ {"Right HP Mixer", "Aux Playback Switch", "Aux DAC"},
-+ {"Right HP Mixer", "Bypass Playback Switch", "Right Line In"},
-+ {"Right HP Mixer", "PCM Playback Switch", "Right DAC"},
-+ {"Right HP Mixer", "MonoIn Playback Switch", "Mono In"},
-+ {"Right HP Mixer", NULL, "Capture Headphone Mux"},
-+
-+ /* virtual mixer - mixes left & right channels for spk and mono */
-+ {"AC97 Mixer", NULL, "Left DAC"},
-+ {"AC97 Mixer", NULL, "Right DAC"},
-+ {"Line Mixer", NULL, "Right Line In"},
-+ {"Line Mixer", NULL, "Left Line In"},
-+ {"HP Mixer", NULL, "Left HP Mixer"},
-+ {"HP Mixer", NULL, "Right HP Mixer"},
-+ {"Capture Mixer", NULL, "Left Capture Source"},
-+ {"Capture Mixer", NULL, "Right Capture Source"},
-+
-+ /* speaker mixer */
-+ {"Speaker Mixer", "PC Beep Playback Switch", "PCBEEP"},
-+ {"Speaker Mixer", "Voice Playback Switch", "Voice DAC"},
-+ {"Speaker Mixer", "Aux Playback Switch", "Aux DAC"},
-+ {"Speaker Mixer", "Bypass Playback Switch", "Line Mixer"},
-+ {"Speaker Mixer", "PCM Playback Switch", "AC97 Mixer"},
-+ {"Speaker Mixer", "MonoIn Playback Switch", "Mono In"},
-+
-+ /* mono mixer */
-+ {"Mono Mixer", "PC Beep Playback Switch", "PCBEEP"},
-+ {"Mono Mixer", "Voice Playback Switch", "Voice DAC"},
-+ {"Mono Mixer", "Aux Playback Switch", "Aux DAC"},
-+ {"Mono Mixer", "Bypass Playback Switch", "Line Mixer"},
-+ {"Mono Mixer", "PCM Playback Switch", "AC97 Mixer"},
-+ {"Mono Mixer", NULL, "Capture Mono Mux"},
-+
-+ /* DAC inv mux 1 */
-+ {"DAC Inv Mux 1", "Mono", "Mono Mixer"},
-+ {"DAC Inv Mux 1", "Speaker", "Speaker Mixer"},
-+ {"DAC Inv Mux 1", "Left Headphone", "Left HP Mixer"},
-+ {"DAC Inv Mux 1", "Right Headphone", "Right HP Mixer"},
-+ {"DAC Inv Mux 1", "Headphone Mono", "HP Mixer"},
-+
-+ /* DAC inv mux 2 */
-+ {"DAC Inv Mux 2", "Mono", "Mono Mixer"},
-+ {"DAC Inv Mux 2", "Speaker", "Speaker Mixer"},
-+ {"DAC Inv Mux 2", "Left Headphone", "Left HP Mixer"},
-+ {"DAC Inv Mux 2", "Right Headphone", "Right HP Mixer"},
-+ {"DAC Inv Mux 2", "Headphone Mono", "HP Mixer"},
-+
-+ /* headphone left mux */
-+ {"Left Headphone Out Mux", "Headphone", "Left HP Mixer"},
-+
-+ /* headphone right mux */
-+ {"Right Headphone Out Mux", "Headphone", "Right HP Mixer"},
-+
-+ /* speaker left mux */
-+ {"Left Speaker Out Mux", "Headphone", "Left HP Mixer"},
-+ {"Left Speaker Out Mux", "Speaker", "Speaker Mixer"},
-+ {"Left Speaker Out Mux", "Inv", "DAC Inv Mux 1"},
-+
-+ /* speaker right mux */
-+ {"Right Speaker Out Mux", "Headphone", "Right HP Mixer"},
-+ {"Right Speaker Out Mux", "Speaker", "Speaker Mixer"},
-+ {"Right Speaker Out Mux", "Inv", "DAC Inv Mux 2"},
-+
-+ /* mono mux */
-+ {"Mono Out Mux", "Mono", "Mono Mixer"},
-+ {"Mono Out Mux", "Inv", "DAC Inv Mux 1"},
-+
-+ /* out 3 mux */
-+ {"Out 3 Mux", "Inv 1", "DAC Inv Mux 1"},
-+
-+ /* out 4 mux */
-+ {"Out 4 Mux", "Inv 2", "DAC Inv Mux 2"},
-+
-+ /* output pga */
-+ {"HPL", NULL, "Left Headphone"},
-+ {"Left Headphone", NULL, "Left Headphone Out Mux"},
-+ {"HPR", NULL, "Right Headphone"},
-+ {"Right Headphone", NULL, "Right Headphone Out Mux"},
-+ {"OUT3", NULL, "Out 3"},
-+ {"Out 3", NULL, "Out 3 Mux"},
-+ {"OUT4", NULL, "Out 4"},
-+ {"Out 4", NULL, "Out 4 Mux"},
-+ {"SPKL", NULL, "Left Speaker"},
-+ {"Left Speaker", NULL, "Left Speaker Out Mux"},
-+ {"SPKR", NULL, "Right Speaker"},
-+ {"Right Speaker", NULL, "Right Speaker Out Mux"},
-+ {"MONO", NULL, "Mono Out"},
-+ {"Mono Out", NULL, "Mono Out Mux"},
-+
-+ /* input pga */
-+ {"Left Line In", NULL, "LINEL"},
-+ {"Right Line In", NULL, "LINER"},
-+ {"Mono In", NULL, "MONOIN"},
-+ {"Mic A PGA", NULL, "Mic A Pre Amp"},
-+ {"Mic B PGA", NULL, "Mic B Pre Amp"},
-+
-+ /* left capture select */
-+ {"Left Capture Source", "Mic 1", "Mic A Pre Amp"},
-+ {"Left Capture Source", "Mic 2", "Mic B Pre Amp"},
-+ {"Left Capture Source", "Line", "LINEL"},
-+ {"Left Capture Source", "Mono In", "MONOIN"},
-+ {"Left Capture Source", "Headphone", "Left HP Mixer"},
-+ {"Left Capture Source", "Speaker", "Speaker Mixer"},
-+ {"Left Capture Source", "Mono Out", "Mono Mixer"},
-+
-+ /* right capture select */
-+ {"Right Capture Source", "Mic 1", "Mic A Pre Amp"},
-+ {"Right Capture Source", "Mic 2", "Mic B Pre Amp"},
-+ {"Right Capture Source", "Line", "LINER"},
-+ {"Right Capture Source", "Mono In", "MONOIN"},
-+ {"Right Capture Source", "Headphone", "Right HP Mixer"},
-+ {"Right Capture Source", "Speaker", "Speaker Mixer"},
-+ {"Right Capture Source", "Mono Out", "Mono Mixer"},
-+
-+ /* left ADC */
-+ {"Left ADC", NULL, "Left Capture Source"},
-+
-+ /* right ADC */
-+ {"Right ADC", NULL, "Right Capture Source"},
-+
-+ /* mic */
-+ {"Mic A Pre Amp", NULL, "Mic A Source"},
-+ {"Mic A Source", "Mic 1", "MIC1"},
-+ {"Mic A Source", "Mic 2 A", "MIC2A"},
-+ {"Mic A Source", "Mic 2 B", "Mic B Source"},
-+ {"Mic B Pre Amp", "MPB", "Mic B Source"},
-+ {"Mic B Source", NULL, "MIC2B"},
-+
-+ /* headphone capture */
-+ {"Capture Headphone Mux", "Stereo", "Capture Mixer"},
-+ {"Capture Headphone Mux", "Left", "Left Capture Source"},
-+ {"Capture Headphone Mux", "Right", "Right Capture Source"},
-+
-+ /* mono capture */
-+ {"Capture Mono Mux", "Stereo", "Capture Mixer"},
-+ {"Capture Mono Mux", "Left", "Left Capture Source"},
-+ {"Capture Mono Mux", "Right", "Right Capture Source"},
-+
-+ {NULL, NULL, NULL},
-+};
-+
-+static int wm9713_add_widgets(struct snd_soc_codec *codec)
-+{
-+ int i;
-+
-+ for(i = 0; i < ARRAY_SIZE(wm9713_dapm_widgets); i++) {
-+ snd_soc_dapm_new_control(codec, &wm9713_dapm_widgets[i]);
-+ }
-+
-+ /* set up audio path audio_mapnects */
-+ for(i = 0; audio_map[i][0] != NULL; i++) {
-+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
-+ audio_map[i][1], audio_map[i][2]);
-+ }
-+
-+ snd_soc_dapm_new_widgets(codec);
-+ return 0;
-+}
-+
-+static unsigned int ac97_read(struct snd_soc_codec *codec,
-+ unsigned int reg)
-+{
-+ u16 *cache = codec->reg_cache;
-+
-+ if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
-+ reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
-+ reg == AC97_CD)
-+ return soc_ac97_ops.read(codec->ac97, reg);
-+ else {
-+ reg = reg >> 1;
-+
-+ if (reg > (ARRAY_SIZE(wm9713_reg)))
-+ return -EIO;
-+
-+ return cache[reg];
-+ }
-+}
-+
-+static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
-+ unsigned int val)
-+{
-+ u16 *cache = codec->reg_cache;
-+ if (reg < 0x7c)
-+ soc_ac97_ops.write(codec->ac97, reg, val);
-+ reg = reg >> 1;
-+ if (reg <= (ARRAY_SIZE(wm9713_reg)))
-+ cache[reg] = val;
-+
-+ return 0;
-+}
-+
-+struct pll_ {
-+ unsigned int in_hz;
-+ unsigned int lf:1; /* allows low frequency use */
-+ unsigned int sdm:1; /* allows fraction n div */
-+ unsigned int divsel:1; /* enables input clock div */
-+ unsigned int divctl:1; /* input clock divider */
-+ unsigned int n:4;
-+ unsigned int k;
-+};
-+
-+struct pll_ pll[] = {
-+ {13000000, 0, 1, 0, 0, 7, 0x23f488},
-+ {2048000, 1, 0, 0, 0, 12, 0x0},
-+ {4096000, 1, 0, 0, 0, 6, 0x0},
-+ {12288000, 0, 0, 0, 0, 8, 0x0},
-+ /* liam - add more entries */
-+};
-+
-+static int wm9713_set_pll(struct snd_soc_codec *codec,
-+ int pll_id, unsigned int freq_in, unsigned int freq_out)
-+{
-+ struct wm9713_priv *wm9713 = codec->private_data;
-+ int i;
-+ u16 reg, reg2;
-+
-+ /* turn PLL off ? */
-+ if (freq_in == 0 || freq_out == 0) {
-+ /* disable PLL power and select ext source */
-+ reg = ac97_read(codec, AC97_HANDSET_RATE);
-+ ac97_write(codec, AC97_HANDSET_RATE, reg | 0x0080);
-+ reg = ac97_read(codec, AC97_EXTENDED_MID);
-+ ac97_write(codec, AC97_EXTENDED_MID, reg | 0x0200);
-+ wm9713->pll_out = 0;
-+ return 0;
-+ }
-+
-+ for (i = 0; i < ARRAY_SIZE(pll); i++) {
-+ if (pll[i].in_hz == freq_in)
-+ goto found;
-+ }
-+ return -EINVAL;
-+
-+found:
-+ if (pll[i].sdm == 0) {
-+ reg = (pll[i].n << 12) | (pll[i].lf << 11) |
-+ (pll[i].divsel << 9) | (pll[i].divctl << 8);
-+ ac97_write(codec, AC97_LINE1_LEVEL, reg);
-+ } else {
-+ /* write the fractional k to the reg 0x46 pages */
-+ reg2 = (pll[i].n << 12) | (pll[i].lf << 11) | (pll[i].sdm << 10) |
-+ (pll[i].divsel << 9) | (pll[i].divctl << 8);
-+
-+ reg = reg2 | (0x5 << 4) | (pll[i].k >> 20); /* K [21:20] */
-+ ac97_write(codec, AC97_LINE1_LEVEL, reg);
-+
-+ reg = reg2 | (0x4 << 4) | ((pll[i].k >> 16) & 0xf); /* K [19:16] */
-+ ac97_write(codec, AC97_LINE1_LEVEL, reg);
-+
-+ reg = reg2 | (0x3 << 4) | ((pll[i].k >> 12) & 0xf); /* K [15:12] */
-+ ac97_write(codec, AC97_LINE1_LEVEL, reg);
-+
-+ reg = reg2 | (0x2 << 4) | ((pll[i].k >> 8) & 0xf); /* K [11:8] */
-+ ac97_write(codec, AC97_LINE1_LEVEL, reg);
-+
-+ reg = reg2 | (0x1 << 4) | ((pll[i].k >> 4) & 0xf); /* K [7:4] */
-+ ac97_write(codec, AC97_LINE1_LEVEL, reg);
-+
-+ reg = reg2 | (0x0 << 4) | (pll[i].k & 0xf); /* K [3:0] */
-+ ac97_write(codec, AC97_LINE1_LEVEL, reg);
-+ }
-+
-+ /* turn PLL on and select as source */
-+ reg = ac97_read(codec, AC97_EXTENDED_MID);
-+ ac97_write(codec, AC97_EXTENDED_MID, reg & 0xfdff);
-+ reg = ac97_read(codec, AC97_HANDSET_RATE);
-+ ac97_write(codec, AC97_HANDSET_RATE, reg & 0xff7f);
-+ wm9713->pll_out = freq_out;
-+ wm9713->pll_in = freq_in;
-+
-+ /* wait 10ms AC97 link frames for the link to stabilise */
-+ schedule_timeout_interruptible(msecs_to_jiffies(10));
-+ return 0;
-+}
-+
-+static int wm9713_set_dai_pll(struct snd_soc_codec_dai *codec_dai,
-+ int pll_id, unsigned int freq_in, unsigned int freq_out)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ return wm9713_set_pll(codec, pll_id, freq_in, freq_out);
-+}
-+
-+/*
-+ * Tristate the PCM DAI lines, tristate can be disabled by calling
-+ * wm9713_set_dai_fmt()
-+ */
-+static int wm9713_set_dai_tristate(struct snd_soc_codec_dai *codec_dai,
-+ int tristate)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0x9fff;
-+
-+ if (tristate)
-+ ac97_write(codec, AC97_CENTER_LFE_MASTER, reg);
-+
-+ return 0;
-+}
-+
-+/*
-+ * Configure WM9713 clock dividers.
-+ * Voice DAC needs 256 FS
-+ */
-+static int wm9713_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
-+ int div_id, int div)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 reg;
-+
-+ switch (div_id) {
-+ case WM9713_PCMCLK_DIV:
-+ reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xf0ff;
-+ ac97_write(codec, AC97_HANDSET_RATE, reg | div);
-+ break;
-+ case WM9713_CLKA_MULT:
-+ reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xfffd;
-+ ac97_write(codec, AC97_HANDSET_RATE, reg | div);
-+ break;
-+ case WM9713_CLKB_MULT:
-+ reg = ac97_read(codec, AC97_HANDSET_RATE) & 0xfffb;
-+ ac97_write(codec, AC97_HANDSET_RATE, reg | div);
-+ break;
-+ case WM9713_HIFI_DIV:
-+ reg = ac97_read(codec, AC97_HANDSET_RATE) & 0x8fff;
-+ ac97_write(codec, AC97_HANDSET_RATE, reg | div);
-+ break;
-+ case WM9713_PCMBCLK_DIV:
-+ reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0xf1ff;
-+ ac97_write(codec, AC97_CENTER_LFE_MASTER, reg | div);
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ return 0;
-+};
-+
-+static int wm9713_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
-+ unsigned int fmt)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 gpio = ac97_read(codec, AC97_GPIO_CFG) & 0xffe2;
-+ u16 reg = 0x8000;
-+
-+ /* clock masters */
-+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK){
-+ case SND_SOC_DAIFMT_CBM_CFM:
-+ reg |= 0x4000;
-+ gpio |= 0x0008;
-+ break;
-+ case SND_SOC_DAIFMT_CBM_CFS:
-+ reg |= 0x6000;
-+ gpio |= 0x000c;
-+ break;
-+ case SND_SOC_DAIFMT_CBS_CFS:
-+ reg |= 0x0200;
-+ gpio |= 0x000d;
-+ break;
-+ case SND_SOC_DAIFMT_CBS_CFM:
-+ gpio |= 0x0009;
-+ break;
-+ }
-+
-+ /* clock inversion */
-+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
-+ case SND_SOC_DAIFMT_IB_IF:
-+ reg |= 0x00c0;
-+ break;
-+ case SND_SOC_DAIFMT_IB_NF:
-+ reg |= 0x0080;
-+ break;
-+ case SND_SOC_DAIFMT_NB_IF:
-+ reg |= 0x0040;
-+ break;
-+ }
-+
-+ /* DAI format */
-+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
-+ case SND_SOC_DAIFMT_I2S:
-+ reg |= 0x0002;
-+ break;
-+ case SND_SOC_DAIFMT_RIGHT_J:
-+ break;
-+ case SND_SOC_DAIFMT_LEFT_J:
-+ reg |= 0x0001;
-+ break;
-+ case SND_SOC_DAIFMT_DSP_A:
-+ reg |= 0x0003;
-+ break;
-+ case SND_SOC_DAIFMT_DSP_B:
-+ reg |= 0x0043;
-+ break;
-+ }
-+
-+ ac97_write(codec, AC97_GPIO_CFG, gpio);
-+ ac97_write(codec, AC97_CENTER_LFE_MASTER, reg);
-+ return 0;
-+}
-+
-+static int wm9713_pcm_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_device *socdev = rtd->socdev;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ u16 reg = ac97_read(codec, AC97_CENTER_LFE_MASTER) & 0xfff3;
-+
-+ switch (params_format(params)) {
-+ case SNDRV_PCM_FORMAT_S16_LE:
-+ break;
-+ case SNDRV_PCM_FORMAT_S20_3LE:
-+ reg |= 0x0004;
-+ break;
-+ case SNDRV_PCM_FORMAT_S24_LE:
-+ reg |= 0x0008;
-+ break;
-+ case SNDRV_PCM_FORMAT_S32_LE:
-+ reg |= 0x000c;
-+ break;
-+ }
-+
-+ /* enable PCM interface in master mode */
-+ ac97_write(codec, AC97_CENTER_LFE_MASTER, reg);
-+ return 0;
-+}
-+
-+static void wm9713_voiceshutdown(snd_pcm_substream_t *substream)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_device *socdev = rtd->socdev;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ u16 status;
-+
-+ /* Gracefully shut down the voice interface. */
-+ status = ac97_read(codec, AC97_EXTENDED_STATUS) | 0x1000;
-+ ac97_write(codec,AC97_HANDSET_RATE,0x0280);
-+ schedule_timeout_interruptible(msecs_to_jiffies(1));
-+ ac97_write(codec,AC97_HANDSET_RATE,0x0F80);
-+ ac97_write(codec,AC97_EXTENDED_MID,status);
-+}
-+
-+static int ac97_hifi_prepare(struct snd_pcm_substream *substream)
-+{
-+ struct snd_pcm_runtime *runtime = substream->runtime;
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_device *socdev = rtd->socdev;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ int reg;
-+ u16 vra;
-+
-+ vra = ac97_read(codec, AC97_EXTENDED_STATUS);
-+ ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
-+
-+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-+ reg = AC97_PCM_FRONT_DAC_RATE;
-+ else
-+ reg = AC97_PCM_LR_ADC_RATE;
-+
-+ return ac97_write(codec, reg, runtime->rate);
-+}
-+
-+static int ac97_aux_prepare(struct snd_pcm_substream *substream)
-+{
-+ struct snd_pcm_runtime *runtime = substream->runtime;
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_device *socdev = rtd->socdev;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ u16 vra, xsle;
-+
-+ vra = ac97_read(codec, AC97_EXTENDED_STATUS);
-+ ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
-+ xsle = ac97_read(codec, AC97_PCI_SID);
-+ ac97_write(codec, AC97_PCI_SID, xsle | 0x8000);
-+
-+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
-+ return -ENODEV;
-+
-+ return ac97_write(codec, AC97_PCM_SURR_DAC_RATE, runtime->rate);
-+}
-+
-+#define WM9713_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
-+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
-+
-+#define WM9713_PCM_FORMATS \
-+ (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \
-+ SNDRV_PCM_FORMAT_S24_LE)
-+
-+struct snd_soc_codec_dai wm9713_dai[] = {
-+{
-+ .name = "AC97 HiFi",
-+ .playback = {
-+ .stream_name = "HiFi Playback",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = WM9713_RATES,
-+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
-+ .capture = {
-+ .stream_name = "HiFi Capture",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = WM9713_RATES,
-+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
-+ .ops = {
-+ .prepare = ac97_hifi_prepare,},
-+ },
-+ {
-+ .name = "AC97 Aux",
-+ .playback = {
-+ .stream_name = "Aux Playback",
-+ .channels_min = 1,
-+ .channels_max = 1,
-+ .rates = WM9713_RATES,
-+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
-+ .ops = {
-+ .prepare = ac97_aux_prepare,},
-+ },
-+ {
-+ .name = "WM9713 Voice",
-+ .playback = {
-+ .stream_name = "Voice Playback",
-+ .channels_min = 1,
-+ .channels_max = 1,
-+ .rates = WM9713_RATES,
-+ .formats = WM9713_PCM_FORMATS,},
-+ .capture = {
-+ .stream_name = "Voice Capture",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = WM9713_RATES,
-+ .formats = WM9713_PCM_FORMATS,},
-+ .ops = {
-+ .hw_params = wm9713_pcm_hw_params,
-+ .shutdown = wm9713_voiceshutdown,},
-+ .dai_ops = {
-+ .set_clkdiv = wm9713_set_dai_clkdiv,
-+ .set_pll = wm9713_set_dai_pll,
-+ .set_fmt = wm9713_set_dai_fmt,
-+ .set_tristate = wm9713_set_dai_tristate,
-+ },
-+ },
-+};
-+EXPORT_SYMBOL_GPL(wm9713_dai);
-+
-+int wm9713_reset(struct snd_soc_codec *codec, int try_warm)
-+{
-+ if (try_warm && soc_ac97_ops.warm_reset) {
-+ soc_ac97_ops.warm_reset(codec->ac97);
-+ if (!(ac97_read(codec, 0) & 0x8000))
-+ return 1;
-+ }
-+
-+ soc_ac97_ops.reset(codec->ac97);
-+ if (ac97_read(codec, 0) & 0x8000)
-+ return -EIO;
-+ return 0;
-+}
-+EXPORT_SYMBOL_GPL(wm9713_reset);
-+
-+static int wm9713_dapm_event(struct snd_soc_codec *codec, int event)
-+{
-+ u16 reg;
-+
-+ switch (event) {
-+ case SNDRV_CTL_POWER_D0: /* full On */
-+ /* enable thermal shutdown */
-+ reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x1bff;
-+ ac97_write(codec, AC97_EXTENDED_MID, reg);
-+ break;
-+ case SNDRV_CTL_POWER_D1: /* partial On */
-+ case SNDRV_CTL_POWER_D2: /* partial On */
-+ break;
-+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
-+ /* enable master bias and vmid */
-+ reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x3bff;
-+ ac97_write(codec, AC97_EXTENDED_MID, reg);
-+ ac97_write(codec, AC97_POWERDOWN, 0x0000);
-+ break;
-+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
-+ /* disable everything including AC link */
-+ ac97_write(codec, AC97_EXTENDED_MID, 0xffff);
-+ ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
-+ ac97_write(codec, AC97_POWERDOWN, 0xffff);
-+ break;
-+ }
-+ codec->dapm_state = event;
-+ return 0;
-+}
-+
-+static int wm9713_soc_suspend(struct platform_device *pdev,
-+ pm_message_t state)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+
-+ wm9713_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
-+ return 0;
-+}
-+
-+static int wm9713_soc_resume(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+ struct wm9713_priv *wm9713 = codec->private_data;
-+ int i, ret;
-+ u16 *cache = codec->reg_cache;
-+
-+ if ((ret = wm9713_reset(codec, 1)) < 0){
-+ printk(KERN_ERR "could not reset AC97 codec\n");
-+ return ret;
-+ }
-+
-+ wm9713_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
-+
-+ /* do we need to re-start the PLL ? */
-+ if (wm9713->pll_out)
-+ wm9713_set_pll(codec, 0, wm9713->pll_in, wm9713->pll_out);
-+
-+ /* only synchronise the codec if warm reset failed */
-+ if (ret == 0) {
-+ for (i = 2; i < ARRAY_SIZE(wm9713_reg) << 1; i+=2) {
-+ if (i == AC97_POWERDOWN || i == AC97_EXTENDED_MID ||
-+ i == AC97_EXTENDED_MSTATUS || i > 0x66)
-+ continue;
-+ soc_ac97_ops.write(codec->ac97, i, cache[i>>1]);
-+ }
-+ }
-+
-+ if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0)
-+ wm9713_dapm_event(codec, SNDRV_CTL_POWER_D0);
-+
-+ return ret;
-+}
-+
-+static int wm9713_soc_probe(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec;
-+ int ret = 0, reg;
-+
-+ printk(KERN_INFO "WM9713/WM9714 SoC Audio Codec %s\n", WM9713_VERSION);
-+
-+ socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
-+ if (socdev->codec == NULL)
-+ return -ENOMEM;
-+ codec = socdev->codec;
-+ mutex_init(&codec->mutex);
-+
-+ codec->reg_cache =
-+ kzalloc(sizeof(u16) * ARRAY_SIZE(wm9713_reg), GFP_KERNEL);
-+ if (codec->reg_cache == NULL){
-+ ret = -ENOMEM;
-+ goto cache_err;
-+ }
-+ memcpy(codec->reg_cache, wm9713_reg,
-+ sizeof(u16) * ARRAY_SIZE(wm9713_reg));
-+ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm9713_reg);
-+ codec->reg_cache_step = 2;
-+
-+ codec->private_data = kzalloc(sizeof(struct wm9713_priv), GFP_KERNEL);
-+ if (codec->private_data == NULL) {
-+ ret = -ENOMEM;
-+ goto priv_err;
-+ }
-+
-+ codec->name = "WM9713";
-+ codec->owner = THIS_MODULE;
-+ codec->dai = wm9713_dai;
-+ codec->num_dai = ARRAY_SIZE(wm9713_dai);
-+ codec->write = ac97_write;
-+ codec->read = ac97_read;
-+ codec->dapm_event = wm9713_dapm_event;
-+ INIT_LIST_HEAD(&codec->dapm_widgets);
-+ INIT_LIST_HEAD(&codec->dapm_paths);
-+
-+ ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
-+ if (ret < 0)
-+ goto codec_err;
-+
-+ /* register pcms */
-+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
-+ if (ret < 0)
-+ goto pcm_err;
-+
-+ /* do a cold reset for the controller and then try
-+ * a warm reset followed by an optional cold reset for codec */
-+ wm9713_reset(codec, 0);
-+ ret = wm9713_reset(codec, 1);
-+ if (ret < 0) {
-+ printk(KERN_ERR "AC97 link error\n");
-+ goto reset_err;
-+ }
-+
-+ wm9713_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
-+
-+ /* unmute the adc - move to kcontrol */
-+ reg = ac97_read(codec, AC97_CD) & 0x7fff;
-+ ac97_write(codec, AC97_CD, reg);
-+
-+ wm9713_add_controls(codec);
-+ wm9713_add_widgets(codec);
-+ ret = snd_soc_register_card(socdev);
-+ if (ret < 0)
-+ goto reset_err;
-+ return 0;
-+
-+reset_err:
-+ snd_soc_free_pcms(socdev);
-+
-+pcm_err:
-+ snd_soc_free_ac97_codec(codec);
-+
-+codec_err:
-+ kfree(codec->private_data);
-+
-+priv_err:
-+ kfree(codec->reg_cache);
-+
-+cache_err:
-+ kfree(socdev->codec);
-+ socdev->codec = NULL;
-+ return ret;
-+}
-+
-+static int wm9713_soc_remove(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+
-+ if (codec == NULL)
-+ return 0;
-+
-+ snd_soc_dapm_free(socdev);
-+ snd_soc_free_pcms(socdev);
-+ snd_soc_free_ac97_codec(codec);
-+ kfree(codec->private_data);
-+ kfree(codec->reg_cache);
-+ kfree(codec->dai);
-+ kfree(codec);
-+ return 0;
-+}
-+
-+struct snd_soc_codec_device soc_codec_dev_wm9713 = {
-+ .probe = wm9713_soc_probe,
-+ .remove = wm9713_soc_remove,
-+ .suspend = wm9713_soc_suspend,
-+ .resume = wm9713_soc_resume,
-+};
-+
-+EXPORT_SYMBOL_GPL(soc_codec_dev_wm9713);
-+
-+MODULE_DESCRIPTION("ASoC WM9713/WM9714 driver");
-+MODULE_AUTHOR("Liam Girdwood");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/codecs/wm9713.h
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/codecs/wm9713.h
-@@ -0,0 +1,51 @@
-+/*
-+ * wm9713.h -- WM9713 Soc Audio driver
-+ */
-+
-+#ifndef _WM9713_H
-+#define _WM9713_H
-+
-+/* clock inputs */
-+#define WM9713_CLKA_PIN 0
-+#define WM9713_CLKB_PIN 1
-+
-+/* clock divider ID's */
-+#define WM9713_PCMCLK_DIV 0
-+#define WM9713_CLKA_MULT 1
-+#define WM9713_CLKB_MULT 2
-+#define WM9713_HIFI_DIV 3
-+#define WM9713_PCMBCLK_DIV 4
-+
-+/* PCM clk div */
-+#define WM9713_PCMDIV(x) ((x - 1) << 8)
-+
-+/* HiFi Div */
-+#define WM9713_HIFIDIV(x) ((x - 1) << 12)
-+
-+/* MCLK clock mulitipliers */
-+#define WM9713_CLKA_X1 (0 << 1)
-+#define WM9713_CLKA_X2 (1 << 1)
-+#define WM9713_CLKB_X1 (0 << 2)
-+#define WM9713_CLKB_X2 (1 << 2)
-+
-+/* MCLK clock MUX */
-+#define WM9713_CLK_MUX_A (0 << 0)
-+#define WM9713_CLK_MUX_B (1 << 0)
-+
-+/* Voice DAI BCLK divider */
-+#define WM9713_PCMBCLK_DIV_1 (0 << 9)
-+#define WM9713_PCMBCLK_DIV_2 (1 << 9)
-+#define WM9713_PCMBCLK_DIV_4 (2 << 9)
-+#define WM9713_PCMBCLK_DIV_8 (3 << 9)
-+#define WM9713_PCMBCLK_DIV_16 (4 << 9)
-+
-+#define WM9713_DAI_AC97_HIFI 0
-+#define WM9713_DAI_AC97_AUX 1
-+#define WM9713_DAI_PCM_VOICE 2
-+
-+extern struct snd_soc_codec_device soc_codec_dev_wm9713;
-+extern struct snd_soc_codec_dai wm9713_dai[3];
-+
-+int wm9713_reset(struct snd_soc_codec *codec, int try_warm);
-+
-+#endif
-Index: linux-2.6.21-moko/sound/soc/pxa/mainstone.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/pxa/mainstone.c
-@@ -0,0 +1,127 @@
-+/*
-+ * mainstone.c -- SoC audio for Mainstone
-+ *
-+ * Copyright 2005 Wolfson Microelectronics PLC.
-+ * Author: Liam Girdwood
-+ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
-+ *
-+ * Mainstone audio amplifier code taken from arch/arm/mach-pxa/mainstone.c
-+ * Copyright: MontaVista Software Inc.
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or (at your
-+ * option) any later version.
-+ *
-+ * Revision history
-+ * 30th Oct 2005 Initial version.
-+ *
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/moduleparam.h>
-+#include <linux/device.h>
-+#include <linux/i2c.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+
-+#include <asm/arch/pxa-regs.h>
-+#include <asm/arch/mainstone.h>
-+#include <asm/arch/audio.h>
-+
-+#include "../codecs/ac97.h"
-+#include "pxa2xx-pcm.h"
-+#include "pxa2xx-ac97.h"
-+
-+static struct snd_soc_machine mainstone;
-+static long mst_audio_suspend_mask;
-+
-+static int mainstone_suspend(struct platform_device *pdev, pm_message_t state)
-+{
-+ mst_audio_suspend_mask = MST_MSCWR2;
-+ MST_MSCWR2 |= MST_MSCWR2_AC97_SPKROFF;
-+ return 0;
-+}
-+
-+static int mainstone_resume(struct platform_device *pdev)
-+{
-+ MST_MSCWR2 &= mst_audio_suspend_mask | ~MST_MSCWR2_AC97_SPKROFF;
-+ return 0;
-+}
-+
-+static int mainstone_probe(struct platform_device *pdev)
-+{
-+ MST_MSCWR2 &= ~MST_MSCWR2_AC97_SPKROFF;
-+ return 0;
-+}
-+
-+static int mainstone_remove(struct platform_device *pdev)
-+{
-+ MST_MSCWR2 |= MST_MSCWR2_AC97_SPKROFF;
-+ return 0;
-+}
-+
-+static struct snd_soc_machine_config codecs[] = {
-+{
-+ .name = "AC97",
-+ .sname = "AC97 HiFi",
-+ .iface = &pxa_ac97_interface[0],
-+},
-+{
-+ .name = "AC97 Aux",
-+ .sname = "AC97 Aux",
-+ .iface = &pxa_ac97_interface[1],
-+},
-+};
-+
-+static struct snd_soc_machine mainstone = {
-+ .name = "Mainstone",
-+ .probe = mainstone_probe,
-+ .remove = mainstone_remove,
-+ .suspend_pre = mainstone_suspend,
-+ .resume_post = mainstone_resume,
-+ .config = codecs,
-+ .nconfigs = ARRAY_SIZE(codecs),
-+};
-+
-+static struct snd_soc_device mainstone_snd_devdata = {
-+ .machine = &mainstone,
-+ .platform = &pxa2xx_soc_platform,
-+ .codec_dev = &soc_codec_dev_ac97,
-+};
-+
-+static struct platform_device *mainstone_snd_device;
-+
-+static int __init mainstone_init(void)
-+{
-+ int ret;
-+
-+ mainstone_snd_device = platform_device_alloc("soc-audio", -1);
-+ if (!mainstone_snd_device)
-+ return -ENOMEM;
-+
-+ platform_set_drvdata(mainstone_snd_device, &mainstone_snd_devdata);
-+ mainstone_snd_devdata.dev = &mainstone_snd_device->dev;
-+ ret = platform_device_add(mainstone_snd_device);
-+
-+ if (ret)
-+ platform_device_put(mainstone_snd_device);
-+
-+ return ret;
-+}
-+
-+static void __exit mainstone_exit(void)
-+{
-+ platform_device_unregister(mainstone_snd_device);
-+}
-+
-+module_init(mainstone_init);
-+module_exit(mainstone_exit);
-+
-+/* Module information */
-+MODULE_AUTHOR("Liam Girdwood, liam.girdwood at wolfsonmicro.com, www.wolfsonmicro.com");
-+MODULE_DESCRIPTION("ALSA SoC Mainstone");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/pxa/mainstone_baseband.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/pxa/mainstone_baseband.c
-@@ -0,0 +1,212 @@
-+/*
-+ * mainstone_baseband.c
-+ * Mainstone Example Baseband modem -- ALSA Soc Audio Layer
-+ *
-+ * Copyright 2006 Wolfson Microelectronics PLC.
-+ * Author: Liam Girdwood
-+ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or (at your
-+ * option) any later version.
-+ *
-+ * Revision history
-+ * 15th Apr 2006 Initial version.
-+ *
-+ * This is example code to demonstrate connecting a baseband modem to the PCM
-+ * DAI on the WM9713 codec on the Intel Mainstone platform. It is by no means
-+ * complete as it requires code to control the modem.
-+ *
-+ * The architecture consists of the WM9713 AC97 DAI connected to the PXA27x
-+ * AC97 controller and the WM9713 PCM DAI connected to the basebands DAI. The
-+ * baseband is controlled via a serial port. Audio is routed between the PXA27x
-+ * and the baseband via internal WM9713 analog paths.
-+ *
-+ * This driver is not the baseband modem driver. This driver only calls
-+ * functions from the Baseband driver to set up it's PCM DAI.
-+ *
-+ * It's intended to use this driver as follows:-
-+ *
-+ * 1. open() WM9713 PCM audio device.
-+ * 2. open() serial device (for AT commands).
-+ * 3. configure PCM audio device (rate etc) - sets up WM9713 PCM DAI,
-+ * this will also set up the baseband PCM DAI (via calling baseband driver).
-+ * 4. send any further AT commands to set up baseband.
-+ * 5. configure codec audio mixer paths.
-+ * 6. open(), configure and read/write AC97 audio device - to Tx/Rx voice
-+ *
-+ * The PCM audio device is opened but IO is never performed on it as the IO is
-+ * directly between the codec and the baseband (and not the CPU).
-+ *
-+ * TODO:
-+ * o Implement callbacks
-+ */
-+
-+#include <linux/init.h>
-+#include <linux/module.h>
-+#include <linux/platform_device.h>
-+
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+
-+#include <asm/hardware.h>
-+#include <asm/arch/pxa-regs.h>
-+#include <asm/arch/audio.h>
-+#include <asm/arch/ssp.h>
-+
-+#include "../codecs/wm9713.h"
-+#include "pxa2xx-pcm.h"
-+#include "pxa2xx-ac97.h"
-+#include "pxa2xx-ssp.h"
-+
-+static struct snd_soc_machine mainstone;
-+
-+/* Do specific baseband PCM voice startup here */
-+static int baseband_startup(struct snd_pcm_substream *substream)
-+{
-+ return 0;
-+}
-+
-+/* Do specific baseband PCM voice shutdown here */
-+static void baseband_shutdown (struct snd_pcm_substream *substream)
-+{
-+}
-+
-+/* Do specific baseband modem PCM voice hw params init here */
-+static int baseband_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ return 0;
-+}
-+
-+/* Do specific baseband modem PCM voice hw params free here */
-+static int baseband_hw_free(struct snd_pcm_substream *substream)
-+{
-+ return 0;
-+}
-+
-+/*
-+ * Baseband Processor DAI
-+ */
-+static struct snd_soc_cpu_dai baseband_dai =
-+{ .name = "Baseband",
-+ .id = 0,
-+ .type = SND_SOC_DAI_PCM,
-+ .playback = {
-+ .channels_min = 1,
-+ .channels_max = 1,
-+ .rates = SNDRV_PCM_RATE_8000,
-+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
-+ .capture = {
-+ .channels_min = 1,
-+ .channels_max = 1,
-+ .rates = SNDRV_PCM_RATE_8000,
-+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
-+ .ops = {
-+ .startup = baseband_startup,
-+ .shutdown = baseband_shutdown,
-+ .hw_params = baseband_hw_params,
-+ .hw_free = baseband_hw_free,
-+ },
-+};
-+
-+/* PM */
-+static int mainstone_suspend(struct platform_device *pdev, pm_message_t state)
-+{
-+ return 0;
-+}
-+
-+static int mainstone_resume(struct platform_device *pdev)
-+{
-+ return 0;
-+}
-+
-+static int mainstone_probe(struct platform_device *pdev)
-+{
-+ return 0;
-+}
-+
-+static int mainstone_remove(struct platform_device *pdev)
-+{
-+ return 0;
-+}
-+
-+static int mainstone_wm9713_init(struct snd_soc_codec *codec)
-+{
-+ return 0;
-+}
-+
-+/* the physical audio connections between the WM9713, Baseband and pxa2xx */
-+static struct snd_soc_dai_link mainstone_dai[] = {
-+{
-+ .name = "AC97",
-+ .stream_name = "AC97 HiFi",
-+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
-+ .codec_dai = &wm9713_dai[WM9713_DAI_AC97_HIFI],
-+ .init = mainstone_wm9713_init,
-+},
-+{
-+ .name = "AC97 Aux",
-+ .stream_name = "AC97 Aux",
-+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
-+ .codec_dai = &wm9713_dai[WM9713_DAI_AC97_AUX],
-+},
-+{
-+ .name = "Baseband",
-+ .stream_name = "Voice",
-+ .cpu_dai = &baseband_dai,
-+ .codec_dai = &wm9713_dai[WM9713_DAI_PCM_VOICE],
-+},
-+};
-+
-+static struct snd_soc_machine mainstone = {
-+ .name = "Mainstone",
-+ .probe = mainstone_probe,
-+ .remove = mainstone_remove,
-+ .suspend_pre = mainstone_suspend,
-+ .resume_post = mainstone_resume,
-+ .dai_link = mainstone_dai,
-+ .num_links = ARRAY_SIZE(mainstone_dai),
-+};
-+
-+static struct snd_soc_device mainstone_snd_ac97_devdata = {
-+ .machine = &mainstone,
-+ .platform = &pxa2xx_soc_platform,
-+ .codec_dev = &soc_codec_dev_wm9713,
-+};
-+
-+static struct platform_device *mainstone_snd_ac97_device;
-+
-+static int __init mainstone_init(void)
-+{
-+ int ret;
-+
-+ mainstone_snd_ac97_device = platform_device_alloc("soc-audio", -1);
-+ if (!mainstone_snd_ac97_device)
-+ return -ENOMEM;
-+
-+ platform_set_drvdata(mainstone_snd_ac97_device, &mainstone_snd_ac97_devdata);
-+ mainstone_snd_ac97_devdata.dev = &mainstone_snd_ac97_device->dev;
-+
-+ if((ret = platform_device_add(mainstone_snd_ac97_device)) != 0)
-+ platform_device_put(mainstone_snd_ac97_device);
-+
-+ return ret;
-+}
-+
-+static void __exit mainstone_exit(void)
-+{
-+ platform_device_unregister(mainstone_snd_ac97_device);
-+}
-+
-+module_init(mainstone_init);
-+module_exit(mainstone_exit);
-+
-+/* Module information */
-+MODULE_AUTHOR("Liam Girdwood, liam.girdwood at wolfsonmicro.com, www.wolfsonmicro.com");
-+MODULE_DESCRIPTION("Mainstone Example Baseband PCM Interface");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/pxa/mainstone_bluetooth.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/pxa/mainstone_bluetooth.c
-@@ -0,0 +1,371 @@
-+/*
-+ * mainstone_bluetooth.c
-+ * Mainstone Example Bluetooth -- ALSA Soc Audio Layer
-+ *
-+ * Copyright 2006 Wolfson Microelectronics PLC.
-+ * Author: Liam Girdwood
-+ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or (at your
-+ * option) any later version.
-+ *
-+ * Revision history
-+ * 15th May 2006 Initial version.
-+ *
-+ * This is example code to demonstrate connecting a bluetooth codec to the PCM
-+ * DAI on the WM8753 codec on the Intel Mainstone platform. It is by no means
-+ * complete as it requires code to control the BT codec.
-+ *
-+ * The architecture consists of the WM8753 HIFI DAI connected to the PXA27x
-+ * I2S controller and the WM8753 PCM DAI connected to the bluetooth DAI. The
-+ * bluetooth codec and wm8753 are controlled via I2C. Audio is routed between
-+ * the PXA27x and the bluetooth via internal WM8753 analog paths.
-+ *
-+ * This example supports the following audio input/outputs.
-+ *
-+ * o Board mounted Mic and Speaker (spk has amplifier)
-+ * o Headphones via jack socket
-+ * o BT source and sink
-+ *
-+ * This driver is not the bluetooth codec driver. This driver only calls
-+ * functions from the Bluetooth driver to set up it's PCM DAI.
-+ *
-+ * It's intended to use the driver as follows:-
-+ *
-+ * 1. open() WM8753 PCM audio device.
-+ * 2. configure PCM audio device (rate etc) - sets up WM8753 PCM DAI,
-+ * this should also set up the BT codec DAI (via calling bt driver).
-+ * 3. configure codec audio mixer paths.
-+ * 4. open(), configure and read/write HIFI audio device - to Tx/Rx voice
-+ *
-+ * The PCM audio device is opened but IO is never performed on it as the IO is
-+ * directly between the codec and the BT codec (and not the CPU).
-+ *
-+ * TODO:
-+ * o Implement callbacks
-+ */
-+
-+#include <linux/init.h>
-+#include <linux/module.h>
-+#include <linux/platform_device.h>
-+
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+
-+#include <asm/hardware.h>
-+#include <asm/arch/pxa-regs.h>
-+#include <asm/arch/audio.h>
-+#include <asm/arch/ssp.h>
-+
-+#include "../codecs/wm8753.h"
-+#include "pxa2xx-pcm.h"
-+#include "pxa2xx-i2s.h"
-+#include "pxa2xx-ssp.h"
-+
-+static struct snd_soc_machine mainstone;
-+
-+/* Do specific bluetooth PCM startup here */
-+static int bt_startup(struct snd_pcm_substream *substream)
-+{
-+ return 0;
-+}
-+
-+/* Do specific bluetooth PCM shutdown here */
-+static void bt_shutdown (struct snd_pcm_substream *substream)
-+{
-+}
-+
-+/* Do pecific bluetooth PCM hw params init here */
-+static int bt_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ return 0;
-+}
-+
-+/* Do specific bluetooth PCM hw params free here */
-+static int bt_hw_free(struct snd_pcm_substream *substream)
-+{
-+ return 0;
-+}
-+
-+#define BT_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
-+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100)
-+
-+/*
-+ * BT Codec DAI
-+ */
-+static struct snd_soc_cpu_dai bt_dai =
-+{ .name = "Bluetooth",
-+ .id = 0,
-+ .type = SND_SOC_DAI_PCM,
-+ .playback = {
-+ .channels_min = 1,
-+ .channels_max = 1,
-+ .rates = BT_RATES,
-+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
-+ .capture = {
-+ .channels_min = 1,
-+ .channels_max = 1,
-+ .rates = BT_RATES,
-+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
-+ .ops = {
-+ .startup = bt_startup,
-+ .shutdown = bt_shutdown,
-+ .hw_params = bt_hw_params,
-+ .hw_free = bt_hw_free,
-+ },
-+};
-+
-+/* PM */
-+static int mainstone_suspend(struct platform_device *pdev, pm_message_t state)
-+{
-+ return 0;
-+}
-+
-+static int mainstone_resume(struct platform_device *pdev)
-+{
-+ return 0;
-+}
-+
-+static int mainstone_probe(struct platform_device *pdev)
-+{
-+ return 0;
-+}
-+
-+static int mainstone_remove(struct platform_device *pdev)
-+{
-+ return 0;
-+}
-+
-+/*
-+ * Machine audio functions.
-+ *
-+ * The machine now has 3 extra audio controls.
-+ *
-+ * Jack function: Sets function (device plugged into Jack) to nothing (Off)
-+ * or Headphones.
-+ *
-+ * Mic function: Set the on board Mic to On or Off
-+ * Spk function: Set the on board Spk to On or Off
-+ *
-+ * example: BT playback (of far end) and capture (of near end)
-+ * Set Mic and Speaker to On, open BT alsa interface as above and set up
-+ * internal audio paths.
-+ */
-+
-+static int machine_jack_func = 0;
-+static int machine_spk_func = 0;
-+static int machine_mic_func = 0;
-+
-+static int machine_get_jack(struct snd_kcontrol *kcontrol,
-+ struct snd_ctl_elem_value *ucontrol)
-+{
-+ ucontrol->value.integer.value[0] = machine_jack_func;
-+ return 0;
-+}
-+
-+static int machine_set_jack(struct snd_kcontrol *kcontrol,
-+ struct snd_ctl_elem_value *ucontrol)
-+{
-+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-+ machine_jack_func = ucontrol->value.integer.value[0];
-+ snd_soc_dapm_set_endpoint(codec, "Headphone Jack", machine_jack_func);
-+ return 0;
-+}
-+
-+static int machine_get_spk(struct snd_kcontrol *kcontrol,
-+ struct snd_ctl_elem_value *ucontrol)
-+{
-+ ucontrol->value.integer.value[0] = machine_spk_func;
-+ return 0;
-+}
-+
-+static int machine_set_spk(struct snd_kcontrol *kcontrol,
-+ struct snd_ctl_elem_value *ucontrol)
-+{
-+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-+
-+ if (machine_spk_func == ucontrol->value.integer.value[0])
-+ return 0;
-+
-+ machine_spk_func = ucontrol->value.integer.value[0];
-+ snd_soc_dapm_set_endpoint(codec, "Spk", machine_spk_func);
-+ return 1;
-+}
-+
-+static int machine_get_mic(struct snd_kcontrol *kcontrol,
-+ struct snd_ctl_elem_value *ucontrol)
-+{
-+ ucontrol->value.integer.value[0] = machine_spk_func;
-+ return 0;
-+}
-+
-+static int machine_set_mic(struct snd_kcontrol *kcontrol,
-+ struct snd_ctl_elem_value *ucontrol)
-+{
-+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-+
-+ if (machine_spk_func == ucontrol->value.integer.value[0])
-+ return 0;
-+
-+ machine_spk_func = ucontrol->value.integer.value[0];
-+ snd_soc_dapm_set_endpoint(codec, "Mic", machine_mic_func);
-+ return 1;
-+}
-+
-+/* turns on board speaker amp on/off */
-+static int machine_amp_event(struct snd_soc_dapm_widget *w, int event)
-+{
-+#if 0
-+ if (SND_SOC_DAPM_EVENT_ON(event))
-+ /* on */
-+ else
-+ /* off */
-+#endif
-+ return 0;
-+}
-+
-+/* machine dapm widgets */
-+static const struct snd_soc_dapm_widget machine_dapm_widgets[] = {
-+SND_SOC_DAPM_HP("Headphone Jack", NULL),
-+SND_SOC_DAPM_SPK("Spk", machine_amp_event),
-+SND_SOC_DAPM_MIC("Mic", NULL),
-+};
-+
-+/* machine connections to the codec pins */
-+static const char* audio_map[][3] = {
-+
-+ /* headphone connected to LOUT1, ROUT1 */
-+ {"Headphone Jack", NULL, "LOUT"},
-+ {"Headphone Jack", NULL, "ROUT"},
-+
-+ /* speaker connected to LOUT2, ROUT2 */
-+ {"Spk", NULL, "ROUT2"},
-+ {"Spk", NULL, "LOUT2"},
-+
-+ /* mic is connected to MIC1 (via Mic Bias) */
-+ {"MIC1", NULL, "Mic Bias"},
-+ {"Mic Bias", NULL, "Mic"},
-+
-+ {NULL, NULL, NULL},
-+};
-+
-+static const char* jack_function[] = {"Off", "Headphone"};
-+static const char* spk_function[] = {"Off", "On"};
-+static const char* mic_function[] = {"Off", "On"};
-+static const struct soc_enum machine_ctl_enum[] = {
-+ SOC_ENUM_SINGLE_EXT(2, jack_function),
-+ SOC_ENUM_SINGLE_EXT(2, spk_function),
-+ SOC_ENUM_SINGLE_EXT(2, mic_function),
-+};
-+
-+static const struct snd_kcontrol_new wm8753_machine_controls[] = {
-+ SOC_ENUM_EXT("Jack Function", machine_ctl_enum[0], machine_get_jack, machine_set_jack),
-+ SOC_ENUM_EXT("Speaker Function", machine_ctl_enum[1], machine_get_spk, machine_set_spk),
-+ SOC_ENUM_EXT("Mic Function", machine_ctl_enum[2], machine_get_mic, machine_set_mic),
-+};
-+
-+static int mainstone_wm8753_init(struct snd_soc_codec *codec)
-+{
-+ int i, err;
-+
-+ /* not used on this machine - e.g. will never be powered up */
-+ snd_soc_dapm_set_endpoint(codec, "OUT3", 0);
-+ snd_soc_dapm_set_endpoint(codec, "OUT4", 0);
-+ snd_soc_dapm_set_endpoint(codec, "MONO2", 0);
-+ snd_soc_dapm_set_endpoint(codec, "MONO1", 0);
-+ snd_soc_dapm_set_endpoint(codec, "LINE1", 0);
-+ snd_soc_dapm_set_endpoint(codec, "LINE2", 0);
-+ snd_soc_dapm_set_endpoint(codec, "RXP", 0);
-+ snd_soc_dapm_set_endpoint(codec, "RXN", 0);
-+ snd_soc_dapm_set_endpoint(codec, "MIC2", 0);
-+
-+ /* Add machine specific controls */
-+ for (i = 0; i < ARRAY_SIZE(wm8753_machine_controls); i++) {
-+ if ((err = snd_ctl_add(codec->card,
-+ snd_soc_cnew(&wm8753_machine_controls[i],codec, NULL))) < 0)
-+ return err;
-+ }
-+
-+ /* Add machine specific widgets */
-+ for(i = 0; i < ARRAY_SIZE(machine_dapm_widgets); i++) {
-+ snd_soc_dapm_new_control(codec, &machine_dapm_widgets[i]);
-+ }
-+
-+ /* Set up machine specific audio path audio_mapnects */
-+ for(i = 0; audio_map[i][0] != NULL; i++) {
-+ snd_soc_dapm_connect_input(codec, audio_map[i][0], audio_map[i][1], audio_map[i][2]);
-+ }
-+
-+ snd_soc_dapm_sync_endpoints(codec);
-+ return 0;
-+}
-+
-+static struct snd_soc_dai_link mainstone_dai[] = {
-+{ /* Hifi Playback - for similatious use with voice below */
-+ .name = "WM8753",
-+ .stream_name = "WM8753 HiFi",
-+ .cpu_dai = &pxa_i2s_dai,
-+ .codec_dai = &wm8753_dai[WM8753_DAI_HIFI],
-+ .init = mainstone_wm8753_init,
-+},
-+{ /* Voice via BT */
-+ .name = "Bluetooth",
-+ .stream_name = "Voice",
-+ .cpu_dai = &bt_dai,
-+ .codec_dai = &wm8753_dai[WM8753_DAI_VOICE],
-+},
-+};
-+
-+static struct snd_soc_machine mainstone = {
-+ .name = "Mainstone",
-+ .probe = mainstone_probe,
-+ .remove = mainstone_remove,
-+ .suspend_pre = mainstone_suspend,
-+ .resume_post = mainstone_resume,
-+ .dai_link = mainstone_dai,
-+ .num_links = ARRAY_SIZE(mainstone_dai),
-+};
-+
-+static struct snd_soc_device mainstone_snd_wm8753_devdata = {
-+ .machine = &mainstone,
-+ .platform = &pxa2xx_soc_platform,
-+ .codec_dev = &soc_codec_dev_wm8753,
-+};
-+
-+static struct platform_device *mainstone_snd_wm8753_device;
-+
-+static int __init mainstone_init(void)
-+{
-+ int ret;
-+
-+ mainstone_snd_wm8753_device = platform_device_alloc("soc-audio", -1);
-+ if (!mainstone_snd_wm8753_device)
-+ return -ENOMEM;
-+
-+ platform_set_drvdata(mainstone_snd_wm8753_device, &mainstone_snd_wm8753_devdata);
-+ mainstone_snd_wm8753_devdata.dev = &mainstone_snd_wm8753_device->dev;
-+
-+ if((ret = platform_device_add(mainstone_snd_wm8753_device)) != 0)
-+ platform_device_put(mainstone_snd_wm8753_device);
-+
-+ return ret;
-+}
-+
-+static void __exit mainstone_exit(void)
-+{
-+ platform_device_unregister(mainstone_snd_wm8753_device);
-+}
-+
-+module_init(mainstone_init);
-+module_exit(mainstone_exit);
-+
-+/* Module information */
-+MODULE_AUTHOR("Liam Girdwood, liam.girdwood at wolfsonmicro.com, www.wolfsonmicro.com");
-+MODULE_DESCRIPTION("Mainstone Example Bluetooth PCM Interface");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/pxa/mainstone_wm8731.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/pxa/mainstone_wm8731.c
-@@ -0,0 +1,203 @@
-+/*
-+ * mainstone.c -- SoC audio for Mainstone
-+ *
-+ * Copyright 2005 Wolfson Microelectronics PLC.
-+ * Author: Liam Girdwood
-+ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
-+ *
-+ * Mainstone audio amplifier code taken from arch/arm/mach-pxa/mainstone.c
-+ * Copyright: MontaVista Software Inc.
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or (at your
-+ * option) any later version.
-+ *
-+ * Revision history
-+ * 5th June 2006 Initial version.
-+ *
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/moduleparam.h>
-+#include <linux/device.h>
-+#include <linux/i2c.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+
-+#include <asm/arch/pxa-regs.h>
-+#include <asm/arch/mainstone.h>
-+#include <asm/arch/audio.h>
-+
-+#include "../codecs/wm8731.h"
-+#include "pxa2xx-pcm.h"
-+#include "pxa2xx-i2s.h"
-+
-+static struct snd_soc_machine mainstone;
-+
-+static int mainstone_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
-+ unsigned int clk = 0;
-+ int ret = 0;
-+
-+ switch (params_rate(params)) {
-+ case 8000:
-+ case 16000:
-+ case 48000:
-+ case 96000:
-+ clk = 12288000;
-+ break;
-+ case 11025:
-+ case 22050:
-+ case 44100:
-+ clk = 11289600;
-+ break;
-+ }
-+
-+ /* set codec DAI configuration */
-+ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
-+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set cpu DAI configuration */
-+ ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
-+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set the codec system clock for DAC and ADC */
-+ ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8731_SYSCLK, clk,
-+ SND_SOC_CLOCK_IN);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set the I2S system clock as input (unused) */
-+ ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
-+ SND_SOC_CLOCK_IN);
-+ if (ret < 0)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+static struct snd_soc_ops mainstone_ops = {
-+ .hw_params = mainstone_hw_params,
-+};
-+
-+static const struct snd_soc_dapm_widget dapm_widgets[] = {
-+ SND_SOC_DAPM_MIC("Int Mic", NULL),
-+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
-+};
-+
-+static const char* intercon[][3] = {
-+
-+ /* speaker connected to LHPOUT */
-+ {"Ext Spk", NULL, "LHPOUT"},
-+
-+ /* mic is connected to Mic Jack, with WM8731 Mic Bias */
-+ {"MICIN", NULL, "Mic Bias"},
-+ {"Mic Bias", NULL, "Int Mic"},
-+
-+ /* terminator */
-+ {NULL, NULL, NULL},
-+};
-+
-+/*
-+ * Logic for a wm8731 as connected on a Endrelia ETI-B1 board.
-+ */
-+static int mainstone_wm8731_init(struct snd_soc_codec *codec)
-+{
-+ int i;
-+
-+
-+ /* Add specific widgets */
-+ for(i = 0; i < ARRAY_SIZE(dapm_widgets); i++) {
-+ snd_soc_dapm_new_control(codec, &dapm_widgets[i]);
-+ }
-+
-+ /* Set up specific audio path interconnects */
-+ for(i = 0; intercon[i][0] != NULL; i++) {
-+ snd_soc_dapm_connect_input(codec, intercon[i][0], intercon[i][1], intercon[i][2]);
-+ }
-+
-+ /* not connected */
-+ snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0);
-+ snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0);
-+
-+ /* always connected */
-+ snd_soc_dapm_set_endpoint(codec, "Int Mic", 1);
-+ snd_soc_dapm_set_endpoint(codec, "Ext Spk", 1);
-+
-+ snd_soc_dapm_sync_endpoints(codec);
-+
-+ return 0;
-+}
-+
-+static struct snd_soc_dai_link mainstone_dai[] = {
-+{
-+ .name = "WM8731",
-+ .stream_name = "WM8731 HiFi",
-+ .cpu_dai = &pxa_i2s_dai,
-+ .codec_dai = &wm8731_dai,
-+ .init = mainstone_wm8731_init,
-+ .ops = &mainstone_ops,
-+ },
-+};
-+
-+static struct snd_soc_machine mainstone = {
-+ .name = "Mainstone",
-+ .dai_link = mainstone_dai,
-+ .num_links = ARRAY_SIZE(mainstone_dai),
-+};
-+
-+static struct wm8731_setup_data corgi_wm8731_setup = {
-+ .i2c_address = 0x1b,
-+};
-+
-+static struct snd_soc_device mainstone_snd_devdata = {
-+ .machine = &mainstone,
-+ .platform = &pxa2xx_soc_platform,
-+ .codec_dev = &soc_codec_dev_wm8731,
-+ .codec_data = &corgi_wm8731_setup,
-+};
-+
-+static struct platform_device *mainstone_snd_device;
-+
-+static int __init mainstone_init(void)
-+{
-+ int ret;
-+
-+ mainstone_snd_device = platform_device_alloc("soc-audio", -1);
-+ if (!mainstone_snd_device)
-+ return -ENOMEM;
-+
-+ platform_set_drvdata(mainstone_snd_device, &mainstone_snd_devdata);
-+ mainstone_snd_devdata.dev = &mainstone_snd_device->dev;
-+ ret = platform_device_add(mainstone_snd_device);
-+
-+ if (ret)
-+ platform_device_put(mainstone_snd_device);
-+
-+ return ret;
-+}
-+
-+static void __exit mainstone_exit(void)
-+{
-+ platform_device_unregister(mainstone_snd_device);
-+}
-+
-+module_init(mainstone_init);
-+module_exit(mainstone_exit);
-+
-+/* Module information */
-+MODULE_AUTHOR("Liam Girdwood, liam.girdwood at wolfsonmicro.com, www.wolfsonmicro.com");
-+MODULE_DESCRIPTION("ALSA SoC WM8731 Mainstone");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/pxa/mainstone_wm8753.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/pxa/mainstone_wm8753.c
-@@ -0,0 +1,547 @@
-+/*
-+ * mainstone.c -- SoC audio for Mainstone
-+ *
-+ * Copyright 2005 Wolfson Microelectronics PLC.
-+ * Author: Liam Girdwood
-+ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
-+ *
-+ * Mainstone audio amplifier code taken from arch/arm/mach-pxa/mainstone.c
-+ * Copyright: MontaVista Software Inc.
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or (at your
-+ * option) any later version.
-+ *
-+ * Revision history
-+ * 30th Oct 2005 Initial version.
-+ *
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/moduleparam.h>
-+#include <linux/device.h>
-+#include <linux/i2c.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+
-+#include <asm/hardware.h>
-+#include <asm/arch/pxa-regs.h>
-+#include <asm/arch/mainstone.h>
-+#include <asm/arch/audio.h>
-+
-+#include "../codecs/wm8753.h"
-+#include "pxa2xx-pcm.h"
-+#include "pxa2xx-i2s.h"
-+#include "pxa2xx-ssp.h"
-+
-+/*
-+ * SSP GPIO's
-+ */
-+#define GPIO26_SSP1RX_MD (26 | GPIO_ALT_FN_1_IN)
-+#define GPIO25_SSP1TX_MD (25 | GPIO_ALT_FN_2_OUT)
-+#define GPIO23_SSP1CLKS_MD (23 | GPIO_ALT_FN_2_IN)
-+#define GPIO24_SSP1FRMS_MD (24 | GPIO_ALT_FN_2_IN)
-+#define GPIO23_SSP1CLKM_MD (23 | GPIO_ALT_FN_2_OUT)
-+#define GPIO24_SSP1FRMM_MD (24 | GPIO_ALT_FN_2_OUT)
-+#define GPIO53_SSP1SYSCLK_MD (53 | GPIO_ALT_FN_2_OUT)
-+
-+#define GPIO11_SSP2RX_MD (11 | GPIO_ALT_FN_2_IN)
-+#define GPIO13_SSP2TX_MD (13 | GPIO_ALT_FN_1_OUT)
-+#define GPIO22_SSP2CLKS_MD (22 | GPIO_ALT_FN_3_IN)
-+#define GPIO88_SSP2FRMS_MD (88 | GPIO_ALT_FN_3_IN)
-+#define GPIO22_SSP2CLKM_MD (22 | GPIO_ALT_FN_3_OUT)
-+#define GPIO88_SSP2FRMM_MD (88 | GPIO_ALT_FN_3_OUT)
-+#define GPIO22_SSP2SYSCLK_MD (22 | GPIO_ALT_FN_2_OUT)
-+
-+#define GPIO82_SSP3RX_MD (82 | GPIO_ALT_FN_1_IN)
-+#define GPIO81_SSP3TX_MD (81 | GPIO_ALT_FN_1_OUT)
-+#define GPIO84_SSP3CLKS_MD (84 | GPIO_ALT_FN_1_IN)
-+#define GPIO83_SSP3FRMS_MD (83 | GPIO_ALT_FN_1_IN)
-+#define GPIO84_SSP3CLKM_MD (84 | GPIO_ALT_FN_1_OUT)
-+#define GPIO83_SSP3FRMM_MD (83 | GPIO_ALT_FN_1_OUT)
-+#define GPIO45_SSP3SYSCLK_MD (45 | GPIO_ALT_FN_3_OUT)
-+
-+#if 0
-+static struct pxa2xx_gpio ssp_gpios[3][4] = {
-+ {{ /* SSP1 SND_SOC_DAIFMT_CBM_CFM */
-+ .rx = GPIO26_SSP1RX_MD,
-+ .tx = GPIO25_SSP1TX_MD,
-+ .clk = (23 | GPIO_ALT_FN_2_IN),
-+ .frm = (24 | GPIO_ALT_FN_2_IN),
-+ .sys = GPIO53_SSP1SYSCLK_MD,
-+ },
-+ { /* SSP1 SND_SOC_DAIFMT_CBS_CFS */
-+ .rx = GPIO26_SSP1RX_MD,
-+ .tx = GPIO25_SSP1TX_MD,
-+ .clk = (23 | GPIO_ALT_FN_2_OUT),
-+ .frm = (24 | GPIO_ALT_FN_2_OUT),
-+ .sys = GPIO53_SSP1SYSCLK_MD,
-+ },
-+ { /* SSP1 SND_SOC_DAIFMT_CBS_CFM */
-+ .rx = GPIO26_SSP1RX_MD,
-+ .tx = GPIO25_SSP1TX_MD,
-+ .clk = (23 | GPIO_ALT_FN_2_OUT),
-+ .frm = (24 | GPIO_ALT_FN_2_IN),
-+ .sys = GPIO53_SSP1SYSCLK_MD,
-+ },
-+ { /* SSP1 SND_SOC_DAIFMT_CBM_CFS */
-+ .rx = GPIO26_SSP1RX_MD,
-+ .tx = GPIO25_SSP1TX_MD,
-+ .clk = (23 | GPIO_ALT_FN_2_IN),
-+ .frm = (24 | GPIO_ALT_FN_2_OUT),
-+ .sys = GPIO53_SSP1SYSCLK_MD,
-+ }},
-+ {{ /* SSP2 SND_SOC_DAIFMT_CBM_CFM */
-+ .rx = GPIO11_SSP2RX_MD,
-+ .tx = GPIO13_SSP2TX_MD,
-+ .clk = (22 | GPIO_ALT_FN_3_IN),
-+ .frm = (88 | GPIO_ALT_FN_3_IN),
-+ .sys = GPIO22_SSP2SYSCLK_MD,
-+ },
-+ { /* SSP2 SND_SOC_DAIFMT_CBS_CFS */
-+ .rx = GPIO11_SSP2RX_MD,
-+ .tx = GPIO13_SSP2TX_MD,
-+ .clk = (22 | GPIO_ALT_FN_3_OUT),
-+ .frm = (88 | GPIO_ALT_FN_3_OUT),
-+ .sys = GPIO22_SSP2SYSCLK_MD,
-+ },
-+ { /* SSP2 SND_SOC_DAIFMT_CBS_CFM */
-+ .rx = GPIO11_SSP2RX_MD,
-+ .tx = GPIO13_SSP2TX_MD,
-+ .clk = (22 | GPIO_ALT_FN_3_OUT),
-+ .frm = (88 | GPIO_ALT_FN_3_IN),
-+ .sys = GPIO22_SSP2SYSCLK_MD,
-+ },
-+ { /* SSP2 SND_SOC_DAIFMT_CBM_CFS */
-+ .rx = GPIO11_SSP2RX_MD,
-+ .tx = GPIO13_SSP2TX_MD,
-+ .clk = (22 | GPIO_ALT_FN_3_IN),
-+ .frm = (88 | GPIO_ALT_FN_3_OUT),
-+ .sys = GPIO22_SSP2SYSCLK_MD,
-+ }},
-+ {{ /* SSP3 SND_SOC_DAIFMT_CBM_CFM */
-+ .rx = GPIO82_SSP3RX_MD,
-+ .tx = GPIO81_SSP3TX_MD,
-+ .clk = (84 | GPIO_ALT_FN_3_IN),
-+ .frm = (83 | GPIO_ALT_FN_3_IN),
-+ .sys = GPIO45_SSP3SYSCLK_MD,
-+ },
-+ { /* SSP3 SND_SOC_DAIFMT_CBS_CFS */
-+ .rx = GPIO82_SSP3RX_MD,
-+ .tx = GPIO81_SSP3TX_MD,
-+ .clk = (84 | GPIO_ALT_FN_3_OUT),
-+ .frm = (83 | GPIO_ALT_FN_3_OUT),
-+ .sys = GPIO45_SSP3SYSCLK_MD,
-+ },
-+ { /* SSP3 SND_SOC_DAIFMT_CBS_CFM */
-+ .rx = GPIO82_SSP3RX_MD,
-+ .tx = GPIO81_SSP3TX_MD,
-+ .clk = (84 | GPIO_ALT_FN_3_OUT),
-+ .frm = (83 | GPIO_ALT_FN_3_IN),
-+ .sys = GPIO45_SSP3SYSCLK_MD,
-+ },
-+ { /* SSP3 SND_SOC_DAIFMT_CBM_CFS */
-+ .rx = GPIO82_SSP3RX_MD,
-+ .tx = GPIO81_SSP3TX_MD,
-+ .clk = (84 | GPIO_ALT_FN_3_IN),
-+ .frm = (83 | GPIO_ALT_FN_3_OUT),
-+ .sys = GPIO45_SSP3SYSCLK_MD,
-+ }},
-+};
-+#endif
-+
-+static struct snd_soc_machine mainstone;
-+
-+static int mainstone_hifi_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
-+ unsigned int pll_out = 0, bclk = 0, fmt = 0;
-+ int ret = 0;
-+
-+ /*
-+ * The WM8753 is far better at generating accurate audio clocks than the
-+ * pxa2xx I2S controller, so we will use it as master when we can.
-+ * i.e all rates except 8k and 16k as BCLK must be 64 * rate when the
-+ * pxa27x or pxa25x is slave. Note this restriction does not apply to SSP
-+ * I2S emulation mode.
-+ */
-+ switch (params_rate(params)) {
-+ case 8000:
-+ case 16000:
-+ fmt = SND_SOC_DAIFMT_CBS_CFS;
-+ pll_out = 12288000;
-+ break;
-+ case 48000:
-+ fmt = SND_SOC_DAIFMT_CBM_CFS;
-+ bclk = WM8753_BCLK_DIV_4;
-+ pll_out = 12288000;
-+ break;
-+ case 96000:
-+ fmt = SND_SOC_DAIFMT_CBM_CFS;
-+ bclk = WM8753_BCLK_DIV_2;
-+ pll_out = 12288000;
-+ break;
-+ case 11025:
-+ fmt = SND_SOC_DAIFMT_CBM_CFS;
-+ bclk = WM8753_BCLK_DIV_16;
-+ pll_out = 11289600;
-+ break;
-+ case 22050:
-+ fmt = SND_SOC_DAIFMT_CBM_CFS;
-+ bclk = WM8753_BCLK_DIV_8;
-+ pll_out = 11289600;
-+ break;
-+ case 44100:
-+ fmt = SND_SOC_DAIFMT_CBM_CFS;
-+ bclk = WM8753_BCLK_DIV_4;
-+ pll_out = 11289600;
-+ break;
-+ case 88200:
-+ fmt = SND_SOC_DAIFMT_CBM_CFS;
-+ bclk = WM8753_BCLK_DIV_2;
-+ pll_out = 11289600;
-+ break;
-+ }
-+
-+ /* set codec DAI configuration */
-+ ret = codec_dai->dai_ops.set_fmt(codec_dai,
-+ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | fmt);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set cpu DAI configuration */
-+ ret = cpu_dai->dai_ops.set_fmt(cpu_dai,
-+ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | fmt);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set the codec system clock for DAC and ADC */
-+ ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_MCLK, pll_out,
-+ SND_SOC_CLOCK_IN);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set the I2S system clock as input (unused) */
-+ ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
-+ SND_SOC_CLOCK_IN);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set codec BCLK division for sample rate */
-+ ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_BCLKDIV, bclk);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* codec PLL input is 13 MHz */
-+ ret = codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1, 13000000, pll_out);
-+ if (ret < 0)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+static int mainstone_hifi_hw_free(struct snd_pcm_substream *substream)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-+
-+ /* disable the PLL */
-+ return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1, 0, 0);
-+}
-+
-+/*
-+ * Mainstone WM8753 HiFi DAI opserations.
-+ */
-+static struct snd_soc_ops mainstone_hifi_ops = {
-+ .hw_params = mainstone_hifi_hw_params,
-+ .hw_free = mainstone_hifi_hw_free,
-+};
-+
-+static int mainstone_voice_startup(struct snd_pcm_substream *substream)
-+{
-+ /* enable USB on the go MUX so we can use SSPFRM2 */
-+ MST_MSCWR2 |= MST_MSCWR2_USB_OTG_SEL;
-+ MST_MSCWR2 &= ~MST_MSCWR2_USB_OTG_RST;
-+
-+ return 0;
-+}
-+
-+static void mainstone_voice_shutdown(struct snd_pcm_substream *substream)
-+{
-+// struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+
-+ /* disable USB on the go MUX so we can use ttyS0 */
-+ MST_MSCWR2 &= ~MST_MSCWR2_USB_OTG_SEL;
-+ MST_MSCWR2 |= MST_MSCWR2_USB_OTG_RST;
-+
-+ /* liam may need to tristate DAI */
-+}
-+
-+static int mainstone_voice_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
-+ unsigned int pll_out = 0, bclk = 0, pcmdiv = 0;
-+ int ret = 0;
-+
-+ /*
-+ * The WM8753 is far better at generating accurate audio clocks than the
-+ * pxa2xx SSP controller, so we will use it as master when we can.
-+ */
-+ switch (params_rate(params)) {
-+ case 8000:
-+ pll_out = 12288000;
-+ pcmdiv = WM8753_PCM_DIV_6; /* 2.048 MHz */
-+ bclk = WM8753_VXCLK_DIV_8; /* 256kHz */
-+ break;
-+ case 16000:
-+ pll_out = 12288000;
-+ pcmdiv = WM8753_PCM_DIV_3; /* 4.096 MHz */
-+ bclk = WM8753_VXCLK_DIV_8; /* 512kHz */
-+ break;
-+ case 48000:
-+ pll_out = 12288000;
-+ pcmdiv = WM8753_PCM_DIV_1; /* 12.288 MHz */
-+ bclk = WM8753_VXCLK_DIV_8; /* 1.536 MHz */
-+ break;
-+ case 11025:
-+ pll_out = 11289600;
-+ pcmdiv = WM8753_PCM_DIV_4; /* 11.2896 MHz */
-+ bclk = WM8753_VXCLK_DIV_8; /* 352.8 kHz */
-+ break;
-+ case 22050:
-+ pll_out = 11289600;
-+ pcmdiv = WM8753_PCM_DIV_2; /* 11.2896 MHz */
-+ bclk = WM8753_VXCLK_DIV_8; /* 705.6 kHz */
-+ break;
-+ case 44100:
-+ pll_out = 11289600;
-+ pcmdiv = WM8753_PCM_DIV_1; /* 11.2896 MHz */
-+ bclk = WM8753_VXCLK_DIV_8; /* 1.4112 MHz */
-+ break;
-+ }
-+
-+ /* set codec DAI configuration */
-+ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A |
-+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set cpu DAI configuration */
-+ ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
-+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set the codec system clock for DAC and ADC */
-+ ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_PCMCLK, pll_out,
-+ SND_SOC_CLOCK_IN);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set the SSP system clock as input (unused) */
-+ ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_SSP_CLK_PLL, 0,
-+ SND_SOC_CLOCK_IN);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set codec BCLK division for sample rate */
-+ ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_VXCLKDIV, bclk);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set codec PCM division for sample rate */
-+ ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* codec PLL input is 13 MHz */
-+ ret = codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL2, 13000000, pll_out);
-+ if (ret < 0)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+static int mainstone_voice_hw_free(struct snd_pcm_substream *substream)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-+
-+ /* disable the PLL */
-+ return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL2, 0, 0);
-+}
-+
-+static struct snd_soc_ops mainstone_voice_ops = {
-+ .startup = mainstone_voice_startup,
-+ .shutdown = mainstone_voice_shutdown,
-+ .hw_params = mainstone_voice_hw_params,
-+ .hw_free = mainstone_voice_hw_free,
-+};
-+
-+static long mst_audio_suspend_mask;
-+
-+static int mainstone_suspend(struct platform_device *pdev, pm_message_t state)
-+{
-+ mst_audio_suspend_mask = MST_MSCWR2;
-+ MST_MSCWR2 |= MST_MSCWR2_AC97_SPKROFF;
-+ return 0;
-+}
-+
-+static int mainstone_resume(struct platform_device *pdev)
-+{
-+ MST_MSCWR2 &= mst_audio_suspend_mask | ~MST_MSCWR2_AC97_SPKROFF;
-+ return 0;
-+}
-+
-+static int mainstone_probe(struct platform_device *pdev)
-+{
-+ MST_MSCWR2 &= ~MST_MSCWR2_AC97_SPKROFF;
-+ return 0;
-+}
-+
-+static int mainstone_remove(struct platform_device *pdev)
-+{
-+ MST_MSCWR2 |= MST_MSCWR2_AC97_SPKROFF;
-+ return 0;
-+}
-+
-+/* example machine audio_mapnections */
-+static const char* audio_map[][3] = {
-+
-+ /* mic is connected to mic1 - with bias */
-+ {"MIC1", NULL, "Mic Bias"},
-+ {"MIC1N", NULL, "Mic Bias"},
-+ {"Mic Bias", NULL, "Mic1 Jack"},
-+ {"Mic Bias", NULL, "Mic1 Jack"},
-+
-+ {"ACIN", NULL, "ACOP"},
-+ {NULL, NULL, NULL},
-+};
-+
-+/* headphone detect support on my board */
-+static const char * hp_pol[] = {"Headphone", "Speaker"};
-+static const struct soc_enum wm8753_enum =
-+ SOC_ENUM_SINGLE(WM8753_OUTCTL, 1, 2, hp_pol);
-+
-+static const struct snd_kcontrol_new wm8753_mainstone_controls[] = {
-+ SOC_SINGLE("Headphone Detect Switch", WM8753_OUTCTL, 6, 1, 0),
-+ SOC_ENUM("Headphone Detect Polarity", wm8753_enum),
-+};
-+
-+/*
-+ * This is an example machine initialisation for a wm8753 connected to a
-+ * Mainstone II. It is missing logic to detect hp/mic insertions and logic
-+ * to re-route the audio in such an event.
-+ */
-+static int mainstone_wm8753_init(struct snd_soc_codec *codec)
-+{
-+ int i, err;
-+
-+ /* set up mainstone codec pins */
-+ snd_soc_dapm_set_endpoint(codec, "RXP", 0);
-+ snd_soc_dapm_set_endpoint(codec, "RXN", 0);
-+ snd_soc_dapm_set_endpoint(codec, "MIC2", 0);
-+
-+ /* add mainstone specific controls */
-+ for (i = 0; i < ARRAY_SIZE(wm8753_mainstone_controls); i++) {
-+ if ((err = snd_ctl_add(codec->card,
-+ snd_soc_cnew(&wm8753_mainstone_controls[i],codec, NULL))) < 0)
-+ return err;
-+ }
-+
-+ /* set up mainstone specific audio path audio_mapnects */
-+ for(i = 0; audio_map[i][0] != NULL; i++) {
-+ snd_soc_dapm_connect_input(codec, audio_map[i][0], audio_map[i][1], audio_map[i][2]);
-+ }
-+
-+ snd_soc_dapm_sync_endpoints(codec);
-+ return 0;
-+}
-+
-+static struct snd_soc_dai_link mainstone_dai[] = {
-+{ /* Hifi Playback - for similatious use with voice below */
-+ .name = "WM8753",
-+ .stream_name = "WM8753 HiFi",
-+ .cpu_dai = &pxa_i2s_dai,
-+ .codec_dai = &wm8753_dai[WM8753_DAI_HIFI],
-+ .init = mainstone_wm8753_init,
-+ .ops = &mainstone_hifi_ops,
-+},
-+{ /* Voice via BT */
-+ .name = "Bluetooth",
-+ .stream_name = "Voice",
-+ .cpu_dai = &pxa_ssp_dai[1],
-+ .codec_dai = &wm8753_dai[WM8753_DAI_VOICE],
-+ .ops = &mainstone_voice_ops,
-+},
-+};
-+
-+static struct snd_soc_machine mainstone = {
-+ .name = "Mainstone",
-+ .probe = mainstone_probe,
-+ .remove = mainstone_remove,
-+ .suspend_pre = mainstone_suspend,
-+ .resume_post = mainstone_resume,
-+ .dai_link = mainstone_dai,
-+ .num_links = ARRAY_SIZE(mainstone_dai),
-+};
-+
-+static struct wm8753_setup_data mainstone_wm8753_setup = {
-+ .i2c_address = 0x1a,
-+};
-+
-+static struct snd_soc_device mainstone_snd_devdata = {
-+ .machine = &mainstone,
-+ .platform = &pxa2xx_soc_platform,
-+ .codec_dev = &soc_codec_dev_wm8753,
-+ .codec_data = &mainstone_wm8753_setup,
-+};
-+
-+static struct platform_device *mainstone_snd_device;
-+
-+static int __init mainstone_init(void)
-+{
-+ int ret;
-+
-+ mainstone_snd_device = platform_device_alloc("soc-audio", -1);
-+ if (!mainstone_snd_device)
-+ return -ENOMEM;
-+
-+ platform_set_drvdata(mainstone_snd_device, &mainstone_snd_devdata);
-+ mainstone_snd_devdata.dev = &mainstone_snd_device->dev;
-+ ret = platform_device_add(mainstone_snd_device);
-+
-+ if (ret)
-+ platform_device_put(mainstone_snd_device);
-+
-+ /* SSP port 2 slave */
-+ pxa_gpio_mode(GPIO11_SSP2RX_MD);
-+ pxa_gpio_mode(GPIO13_SSP2TX_MD);
-+ pxa_gpio_mode(GPIO22_SSP2CLKS_MD);
-+ pxa_gpio_mode(GPIO88_SSP2FRMS_MD);
-+
-+ return ret;
-+}
-+
-+static void __exit mainstone_exit(void)
-+{
-+ platform_device_unregister(mainstone_snd_device);
-+}
-+
-+module_init(mainstone_init);
-+module_exit(mainstone_exit);
-+
-+/* Module information */
-+MODULE_AUTHOR("Liam Girdwood, liam.girdwood at wolfsonmicro.com, www.wolfsonmicro.com");
-+MODULE_DESCRIPTION("ALSA SoC WM8753 Mainstone");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/pxa/mainstone_wm8974.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/pxa/mainstone_wm8974.c
-@@ -0,0 +1,104 @@
-+/*
-+ * mainstone.c -- SoC audio for Mainstone
-+ *
-+ * Copyright 2005 Wolfson Microelectronics PLC.
-+ * Author: Liam Girdwood
-+ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
-+ *
-+ * Mainstone audio amplifier code taken from arch/arm/mach-pxa/mainstone.c
-+ * Copyright: MontaVista Software Inc.
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or (at your
-+ * option) any later version.
-+ *
-+ * Revision history
-+ * 30th Oct 2005 Initial version.
-+ *
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/moduleparam.h>
-+#include <linux/device.h>
-+#include <linux/i2c.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+
-+#include <asm/arch/pxa-regs.h>
-+#include <asm/arch/mainstone.h>
-+#include <asm/arch/audio.h>
-+
-+#include "../codecs/wm8974.h"
-+#include "pxa2xx-pcm.h"
-+#include "pxa2xx-i2s.h"
-+
-+static struct snd_soc_machine mainstone;
-+
-+static int mainstone_wm8974_init(struct snd_soc_codec *codec)
-+{
-+ return 0;
-+}
-+
-+static struct snd_soc_dai_link mainstone_dai[] = {
-+{
-+ .name = "WM8974",
-+ .stream_name = "WM8974 HiFi",
-+ .cpu_dai = &pxa_i2s_dai,
-+ .codec_dai = &wm8974_dai,
-+ .init = mainstone_wm8974_init,
-+},
-+};
-+
-+static struct snd_soc_machine mainstone = {
-+ .name = "Mainstone",
-+ .dai_link = mainstone_dai,
-+ .num_links = ARRAY_SIZE(mainstone_dai),
-+};
-+
-+static struct wm8974_setup_data mainstone_wm8974_setup = {
-+ .i2c_address = 0x1a,
-+};
-+
-+static struct snd_soc_device mainstone_snd_devdata = {
-+ .machine = &mainstone,
-+ .platform = &pxa2xx_soc_platform,
-+ .codec_dev = &soc_codec_dev_wm8974,
-+ .codec_data = &mainstone_wm8974_setup,
-+};
-+
-+static struct platform_device *mainstone_snd_device;
-+
-+static int __init mainstone_init(void)
-+{
-+ int ret;
-+
-+ mainstone_snd_device = platform_device_alloc("soc-audio", -1);
-+ if (!mainstone_snd_device)
-+ return -ENOMEM;
-+
-+ platform_set_drvdata(mainstone_snd_device, &mainstone_snd_devdata);
-+ mainstone_snd_devdata.dev = &mainstone_snd_device->dev;
-+ ret = platform_device_add(mainstone_snd_device);
-+
-+ if (ret)
-+ platform_device_put(mainstone_snd_device);
-+
-+ return ret;
-+}
-+
-+static void __exit mainstone_exit(void)
-+{
-+ platform_device_unregister(mainstone_snd_device);
-+}
-+
-+module_init(mainstone_init);
-+module_exit(mainstone_exit);
-+
-+/* Module information */
-+MODULE_AUTHOR("Liam Girdwood, liam.girdwood at wolfsonmicro.com, www.wolfsonmicro.com");
-+MODULE_DESCRIPTION("ALSA SoC Mainstone");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/pxa/mainstone_wm9712.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/pxa/mainstone_wm9712.c
-@@ -0,0 +1,172 @@
-+/*
-+ * mainstone.c -- SoC audio for Mainstone
-+ *
-+ * Copyright 2006 Wolfson Microelectronics PLC.
-+ * Author: Liam Girdwood
-+ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
-+ *
-+ * Mainstone audio amplifier code taken from arch/arm/mach-pxa/mainstone.c
-+ * Copyright: MontaVista Software Inc.
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or (at your
-+ * option) any later version.
-+ *
-+ * Revision history
-+ * 29th Jan 2006 Initial version.
-+ *
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/moduleparam.h>
-+#include <linux/device.h>
-+#include <linux/i2c.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+
-+#include <asm/arch/pxa-regs.h>
-+#include <asm/arch/mainstone.h>
-+#include <asm/arch/audio.h>
-+
-+#include "../codecs/wm9712.h"
-+#include "pxa2xx-pcm.h"
-+#include "pxa2xx-ac97.h"
-+
-+static struct snd_soc_machine mainstone;
-+static long mst_audio_suspend_mask;
-+
-+static int mainstone_suspend(struct platform_device *pdev, pm_message_t state)
-+{
-+ mst_audio_suspend_mask = MST_MSCWR2;
-+ MST_MSCWR2 |= MST_MSCWR2_AC97_SPKROFF;
-+ return 0;
-+}
-+
-+static int mainstone_resume(struct platform_device *pdev)
-+{
-+ MST_MSCWR2 &= mst_audio_suspend_mask | ~MST_MSCWR2_AC97_SPKROFF;
-+ return 0;
-+}
-+
-+static int mainstone_probe(struct platform_device *pdev)
-+{
-+ MST_MSCWR2 &= ~MST_MSCWR2_AC97_SPKROFF;
-+ return 0;
-+}
-+
-+static int mainstone_remove(struct platform_device *pdev)
-+{
-+ MST_MSCWR2 |= MST_MSCWR2_AC97_SPKROFF;
-+ return 0;
-+}
-+
-+/* mainstone machine dapm widgets */
-+static const struct snd_soc_dapm_widget mainstone_dapm_widgets[] = {
-+ SND_SOC_DAPM_MIC("Mic (Internal)", NULL),
-+};
-+
-+/* example machine interconnections */
-+static const char* intercon[][3] = {
-+
-+ /* mic is connected to mic1 - with bias */
-+ {"MIC1", NULL, "Mic Bias"},
-+ {"Mic Bias", NULL, "Mic (Internal)"},
-+
-+ {NULL, NULL, NULL},
-+};
-+
-+/*
-+ * This is an example machine initialisation for a wm8753 connected to a
-+ * Mainstone II. It is missing logic to detect hp/mic insertions and logic
-+ * to re-route the audio in such an event.
-+ */
-+static int mainstone_wm9712_init(struct snd_soc_codec *codec)
-+{
-+ int i;
-+
-+ /* set up mainstone codec pins */
-+ snd_soc_dapm_set_endpoint(codec, "RXP", 0);
-+ snd_soc_dapm_set_endpoint(codec, "RXN", 0);
-+ //snd_soc_dapm_set_endpoint(codec, "MIC2", 0);
-+
-+ /* Add mainstone specific widgets */
-+ for(i = 0; i < ARRAY_SIZE(mainstone_dapm_widgets); i++) {
-+ snd_soc_dapm_new_control(codec, &mainstone_dapm_widgets[i]);
-+ }
-+
-+ /* set up mainstone specific audio path interconnects */
-+ for(i = 0; intercon[i][0] != NULL; i++) {
-+ snd_soc_dapm_connect_input(codec, intercon[i][0], intercon[i][1], intercon[i][2]);
-+ }
-+
-+ snd_soc_dapm_sync_endpoints(codec);
-+ return 0;
-+}
-+
-+static struct snd_soc_dai_link mainstone_dai[] = {
-+{
-+ .name = "AC97",
-+ .stream_name = "AC97 HiFi",
-+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
-+ .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI],
-+ .init = mainstone_wm9712_init,
-+},
-+{
-+ .name = "AC97 Aux",
-+ .stream_name = "AC97 Aux",
-+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
-+ .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX],
-+},
-+};
-+
-+static struct snd_soc_machine mainstone = {
-+ .name = "Mainstone",
-+ .probe = mainstone_probe,
-+ .remove = mainstone_remove,
-+ .suspend_pre = mainstone_suspend,
-+ .resume_post = mainstone_resume,
-+ .dai_link = mainstone_dai,
-+ .num_links = ARRAY_SIZE(mainstone_dai),
-+};
-+
-+static struct snd_soc_device mainstone_snd_ac97_devdata = {
-+ .machine = &mainstone,
-+ .platform = &pxa2xx_soc_platform,
-+ .codec_dev = &soc_codec_dev_wm9712,
-+};
-+
-+static struct platform_device *mainstone_snd_ac97_device;
-+
-+static int __init mainstone_init(void)
-+{
-+ int ret;
-+
-+ mainstone_snd_ac97_device = platform_device_alloc("soc-audio", -1);
-+ if (!mainstone_snd_ac97_device)
-+ return -ENOMEM;
-+
-+ platform_set_drvdata(mainstone_snd_ac97_device, &mainstone_snd_ac97_devdata);
-+ mainstone_snd_ac97_devdata.dev = &mainstone_snd_ac97_device->dev;
-+
-+ if((ret = platform_device_add(mainstone_snd_ac97_device)) != 0)
-+ platform_device_put(mainstone_snd_ac97_device);
-+
-+ return ret;
-+}
-+
-+static void __exit mainstone_exit(void)
-+{
-+ platform_device_unregister(mainstone_snd_ac97_device);
-+}
-+
-+module_init(mainstone_init);
-+module_exit(mainstone_exit);
-+
-+/* Module information */
-+MODULE_AUTHOR("Liam Girdwood, liam.girdwood at wolfsonmicro.com, www.wolfsonmicro.com");
-+MODULE_DESCRIPTION("ALSA SoC WM9712 Mainstone");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/pxa/mainstone_wm9713.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/pxa/mainstone_wm9713.c
-@@ -0,0 +1,318 @@
-+/*
-+ * mainstone.c -- SoC audio for Mainstone
-+ *
-+ * Copyright 2006 Wolfson Microelectronics PLC.
-+ * Author: Liam Girdwood
-+ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
-+ *
-+ * Mainstone audio amplifier code taken from arch/arm/mach-pxa/mainstone.c
-+ * Copyright: MontaVista Software Inc.
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or (at your
-+ * option) any later version.
-+ *
-+ * Revision history
-+ * 29th Jan 2006 Initial version.
-+ *
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/moduleparam.h>
-+#include <linux/device.h>
-+#include <linux/i2c.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+
-+#include <asm/arch/pxa-regs.h>
-+#include <asm/arch/mainstone.h>
-+#include <asm/arch/audio.h>
-+
-+#include "../codecs/wm9713.h"
-+#include "pxa2xx-pcm.h"
-+#include "pxa2xx-ac97.h"
-+#include "pxa2xx-ssp.h"
-+
-+#define GPIO11_SSP2RX_MD (11 | GPIO_ALT_FN_2_IN)
-+#define GPIO13_SSP2TX_MD (13 | GPIO_ALT_FN_1_OUT)
-+#define GPIO22_SSP2CLKS_MD (22 | GPIO_ALT_FN_3_IN)
-+#define GPIO88_SSP2FRMS_MD (88 | GPIO_ALT_FN_3_IN)
-+#define GPIO22_SSP2CLKM_MD (22 | GPIO_ALT_FN_3_OUT)
-+#define GPIO88_SSP2FRMM_MD (88 | GPIO_ALT_FN_3_OUT)
-+#define GPIO22_SSP2SYSCLK_MD (22 | GPIO_ALT_FN_2_OUT)
-+
-+static struct snd_soc_machine mainstone;
-+
-+static int mainstone_voice_startup(struct snd_pcm_substream *substream)
-+{
-+ /* enable USB on the go MUX so we can use SSPFRM2 */
-+ MST_MSCWR2 |= MST_MSCWR2_USB_OTG_SEL;
-+ MST_MSCWR2 &= ~MST_MSCWR2_USB_OTG_RST;
-+ return 0;
-+}
-+
-+static void mainstone_voice_shutdown(struct snd_pcm_substream *substream)
-+{
-+ /* disable USB on the go MUX so we can use ttyS0 */
-+ MST_MSCWR2 &= ~MST_MSCWR2_USB_OTG_SEL;
-+ MST_MSCWR2 |= MST_MSCWR2_USB_OTG_RST;
-+}
-+
-+static int mainstone_voice_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
-+ unsigned int bclk = 0, pcmdiv = 0;
-+ int ret = 0;
-+
-+ switch (params_rate(params)) {
-+ case 8000:
-+ pcmdiv = WM9713_PCMDIV(12); /* 2.048 MHz */
-+ bclk = WM9713_PCMBCLK_DIV_16; /* 128kHz */
-+ break;
-+ case 16000:
-+ pcmdiv = WM9713_PCMDIV(6); /* 4.096 MHz */
-+ bclk = WM9713_PCMBCLK_DIV_16; /* 256kHz */
-+ break;
-+ case 48000:
-+ pcmdiv = WM9713_PCMDIV(2); /* 12.288 MHz */
-+ bclk = WM9713_PCMBCLK_DIV_16; /* 512kHz */
-+ break;
-+ }
-+
-+ /* set codec DAI configuration */
-+ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A |
-+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set cpu DAI configuration */
-+ ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
-+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set the SSP system clock as input (unused) */
-+ ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_SSP_CLK_PLL, 0,
-+ SND_SOC_CLOCK_IN);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set codec BCLK division for sample rate */
-+ ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM9713_PCMBCLK_DIV, bclk);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set codec PCM division for sample rate */
-+ ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM9713_PCMCLK_DIV, pcmdiv);
-+ if (ret < 0)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+static struct snd_soc_ops mainstone_voice_ops = {
-+ .startup = mainstone_voice_startup,
-+ .shutdown = mainstone_voice_shutdown,
-+ .hw_params = mainstone_voice_hw_params,
-+};
-+
-+static int test = 0;
-+static int get_test(struct snd_kcontrol *kcontrol,
-+ struct snd_ctl_elem_value *ucontrol)
-+{
-+ ucontrol->value.integer.value[0] = test;
-+ return 0;
-+}
-+
-+static int set_test(struct snd_kcontrol *kcontrol,
-+ struct snd_ctl_elem_value *ucontrol)
-+{
-+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-+
-+ test = ucontrol->value.integer.value[0];
-+ if(test) {
-+
-+ } else {
-+
-+ }
-+ return 0;
-+}
-+
-+static long mst_audio_suspend_mask;
-+
-+static int mainstone_suspend(struct platform_device *pdev, pm_message_t state)
-+{
-+ mst_audio_suspend_mask = MST_MSCWR2;
-+ MST_MSCWR2 |= MST_MSCWR2_AC97_SPKROFF;
-+ return 0;
-+}
-+
-+static int mainstone_resume(struct platform_device *pdev)
-+{
-+ MST_MSCWR2 &= mst_audio_suspend_mask | ~MST_MSCWR2_AC97_SPKROFF;
-+ return 0;
-+}
-+
-+static int mainstone_probe(struct platform_device *pdev)
-+{
-+ MST_MSCWR2 &= ~MST_MSCWR2_AC97_SPKROFF;
-+ return 0;
-+}
-+
-+static int mainstone_remove(struct platform_device *pdev)
-+{
-+ MST_MSCWR2 |= MST_MSCWR2_AC97_SPKROFF;
-+ return 0;
-+}
-+
-+static const char* test_function[] = {"Off", "On"};
-+static const struct soc_enum mainstone_enum[] = {
-+ SOC_ENUM_SINGLE_EXT(2, test_function),
-+};
-+
-+static const struct snd_kcontrol_new mainstone_controls[] = {
-+ SOC_ENUM_EXT("ATest Function", mainstone_enum[0], get_test, set_test),
-+};
-+
-+/* mainstone machine dapm widgets */
-+static const struct snd_soc_dapm_widget mainstone_dapm_widgets[] = {
-+ SND_SOC_DAPM_MIC("Mic 1", NULL),
-+ SND_SOC_DAPM_MIC("Mic 2", NULL),
-+ SND_SOC_DAPM_MIC("Mic 3", NULL),
-+};
-+
-+/* example machine audio_mapnections */
-+static const char* audio_map[][3] = {
-+
-+ /* mic is connected to mic1 - with bias */
-+ {"MIC1", NULL, "Mic Bias"},
-+ {"Mic Bias", NULL, "Mic 1"},
-+ /* mic is connected to mic2A - with bias */
-+ {"MIC2A", NULL, "Mic Bias"},
-+ {"Mic Bias", NULL, "Mic 2"},
-+ /* mic is connected to mic2B - with bias */
-+ {"MIC2B", NULL, "Mic Bias"},
-+ {"Mic Bias", NULL, "Mic 3"},
-+
-+ {NULL, NULL, NULL},
-+};
-+
-+/*
-+ * This is an example machine initialisation for a wm9713 connected to a
-+ * Mainstone II. It is missing logic to detect hp/mic insertions and logic
-+ * to re-route the audio in such an event.
-+ */
-+static int mainstone_wm9713_init(struct snd_soc_codec *codec)
-+{
-+ int i, err;
-+
-+ /* set up mainstone codec pins */
-+ snd_soc_dapm_set_endpoint(codec, "RXP", 0);
-+ snd_soc_dapm_set_endpoint(codec, "RXN", 0);
-+ //snd_soc_dapm_set_endpoint(codec, "MIC2", 0);
-+
-+ /* Add test specific controls */
-+ for (i = 0; i < ARRAY_SIZE(mainstone_controls); i++) {
-+ if ((err = snd_ctl_add(codec->card,
-+ snd_soc_cnew(&mainstone_controls[i],codec, NULL))) < 0)
-+ return err;
-+ }
-+
-+ /* Add mainstone specific widgets */
-+ for(i = 0; i < ARRAY_SIZE(mainstone_dapm_widgets); i++) {
-+ snd_soc_dapm_new_control(codec, &mainstone_dapm_widgets[i]);
-+ }
-+
-+ /* set up mainstone specific audio path audio_mapnects */
-+ for(i = 0; audio_map[i][0] != NULL; i++) {
-+ snd_soc_dapm_connect_input(codec, audio_map[i][0], audio_map[i][1],
-+ audio_map[i][2]);
-+ }
-+
-+ snd_soc_dapm_sync_endpoints(codec);
-+ return 0;
-+}
-+
-+static struct snd_soc_dai_link mainstone_dai[] = {
-+{
-+ .name = "AC97",
-+ .stream_name = "AC97 HiFi",
-+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
-+ .codec_dai = &wm9713_dai[WM9713_DAI_AC97_HIFI],
-+ .init = mainstone_wm9713_init,
-+},
-+{
-+ .name = "AC97 Aux",
-+ .stream_name = "AC97 Aux",
-+ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
-+ .codec_dai = &wm9713_dai[WM9713_DAI_AC97_AUX],
-+},
-+{
-+ .name = "WM9713",
-+ .stream_name = "WM9713 Voice",
-+ .cpu_dai = &pxa_ssp_dai[PXA2XX_DAI_SSP2],
-+ .codec_dai = &wm9713_dai[WM9713_DAI_PCM_VOICE],
-+ .ops = &mainstone_voice_ops,
-+},
-+};
-+
-+static struct snd_soc_machine mainstone = {
-+ .name = "Mainstone",
-+ .probe = mainstone_probe,
-+ .remove = mainstone_remove,
-+ .suspend_pre = mainstone_suspend,
-+ .resume_post = mainstone_resume,
-+ .dai_link = mainstone_dai,
-+ .num_links = ARRAY_SIZE(mainstone_dai),
-+};
-+
-+static struct snd_soc_device mainstone_snd_ac97_devdata = {
-+ .machine = &mainstone,
-+ .platform = &pxa2xx_soc_platform,
-+ .codec_dev = &soc_codec_dev_wm9713,
-+};
-+
-+static struct platform_device *mainstone_snd_ac97_device;
-+
-+static int __init mainstone_init(void)
-+{
-+ int ret;
-+
-+ mainstone_snd_ac97_device = platform_device_alloc("soc-audio", -1);
-+ if (!mainstone_snd_ac97_device)
-+ return -ENOMEM;
-+
-+ platform_set_drvdata(mainstone_snd_ac97_device, &mainstone_snd_ac97_devdata);
-+ mainstone_snd_ac97_devdata.dev = &mainstone_snd_ac97_device->dev;
-+
-+ if((ret = platform_device_add(mainstone_snd_ac97_device)) != 0)
-+ platform_device_put(mainstone_snd_ac97_device);
-+
-+ /* SSP port 2 slave */
-+ pxa_gpio_mode(GPIO11_SSP2RX_MD);
-+ pxa_gpio_mode(GPIO13_SSP2TX_MD);
-+ pxa_gpio_mode(GPIO22_SSP2CLKS_MD);
-+ pxa_gpio_mode(GPIO88_SSP2FRMS_MD);
-+
-+ return ret;
-+}
-+
-+static void __exit mainstone_exit(void)
-+{
-+ platform_device_unregister(mainstone_snd_ac97_device);
-+}
-+
-+module_init(mainstone_init);
-+module_exit(mainstone_exit);
-+
-+/* Module information */
-+MODULE_AUTHOR("Liam Girdwood, liam.girdwood at wolfsonmicro.com, www.wolfsonmicro.com");
-+MODULE_DESCRIPTION("ALSA SoC WM9713 Mainstone");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/pxa/pxa2xx-ssp.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/pxa/pxa2xx-ssp.c
-@@ -0,0 +1,666 @@
-+/*
-+ * pxa2xx-ssp.c -- ALSA Soc Audio Layer
-+ *
-+ * Copyright 2005 Wolfson Microelectronics PLC.
-+ * Author: Liam Girdwood
-+ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or (at your
-+ * option) any later version.
-+ *
-+ * Revision history
-+ * 12th Aug 2005 Initial version.
-+ *
-+ * TODO:
-+ * o Test network mode for > 16bit sample size
-+ */
-+
-+#include <linux/init.h>
-+#include <linux/module.h>
-+#include <linux/platform_device.h>
-+
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/initval.h>
-+#include <sound/pcm_params.h>
-+#include <sound/soc.h>
-+
-+#include <asm/hardware.h>
-+#include <asm/arch/pxa-regs.h>
-+#include <asm/arch/audio.h>
-+#include <asm/arch/ssp.h>
-+
-+#include "pxa2xx-pcm.h"
-+#include "pxa2xx-ssp.h"
-+
-+#define PXA_SSP_DEBUG 0
-+
-+#if PXA_SSP_DEBUG
-+#define dbg(format, arg...) \
-+ printk(KERN_DEBUG format "\n" , ## arg)
-+#else
-+#define dbg(format, arg...) do {} while (0)
-+#endif
-+
-+/*
-+ * SSP audio private data
-+ */
-+struct ssp_priv {
-+ unsigned int sysclk;
-+};
-+
-+static struct ssp_priv ssp_clk[3];
-+static struct ssp_dev ssp[3];
-+#ifdef CONFIG_PM
-+static struct ssp_state ssp_state[3];
-+#endif
-+
-+static struct pxa2xx_pcm_dma_params pxa2xx_ssp1_pcm_mono_out = {
-+ .name = "SSP1 PCM Mono out",
-+ .dev_addr = __PREG(SSDR_P1),
-+ .drcmr = &DRCMRTXSSDR,
-+ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
-+ DCMD_BURST16 | DCMD_WIDTH2,
-+};
-+
-+static struct pxa2xx_pcm_dma_params pxa2xx_ssp1_pcm_mono_in = {
-+ .name = "SSP1 PCM Mono in",
-+ .dev_addr = __PREG(SSDR_P1),
-+ .drcmr = &DRCMRRXSSDR,
-+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
-+ DCMD_BURST16 | DCMD_WIDTH2,
-+};
-+
-+static struct pxa2xx_pcm_dma_params pxa2xx_ssp1_pcm_stereo_out = {
-+ .name = "SSP1 PCM Stereo out",
-+ .dev_addr = __PREG(SSDR_P1),
-+ .drcmr = &DRCMRTXSSDR,
-+ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
-+ DCMD_BURST16 | DCMD_WIDTH4,
-+};
-+
-+static struct pxa2xx_pcm_dma_params pxa2xx_ssp1_pcm_stereo_in = {
-+ .name = "SSP1 PCM Stereo in",
-+ .dev_addr = __PREG(SSDR_P1),
-+ .drcmr = &DRCMRRXSSDR,
-+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
-+ DCMD_BURST16 | DCMD_WIDTH4,
-+};
-+
-+static struct pxa2xx_pcm_dma_params pxa2xx_ssp2_pcm_mono_out = {
-+ .name = "SSP2 PCM Mono out",
-+ .dev_addr = __PREG(SSDR_P2),
-+ .drcmr = &DRCMRTXSS2DR,
-+ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
-+ DCMD_BURST16 | DCMD_WIDTH2,
-+};
-+
-+static struct pxa2xx_pcm_dma_params pxa2xx_ssp2_pcm_mono_in = {
-+ .name = "SSP2 PCM Mono in",
-+ .dev_addr = __PREG(SSDR_P2),
-+ .drcmr = &DRCMRRXSS2DR,
-+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
-+ DCMD_BURST16 | DCMD_WIDTH2,
-+};
-+
-+static struct pxa2xx_pcm_dma_params pxa2xx_ssp2_pcm_stereo_out = {
-+ .name = "SSP2 PCM Stereo out",
-+ .dev_addr = __PREG(SSDR_P2),
-+ .drcmr = &DRCMRTXSS2DR,
-+ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
-+ DCMD_BURST16 | DCMD_WIDTH4,
-+};
-+
-+static struct pxa2xx_pcm_dma_params pxa2xx_ssp2_pcm_stereo_in = {
-+ .name = "SSP2 PCM Stereo in",
-+ .dev_addr = __PREG(SSDR_P2),
-+ .drcmr = &DRCMRRXSS2DR,
-+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
-+ DCMD_BURST16 | DCMD_WIDTH4,
-+};
-+
-+static struct pxa2xx_pcm_dma_params pxa2xx_ssp3_pcm_mono_out = {
-+ .name = "SSP3 PCM Mono out",
-+ .dev_addr = __PREG(SSDR_P3),
-+ .drcmr = &DRCMRTXSS3DR,
-+ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
-+ DCMD_BURST16 | DCMD_WIDTH2,
-+};
-+
-+static struct pxa2xx_pcm_dma_params pxa2xx_ssp3_pcm_mono_in = {
-+ .name = "SSP3 PCM Mono in",
-+ .dev_addr = __PREG(SSDR_P3),
-+ .drcmr = &DRCMRRXSS3DR,
-+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
-+ DCMD_BURST16 | DCMD_WIDTH2,
-+};
-+
-+static struct pxa2xx_pcm_dma_params pxa2xx_ssp3_pcm_stereo_out = {
-+ .name = "SSP3 PCM Stereo out",
-+ .dev_addr = __PREG(SSDR_P3),
-+ .drcmr = &DRCMRTXSS3DR,
-+ .dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG |
-+ DCMD_BURST16 | DCMD_WIDTH4,
-+};
-+
-+static struct pxa2xx_pcm_dma_params pxa2xx_ssp3_pcm_stereo_in = {
-+ .name = "SSP3 PCM Stereo in",
-+ .dev_addr = __PREG(SSDR_P3),
-+ .drcmr = &DRCMRRXSS3DR,
-+ .dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC |
-+ DCMD_BURST16 | DCMD_WIDTH4,
-+};
-+
-+static struct pxa2xx_pcm_dma_params *ssp_dma_params[3][4] = {
-+ {&pxa2xx_ssp1_pcm_mono_out, &pxa2xx_ssp1_pcm_mono_in,
-+ &pxa2xx_ssp1_pcm_stereo_out,&pxa2xx_ssp1_pcm_stereo_in,},
-+ {&pxa2xx_ssp2_pcm_mono_out, &pxa2xx_ssp2_pcm_mono_in,
-+ &pxa2xx_ssp2_pcm_stereo_out, &pxa2xx_ssp2_pcm_stereo_in,},
-+ {&pxa2xx_ssp3_pcm_mono_out, &pxa2xx_ssp3_pcm_mono_in,
-+ &pxa2xx_ssp3_pcm_stereo_out,&pxa2xx_ssp3_pcm_stereo_in,},
-+};
-+
-+static int pxa2xx_ssp_startup(struct snd_pcm_substream *substream)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
-+ int ret = 0;
-+
-+ if (!rtd->dai->cpu_dai->active) {
-+ ret = ssp_init (&ssp[cpu_dai->id], cpu_dai->id + 1,
-+ SSP_NO_IRQ);
-+ if (ret < 0)
-+ return ret;
-+ ssp_disable(&ssp[cpu_dai->id]);
-+ }
-+ return ret;
-+}
-+
-+static void pxa2xx_ssp_shutdown(struct snd_pcm_substream *substream)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
-+
-+ if (!cpu_dai->active) {
-+ ssp_disable(&ssp[cpu_dai->id]);
-+ ssp_exit(&ssp[cpu_dai->id]);
-+ }
-+}
-+
-+#if defined (CONFIG_PXA27x)
-+static int cken[3] = {CKEN23_SSP1, CKEN3_SSP2, CKEN4_SSP3};
-+#else
-+static int cken[3] = {CKEN3_SSP, CKEN9_NSSP, CKEN10_ASSP};
-+#endif
-+
-+#ifdef CONFIG_PM
-+
-+static int pxa2xx_ssp_suspend(struct platform_device *pdev,
-+ struct snd_soc_cpu_dai *dai)
-+{
-+ if (!dai->active)
-+ return 0;
-+
-+ ssp_save_state(&ssp[dai->id], &ssp_state[dai->id]);
-+ pxa_set_cken(cken[dai->id], 0);
-+ return 0;
-+}
-+
-+static int pxa2xx_ssp_resume(struct platform_device *pdev,
-+ struct snd_soc_cpu_dai *dai)
-+{
-+ if (!dai->active)
-+ return 0;
-+
-+ pxa_set_cken(cken[dai->id], 1);
-+ ssp_restore_state(&ssp[dai->id], &ssp_state[dai->id]);
-+ ssp_enable(&ssp[dai->id]);
-+
-+ return 0;
-+}
-+
-+#else
-+#define pxa2xx_ssp_suspend NULL
-+#define pxa2xx_ssp_resume NULL
-+#endif
-+
-+/*
-+ * Set the SSP ports SYSCLK.
-+ */
-+static int pxa2xx_ssp_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
-+ int clk_id, unsigned int freq, int dir)
-+{
-+ int port = cpu_dai->id + 1;
-+ u32 sscr0 = SSCR0_P(port) &
-+ ~(SSCR0_ECS | SSCR0_NCS | SSCR0_MOD | SSCR0_ADC);
-+
-+ dbg("pxa2xx_ssp_set_dai_sysclk id: %d, clk_id %d, freq %d",
-+ cpu_dai->id, clk_id, freq);
-+
-+ switch (clk_id) {
-+ case PXA2XX_SSP_CLK_NET_PLL:
-+ sscr0 |= SSCR0_MOD;
-+ case PXA2XX_SSP_CLK_PLL:
-+ /* Internal PLL is fixed on pxa25x and pxa27x */
-+#ifdef CONFIG_PXA27x
-+ ssp_clk[cpu_dai->id].sysclk = 13000000;
-+#else
-+ ssp_clk[cpu_dai->id].sysclk = 1843200;
-+#endif
-+ break;
-+ case PXA2XX_SSP_CLK_EXT:
-+ ssp_clk[cpu_dai->id].sysclk = freq;
-+ sscr0 |= SSCR0_ECS;
-+ break;
-+ case PXA2XX_SSP_CLK_NET:
-+ ssp_clk[cpu_dai->id].sysclk = freq;
-+ sscr0 |= SSCR0_NCS | SSCR0_MOD;
-+ break;
-+ case PXA2XX_SSP_CLK_AUDIO:
-+ ssp_clk[cpu_dai->id].sysclk = 0;
-+ SSCR0_P(port) |= SSCR0_SerClkDiv(1);
-+ sscr0 |= SSCR0_ADC;
-+ break;
-+ default:
-+ return -ENODEV;
-+ }
-+
-+ /* the SSP CKEN clock must be disabled when changing SSP clock mode */
-+ pxa_set_cken(cken[cpu_dai->id], 0);
-+ SSCR0_P(port) |= sscr0;
-+ pxa_set_cken(cken[cpu_dai->id], 1);
-+ return 0;
-+}
-+
-+/*
-+ * Set the SSP clock dividers.
-+ */
-+static int pxa2xx_ssp_set_dai_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
-+ int div_id, int div)
-+{
-+ int port = cpu_dai->id + 1;
-+
-+ switch (div_id) {
-+ case PXA2XX_SSP_AUDIO_DIV_ACDS:
-+ SSACD_P(port) &= ~ 0x7;
-+ SSACD_P(port) |= SSACD_ACDS(div);
-+ break;
-+ case PXA2XX_SSP_AUDIO_DIV_SCDB:
-+ SSACD_P(port) &= ~0x8;
-+ if (div == PXA2XX_SSP_CLK_SCDB_1)
-+ SSACD_P(port) |= SSACD_SCDB;
-+ break;
-+ case PXA2XX_SSP_DIV_SCR:
-+ SSCR0_P(port) &= ~SSCR0_SCR;
-+ SSCR0_P(port) |= SSCR0_SerClkDiv(div);
-+ break;
-+ default:
-+ return -ENODEV;
-+ }
-+
-+ return 0;
-+}
-+
-+/*
-+ * Configure the PLL frequency pxa27x and (afaik - pxa320 only)
-+ */
-+static int pxa2xx_ssp_set_dai_pll(struct snd_soc_cpu_dai *cpu_dai,
-+ int pll_id, unsigned int freq_in, unsigned int freq_out)
-+{
-+ int port = cpu_dai->id + 1;
-+
-+ SSACD_P(port) &= ~0x70;
-+ switch (freq_out) {
-+ case 5622000:
-+ break;
-+ case 11345000:
-+ SSACD_P(port) |= (0x1 << 4);
-+ break;
-+ case 12235000:
-+ SSACD_P(port) |= (0x2 << 4);
-+ break;
-+ case 14857000:
-+ SSACD_P(port) |= (0x3 << 4);
-+ break;
-+ case 32842000:
-+ SSACD_P(port) |= (0x4 << 4);
-+ break;
-+ case 48000000:
-+ SSACD_P(port) |= (0x5 << 4);
-+ break;
-+ }
-+ return 0;
-+}
-+
-+/*
-+ * Set the active slots in TDM/Network mode
-+ */
-+static int pxa2xx_ssp_set_dai_tdm_slot(struct snd_soc_cpu_dai *cpu_dai,
-+ unsigned int mask, int slots)
-+{
-+ int port = cpu_dai->id + 1;
-+
-+ SSCR0_P(port) &= ~SSCR0_SlotsPerFrm(7);
-+
-+ /* set number of active slots */
-+ SSCR0_P(port) |= SSCR0_SlotsPerFrm(slots);
-+
-+ /* set active slot mask */
-+ SSTSA_P(port) = mask;
-+ SSRSA_P(port) = mask;
-+ return 0;
-+}
-+
-+/*
-+ * Tristate the SSP DAI lines
-+ */
-+static int pxa2xx_ssp_set_dai_tristate(struct snd_soc_cpu_dai *cpu_dai,
-+ int tristate)
-+{
-+ int port = cpu_dai->id + 1;
-+
-+ if (tristate)
-+ SSCR1_P(port) &= ~SSCR1_TTE;
-+ else
-+ SSCR1_P(port) |= SSCR1_TTE;
-+
-+ return 0;
-+}
-+
-+/*
-+ * Set up the SSP DAI format.
-+ * The SSP Port must be inactive before calling this function as the
-+ * physical interface format is changed.
-+ */
-+static int pxa2xx_ssp_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
-+ unsigned int fmt)
-+{
-+ int port = cpu_dai->id + 1;
-+
-+ /* reset port settings */
-+ SSCR0_P(port) = 0;
-+ SSCR1_P(port) = 0;
-+ SSPSP_P(port) = 0;
-+
-+ /* NOTE: I2S emulation is still very much work in progress here */
-+
-+ /* FIXME: this is what wince uses for msb */
-+ if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_MSB) {
-+ SSCR0_P(port) = SSCR0_EDSS | SSCR0_TISSP | SSCR0_DataSize(16);
-+ goto master;
-+ }
-+
-+ /* check for I2S emulation mode - handle it separately */
-+ if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S) {
-+ /* 8.4.11 */
-+
-+ /* Only SSCR0[NCS] or SSCR0[ECS] bit fields settings are optional */
-+ SSCR0_P(port) = SSCR0_EDSS | SSCR0_PSP | SSCR0_DataSize(16);
-+
-+ /* set FIFO thresholds */
-+ SSCR1_P(port) = SSCR1_RxTresh(14) | SSCR1_TxTresh(1);
-+
-+ /* normal: */
-+ /* all bit fields must be cleared except: FSRT = 1 and
-+ * SFRMWDTH = 16, DMYSTART=0,1) */
-+ SSPSP_P(port) = SSPSP_FSRT | SSPSP_SFRMWDTH(16) | SSPSP_DMYSTRT(0);
-+ goto master;
-+ }
-+
-+ SSCR0_P(port) |= SSCR0_PSP;
-+ SSCR1_P(port) = SSCR1_RxTresh(14) | SSCR1_TxTresh(1) |
-+ SSCR1_TRAIL | SSCR1_RWOT;
-+
-+master:
-+ switch(fmt & SND_SOC_DAIFMT_MASTER_MASK) {
-+ case SND_SOC_DAIFMT_CBM_CFM:
-+ SSCR1_P(port) |= (SSCR1_SCLKDIR | SSCR1_SFRMDIR);
-+ break;
-+ case SND_SOC_DAIFMT_CBM_CFS:
-+ SSCR1_P(port) |= SSCR1_SCLKDIR;
-+ break;
-+ case SND_SOC_DAIFMT_CBS_CFM:
-+ SSCR1_P(port) |= SSCR1_SFRMDIR;
-+ break;
-+ case SND_SOC_DAIFMT_CBS_CFS:
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
-+ case SND_SOC_DAIFMT_NB_NF:
-+ SSPSP_P(port) |= SSPSP_SFRMP | SSPSP_FSRT;
-+ break;
-+ case SND_SOC_DAIFMT_IB_IF:
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
-+ case SND_SOC_DAIFMT_DSP_A:
-+ SSPSP_P(port) |= SSPSP_DMYSTRT(1);
-+ case SND_SOC_DAIFMT_DSP_B:
-+ SSPSP_P(port) |= SSPSP_SCMODE(2);
-+ break;
-+ case SND_SOC_DAIFMT_I2S:
-+ case SND_SOC_DAIFMT_MSB:
-+ /* handled above */
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ return 0;
-+}
-+
-+/*
-+ * Set the SSP audio DMA parameters and sample size.
-+ * Can be called multiple times by oss emulation.
-+ */
-+static int pxa2xx_ssp_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
-+ int dma = 0, chn = params_channels(params);
-+ int port = cpu_dai->id + 1;
-+
-+ /* select correct DMA params */
-+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
-+ dma = 1; /* capture DMA offset is 1,3 */
-+ if (chn == 2)
-+ dma += 2; /* stereo DMA offset is 2, mono is 0 */
-+ cpu_dai->dma_data = ssp_dma_params[cpu_dai->id][dma];
-+
-+ dbg("pxa2xx_ssp_hw_params: dma %d", dma);
-+
-+ /* we can only change the settings if the port is not in use */
-+ if (SSCR0_P(port) & SSCR0_SSE)
-+ return 0;
-+
-+ /* clear selected SSP bits */
-+ SSCR0_P(port) &= ~(SSCR0_DSS | SSCR0_EDSS);
-+
-+ /* bit size */
-+ switch(params_format(params)) {
-+ case SNDRV_PCM_FORMAT_S16_LE:
-+ SSCR0_P(port) |= SSCR0_DataSize(16);
-+ break;
-+ case SNDRV_PCM_FORMAT_S24_LE:
-+ SSCR0_P(port) |=(SSCR0_EDSS | SSCR0_DataSize(8));
-+ /* we must be in network mode (2 slots) for 24 bit stereo */
-+ break;
-+ case SNDRV_PCM_FORMAT_S32_LE:
-+ SSCR0_P(port) |= (SSCR0_EDSS | SSCR0_DataSize(16));
-+ /* we must be in network mode (2 slots) for 32 bit stereo */
-+ break;
-+ }
-+
-+ dbg("SSCR0 0x%08x SSCR1 0x%08x SSTO 0x%08x SSPSP 0x%08x SSSR 0x%08x SSACD 0x%08x",
-+ SSCR0_P(port), SSCR1_P(port),
-+ SSTO_P(port), SSPSP_P(port),
-+ SSSR_P(port), SSACD_P(port));
-+
-+ return 0;
-+}
-+
-+static int pxa2xx_ssp_trigger(struct snd_pcm_substream *substream, int cmd)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
-+ int ret = 0;
-+ int port = cpu_dai->id + 1;
-+
-+ switch (cmd) {
-+ case SNDRV_PCM_TRIGGER_RESUME:
-+ ssp_enable(&ssp[cpu_dai->id]);
-+ break;
-+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-+ SSCR1_P(port) |= SSCR1_TSRE;
-+ else
-+ SSCR1_P(port) |= SSCR1_RSRE;
-+ SSSR_P(port) |= SSSR_P(port);
-+ break;
-+ case SNDRV_PCM_TRIGGER_START:
-+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-+ SSCR1_P(port) |= SSCR1_TSRE;
-+ else
-+ SSCR1_P(port) |= SSCR1_RSRE;
-+ ssp_enable(&ssp[cpu_dai->id]);
-+ break;
-+ case SNDRV_PCM_TRIGGER_STOP:
-+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-+ SSCR1_P(port) &= ~SSCR1_TSRE;
-+ else
-+ SSCR1_P(port) &= ~SSCR1_RSRE;
-+ break;
-+ case SNDRV_PCM_TRIGGER_SUSPEND:
-+ ssp_disable(&ssp[cpu_dai->id]);
-+ break;
-+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-+ SSCR1_P(port) &= ~SSCR1_TSRE;
-+ else
-+ SSCR1_P(port) &= ~SSCR1_RSRE;
-+ break;
-+
-+ default:
-+ ret = -EINVAL;
-+ }
-+
-+ dbg("SSCR0 0x%08x SSCR1 0x%08x SSTO 0x%08x SSPSP 0x%08x SSSR 0x%08x",
-+ SSCR0_P(port), SSCR1_P(port),
-+ SSTO_P(port), SSPSP_P(port),
-+ SSSR_P(port));
-+
-+ return ret;
-+}
-+
-+#define PXA2XX_SSP_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
-+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
-+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
-+
-+#define PXA2XX_SSP_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
-+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
-+
-+struct snd_soc_cpu_dai pxa_ssp_dai[] = {
-+ { .name = "pxa2xx-ssp1",
-+ .id = 0,
-+ .type = SND_SOC_DAI_PCM,
-+ .suspend = pxa2xx_ssp_suspend,
-+ .resume = pxa2xx_ssp_resume,
-+ .playback = {
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = PXA2XX_SSP_RATES,
-+ .formats = PXA2XX_SSP_FORMATS,},
-+ .capture = {
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = PXA2XX_SSP_RATES,
-+ .formats = PXA2XX_SSP_FORMATS,},
-+ .ops = {
-+ .startup = pxa2xx_ssp_startup,
-+ .shutdown = pxa2xx_ssp_shutdown,
-+ .trigger = pxa2xx_ssp_trigger,
-+ .hw_params = pxa2xx_ssp_hw_params,},
-+ .dai_ops = {
-+ .set_sysclk = pxa2xx_ssp_set_dai_sysclk,
-+ .set_clkdiv = pxa2xx_ssp_set_dai_clkdiv,
-+ .set_pll = pxa2xx_ssp_set_dai_pll,
-+ .set_fmt = pxa2xx_ssp_set_dai_fmt,
-+ .set_tdm_slot = pxa2xx_ssp_set_dai_tdm_slot,
-+ .set_tristate = pxa2xx_ssp_set_dai_tristate,
-+ },
-+ },
-+ { .name = "pxa2xx-ssp2",
-+ .id = 1,
-+ .type = SND_SOC_DAI_PCM,
-+ .suspend = pxa2xx_ssp_suspend,
-+ .resume = pxa2xx_ssp_resume,
-+ .playback = {
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = PXA2XX_SSP_RATES,
-+ .formats = PXA2XX_SSP_FORMATS,},
-+ .capture = {
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = PXA2XX_SSP_RATES,
-+ .formats = PXA2XX_SSP_FORMATS,},
-+ .ops = {
-+ .startup = pxa2xx_ssp_startup,
-+ .shutdown = pxa2xx_ssp_shutdown,
-+ .trigger = pxa2xx_ssp_trigger,
-+ .hw_params = pxa2xx_ssp_hw_params,},
-+ .dai_ops = {
-+ .set_sysclk = pxa2xx_ssp_set_dai_sysclk,
-+ .set_clkdiv = pxa2xx_ssp_set_dai_clkdiv,
-+ .set_pll = pxa2xx_ssp_set_dai_pll,
-+ .set_fmt = pxa2xx_ssp_set_dai_fmt,
-+ .set_tdm_slot = pxa2xx_ssp_set_dai_tdm_slot,
-+ .set_tristate = pxa2xx_ssp_set_dai_tristate,
-+ },
-+ },
-+ { .name = "pxa2xx-ssp3",
-+ .id = 2,
-+ .type = SND_SOC_DAI_PCM,
-+ .suspend = pxa2xx_ssp_suspend,
-+ .resume = pxa2xx_ssp_resume,
-+ .playback = {
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = PXA2XX_SSP_RATES,
-+ .formats = PXA2XX_SSP_FORMATS,},
-+ .capture = {
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = PXA2XX_SSP_RATES,
-+ .formats = PXA2XX_SSP_FORMATS,},
-+ .ops = {
-+ .startup = pxa2xx_ssp_startup,
-+ .shutdown = pxa2xx_ssp_shutdown,
-+ .trigger = pxa2xx_ssp_trigger,
-+ .hw_params = pxa2xx_ssp_hw_params,},
-+ .dai_ops = {
-+ .set_sysclk = pxa2xx_ssp_set_dai_sysclk,
-+ .set_clkdiv = pxa2xx_ssp_set_dai_clkdiv,
-+ .set_pll = pxa2xx_ssp_set_dai_pll,
-+ .set_fmt = pxa2xx_ssp_set_dai_fmt,
-+ .set_tdm_slot = pxa2xx_ssp_set_dai_tdm_slot,
-+ .set_tristate = pxa2xx_ssp_set_dai_tristate,
-+ },
-+ },
-+};
-+EXPORT_SYMBOL_GPL(pxa_ssp_dai);
-+
-+/* Module information */
-+MODULE_AUTHOR("Liam Girdwood, liam.girdwood at wolfsonmicro.com, www.wolfsonmicro.com");
-+MODULE_DESCRIPTION("pxa2xx SSP/PCM SoC Interface");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/imx/imx-ssi.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/imx/imx-ssi.c
-@@ -0,0 +1,591 @@
-+/*
-+ * imx-ssi.c -- SSI driver for Freescale IMX
-+ *
-+ * Copyright 2006 Wolfson Microelectronics PLC.
-+ * Author: Liam Girdwood
-+ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
-+ *
-+ * Based on mxc-alsa-mc13783 (C) 2006 Freescale.
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or (at your
-+ * option) any later version.
-+ *
-+ * Revision history
-+ * 29th Aug 2006 Initial version.
-+ *
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/init.h>
-+#include <linux/platform_device.h>
-+#include <linux/slab.h>
-+#include <linux/dma-mapping.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/pcm_params.h>
-+#include <sound/soc.h>
-+#include <asm/arch/dma.h>
-+#include <asm/arch/spba.h>
-+#include <asm/arch/clock.h>
-+#include <asm/mach-types.h>
-+#include <asm/hardware.h>
-+
-+#include "imx-ssi.h"
-+#include "imx31-pcm.h"
-+
-+static struct mxc_pcm_dma_params imx_ssi1_pcm_stereo_out = {
-+ .name = "SSI1 PCM Stereo out",
-+ .params = {
-+ .bd_number = 1,
-+ .transfer_type = emi_2_per,
-+ .watermark_level = SDMA_TXFIFO_WATERMARK,
-+ .word_size = TRANSFER_16BIT, // maybe add this in setup func
-+ .per_address = SSI1_STX0,
-+ .event_id = DMA_REQ_SSI1_TX1,
-+ .peripheral_type = SSI,
-+ },
-+};
-+
-+static struct mxc_pcm_dma_params imx_ssi1_pcm_stereo_in = {
-+ .name = "SSI1 PCM Stereo in",
-+ .params = {
-+ .bd_number = 1,
-+ .transfer_type = per_2_emi,
-+ .watermark_level = SDMA_RXFIFO_WATERMARK,
-+ .word_size = TRANSFER_16BIT, // maybe add this in setup func
-+ .per_address = SSI1_SRX0,
-+ .event_id = DMA_REQ_SSI1_RX1,
-+ .peripheral_type = SSI,
-+ },
-+};
-+
-+static struct mxc_pcm_dma_params imx_ssi2_pcm_stereo_out = {
-+ .name = "SSI2 PCM Stereo out",
-+ .params = {
-+ .bd_number = 1,
-+ .transfer_type = per_2_emi,
-+ .watermark_level = SDMA_TXFIFO_WATERMARK,
-+ .word_size = TRANSFER_16BIT, // maybe add this in setup func
-+ .per_address = SSI2_STX0,
-+ .event_id = DMA_REQ_SSI2_TX1,
-+ .peripheral_type = SSI,
-+ },
-+};
-+
-+static struct mxc_pcm_dma_params imx_ssi2_pcm_stereo_in = {
-+ .name = "SSI2 PCM Stereo in",
-+ .params = {
-+ .bd_number = 1,
-+ .transfer_type = per_2_emi,
-+ .watermark_level = SDMA_RXFIFO_WATERMARK,
-+ .word_size = TRANSFER_16BIT, // maybe add this in setup func
-+ .per_address = SSI2_SRX0,
-+ .event_id = DMA_REQ_SSI2_RX1,
-+ .peripheral_type = SSI,
-+ },
-+};
-+
-+/*
-+ * SSI system clock configuration.
-+ */
-+static int imx_ssi_set_dai_sysclk(struct snd_soc_cpu_dai *cpu_dai,
-+ int clk_id, unsigned int freq, int dir)
-+{
-+ u32 scr;
-+
-+ if (cpu_dai->id == IMX_DAI_SSI1)
-+ scr = __raw_readw(SSI1_SCR);
-+ else
-+ scr = __raw_readw(SSI2_SCR);
-+
-+ switch (clk_id) {
-+ case IMX_SSP_SYS_CLK:
-+ if (dir == SND_SOC_CLOCK_OUT)
-+ scr |= SSI_SCR_SYS_CLK_EN;
-+ else
-+ scr &= ~SSI_SCR_SYS_CLK_EN;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ if (cpu_dai->id == IMX_DAI_SSI1)
-+ __raw_writew(scr, SSI1_SCR);
-+ else
-+ __raw_writew(scr, SSI2_SCR);
-+
-+ return 0;
-+}
-+
-+/*
-+ * SSI Clock dividers
-+ */
-+static int imx_ssi_set_dai_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
-+ int div_id, int div)
-+{
-+ u32 stccr;
-+
-+ if (cpu_dai->id == IMX_DAI_SSI1)
-+ stccr = __raw_readw(SSI1_STCCR);
-+ else
-+ stccr = __raw_readw(SSI2_STCCR);
-+
-+ switch (div_id) {
-+ case IMX_SSI_DIV_2:
-+ stccr &= ~SSI_STCCR_DIV2;
-+ stccr |= div;
-+ break;
-+ case IMX_SSI_DIV_PSR:
-+ stccr &= ~SSI_STCCR_PSR;
-+ stccr |= div;
-+ break;
-+ case IMX_SSI_DIV_PM:
-+ stccr &= ~0xff;
-+ stccr |= SSI_STCCR_PM(div);
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ if (cpu_dai->id == IMX_DAI_SSI1)
-+ __raw_writew(stccr, SSI1_STCCR);
-+ else
-+ __raw_writew(stccr, SSI2_STCCR);
-+
-+ return 0;
-+}
-+
-+/*
-+ * SSI Network Mode or TDM slots configuration.
-+ */
-+static int imx_ssi_set_dai_tdm_slot(struct snd_soc_cpu_dai *cpu_dai,
-+ unsigned int mask, int slots)
-+{
-+ u32 stmsk, srmsk, scr, stccr;
-+
-+ if (cpu_dai->id == IMX_DAI_SSI1) {
-+ stmsk = __raw_readw(SSI1_STMSK);
-+ srmsk = __raw_readw(SSI1_SRMSK);
-+ scr = __raw_readw(SSI1_SCR);
-+ stccr = __raw_readw(SSI1_STCCR);
-+ } else {
-+ stmsk = __raw_readw(SSI2_STMSK);
-+ srmsk = __raw_readw(SSI2_SRMSK);
-+ scr = __raw_readw(SSI2_SCR);
-+ stccr = __raw_readw(SSI2_STCCR);
-+ }
-+
-+ stmsk = srmsk = mask;
-+ scr |= SSI_SCR_NET;
-+ stccr &= ~0x1f00;
-+ stccr |= SSI_STCCR_DC(slots);
-+
-+ if (cpu_dai->id == IMX_DAI_SSI1) {
-+ __raw_writew(stmsk, SSI1_STMSK);
-+ __raw_writew(srmsk, SSI1_SRMSK);
-+ __raw_writew(scr, SSI1_SCR);
-+ __raw_writew(stccr, SSI1_STCCR);
-+ } else {
-+ __raw_writew(stmsk, SSI2_STMSK);
-+ __raw_writew(srmsk, SSI2_SRMSK);
-+ __raw_writew(scr, SSI2_SCR);
-+ __raw_writew(stccr, SSI2_STCCR);
-+ }
-+
-+ return 0;
-+}
-+
-+/*
-+ * SSI DAI format configuration.
-+ */
-+static int imx_ssi_set_dai_fmt(struct snd_soc_cpu_dai *cpu_dai,
-+ unsigned int fmt)
-+{
-+ u32 stcr = 0, srcr = 0;
-+
-+ /* DAI mode */
-+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
-+ case SND_SOC_DAIFMT_I2S:
-+ stcr |= SSI_STCR_TSCKP | SSI_STCR_TFSI |
-+ SSI_STCR_TEFS | SSI_STCR_TXBIT0;
-+ srcr |= SSI_SRCR_RSCKP | SSI_SRCR_RFSI |
-+ SSI_SRCR_REFS | SSI_SRCR_RXBIT0;
-+ break;
-+ case SND_SOC_DAIFMT_LEFT_J:
-+ stcr |= SSI_STCR_TSCKP | SSI_STCR_TFSI | SSI_STCR_TXBIT0;
-+ srcr |= SSI_SRCR_RSCKP | SSI_SRCR_RFSI | SSI_SRCR_RXBIT0;
-+ break;
-+ case SND_SOC_DAIFMT_DSP_B:
-+ stcr |= SSI_STCR_TEFS; // data 1 bit after sync
-+ srcr |= SSI_SRCR_REFS; // data 1 bit after sync
-+ case SND_SOC_DAIFMT_DSP_A:
-+ stcr |= SSI_STCR_TFSL; // frame is 1 bclk long
-+ srcr |= SSI_SRCR_RFSL; // frame is 1 bclk long
-+
-+ /* DAI clock inversion */
-+ switch(fmt & SND_SOC_DAIFMT_INV_MASK) {
-+ case SND_SOC_DAIFMT_IB_IF:
-+ stcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP;
-+ srcr |= SSI_SRCR_RFSI | SSI_SRCR_RSCKP;
-+ break;
-+ case SND_SOC_DAIFMT_IB_NF:
-+ stcr |= SSI_STCR_TSCKP;
-+ srcr |= SSI_SRCR_RSCKP;
-+ break;
-+ case SND_SOC_DAIFMT_NB_IF:
-+ stcr |= SSI_STCR_TFSI;
-+ srcr |= SSI_SRCR_RFSI;
-+ break;
-+ }
-+ break;
-+ }
-+
-+ /* DAI clock master masks */
-+ switch(fmt & SND_SOC_DAIFMT_CLOCK_MASK){
-+ case SND_SOC_DAIFMT_CBM_CFM:
-+ stcr |= SSI_STCR_TFDIR | SSI_STCR_TXDIR;
-+ srcr |= SSI_SRCR_RFDIR | SSI_SRCR_RXDIR;
-+ break;
-+ case SND_SOC_DAIFMT_CBS_CFM:
-+ stcr |= SSI_STCR_TFDIR;
-+ srcr |= SSI_SRCR_RFDIR;
-+ break;
-+ case SND_SOC_DAIFMT_CBM_CFS:
-+ stcr |= SSI_STCR_TXDIR;
-+ srcr |= SSI_SRCR_RXDIR;
-+ break;
-+ }
-+
-+ /* async */
-+ //if (rtd->cpu_dai->flags & SND_SOC_DAI_ASYNC)
-+ // SSI1_SCR |= SSI_SCR_SYN;
-+
-+ if (cpu_dai->id == IMX_DAI_SSI1) {
-+ __raw_writew(stcr, SSI1_STCR);
-+ __raw_writew(0, SSI1_STCCR);
-+ __raw_writew(srcr, SSI1_SRCR);
-+ __raw_writew(0, SSI1_SRCCR);
-+ } else {
-+ __raw_writew(stcr, SSI2_STCR);
-+ __raw_writew(0, SSI2_STCCR);
-+ __raw_writew(srcr, SSI2_SRCR);
-+ __raw_writew(0, SSI2_SRCCR);
-+ }
-+
-+ return 0;
-+}
-+
-+static int imx_ssi_set_dai_tristate(struct snd_soc_cpu_dai *cpu_dai,
-+ int tristate)
-+{
-+ // via GPIO ??
-+ return 0;
-+}
-+
-+static int imx_ssi_startup(struct snd_pcm_substream *substream)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
-+
-+ if (cpu_dai->id == IMX_DAI_SSI1)
-+ mxc_clks_enable(SSI1_BAUD);
-+ else
-+ mxc_clks_enable(SSI2_BAUD);
-+ return 0;
-+}
-+
-+static int imx_ssi_hw_tx_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
-+ u32 stccr, stcr;
-+
-+ if (cpu_dai->id == IMX_DAI_SSI1) {
-+ stccr = __raw_readw(SSI1_STCCR) & 0x600ff;
-+ stcr = __raw_readw(SSI1_STCR);
-+ } else {
-+ stccr = __raw_readw(SSI2_STCCR) & 0x600ff;
-+ stcr = __raw_readw(SSI2_STCR);
-+ }
-+
-+ /* DAI data (word) size */
-+ switch(params_format(params)) {
-+ case SNDRV_PCM_FORMAT_S16_LE:
-+ stccr |= SSI_STCCR_WL(16);
-+ break;
-+ case SNDRV_PCM_FORMAT_S20_3LE:
-+ stccr |= SSI_STCCR_WL(20);
-+ break;
-+ case SNDRV_PCM_FORMAT_S24_LE:
-+ stccr |= SSI_STCCR_WL(24);
-+ break;
-+ }
-+
-+ /* TDM - todo, only fifo 0 atm */
-+ stcr |= SSI_STCR_TFEN0;
-+ stccr |= SSI_STCCR_DC(params_channels(params));
-+
-+ if (cpu_dai->id == IMX_DAI_SSI1) {
-+ __raw_writew(stcr, SSI1_STCR);
-+ __raw_writew(stccr, SSI1_STCCR);
-+ } else {
-+ __raw_writew(stcr, SSI2_STCR);
-+ __raw_writew(stccr, SSI2_STCCR);
-+ }
-+
-+ return 0;
-+}
-+
-+static int imx_ssi_hw_rx_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
-+ u32 srccr, srcr;
-+
-+ if (cpu_dai->id == IMX_DAI_SSI1) {
-+ srccr = __raw_readw(SSI1_SRCCR) & 0x600ff;
-+ srcr = __raw_readw(SSI1_SRCR);
-+ } else {
-+ srccr = __raw_readw(SSI2_SRCCR) & 0x600ff;
-+ srcr = __raw_readw(SSI2_SRCR);
-+ }
-+
-+ /* DAI data (word) size */
-+ switch(params_format(params)) {
-+ case SNDRV_PCM_FORMAT_S16_LE:
-+ srccr |= SSI_SRCCR_WL(16);
-+ break;
-+ case SNDRV_PCM_FORMAT_S20_3LE:
-+ srccr |= SSI_SRCCR_WL(20);
-+ break;
-+ case SNDRV_PCM_FORMAT_S24_LE:
-+ srccr |= SSI_SRCCR_WL(24);
-+ break;
-+ }
-+
-+ /* TDM - todo, only fifo 0 atm */
-+ srcr |= SSI_SRCR_RFEN0;
-+ srccr |= SSI_SRCCR_DC(params_channels(params));
-+
-+ if (cpu_dai->id == IMX_DAI_SSI1) {
-+ __raw_writew(srcr, SSI1_SRCR);
-+ __raw_writew(srccr, SSI1_SRCCR);
-+ } else {
-+ __raw_writew(srcr, SSI2_SRCR);
-+ __raw_writew(srccr, SSI2_SRCCR);
-+ }
-+ return 0;
-+}
-+
-+static int imx_ssi_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
-+
-+ /* Tx/Rx config */
-+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-+ if (cpu_dai->id == IMX_DAI_SSI1)
-+ cpu_dai->dma_data = &imx_ssi1_pcm_stereo_out;
-+ else
-+ cpu_dai->dma_data = &imx_ssi2_pcm_stereo_out;
-+ return imx_ssi_hw_tx_params(substream, params);
-+ } else {
-+ if (cpu_dai->id == IMX_DAI_SSI1)
-+ cpu_dai->dma_data = &imx_ssi1_pcm_stereo_in;
-+ else
-+ cpu_dai->dma_data = &imx_ssi2_pcm_stereo_in;
-+ return imx_ssi_hw_rx_params(substream, params);
-+ }
-+}
-+
-+static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
-+ u32 scr, sier;
-+
-+ if (cpu_dai->id == IMX_DAI_SSI1) {
-+ scr = __raw_readw(SSI1_SCR) & 0x600ff;
-+ sier = __raw_readw(SSI1_SIER);
-+ } else {
-+ scr = __raw_readw(SSI2_SCR) & 0x600ff;
-+ sier = __raw_readw(SSI2_SIER);
-+ }
-+
-+ switch (cmd) {
-+ case SNDRV_PCM_TRIGGER_START:
-+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-+ scr |= SSI_SCR_TE;
-+ sier |= SSI_SIER_TDMAE;
-+ } else {
-+ scr |= SSI_SCR_RE;
-+ sier |= SSI_SIER_RDMAE;
-+ }
-+ scr |= SSI_SCR_SSIEN;
-+ break;
-+ case SNDRV_PCM_TRIGGER_RESUME:
-+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-+ scr |= SSI_SCR_TE;
-+ else
-+ scr |= SSI_SCR_RE;
-+ scr |= SSI_SCR_SSIEN;
-+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-+ sier |= SSI_SIER_TDMAE;
-+ else
-+ sier |= SSI_SIER_RDMAE;
-+ break;
-+ case SNDRV_PCM_TRIGGER_SUSPEND:
-+ scr &= ~SSI_SCR_SSIEN;
-+ case SNDRV_PCM_TRIGGER_STOP:
-+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-+ scr &= ~SSI_SCR_TE;
-+ else
-+ scr &= ~SSI_SCR_RE;
-+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-+ sier &= ~SSI_SIER_TDMAE;
-+ else
-+ sier &= ~SSI_SIER_TDMAE;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ if (cpu_dai->id == IMX_DAI_SSI1) {
-+ __raw_writew(scr, SSI1_SCR);
-+ __raw_writew(sier, SSI1_SIER);
-+ } else {
-+ __raw_writew(scr, SSI2_SCR);
-+ __raw_writew(sier, SSI2_SIER);
-+ }
-+
-+ return 0;
-+}
-+
-+static void imx_ssi_shutdown(struct snd_pcm_substream *substream)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
-+
-+ /* shutdown SSI */
-+ if (!cpu_dai->active) {
-+ if(cpu_dai->id == IMX_DAI_SSI1) {
-+ __raw_writew(__raw_readw(SSI1_SCR) & ~SSI_SCR_SSIEN, SSI1_SCR);
-+ mxc_clks_disable(SSI1_BAUD);
-+ } else {
-+ __raw_writew(__raw_readw(SSI2_SCR) & ~SSI_SCR_SSIEN, SSI2_SCR);
-+ mxc_clks_disable(SSI2_BAUD);
-+ }
-+ }
-+}
-+
-+#ifdef CONFIG_PM
-+static int imx_ssi_suspend(struct platform_device *dev,
-+ struct snd_soc_cpu_dai *dai)
-+{
-+ if(!dai->active)
-+ return 0;
-+
-+ // do we need to disable any clocks
-+
-+ return 0;
-+}
-+
-+static int imx_ssi_resume(struct platform_device *pdev,
-+ struct snd_soc_cpu_dai *dai)
-+{
-+ if(!dai->active)
-+ return 0;
-+
-+ // do we need to enable any clocks
-+ return 0;
-+}
-+
-+#else
-+#define imx_ssi_suspend NULL
-+#define imx_ssi_resume NULL
-+#endif
-+
-+#define IMX_SSI_RATES \
-+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \
-+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
-+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
-+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \
-+ SNDRV_PCM_RATE_96000)
-+
-+#define IMX_SSI_BITS \
-+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
-+ SNDRV_PCM_FMTBIT_S24_LE)
-+
-+struct snd_soc_cpu_dai imx_ssi_pcm_dai[] = {
-+{
-+ .name = "imx-i2s-1",
-+ .id = IMX_DAI_SSI1,
-+ .type = SND_SOC_DAI_I2S,
-+ .suspend = imx_ssi_suspend,
-+ .resume = imx_ssi_resume,
-+ .playback = {
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .formats = IMX_SSI_BITS,
-+ .rates = IMX_SSI_RATES,},
-+ .capture = {
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .formats = IMX_SSI_BITS,
-+ .rates = IMX_SSI_RATES,},
-+ .ops = {
-+ .startup = imx_ssi_startup,
-+ .shutdown = imx_ssi_shutdown,
-+ .trigger = imx_ssi_trigger,
-+ .hw_params = imx_ssi_hw_params,},
-+ .dai_ops = {
-+ .set_sysclk = imx_ssi_set_dai_sysclk,
-+ .set_clkdiv = imx_ssi_set_dai_clkdiv,
-+ .set_fmt = imx_ssi_set_dai_fmt,
-+ .set_tdm_slot = imx_ssi_set_dai_tdm_slot,
-+ .set_tristate = imx_ssi_set_dai_tristate,
-+ },
-+},
-+{
-+ .name = "imx-i2s-2",
-+ .id = IMX_DAI_SSI2,
-+ .type = SND_SOC_DAI_I2S,
-+ .suspend = imx_ssi_suspend,
-+ .resume = imx_ssi_resume,
-+ .playback = {
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .formats = IMX_SSI_BITS,
-+ .rates = IMX_SSI_RATES,},
-+ .capture = {
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .formats = IMX_SSI_BITS,
-+ .rates = IMX_SSI_RATES,},
-+ .ops = {
-+ .startup = imx_ssi_startup,
-+ .shutdown = imx_ssi_shutdown,
-+ .trigger = imx_ssi_trigger,
-+ .hw_params = imx_ssi_hw_params,},
-+ .dai_ops = {
-+ .set_sysclk = imx_ssi_set_dai_sysclk,
-+ .set_clkdiv = imx_ssi_set_dai_clkdiv,
-+ .set_fmt = imx_ssi_set_dai_fmt,
-+ .set_tdm_slot = imx_ssi_set_dai_tdm_slot,
-+ .set_tristate = imx_ssi_set_dai_tristate,
-+ },
-+},};
-+EXPORT_SYMBOL_GPL(imx_ssi_pcm_dai);
-+
-+/* Module information */
-+MODULE_AUTHOR("Liam Girdwood, liam.girdwood at wolfsonmicro.com, www.wolfsonmicro.com");
-+MODULE_DESCRIPTION("i.MX ASoC I2S driver");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/imx/Kconfig
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/imx/Kconfig
-@@ -0,0 +1,31 @@
-+menu "SoC Audio for the Freescale i.MX"
-+
-+config SND_MXC_SOC
-+ tristate "SoC Audio for the Freescale i.MX CPU"
-+ depends on ARCH_MXC && SND
-+ select SND_PCM
-+ help
-+ Say Y or M if you want to add support for codecs attached to
-+ the MXC AC97, I2S or SSP interface. You will also need
-+ to select the audio interfaces to support below.
-+
-+config SND_MXC_AC97
-+ tristate
-+ select SND_AC97_CODEC
-+
-+config SND_MXC_SOC_AC97
-+ tristate
-+ select AC97_BUS
-+
-+config SND_MXC_SOC_SSI
-+ tristate
-+
-+config SND_SOC_MX31ADS_WM8753
-+ tristate "SoC Audio support for MX31 - WM8753"
-+ depends on SND_MXC_SOC && ARCH_MX3
-+ select SND_MXC_SOC_SSI
-+ help
-+ Say Y if you want to add support for SoC audio on MX31ADS
-+ with the WM8753.
-+
-+endmenu
-Index: linux-2.6.21-moko/sound/soc/imx/Makefile
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/imx/Makefile
-@@ -0,0 +1,18 @@
-+# i.MX Platform Support
-+snd-soc-imx21-objs := imx21-pcm.o
-+snd-soc-imx31-objs := imx31-pcm.o
-+snd-soc-imx-ac97-objs := imx-ac97.o
-+snd-soc-imx-ssi-objs := imx-ssi.o
-+
-+obj-$(CONFIG_SND_MXC_SOC) += snd-soc-imx31.o
-+obj-$(CONFIG_SND_MXC_SOC_AC97) += snd-soc-imx-ac97.o
-+obj-$(CONFIG_SND_MXC_SOC_SSI) += snd-soc-imx-ssi.o
-+
-+# i.MX Machine Support
-+snd-soc-mx31ads-wm8753-objs := mx31ads_wm8753.o
-+obj-$(CONFIG_SND_SOC_MX31ADS_WM8753) += snd-soc-mx31ads-wm8753.o
-+snd-soc-mx21ads-wm8753-objs := mx21ads_wm8753.o
-+obj-$(CONFIG_SND_SOC_MX21ADS_WM8753) += snd-soc-mx21ads-wm8753.o
-+snd-soc-mx21ads-wm8731-objs := mx21ads_wm8731.o
-+obj-$(CONFIG_SND_SOC_MX21ADS_WM8731) += snd-soc-mx21ads-wm8731.o
-+
-Index: linux-2.6.21-moko/sound/soc/codecs/wm8711.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/codecs/wm8711.c
-@@ -0,0 +1,715 @@
-+/*
-+ * wm8711.c -- WM8711 ALSA SoC Audio driver
-+ *
-+ * Copyright 2006 Wolfson Microelectronics
-+ *
-+ * Author: Mike Arthur <linux at wolfsonmicro.com>
-+ *
-+ * Based on wm8731.c by Richard Purdie
-+ *
-+ * 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.
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/moduleparam.h>
-+#include <linux/init.h>
-+#include <linux/delay.h>
-+#include <linux/pm.h>
-+#include <linux/i2c.h>
-+#include <linux/platform_device.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/pcm_params.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+#include <sound/initval.h>
-+
-+#include "wm8711.h"
-+
-+#define AUDIO_NAME "wm8711"
-+#define WM8711_VERSION "0.3"
-+
-+/*
-+ * Debug
-+ */
-+
-+#define WM8711_DEBUG 0
-+
-+#ifdef WM8711_DEBUG
-+#define dbg(format, arg...) \
-+ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
-+#else
-+#define dbg(format, arg...) do {} while (0)
-+#endif
-+#define err(format, arg...) \
-+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
-+#define info(format, arg...) \
-+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
-+#define warn(format, arg...) \
-+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
-+
-+struct snd_soc_codec_device soc_codec_dev_wm8711;
-+
-+/* codec private data */
-+struct wm8711_priv {
-+ unsigned int sysclk;
-+};
-+
-+/*
-+ * wm8711 register cache
-+ * We can't read the WM8711 register space when we are
-+ * using 2 wire for device control, so we cache them instead.
-+ * There is no point in caching the reset register
-+ */
-+static const u16 wm8711_reg[WM8711_CACHEREGNUM] = {
-+ 0x0079, 0x0079, 0x000a, 0x0008,
-+ 0x009f, 0x000a, 0x0000, 0x0000
-+};
-+
-+/*
-+ * read wm8711 register cache
-+ */
-+static inline unsigned int wm8711_read_reg_cache(struct snd_soc_codec * codec,
-+ unsigned int reg)
-+{
-+ u16 *cache = codec->reg_cache;
-+ if (reg == WM8711_RESET)
-+ return 0;
-+ if (reg >= WM8711_CACHEREGNUM)
-+ return -1;
-+ return cache[reg];
-+}
-+
-+/*
-+ * write wm8711 register cache
-+ */
-+static inline void wm8711_write_reg_cache(struct snd_soc_codec *codec,
-+ u16 reg, unsigned int value)
-+{
-+ u16 *cache = codec->reg_cache;
-+ if (reg >= WM8711_CACHEREGNUM)
-+ return;
-+ cache[reg] = value;
-+}
-+
-+/*
-+ * write to the WM8711 register space
-+ */
-+static int wm8711_write(struct snd_soc_codec * codec, unsigned int reg,
-+ unsigned int value)
-+{
-+ u8 data[2];
-+
-+ /* data is
-+ * D15..D9 WM8753 register offset
-+ * D8...D0 register data
-+ */
-+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
-+ data[1] = value & 0x00ff;
-+
-+ wm8711_write_reg_cache (codec, reg, value);
-+ if (codec->hw_write(codec->control_data, data, 2) == 2)
-+ return 0;
-+ else
-+ return -EIO;
-+}
-+
-+#define wm8711_reset(c) wm8711_write(c, WM8711_RESET, 0)
-+
-+static const struct snd_kcontrol_new wm8711_snd_controls[] = {
-+
-+SOC_DOUBLE_R("Master Playback Volume", WM8711_LOUT1V, WM8711_ROUT1V,
-+ 0, 127, 0),
-+SOC_DOUBLE_R("Master Playback ZC Switch", WM8711_LOUT1V, WM8711_ROUT1V,
-+ 7, 1, 0),
-+
-+};
-+
-+/* add non dapm controls */
-+static int wm8711_add_controls(struct snd_soc_codec *codec)
-+{
-+ int err, i;
-+
-+ for (i = 0; i < ARRAY_SIZE(wm8711_snd_controls); i++) {
-+ err = snd_ctl_add(codec->card,
-+ snd_soc_cnew(&wm8711_snd_controls[i],codec, NULL));
-+ if (err < 0)
-+ return err;
-+ }
-+
-+ return 0;
-+}
-+
-+/* Output Mixer */
-+static const snd_kcontrol_new_t wm8711_output_mixer_controls[] = {
-+SOC_DAPM_SINGLE("Line Bypass Switch", WM8711_APANA, 3, 1, 0),
-+SOC_DAPM_SINGLE("HiFi Playback Switch", WM8711_APANA, 4, 1, 0),
-+};
-+
-+static const struct snd_soc_dapm_widget wm8711_dapm_widgets[] = {
-+SND_SOC_DAPM_MIXER("Output Mixer", WM8711_PWR, 4, 1,
-+ &wm8711_output_mixer_controls[0],
-+ ARRAY_SIZE(wm8711_output_mixer_controls)),
-+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8711_PWR, 3, 1),
-+SND_SOC_DAPM_OUTPUT("LOUT"),
-+SND_SOC_DAPM_OUTPUT("LHPOUT"),
-+SND_SOC_DAPM_OUTPUT("ROUT"),
-+SND_SOC_DAPM_OUTPUT("RHPOUT"),
-+};
-+
-+static const char *intercon[][3] = {
-+ /* output mixer */
-+ {"Output Mixer", "Line Bypass Switch", "Line Input"},
-+ {"Output Mixer", "HiFi Playback Switch", "DAC"},
-+
-+ /* outputs */
-+ {"RHPOUT", NULL, "Output Mixer"},
-+ {"ROUT", NULL, "Output Mixer"},
-+ {"LHPOUT", NULL, "Output Mixer"},
-+ {"LOUT", NULL, "Output Mixer"},
-+
-+ /* terminator */
-+ {NULL, NULL, NULL},
-+};
-+
-+static int wm8711_add_widgets(struct snd_soc_codec *codec)
-+{
-+ int i;
-+
-+ for(i = 0; i < ARRAY_SIZE(wm8711_dapm_widgets); i++) {
-+ snd_soc_dapm_new_control(codec, &wm8711_dapm_widgets[i]);
-+ }
-+
-+ /* set up audio path interconnects */
-+ for(i = 0; intercon[i][0] != NULL; i++) {
-+ snd_soc_dapm_connect_input(codec, intercon[i][0], intercon[i][1],
-+ intercon[i][2]);
-+ }
-+
-+ snd_soc_dapm_new_widgets(codec);
-+ return 0;
-+}
-+
-+struct _coeff_div {
-+ u32 mclk;
-+ u32 rate;
-+ u16 fs;
-+ u8 sr:4;
-+ u8 bosr:1;
-+ u8 usb:1;
-+};
-+
-+/* codec mclk clock divider coefficients */
-+static const struct _coeff_div coeff_div[] = {
-+ /* 48k */
-+ {12288000, 48000, 256, 0x0, 0x0, 0x0},
-+ {18432000, 48000, 384, 0x0, 0x1, 0x0},
-+ {12000000, 48000, 250, 0x0, 0x0, 0x1},
-+
-+ /* 32k */
-+ {12288000, 32000, 384, 0x6, 0x0, 0x0},
-+ {18432000, 32000, 576, 0x6, 0x1, 0x0},
-+ {12000000, 32000, 375, 0x6, 0x0, 0x1},
-+
-+ /* 8k */
-+ {12288000, 8000, 1536, 0x3, 0x0, 0x0},
-+ {18432000, 8000, 2304, 0x3, 0x1, 0x0},
-+ {11289600, 8000, 1408, 0xb, 0x0, 0x0},
-+ {16934400, 8000, 2112, 0xb, 0x1, 0x0},
-+ {12000000, 8000, 1500, 0x3, 0x0, 0x1},
-+
-+ /* 96k */
-+ {12288000, 96000, 128, 0x7, 0x0, 0x0},
-+ {18432000, 96000, 192, 0x7, 0x1, 0x0},
-+ {12000000, 96000, 125, 0x7, 0x0, 0x1},
-+
-+ /* 44.1k */
-+ {11289600, 44100, 256, 0x8, 0x0, 0x0},
-+ {16934400, 44100, 384, 0x8, 0x1, 0x0},
-+ {12000000, 44100, 272, 0x8, 0x1, 0x1},
-+
-+ /* 88.2k */
-+ {11289600, 88200, 128, 0xf, 0x0, 0x0},
-+ {16934400, 88200, 192, 0xf, 0x1, 0x0},
-+ {12000000, 88200, 136, 0xf, 0x1, 0x1},
-+};
-+
-+static inline int get_coeff(int mclk, int rate)
-+{
-+ int i;
-+
-+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
-+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
-+ return i;
-+ }
-+ return 0;
-+}
-+
-+static int wm8711_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_device *socdev = rtd->socdev;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ struct wm8711_priv *wm8711 = codec->private_data;
-+ u16 iface = wm8711_read_reg_cache(codec, WM8711_IFACE) & 0xfffc;
-+ int i = get_coeff(wm8711->sysclk, params_rate(params));
-+ u16 srate = (coeff_div[i].sr << 2) |
-+ (coeff_div[i].bosr << 1) | coeff_div[i].usb;
-+
-+ wm8711_write(codec, WM8711_SRATE, srate);
-+
-+ /* bit size */
-+ switch (params_format(params)) {
-+ case SNDRV_PCM_FORMAT_S16_LE:
-+ break;
-+ case SNDRV_PCM_FORMAT_S20_3LE:
-+ iface |= 0x0004;
-+ break;
-+ case SNDRV_PCM_FORMAT_S24_LE:
-+ iface |= 0x0008;
-+ break;
-+ }
-+
-+ wm8711_write(codec, WM8711_IFACE, iface);
-+ return 0;
-+}
-+
-+static int wm8711_pcm_prepare(struct snd_pcm_substream *substream)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_device *socdev = rtd->socdev;
-+ struct snd_soc_codec *codec = socdev->codec;
-+
-+ /* set active */
-+ wm8711_write(codec, WM8711_ACTIVE, 0x0001);
-+ return 0;
-+}
-+
-+static void wm8711_shutdown(struct snd_pcm_substream *substream)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_device *socdev = rtd->socdev;
-+ struct snd_soc_codec *codec = socdev->codec;
-+
-+ /* deactivate */
-+ if (!codec->active) {
-+ udelay(50);
-+ wm8711_write(codec, WM8711_ACTIVE, 0x0);
-+ }
-+}
-+
-+static int wm8711_mute(struct snd_soc_codec_dai *dai, int mute)
-+{
-+ struct snd_soc_codec *codec = dai->codec;
-+ u16 mute_reg = wm8711_read_reg_cache(codec, WM8711_APDIGI) & 0xfff7;
-+
-+ if (mute)
-+ wm8711_write(codec, WM8711_APDIGI, mute_reg | 0x8);
-+ else
-+ wm8711_write(codec, WM8711_APDIGI, mute_reg);
-+
-+ return 0;
-+}
-+
-+static int wm8711_set_dai_sysclk(struct snd_soc_codec_dai *codec_dai,
-+ int clk_id, unsigned int freq, int dir)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ struct wm8711_priv *wm8711 = codec->private_data;
-+
-+ switch (freq) {
-+ case 11289600:
-+ case 12000000:
-+ case 12288000:
-+ case 16934400:
-+ case 18432000:
-+ wm8711->sysclk = freq;
-+ return 0;
-+ }
-+ return -EINVAL;
-+}
-+
-+static int wm8711_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
-+ unsigned int fmt)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 iface = 0;
-+
-+ /* set master/slave audio interface */
-+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
-+ case SND_SOC_DAIFMT_CBM_CFM:
-+ iface |= 0x0040;
-+ break;
-+ case SND_SOC_DAIFMT_CBS_CFS:
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ /* interface format */
-+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
-+ case SND_SOC_DAIFMT_I2S:
-+ iface |= 0x0002;
-+ break;
-+ case SND_SOC_DAIFMT_RIGHT_J:
-+ break;
-+ case SND_SOC_DAIFMT_LEFT_J:
-+ iface |= 0x0001;
-+ break;
-+ case SND_SOC_DAIFMT_DSP_A:
-+ iface |= 0x0003;
-+ break;
-+ case SND_SOC_DAIFMT_DSP_B:
-+ iface |= 0x0013;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ /* clock inversion */
-+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
-+ case SND_SOC_DAIFMT_NB_NF:
-+ break;
-+ case SND_SOC_DAIFMT_IB_IF:
-+ iface |= 0x0090;
-+ break;
-+ case SND_SOC_DAIFMT_IB_NF:
-+ iface |= 0x0080;
-+ break;
-+ case SND_SOC_DAIFMT_NB_IF:
-+ iface |= 0x0010;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ /* set iface */
-+ wm8711_write(codec, WM8711_IFACE, iface);
-+ return 0;
-+}
-+
-+
-+static int wm8711_dapm_event(struct snd_soc_codec *codec, int event)
-+{
-+ u16 reg = wm8711_read_reg_cache(codec, WM8711_PWR) & 0xff7f;
-+
-+ switch (event) {
-+ case SNDRV_CTL_POWER_D0: /* full On */
-+ /* vref/mid, osc on, dac unmute */
-+ wm8711_write(codec, WM8711_PWR, reg);
-+ break;
-+ case SNDRV_CTL_POWER_D1: /* partial On */
-+ case SNDRV_CTL_POWER_D2: /* partial On */
-+ break;
-+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
-+ /* everything off except vref/vmid, */
-+ wm8711_write(codec, WM8711_PWR, reg | 0x0040);
-+ break;
-+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
-+ /* everything off, dac mute, inactive */
-+ wm8711_write(codec, WM8711_ACTIVE, 0x0);
-+ wm8711_write(codec, WM8711_PWR, 0xffff);
-+ break;
-+ }
-+ codec->dapm_state = event;
-+ return 0;
-+}
-+
-+#define WM8711_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
-+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
-+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
-+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
-+ SNDRV_PCM_RATE_96000)
-+
-+#define WM8711_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
-+ SNDRV_PCM_FMTBIT_S24_LE)
-+
-+struct snd_soc_codec_dai wm8711_dai = {
-+ .name = "WM8711",
-+ .playback = {
-+ .stream_name = "Playback",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = WM8711_RATES,
-+ .formats = WM8711_FORMATS,},
-+ .ops = {
-+ .prepare = wm8711_pcm_prepare,
-+ .hw_params = wm8711_hw_params,
-+ .shutdown = wm8711_shutdown,
-+ },
-+ .dai_ops = {
-+ .digital_mute = wm8711_mute,
-+ .set_sysclk = wm8711_set_dai_sysclk,
-+ .set_fmt = wm8711_set_dai_fmt,
-+ },
-+};
-+EXPORT_SYMBOL_GPL(wm8711_dai);
-+
-+static int wm8711_suspend(struct platform_device *pdev, pm_message_t state)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+
-+ wm8711_write(codec, WM8711_ACTIVE, 0x0);
-+ wm8711_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
-+ return 0;
-+}
-+
-+static int wm8711_resume(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+ int i;
-+ u8 data[2];
-+ u16 *cache = codec->reg_cache;
-+
-+ /* Sync reg_cache with the hardware */
-+ for (i = 0; i < ARRAY_SIZE(wm8711_reg); i++) {
-+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
-+ data[1] = cache[i] & 0x00ff;
-+ codec->hw_write(codec->control_data, data, 2);
-+ }
-+ wm8711_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
-+ wm8711_dapm_event(codec, codec->suspend_dapm_state);
-+ return 0;
-+}
-+
-+/*
-+ * initialise the WM8711 driver
-+ * register the mixer and dsp interfaces with the kernel
-+ */
-+static int wm8711_init(struct snd_soc_device* socdev)
-+{
-+ struct snd_soc_codec* codec = socdev->codec;
-+ int reg, ret = 0;
-+
-+ codec->name = "WM8711";
-+ codec->owner = THIS_MODULE;
-+ codec->read = wm8711_read_reg_cache;
-+ codec->write = wm8711_write;
-+ codec->dapm_event = wm8711_dapm_event;
-+ codec->dai = &wm8711_dai;
-+ codec->num_dai = 1;
-+ codec->reg_cache_size = ARRAY_SIZE(wm8711_reg);
-+ codec->reg_cache =
-+ kzalloc(sizeof(u16) * ARRAY_SIZE(wm8711_reg), GFP_KERNEL);
-+ if (codec->reg_cache == NULL)
-+ return -ENOMEM;
-+ memcpy(codec->reg_cache, wm8711_reg,
-+ sizeof(u16) * ARRAY_SIZE(wm8711_reg));
-+ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8711_reg);
-+
-+ wm8711_reset(codec);
-+
-+ /* register pcms */
-+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
-+ if (ret < 0) {
-+ printk(KERN_ERR "wm8711: failed to create pcms\n");
-+ goto pcm_err;
-+ }
-+
-+ /* power on device */
-+ wm8711_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
-+
-+ /* set the update bits */
-+ reg = wm8711_read_reg_cache(codec, WM8711_LOUT1V);
-+ wm8711_write(codec, WM8711_LOUT1V, reg | 0x0100);
-+ reg = wm8711_read_reg_cache(codec, WM8711_ROUT1V);
-+ wm8711_write(codec, WM8711_ROUT1V, reg | 0x0100);
-+
-+ wm8711_add_controls(codec);
-+ wm8711_add_widgets(codec);
-+ ret = snd_soc_register_card(socdev);
-+ if (ret < 0) {
-+ printk(KERN_ERR "wm8711: failed to register card\n");
-+ goto card_err;
-+ }
-+ return ret;
-+
-+card_err:
-+ snd_soc_free_pcms(socdev);
-+ snd_soc_dapm_free(socdev);
-+pcm_err:
-+ kfree(codec->reg_cache);
-+ return ret;
-+}
-+
-+static struct snd_soc_device *wm8711_socdev;
-+
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+
-+/*
-+ * WM8711 2 wire address is determined by GPIO5
-+ * state during powerup.
-+ * low = 0x1a
-+ * high = 0x1b
-+ */
-+#define I2C_DRIVERID_WM8711 0xfefe /* liam - need a proper id */
-+
-+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
-+
-+/* Magic definition of all other variables and things */
-+I2C_CLIENT_INSMOD;
-+
-+static struct i2c_driver wm8711_i2c_driver;
-+static struct i2c_client client_template;
-+
-+/* If the i2c layer weren't so broken, we could pass this kind of data
-+ around */
-+
-+static int wm8711_codec_probe(struct i2c_adapter *adap, int addr, int kind)
-+{
-+ struct snd_soc_device *socdev = wm8711_socdev;
-+ struct wm8711_setup_data *setup = socdev->codec_data;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ struct i2c_client *i2c;
-+ int ret;
-+
-+ if (addr != setup->i2c_address)
-+ return -ENODEV;
-+
-+ client_template.adapter = adap;
-+ client_template.addr = addr;
-+
-+ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
-+ if (i2c == NULL){
-+ kfree(codec);
-+ return -ENOMEM;
-+ }
-+ memcpy(i2c, &client_template, sizeof(struct i2c_client));
-+
-+ i2c_set_clientdata(i2c, codec);
-+
-+ codec->control_data = i2c;
-+
-+ ret = i2c_attach_client(i2c);
-+ if (ret < 0) {
-+ err("failed to attach codec at addr %x\n", addr);
-+ goto err;
-+ }
-+
-+ ret = wm8711_init(socdev);
-+ if (ret < 0) {
-+ err("failed to initialise WM8711\n");
-+ goto err;
-+ }
-+ return ret;
-+
-+err:
-+ kfree(codec);
-+ kfree(i2c);
-+ return ret;
-+
-+}
-+
-+static int wm8711_i2c_detach(struct i2c_client *client)
-+{
-+ struct snd_soc_codec* codec = i2c_get_clientdata(client);
-+
-+ i2c_detach_client(client);
-+ kfree(codec->reg_cache);
-+ kfree(client);
-+ return 0;
-+}
-+
-+static int wm8711_i2c_attach(struct i2c_adapter *adap)
-+{
-+ return i2c_probe(adap, &addr_data, wm8711_codec_probe);
-+}
-+
-+/* corgi i2c codec control layer */
-+static struct i2c_driver wm8711_i2c_driver = {
-+ .driver = {
-+ .name = "WM8711 I2C Codec",
-+ .owner = THIS_MODULE,
-+ },
-+ .id = I2C_DRIVERID_WM8711,
-+ .attach_adapter = wm8711_i2c_attach,
-+ .detach_client = wm8711_i2c_detach,
-+ .command = NULL,
-+};
-+
-+static struct i2c_client client_template = {
-+ .name = "WM8711",
-+ .driver = &wm8711_i2c_driver,
-+};
-+#endif
-+
-+static int wm8711_probe(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct wm8711_setup_data *setup;
-+ struct snd_soc_codec* codec;
-+ struct wm8711_priv *wm8711;
-+ int ret = 0;
-+
-+ info("WM8711 Audio Codec %s", WM8711_VERSION);
-+
-+ setup = socdev->codec_data;
-+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
-+ if (codec == NULL)
-+ return -ENOMEM;
-+
-+ wm8711 = kzalloc(sizeof(struct wm8711_priv), GFP_KERNEL);
-+ if (wm8711 == NULL) {
-+ kfree(codec);
-+ return -ENOMEM;
-+ }
-+
-+ codec->private_data = wm8711;
-+ socdev->codec = codec;
-+ mutex_init(&codec->mutex);
-+ INIT_LIST_HEAD(&codec->dapm_widgets);
-+ INIT_LIST_HEAD(&codec->dapm_paths);
-+
-+ wm8711_socdev = socdev;
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+ if (setup->i2c_address) {
-+ normal_i2c[0] = setup->i2c_address;
-+ codec->hw_write = (hw_write_t)i2c_master_send;
-+ ret = i2c_add_driver(&wm8711_i2c_driver);
-+ if (ret != 0)
-+ printk(KERN_ERR "can't add i2c driver");
-+ }
-+#else
-+ /* Add other interfaces here */
-+#endif
-+ return ret;
-+}
-+
-+/* power down chip */
-+static int wm8711_remove(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+
-+ if (codec->control_data)
-+ wm8711_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
-+
-+ snd_soc_free_pcms(socdev);
-+ snd_soc_dapm_free(socdev);
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+ i2c_del_driver(&wm8711_i2c_driver);
-+#endif
-+ kfree(codec->private_data);
-+ kfree(codec);
-+
-+ return 0;
-+}
-+
-+struct snd_soc_codec_device soc_codec_dev_wm8711 = {
-+ .probe = wm8711_probe,
-+ .remove = wm8711_remove,
-+ .suspend = wm8711_suspend,
-+ .resume = wm8711_resume,
-+};
-+
-+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8711);
-+
-+MODULE_DESCRIPTION("ASoC WM8711 driver");
-+MODULE_AUTHOR("Mike Arthur");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/codecs/wm8711.h
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/codecs/wm8711.h
-@@ -0,0 +1,42 @@
-+/*
-+ * wm8711.h -- WM8711 Soc Audio driver
-+ *
-+ * Copyright 2006 Wolfson Microelectronics
-+ *
-+ * Author: Mike Arthur <linux at wolfsonmicro.com>
-+ *
-+ * Based on wm8731.h
-+ *
-+ * 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 _WM8711_H
-+#define _WM8711_H
-+
-+/* WM8711 register space */
-+
-+#define WM8711_LOUT1V 0x02
-+#define WM8711_ROUT1V 0x03
-+#define WM8711_APANA 0x04
-+#define WM8711_APDIGI 0x05
-+#define WM8711_PWR 0x06
-+#define WM8711_IFACE 0x07
-+#define WM8711_SRATE 0x08
-+#define WM8711_ACTIVE 0x09
-+#define WM8711_RESET 0x0f
-+
-+#define WM8711_CACHEREGNUM 8
-+
-+#define WM8711_SYSCLK 0
-+#define WM8711_DAI 0
-+
-+struct wm8711_setup_data {
-+ unsigned short i2c_address;
-+};
-+
-+extern struct snd_soc_codec_dai wm8711_dai;
-+extern struct snd_soc_codec_device soc_codec_dev_wm8711;
-+
-+#endif
-Index: linux-2.6.21-moko/sound/soc/codecs/wm8980.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/codecs/wm8980.c
-@@ -0,0 +1,923 @@
-+/*
-+ * wm8980.c -- WM8980 ALSA Soc Audio driver
-+ *
-+ * Copyright 2006 Wolfson Microelectronics PLC.
-+ *
-+ * Authors:
-+ * Mike Arthur <linux at wolfsonmicro.com>
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License version 2 as
-+ * published by the Free Software Foundation.
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/moduleparam.h>
-+#include <linux/version.h>
-+#include <linux/kernel.h>
-+#include <linux/init.h>
-+#include <linux/delay.h>
-+#include <linux/pm.h>
-+#include <linux/i2c.h>
-+#include <linux/platform_device.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/pcm_params.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+#include <sound/initval.h>
-+
-+#include "wm8980.h"
-+
-+#define AUDIO_NAME "wm8980"
-+#define WM8980_VERSION "0.3"
-+
-+/*
-+ * Debug
-+ */
-+
-+#define WM8980_DEBUG 0
-+
-+#ifdef WM8980_DEBUG
-+#define dbg(format, arg...) \
-+ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
-+#else
-+#define dbg(format, arg...) do {} while (0)
-+#endif
-+#define err(format, arg...) \
-+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
-+#define info(format, arg...) \
-+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
-+#define warn(format, arg...) \
-+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
-+
-+struct snd_soc_codec_device soc_codec_dev_wm8980;
-+
-+/*
-+ * wm8980 register cache
-+ * We can't read the WM8980 register space when we are
-+ * using 2 wire for device control, so we cache them instead.
-+ */
-+static const u16 wm8980_reg[WM8980_CACHEREGNUM] = {
-+ 0x0000, 0x0000, 0x0000, 0x0000,
-+ 0x0050, 0x0000, 0x0140, 0x0000,
-+ 0x0000, 0x0000, 0x0000, 0x00ff,
-+ 0x00ff, 0x0000, 0x0100, 0x00ff,
-+ 0x00ff, 0x0000, 0x012c, 0x002c,
-+ 0x002c, 0x002c, 0x002c, 0x0000,
-+ 0x0032, 0x0000, 0x0000, 0x0000,
-+ 0x0000, 0x0000, 0x0000, 0x0000,
-+ 0x0038, 0x000b, 0x0032, 0x0000,
-+ 0x0008, 0x000c, 0x0093, 0x00e9,
-+ 0x0000, 0x0000, 0x0000, 0x0000,
-+ 0x0033, 0x0010, 0x0010, 0x0100,
-+ 0x0100, 0x0002, 0x0001, 0x0001,
-+ 0x0039, 0x0039, 0x0039, 0x0039,
-+ 0x0001, 0x0001,
-+};
-+
-+/*
-+ * read wm8980 register cache
-+ */
-+static inline unsigned int wm8980_read_reg_cache(struct snd_soc_codec *codec,
-+ unsigned int reg)
-+{
-+ u16 *cache = codec->reg_cache;
-+ if (reg == WM8980_RESET)
-+ return 0;
-+ if (reg >= WM8980_CACHEREGNUM)
-+ return -1;
-+ return cache[reg];
-+}
-+
-+/*
-+ * write wm8980 register cache
-+ */
-+static inline void wm8980_write_reg_cache(struct snd_soc_codec *codec,
-+ u16 reg, unsigned int value)
-+{
-+ u16 *cache = codec->reg_cache;
-+ if (reg >= WM8980_CACHEREGNUM)
-+ return;
-+ cache[reg] = value;
-+}
-+
-+/*
-+ * write to the WM8980 register space
-+ */
-+static int wm8980_write(struct snd_soc_codec *codec, unsigned int reg,
-+ unsigned int value)
-+{
-+ u8 data[2];
-+
-+ /* data is
-+ * D15..D9 WM8980 register offset
-+ * D8...D0 register data
-+ */
-+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
-+ data[1] = value & 0x00ff;
-+
-+ wm8980_write_reg_cache (codec, reg, value);
-+ if (codec->hw_write(codec->control_data, data, 2) == 2)
-+ return 0;
-+ else
-+ return -1;
-+}
-+
-+#define wm8980_reset(c) wm8980_write(c, WM8980_RESET, 0)
-+
-+static const char *wm8980_companding[] = {"Off", "NC", "u-law", "A-law" };
-+static const char *wm8980_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
-+static const char *wm8980_eqmode[] = {"Capture", "Playback" };
-+static const char *wm8980_bw[] = {"Narrow", "Wide" };
-+static const char *wm8980_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" };
-+static const char *wm8980_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" };
-+static const char *wm8980_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" };
-+static const char *wm8980_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" };
-+static const char *wm8980_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" };
-+static const char *wm8980_alc[] =
-+ {"ALC both on", "ALC left only", "ALC right only", "Limiter" };
-+
-+static const struct soc_enum wm8980_enum[] = {
-+ SOC_ENUM_SINGLE(WM8980_COMP, 1, 4, wm8980_companding), /* adc */
-+ SOC_ENUM_SINGLE(WM8980_COMP, 3, 4, wm8980_companding), /* dac */
-+ SOC_ENUM_SINGLE(WM8980_DAC, 4, 4, wm8980_deemp),
-+ SOC_ENUM_SINGLE(WM8980_EQ1, 8, 2, wm8980_eqmode),
-+
-+ SOC_ENUM_SINGLE(WM8980_EQ1, 5, 4, wm8980_eq1),
-+ SOC_ENUM_SINGLE(WM8980_EQ2, 8, 2, wm8980_bw),
-+ SOC_ENUM_SINGLE(WM8980_EQ2, 5, 4, wm8980_eq2),
-+ SOC_ENUM_SINGLE(WM8980_EQ3, 8, 2, wm8980_bw),
-+
-+ SOC_ENUM_SINGLE(WM8980_EQ3, 5, 4, wm8980_eq3),
-+ SOC_ENUM_SINGLE(WM8980_EQ4, 8, 2, wm8980_bw),
-+ SOC_ENUM_SINGLE(WM8980_EQ4, 5, 4, wm8980_eq4),
-+ SOC_ENUM_SINGLE(WM8980_EQ5, 8, 2, wm8980_bw),
-+
-+ SOC_ENUM_SINGLE(WM8980_EQ5, 5, 4, wm8980_eq5),
-+ SOC_ENUM_SINGLE(WM8980_ALC3, 8, 2, wm8980_alc),
-+};
-+
-+static const struct snd_kcontrol_new wm8980_snd_controls[] = {
-+SOC_SINGLE("Digital Loopback Switch", WM8980_COMP, 0, 1, 0),
-+
-+SOC_ENUM("ADC Companding", wm8980_enum[0]),
-+SOC_ENUM("DAC Companding", wm8980_enum[1]),
-+
-+SOC_SINGLE("Jack Detection Enable", WM8980_JACK1, 6, 1, 0),
-+
-+SOC_SINGLE("DAC Right Inversion Switch", WM8980_DAC, 1, 1, 0),
-+SOC_SINGLE("DAC Left Inversion Switch", WM8980_DAC, 0, 1, 0),
-+
-+SOC_SINGLE("Left Playback Volume", WM8980_DACVOLL, 0, 127, 0),
-+SOC_SINGLE("Right Playback Volume", WM8980_DACVOLR, 0, 127, 0),
-+
-+SOC_SINGLE("High Pass Filter Switch", WM8980_ADC, 8, 1, 0),
-+SOC_SINGLE("High Pass Filter Switch", WM8980_ADC, 8, 1, 0),
-+SOC_SINGLE("High Pass Cut Off", WM8980_ADC, 4, 7, 0),
-+SOC_SINGLE("Right ADC Inversion Switch", WM8980_ADC, 1, 1, 0),
-+SOC_SINGLE("Left ADC Inversion Switch", WM8980_ADC, 0, 1, 0),
-+
-+SOC_SINGLE("Left Capture Volume", WM8980_ADCVOLL, 0, 127, 0),
-+SOC_SINGLE("Right Capture Volume", WM8980_ADCVOLR, 0, 127, 0),
-+
-+SOC_ENUM("Equaliser Function", wm8980_enum[3]),
-+SOC_ENUM("EQ1 Cut Off", wm8980_enum[4]),
-+SOC_SINGLE("EQ1 Volume", WM8980_EQ1, 0, 31, 1),
-+
-+SOC_ENUM("Equaliser EQ2 Bandwith", wm8980_enum[5]),
-+SOC_ENUM("EQ2 Cut Off", wm8980_enum[6]),
-+SOC_SINGLE("EQ2 Volume", WM8980_EQ2, 0, 31, 1),
-+
-+SOC_ENUM("Equaliser EQ3 Bandwith", wm8980_enum[7]),
-+SOC_ENUM("EQ3 Cut Off", wm8980_enum[8]),
-+SOC_SINGLE("EQ3 Volume", WM8980_EQ3, 0, 31, 1),
-+
-+SOC_ENUM("Equaliser EQ4 Bandwith", wm8980_enum[9]),
-+SOC_ENUM("EQ4 Cut Off", wm8980_enum[10]),
-+SOC_SINGLE("EQ4 Volume", WM8980_EQ4, 0, 31, 1),
-+
-+SOC_ENUM("Equaliser EQ5 Bandwith", wm8980_enum[11]),
-+SOC_ENUM("EQ5 Cut Off", wm8980_enum[12]),
-+SOC_SINGLE("EQ5 Volume", WM8980_EQ5, 0, 31, 1),
-+
-+SOC_SINGLE("DAC Playback Limiter Switch", WM8980_DACLIM1, 8, 1, 0),
-+SOC_SINGLE("DAC Playback Limiter Decay", WM8980_DACLIM1, 4, 15, 0),
-+SOC_SINGLE("DAC Playback Limiter Attack", WM8980_DACLIM1, 0, 15, 0),
-+
-+SOC_SINGLE("DAC Playback Limiter Threshold", WM8980_DACLIM2, 4, 7, 0),
-+SOC_SINGLE("DAC Playback Limiter Boost", WM8980_DACLIM2, 0, 15, 0),
-+
-+SOC_SINGLE("ALC Enable Switch", WM8980_ALC1, 8, 1, 0),
-+SOC_SINGLE("ALC Capture Max Gain", WM8980_ALC1, 3, 7, 0),
-+SOC_SINGLE("ALC Capture Min Gain", WM8980_ALC1, 0, 7, 0),
-+
-+SOC_SINGLE("ALC Capture ZC Switch", WM8980_ALC2, 8, 1, 0),
-+SOC_SINGLE("ALC Capture Hold", WM8980_ALC2, 4, 7, 0),
-+SOC_SINGLE("ALC Capture Target", WM8980_ALC2, 0, 15, 0),
-+
-+SOC_ENUM("ALC Capture Mode", wm8980_enum[13]),
-+SOC_SINGLE("ALC Capture Decay", WM8980_ALC3, 4, 15, 0),
-+SOC_SINGLE("ALC Capture Attack", WM8980_ALC3, 0, 15, 0),
-+
-+SOC_SINGLE("ALC Capture Noise Gate Switch", WM8980_NGATE, 3, 1, 0),
-+SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8980_NGATE, 0, 7, 0),
-+
-+SOC_SINGLE("Left Capture PGA ZC Switch", WM8980_INPPGAL, 7, 1, 0),
-+SOC_SINGLE("Left Capture PGA Volume", WM8980_INPPGAL, 0, 63, 0),
-+
-+SOC_SINGLE("Right Capture PGA ZC Switch", WM8980_INPPGAR, 7, 1, 0),
-+SOC_SINGLE("Right Capture PGA Volume", WM8980_INPPGAR, 0, 63, 0),
-+
-+SOC_SINGLE("Left Headphone Playback ZC Switch", WM8980_HPVOLL, 7, 1, 0),
-+SOC_SINGLE("Left Headphone Playback Switch", WM8980_HPVOLL, 6, 1, 1),
-+SOC_SINGLE("Left Headphone Playback Volume", WM8980_HPVOLL, 0, 63, 0),
-+
-+SOC_SINGLE("Right Headphone Playback ZC Switch", WM8980_HPVOLR, 7, 1, 0),
-+SOC_SINGLE("Right Headphone Playback Switch", WM8980_HPVOLR, 6, 1, 1),
-+SOC_SINGLE("Right Headphone Playback Volume", WM8980_HPVOLR, 0, 63, 0),
-+
-+SOC_SINGLE("Left Speaker Playback ZC Switch", WM8980_SPKVOLL, 7, 1, 0),
-+SOC_SINGLE("Left Speaker Playback Switch", WM8980_SPKVOLL, 6, 1, 1),
-+SOC_SINGLE("Left Speaker Playback Volume", WM8980_SPKVOLL, 0, 63, 0),
-+
-+SOC_SINGLE("Right Speaker Playback ZC Switch", WM8980_SPKVOLR, 7, 1, 0),
-+SOC_SINGLE("Right Speaker Playback Switch", WM8980_SPKVOLR, 6, 1, 1),
-+SOC_SINGLE("Right Speaker Playback Volume", WM8980_SPKVOLR, 0, 63, 0),
-+
-+SOC_DOUBLE_R("Capture Boost(+20dB)", WM8980_ADCBOOSTL, WM8980_ADCBOOSTR,
-+ 8, 1, 0),
-+};
-+
-+/* add non dapm controls */
-+static int wm8980_add_controls(struct snd_soc_codec *codec)
-+{
-+ int err, i;
-+
-+ for (i = 0; i < ARRAY_SIZE(wm8980_snd_controls); i++) {
-+ err = snd_ctl_add(codec->card,
-+ snd_soc_cnew(&wm8980_snd_controls[i],codec, NULL));
-+ if (err < 0)
-+ return err;
-+ }
-+
-+ return 0;
-+}
-+
-+/* Left Output Mixer */
-+static const snd_kcontrol_new_t wm8980_left_mixer_controls[] = {
-+SOC_DAPM_SINGLE("Right PCM Playback Switch", WM8980_OUTPUT, 6, 1, 1),
-+SOC_DAPM_SINGLE("Left PCM Playback Switch", WM8980_MIXL, 0, 1, 1),
-+SOC_DAPM_SINGLE("Line Bypass Switch", WM8980_MIXL, 1, 1, 0),
-+SOC_DAPM_SINGLE("Aux Playback Switch", WM8980_MIXL, 5, 1, 0),
-+};
-+
-+/* Right Output Mixer */
-+static const snd_kcontrol_new_t wm8980_right_mixer_controls[] = {
-+SOC_DAPM_SINGLE("Left PCM Playback Switch", WM8980_OUTPUT, 5, 1, 1),
-+SOC_DAPM_SINGLE("Right PCM Playback Switch", WM8980_MIXR, 0, 1, 1),
-+SOC_DAPM_SINGLE("Line Bypass Switch", WM8980_MIXR, 1, 1, 0),
-+SOC_DAPM_SINGLE("Aux Playback Switch", WM8980_MIXR, 5, 1, 0),
-+};
-+
-+/* Left AUX Input boost vol */
-+static const snd_kcontrol_new_t wm8980_laux_boost_controls =
-+SOC_DAPM_SINGLE("Left Aux Volume", WM8980_ADCBOOSTL, 0, 3, 0);
-+
-+/* Right AUX Input boost vol */
-+static const snd_kcontrol_new_t wm8980_raux_boost_controls =
-+SOC_DAPM_SINGLE("Right Aux Volume", WM8980_ADCBOOSTR, 0, 3, 0);
-+
-+/* Left Input boost vol */
-+static const snd_kcontrol_new_t wm8980_lmic_boost_controls =
-+SOC_DAPM_SINGLE("Left Input Volume", WM8980_ADCBOOSTL, 4, 3, 0);
-+
-+/* Right Input boost vol */
-+static const snd_kcontrol_new_t wm8980_rmic_boost_controls =
-+SOC_DAPM_SINGLE("Right Input Volume", WM8980_ADCBOOSTR, 4, 3, 0);
-+
-+/* Left Aux In to PGA */
-+static const snd_kcontrol_new_t wm8980_laux_capture_boost_controls =
-+SOC_DAPM_SINGLE("Left Capture Switch", WM8980_ADCBOOSTL, 8, 1, 0);
-+
-+/* Right Aux In to PGA */
-+static const snd_kcontrol_new_t wm8980_raux_capture_boost_controls =
-+SOC_DAPM_SINGLE("Right Capture Switch", WM8980_ADCBOOSTR, 8, 1, 0);
-+
-+/* Left Input P In to PGA */
-+static const snd_kcontrol_new_t wm8980_lmicp_capture_boost_controls =
-+SOC_DAPM_SINGLE("Left Input P Capture Boost Switch", WM8980_INPUT, 0, 1, 0);
-+
-+/* Right Input P In to PGA */
-+static const snd_kcontrol_new_t wm8980_rmicp_capture_boost_controls =
-+SOC_DAPM_SINGLE("Right Input P Capture Boost Switch", WM8980_INPUT, 4, 1, 0);
-+
-+/* Left Input N In to PGA */
-+static const snd_kcontrol_new_t wm8980_lmicn_capture_boost_controls =
-+SOC_DAPM_SINGLE("Left Input N Capture Boost Switch", WM8980_INPUT, 1, 1, 0);
-+
-+/* Right Input N In to PGA */
-+static const snd_kcontrol_new_t wm8980_rmicn_capture_boost_controls =
-+SOC_DAPM_SINGLE("Right Input N Capture Boost Switch", WM8980_INPUT, 5, 1, 0);
-+
-+// TODO Widgets
-+static const struct snd_soc_dapm_widget wm8980_dapm_widgets[] = {
-+#if 0
-+//SND_SOC_DAPM_MUTE("Mono Mute", WM8980_MONOMIX, 6, 0),
-+//SND_SOC_DAPM_MUTE("Speaker Mute", WM8980_SPKMIX, 6, 0),
-+
-+SND_SOC_DAPM_MIXER("Speaker Mixer", WM8980_POWER3, 2, 0,
-+ &wm8980_speaker_mixer_controls[0],
-+ ARRAY_SIZE(wm8980_speaker_mixer_controls)),
-+SND_SOC_DAPM_MIXER("Mono Mixer", WM8980_POWER3, 3, 0,
-+ &wm8980_mono_mixer_controls[0],
-+ ARRAY_SIZE(wm8980_mono_mixer_controls)),
-+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8980_POWER3, 0, 0),
-+SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8980_POWER3, 0, 0),
-+SND_SOC_DAPM_PGA("Aux Input", WM8980_POWER1, 6, 0, NULL, 0),
-+SND_SOC_DAPM_PGA("SpkN Out", WM8980_POWER3, 5, 0, NULL, 0),
-+SND_SOC_DAPM_PGA("SpkP Out", WM8980_POWER3, 6, 0, NULL, 0),
-+SND_SOC_DAPM_PGA("Mono Out", WM8980_POWER3, 7, 0, NULL, 0),
-+SND_SOC_DAPM_PGA("Mic PGA", WM8980_POWER2, 2, 0, NULL, 0),
-+
-+SND_SOC_DAPM_PGA("Aux Boost", SND_SOC_NOPM, 0, 0,
-+ &wm8980_aux_boost_controls, 1),
-+SND_SOC_DAPM_PGA("Mic Boost", SND_SOC_NOPM, 0, 0,
-+ &wm8980_mic_boost_controls, 1),
-+SND_SOC_DAPM_SWITCH("Capture Boost", SND_SOC_NOPM, 0, 0,
-+ &wm8980_capture_boost_controls),
-+
-+SND_SOC_DAPM_MIXER("Boost Mixer", WM8980_POWER2, 4, 0, NULL, 0),
-+
-+SND_SOC_DAPM_MICBIAS("Mic Bias", WM8980_POWER1, 4, 0),
-+
-+SND_SOC_DAPM_INPUT("MICN"),
-+SND_SOC_DAPM_INPUT("MICP"),
-+SND_SOC_DAPM_INPUT("AUX"),
-+SND_SOC_DAPM_OUTPUT("MONOOUT"),
-+SND_SOC_DAPM_OUTPUT("SPKOUTP"),
-+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
-+#endif
-+};
-+
-+static const char *audio_map[][3] = {
-+ /* Mono output mixer */
-+ {"Mono Mixer", "PCM Playback Switch", "DAC"},
-+ {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
-+ {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
-+
-+ /* Speaker output mixer */
-+ {"Speaker Mixer", "PCM Playback Switch", "DAC"},
-+ {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
-+ {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
-+
-+ /* Outputs */
-+ {"Mono Out", NULL, "Mono Mixer"},
-+ {"MONOOUT", NULL, "Mono Out"},
-+ {"SpkN Out", NULL, "Speaker Mixer"},
-+ {"SpkP Out", NULL, "Speaker Mixer"},
-+ {"SPKOUTN", NULL, "SpkN Out"},
-+ {"SPKOUTP", NULL, "SpkP Out"},
-+
-+ /* Boost Mixer */
-+ {"Boost Mixer", NULL, "ADC"},
-+ {"Capture Boost Switch", "Aux Capture Boost Switch", "AUX"},
-+ {"Aux Boost", "Aux Volume", "Boost Mixer"},
-+ {"Capture Boost", "Capture Switch", "Boost Mixer"},
-+ {"Mic Boost", "Mic Volume", "Boost Mixer"},
-+
-+ /* Inputs */
-+ {"MICP", NULL, "Mic Boost"},
-+ {"MICN", NULL, "Mic PGA"},
-+ {"Mic PGA", NULL, "Capture Boost"},
-+ {"AUX", NULL, "Aux Input"},
-+
-+ /* */
-+
-+ /* terminator */
-+ {NULL, NULL, NULL},
-+};
-+
-+static int wm8980_add_widgets(struct snd_soc_codec *codec)
-+{
-+ int i;
-+
-+ for(i = 0; i < ARRAY_SIZE(wm8980_dapm_widgets); i++) {
-+ snd_soc_dapm_new_control(codec, &wm8980_dapm_widgets[i]);
-+ }
-+
-+ /* set up audio path map */
-+ for(i = 0; audio_map[i][0] != NULL; i++) {
-+ snd_soc_dapm_connect_input(codec, audio_map[i][0], audio_map[i][1],
-+ audio_map[i][2]);
-+ }
-+
-+ snd_soc_dapm_new_widgets(codec);
-+ return 0;
-+}
-+
-+struct pll_ {
-+ unsigned int in_hz, out_hz;
-+ unsigned int pre:4; /* prescale - 1 */
-+ unsigned int n:4;
-+ unsigned int k;
-+};
-+
-+struct pll_ pll[] = {
-+ {12000000, 11289600, 0, 7, 0x86c220},
-+ {12000000, 12288000, 0, 8, 0x3126e8},
-+ {13000000, 11289600, 0, 6, 0xf28bd4},
-+ {13000000, 12288000, 0, 7, 0x8fd525},
-+ {12288000, 11289600, 0, 7, 0x59999a},
-+ {11289600, 12288000, 0, 8, 0x80dee9},
-+ /* TODO: liam - add more entries */
-+};
-+
-+static int wm8980_set_dai_pll(struct snd_soc_codec_dai *codec_dai,
-+ int pll_id, unsigned int freq_in, unsigned int freq_out)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ int i;
-+ u16 reg;
-+
-+ if(freq_in == 0 || freq_out == 0) {
-+ reg = wm8980_read_reg_cache(codec, WM8980_POWER1);
-+ wm8980_write(codec, WM8980_POWER1, reg & 0x1df);
-+ return 0;
-+ }
-+
-+ for(i = 0; i < ARRAY_SIZE(pll); i++) {
-+ if (freq_in == pll[i].in_hz && freq_out == pll[i].out_hz) {
-+ wm8980_write(codec, WM8980_PLLN, (pll[i].pre << 4) | pll[i].n);
-+ wm8980_write(codec, WM8980_PLLK1, pll[i].k >> 18);
-+ wm8980_write(codec, WM8980_PLLK1, (pll[i].k >> 9) && 0x1ff);
-+ wm8980_write(codec, WM8980_PLLK1, pll[i].k && 0x1ff);
-+ reg = wm8980_read_reg_cache(codec, WM8980_POWER1);
-+ wm8980_write(codec, WM8980_POWER1, reg | 0x020);
-+ return 0;
-+ }
-+ }
-+ return -EINVAL;
-+}
-+
-+static int wm8980_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
-+ unsigned int fmt)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 iface = wm8980_read_reg_cache(codec, WM8980_IFACE) & 0x3;
-+ u16 clk = wm8980_read_reg_cache(codec, WM8980_CLOCK) & 0xfffe;
-+
-+ /* set master/slave audio interface */
-+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
-+ case SND_SOC_DAIFMT_CBM_CFM:
-+ clk |= 0x0001;
-+ break;
-+ case SND_SOC_DAIFMT_CBS_CFS:
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ /* interface format */
-+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
-+ case SND_SOC_DAIFMT_I2S:
-+ iface |= 0x0010;
-+ break;
-+ case SND_SOC_DAIFMT_RIGHT_J:
-+ break;
-+ case SND_SOC_DAIFMT_LEFT_J:
-+ iface |= 0x0008;
-+ break;
-+ case SND_SOC_DAIFMT_DSP_A:
-+ iface |= 0x00018;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ /* clock inversion */
-+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
-+ case SND_SOC_DAIFMT_NB_NF:
-+ break;
-+ case SND_SOC_DAIFMT_IB_IF:
-+ iface |= 0x0180;
-+ break;
-+ case SND_SOC_DAIFMT_IB_NF:
-+ iface |= 0x0100;
-+ break;
-+ case SND_SOC_DAIFMT_NB_IF:
-+ iface |= 0x0080;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ wm8980_write(codec, WM8980_IFACE, iface);
-+ return 0;
-+}
-+
-+static int wm8980_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_device *socdev = rtd->socdev;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ u16 iface = wm8980_read_reg_cache(codec, WM8980_IFACE) & 0xff9f;
-+ u16 adn = wm8980_read_reg_cache(codec, WM8980_ADD) & 0x1f1;
-+
-+ /* bit size */
-+ switch (params_format(params)) {
-+ case SNDRV_PCM_FORMAT_S16_LE:
-+ break;
-+ case SNDRV_PCM_FORMAT_S20_3LE:
-+ iface |= 0x0020;
-+ break;
-+ case SNDRV_PCM_FORMAT_S24_LE:
-+ iface |= 0x0040;
-+ break;
-+ }
-+
-+ /* filter coefficient */
-+ switch (params_rate(params)) {
-+ case SNDRV_PCM_RATE_8000:
-+ adn |= 0x5 << 1;
-+ break;
-+ case SNDRV_PCM_RATE_11025:
-+ adn |= 0x4 << 1;
-+ break;
-+ case SNDRV_PCM_RATE_16000:
-+ adn |= 0x3 << 1;
-+ break;
-+ case SNDRV_PCM_RATE_22050:
-+ adn |= 0x2 << 1;
-+ break;
-+ case SNDRV_PCM_RATE_32000:
-+ adn |= 0x1 << 1;
-+ break;
-+ }
-+
-+ /* set iface */
-+ wm8980_write(codec, WM8980_IFACE, iface);
-+ wm8980_write(codec, WM8980_ADD, adn);
-+ return 0;
-+}
-+
-+static int wm8980_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
-+ int div_id, int div)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 reg;
-+
-+ switch (div_id) {
-+ case WM8980_MCLKDIV:
-+ reg = wm8980_read_reg_cache(codec, WM8980_CLOCK) & 0x11f;
-+ wm8980_write(codec, WM8980_CLOCK, reg | div);
-+ break;
-+ case WM8980_BCLKDIV:
-+ reg = wm8980_read_reg_cache(codec, WM8980_CLOCK) & 0x1c7;
-+ wm8980_write(codec, WM8980_CLOCK, reg | div);
-+ break;
-+ case WM8980_OPCLKDIV:
-+ reg = wm8980_read_reg_cache(codec, WM8980_GPIO) & 0x1cf;
-+ wm8980_write(codec, WM8980_GPIO, reg | div);
-+ break;
-+ case WM8980_DACOSR:
-+ reg = wm8980_read_reg_cache(codec, WM8980_DAC) & 0x1f7;
-+ wm8980_write(codec, WM8980_DAC, reg | div);
-+ break;
-+ case WM8980_ADCOSR:
-+ reg = wm8980_read_reg_cache(codec, WM8980_ADC) & 0x1f7;
-+ wm8980_write(codec, WM8980_ADC, reg | div);
-+ break;
-+ case WM8980_MCLKSEL:
-+ reg = wm8980_read_reg_cache(codec, WM8980_CLOCK) & 0x0ff;
-+ wm8980_write(codec, WM8980_CLOCK, reg | div);
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ return 0;
-+}
-+
-+static int wm8980_mute(struct snd_soc_codec_dai *dai, int mute)
-+{
-+ struct snd_soc_codec *codec = dai->codec;
-+ u16 mute_reg = wm8980_read_reg_cache(codec, WM8980_DAC) & 0xffbf;
-+
-+ if(mute)
-+ wm8980_write(codec, WM8980_DAC, mute_reg | 0x40);
-+ else
-+ wm8980_write(codec, WM8980_DAC, mute_reg);
-+
-+ return 0;
-+}
-+
-+/* TODO: liam need to make this lower power with dapm */
-+static int wm8980_dapm_event(struct snd_soc_codec *codec, int event)
-+{
-+
-+ switch (event) {
-+ case SNDRV_CTL_POWER_D0: /* full On */
-+ /* vref/mid, clk and osc on, dac unmute, active */
-+ wm8980_write(codec, WM8980_POWER1, 0x1ff);
-+ wm8980_write(codec, WM8980_POWER2, 0x1ff);
-+ wm8980_write(codec, WM8980_POWER3, 0x1ff);
-+ break;
-+ case SNDRV_CTL_POWER_D1: /* partial On */
-+ case SNDRV_CTL_POWER_D2: /* partial On */
-+ break;
-+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
-+ /* everything off except vref/vmid, dac mute, inactive */
-+
-+ break;
-+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
-+ /* everything off, dac mute, inactive */
-+ wm8980_write(codec, WM8980_POWER1, 0x0);
-+ wm8980_write(codec, WM8980_POWER2, 0x0);
-+ wm8980_write(codec, WM8980_POWER3, 0x0);
-+ break;
-+ }
-+ codec->dapm_state = event;
-+ return 0;
-+}
-+
-+#define WM8980_RATES \
-+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
-+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
-+ SNDRV_PCM_RATE_48000)
-+
-+#define WM8980_FORMATS \
-+ (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \
-+ SNDRV_PCM_FORMAT_S24_3LE | SNDRV_PCM_FORMAT_S24_LE)
-+
-+struct snd_soc_codec_dai wm8980_dai = {
-+ .name = "WM8980 HiFi",
-+ .playback = {
-+ .stream_name = "Playback",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = WM8980_RATES,
-+ .formats = WM8980_FORMATS,},
-+ .capture = {
-+ .stream_name = "Capture",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = WM8980_RATES,
-+ .formats = WM8980_FORMATS,},
-+ .ops = {
-+ .hw_params = wm8980_hw_params,
-+ },
-+ .dai_ops = {
-+ .digital_mute = wm8980_mute,
-+ .set_fmt = wm8980_set_dai_fmt,
-+ .set_clkdiv = wm8980_set_dai_clkdiv,
-+ .set_pll = wm8980_set_dai_pll,
-+ },
-+};
-+EXPORT_SYMBOL_GPL(wm8980_dai);
-+
-+static int wm8980_suspend(struct platform_device *pdev, pm_message_t state)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+
-+ wm8980_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
-+ return 0;
-+}
-+
-+static int wm8980_resume(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+ int i;
-+ u8 data[2];
-+ u16 *cache = codec->reg_cache;
-+
-+ /* Sync reg_cache with the hardware */
-+ for (i = 0; i < ARRAY_SIZE(wm8980_reg); i++) {
-+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
-+ data[1] = cache[i] & 0x00ff;
-+ codec->hw_write(codec->control_data, data, 2);
-+ }
-+ wm8980_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
-+ wm8980_dapm_event(codec, codec->suspend_dapm_state);
-+ return 0;
-+}
-+
-+/*
-+ * initialise the WM8980 driver
-+ * register the mixer and dsp interfaces with the kernel
-+ */
-+static int wm8980_init(struct snd_soc_device* socdev)
-+{
-+ struct snd_soc_codec *codec = socdev->codec;
-+ int ret = 0;
-+
-+ codec->name = "WM8980";
-+ codec->owner = THIS_MODULE;
-+ codec->read = wm8980_read_reg_cache;
-+ codec->write = wm8980_write;
-+ codec->dapm_event = wm8980_dapm_event;
-+ codec->dai = &wm8980_dai;
-+ codec->num_dai = 1;
-+ codec->reg_cache_size = ARRAY_SIZE(wm8980_reg);
-+ codec->reg_cache =
-+ kzalloc(sizeof(u16) * ARRAY_SIZE(wm8980_reg), GFP_KERNEL);
-+ if (codec->reg_cache == NULL)
-+ return -ENOMEM;
-+ memcpy(codec->reg_cache, wm8980_reg,
-+ sizeof(u16) * ARRAY_SIZE(wm8980_reg));
-+ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8980_reg);
-+
-+ wm8980_reset(codec);
-+
-+ /* register pcms */
-+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
-+ if(ret < 0) {
-+ printk(KERN_ERR "wm8980: failed to create pcms\n");
-+ goto pcm_err;
-+ }
-+
-+ /* power on device */
-+ wm8980_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
-+ wm8980_add_controls(codec);
-+ wm8980_add_widgets(codec);
-+ ret = snd_soc_register_card(socdev);
-+ if (ret < 0) {
-+ printk(KERN_ERR "wm8980: failed to register card\n");
-+ goto card_err;
-+ }
-+ return ret;
-+
-+card_err:
-+ snd_soc_free_pcms(socdev);
-+ snd_soc_dapm_free(socdev);
-+pcm_err:
-+ kfree(codec->reg_cache);
-+ return ret;
-+}
-+
-+static struct snd_soc_device *wm8980_socdev;
-+
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+
-+/*
-+ * WM8980 2 wire address is 0x1a
-+ */
-+#define I2C_DRIVERID_WM8980 0xfefe /* liam - need a proper id */
-+
-+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
-+
-+/* Magic definition of all other variables and things */
-+I2C_CLIENT_INSMOD;
-+
-+static struct i2c_driver wm8980_i2c_driver;
-+static struct i2c_client client_template;
-+
-+/* If the i2c layer weren't so broken, we could pass this kind of data
-+ around */
-+
-+static int wm8980_codec_probe(struct i2c_adapter *adap, int addr, int kind)
-+{
-+ struct snd_soc_device *socdev = wm8980_socdev;
-+ struct wm8980_setup_data *setup = socdev->codec_data;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ struct i2c_client *i2c;
-+ int ret;
-+
-+ if (addr != setup->i2c_address)
-+ return -ENODEV;
-+
-+ client_template.adapter = adap;
-+ client_template.addr = addr;
-+
-+ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
-+ if (i2c == NULL){
-+ kfree(codec);
-+ return -ENOMEM;
-+ }
-+ memcpy(i2c, &client_template, sizeof(struct i2c_client));
-+
-+ i2c_set_clientdata(i2c, codec);
-+
-+ codec->control_data = i2c;
-+
-+ ret = i2c_attach_client(i2c);
-+ if(ret < 0) {
-+ err("failed to attach codec at addr %x\n", addr);
-+ goto err;
-+ }
-+
-+ ret = wm8980_init(socdev);
-+ if(ret < 0) {
-+ err("failed to initialise WM8980\n");
-+ goto err;
-+ }
-+ return ret;
-+
-+err:
-+ kfree(codec);
-+ kfree(i2c);
-+ return ret;
-+
-+}
-+
-+static int wm8980_i2c_detach(struct i2c_client *client)
-+{
-+ struct snd_soc_codec *codec = i2c_get_clientdata(client);
-+ i2c_detach_client(client);
-+ kfree(codec->reg_cache);
-+ kfree(client);
-+ return 0;
-+}
-+
-+static int wm8980_i2c_attach(struct i2c_adapter *adap)
-+{
-+ return i2c_probe(adap, &addr_data, wm8980_codec_probe);
-+}
-+
-+/* corgi i2c codec control layer */
-+static struct i2c_driver wm8980_i2c_driver = {
-+ .driver = {
-+ .name = "WM8980 I2C Codec",
-+ .owner = THIS_MODULE,
-+ },
-+ .id = I2C_DRIVERID_WM8980,
-+ .attach_adapter = wm8980_i2c_attach,
-+ .detach_client = wm8980_i2c_detach,
-+ .command = NULL,
-+};
-+
-+static struct i2c_client client_template = {
-+ .name = "WM8980",
-+ .driver = &wm8980_i2c_driver,
-+};
-+#endif
-+
-+static int wm8980_probe(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct wm8980_setup_data *setup;
-+ struct snd_soc_codec *codec;
-+ int ret = 0;
-+
-+ info("WM8980 Audio Codec %s", WM8980_VERSION);
-+
-+ setup = socdev->codec_data;
-+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
-+ if (codec == NULL)
-+ return -ENOMEM;
-+
-+ socdev->codec = codec;
-+ mutex_init(&codec->mutex);
-+ INIT_LIST_HEAD(&codec->dapm_widgets);
-+ INIT_LIST_HEAD(&codec->dapm_paths);
-+
-+ wm8980_socdev = socdev;
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+ if (setup->i2c_address) {
-+ normal_i2c[0] = setup->i2c_address;
-+ codec->hw_write = (hw_write_t)i2c_master_send;
-+ ret = i2c_add_driver(&wm8980_i2c_driver);
-+ if (ret != 0)
-+ printk(KERN_ERR "can't add i2c driver");
-+ }
-+#else
-+ /* Add other interfaces here */
-+#endif
-+ return ret;
-+}
-+
-+/* power down chip */
-+static int wm8980_remove(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+
-+ if (codec->control_data)
-+ wm8980_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
-+
-+ snd_soc_free_pcms(socdev);
-+ snd_soc_dapm_free(socdev);
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+ i2c_del_driver(&wm8980_i2c_driver);
-+#endif
-+ kfree(codec);
-+
-+ return 0;
-+}
-+
-+struct snd_soc_codec_device soc_codec_dev_wm8980 = {
-+ .probe = wm8980_probe,
-+ .remove = wm8980_remove,
-+ .suspend = wm8980_suspend,
-+ .resume = wm8980_resume,
-+};
-+
-+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8980);
-+
-+MODULE_DESCRIPTION("ASoC WM8980 driver");
-+MODULE_AUTHOR("Mike Arthur");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/codecs/wm8980.h
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/codecs/wm8980.h
-@@ -0,0 +1,116 @@
-+/*
-+ * wm8980.h -- WM8980 Soc Audio driver
-+ *
-+ * 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 _WM8980_H
-+#define _WM8980_H
-+
-+/* WM8980 register space */
-+
-+#define WM8980_RESET 0x0
-+#define WM8980_POWER1 0x1
-+#define WM8980_POWER2 0x2
-+#define WM8980_POWER3 0x3
-+#define WM8980_IFACE 0x4
-+#define WM8980_COMP 0x5
-+#define WM8980_CLOCK 0x6
-+#define WM8980_ADD 0x7
-+#define WM8980_GPIO 0x8
-+#define WM8980_JACK1 0x9
-+#define WM8980_DAC 0xa
-+#define WM8980_DACVOLL 0xb
-+#define WM8980_DACVOLR 0xc
-+#define WM8980_JACK2 0xd
-+#define WM8980_ADC 0xe
-+#define WM8980_ADCVOLL 0xf
-+#define WM8980_ADCVOLR 0x10
-+#define WM8980_EQ1 0x12
-+#define WM8980_EQ2 0x13
-+#define WM8980_EQ3 0x14
-+#define WM8980_EQ4 0x15
-+#define WM8980_EQ5 0x16
-+#define WM8980_DACLIM1 0x18
-+#define WM8980_DACLIM2 0x19
-+#define WM8980_NOTCH1 0x1b
-+#define WM8980_NOTCH2 0x1c
-+#define WM8980_NOTCH3 0x1d
-+#define WM8980_NOTCH4 0x1e
-+#define WM8980_ALC1 0x20
-+#define WM8980_ALC2 0x21
-+#define WM8980_ALC3 0x22
-+#define WM8980_NGATE 0x23
-+#define WM8980_PLLN 0x24
-+#define WM8980_PLLK1 0x25
-+#define WM8980_PLLK2 0x26
-+#define WM8980_PLLK3 0x27
-+#define WM8980_VIDEO 0x28
-+#define WM8980_3D 0x29
-+#define WM8980_BEEP 0x2b
-+#define WM8980_INPUT 0x2c
-+#define WM8980_INPPGAL 0x2d
-+#define WM8980_INPPGAR 0x2e
-+#define WM8980_ADCBOOSTL 0x2f
-+#define WM8980_ADCBOOSTR 0x30
-+#define WM8980_OUTPUT 0x31
-+#define WM8980_MIXL 0x32
-+#define WM8980_MIXR 0x33
-+#define WM8980_HPVOLL 0x34
-+#define WM8980_HPVOLR 0x35
-+#define WM8980_SPKVOLL 0x36
-+#define WM8980_SPKVOLR 0x37
-+#define WM8980_OUT3MIX 0x38
-+#define WM8980_MONOMIX 0x39
-+
-+#define WM8980_CACHEREGNUM 58
-+
-+/*
-+ * WM8980 Clock dividers
-+ */
-+#define WM8980_MCLKDIV 0
-+#define WM8980_BCLKDIV 1
-+#define WM8980_OPCLKDIV 2
-+#define WM8980_DACOSR 3
-+#define WM8980_ADCOSR 4
-+#define WM8980_MCLKSEL 5
-+
-+#define WM8980_MCLK_MCLK (0 << 8)
-+#define WM8980_MCLK_PLL (1 << 8)
-+
-+#define WM8980_MCLK_DIV_1 (0 << 5)
-+#define WM8980_MCLK_DIV_1_5 (1 << 5)
-+#define WM8980_MCLK_DIV_2 (2 << 5)
-+#define WM8980_MCLK_DIV_3 (3 << 5)
-+#define WM8980_MCLK_DIV_4 (4 << 5)
-+#define WM8980_MCLK_DIV_5_5 (5 << 5)
-+#define WM8980_MCLK_DIV_6 (6 << 5)
-+
-+#define WM8980_BCLK_DIV_1 (0 << 2)
-+#define WM8980_BCLK_DIV_2 (1 << 2)
-+#define WM8980_BCLK_DIV_4 (2 << 2)
-+#define WM8980_BCLK_DIV_8 (3 << 2)
-+#define WM8980_BCLK_DIV_16 (4 << 2)
-+#define WM8980_BCLK_DIV_32 (5 << 2)
-+
-+#define WM8980_DACOSR_64 (0 << 3)
-+#define WM8980_DACOSR_128 (1 << 3)
-+
-+#define WM8980_ADCOSR_64 (0 << 3)
-+#define WM8980_ADCOSR_128 (1 << 3)
-+
-+#define WM8980_OPCLK_DIV_1 (0 << 4)
-+#define WM8980_OPCLK_DIV_2 (1 << 4)
-+#define WM8980_OPCLK_DIV_3 (2 << 4)
-+#define WM8980_OPCLK_DIV_4 (3 << 4)
-+
-+struct wm8980_setup_data {
-+ unsigned short i2c_address;
-+};
-+
-+extern struct snd_soc_codec_dai wm8980_dai;
-+extern struct snd_soc_codec_device soc_codec_dev_wm8980;
-+
-+#endif
-Index: linux-2.6.21-moko/sound/soc/codecs/wm8510.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/codecs/wm8510.c
-@@ -0,0 +1,860 @@
-+/*
-+ * wm8510.c -- WM8510 ALSA Soc Audio driver
-+ *
-+ * Copyright 2006 Wolfson Microelectronics PLC.
-+ *
-+ * Author: Liam Girdwood <liam.girdwood at wolfsonmicro.com>
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License version 2 as
-+ * published by the Free Software Foundation.
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/moduleparam.h>
-+#include <linux/version.h>
-+#include <linux/kernel.h>
-+#include <linux/init.h>
-+#include <linux/delay.h>
-+#include <linux/pm.h>
-+#include <linux/i2c.h>
-+#include <linux/platform_device.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/pcm_params.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+#include <sound/initval.h>
-+
-+#include "wm8510.h"
-+
-+#define AUDIO_NAME "wm8510"
-+#define WM8510_VERSION "0.6"
-+
-+/*
-+ * Debug
-+ */
-+
-+#define WM8510_DEBUG 0
-+
-+#ifdef WM8510_DEBUG
-+#define dbg(format, arg...) \
-+ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
-+#else
-+#define dbg(format, arg...) do {} while (0)
-+#endif
-+#define err(format, arg...) \
-+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
-+#define info(format, arg...) \
-+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
-+#define warn(format, arg...) \
-+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
-+
-+struct snd_soc_codec_device soc_codec_dev_wm8510;
-+
-+/*
-+ * wm8510 register cache
-+ * We can't read the WM8510 register space when we are
-+ * using 2 wire for device control, so we cache them instead.
-+ */
-+static const u16 wm8510_reg[WM8510_CACHEREGNUM] = {
-+ 0x0000, 0x0000, 0x0000, 0x0000,
-+ 0x0050, 0x0000, 0x0140, 0x0000,
-+ 0x0000, 0x0000, 0x0000, 0x00ff,
-+ 0x0000, 0x0000, 0x0100, 0x00ff,
-+ 0x0000, 0x0000, 0x012c, 0x002c,
-+ 0x002c, 0x002c, 0x002c, 0x0000,
-+ 0x0032, 0x0000, 0x0000, 0x0000,
-+ 0x0000, 0x0000, 0x0000, 0x0000,
-+ 0x0038, 0x000b, 0x0032, 0x0000,
-+ 0x0008, 0x000c, 0x0093, 0x00e9,
-+ 0x0000, 0x0000, 0x0000, 0x0000,
-+ 0x0003, 0x0010, 0x0000, 0x0000,
-+ 0x0000, 0x0002, 0x0000, 0x0000,
-+ 0x0000, 0x0000, 0x0039, 0x0000,
-+ 0x0000,
-+};
-+
-+/*
-+ * read wm8510 register cache
-+ */
-+static inline unsigned int wm8510_read_reg_cache(struct snd_soc_codec * codec,
-+ unsigned int reg)
-+{
-+ u16 *cache = codec->reg_cache;
-+ if (reg == WM8510_RESET)
-+ return 0;
-+ if (reg >= WM8510_CACHEREGNUM)
-+ return -1;
-+ return cache[reg];
-+}
-+
-+/*
-+ * write wm8510 register cache
-+ */
-+static inline void wm8510_write_reg_cache(struct snd_soc_codec *codec,
-+ u16 reg, unsigned int value)
-+{
-+ u16 *cache = codec->reg_cache;
-+ if (reg >= WM8510_CACHEREGNUM)
-+ return;
-+ cache[reg] = value;
-+}
-+
-+/*
-+ * write to the WM8510 register space
-+ */
-+static int wm8510_write(struct snd_soc_codec *codec, unsigned int reg,
-+ unsigned int value)
-+{
-+ u8 data[2];
-+
-+ /* data is
-+ * D15..D9 WM8510 register offset
-+ * D8...D0 register data
-+ */
-+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
-+ data[1] = value & 0x00ff;
-+
-+ wm8510_write_reg_cache (codec, reg, value);
-+ if (codec->hw_write(codec->control_data, data, 2) == 2)
-+ return 0;
-+ else
-+ return -EIO;
-+}
-+
-+#define wm8510_reset(c) wm8510_write(c, WM8510_RESET, 0)
-+
-+static const char *wm8510_companding[] = {"Off", "NC", "u-law", "A-law" };
-+static const char *wm8510_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
-+static const char *wm8510_alc[] = {"ALC", "Limiter" };
-+
-+static const struct soc_enum wm8510_enum[] = {
-+ SOC_ENUM_SINGLE(WM8510_COMP, 1, 4, wm8510_companding), /* adc */
-+ SOC_ENUM_SINGLE(WM8510_COMP, 3, 4, wm8510_companding), /* dac */
-+ SOC_ENUM_SINGLE(WM8510_DAC, 4, 4, wm8510_deemp),
-+ SOC_ENUM_SINGLE(WM8510_ALC3, 8, 2, wm8510_alc),
-+};
-+
-+static const struct snd_kcontrol_new wm8510_snd_controls[] = {
-+
-+SOC_SINGLE("Digital Loopback Switch", WM8510_COMP, 0, 1, 0),
-+
-+SOC_ENUM("DAC Companding", wm8510_enum[1]),
-+SOC_ENUM("ADC Companding", wm8510_enum[0]),
-+
-+SOC_ENUM("Playback De-emphasis", wm8510_enum[2]),
-+SOC_SINGLE("DAC Inversion Switch", WM8510_DAC, 0, 1, 0),
-+
-+SOC_SINGLE("Master Playback Volume", WM8510_DACVOL, 0, 127, 0),
-+
-+SOC_SINGLE("High Pass Filter Switch", WM8510_ADC, 8, 1, 0),
-+SOC_SINGLE("High Pass Cut Off", WM8510_ADC, 4, 7, 0),
-+SOC_SINGLE("ADC Inversion Switch", WM8510_COMP, 0, 1, 0),
-+
-+SOC_SINGLE("Capture Volume", WM8510_ADCVOL, 0, 127, 0),
-+
-+SOC_SINGLE("DAC Playback Limiter Switch", WM8510_DACLIM1, 8, 1, 0),
-+SOC_SINGLE("DAC Playback Limiter Decay", WM8510_DACLIM1, 4, 15, 0),
-+SOC_SINGLE("DAC Playback Limiter Attack", WM8510_DACLIM1, 0, 15, 0),
-+
-+SOC_SINGLE("DAC Playback Limiter Threshold", WM8510_DACLIM2, 4, 7, 0),
-+SOC_SINGLE("DAC Playback Limiter Boost", WM8510_DACLIM2, 0, 15, 0),
-+
-+SOC_SINGLE("ALC Enable Switch", WM8510_ALC1, 8, 1, 0),
-+SOC_SINGLE("ALC Capture Max Gain", WM8510_ALC1, 3, 7, 0),
-+SOC_SINGLE("ALC Capture Min Gain", WM8510_ALC1, 0, 7, 0),
-+
-+SOC_SINGLE("ALC Capture ZC Switch", WM8510_ALC2, 8, 1, 0),
-+SOC_SINGLE("ALC Capture Hold", WM8510_ALC2, 4, 7, 0),
-+SOC_SINGLE("ALC Capture Target", WM8510_ALC2, 0, 15, 0),
-+
-+SOC_ENUM("ALC Capture Mode", wm8510_enum[3]),
-+SOC_SINGLE("ALC Capture Decay", WM8510_ALC3, 4, 15, 0),
-+SOC_SINGLE("ALC Capture Attack", WM8510_ALC3, 0, 15, 0),
-+
-+SOC_SINGLE("ALC Capture Noise Gate Switch", WM8510_NGATE, 3, 1, 0),
-+SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8510_NGATE, 0, 7, 0),
-+
-+SOC_SINGLE("Capture PGA ZC Switch", WM8510_INPPGA, 7, 1, 0),
-+SOC_SINGLE("Capture PGA Volume", WM8510_INPPGA, 0, 63, 0),
-+
-+SOC_SINGLE("Speaker Playback ZC Switch", WM8510_SPKVOL, 7, 1, 0),
-+SOC_SINGLE("Speaker Playback Switch", WM8510_SPKVOL, 6, 1, 1),
-+SOC_SINGLE("Speaker Playback Volume", WM8510_SPKVOL, 0, 63, 0),
-+
-+SOC_SINGLE("Capture Boost(+20dB)", WM8510_ADCBOOST, 8, 1, 0),
-+SOC_SINGLE("Mono Playback Switch", WM8510_MONOMIX, 6, 1, 0),
-+};
-+
-+/* add non dapm controls */
-+static int wm8510_add_controls(struct snd_soc_codec *codec)
-+{
-+ int err, i;
-+
-+ for (i = 0; i < ARRAY_SIZE(wm8510_snd_controls); i++) {
-+ err = snd_ctl_add(codec->card,
-+ snd_soc_cnew(&wm8510_snd_controls[i],codec, NULL));
-+ if (err < 0)
-+ return err;
-+ }
-+
-+ return 0;
-+}
-+
-+/* Speaker Output Mixer */
-+static const struct snd_kcontrol_new wm8510_speaker_mixer_controls[] = {
-+SOC_DAPM_SINGLE("Line Bypass Switch", WM8510_SPKMIX, 1, 1, 0),
-+SOC_DAPM_SINGLE("Aux Playback Switch", WM8510_SPKMIX, 5, 1, 0),
-+SOC_DAPM_SINGLE("PCM Playback Switch", WM8510_SPKMIX, 0, 1, 1),
-+};
-+
-+/* Mono Output Mixer */
-+static const struct snd_kcontrol_new wm8510_mono_mixer_controls[] = {
-+SOC_DAPM_SINGLE("Line Bypass Switch", WM8510_MONOMIX, 1, 1, 0),
-+SOC_DAPM_SINGLE("Aux Playback Switch", WM8510_MONOMIX, 2, 1, 0),
-+SOC_DAPM_SINGLE("PCM Playback Switch", WM8510_MONOMIX, 0, 1, 1),
-+};
-+
-+/* AUX Input boost vol */
-+static const struct snd_kcontrol_new wm8510_aux_boost_controls =
-+SOC_DAPM_SINGLE("Aux Volume", WM8510_ADCBOOST, 0, 7, 0);
-+
-+/* Mic Input boost vol */
-+static const struct snd_kcontrol_new wm8510_mic_boost_controls =
-+SOC_DAPM_SINGLE("Mic Volume", WM8510_ADCBOOST, 4, 7, 0);
-+
-+/* Capture boost switch */
-+static const struct snd_kcontrol_new wm8510_capture_boost_controls =
-+SOC_DAPM_SINGLE("Capture Boost Switch", WM8510_INPPGA, 6, 1, 0);
-+
-+/* Aux In to PGA */
-+static const struct snd_kcontrol_new wm8510_aux_capture_boost_controls =
-+SOC_DAPM_SINGLE("Aux Capture Boost Switch", WM8510_INPPGA, 2, 1, 0);
-+
-+/* Mic P In to PGA */
-+static const struct snd_kcontrol_new wm8510_micp_capture_boost_controls =
-+SOC_DAPM_SINGLE("Mic P Capture Boost Switch", WM8510_INPPGA, 0, 1, 0);
-+
-+/* Mic N In to PGA */
-+static const struct snd_kcontrol_new wm8510_micn_capture_boost_controls =
-+SOC_DAPM_SINGLE("Mic N Capture Boost Switch", WM8510_INPPGA, 1, 1, 0);
-+
-+static const struct snd_soc_dapm_widget wm8510_dapm_widgets[] = {
-+SND_SOC_DAPM_MIXER("Speaker Mixer", WM8510_POWER3, 2, 0,
-+ &wm8510_speaker_mixer_controls[0],
-+ ARRAY_SIZE(wm8510_speaker_mixer_controls)),
-+SND_SOC_DAPM_MIXER("Mono Mixer", WM8510_POWER3, 3, 0,
-+ &wm8510_mono_mixer_controls[0],
-+ ARRAY_SIZE(wm8510_mono_mixer_controls)),
-+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8510_POWER3, 0, 0),
-+SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8510_POWER3, 0, 0),
-+SND_SOC_DAPM_PGA("Aux Input", WM8510_POWER1, 6, 0, NULL, 0),
-+SND_SOC_DAPM_PGA("SpkN Out", WM8510_POWER3, 5, 0, NULL, 0),
-+SND_SOC_DAPM_PGA("SpkP Out", WM8510_POWER3, 6, 0, NULL, 0),
-+SND_SOC_DAPM_PGA("Mono Out", WM8510_POWER3, 7, 0, NULL, 0),
-+SND_SOC_DAPM_PGA("Mic PGA", WM8510_POWER2, 2, 0, NULL, 0),
-+
-+SND_SOC_DAPM_PGA("Aux Boost", SND_SOC_NOPM, 0, 0,
-+ &wm8510_aux_boost_controls, 1),
-+SND_SOC_DAPM_PGA("Mic Boost", SND_SOC_NOPM, 0, 0,
-+ &wm8510_mic_boost_controls, 1),
-+SND_SOC_DAPM_SWITCH("Capture Boost", SND_SOC_NOPM, 0, 0,
-+ &wm8510_capture_boost_controls),
-+
-+SND_SOC_DAPM_MIXER("Boost Mixer", WM8510_POWER2, 4, 0, NULL, 0),
-+
-+SND_SOC_DAPM_MICBIAS("Mic Bias", WM8510_POWER1, 4, 0),
-+
-+SND_SOC_DAPM_INPUT("MICN"),
-+SND_SOC_DAPM_INPUT("MICP"),
-+SND_SOC_DAPM_INPUT("AUX"),
-+SND_SOC_DAPM_OUTPUT("MONOOUT"),
-+SND_SOC_DAPM_OUTPUT("SPKOUTP"),
-+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
-+};
-+
-+static const char *audio_map[][3] = {
-+ /* Mono output mixer */
-+ {"Mono Mixer", "PCM Playback Switch", "DAC"},
-+ {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
-+ {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
-+
-+ /* Speaker output mixer */
-+ {"Speaker Mixer", "PCM Playback Switch", "DAC"},
-+ {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
-+ {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
-+
-+ /* Outputs */
-+ {"Mono Out", NULL, "Mono Mixer"},
-+ {"MONOOUT", NULL, "Mono Out"},
-+ {"SpkN Out", NULL, "Speaker Mixer"},
-+ {"SpkP Out", NULL, "Speaker Mixer"},
-+ {"SPKOUTN", NULL, "SpkN Out"},
-+ {"SPKOUTP", NULL, "SpkP Out"},
-+
-+ /* Boost Mixer */
-+ {"Boost Mixer", NULL, "ADC"},
-+ {"Capture Boost Switch", "Aux Capture Boost Switch", "AUX"},
-+ {"Aux Boost", "Aux Volume", "Boost Mixer"},
-+ {"Capture Boost", "Capture Switch", "Boost Mixer"},
-+ {"Mic Boost", "Mic Volume", "Boost Mixer"},
-+
-+ /* Inputs */
-+ {"MICP", NULL, "Mic Boost"},
-+ {"MICN", NULL, "Mic PGA"},
-+ {"Mic PGA", NULL, "Capture Boost"},
-+ {"AUX", NULL, "Aux Input"},
-+
-+ /* terminator */
-+ {NULL, NULL, NULL},
-+};
-+
-+static int wm8510_add_widgets(struct snd_soc_codec *codec)
-+{
-+ int i;
-+
-+ for(i = 0; i < ARRAY_SIZE(wm8510_dapm_widgets); i++) {
-+ snd_soc_dapm_new_control(codec, &wm8510_dapm_widgets[i]);
-+ }
-+
-+ /* set up audio path audio_mapnects */
-+ for(i = 0; audio_map[i][0] != NULL; i++) {
-+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
-+ audio_map[i][1], audio_map[i][2]);
-+ }
-+
-+ snd_soc_dapm_new_widgets(codec);
-+ return 0;
-+}
-+
-+struct pll_ {
-+ unsigned int pre_div:4; /* prescale - 1 */
-+ unsigned int n:4;
-+ unsigned int k;
-+};
-+
-+static struct pll_ pll_div;
-+
-+/* The size in bits of the pll divide multiplied by 10
-+ * to allow rounding later */
-+#define FIXED_PLL_SIZE ((1 << 24) * 10)
-+
-+static void pll_factors(unsigned int target, unsigned int source)
-+{
-+ unsigned long long Kpart;
-+ unsigned int K, Ndiv, Nmod;
-+
-+ Ndiv = target / source;
-+ if (Ndiv < 6) {
-+ source >>= 1;
-+ pll_div.pre_div = 1;
-+ Ndiv = target / source;
-+ } else
-+ pll_div.pre_div = 0;
-+
-+ if ((Ndiv < 6) || (Ndiv > 12))
-+ printk(KERN_WARNING
-+ "WM8510 N value outwith recommended range! N = %d\n",Ndiv);
-+
-+ pll_div.n = Ndiv;
-+ Nmod = target % source;
-+ Kpart = FIXED_PLL_SIZE * (long long)Nmod;
-+
-+ do_div(Kpart, source);
-+
-+ K = Kpart & 0xFFFFFFFF;
-+
-+ /* Check if we need to round */
-+ if ((K % 10) >= 5)
-+ K += 5;
-+
-+ /* Move down to proper range now rounding is done */
-+ K /= 10;
-+
-+ pll_div.k = K;
-+}
-+
-+static int wm8510_set_dai_pll(struct snd_soc_codec_dai *codec_dai,
-+ int pll_id, unsigned int freq_in, unsigned int freq_out)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 reg;
-+
-+ if(freq_in == 0 || freq_out == 0) {
-+ reg = wm8510_read_reg_cache(codec, WM8510_POWER1);
-+ wm8510_write(codec, WM8510_POWER1, reg & 0x1df);
-+ return 0;
-+ }
-+
-+ pll_factors(freq_out*8, freq_in);
-+
-+ wm8510_write(codec, WM8510_PLLN, (pll_div.pre_div << 4) | pll_div.n);
-+ wm8510_write(codec, WM8510_PLLK1, pll_div.k >> 18);
-+ wm8510_write(codec, WM8510_PLLK1, (pll_div.k >> 9) && 0x1ff);
-+ wm8510_write(codec, WM8510_PLLK1, pll_div.k && 0x1ff);
-+ reg = wm8510_read_reg_cache(codec, WM8510_POWER1);
-+ wm8510_write(codec, WM8510_POWER1, reg | 0x020);
-+ return 0;
-+
-+}
-+
-+/*
-+ * Configure WM8510 clock dividers.
-+ */
-+static int wm8510_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
-+ int div_id, int div)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 reg;
-+
-+ switch (div_id) {
-+ case WM8510_OPCLKDIV:
-+ reg = wm8510_read_reg_cache(codec, WM8510_GPIO & 0x1cf);
-+ wm8510_write(codec, WM8510_GPIO, reg | div);
-+ break;
-+ case WM8510_MCLKDIV:
-+ reg = wm8510_read_reg_cache(codec, WM8510_CLOCK & 0x1f);
-+ wm8510_write(codec, WM8510_CLOCK, reg | div);
-+ break;
-+ case WM8510_ADCCLK:
-+ reg = wm8510_read_reg_cache(codec, WM8510_ADC & 0x1f7);
-+ wm8510_write(codec, WM8510_ADC, reg | div);
-+ break;
-+ case WM8510_DACCLK:
-+ reg = wm8510_read_reg_cache(codec, WM8510_DAC & 0x1f7);
-+ wm8510_write(codec, WM8510_DAC, reg | div);
-+ break;
-+ case WM8510_BCLKDIV:
-+ reg = wm8510_read_reg_cache(codec, WM8510_CLOCK & 0x1e3);
-+ wm8510_write(codec, WM8510_CLOCK, reg | div);
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ return 0;
-+}
-+
-+static int wm8510_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
-+ unsigned int fmt)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 iface = 0;
-+ u16 clk = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x1fe;
-+
-+ /* set master/slave audio interface */
-+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
-+ case SND_SOC_DAIFMT_CBM_CFM:
-+ clk |= 0x0001;
-+ break;
-+ case SND_SOC_DAIFMT_CBS_CFS:
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ /* interface format */
-+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
-+ case SND_SOC_DAIFMT_I2S:
-+ iface |= 0x0010;
-+ break;
-+ case SND_SOC_DAIFMT_RIGHT_J:
-+ break;
-+ case SND_SOC_DAIFMT_LEFT_J:
-+ iface |= 0x0008;
-+ break;
-+ case SND_SOC_DAIFMT_DSP_A:
-+ iface |= 0x00018;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ /* clock inversion */
-+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
-+ case SND_SOC_DAIFMT_NB_NF:
-+ break;
-+ case SND_SOC_DAIFMT_IB_IF:
-+ iface |= 0x0180;
-+ break;
-+ case SND_SOC_DAIFMT_IB_NF:
-+ iface |= 0x0100;
-+ break;
-+ case SND_SOC_DAIFMT_NB_IF:
-+ iface |= 0x0080;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ wm8510_write(codec, WM8510_IFACE, iface);
-+ wm8510_write(codec, WM8510_CLOCK, clk);
-+ return 0;
-+}
-+
-+static int wm8510_pcm_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_device *socdev = rtd->socdev;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ u16 iface = wm8510_read_reg_cache(codec, WM8510_ADD) & 0x19f;
-+ u16 adn = wm8510_read_reg_cache(codec, WM8510_ADD) & 0x1f1;
-+
-+ /* bit size */
-+ switch (params_format(params)) {
-+ case SNDRV_PCM_FORMAT_S16_LE:
-+ break;
-+ case SNDRV_PCM_FORMAT_S20_3LE:
-+ iface |= 0x0020;
-+ break;
-+ case SNDRV_PCM_FORMAT_S24_LE:
-+ iface |= 0x0040;
-+ break;
-+ case SNDRV_PCM_FORMAT_S32_LE:
-+ iface |= 0x0060;
-+ break;
-+ }
-+
-+ /* filter coefficient */
-+ switch (params_rate(params)) {
-+ case SNDRV_PCM_RATE_8000:
-+ adn |= 0x5 << 1;
-+ break;
-+ case SNDRV_PCM_RATE_11025:
-+ adn |= 0x4 << 1;
-+ break;
-+ case SNDRV_PCM_RATE_16000:
-+ adn |= 0x3 << 1;
-+ break;
-+ case SNDRV_PCM_RATE_22050:
-+ adn |= 0x2 << 1;
-+ break;
-+ case SNDRV_PCM_RATE_32000:
-+ adn |= 0x1 << 1;
-+ break;
-+ case SNDRV_PCM_RATE_44100:
-+ break;
-+ }
-+
-+ wm8510_write(codec, WM8510_IFACE, iface);
-+ wm8510_write(codec, WM8510_ADD, adn);
-+ return 0;
-+}
-+
-+static int wm8510_mute(struct snd_soc_codec_dai *dai, int mute)
-+{
-+ struct snd_soc_codec *codec = dai->codec;
-+ u16 mute_reg = wm8510_read_reg_cache(codec, WM8510_DAC) & 0xffbf;
-+
-+ if(mute)
-+ wm8510_write(codec, WM8510_DAC, mute_reg | 0x40);
-+ else
-+ wm8510_write(codec, WM8510_DAC, mute_reg);
-+ return 0;
-+}
-+
-+/* liam need to make this lower power with dapm */
-+static int wm8510_dapm_event(struct snd_soc_codec *codec, int event)
-+{
-+
-+ switch (event) {
-+ case SNDRV_CTL_POWER_D0: /* full On */
-+ /* vref/mid, clk and osc on, dac unmute, active */
-+ wm8510_write(codec, WM8510_POWER1, 0x1ff);
-+ wm8510_write(codec, WM8510_POWER2, 0x1ff);
-+ wm8510_write(codec, WM8510_POWER3, 0x1ff);
-+ break;
-+ case SNDRV_CTL_POWER_D1: /* partial On */
-+ case SNDRV_CTL_POWER_D2: /* partial On */
-+ break;
-+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
-+ /* everything off except vref/vmid, dac mute, inactive */
-+
-+ break;
-+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
-+ /* everything off, dac mute, inactive */
-+ wm8510_write(codec, WM8510_POWER1, 0x0);
-+ wm8510_write(codec, WM8510_POWER2, 0x0);
-+ wm8510_write(codec, WM8510_POWER3, 0x0);
-+ break;
-+ }
-+ codec->dapm_state = event;
-+ return 0;
-+}
-+
-+#define WM8510_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
-+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
-+ SNDRV_PCM_RATE_48000)
-+
-+#define WM8510_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
-+ SNDRV_PCM_FMTBIT_S24_LE)
-+
-+struct snd_soc_codec_dai wm8510_dai = {
-+ .name = "WM8510 HiFi",
-+ .playback = {
-+ .stream_name = "Playback",
-+ .channels_min = 1,
-+ .channels_max = 1,
-+ .rates = WM8510_RATES,
-+ .formats = WM8510_FORMATS,},
-+ .capture = {
-+ .stream_name = "Capture",
-+ .channels_min = 1,
-+ .channels_max = 1,
-+ .rates = WM8510_RATES,
-+ .formats = WM8510_FORMATS,},
-+ .ops = {
-+ .hw_params = wm8510_pcm_hw_params,
-+ },
-+ .dai_ops = {
-+ .digital_mute = wm8510_mute,
-+ .set_fmt = wm8510_set_dai_fmt,
-+ .set_clkdiv = wm8510_set_dai_clkdiv,
-+ .set_pll = wm8510_set_dai_pll,
-+ },
-+};
-+EXPORT_SYMBOL_GPL(wm8510_dai);
-+
-+static int wm8510_suspend(struct platform_device *pdev, pm_message_t state)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+
-+ wm8510_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
-+ return 0;
-+}
-+
-+static int wm8510_resume(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+ int i;
-+ u8 data[2];
-+ u16 *cache = codec->reg_cache;
-+
-+ /* Sync reg_cache with the hardware */
-+ for (i = 0; i < ARRAY_SIZE(wm8510_reg); i++) {
-+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
-+ data[1] = cache[i] & 0x00ff;
-+ codec->hw_write(codec->control_data, data, 2);
-+ }
-+ wm8510_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
-+ wm8510_dapm_event(codec, codec->suspend_dapm_state);
-+ return 0;
-+}
-+
-+/*
-+ * initialise the WM8510 driver
-+ * register the mixer and dsp interfaces with the kernel
-+ */
-+static int wm8510_init(struct snd_soc_device *socdev)
-+{
-+ struct snd_soc_codec *codec = socdev->codec;
-+ int ret = 0;
-+
-+ codec->name = "WM8510";
-+ codec->owner = THIS_MODULE;
-+ codec->read = wm8510_read_reg_cache;
-+ codec->write = wm8510_write;
-+ codec->dapm_event = wm8510_dapm_event;
-+ codec->dai = &wm8510_dai;
-+ codec->num_dai = 1;
-+ codec->reg_cache_size = ARRAY_SIZE(wm8510_reg);
-+ codec->reg_cache =
-+ kzalloc(sizeof(u16) * ARRAY_SIZE(wm8510_reg), GFP_KERNEL);
-+ if (codec->reg_cache == NULL)
-+ return -ENOMEM;
-+ memcpy(codec->reg_cache, wm8510_reg,
-+ sizeof(u16) * ARRAY_SIZE(wm8510_reg));
-+ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8510_reg);
-+
-+ wm8510_reset(codec);
-+
-+ /* register pcms */
-+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
-+ if(ret < 0) {
-+ printk(KERN_ERR "wm8510: failed to create pcms\n");
-+ goto pcm_err;
-+ }
-+
-+ /* power on device */
-+ wm8510_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
-+ wm8510_add_controls(codec);
-+ wm8510_add_widgets(codec);
-+ ret = snd_soc_register_card(socdev);
-+ if (ret < 0) {
-+ printk(KERN_ERR "wm8510: failed to register card\n");
-+ goto card_err;
-+ }
-+ return ret;
-+
-+card_err:
-+ snd_soc_free_pcms(socdev);
-+ snd_soc_dapm_free(socdev);
-+pcm_err:
-+ kfree(codec->reg_cache);
-+ return ret;
-+}
-+
-+static struct snd_soc_device *wm8510_socdev;
-+
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+
-+/*
-+ * WM8510 2 wire address is 0x1a
-+ */
-+#define I2C_DRIVERID_WM8510 0xfefe /* liam - need a proper id */
-+
-+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
-+
-+/* Magic definition of all other variables and things */
-+I2C_CLIENT_INSMOD;
-+
-+static struct i2c_driver wm8510_i2c_driver;
-+static struct i2c_client client_template;
-+
-+/* If the i2c layer weren't so broken, we could pass this kind of data
-+ around */
-+
-+static int wm8510_codec_probe(struct i2c_adapter *adap, int addr, int kind)
-+{
-+ struct snd_soc_device *socdev = wm8510_socdev;
-+ struct wm8510_setup_data *setup = socdev->codec_data;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ struct i2c_client *i2c;
-+ int ret;
-+
-+ if (addr != setup->i2c_address)
-+ return -ENODEV;
-+
-+ client_template.adapter = adap;
-+ client_template.addr = addr;
-+
-+ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
-+ if (i2c == NULL){
-+ kfree(codec);
-+ return -ENOMEM;
-+ }
-+ memcpy(i2c, &client_template, sizeof(struct i2c_client));
-+ i2c_set_clientdata(i2c, codec);
-+ codec->control_data = i2c;
-+
-+ ret = i2c_attach_client(i2c);
-+ if(ret < 0) {
-+ err("failed to attach codec at addr %x\n", addr);
-+ goto err;
-+ }
-+
-+ ret = wm8510_init(socdev);
-+ if(ret < 0) {
-+ err("failed to initialise WM8510\n");
-+ goto err;
-+ }
-+ return ret;
-+
-+err:
-+ kfree(codec);
-+ kfree(i2c);
-+ return ret;
-+}
-+
-+static int wm8510_i2c_detach(struct i2c_client *client)
-+{
-+ struct snd_soc_codec *codec = i2c_get_clientdata(client);
-+ i2c_detach_client(client);
-+ kfree(codec->reg_cache);
-+ kfree(client);
-+ return 0;
-+}
-+
-+static int wm8510_i2c_attach(struct i2c_adapter *adap)
-+{
-+ return i2c_probe(adap, &addr_data, wm8510_codec_probe);
-+}
-+
-+/* corgi i2c codec control layer */
-+static struct i2c_driver wm8510_i2c_driver = {
-+ .driver = {
-+ .name = "WM8510 I2C Codec",
-+ .owner = THIS_MODULE,
-+ },
-+ .id = I2C_DRIVERID_WM8510,
-+ .attach_adapter = wm8510_i2c_attach,
-+ .detach_client = wm8510_i2c_detach,
-+ .command = NULL,
-+};
-+
-+static struct i2c_client client_template = {
-+ .name = "WM8510",
-+ .driver = &wm8510_i2c_driver,
-+};
-+#endif
-+
-+static int wm8510_probe(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct wm8510_setup_data *setup;
-+ struct snd_soc_codec *codec;
-+ int ret = 0;
-+
-+ info("WM8510 Audio Codec %s", WM8510_VERSION);
-+
-+ setup = socdev->codec_data;
-+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
-+ if (codec == NULL)
-+ return -ENOMEM;
-+
-+ socdev->codec = codec;
-+ mutex_init(&codec->mutex);
-+ INIT_LIST_HEAD(&codec->dapm_widgets);
-+ INIT_LIST_HEAD(&codec->dapm_paths);
-+
-+ wm8510_socdev = socdev;
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+ if (setup->i2c_address) {
-+ normal_i2c[0] = setup->i2c_address;
-+ codec->hw_write = (hw_write_t)i2c_master_send;
-+ ret = i2c_add_driver(&wm8510_i2c_driver);
-+ if (ret != 0)
-+ printk(KERN_ERR "can't add i2c driver");
-+ }
-+#else
-+ /* Add other interfaces here */
-+#endif
-+ return ret;
-+}
-+
-+/* power down chip */
-+static int wm8510_remove(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+
-+ if (codec->control_data)
-+ wm8510_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
-+
-+ snd_soc_free_pcms(socdev);
-+ snd_soc_dapm_free(socdev);
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+ i2c_del_driver(&wm8510_i2c_driver);
-+#endif
-+ kfree(codec);
-+
-+ return 0;
-+}
-+
-+struct snd_soc_codec_device soc_codec_dev_wm8510 = {
-+ .probe = wm8510_probe,
-+ .remove = wm8510_remove,
-+ .suspend = wm8510_suspend,
-+ .resume = wm8510_resume,
-+};
-+
-+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8510);
-+
-+MODULE_DESCRIPTION("ASoC WM8510 driver");
-+MODULE_AUTHOR("Liam Girdwood");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/codecs/wm8510.h
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/codecs/wm8510.h
-@@ -0,0 +1,103 @@
-+/*
-+ * wm8510.h -- WM8510 Soc Audio driver
-+ *
-+ * 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 _WM8510_H
-+#define _WM8510_H
-+
-+/* WM8510 register space */
-+
-+#define WM8510_RESET 0x0
-+#define WM8510_POWER1 0x1
-+#define WM8510_POWER2 0x2
-+#define WM8510_POWER3 0x3
-+#define WM8510_IFACE 0x4
-+#define WM8510_COMP 0x5
-+#define WM8510_CLOCK 0x6
-+#define WM8510_ADD 0x7
-+#define WM8510_GPIO 0x8
-+#define WM8510_DAC 0xa
-+#define WM8510_DACVOL 0xb
-+#define WM8510_ADC 0xe
-+#define WM8510_ADCVOL 0xf
-+#define WM8510_EQ1 0x12
-+#define WM8510_EQ2 0x13
-+#define WM8510_EQ3 0x14
-+#define WM8510_EQ4 0x15
-+#define WM8510_EQ5 0x16
-+#define WM8510_DACLIM1 0x18
-+#define WM8510_DACLIM2 0x19
-+#define WM8510_NOTCH1 0x1b
-+#define WM8510_NOTCH2 0x1c
-+#define WM8510_NOTCH3 0x1d
-+#define WM8510_NOTCH4 0x1e
-+#define WM8510_ALC1 0x20
-+#define WM8510_ALC2 0x21
-+#define WM8510_ALC3 0x22
-+#define WM8510_NGATE 0x23
-+#define WM8510_PLLN 0x24
-+#define WM8510_PLLK1 0x25
-+#define WM8510_PLLK2 0x26
-+#define WM8510_PLLK3 0x27
-+#define WM8510_ATTEN 0x28
-+#define WM8510_INPUT 0x2c
-+#define WM8510_INPPGA 0x2d
-+#define WM8510_ADCBOOST 0x2f
-+#define WM8510_OUTPUT 0x31
-+#define WM8510_SPKMIX 0x32
-+#define WM8510_SPKVOL 0x36
-+#define WM8510_MONOMIX 0x38
-+
-+#define WM8510_CACHEREGNUM 57
-+
-+/* Clock divider Id's */
-+#define WM8510_OPCLKDIV 0
-+#define WM8510_MCLKDIV 1
-+#define WM8510_ADCCLK 2
-+#define WM8510_DACCLK 3
-+#define WM8510_BCLKDIV 4
-+
-+/* DAC clock dividers */
-+#define WM8510_DACCLK_F2 (1 << 3)
-+#define WM8510_DACCLK_F4 (0 << 3)
-+
-+/* ADC clock dividers */
-+#define WM8510_ADCCLK_F2 (1 << 3)
-+#define WM8510_ADCCLK_F4 (0 << 3)
-+
-+/* PLL Out dividers */
-+#define WM8510_OPCLKDIV_1 (0 << 4)
-+#define WM8510_OPCLKDIV_2 (1 << 4)
-+#define WM8510_OPCLKDIV_3 (2 << 4)
-+#define WM8510_OPCLKDIV_4 (3 << 4)
-+
-+/* BCLK clock dividers */
-+#define WM8510_BCLKDIV_1 (0 << 2)
-+#define WM8510_BCLKDIV_2 (1 << 2)
-+#define WM8510_BCLKDIV_4 (2 << 2)
-+#define WM8510_BCLKDIV_8 (3 << 2)
-+#define WM8510_BCLKDIV_16 (4 << 2)
-+#define WM8510_BCLKDIV_32 (5 << 2)
-+
-+/* MCLK clock dividers */
-+#define WM8510_MCLKDIV_1 (0 << 5)
-+#define WM8510_MCLKDIV_1_5 (1 << 5)
-+#define WM8510_MCLKDIV_2 (2 << 5)
-+#define WM8510_MCLKDIV_3 (3 << 5)
-+#define WM8510_MCLKDIV_4 (4 << 5)
-+#define WM8510_MCLKDIV_6 (5 << 5)
-+#define WM8510_MCLKDIV_8 (6 << 5)
-+#define WM8510_MCLKDIV_12 (7 << 5)
-+
-+struct wm8510_setup_data {
-+ unsigned short i2c_address;
-+};
-+
-+extern struct snd_soc_codec_dai wm8510_dai;
-+extern struct snd_soc_codec_device soc_codec_dev_wm8510;
-+
-+#endif
-Index: linux-2.6.21-moko/sound/soc/imx/imx-ac97.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/imx/imx-ac97.c
-@@ -0,0 +1,222 @@
-+/*
-+ * imx-ssi.c -- SSI driver for Freescale IMX
-+ *
-+ * Copyright 2006 Wolfson Microelectronics PLC.
-+ * Author: Liam Girdwood
-+ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
-+ *
-+ * Based on mxc-alsa-mc13783 (C) 2006 Freescale.
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or (at your
-+ * option) any later version.
-+ *
-+ * Revision history
-+ * 29th Aug 2006 Initial version.
-+ *
-+ */
-+
-+
-+static imx_pcm_dma_params_t imx_ssi1_pcm_stereo_out = {
-+ .name = "SSI1 PCM Stereo out",
-+ .params = {
-+ .bd_number = 1,
-+ .transfer_type = emi_2_per,
-+ .watermark_level = SDMA_TXFIFO_WATERMARK,
-+ .word_size = TRANSFER_16BIT, // maybe add this in setup func
-+ .per_address = SSI1_STX0,
-+ .event_id = DMA_REQ_SSI1_TX1,
-+ .peripheral_type = SSI,
-+ },
-+};
-+
-+static imx_pcm_dma_params_t imx_ssi1_pcm_stereo_in = {
-+ .name = "SSI1 PCM Stereo in",
-+ .params = {
-+ .bd_number = 1,
-+ .transfer_type = per_2_emi,
-+ .watermark_level = SDMA_RXFIFO_WATERMARK,
-+ .word_size = TRANSFER_16BIT, // maybe add this in setup func
-+ .per_address = SSI1_SRX0,
-+ .event_id = DMA_REQ_SSI1_RX1,
-+ .peripheral_type = SSI,
-+ },
-+};
-+
-+static imx_pcm_dma_params_t imx_ssi2_pcm_stereo_out = {
-+ .name = "SSI2 PCM Stereo out",
-+ .params = {
-+ .bd_number = 1,
-+ .transfer_type = per_2_emi,
-+ .watermark_level = SDMA_TXFIFO_WATERMARK,
-+ .word_size = TRANSFER_16BIT, // maybe add this in setup func
-+ .per_address = SSI2_STX0,
-+ .event_id = DMA_REQ_SSI2_TX1,
-+ .peripheral_type = SSI,
-+ },
-+};
-+
-+static imx_pcm_dma_params_t imx_ssi2_pcm_stereo_in = {
-+ .name = "SSI2 PCM Stereo in",
-+ .params = {
-+ .bd_number = 1,
-+ .transfer_type = per_2_emi,
-+ .watermark_level = SDMA_RXFIFO_WATERMARK,
-+ .word_size = TRANSFER_16BIT, // maybe add this in setup func
-+ .per_address = SSI2_SRX0,
-+ .event_id = DMA_REQ_SSI2_RX1,
-+ .peripheral_type = SSI,
-+ },
-+};
-+
-+static unsigned short imx_ssi_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
-+{
-+}
-+
-+static void imx_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
-+{
-+}
-+
-+static void imx_ssi_ac97_warm_reset(struct snd_ac97 *ac97)
-+{
-+}
-+
-+static void imx_ssi_ac97_cold_reset(struct snd_ac97 *ac97)
-+{
-+}
-+
-+struct snd_ac97_bus_ops soc_ac97_ops = {
-+ .read = imx_ssi_ac97_read,
-+ .write = imx_ssi_ac97_write,
-+ .warm_reset = imx_ssi_ac97_warm_reset,
-+ .reset = imx_ssi_ac97_cold_reset,
-+};
-+
-+
-+static intimx_ssi1_ac97_probe(struct platform_device *pdev)
-+{
-+ int ret;
-+
-+
-+ return ret;
-+}
-+
-+static void imx_ssi1_ac97_remove(struct platform_device *pdev)
-+{
-+ /* shutdown SSI */
-+ if(rtd->cpu_dai->id == 0)
-+ SSI1_SCR &= ~SSI_SCR_SSIEN;
-+ else
-+ SSI2_SCR &= ~SSI_SCR_SSIEN;
-+ }
-+
-+}
-+
-+static int imx_ssi1_ac97_prepare(struct snd_pcm_substream *substream)
-+{
-+ // set vra
-+}
-+
-+static int imx_ssi_startup(struct snd_pcm_substream *substream)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+
-+ if (!rtd->cpu_dai->active) {
-+
-+ }
-+
-+ return 0;
-+}
-+
-+static int imx_ssi1_trigger(struct snd_pcm_substream *substream, int cmd)
-+{
-+
-+ return ret;
-+}
-+
-+static void imx_ssi_shutdown(struct snd_pcm_substream *substream)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+
-+
-+}
-+
-+#ifdef CONFIG_PM
-+static int imx_ssi_suspend(struct platform_device *dev,
-+ struct snd_soc_cpu_dai *dai)
-+{
-+ if(!dai->active)
-+ return 0;
-+
-+
-+ return 0;
-+}
-+
-+static int imx_ssi_resume(struct platform_device *pdev,
-+ struct snd_soc_cpu_dai *dai)
-+{
-+ if(!dai->active)
-+ return 0;
-+
-+ return 0;
-+}
-+
-+#else
-+#define imx_ssi_suspend NULL
-+#define imx_ssi_resume NULL
-+#endif
-+
-+#define IMX_AC97_RATES \
-+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
-+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
-+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
-+ SNDRV_PCM_RATE_48000)
-+
-+struct snd_soc_cpu_dai imx_ssi_ac97_dai = {
-+ .name = "imx-ac97-1",
-+ .id = 0,
-+ .type = SND_SOC_DAI_AC97,
-+ .suspend = imx_ssi_suspend,
-+ .resume = imx_ssi_resume,
-+ .playback = {
-+ .channels_min = 2,
-+ .channels_max = 2,
-+ .rates = IMX_AC97_RATES,},
-+ .capture = {
-+ .channels_min = 2,
-+ .channels_max = 2,
-+ .rates = IMX_AC97_RATES,},
-+ .ops = {
-+ .probe = imx_ac97_probe,
-+ .remove = imx_ac97_shutdown,
-+ .trigger = imx_ssi_trigger,
-+ .prepare = imx_ssi_ac97_prepare,},
-+},
-+{
-+ .name = "imx-ac97-2",
-+ .id = 1,
-+ .type = SND_SOC_DAI_AC97,
-+ .suspend = imx_ssi_suspend,
-+ .resume = imx_ssi_resume,
-+ .playback = {
-+ .channels_min = 2,
-+ .channels_max = 2,
-+ .rates = IMX_AC97_RATES,},
-+ .capture = {
-+ .channels_min = 2,
-+ .channels_max = 2,
-+ .rates = IMX_AC97_RATES,},
-+ .ops = {
-+ .probe = imx_ac97_probe,
-+ .remove = imx_ac97_shutdown,
-+ .trigger = imx_ssi_trigger,
-+ .prepare = imx_ssi_ac97_prepare,},
-+};
-+
-+EXPORT_SYMBOL_GPL(imx_ssi_ac97_dai);
-+
-+/* Module information */
-+MODULE_AUTHOR("Liam Girdwood, liam.girdwood at wolfsonmicro.com, www.wolfsonmicro.com");
-+MODULE_DESCRIPTION("i.MX ASoC AC97 driver");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/codecs/wm8976.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/codecs/wm8976.c
-@@ -0,0 +1,885 @@
-+/*
-+ * wm8976.c -- WM8976 ALSA Soc Audio driver
-+ *
-+ * Copyright 2006 Wolfson Microelectronics PLC.
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License version 2 as
-+ * published by the Free Software Foundation.
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/moduleparam.h>
-+#include <linux/version.h>
-+#include <linux/kernel.h>
-+#include <linux/init.h>
-+#include <linux/delay.h>
-+#include <linux/pm.h>
-+#include <linux/i2c.h>
-+#include <linux/platform_device.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/pcm_params.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+#include <sound/initval.h>
-+
-+#include "wm8976.h"
-+
-+#define AUDIO_NAME "wm8976"
-+#define WM8976_VERSION "0.4"
-+
-+/*
-+ * Debug
-+ */
-+
-+#define WM8976_DEBUG 0
-+
-+#ifdef WM8976_DEBUG
-+#define dbg(format, arg...) \
-+ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
-+#else
-+#define dbg(format, arg...) do {} while (0)
-+#endif
-+#define err(format, arg...) \
-+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
-+#define info(format, arg...) \
-+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
-+#define warn(format, arg...) \
-+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
-+
-+struct snd_soc_codec_device soc_codec_dev_wm8976;
-+
-+/*
-+ * wm8976 register cache
-+ * We can't read the WM8976 register space when we are
-+ * using 2 wire for device control, so we cache them instead.
-+ */
-+static const u16 wm8976_reg[WM8976_CACHEREGNUM] = {
-+ 0x0000, 0x0000, 0x0000, 0x0000,
-+ 0x0050, 0x0000, 0x0140, 0x0000,
-+ 0x0000, 0x0000, 0x0000, 0x00ff,
-+ 0x00ff, 0x0000, 0x0100, 0x00ff,
-+ 0x00ff, 0x0000, 0x012c, 0x002c,
-+ 0x002c, 0x002c, 0x002c, 0x0000,
-+ 0x0032, 0x0000, 0x0000, 0x0000,
-+ 0x0000, 0x0000, 0x0000, 0x0000,
-+ 0x0038, 0x000b, 0x0032, 0x0000,
-+ 0x0008, 0x000c, 0x0093, 0x00e9,
-+ 0x0000, 0x0000, 0x0000, 0x0000,
-+ 0x0033, 0x0010, 0x0010, 0x0100,
-+ 0x0100, 0x0002, 0x0001, 0x0001,
-+ 0x0039, 0x0039, 0x0039, 0x0039,
-+ 0x0001, 0x0001,
-+};
-+
-+/*
-+ * read wm8976 register cache
-+ */
-+static inline unsigned int wm8976_read_reg_cache(struct snd_soc_codec *codec,
-+ unsigned int reg)
-+{
-+ u16 *cache = codec->reg_cache;
-+ if (reg == WM8976_RESET)
-+ return 0;
-+ if (reg >= WM8976_CACHEREGNUM)
-+ return -1;
-+ return cache[reg];
-+}
-+
-+/*
-+ * write wm8976 register cache
-+ */
-+static inline void wm8976_write_reg_cache(struct snd_soc_codec *codec,
-+ u16 reg, unsigned int value)
-+{
-+ u16 *cache = codec->reg_cache;
-+ if (reg >= WM8976_CACHEREGNUM)
-+ return;
-+ cache[reg] = value;
-+}
-+
-+/*
-+ * write to the WM8976 register space
-+ */
-+static int wm8976_write(struct snd_soc_codec *codec, unsigned int reg,
-+ unsigned int value)
-+{
-+ u8 data[2];
-+
-+ /* data is
-+ * D15..D9 WM8976 register offset
-+ * D8...D0 register data
-+ */
-+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
-+ data[1] = value & 0x00ff;
-+
-+ wm8976_write_reg_cache (codec, reg, value);
-+ if (codec->hw_write(codec->control_data, data, 2) == 2)
-+ return 0;
-+ else
-+ return -1;
-+}
-+
-+#define wm8976_reset(c) wm8976_write(c, WM8976_RESET, 0)
-+
-+static const char *wm8976_companding[] = {"Off", "NC", "u-law", "A-law" };
-+static const char *wm8976_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
-+static const char *wm8976_eqmode[] = {"Capture", "Playback" };
-+static const char *wm8976_bw[] = {"Narrow", "Wide" };
-+static const char *wm8976_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" };
-+static const char *wm8976_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" };
-+static const char *wm8976_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" };
-+static const char *wm8976_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" };
-+static const char *wm8976_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" };
-+static const char *wm8976_alc[] =
-+ {"ALC both on", "ALC left only", "ALC right only", "Limiter" };
-+
-+static const struct soc_enum wm8976_enum[] = {
-+ SOC_ENUM_SINGLE(WM8976_COMP, 1, 4, wm8976_companding), /* adc */
-+ SOC_ENUM_SINGLE(WM8976_COMP, 3, 4, wm8976_companding), /* dac */
-+ SOC_ENUM_SINGLE(WM8976_DAC, 4, 4, wm8976_deemp),
-+ SOC_ENUM_SINGLE(WM8976_EQ1, 8, 2, wm8976_eqmode),
-+
-+ SOC_ENUM_SINGLE(WM8976_EQ1, 5, 4, wm8976_eq1),
-+ SOC_ENUM_SINGLE(WM8976_EQ2, 8, 2, wm8976_bw),
-+ SOC_ENUM_SINGLE(WM8976_EQ2, 5, 4, wm8976_eq2),
-+ SOC_ENUM_SINGLE(WM8976_EQ3, 8, 2, wm8976_bw),
-+
-+ SOC_ENUM_SINGLE(WM8976_EQ3, 5, 4, wm8976_eq3),
-+ SOC_ENUM_SINGLE(WM8976_EQ4, 8, 2, wm8976_bw),
-+ SOC_ENUM_SINGLE(WM8976_EQ4, 5, 4, wm8976_eq4),
-+ SOC_ENUM_SINGLE(WM8976_EQ5, 8, 2, wm8976_bw),
-+
-+ SOC_ENUM_SINGLE(WM8976_EQ5, 5, 4, wm8976_eq5),
-+ SOC_ENUM_SINGLE(WM8976_ALC3, 8, 2, wm8976_alc),
-+};
-+
-+static const struct snd_kcontrol_new wm8976_snd_controls[] = {
-+SOC_SINGLE("Digital Loopback Switch", WM8976_COMP, 0, 1, 0),
-+
-+SOC_ENUM("ADC Companding", wm8976_enum[0]),
-+SOC_ENUM("DAC Companding", wm8976_enum[1]),
-+
-+SOC_SINGLE("Jack Detection Enable", WM8976_JACK1, 6, 1, 0),
-+
-+SOC_DOUBLE("DAC Inversion Switch", WM8976_DAC, 0, 1, 1, 0),
-+
-+SOC_DOUBLE_R("Headphone Playback Volume", WM8976_DACVOLL, WM8976_DACVOLR, 0, 127, 0),
-+
-+SOC_SINGLE("High Pass Filter Switch", WM8976_ADC, 8, 1, 0),
-+SOC_SINGLE("High Pass Filter Switch", WM8976_ADC, 8, 1, 0),
-+SOC_SINGLE("High Pass Cut Off", WM8976_ADC, 4, 7, 0),
-+
-+SOC_DOUBLE("ADC Inversion Switch", WM8976_ADC, 0, 1, 1, 0),
-+
-+SOC_SINGLE("Capture Volume", WM8976_ADCVOL, 0, 127, 0),
-+
-+SOC_ENUM("Equaliser Function", wm8976_enum[3]),
-+SOC_ENUM("EQ1 Cut Off", wm8976_enum[4]),
-+SOC_SINGLE("EQ1 Volume", WM8976_EQ1, 0, 31, 1),
-+
-+SOC_ENUM("Equaliser EQ2 Bandwith", wm8976_enum[5]),
-+SOC_ENUM("EQ2 Cut Off", wm8976_enum[6]),
-+SOC_SINGLE("EQ2 Volume", WM8976_EQ2, 0, 31, 1),
-+
-+SOC_ENUM("Equaliser EQ3 Bandwith", wm8976_enum[7]),
-+SOC_ENUM("EQ3 Cut Off", wm8976_enum[8]),
-+SOC_SINGLE("EQ3 Volume", WM8976_EQ3, 0, 31, 1),
-+
-+SOC_ENUM("Equaliser EQ4 Bandwith", wm8976_enum[9]),
-+SOC_ENUM("EQ4 Cut Off", wm8976_enum[10]),
-+SOC_SINGLE("EQ4 Volume", WM8976_EQ4, 0, 31, 1),
-+
-+SOC_ENUM("Equaliser EQ5 Bandwith", wm8976_enum[11]),
-+SOC_ENUM("EQ5 Cut Off", wm8976_enum[12]),
-+SOC_SINGLE("EQ5 Volume", WM8976_EQ5, 0, 31, 1),
-+
-+SOC_SINGLE("DAC Playback Limiter Switch", WM8976_DACLIM1, 8, 1, 0),
-+SOC_SINGLE("DAC Playback Limiter Decay", WM8976_DACLIM1, 4, 15, 0),
-+SOC_SINGLE("DAC Playback Limiter Attack", WM8976_DACLIM1, 0, 15, 0),
-+
-+SOC_SINGLE("DAC Playback Limiter Threshold", WM8976_DACLIM2, 4, 7, 0),
-+SOC_SINGLE("DAC Playback Limiter Boost", WM8976_DACLIM2, 0, 15, 0),
-+
-+SOC_SINGLE("ALC Enable Switch", WM8976_ALC1, 8, 1, 0),
-+SOC_SINGLE("ALC Capture Max Gain", WM8976_ALC1, 3, 7, 0),
-+SOC_SINGLE("ALC Capture Min Gain", WM8976_ALC1, 0, 7, 0),
-+
-+SOC_SINGLE("ALC Capture ZC Switch", WM8976_ALC2, 8, 1, 0),
-+SOC_SINGLE("ALC Capture Hold", WM8976_ALC2, 4, 7, 0),
-+SOC_SINGLE("ALC Capture Target", WM8976_ALC2, 0, 15, 0),
-+
-+SOC_ENUM("ALC Capture Mode", wm8976_enum[13]),
-+SOC_SINGLE("ALC Capture Decay", WM8976_ALC3, 4, 15, 0),
-+SOC_SINGLE("ALC Capture Attack", WM8976_ALC3, 0, 15, 0),
-+
-+SOC_SINGLE("ALC Capture Noise Gate Switch", WM8976_NGATE, 3, 1, 0),
-+SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8976_NGATE, 0, 7, 0),
-+
-+SOC_SINGLE("Capture PGA ZC Switch", WM8976_INPPGA, 7, 1, 0),
-+SOC_SINGLE("Capture PGA Volume", WM8976_INPPGA, 0, 63, 0),
-+
-+SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8976_HPVOLL, WM8976_HPVOLR, 7, 1, 0),
-+SOC_DOUBLE_R("Headphone Playback Switch", WM8976_HPVOLL, WM8976_HPVOLR, 6, 1, 1),
-+SOC_DOUBLE_R("Headphone Playback Volume", WM8976_HPVOLL, WM8976_HPVOLR, 0, 63, 0),
-+
-+SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8976_SPKVOLL, WM8976_SPKVOLR, 7, 1, 0),
-+SOC_DOUBLE_R("Speaker Playback Switch", WM8976_SPKVOLL, WM8976_SPKVOLR, 6, 1, 1),
-+SOC_DOUBLE_R("Speaker Playback Volume", WM8976_SPKVOLL, WM8976_SPKVOLR, 0, 63, 0),
-+
-+SOC_SINGLE("Capture Boost(+20dB)", WM8976_ADCBOOST, 8, 1, 0),
-+};
-+
-+/* add non dapm controls */
-+static int wm8976_add_controls(struct snd_soc_codec *codec)
-+{
-+ int err, i;
-+
-+ for (i = 0; i < ARRAY_SIZE(wm8976_snd_controls); i++) {
-+ err = snd_ctl_add(codec->card,
-+ snd_soc_cnew(&wm8976_snd_controls[i],codec, NULL));
-+ if (err < 0)
-+ return err;
-+ }
-+
-+ return 0;
-+}
-+
-+/* Left Output Mixer */
-+static const snd_kcontrol_new_t wm8976_left_mixer_controls[] = {
-+SOC_DAPM_SINGLE("Right PCM Playback Switch", WM8976_OUTPUT, 6, 1, 1),
-+SOC_DAPM_SINGLE("Left PCM Playback Switch", WM8976_MIXL, 0, 1, 1),
-+SOC_DAPM_SINGLE("Line Bypass Switch", WM8976_MIXL, 1, 1, 0),
-+SOC_DAPM_SINGLE("Aux Playback Switch", WM8976_MIXL, 5, 1, 0),
-+};
-+
-+/* Right Output Mixer */
-+static const snd_kcontrol_new_t wm8976_right_mixer_controls[] = {
-+SOC_DAPM_SINGLE("Left PCM Playback Switch", WM8976_OUTPUT, 5, 1, 1),
-+SOC_DAPM_SINGLE("Right PCM Playback Switch", WM8976_MIXR, 0, 1, 1),
-+SOC_DAPM_SINGLE("Line Bypass Switch", WM8976_MIXR, 1, 1, 0),
-+SOC_DAPM_SINGLE("Aux Playback Switch", WM8976_MIXR, 5, 1, 0),
-+};
-+
-+/* Left AUX Input boost vol */
-+static const snd_kcontrol_new_t wm8976_laux_boost_controls =
-+SOC_DAPM_SINGLE("Aux Volume", WM8976_ADCBOOST, 0, 3, 0);
-+
-+/* Left Input boost vol */
-+static const snd_kcontrol_new_t wm8976_lmic_boost_controls =
-+SOC_DAPM_SINGLE("Input Volume", WM8976_ADCBOOST, 4, 3, 0);
-+
-+/* Left Aux In to PGA */
-+static const snd_kcontrol_new_t wm8976_laux_capture_boost_controls =
-+SOC_DAPM_SINGLE("Capture Switch", WM8976_ADCBOOST, 8, 1, 0);
-+
-+/* Left Input P In to PGA */
-+static const snd_kcontrol_new_t wm8976_lmicp_capture_boost_controls =
-+SOC_DAPM_SINGLE("Input P Capture Boost Switch", WM8976_INPUT, 0, 1, 0);
-+
-+/* Left Input N In to PGA */
-+static const snd_kcontrol_new_t wm8976_lmicn_capture_boost_controls =
-+SOC_DAPM_SINGLE("Input N Capture Boost Switch", WM8976_INPUT, 1, 1, 0);
-+
-+// TODO Widgets
-+static const struct snd_soc_dapm_widget wm8976_dapm_widgets[] = {
-+#if 0
-+//SND_SOC_DAPM_MUTE("Mono Mute", WM8976_MONOMIX, 6, 0),
-+//SND_SOC_DAPM_MUTE("Speaker Mute", WM8976_SPKMIX, 6, 0),
-+
-+SND_SOC_DAPM_MIXER("Speaker Mixer", WM8976_POWER3, 2, 0,
-+ &wm8976_speaker_mixer_controls[0],
-+ ARRAY_SIZE(wm8976_speaker_mixer_controls)),
-+SND_SOC_DAPM_MIXER("Mono Mixer", WM8976_POWER3, 3, 0,
-+ &wm8976_mono_mixer_controls[0],
-+ ARRAY_SIZE(wm8976_mono_mixer_controls)),
-+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8976_POWER3, 0, 0),
-+SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8976_POWER3, 0, 0),
-+SND_SOC_DAPM_PGA("Aux Input", WM8976_POWER1, 6, 0, NULL, 0),
-+SND_SOC_DAPM_PGA("SpkN Out", WM8976_POWER3, 5, 0, NULL, 0),
-+SND_SOC_DAPM_PGA("SpkP Out", WM8976_POWER3, 6, 0, NULL, 0),
-+SND_SOC_DAPM_PGA("Mono Out", WM8976_POWER3, 7, 0, NULL, 0),
-+SND_SOC_DAPM_PGA("Mic PGA", WM8976_POWER2, 2, 0, NULL, 0),
-+
-+SND_SOC_DAPM_PGA("Aux Boost", SND_SOC_NOPM, 0, 0,
-+ &wm8976_aux_boost_controls, 1),
-+SND_SOC_DAPM_PGA("Mic Boost", SND_SOC_NOPM, 0, 0,
-+ &wm8976_mic_boost_controls, 1),
-+SND_SOC_DAPM_SWITCH("Capture Boost", SND_SOC_NOPM, 0, 0,
-+ &wm8976_capture_boost_controls),
-+
-+SND_SOC_DAPM_MIXER("Boost Mixer", WM8976_POWER2, 4, 0, NULL, 0),
-+
-+SND_SOC_DAPM_MICBIAS("Mic Bias", WM8976_POWER1, 4, 0),
-+
-+SND_SOC_DAPM_INPUT("MICN"),
-+SND_SOC_DAPM_INPUT("MICP"),
-+SND_SOC_DAPM_INPUT("AUX"),
-+SND_SOC_DAPM_OUTPUT("MONOOUT"),
-+SND_SOC_DAPM_OUTPUT("SPKOUTP"),
-+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
-+#endif
-+};
-+
-+static const char *audio_map[][3] = {
-+ /* Mono output mixer */
-+ {"Mono Mixer", "PCM Playback Switch", "DAC"},
-+ {"Mono Mixer", "Aux Playback Switch", "Aux Input"},
-+ {"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
-+
-+ /* Speaker output mixer */
-+ {"Speaker Mixer", "PCM Playback Switch", "DAC"},
-+ {"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
-+ {"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
-+
-+ /* Outputs */
-+ {"Mono Out", NULL, "Mono Mixer"},
-+ {"MONOOUT", NULL, "Mono Out"},
-+ {"SpkN Out", NULL, "Speaker Mixer"},
-+ {"SpkP Out", NULL, "Speaker Mixer"},
-+ {"SPKOUTN", NULL, "SpkN Out"},
-+ {"SPKOUTP", NULL, "SpkP Out"},
-+
-+ /* Boost Mixer */
-+ {"Boost Mixer", NULL, "ADC"},
-+ {"Capture Boost Switch", "Aux Capture Boost Switch", "AUX"},
-+ {"Aux Boost", "Aux Volume", "Boost Mixer"},
-+ {"Capture Boost", "Capture Switch", "Boost Mixer"},
-+ {"Mic Boost", "Mic Volume", "Boost Mixer"},
-+
-+ /* Inputs */
-+ {"MICP", NULL, "Mic Boost"},
-+ {"MICN", NULL, "Mic PGA"},
-+ {"Mic PGA", NULL, "Capture Boost"},
-+ {"AUX", NULL, "Aux Input"},
-+
-+ /* */
-+
-+ /* terminator */
-+ {NULL, NULL, NULL},
-+};
-+
-+static int wm8976_add_widgets(struct snd_soc_codec *codec)
-+{
-+ int i;
-+
-+ for(i = 0; i < ARRAY_SIZE(wm8976_dapm_widgets); i++) {
-+ snd_soc_dapm_new_control(codec, &wm8976_dapm_widgets[i]);
-+ }
-+
-+ /* set up audio path map */
-+ for(i = 0; audio_map[i][0] != NULL; i++) {
-+ snd_soc_dapm_connect_input(codec, audio_map[i][0], audio_map[i][1],
-+ audio_map[i][2]);
-+ }
-+
-+ snd_soc_dapm_new_widgets(codec);
-+ return 0;
-+}
-+
-+struct pll_ {
-+ unsigned int in_hz, out_hz;
-+ unsigned int pre:4; /* prescale - 1 */
-+ unsigned int n:4;
-+ unsigned int k;
-+};
-+
-+struct pll_ pll[] = {
-+ {12000000, 11289600, 0, 7, 0x86c220},
-+ {12000000, 12288000, 0, 8, 0x3126e8},
-+ {13000000, 11289600, 0, 6, 0xf28bd4},
-+ {13000000, 12288000, 0, 7, 0x8fd525},
-+ {12288000, 11289600, 0, 7, 0x59999a},
-+ {11289600, 12288000, 0, 8, 0x80dee9},
-+ /* TODO: liam - add more entries */
-+};
-+
-+static int wm8976_set_dai_pll(struct snd_soc_codec_dai *codec_dai,
-+ int pll_id, unsigned int freq_in, unsigned int freq_out)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ int i;
-+ u16 reg;
-+
-+ if(freq_in == 0 || freq_out == 0) {
-+ reg = wm8976_read_reg_cache(codec, WM8976_POWER1);
-+ wm8976_write(codec, WM8976_POWER1, reg & 0x1df);
-+ return 0;
-+ }
-+
-+ for(i = 0; i < ARRAY_SIZE(pll); i++) {
-+ if (freq_in == pll[i].in_hz && freq_out == pll[i].out_hz) {
-+ wm8976_write(codec, WM8976_PLLN, (pll[i].pre << 4) | pll[i].n);
-+ wm8976_write(codec, WM8976_PLLK1, pll[i].k >> 18);
-+ wm8976_write(codec, WM8976_PLLK1, (pll[i].k >> 9) && 0x1ff);
-+ wm8976_write(codec, WM8976_PLLK1, pll[i].k && 0x1ff);
-+ reg = wm8976_read_reg_cache(codec, WM8976_POWER1);
-+ wm8976_write(codec, WM8976_POWER1, reg | 0x020);
-+ return 0;
-+ }
-+ }
-+ return -EINVAL;
-+}
-+
-+static int wm8976_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
-+ unsigned int fmt)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 iface = wm8976_read_reg_cache(codec, WM8976_IFACE) & 0x3;
-+ u16 clk = wm8976_read_reg_cache(codec, WM8976_CLOCK) & 0xfffe;
-+
-+ /* set master/slave audio interface */
-+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
-+ case SND_SOC_DAIFMT_CBM_CFM:
-+ clk |= 0x0001;
-+ break;
-+ case SND_SOC_DAIFMT_CBS_CFS:
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ /* interface format */
-+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
-+ case SND_SOC_DAIFMT_I2S:
-+ iface |= 0x0010;
-+ break;
-+ case SND_SOC_DAIFMT_RIGHT_J:
-+ break;
-+ case SND_SOC_DAIFMT_LEFT_J:
-+ iface |= 0x0008;
-+ break;
-+ case SND_SOC_DAIFMT_DSP_A:
-+ iface |= 0x00018;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ /* clock inversion */
-+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
-+ case SND_SOC_DAIFMT_NB_NF:
-+ break;
-+ case SND_SOC_DAIFMT_IB_IF:
-+ iface |= 0x0180;
-+ break;
-+ case SND_SOC_DAIFMT_IB_NF:
-+ iface |= 0x0100;
-+ break;
-+ case SND_SOC_DAIFMT_NB_IF:
-+ iface |= 0x0080;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ wm8976_write(codec, WM8976_IFACE, iface);
-+ return 0;
-+}
-+
-+static int wm8976_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_device *socdev = rtd->socdev;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ u16 iface = wm8976_read_reg_cache(codec, WM8976_IFACE) & 0xff9f;
-+ u16 adn = wm8976_read_reg_cache(codec, WM8976_ADD) & 0x1f1;
-+
-+ /* bit size */
-+ switch (params_format(params)) {
-+ case SNDRV_PCM_FORMAT_S16_LE:
-+ break;
-+ case SNDRV_PCM_FORMAT_S20_3LE:
-+ iface |= 0x0020;
-+ break;
-+ case SNDRV_PCM_FORMAT_S24_LE:
-+ iface |= 0x0040;
-+ break;
-+ }
-+
-+ /* filter coefficient */
-+ switch (params_rate(params)) {
-+ case SNDRV_PCM_RATE_8000:
-+ adn |= 0x5 << 1;
-+ break;
-+ case SNDRV_PCM_RATE_11025:
-+ adn |= 0x4 << 1;
-+ break;
-+ case SNDRV_PCM_RATE_16000:
-+ adn |= 0x3 << 1;
-+ break;
-+ case SNDRV_PCM_RATE_22050:
-+ adn |= 0x2 << 1;
-+ break;
-+ case SNDRV_PCM_RATE_32000:
-+ adn |= 0x1 << 1;
-+ break;
-+ }
-+
-+ /* set iface */
-+ wm8976_write(codec, WM8976_IFACE, iface);
-+ wm8976_write(codec, WM8976_ADD, adn);
-+ return 0;
-+}
-+
-+static int wm8976_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
-+ int div_id, int div)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 reg;
-+
-+ switch (div_id) {
-+ case WM8976_MCLKDIV:
-+ reg = wm8976_read_reg_cache(codec, WM8976_CLOCK) & 0x11f;
-+ wm8976_write(codec, WM8976_CLOCK, reg | div);
-+ break;
-+ case WM8976_BCLKDIV:
-+ reg = wm8976_read_reg_cache(codec, WM8976_CLOCK) & 0x1c7;
-+ wm8976_write(codec, WM8976_CLOCK, reg | div);
-+ break;
-+ case WM8976_OPCLKDIV:
-+ reg = wm8976_read_reg_cache(codec, WM8976_GPIO) & 0x1cf;
-+ wm8976_write(codec, WM8976_GPIO, reg | div);
-+ break;
-+ case WM8976_DACOSR:
-+ reg = wm8976_read_reg_cache(codec, WM8976_DAC) & 0x1f7;
-+ wm8976_write(codec, WM8976_DAC, reg | div);
-+ break;
-+ case WM8976_ADCOSR:
-+ reg = wm8976_read_reg_cache(codec, WM8976_ADC) & 0x1f7;
-+ wm8976_write(codec, WM8976_ADC, reg | div);
-+ break;
-+ case WM8976_MCLKSEL:
-+ reg = wm8976_read_reg_cache(codec, WM8976_CLOCK) & 0x0ff;
-+ wm8976_write(codec, WM8976_CLOCK, reg | div);
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ return 0;
-+}
-+
-+static int wm8976_mute(struct snd_soc_codec_dai *dai, int mute)
-+{
-+ struct snd_soc_codec *codec = dai->codec;
-+ u16 mute_reg = wm8976_read_reg_cache(codec, WM8976_DAC) & 0xffbf;
-+
-+ if(mute)
-+ wm8976_write(codec, WM8976_DAC, mute_reg | 0x40);
-+ else
-+ wm8976_write(codec, WM8976_DAC, mute_reg);
-+
-+ return 0;
-+}
-+
-+/* TODO: liam need to make this lower power with dapm */
-+static int wm8976_dapm_event(struct snd_soc_codec *codec, int event)
-+{
-+
-+ switch (event) {
-+ case SNDRV_CTL_POWER_D0: /* full On */
-+ /* vref/mid, clk and osc on, dac unmute, active */
-+ wm8976_write(codec, WM8976_POWER1, 0x1ff);
-+ wm8976_write(codec, WM8976_POWER2, 0x1ff);
-+ wm8976_write(codec, WM8976_POWER3, 0x1ff);
-+ break;
-+ case SNDRV_CTL_POWER_D1: /* partial On */
-+ case SNDRV_CTL_POWER_D2: /* partial On */
-+ break;
-+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
-+ /* everything off except vref/vmid, dac mute, inactive */
-+
-+ break;
-+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
-+ /* everything off, dac mute, inactive */
-+ wm8976_write(codec, WM8976_POWER1, 0x0);
-+ wm8976_write(codec, WM8976_POWER2, 0x0);
-+ wm8976_write(codec, WM8976_POWER3, 0x0);
-+ break;
-+ }
-+ codec->dapm_state = event;
-+ return 0;
-+}
-+
-+#define WM8976_RATES \
-+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
-+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
-+ SNDRV_PCM_RATE_48000)
-+
-+#define WM8976_FORMATS \
-+ (SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \
-+ SNDRV_PCM_FORMAT_S24_3LE | SNDRV_PCM_FORMAT_S24_LE)
-+
-+struct snd_soc_codec_dai wm8976_dai = {
-+ .name = "WM8976 HiFi",
-+ .playback = {
-+ .stream_name = "Playback",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = WM8976_RATES,
-+ .formats = WM8976_FORMATS,},
-+ .capture = {
-+ .stream_name = "Capture",
-+ .channels_min = 1,
-+ .channels_max = 1,
-+ .rates = WM8976_RATES,
-+ .formats = WM8976_FORMATS,},
-+ .ops = {
-+ .hw_params = wm8976_hw_params,
-+ },
-+ .dai_ops = {
-+ .digital_mute = wm8976_mute,
-+ .set_fmt = wm8976_set_dai_fmt,
-+ .set_clkdiv = wm8976_set_dai_clkdiv,
-+ .set_pll = wm8976_set_dai_pll,
-+ },
-+};
-+EXPORT_SYMBOL_GPL(wm8976_dai);
-+
-+static int wm8976_suspend(struct platform_device *pdev, pm_message_t state)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+
-+ wm8976_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
-+ return 0;
-+}
-+
-+static int wm8976_resume(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+ int i;
-+ u8 data[2];
-+ u16 *cache = codec->reg_cache;
-+
-+ /* Sync reg_cache with the hardware */
-+ for (i = 0; i < ARRAY_SIZE(wm8976_reg); i++) {
-+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
-+ data[1] = cache[i] & 0x00ff;
-+ codec->hw_write(codec->control_data, data, 2);
-+ }
-+ wm8976_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
-+ wm8976_dapm_event(codec, codec->suspend_dapm_state);
-+ return 0;
-+}
-+
-+/*
-+ * initialise the WM8976 driver
-+ * register the mixer and dsp interfaces with the kernel
-+ */
-+static int wm8976_init(struct snd_soc_device* socdev)
-+{
-+ struct snd_soc_codec *codec = socdev->codec;
-+ int ret = 0;
-+
-+ codec->name = "WM8976";
-+ codec->owner = THIS_MODULE;
-+ codec->read = wm8976_read_reg_cache;
-+ codec->write = wm8976_write;
-+ codec->dapm_event = wm8976_dapm_event;
-+ codec->dai = &wm8976_dai;
-+ codec->num_dai = 1;
-+ codec->reg_cache_size = ARRAY_SIZE(wm8976_reg);
-+ codec->reg_cache =
-+ kzalloc(sizeof(u16) * ARRAY_SIZE(wm8976_reg), GFP_KERNEL);
-+ if (codec->reg_cache == NULL)
-+ return -ENOMEM;
-+ memcpy(codec->reg_cache, wm8976_reg,
-+ sizeof(u16) * ARRAY_SIZE(wm8976_reg));
-+ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8976_reg);
-+
-+ wm8976_reset(codec);
-+
-+ /* register pcms */
-+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
-+ if(ret < 0) {
-+ printk(KERN_ERR "wm8976: failed to create pcms\n");
-+ goto pcm_err;
-+ }
-+
-+ /* power on device */
-+ wm8976_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
-+ wm8976_add_controls(codec);
-+ wm8976_add_widgets(codec);
-+ ret = snd_soc_register_card(socdev);
-+ if (ret < 0) {
-+ printk(KERN_ERR "wm8976: failed to register card\n");
-+ goto card_err;
-+ }
-+ return ret;
-+
-+card_err:
-+ snd_soc_free_pcms(socdev);
-+ snd_soc_dapm_free(socdev);
-+pcm_err:
-+ kfree(codec->reg_cache);
-+ return ret;
-+}
-+
-+static struct snd_soc_device *wm8976_socdev;
-+
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+
-+/*
-+ * WM8976 2 wire address is 0x1a
-+ */
-+#define I2C_DRIVERID_WM8976 0xfefe /* liam - need a proper id */
-+
-+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
-+
-+/* Magic definition of all other variables and things */
-+I2C_CLIENT_INSMOD;
-+
-+static struct i2c_driver wm8976_i2c_driver;
-+static struct i2c_client client_template;
-+
-+/* If the i2c layer weren't so broken, we could pass this kind of data
-+ around */
-+
-+static int wm8976_codec_probe(struct i2c_adapter *adap, int addr, int kind)
-+{
-+ struct snd_soc_device *socdev = wm8976_socdev;
-+ struct wm8976_setup_data *setup = socdev->codec_data;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ struct i2c_client *i2c;
-+ int ret;
-+
-+ if (addr != setup->i2c_address)
-+ return -ENODEV;
-+
-+ client_template.adapter = adap;
-+ client_template.addr = addr;
-+
-+ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
-+ if (i2c == NULL){
-+ kfree(codec);
-+ return -ENOMEM;
-+ }
-+ memcpy(i2c, &client_template, sizeof(struct i2c_client));
-+
-+ i2c_set_clientdata(i2c, codec);
-+
-+ codec->control_data = i2c;
-+
-+ ret = i2c_attach_client(i2c);
-+ if(ret < 0) {
-+ err("failed to attach codec at addr %x\n", addr);
-+ goto err;
-+ }
-+
-+ ret = wm8976_init(socdev);
-+ if(ret < 0) {
-+ err("failed to initialise WM8976\n");
-+ goto err;
-+ }
-+ return ret;
-+
-+err:
-+ kfree(codec);
-+ kfree(i2c);
-+ return ret;
-+
-+}
-+
-+static int wm8976_i2c_detach(struct i2c_client *client)
-+{
-+ struct snd_soc_codec *codec = i2c_get_clientdata(client);
-+ i2c_detach_client(client);
-+ kfree(codec->reg_cache);
-+ kfree(client);
-+ return 0;
-+}
-+
-+static int wm8976_i2c_attach(struct i2c_adapter *adap)
-+{
-+ return i2c_probe(adap, &addr_data, wm8976_codec_probe);
-+}
-+
-+/* corgi i2c codec control layer */
-+static struct i2c_driver wm8976_i2c_driver = {
-+ .driver = {
-+ .name = "WM8976 I2C Codec",
-+ .owner = THIS_MODULE,
-+ },
-+ .id = I2C_DRIVERID_WM8976,
-+ .attach_adapter = wm8976_i2c_attach,
-+ .detach_client = wm8976_i2c_detach,
-+ .command = NULL,
-+};
-+
-+static struct i2c_client client_template = {
-+ .name = "WM8976",
-+ .driver = &wm8976_i2c_driver,
-+};
-+#endif
-+
-+static int wm8976_probe(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct wm8976_setup_data *setup;
-+ struct snd_soc_codec *codec;
-+ int ret = 0;
-+
-+ info("WM8976 Audio Codec %s", WM8976_VERSION);
-+
-+ setup = socdev->codec_data;
-+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
-+ if (codec == NULL)
-+ return -ENOMEM;
-+
-+ socdev->codec = codec;
-+ mutex_init(&codec->mutex);
-+ INIT_LIST_HEAD(&codec->dapm_widgets);
-+ INIT_LIST_HEAD(&codec->dapm_paths);
-+
-+ wm8976_socdev = socdev;
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+ if (setup->i2c_address) {
-+ normal_i2c[0] = setup->i2c_address;
-+ codec->hw_write = (hw_write_t)i2c_master_send;
-+ ret = i2c_add_driver(&wm8976_i2c_driver);
-+ if (ret != 0)
-+ printk(KERN_ERR "can't add i2c driver");
-+ }
-+#else
-+ /* Add other interfaces here */
-+#endif
-+ return ret;
-+}
-+
-+/* power down chip */
-+static int wm8976_remove(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+
-+ if (codec->control_data)
-+ wm8976_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
-+
-+ snd_soc_free_pcms(socdev);
-+ snd_soc_dapm_free(socdev);
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+ i2c_del_driver(&wm8976_i2c_driver);
-+#endif
-+ kfree(codec);
-+
-+ return 0;
-+}
-+
-+struct snd_soc_codec_device soc_codec_dev_wm8976 = {
-+ .probe = wm8976_probe,
-+ .remove = wm8976_remove,
-+ .suspend = wm8976_suspend,
-+ .resume = wm8976_resume,
-+};
-+
-+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8976);
-+
-+MODULE_DESCRIPTION("ASoC WM8976 driver");
-+MODULE_AUTHOR("Graeme Gregory");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/codecs/wm8976.h
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/codecs/wm8976.h
-@@ -0,0 +1,112 @@
-+/*
-+ * wm8976.h -- WM8976 Soc Audio driver
-+ *
-+ * 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 _WM8976_H
-+#define _WM8976_H
-+
-+/* WM8976 register space */
-+
-+#define WM8976_RESET 0x0
-+#define WM8976_POWER1 0x1
-+#define WM8976_POWER2 0x2
-+#define WM8976_POWER3 0x3
-+#define WM8976_IFACE 0x4
-+#define WM8976_COMP 0x5
-+#define WM8976_CLOCK 0x6
-+#define WM8976_ADD 0x7
-+#define WM8976_GPIO 0x8
-+#define WM8976_JACK1 0x9
-+#define WM8976_DAC 0xa
-+#define WM8976_DACVOLL 0xb
-+#define WM8976_DACVOLR 0xc
-+#define WM8976_JACK2 0xd
-+#define WM8976_ADC 0xe
-+#define WM8976_ADCVOL 0xf
-+#define WM8976_EQ1 0x12
-+#define WM8976_EQ2 0x13
-+#define WM8976_EQ3 0x14
-+#define WM8976_EQ4 0x15
-+#define WM8976_EQ5 0x16
-+#define WM8976_DACLIM1 0x18
-+#define WM8976_DACLIM2 0x19
-+#define WM8976_NOTCH1 0x1b
-+#define WM8976_NOTCH2 0x1c
-+#define WM8976_NOTCH3 0x1d
-+#define WM8976_NOTCH4 0x1e
-+#define WM8976_ALC1 0x20
-+#define WM8976_ALC2 0x21
-+#define WM8976_ALC3 0x22
-+#define WM8976_NGATE 0x23
-+#define WM8976_PLLN 0x24
-+#define WM8976_PLLK1 0x25
-+#define WM8976_PLLK2 0x26
-+#define WM8976_PLLK3 0x27
-+#define WM8976_3D 0x29
-+#define WM8976_BEEP 0x2b
-+#define WM8976_INPUT 0x2c
-+#define WM8976_INPPGA 0x2d
-+#define WM8976_ADCBOOST 0x2f
-+#define WM8976_OUTPUT 0x31
-+#define WM8976_MIXL 0x32
-+#define WM8976_MIXR 0x33
-+#define WM8976_HPVOLL 0x34
-+#define WM8976_HPVOLR 0x35
-+#define WM8976_SPKVOLL 0x36
-+#define WM8976_SPKVOLR 0x37
-+#define WM8976_OUT3MIX 0x38
-+#define WM8976_MONOMIX 0x39
-+
-+#define WM8976_CACHEREGNUM 58
-+
-+/*
-+ * WM8976 Clock dividers
-+ */
-+#define WM8976_MCLKDIV 0
-+#define WM8976_BCLKDIV 1
-+#define WM8976_OPCLKDIV 2
-+#define WM8976_DACOSR 3
-+#define WM8976_ADCOSR 4
-+#define WM8976_MCLKSEL 5
-+
-+#define WM8976_MCLK_MCLK (0 << 8)
-+#define WM8976_MCLK_PLL (1 << 8)
-+
-+#define WM8976_MCLK_DIV_1 (0 << 5)
-+#define WM8976_MCLK_DIV_1_5 (1 << 5)
-+#define WM8976_MCLK_DIV_2 (2 << 5)
-+#define WM8976_MCLK_DIV_3 (3 << 5)
-+#define WM8976_MCLK_DIV_4 (4 << 5)
-+#define WM8976_MCLK_DIV_5_5 (5 << 5)
-+#define WM8976_MCLK_DIV_6 (6 << 5)
-+
-+#define WM8976_BCLK_DIV_1 (0 << 2)
-+#define WM8976_BCLK_DIV_2 (1 << 2)
-+#define WM8976_BCLK_DIV_4 (2 << 2)
-+#define WM8976_BCLK_DIV_8 (3 << 2)
-+#define WM8976_BCLK_DIV_16 (4 << 2)
-+#define WM8976_BCLK_DIV_32 (5 << 2)
-+
-+#define WM8976_DACOSR_64 (0 << 3)
-+#define WM8976_DACOSR_128 (1 << 3)
-+
-+#define WM8976_ADCOSR_64 (0 << 3)
-+#define WM8976_ADCOSR_128 (1 << 3)
-+
-+#define WM8976_OPCLK_DIV_1 (0 << 4)
-+#define WM8976_OPCLK_DIV_2 (1 << 4)
-+#define WM8976_OPCLK_DIV_3 (2 << 4)
-+#define WM8976_OPCLK_DIV_4 (3 << 4)
-+
-+struct wm8976_setup_data {
-+ unsigned short i2c_address;
-+};
-+
-+extern struct snd_soc_codec_dai wm8976_dai;
-+extern struct snd_soc_codec_device soc_codec_dev_wm8976;
-+
-+#endif
-Index: linux-2.6.21-moko/sound/soc/imx/imx21-pcm.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/imx/imx21-pcm.c
-@@ -0,0 +1,454 @@
-+/*
-+ * linux/sound/arm/mxc-pcm.c -- ALSA SoC interface for the Freescale i.MX CPU's
-+ *
-+ * Copyright 2006 Wolfson Microelectronics PLC.
-+ * Author: Liam Girdwood
-+ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
-+ *
-+ * Based on pxa2xx-pcm.c by Nicolas Pitre, (C) 2004 MontaVista Software, Inc.
-+ * and on mxc-alsa-mc13783 (C) 2006 Freescale.
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License version 2 as
-+ * published by the Free Software Foundation.
-+ *
-+ * Revision history
-+ * 29th Aug 2006 Initial version.
-+ *
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/init.h>
-+#include <linux/platform_device.h>
-+#include <linux/slab.h>
-+#include <linux/dma-mapping.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/pcm_params.h>
-+#include <sound/soc.h>
-+#include <asm/dma.h>
-+#include <asm/hardware.h>
-+
-+#include "imx-pcm.h"
-+
-+/* debug */
-+#define IMX_DEBUG 0
-+#if IMX_DEBUG
-+#define dbg(format, arg...) printk(format, ## arg)
-+#else
-+#define dbg(format, arg...)
-+#endif
-+
-+static const struct snd_pcm_hardware mxc_pcm_hardware = {
-+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
-+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
-+ SNDRV_PCM_INFO_MMAP |
-+ SNDRV_PCM_INFO_MMAP_VALID |
-+ SNDRV_PCM_INFO_PAUSE |
-+ SNDRV_PCM_INFO_RESUME),
-+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
-+ SNDRV_PCM_FMTBIT_S24_LE,
-+ .buffer_bytes_max = 32 * 1024,
-+ .period_bytes_min = 64,
-+ .period_bytes_max = 8 * 1024,
-+ .periods_min = 2,
-+ .periods_max = 255,
-+ .fifo_size = 0,
-+};
-+
-+struct mxc_runtime_data {
-+ int dma_ch;
-+ struct mxc_pcm_dma_param *dma_params;
-+};
-+
-+/*!
-+ * This function stops the current dma transfert for playback
-+ * and clears the dma pointers.
-+ *
-+ * @param substream pointer to the structure of the current stream.
-+ *
-+ */
-+static void audio_stop_dma(struct snd_pcm_substream *substream)
-+{
-+ struct snd_pcm_runtime *runtime = substream->runtime;
-+ struct mxc_runtime_data *prtd = runtime->private_data;
-+ unsigned int dma_size = frames_to_bytes(runtime, runtime->period_size);
-+ unsigned int offset dma_size * s->periods;
-+ unsigned long flags;
-+
-+ spin_lock_irqsave(&prtd->dma_lock, flags);
-+
-+ dbg("MXC : audio_stop_dma active = 0\n");
-+ prtd->active = 0;
-+ prtd->period = 0;
-+ prtd->periods = 0;
-+
-+ /* this stops the dma channel and clears the buffer ptrs */
-+ mxc_dma_stop(prtd->dma_wchannel);
-+ if(substream == SNDRV_PCM_STREAM_PLAYBACK)
-+ dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
-+ DMA_TO_DEVICE);
-+ else
-+ dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
-+ DMA_FROM_DEVICE);
-+
-+ spin_unlock_irqrestore(&prtd->dma_lock, flags);
-+}
-+
-+/*!
-+ * This function is called whenever a new audio block needs to be
-+ * transferred to mc13783. The function receives the address and the size
-+ * of the new block and start a new DMA transfer.
-+ *
-+ * @param substream pointer to the structure of the current stream.
-+ *
-+ */
-+static int dma_new_period(struct snd_pcm_substream *substream)
-+{
-+ struct snd_pcm_runtime *runtime = substream->runtime;
-+ struct mxc_runtime_data *prtd = runtime->private_data;
-+ unsigned int dma_size;
-+ unsigned int offset;
-+ int ret=0;
-+ dma_request_t sdma_request;
-+
-+ if (prtd->active){
-+ memset(&sdma_request, 0, sizeof(dma_request_t));
-+ dma_size = frames_to_bytes(runtime, runtime->period_size);
-+ dbg("s->period (%x) runtime->periods (%d)\n",
-+ s->period,runtime->periods);
-+ dbg("runtime->period_size (%d) dma_size (%d)\n",
-+ (unsigned int)runtime->period_size,
-+ runtime->dma_bytes);
-+
-+ offset = dma_size * prtd->period;
-+ snd_assert(dma_size <= DMA_BUF_SIZE, );
-+ if(substream == SNDRV_PCM_STREAM_PLAYBACK)
-+ sdma_request.sourceAddr = (char*)(dma_map_single(NULL,
-+ runtime->dma_area + offset, dma_size, DMA_TO_DEVICE));
-+ else
-+ sdma_request.destAddr = (char*)(dma_map_single(NULL,
-+ runtime->dma_area + offset, dma_size, DMA_FROM_DEVICE));
-+ sdma_request.count = dma_size;
-+
-+ dbg("MXC: Start DMA offset (%d) size (%d)\n", offset,
-+ runtime->dma_bytes);
-+
-+ mxc_dma_set_config(prtd->dma_wchannel, &sdma_request, 0);
-+ if((ret = mxc_dma_start(prtd->dma_wchannel)) < 0) {
-+ dbg("audio_process_dma: cannot queue DMA buffer\
-+ (%i)\n", ret);
-+ return err;
-+ }
-+ prtd->tx_spin = 1; /* FGA little trick to retrieve DMA pos */
-+ prtd->period++;
-+ prtd->period %= runtime->periods;
-+ }
-+ return ret;
-+}
-+
-+
-+/*!
-+ * This is a callback which will be called
-+ * when a TX transfer finishes. The call occurs
-+ * in interrupt context.
-+ *
-+ * @param dat pointer to the structure of the current stream.
-+ *
-+ */
-+static void audio_dma_irq(void *data)
-+{
-+ struct snd_pcm_substream *substream;
-+ struct snd_pcm_runtime *runtime;
-+ struct mxc_runtime_data *prtd;
-+ unsigned int dma_size;
-+ unsigned int previous_period;
-+ unsigned int offset;
-+
-+ substream = data;
-+ runtime = substream->runtime;
-+ prtd = runtime->private_data;
-+ previous_period = prtd->periods;
-+ dma_size = frames_to_bytes(runtime, runtime->period_size);
-+ offset = dma_size * previous_period;
-+
-+ prtd->tx_spin = 0;
-+ prtd->periods++;
-+ prtd->periods %= runtime->periods;
-+
-+ /*
-+ * Give back to the CPU the access to the non cached memory
-+ */
-+ if(substream == SNDRV_PCM_STREAM_PLAYBACK)
-+ dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
-+ DMA_TO_DEVICE);
-+ else
-+ dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
-+ DMA_FROM_DEVICE);
-+ /*
-+ * If we are getting a callback for an active stream then we inform
-+ * the PCM middle layer we've finished a period
-+ */
-+ if (prtd->active)
-+ snd_pcm_period_elapsed(substream);
-+
-+ /*
-+ * Trig next DMA transfer
-+ */
-+ dma_new_period(substream);
-+}
-+
-+/*!
-+ * This function configures the hardware to allow audio
-+ * playback operations. It is called by ALSA framework.
-+ *
-+ * @param substream pointer to the structure of the current stream.
-+ *
-+ * @return 0 on success, -1 otherwise.
-+ */
-+static int
-+snd_mxc_prepare(struct snd_pcm_substream *substream)
-+{
-+ struct mxc_runtime_data *prtd = runtime->private_data;
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ int ret = 0;
-+ prtd->period = 0;
-+ prtd->periods = 0;
-+
-+ dma_channel_params params;
-+ int channel = 0; // passed in ?
-+
-+ if ((ret = mxc_request_dma(&channel, "ALSA TX SDMA") < 0)){
-+ dbg("error requesting a write dma channel\n");
-+ return ret;
-+ }
-+
-+ /* configure DMA params */
-+ memset(¶ms, 0, sizeof(dma_channel_params));
-+ params.bd_number = 1;
-+ params.arg = s;
-+ params.callback = callback;
-+ params.transfer_type = emi_2_per;
-+ params.watermark_level = SDMA_TXFIFO_WATERMARK;
-+ params.word_size = TRANSFER_16BIT;
-+ //dbg(KERN_ERR "activating connection SSI1 - SDMA\n");
-+ params.per_address = SSI1_BASE_ADDR;
-+ params.event_id = DMA_REQ_SSI1_TX1;
-+ params.peripheral_type = SSI;
-+
-+ /* set up chn with params */
-+ mxc_dma_setup_channel(channel, ¶ms);
-+ s->dma_wchannel = channel;
-+
-+ return ret;
-+}
-+
-+static int mxc_pcm_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_pcm_runtime *runtime = substream->runtime;
-+ int ret;
-+
-+ if((ret=snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
-+ return ret;
-+ runtime->dma_addr = virt_to_phys(runtime->dma_area);
-+
-+ return ret;
-+}
-+
-+static int mxc_pcm_hw_free(struct snd_pcm_substream *substream)
-+{
-+ return snd_pcm_lib_free_pages(substream);
-+}
-+
-+static int mxc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-+{
-+ struct mxc_runtime_data *prtd = substream->runtime->private_data;
-+ int ret = 0;
-+
-+ switch (cmd) {
-+ case SNDRV_PCM_TRIGGER_START:
-+ prtd->tx_spin = 0;
-+ /* requested stream startup */
-+ prtd->active = 1;
-+ ret = dma_new_period(substream);
-+ break;
-+ case SNDRV_PCM_TRIGGER_STOP:
-+ /* requested stream shutdown */
-+ ret = audio_stop_dma(substream);
-+ break;
-+ case SNDRV_PCM_TRIGGER_SUSPEND:
-+ prtd->active = 0;
-+ prtd->periods = 0;
-+ break;
-+ case SNDRV_PCM_TRIGGER_RESUME:
-+ prtd->active = 1;
-+ prtd->tx_spin = 0;
-+ ret = dma_new_period(substream);
-+ break;
-+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-+ prtd->active = 0;
-+ break;
-+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-+ prtd->active = 1;
-+ if (prtd->old_offset) {
-+ prtd->tx_spin = 0;
-+ ret = dma_new_period(substream);
-+ }
-+ break;
-+ default:
-+ ret = -EINVAL;
-+ break;
-+ }
-+
-+ return ret;
-+}
-+
-+static snd_pcm_uframes_t mxc_pcm_pointer(struct snd_pcm_substream *substream)
-+{
-+ struct snd_pcm_runtime *runtime = substream->runtime;
-+ struct mxc_runtime_data *prtd = runtime->private_data;
-+ unsigned int offset = 0;
-+
-+ /* tx_spin value is used here to check if a transfert is active */
-+ if (prtd->tx_spin){
-+ offset = (runtime->period_size * (prtd->periods)) +
-+ (runtime->period_size >> 1);
-+ if (offset >= runtime->buffer_size)
-+ offset = runtime->period_size >> 1;
-+ } else {
-+ offset = (runtime->period_size * (s->periods));
-+ if (offset >= runtime->buffer_size)
-+ offset = 0;
-+ }
-+
-+ return offset;
-+}
-+
-+
-+static int mxc_pcm_open(struct snd_pcm_substream *substream)
-+{
-+ struct snd_pcm_runtime *runtime = substream->runtime;
-+ struct mxc_runtime_data *prtd;
-+ int ret;
-+
-+ snd_soc_set_runtime_hwparams(substream, &mxc_pcm_hardware);
-+
-+ if ((err = snd_pcm_hw_constraint_integer(runtime,
-+ SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
-+ return err;
-+ if ((err = snd_pcm_hw_constraint_list(runtime, 0,
-+ SNDRV_PCM_HW_PARAM_RATE, &hw_playback_rates)) < 0)
-+ return err;
-+ msleep(10); // liam - why
-+
-+ /* setup DMA controller for playback */
-+ if((err = configure_write_channel(&mxc_mc13783->s[SNDRV_PCM_STREAM_PLAYBACK],
-+ audio_dma_irq)) < 0 )
-+ return err;
-+
-+ if((prtd = kzalloc(sizeof(struct mxc_runtime_data), GFP_KERNEL)) == NULL) {
-+ ret = -ENOMEM;
-+ goto out;
-+ }
-+
-+ runtime->private_data = prtd;
-+ return 0;
-+
-+ err1:
-+ kfree(prtd);
-+ out:
-+ return ret;
-+}
-+
-+static int mxc_pcm_close(struct snd_pcm_substream *substream)
-+{
-+ struct snd_pcm_runtime *runtime = substream->runtime;
-+ struct mxc_runtime_data *prtd = runtime->private_data;
-+
-+// mxc_mc13783_t *chip;
-+ audio_stream_t *s;
-+ device_data_t* device;
-+ int ssi;
-+
-+ //chip = snd_pcm_substream_chip(substream);
-+ s = &chip->s[substream->pstr->stream];
-+ device = &s->stream_device;
-+ ssi = device->ssi;
-+
-+ //disable_stereodac();
-+
-+ ssi_transmit_enable(ssi, false);
-+ ssi_interrupt_disable(ssi, ssi_tx_dma_interrupt_enable);
-+ ssi_tx_fifo_enable(ssi, ssi_fifo_0, false);
-+ ssi_enable(ssi, false);
-+
-+ chip->s[substream->pstr->stream].stream = NULL;
-+
-+ return 0;
-+}
-+
-+static int
-+mxc_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma)
-+{
-+ struct snd_pcm_runtime *runtime = substream->runtime;
-+ return dma_mmap_writecombine(substream->pcm->card->dev, vma,
-+ runtime->dma_area,
-+ runtime->dma_addr,
-+ runtime->dma_bytes);
-+}
-+
-+struct snd_pcm_ops mxc_pcm_ops = {
-+ .open = mxc_pcm_open,
-+ .close = mxc_pcm_close,
-+ .ioctl = snd_pcm_lib_ioctl,
-+ .hw_params = mxc_pcm_hw_params,
-+ .hw_free = mxc_pcm_hw_free,
-+ .prepare = mxc_pcm_prepare,
-+ .trigger = mxc_pcm_trigger,
-+ .pointer = mxc_pcm_pointer,
-+ .mmap = mxc_pcm_mmap,
-+};
-+
-+static u64 mxc_pcm_dmamask = 0xffffffff;
-+
-+int mxc_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
-+ struct snd_pcm *pcm)
-+{
-+ int ret = 0;
-+
-+ if (!card->dev->dma_mask)
-+ card->dev->dma_mask = &mxc_pcm_dmamask;
-+ if (!card->dev->coherent_dma_mask)
-+ card->dev->coherent_dma_mask = 0xffffffff;
-+
-+ if (dai->playback.channels_min) {
-+ ret = mxc_pcm_preallocate_dma_buffer(pcm,
-+ SNDRV_PCM_STREAM_PLAYBACK);
-+ if (ret)
-+ goto out;
-+ }
-+
-+ if (dai->capture.channels_min) {
-+ ret = mxc_pcm_preallocate_dma_buffer(pcm,
-+ SNDRV_PCM_STREAM_CAPTURE);
-+ if (ret)
-+ goto out;
-+ }
-+ out:
-+ return ret;
-+}
-+
-+struct snd_soc_platform mxc_soc_platform = {
-+ .name = "mxc-audio",
-+ .pcm_ops = &mxc_pcm_ops,
-+ .pcm_new = mxc_pcm_new,
-+ .pcm_free = mxc_pcm_free_dma_buffers,
-+};
-+
-+EXPORT_SYMBOL_GPL(mxc_soc_platform);
-+
-+MODULE_AUTHOR("Liam Girdwood");
-+MODULE_DESCRIPTION("Freescale i.MX PCM DMA module");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/imx/imx21-pcm.h
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/imx/imx21-pcm.h
-@@ -0,0 +1,237 @@
-+/*
-+ * mxc-pcm.h :- ASoC platform header for Freescale i.MX
-+ *
-+ * 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 _MXC_PCM_H
-+#define _MXC_PCM_H
-+
-+struct {
-+ char *name; /* stream identifier */
-+ dma_channel_params dma_params;
-+} mxc_pcm_dma_param;
-+
-+extern struct snd_soc_cpu_dai mxc_ssi_dai[3];
-+
-+/* platform data */
-+extern struct snd_soc_platform mxc_soc_platform;
-+extern struct snd_ac97_bus_ops mxc_ac97_ops;
-+
-+/* temp until imx-regs.h is up2date */
-+#define SSI1_STX0 __REG(IMX_SSI1_BASE + 0x00)
-+#define SSI1_STX0_PHYS __PHYS_REG(IMX_SSI1_BASE + 0x00)
-+#define SSI1_STX1 __REG(IMX_SSI1_BASE + 0x04)
-+#define SSI1_STX1_PHYS __PHYS_REG(IMX_SSI1_BASE + 0x04)
-+#define SSI1_SRX0 __REG(IMX_SSI1_BASE + 0x08)
-+#define SSI1_SRX0_PHYS __PHYS_REG(IMX_SSI1_BASE + 0x08)
-+#define SSI1_SRX1 __REG(IMX_SSI1_BASE + 0x0c)
-+#define SSI1_SRX1_PHYS __PHYS_REG(IMX_SSI1_BASE + 0x0c)
-+#define SSI1_SCR __REG(IMX_SSI1_BASE + 0x10)
-+#define SSI1_SISR __REG(IMX_SSI1_BASE + 0x14)
-+#define SSI1_SIER __REG(IMX_SSI1_BASE + 0x18)
-+#define SSI1_STCR __REG(IMX_SSI1_BASE + 0x1c)
-+#define SSI1_SRCR __REG(IMX_SSI1_BASE + 0x20)
-+#define SSI1_STCCR __REG(IMX_SSI1_BASE + 0x24)
-+#define SSI1_SRCCR __REG(IMX_SSI1_BASE + 0x28)
-+#define SSI1_SFCSR __REG(IMX_SSI1_BASE + 0x2c)
-+#define SSI1_STR __REG(IMX_SSI1_BASE + 0x30)
-+#define SSI1_SOR __REG(IMX_SSI1_BASE + 0x34)
-+#define SSI1_SACNT __REG(IMX_SSI1_BASE + 0x38)
-+#define SSI1_SACADD __REG(IMX_SSI1_BASE + 0x3c)
-+#define SSI1_SACDAT __REG(IMX_SSI1_BASE + 0x40)
-+#define SSI1_SATAG __REG(IMX_SSI1_BASE + 0x44)
-+#define SSI1_STMSK __REG(IMX_SSI1_BASE + 0x48)
-+#define SSI1_SRMSK __REG(IMX_SSI1_BASE + 0x4c)
-+
-+#define SSI2_STX0 __REG(IMX_SSI2_BASE + 0x00)
-+#define SSI2_STX0_PHYS __PHYS_REG(IMX_SSI2_BASE + 0x00)
-+#define SSI2_STX1 __REG(IMX_SSI2_BASE + 0x04)
-+#define SSI2_STX1_PHYS __PHYS_REG(IMX_SSI2_BASE + 0x04)
-+#define SSI2_SRX0 __REG(IMX_SSI2_BASE + 0x08)
-+#define SSI2_SRX0_PHYS __PHYS_REG(IMX_SSI2_BASE + 0x08)
-+#define SSI2_SRX1 __REG(IMX_SSI2_BASE + 0x0c)
-+#define SSI2_SRX1_PHYS __PHYS_REG(IMX_SSI2_BASE + 0x0c)
-+#define SSI2_SCR __REG(IMX_SSI2_BASE + 0x10)
-+#define SSI2_SISR __REG(IMX_SSI2_BASE + 0x14)
-+#define SSI2_SIER __REG(IMX_SSI2_BASE + 0x18)
-+#define SSI2_STCR __REG(IMX_SSI2_BASE + 0x1c)
-+#define SSI2_SRCR __REG(IMX_SSI2_BASE + 0x20)
-+#define SSI2_STCCR __REG(IMX_SSI2_BASE + 0x24)
-+#define SSI2_SRCCR __REG(IMX_SSI2_BASE + 0x28)
-+#define SSI2_SFCSR __REG(IMX_SSI2_BASE + 0x2c)
-+#define SSI2_STR __REG(IMX_SSI2_BASE + 0x30)
-+#define SSI2_SOR __REG(IMX_SSI2_BASE + 0x34)
-+#define SSI2_SACNT __REG(IMX_SSI2_BASE + 0x38)
-+#define SSI2_SACADD __REG(IMX_SSI2_BASE + 0x3c)
-+#define SSI2_SACDAT __REG(IMX_SSI2_BASE + 0x40)
-+#define SSI2_SATAG __REG(IMX_SSI2_BASE + 0x44)
-+#define SSI2_STMSK __REG(IMX_SSI2_BASE + 0x48)
-+#define SSI2_SRMSK __REG(IMX_SSI2_BASE + 0x4c)
-+
-+#define SSI_SCR_CLK_IST (1 << 9)
-+#define SSI_SCR_TCH_EN (1 << 8)
-+#define SSI_SCR_SYS_CLK_EN (1 << 7)
-+#define SSI_SCR_I2S_MODE_NORM (0 << 5)
-+#define SSI_SCR_I2S_MODE_MSTR (1 << 5)
-+#define SSI_SCR_I2S_MODE_SLAVE (2 << 5)
-+#define SSI_SCR_SYN (1 << 4)
-+#define SSI_SCR_NET (1 << 3)
-+#define SSI_SCR_RE (1 << 2)
-+#define SSI_SCR_TE (1 << 1)
-+#define SSI_SCR_SSIEN (1 << 0)
-+
-+#define SSI_SISR_CMDAU (1 << 18)
-+#define SSI_SISR_CMDDU (1 << 17)
-+#define SSI_SISR_RXT (1 << 16)
-+#define SSI_SISR_RDR1 (1 << 15)
-+#define SSI_SISR_RDR0 (1 << 14)
-+#define SSI_SISR_TDE1 (1 << 13)
-+#define SSI_SISR_TDE0 (1 << 12)
-+#define SSI_SISR_ROE1 (1 << 11)
-+#define SSI_SISR_ROE0 (1 << 10)
-+#define SSI_SISR_TUE1 (1 << 9)
-+#define SSI_SISR_TUE0 (1 << 8)
-+#define SSI_SISR_TFS (1 << 7)
-+#define SSI_SISR_RFS (1 << 6)
-+#define SSI_SISR_TLS (1 << 5)
-+#define SSI_SISR_RLS (1 << 4)
-+#define SSI_SISR_RFF1 (1 << 3)
-+#define SSI_SISR_RFF0 (1 << 2)
-+#define SSI_SISR_TFE1 (1 << 1)
-+#define SSI_SISR_TFE0 (1 << 0)
-+
-+#define SSI_SIER_RDMAE (1 << 22)
-+#define SSI_SIER_RIE (1 << 21)
-+#define SSI_SIER_TDMAE (1 << 20)
-+#define SSI_SIER_TIE (1 << 19)
-+#define SSI_SIER_CMDAU_EN (1 << 18)
-+#define SSI_SIER_CMDDU_EN (1 << 17)
-+#define SSI_SIER_RXT_EN (1 << 16)
-+#define SSI_SIER_RDR1_EN (1 << 15)
-+#define SSI_SIER_RDR0_EN (1 << 14)
-+#define SSI_SIER_TDE1_EN (1 << 13)
-+#define SSI_SIER_TDE0_EN (1 << 12)
-+#define SSI_SIER_ROE1_EN (1 << 11)
-+#define SSI_SIER_ROE0_EN (1 << 10)
-+#define SSI_SIER_TUE1_EN (1 << 9)
-+#define SSI_SIER_TUE0_EN (1 << 8)
-+#define SSI_SIER_TFS_EN (1 << 7)
-+#define SSI_SIER_RFS_EN (1 << 6)
-+#define SSI_SIER_TLS_EN (1 << 5)
-+#define SSI_SIER_RLS_EN (1 << 4)
-+#define SSI_SIER_RFF1_EN (1 << 3)
-+#define SSI_SIER_RFF0_EN (1 << 2)
-+#define SSI_SIER_TFE1_EN (1 << 1)
-+#define SSI_SIER_TFE0_EN (1 << 0)
-+
-+#define SSI_STCR_TXBIT0 (1 << 9)
-+#define SSI_STCR_TFEN1 (1 << 8)
-+#define SSI_STCR_TFEN0 (1 << 7)
-+#define SSI_STCR_TFDIR (1 << 6)
-+#define SSI_STCR_TXDIR (1 << 5)
-+#define SSI_STCR_TSHFD (1 << 4)
-+#define SSI_STCR_TSCKP (1 << 3)
-+#define SSI_STCR_TFSI (1 << 2)
-+#define SSI_STCR_TFSL (1 << 1)
-+#define SSI_STCR_TEFS (1 << 0)
-+
-+#define SSI_SRCR_RXBIT0 (1 << 9)
-+#define SSI_SRCR_RFEN1 (1 << 8)
-+#define SSI_SRCR_RFEN0 (1 << 7)
-+#define SSI_SRCR_RFDIR (1 << 6)
-+#define SSI_SRCR_RXDIR (1 << 5)
-+#define SSI_SRCR_RSHFD (1 << 4)
-+#define SSI_SRCR_RSCKP (1 << 3)
-+#define SSI_SRCR_RFSI (1 << 2)
-+#define SSI_SRCR_RFSL (1 << 1)
-+#define SSI_SRCR_REFS (1 << 0)
-+
-+#define SSI_STCCR_DIV2 (1 << 18)
-+#define SSI_STCCR_PSR (1 << 15)
-+#define SSI_STCCR_WL(x) ((((x) - 2) >> 1) << 13)
-+#define SSI_STCCR_DC(x) (((x) & 0x1f) << 8)
-+#define SSI_STCCR_PM(x) (((x) & 0xff) << 0)
-+
-+#define SSI_SRCCR_DIV2 (1 << 18)
-+#define SSI_SRCCR_PSR (1 << 15)
-+#define SSI_SRCCR_WL(x) ((((x) - 2) >> 1) << 13)
-+#define SSI_SRCCR_DC(x) (((x) & 0x1f) << 8)
-+#define SSI_SRCCR_PM(x) (((x) & 0xff) << 0)
-+
-+
-+#define SSI_SFCSR_RFCNT1(x) (((x) & 0xf) << 28)
-+#define SSI_SFCSR_TFCNT1(x) (((x) & 0xf) << 24)
-+#define SSI_SFCSR_RFWM1(x) (((x) & 0xf) << 20)
-+#define SSI_SFCSR_TFWM1(x) (((x) & 0xf) << 16)
-+#define SSI_SFCSR_RFCNT0(x) (((x) & 0xf) << 12)
-+#define SSI_SFCSR_TFCNT0(x) (((x) & 0xf) << 8)
-+#define SSI_SFCSR_RFWM0(x) (((x) & 0xf) << 4)
-+#define SSI_SFCSR_TFWM0(x) (((x) & 0xf) << 0)
-+
-+#define SSI_STR_TEST (1 << 15)
-+#define SSI_STR_RCK2TCK (1 << 14)
-+#define SSI_STR_RFS2TFS (1 << 13)
-+#define SSI_STR_RXSTATE(x) (((x) & 0xf) << 8)
-+#define SSI_STR_TXD2RXD (1 << 7)
-+#define SSI_STR_TCK2RCK (1 << 6)
-+#define SSI_STR_TFS2RFS (1 << 5)
-+#define SSI_STR_TXSTATE(x) (((x) & 0xf) << 0)
-+
-+#define SSI_SOR_CLKOFF (1 << 6)
-+#define SSI_SOR_RX_CLR (1 << 5)
-+#define SSI_SOR_TX_CLR (1 << 4)
-+#define SSI_SOR_INIT (1 << 3)
-+#define SSI_SOR_WAIT(x) (((x) & 0x3) << 1)
-+#define SSI_SOR_SYNRST (1 << 0)
-+
-+#define SSI_SACNT_FRDIV(x) (((x) & 0x3f) << 5)
-+#define SSI_SACNT_WR (x << 4)
-+#define SSI_SACNT_RD (x << 3)
-+#define SSI_SACNT_TIF (x << 2)
-+#define SSI_SACNT_FV (x << 1)
-+#define SSI_SACNT_A97EN (x << 0)
-+
-+
-+/* AUDMUX registers */
-+#define AUDMUX_HPCR1 __REG(IMX_AUDMUX_BASE + 0x00)
-+#define AUDMUX_HPCR2 __REG(IMX_AUDMUX_BASE + 0x04)
-+#define AUDMUX_HPCR3 __REG(IMX_AUDMUX_BASE + 0x08)
-+#define AUDMUX_PPCR1 __REG(IMX_AUDMUX_BASE + 0x10)
-+#define AUDMUX_PPCR2 __REG(IMX_AUDMUX_BASE + 0x14)
-+#define AUDMUX_PPCR3 __REG(IMX_AUDMUX_BASE + 0x18)
-+
-+#define AUDMUX_HPCR_TFSDIR (1 << 31)
-+#define AUDMUX_HPCR_TCLKDIR (1 << 30)
-+#define AUDMUX_HPCR_TFCSEL_TX (0 << 26)
-+#define AUDMUX_HPCR_TFCSEL_RX (8 << 26)
-+#define AUDMUX_HPCR_TFCSEL(x) (((x) & 0x7) << 26)
-+#define AUDMUX_HPCR_RFSDIR (1 << 25)
-+#define AUDMUX_HPCR_RCLKDIR (1 << 24)
-+#define AUDMUX_HPCR_RFCSEL_TX (0 << 20)
-+#define AUDMUX_HPCR_RFCSEL_RX (8 << 20)
-+#define AUDMUX_HPCR_RFCSEL(x) (((x) & 0x7) << 20)
-+#define AUDMUX_HPCR_RXDSEL(x) (((x) & 0x7) << 13)
-+#define AUDMUX_HPCR_SYN (1 << 12)
-+#define AUDMUX_HPCR_TXRXEN (1 << 10)
-+#define AUDMUX_HPCR_INMEN (1 << 8)
-+#define AUDMUX_HPCR_INMMASK(x) (((x) & 0xff) << 0)
-+
-+#define AUDMUX_PPCR_TFSDIR (1 << 31)
-+#define AUDMUX_PPCR_TCLKDIR (1 << 30)
-+#define AUDMUX_PPCR_TFCSEL_TX (0 << 26)
-+#define AUDMUX_PPCR_TFCSEL_RX (8 << 26)
-+#define AUDMUX_PPCR_TFCSEL(x) (((x) & 0x7) << 26)
-+#define AUDMUX_PPCR_RFSDIR (1 << 25)
-+#define AUDMUX_PPCR_RCLKDIR (1 << 24)
-+#define AUDMUX_PPCR_RFCSEL_TX (0 << 20)
-+#define AUDMUX_PPCR_RFCSEL_RX (8 << 20)
-+#define AUDMUX_PPCR_RFCSEL(x) (((x) & 0x7) << 20)
-+#define AUDMUX_PPCR_RXDSEL(x) (((x) & 0x7) << 13)
-+#define AUDMUX_PPCR_SYN (1 << 12)
-+#define AUDMUX_PPCR_TXRXEN (1 << 10)
-+
-+
-+#endif
-Index: linux-2.6.21-moko/sound/soc/imx/imx31-pcm.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/imx/imx31-pcm.c
-@@ -0,0 +1,417 @@
-+/*
-+ * linux/sound/arm/mxc-pcm.c -- ALSA SoC interface for the Freescale i.MX CPU's
-+ *
-+ * Copyright 2006 Wolfson Microelectronics PLC.
-+ * Author: Liam Girdwood
-+ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
-+ *
-+ * Based on pxa2xx-pcm.c by Nicolas Pitre, (C) 2004 MontaVista Software, Inc.
-+ * and on mxc-alsa-mc13783 (C) 2006 Freescale.
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License version 2 as
-+ * published by the Free Software Foundation.
-+ *
-+ * Revision history
-+ * 29th Aug 2006 Initial version.
-+ *
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/init.h>
-+#include <linux/platform_device.h>
-+#include <linux/slab.h>
-+#include <linux/dma-mapping.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/pcm_params.h>
-+#include <sound/soc.h>
-+#include <asm/arch/dma.h>
-+#include <asm/arch/spba.h>
-+#include <asm/arch/clock.h>
-+#include <asm/mach-types.h>
-+#include <asm/hardware.h>
-+
-+#include "imx31-pcm.h"
-+
-+/* debug */
-+#define IMX_DEBUG 0
-+#if IMX_DEBUG
-+#define dbg(format, arg...) printk(format, ## arg)
-+#else
-+#define dbg(format, arg...)
-+#endif
-+
-+static const struct snd_pcm_hardware mxc_pcm_hardware = {
-+ .info = (SNDRV_PCM_INFO_INTERLEAVED |
-+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
-+ SNDRV_PCM_INFO_MMAP |
-+ SNDRV_PCM_INFO_MMAP_VALID |
-+ SNDRV_PCM_INFO_PAUSE |
-+ SNDRV_PCM_INFO_RESUME),
-+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
-+ SNDRV_PCM_FMTBIT_S24_LE,
-+ .buffer_bytes_max = 32 * 1024,
-+ .period_bytes_min = 64,
-+ .period_bytes_max = 8 * 1024,
-+ .periods_min = 2,
-+ .periods_max = 255,
-+ .fifo_size = 0,
-+};
-+
-+struct mxc_runtime_data {
-+ int dma_ch;
-+ struct mxc_pcm_dma_param *dma_params;
-+ spinlock_t dma_lock;
-+ int active, period, periods;
-+ int dma_wchannel;
-+ int tx_spin, rx_spin;
-+ int old_offset;
-+};
-+
-+/*!
-+ * This function stops the current dma transfer for playback
-+ * and clears the dma pointers.
-+ *
-+ * @param substream pointer to the structure of the current stream.
-+ *
-+ */
-+static void audio_stop_dma(struct snd_pcm_substream *substream)
-+{
-+ struct snd_pcm_runtime *runtime = substream->runtime;
-+ struct mxc_runtime_data *prtd = runtime->private_data;
-+ unsigned int dma_size = frames_to_bytes(runtime, runtime->period_size);
-+ unsigned int offset = dma_size * runtime->periods;
-+ unsigned long flags;
-+
-+ spin_lock_irqsave(&prtd->dma_lock, flags);
-+
-+ dbg("MXC : audio_stop_dma active = 0\n");
-+ prtd->active = 0;
-+ prtd->period = 0;
-+ prtd->periods = 0;
-+
-+ /* this stops the dma channel and clears the buffer ptrs */
-+ mxc_dma_stop(prtd->dma_wchannel);
-+ if(substream == SNDRV_PCM_STREAM_PLAYBACK)
-+ dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
-+ DMA_TO_DEVICE);
-+ else
-+ dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
-+ DMA_FROM_DEVICE);
-+
-+ spin_unlock_irqrestore(&prtd->dma_lock, flags);
-+}
-+
-+/*!
-+ * This function is called whenever a new audio block needs to be
-+ * transferred to mc13783. The function receives the address and the size
-+ * of the new block and start a new DMA transfer.
-+ *
-+ * @param substream pointer to the structure of the current stream.
-+ *
-+ */
-+static int dma_new_period(struct snd_pcm_substream *substream)
-+{
-+ struct snd_pcm_runtime *runtime = substream->runtime;
-+ struct mxc_runtime_data *prtd = runtime->private_data;
-+ unsigned int dma_size;
-+ unsigned int offset;
-+ int ret = 0;
-+ dma_request_t sdma_request;
-+
-+ if (prtd->active){
-+ memset(&sdma_request, 0, sizeof(dma_request_t));
-+ dma_size = frames_to_bytes(runtime, runtime->period_size);
-+ dbg("s->period (%x) runtime->periods (%d)\n",
-+ s->period,runtime->periods);
-+ dbg("runtime->period_size (%d) dma_size (%d)\n",
-+ (unsigned int)runtime->period_size,
-+ runtime->dma_bytes);
-+
-+ offset = dma_size * prtd->period;
-+// snd_assert(dma_size <= DMA_BUF_SIZE, );
-+ if(substream == SNDRV_PCM_STREAM_PLAYBACK)
-+ sdma_request.sourceAddr = (char*)(dma_map_single(NULL,
-+ runtime->dma_area + offset, dma_size, DMA_TO_DEVICE));
-+ else
-+ sdma_request.destAddr = (char*)(dma_map_single(NULL,
-+ runtime->dma_area + offset, dma_size, DMA_FROM_DEVICE));
-+ sdma_request.count = dma_size;
-+
-+ dbg("MXC: Start DMA offset (%d) size (%d)\n", offset,
-+ runtime->dma_bytes);
-+
-+ mxc_dma_set_config(prtd->dma_wchannel, &sdma_request, 0);
-+ if((ret = mxc_dma_start(prtd->dma_wchannel)) < 0) {
-+ dbg("audio_process_dma: cannot queue DMA buffer\
-+ (%i)\n", ret);
-+ return ret;
-+ }
-+ prtd->tx_spin = 1; /* FGA little trick to retrieve DMA pos */
-+ prtd->period++;
-+ prtd->period %= runtime->periods;
-+ }
-+ return ret;
-+}
-+
-+
-+/*!
-+ * This is a callback which will be called
-+ * when a TX transfer finishes. The call occurs
-+ * in interrupt context.
-+ *
-+ * @param dat pointer to the structure of the current stream.
-+ *
-+ */
-+static void audio_dma_irq(void *data)
-+{
-+ struct snd_pcm_substream *substream;
-+ struct snd_pcm_runtime *runtime;
-+ struct mxc_runtime_data *prtd;
-+ unsigned int dma_size;
-+ unsigned int previous_period;
-+ unsigned int offset;
-+
-+ substream = data;
-+ runtime = substream->runtime;
-+ prtd = runtime->private_data;
-+ previous_period = prtd->periods;
-+ dma_size = frames_to_bytes(runtime, runtime->period_size);
-+ offset = dma_size * previous_period;
-+
-+ prtd->tx_spin = 0;
-+ prtd->periods++;
-+ prtd->periods %= runtime->periods;
-+
-+ /*
-+ * Give back to the CPU the access to the non cached memory
-+ */
-+ if(substream == SNDRV_PCM_STREAM_PLAYBACK)
-+ dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
-+ DMA_TO_DEVICE);
-+ else
-+ dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
-+ DMA_FROM_DEVICE);
-+ /*
-+ * If we are getting a callback for an active stream then we inform
-+ * the PCM middle layer we've finished a period
-+ */
-+ if (prtd->active)
-+ snd_pcm_period_elapsed(substream);
-+
-+ /*
-+ * Trig next DMA transfer
-+ */
-+ dma_new_period(substream);
-+}
-+
-+/*!
-+ * This function configures the hardware to allow audio
-+ * playback operations. It is called by ALSA framework.
-+ *
-+ * @param substream pointer to the structure of the current stream.
-+ *
-+ * @return 0 on success, -1 otherwise.
-+ */
-+static int
-+mxc_pcm_prepare(struct snd_pcm_substream *substream)
-+{
-+ struct snd_pcm_runtime *runtime = substream->runtime;
-+ struct mxc_runtime_data *prtd = runtime->private_data;
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ dma_channel_params *params = rtd->dai->cpu_dai->dma_data;
-+ int ret = 0, channel = 0; // passed in ?;
-+
-+ prtd->period = 0;
-+ prtd->periods = 0;
-+
-+ if(substream == SNDRV_PCM_STREAM_PLAYBACK) {
-+ ret = mxc_request_dma(&channel, "ALSA TX SDMA");
-+ if (ret < 0) {
-+ dbg("error requesting a write dma channel\n");
-+ return ret;
-+ }
-+
-+ } else {
-+ ret = mxc_request_dma(&channel, "ALSA RX SDMA");
-+ if (ret < 0) {
-+ dbg("error requesting a read dma channel\n");
-+ return ret;
-+ }
-+ }
-+
-+ /* set up chn with params */
-+ params->callback = audio_dma_irq;
-+ mxc_dma_setup_channel(channel, params);
-+ prtd->dma_wchannel = channel;
-+
-+ return ret;
-+}
-+
-+static int mxc_pcm_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_pcm_runtime *runtime = substream->runtime;
-+ int ret;
-+
-+ ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
-+ if(ret < 0)
-+ return ret;
-+ runtime->dma_addr = virt_to_phys(runtime->dma_area);
-+
-+ return ret;
-+}
-+
-+static int mxc_pcm_hw_free(struct snd_pcm_substream *substream)
-+{
-+ return snd_pcm_lib_free_pages(substream);
-+}
-+
-+static int mxc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-+{
-+ struct mxc_runtime_data *prtd = substream->runtime->private_data;
-+ int ret = 0;
-+
-+ switch (cmd) {
-+ case SNDRV_PCM_TRIGGER_START:
-+ prtd->tx_spin = 0;
-+ /* requested stream startup */
-+ prtd->active = 1;
-+ ret = dma_new_period(substream);
-+ break;
-+ case SNDRV_PCM_TRIGGER_STOP:
-+ /* requested stream shutdown */
-+ audio_stop_dma(substream);
-+ break;
-+ case SNDRV_PCM_TRIGGER_SUSPEND:
-+ prtd->active = 0;
-+ prtd->periods = 0;
-+ break;
-+ case SNDRV_PCM_TRIGGER_RESUME:
-+ prtd->active = 1;
-+ prtd->tx_spin = 0;
-+ ret = dma_new_period(substream);
-+ break;
-+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-+ prtd->active = 0;
-+ break;
-+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-+ prtd->active = 1;
-+ if (prtd->old_offset) {
-+ prtd->tx_spin = 0;
-+ ret = dma_new_period(substream);
-+ }
-+ break;
-+ default:
-+ ret = -EINVAL;
-+ break;
-+ }
-+
-+ return ret;
-+}
-+
-+static snd_pcm_uframes_t mxc_pcm_pointer(struct snd_pcm_substream *substream)
-+{
-+ struct snd_pcm_runtime *runtime = substream->runtime;
-+ struct mxc_runtime_data *prtd = runtime->private_data;
-+ unsigned int offset = 0;
-+
-+ /* tx_spin value is used here to check if a transfert is active */
-+ if (prtd->tx_spin){
-+ offset = (runtime->period_size * (prtd->periods)) +
-+ (runtime->period_size >> 1);
-+ if (offset >= runtime->buffer_size)
-+ offset = runtime->period_size >> 1;
-+ } else {
-+ offset = (runtime->period_size * (prtd->periods));
-+ if (offset >= runtime->buffer_size)
-+ offset = 0;
-+ }
-+
-+ return offset;
-+}
-+
-+
-+static int mxc_pcm_open(struct snd_pcm_substream *substream)
-+{
-+ struct snd_pcm_runtime *runtime = substream->runtime;
-+ struct mxc_runtime_data *prtd;
-+ int ret;
-+
-+ snd_soc_set_runtime_hwparams(substream, &mxc_pcm_hardware);
-+
-+ ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
-+ if (ret < 0)
-+ return ret;
-+ //ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
-+ // &hw_playback_rates);
-+ //if (ret < 0)
-+ // return ret;
-+
-+ prtd = kzalloc(sizeof(struct mxc_runtime_data), GFP_KERNEL);
-+ if(prtd == NULL)
-+ return -ENOMEM;
-+
-+ runtime->private_data = prtd;
-+ return 0;
-+}
-+
-+static int mxc_pcm_close(struct snd_pcm_substream *substream)
-+{
-+ struct snd_pcm_runtime *runtime = substream->runtime;
-+ struct mxc_runtime_data *prtd = runtime->private_data;
-+
-+ kfree(prtd);
-+ return 0;
-+}
-+
-+static int
-+mxc_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma)
-+{
-+ struct snd_pcm_runtime *runtime = substream->runtime;
-+ return dma_mmap_writecombine(substream->pcm->card->dev, vma,
-+ runtime->dma_area,
-+ runtime->dma_addr,
-+ runtime->dma_bytes);
-+}
-+
-+struct snd_pcm_ops mxc_pcm_ops = {
-+ .open = mxc_pcm_open,
-+ .close = mxc_pcm_close,
-+ .ioctl = snd_pcm_lib_ioctl,
-+ .hw_params = mxc_pcm_hw_params,
-+ .hw_free = mxc_pcm_hw_free,
-+ .prepare = mxc_pcm_prepare,
-+ .trigger = mxc_pcm_trigger,
-+ .pointer = mxc_pcm_pointer,
-+ .mmap = mxc_pcm_mmap,
-+};
-+
-+static u64 mxc_pcm_dmamask = 0xffffffff;
-+
-+int mxc_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
-+ struct snd_pcm *pcm)
-+{
-+ int ret = 0;
-+
-+ if (!card->dev->dma_mask)
-+ card->dev->dma_mask = &mxc_pcm_dmamask;
-+ if (!card->dev->coherent_dma_mask)
-+ card->dev->coherent_dma_mask = 0xffffffff;
-+
-+ return ret;
-+}
-+
-+struct snd_soc_platform mxc_soc_platform = {
-+ .name = "mxc-audio",
-+ .pcm_ops = &mxc_pcm_ops,
-+ .pcm_new = mxc_pcm_new,
-+};
-+
-+EXPORT_SYMBOL_GPL(mxc_soc_platform);
-+
-+MODULE_AUTHOR("Liam Girdwood");
-+MODULE_DESCRIPTION("Freescale i.MX31 PCM DMA module");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/imx/imx31-pcm.h
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/imx/imx31-pcm.h
-@@ -0,0 +1,241 @@
-+/*
-+ * mxc-pcm.h :- ASoC platform header for Freescale i.MX
-+ *
-+ * 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 _MXC_PCM_H
-+#define _MXC_PCM_H
-+
-+#include <asm/arch/dma.h>
-+
-+/* temp until imx-regs.h is up2date */
-+#define SSI1_STX0 (SSI1_BASE_ADDR + 0x00)
-+#define SSI1_STX0_PHYS __PHYS_REG(SSI1_BASE_ADDR + 0x00)
-+#define SSI1_STX1 (SSI1_BASE_ADDR + 0x04)
-+#define SSI1_STX1_PHYS __PHYS_REG(SSI1_BASE_ADDR + 0x04)
-+#define SSI1_SRX0 (SSI1_BASE_ADDR + 0x08)
-+#define SSI1_SRX0_PHYS __PHYS_REG(SSI1_BASE_ADDR + 0x08)
-+#define SSI1_SRX1 (SSI1_BASE_ADDR + 0x0c)
-+#define SSI1_SRX1_PHYS __PHYS_REG(SSI1_BASE_ADDR + 0x0c)
-+#define SSI1_SCR (SSI1_BASE_ADDR + 0x10)
-+#define SSI1_SISR (SSI1_BASE_ADDR + 0x14)
-+#define SSI1_SIER (SSI1_BASE_ADDR + 0x18)
-+#define SSI1_STCR (SSI1_BASE_ADDR + 0x1c)
-+#define SSI1_SRCR (SSI1_BASE_ADDR + 0x20)
-+#define SSI1_STCCR (SSI1_BASE_ADDR + 0x24)
-+#define SSI1_SRCCR (SSI1_BASE_ADDR + 0x28)
-+#define SSI1_SFCSR (SSI1_BASE_ADDR + 0x2c)
-+#define SSI1_STR (SSI1_BASE_ADDR + 0x30)
-+#define SSI1_SOR (SSI1_BASE_ADDR + 0x34)
-+#define SSI1_SACNT (SSI1_BASE_ADDR + 0x38)
-+#define SSI1_SACADD (SSI1_BASE_ADDR + 0x3c)
-+#define SSI1_SACDAT (SSI1_BASE_ADDR + 0x40)
-+#define SSI1_SATAG (SSI1_BASE_ADDR + 0x44)
-+#define SSI1_STMSK (SSI1_BASE_ADDR + 0x48)
-+#define SSI1_SRMSK (SSI1_BASE_ADDR + 0x4c)
-+
-+#define SSI2_STX0 (SSI2_BASE_ADDR + 0x00)
-+#define SSI2_STX0_PHYS __PHYS_REG(SSI2_BASE_ADDR + 0x00)
-+#define SSI2_STX1 (SSI2_BASE_ADDR + 0x04)
-+#define SSI2_STX1_PHYS __PHYS_REG(SSI2_BASE_ADDR + 0x04)
-+#define SSI2_SRX0 (SSI2_BASE_ADDR + 0x08)
-+#define SSI2_SRX0_PHYS __PHYS_REG(SSI2_BASE_ADDR + 0x08)
-+#define SSI2_SRX1 (SSI2_BASE_ADDR + 0x0c)
-+#define SSI2_SRX1_PHYS __PHYS_REG(SSI2_BASE_ADDR + 0x0c)
-+#define SSI2_SCR (SSI2_BASE_ADDR + 0x10)
-+#define SSI2_SISR (SSI2_BASE_ADDR + 0x14)
-+#define SSI2_SIER (SSI2_BASE_ADDR + 0x18)
-+#define SSI2_STCR (SSI2_BASE_ADDR + 0x1c)
-+#define SSI2_SRCR (SSI2_BASE_ADDR + 0x20)
-+#define SSI2_STCCR (SSI2_BASE_ADDR + 0x24)
-+#define SSI2_SRCCR (SSI2_BASE_ADDR + 0x28)
-+#define SSI2_SFCSR (SSI2_BASE_ADDR + 0x2c)
-+#define SSI2_STR (SSI2_BASE_ADDR + 0x30)
-+#define SSI2_SOR (SSI2_BASE_ADDR + 0x34)
-+#define SSI2_SACNT (SSI2_BASE_ADDR + 0x38)
-+#define SSI2_SACADD (SSI2_BASE_ADDR + 0x3c)
-+#define SSI2_SACDAT (SSI2_BASE_ADDR + 0x40)
-+#define SSI2_SATAG (SSI2_BASE_ADDR + 0x44)
-+#define SSI2_STMSK (SSI2_BASE_ADDR + 0x48)
-+#define SSI2_SRMSK (SSI2_BASE_ADDR + 0x4c)
-+
-+#define SSI_SCR_CLK_IST (1 << 9)
-+#define SSI_SCR_TCH_EN (1 << 8)
-+#define SSI_SCR_SYS_CLK_EN (1 << 7)
-+#define SSI_SCR_I2S_MODE_NORM (0 << 5)
-+#define SSI_SCR_I2S_MODE_MSTR (1 << 5)
-+#define SSI_SCR_I2S_MODE_SLAVE (2 << 5)
-+#define SSI_SCR_SYN (1 << 4)
-+#define SSI_SCR_NET (1 << 3)
-+#define SSI_SCR_RE (1 << 2)
-+#define SSI_SCR_TE (1 << 1)
-+#define SSI_SCR_SSIEN (1 << 0)
-+
-+#define SSI_SISR_CMDAU (1 << 18)
-+#define SSI_SISR_CMDDU (1 << 17)
-+#define SSI_SISR_RXT (1 << 16)
-+#define SSI_SISR_RDR1 (1 << 15)
-+#define SSI_SISR_RDR0 (1 << 14)
-+#define SSI_SISR_TDE1 (1 << 13)
-+#define SSI_SISR_TDE0 (1 << 12)
-+#define SSI_SISR_ROE1 (1 << 11)
-+#define SSI_SISR_ROE0 (1 << 10)
-+#define SSI_SISR_TUE1 (1 << 9)
-+#define SSI_SISR_TUE0 (1 << 8)
-+#define SSI_SISR_TFS (1 << 7)
-+#define SSI_SISR_RFS (1 << 6)
-+#define SSI_SISR_TLS (1 << 5)
-+#define SSI_SISR_RLS (1 << 4)
-+#define SSI_SISR_RFF1 (1 << 3)
-+#define SSI_SISR_RFF0 (1 << 2)
-+#define SSI_SISR_TFE1 (1 << 1)
-+#define SSI_SISR_TFE0 (1 << 0)
-+
-+#define SSI_SIER_RDMAE (1 << 22)
-+#define SSI_SIER_RIE (1 << 21)
-+#define SSI_SIER_TDMAE (1 << 20)
-+#define SSI_SIER_TIE (1 << 19)
-+#define SSI_SIER_CMDAU_EN (1 << 18)
-+#define SSI_SIER_CMDDU_EN (1 << 17)
-+#define SSI_SIER_RXT_EN (1 << 16)
-+#define SSI_SIER_RDR1_EN (1 << 15)
-+#define SSI_SIER_RDR0_EN (1 << 14)
-+#define SSI_SIER_TDE1_EN (1 << 13)
-+#define SSI_SIER_TDE0_EN (1 << 12)
-+#define SSI_SIER_ROE1_EN (1 << 11)
-+#define SSI_SIER_ROE0_EN (1 << 10)
-+#define SSI_SIER_TUE1_EN (1 << 9)
-+#define SSI_SIER_TUE0_EN (1 << 8)
-+#define SSI_SIER_TFS_EN (1 << 7)
-+#define SSI_SIER_RFS_EN (1 << 6)
-+#define SSI_SIER_TLS_EN (1 << 5)
-+#define SSI_SIER_RLS_EN (1 << 4)
-+#define SSI_SIER_RFF1_EN (1 << 3)
-+#define SSI_SIER_RFF0_EN (1 << 2)
-+#define SSI_SIER_TFE1_EN (1 << 1)
-+#define SSI_SIER_TFE0_EN (1 << 0)
-+
-+#define SSI_STCR_TXBIT0 (1 << 9)
-+#define SSI_STCR_TFEN1 (1 << 8)
-+#define SSI_STCR_TFEN0 (1 << 7)
-+#define SSI_STCR_TFDIR (1 << 6)
-+#define SSI_STCR_TXDIR (1 << 5)
-+#define SSI_STCR_TSHFD (1 << 4)
-+#define SSI_STCR_TSCKP (1 << 3)
-+#define SSI_STCR_TFSI (1 << 2)
-+#define SSI_STCR_TFSL (1 << 1)
-+#define SSI_STCR_TEFS (1 << 0)
-+
-+#define SSI_SRCR_RXBIT0 (1 << 9)
-+#define SSI_SRCR_RFEN1 (1 << 8)
-+#define SSI_SRCR_RFEN0 (1 << 7)
-+#define SSI_SRCR_RFDIR (1 << 6)
-+#define SSI_SRCR_RXDIR (1 << 5)
-+#define SSI_SRCR_RSHFD (1 << 4)
-+#define SSI_SRCR_RSCKP (1 << 3)
-+#define SSI_SRCR_RFSI (1 << 2)
-+#define SSI_SRCR_RFSL (1 << 1)
-+#define SSI_SRCR_REFS (1 << 0)
-+
-+#define SSI_STCCR_DIV2 (1 << 18)
-+#define SSI_STCCR_PSR (1 << 15)
-+#define SSI_STCCR_WL(x) ((((x) - 2) >> 1) << 13)
-+#define SSI_STCCR_DC(x) (((x) & 0x1f) << 8)
-+#define SSI_STCCR_PM(x) (((x) & 0xff) << 0)
-+
-+#define SSI_SRCCR_DIV2 (1 << 18)
-+#define SSI_SRCCR_PSR (1 << 15)
-+#define SSI_SRCCR_WL(x) ((((x) - 2) >> 1) << 13)
-+#define SSI_SRCCR_DC(x) (((x) & 0x1f) << 8)
-+#define SSI_SRCCR_PM(x) (((x) & 0xff) << 0)
-+
-+
-+#define SSI_SFCSR_RFCNT1(x) (((x) & 0xf) << 28)
-+#define SSI_SFCSR_TFCNT1(x) (((x) & 0xf) << 24)
-+#define SSI_SFCSR_RFWM1(x) (((x) & 0xf) << 20)
-+#define SSI_SFCSR_TFWM1(x) (((x) & 0xf) << 16)
-+#define SSI_SFCSR_RFCNT0(x) (((x) & 0xf) << 12)
-+#define SSI_SFCSR_TFCNT0(x) (((x) & 0xf) << 8)
-+#define SSI_SFCSR_RFWM0(x) (((x) & 0xf) << 4)
-+#define SSI_SFCSR_TFWM0(x) (((x) & 0xf) << 0)
-+
-+#define SSI_STR_TEST (1 << 15)
-+#define SSI_STR_RCK2TCK (1 << 14)
-+#define SSI_STR_RFS2TFS (1 << 13)
-+#define SSI_STR_RXSTATE(x) (((x) & 0xf) << 8)
-+#define SSI_STR_TXD2RXD (1 << 7)
-+#define SSI_STR_TCK2RCK (1 << 6)
-+#define SSI_STR_TFS2RFS (1 << 5)
-+#define SSI_STR_TXSTATE(x) (((x) & 0xf) << 0)
-+
-+#define SSI_SOR_CLKOFF (1 << 6)
-+#define SSI_SOR_RX_CLR (1 << 5)
-+#define SSI_SOR_TX_CLR (1 << 4)
-+#define SSI_SOR_INIT (1 << 3)
-+#define SSI_SOR_WAIT(x) (((x) & 0x3) << 1)
-+#define SSI_SOR_SYNRST (1 << 0)
-+
-+#define SSI_SACNT_FRDIV(x) (((x) & 0x3f) << 5)
-+#define SSI_SACNT_WR (x << 4)
-+#define SSI_SACNT_RD (x << 3)
-+#define SSI_SACNT_TIF (x << 2)
-+#define SSI_SACNT_FV (x << 1)
-+#define SSI_SACNT_AC97EN (x << 0)
-+
-+
-+/* AUDMUX registers */
-+#define AUDMUX_HPCR1 (IMX_AUDMUX_BASE + 0x00)
-+#define AUDMUX_HPCR2 (IMX_AUDMUX_BASE + 0x04)
-+#define AUDMUX_HPCR3 (IMX_AUDMUX_BASE + 0x08)
-+#define AUDMUX_PPCR1 (IMX_AUDMUX_BASE + 0x10)
-+#define AUDMUX_PPCR2 (IMX_AUDMUX_BASE + 0x14)
-+#define AUDMUX_PPCR3 (IMX_AUDMUX_BASE + 0x18)
-+
-+#define AUDMUX_HPCR_TFSDIR (1 << 31)
-+#define AUDMUX_HPCR_TCLKDIR (1 << 30)
-+#define AUDMUX_HPCR_TFCSEL_TX (0 << 26)
-+#define AUDMUX_HPCR_TFCSEL_RX (8 << 26)
-+#define AUDMUX_HPCR_TFCSEL(x) (((x) & 0x7) << 26)
-+#define AUDMUX_HPCR_RFSDIR (1 << 25)
-+#define AUDMUX_HPCR_RCLKDIR (1 << 24)
-+#define AUDMUX_HPCR_RFCSEL_TX (0 << 20)
-+#define AUDMUX_HPCR_RFCSEL_RX (8 << 20)
-+#define AUDMUX_HPCR_RFCSEL(x) (((x) & 0x7) << 20)
-+#define AUDMUX_HPCR_RXDSEL(x) (((x) & 0x7) << 13)
-+#define AUDMUX_HPCR_SYN (1 << 12)
-+#define AUDMUX_HPCR_TXRXEN (1 << 10)
-+#define AUDMUX_HPCR_INMEN (1 << 8)
-+#define AUDMUX_HPCR_INMMASK(x) (((x) & 0xff) << 0)
-+
-+#define AUDMUX_PPCR_TFSDIR (1 << 31)
-+#define AUDMUX_PPCR_TCLKDIR (1 << 30)
-+#define AUDMUX_PPCR_TFCSEL_TX (0 << 26)
-+#define AUDMUX_PPCR_TFCSEL_RX (8 << 26)
-+#define AUDMUX_PPCR_TFCSEL(x) (((x) & 0x7) << 26)
-+#define AUDMUX_PPCR_RFSDIR (1 << 25)
-+#define AUDMUX_PPCR_RCLKDIR (1 << 24)
-+#define AUDMUX_PPCR_RFCSEL_TX (0 << 20)
-+#define AUDMUX_PPCR_RFCSEL_RX (8 << 20)
-+#define AUDMUX_PPCR_RFCSEL(x) (((x) & 0x7) << 20)
-+#define AUDMUX_PPCR_RXDSEL(x) (((x) & 0x7) << 13)
-+#define AUDMUX_PPCR_SYN (1 << 12)
-+#define AUDMUX_PPCR_TXRXEN (1 << 10)
-+
-+#define SDMA_TXFIFO_WATERMARK 0x4
-+#define SDMA_RXFIFO_WATERMARK 0x6
-+
-+struct mxc_pcm_dma_params {
-+ char *name; /* stream identifier */
-+ dma_channel_params params;
-+};
-+
-+extern struct snd_soc_cpu_dai mxc_ssi_dai[3];
-+
-+/* platform data */
-+extern struct snd_soc_platform mxc_soc_platform;
-+extern struct snd_ac97_bus_ops mxc_ac97_ops;
-+
-+#endif
-Index: linux-2.6.21-moko/sound/soc/pxa/magician.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/pxa/magician.c
-@@ -0,0 +1,563 @@
-+/*
-+ * SoC audio for HTC Magician
-+ *
-+ * Copyright (c) 2006 Philipp Zabel <philipp.zabel at gmail.com>
-+ *
-+ * based on spitz.c,
-+ * Authors: Liam Girdwood <liam.girdwood at wolfsonmicro.com>
-+ * Richard Purdie <richard at openedhand.com>
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or (at your
-+ * option) any later version.
-+ *
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/timer.h>
-+#include <linux/interrupt.h>
-+#include <linux/platform_device.h>
-+#include <linux/delay.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+
-+#include <asm/hardware/scoop.h>
-+#include <asm/arch/pxa-regs.h>
-+#include <asm/arch/hardware.h>
-+#include <asm/arch/magician.h>
-+#include <asm/arch/magician_cpld.h>
-+#include <asm/mach-types.h>
-+#include "../codecs/uda1380.h"
-+#include "pxa2xx-pcm.h"
-+#include "pxa2xx-i2s.h"
-+#include "pxa2xx-ssp.h"
-+
-+#define MAGICIAN_HP_OFF 0
-+#define MAGICIAN_HEADSET 1
-+#define MAGICIAN_HP 2
-+
-+#define MAGICIAN_SPK_ON 0
-+#define MAGICIAN_SPK_OFF 1
-+
-+#define MAGICIAN_MIC 0
-+#define MAGICIAN_MIC_EXT 1
-+#define MAGICIAN_BT_HS 2
-+
-+/*
-+ * SSP GPIO's
-+ */
-+#define GPIO26_SSP1RX_MD (26 | GPIO_ALT_FN_1_IN)
-+#define GPIO25_SSP1TX_MD (25 | GPIO_ALT_FN_2_OUT)
-+#define GPIO23_SSP1CLKS_MD (23 | GPIO_ALT_FN_2_IN)
-+#define GPIO24_SSP1FRMS_MD (24 | GPIO_ALT_FN_2_IN)
-+#define GPIO23_SSP1CLKM_MD (23 | GPIO_ALT_FN_2_OUT)
-+#define GPIO24_SSP1FRMM_MD (24 | GPIO_ALT_FN_2_OUT)
-+#define GPIO53_SSP1SYSCLK_MD (53 | GPIO_ALT_FN_2_OUT)
-+
-+static int magician_jack_func = MAGICIAN_HP_OFF;
-+static int magician_spk_func = MAGICIAN_SPK_ON;
-+static int magician_in_sel = MAGICIAN_MIC;
-+
-+extern struct platform_device magician_cpld;
-+
-+static void magician_ext_control(struct snd_soc_codec *codec)
-+{
-+ if (magician_spk_func == MAGICIAN_SPK_ON)
-+ snd_soc_dapm_set_endpoint(codec, "Speaker", 1);
-+ else
-+ snd_soc_dapm_set_endpoint(codec, "Speaker", 0);
-+
-+ /* set up jack connection */
-+ switch (magician_jack_func) {
-+ case MAGICIAN_HP:
-+ /* enable and unmute hp jack, disable mic bias */
-+ snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0);
-+ snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1);
-+ break;
-+ case MAGICIAN_HEADSET:
-+ /* enable mic jack and bias, mute hp */
-+ snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1);
-+ snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1);
-+ break;
-+ case MAGICIAN_HP_OFF:
-+ /* jack removed, everything off */
-+ snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0);
-+ snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0);
-+ break;
-+ }
-+#if 0
-+ /* fixme pH5, can we detect and config the correct Mic type ? */
-+ switch(magician_in_sel) {
-+ case MAGICIAN_IN_MIC:
-+ snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1);
-+ break;
-+ case MAGICIAN_IN_MIC_EXT:
-+ snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1);
-+ break;
-+ case MAGICIAN_IN_BT_HS:
-+ snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0);
-+ break;
-+ }
-+#endif
-+ snd_soc_dapm_sync_endpoints(codec);
-+}
-+
-+static int magician_startup(snd_pcm_substream_t *substream)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_codec *codec = rtd->socdev->codec;
-+
-+ /* check the jack status at stream startup */
-+ magician_ext_control(codec);
-+
-+ return 0;
-+}
-+
-+/*
-+ * Magician uses SSP port for playback.
-+ */
-+static int magician_playback_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
-+ unsigned int acps, acds, div4;
-+ int ret = 0;
-+
-+ /*
-+ * Rate = SSPSCLK / (word size(16))
-+ * SSPSCLK = (ACPS / ACDS) / SSPSCLKDIV(div4 or div1)
-+ */
-+ switch (params_rate(params)) {
-+ case 8000:
-+ acps = 32842000;
-+ acds = PXA2XX_SSP_CLK_AUDIO_DIV_32; /* wrong - 32 bits/sample */
-+ div4 = PXA2XX_SSP_CLK_SCDB_4;
-+ break;
-+ case 11025:
-+ acps = 5622000;
-+ acds = PXA2XX_SSP_CLK_AUDIO_DIV_8; /* 16 bits/sample, 1 slot */
-+ div4 = PXA2XX_SSP_CLK_SCDB_4;
-+ break;
-+ case 22050:
-+ acps = 5622000;
-+ acds = PXA2XX_SSP_CLK_AUDIO_DIV_4;
-+ div4 = PXA2XX_SSP_CLK_SCDB_4;
-+ break;
-+ case 44100:
-+ acps = 11345000;
-+ acds = PXA2XX_SSP_CLK_AUDIO_DIV_4;
-+ div4 = PXA2XX_SSP_CLK_SCDB_4;
-+ break;
-+ case 48000:
-+ acps = 12235000;
-+ acds = PXA2XX_SSP_CLK_AUDIO_DIV_4;
-+ div4 = PXA2XX_SSP_CLK_SCDB_4;
-+ break;
-+ }
-+
-+ /* set codec DAI configuration */
-+ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_MSB |
-+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set cpu DAI configuration */
-+ ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_MSB |
-+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set audio clock as clock source */
-+ ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_SSP_CLK_AUDIO, 0,
-+ SND_SOC_CLOCK_OUT);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set the SSP audio system clock ACDS divider */
-+ ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai,
-+ PXA2XX_SSP_AUDIO_DIV_ACDS, acds);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set the SSP audio system clock SCDB divider4 */
-+ ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai,
-+ PXA2XX_SSP_AUDIO_DIV_SCDB, div4);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set SSP audio pll clock */
-+ ret = cpu_dai->dai_ops.set_pll(cpu_dai, 0, 0, acps);
-+ if (ret < 0)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+/*
-+ * We have to enable the SSP port early so the UDA1380 can flush
-+ * it's register cache. The UDA1380 can only write it's interpolator and
-+ * decimator registers when the link is running.
-+ */
-+static int magician_playback_prepare(struct snd_pcm_substream *substream)
-+{
-+ /* enable SSP clock - is this needed ? */
-+ SSCR0_P(1) |= SSCR0_SSE;
-+
-+ /* FIXME: ENABLE I2S */
-+ SACR0 |= SACR0_BCKD;
-+ SACR0 |= SACR0_ENB;
-+ pxa_set_cken(CKEN8_I2S, 1);
-+
-+ return 0;
-+}
-+
-+static int magician_playback_hw_free(struct snd_pcm_substream *substream)
-+{
-+ /* FIXME: DISABLE I2S */
-+ SACR0 &= ~SACR0_ENB;
-+ SACR0 &= ~SACR0_BCKD;
-+ pxa_set_cken(CKEN8_I2S, 0);
-+ return 0;
-+}
-+
-+/*
-+ * Magician uses I2S for capture.
-+ */
-+static int magician_capture_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
-+ int ret = 0;
-+
-+ /* set codec DAI configuration */
-+ ret = codec_dai->dai_ops.set_fmt(codec_dai,
-+ SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set cpu DAI configuration */
-+ ret = cpu_dai->dai_ops.set_fmt(cpu_dai,
-+ SND_SOC_DAIFMT_MSB | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set the I2S system clock as output */
-+ ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, 0,
-+ SND_SOC_CLOCK_OUT);
-+ if (ret < 0)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+/*
-+ * We have to enable the I2S port early so the UDA1380 can flush
-+ * it's register cache. The UDA1380 can only write it's interpolator and
-+ * decimator registers when the link is running.
-+ */
-+static int magician_capture_prepare(struct snd_pcm_substream *substream)
-+{
-+ SACR0 |= SACR0_ENB;
-+ return 0;
-+}
-+
-+static struct snd_soc_ops magician_capture_ops = {
-+ .startup = magician_startup,
-+ .hw_params = magician_capture_hw_params,
-+ .prepare = magician_capture_prepare,
-+};
-+
-+static struct snd_soc_ops magician_playback_ops = {
-+ .startup = magician_startup,
-+ .hw_params = magician_playback_hw_params,
-+ .prepare = magician_playback_prepare,
-+ .hw_free = magician_playback_hw_free,
-+};
-+
-+static int magician_get_jack(snd_kcontrol_t * kcontrol,
-+ snd_ctl_elem_value_t * ucontrol)
-+{
-+ ucontrol->value.integer.value[0] = magician_jack_func;
-+ return 0;
-+}
-+
-+static int magician_set_jack(snd_kcontrol_t * kcontrol,
-+ snd_ctl_elem_value_t * ucontrol)
-+{
-+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-+
-+ if (magician_jack_func == ucontrol->value.integer.value[0])
-+ return 0;
-+
-+ magician_jack_func = ucontrol->value.integer.value[0];
-+ magician_ext_control(codec);
-+ return 1;
-+}
-+
-+static int magician_get_spk(snd_kcontrol_t * kcontrol,
-+ snd_ctl_elem_value_t * ucontrol)
-+{
-+ ucontrol->value.integer.value[0] = magician_spk_func;
-+ return 0;
-+}
-+
-+static int magician_set_spk(snd_kcontrol_t * kcontrol,
-+ snd_ctl_elem_value_t * ucontrol)
-+{
-+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-+
-+ if (magician_spk_func == ucontrol->value.integer.value[0])
-+ return 0;
-+
-+ magician_spk_func = ucontrol->value.integer.value[0];
-+ magician_ext_control(codec);
-+ return 1;
-+}
-+
-+static int magician_get_input(snd_kcontrol_t * kcontrol,
-+ snd_ctl_elem_value_t * ucontrol)
-+{
-+ ucontrol->value.integer.value[0] = magician_in_sel;
-+ return 0;
-+}
-+
-+static int magician_set_input(snd_kcontrol_t * kcontrol,
-+ snd_ctl_elem_value_t * ucontrol)
-+{
-+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-+
-+ if (magician_in_sel == ucontrol->value.integer.value[0])
-+ return 0;
-+
-+ magician_in_sel = ucontrol->value.integer.value[0];
-+
-+ switch (magician_in_sel) {
-+ case MAGICIAN_MIC:
-+ magician_egpio_disable(&magician_cpld,
-+ EGPIO_NR_MAGICIAN_IN_SEL0);
-+ magician_egpio_enable(&magician_cpld,
-+ EGPIO_NR_MAGICIAN_IN_SEL1);
-+ break;
-+ case MAGICIAN_MIC_EXT:
-+ magician_egpio_disable(&magician_cpld,
-+ EGPIO_NR_MAGICIAN_IN_SEL0);
-+ magician_egpio_disable(&magician_cpld,
-+ EGPIO_NR_MAGICIAN_IN_SEL1);
-+ }
-+
-+ return 1;
-+}
-+
-+static int magician_spk_power(struct snd_soc_dapm_widget *w, int event)
-+{
-+ if (SND_SOC_DAPM_EVENT_ON(event))
-+ magician_egpio_enable(&magician_cpld,
-+ EGPIO_NR_MAGICIAN_SPK_POWER);
-+ else
-+ magician_egpio_disable(&magician_cpld,
-+ EGPIO_NR_MAGICIAN_SPK_POWER);
-+ return 0;
-+}
-+
-+static int magician_hp_power(struct snd_soc_dapm_widget *w, int event)
-+{
-+ if (SND_SOC_DAPM_EVENT_ON(event))
-+ magician_egpio_enable(&magician_cpld,
-+ EGPIO_NR_MAGICIAN_EP_POWER);
-+ else
-+ magician_egpio_disable(&magician_cpld,
-+ EGPIO_NR_MAGICIAN_EP_POWER);
-+ return 0;
-+}
-+
-+static int magician_mic_bias(struct snd_soc_dapm_widget *w, int event)
-+{
-+ if (SND_SOC_DAPM_EVENT_ON(event))
-+ magician_egpio_enable(&magician_cpld,
-+ EGPIO_NR_MAGICIAN_MIC_POWER);
-+ else
-+ magician_egpio_disable(&magician_cpld,
-+ EGPIO_NR_MAGICIAN_MIC_POWER);
-+ return 0;
-+}
-+
-+/* magician machine dapm widgets */
-+static const struct snd_soc_dapm_widget uda1380_dapm_widgets[] = {
-+ SND_SOC_DAPM_HP("Headphone Jack", magician_hp_power),
-+ SND_SOC_DAPM_MIC("Mic Jack", magician_mic_bias),
-+ SND_SOC_DAPM_SPK("Speaker", magician_spk_power),
-+};
-+
-+/* magician machine audio_map */
-+static const char *audio_map[][3] = {
-+
-+ /* headphone connected to VOUTLHP, VOUTRHP */
-+ {"Headphone Jack", NULL, "VOUTLHP"},
-+ {"Headphone Jack", NULL, "VOUTRHP"},
-+
-+ /* ext speaker connected to VOUTL, VOUTR */
-+ {"Speaker", NULL, "VOUTL"},
-+ {"Speaker", NULL, "VOUTR"},
-+
-+ /* mic is connected to VINM */
-+ {"VINM", NULL, "Mic Jack"},
-+
-+ /* line is connected to VINL, VINR */
-+ {"VINL", NULL, "Line Jack"},
-+ {"VINR", NULL, "Line Jack"},
-+
-+ {NULL, NULL, NULL},
-+};
-+
-+static const char *jack_function[] = { "Off", "Headset", "Headphone" };
-+static const char *spk_function[] = { "On", "Off" };
-+static const char *input_select[] = { "Internal Mic", "External Mic" };
-+static const struct soc_enum magician_enum[] = {
-+ SOC_ENUM_SINGLE_EXT(4, jack_function),
-+ SOC_ENUM_SINGLE_EXT(2, spk_function),
-+ SOC_ENUM_SINGLE_EXT(2, input_select),
-+};
-+
-+static const struct snd_kcontrol_new uda1380_magician_controls[] = {
-+ SOC_ENUM_EXT("Jack Function", magician_enum[0], magician_get_jack,
-+ magician_set_jack),
-+ SOC_ENUM_EXT("Speaker Function", magician_enum[1], magician_get_spk,
-+ magician_set_spk),
-+ SOC_ENUM_EXT("Input Select", magician_enum[2], magician_get_input,
-+ magician_set_input),
-+};
-+
-+/*
-+ * Logic for a uda1380 as connected on a HTC Magician
-+ */
-+static int magician_uda1380_init(struct snd_soc_codec *codec)
-+{
-+ int i, err;
-+
-+ /* NC codec pins */
-+ snd_soc_dapm_set_endpoint(codec, "VOUTLHP", 0);
-+ snd_soc_dapm_set_endpoint(codec, "VOUTRHP", 0);
-+
-+ /* Add magician specific controls */
-+ for (i = 0; i < ARRAY_SIZE(uda1380_magician_controls); i++) {
-+ if ((err = snd_ctl_add(codec->card,
-+ snd_soc_cnew(&uda1380_magician_controls[i],
-+ codec, NULL))) < 0)
-+ return err;
-+ }
-+
-+ /* Add magician specific widgets */
-+ for (i = 0; i < ARRAY_SIZE(uda1380_dapm_widgets); i++) {
-+ snd_soc_dapm_new_control(codec, &uda1380_dapm_widgets[i]);
-+ }
-+
-+ /* Set up magician specific audio path interconnects */
-+ for (i = 0; audio_map[i][0] != NULL; i++) {
-+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
-+ audio_map[i][1], audio_map[i][2]);
-+ }
-+
-+ snd_soc_dapm_sync_endpoints(codec);
-+ return 0;
-+}
-+
-+/* magician digital audio interface glue - connects codec <--> CPU */
-+static struct snd_soc_dai_link magician_dai[] = {
-+{
-+ .name = "uda1380",
-+ .stream_name = "UDA1380 Playback",
-+ .cpu_dai = &pxa_ssp_dai[0],
-+ .codec_dai = &uda1380_dai[UDA1380_DAI_PLAYBACK],
-+ .init = magician_uda1380_init,
-+ .ops = &magician_playback_ops,
-+},
-+{
-+ .name = "uda1380",
-+ .stream_name = "UDA1380 Capture",
-+ .cpu_dai = &pxa_i2s_dai,
-+ .codec_dai = &uda1380_dai[UDA1380_DAI_CAPTURE],
-+ .ops = &magician_capture_ops,
-+}
-+};
-+
-+/* magician audio machine driver */
-+static struct snd_soc_machine snd_soc_machine_magician = {
-+ .name = "Magician",
-+ .dai_link = magician_dai,
-+ .num_links = ARRAY_SIZE(magician_dai),
-+};
-+
-+/* magician audio private data */
-+static struct uda1380_setup_data magician_uda1380_setup = {
-+ .i2c_address = 0x18,
-+ .dac_clk = UDA1380_DAC_CLK_WSPLL,
-+};
-+
-+/* magician audio subsystem */
-+static struct snd_soc_device magician_snd_devdata = {
-+ .machine = &snd_soc_machine_magician,
-+ .platform = &pxa2xx_soc_platform,
-+ .codec_dev = &soc_codec_dev_uda1380,
-+ .codec_data = &magician_uda1380_setup,
-+};
-+
-+static struct platform_device *magician_snd_device;
-+
-+static int __init magician_init(void)
-+{
-+ int ret;
-+
-+ if (!machine_is_magician())
-+ return -ENODEV;
-+
-+ magician_egpio_enable(&magician_cpld, EGPIO_NR_MAGICIAN_CODEC_POWER);
-+
-+ /* we may need to have the clock running here - pH5 */
-+ magician_egpio_enable(&magician_cpld, EGPIO_NR_MAGICIAN_CODEC_RESET);
-+ udelay(5);
-+ magician_egpio_disable(&magician_cpld, EGPIO_NR_MAGICIAN_CODEC_RESET);
-+
-+ magician_snd_device = platform_device_alloc("soc-audio", -1);
-+ if (!magician_snd_device)
-+ return -ENOMEM;
-+
-+ platform_set_drvdata(magician_snd_device, &magician_snd_devdata);
-+ magician_snd_devdata.dev = &magician_snd_device->dev;
-+ ret = platform_device_add(magician_snd_device);
-+
-+ if (ret)
-+ platform_device_put(magician_snd_device);
-+
-+ pxa_gpio_mode(GPIO53_SSP1SYSCLK_MD);
-+ pxa_gpio_mode(GPIO26_SSP1RX_MD);
-+ pxa_gpio_mode(GPIO25_SSP1TX_MD);
-+ pxa_gpio_mode(GPIO23_SSP1CLKM_MD);
-+ pxa_gpio_mode(GPIO24_SSP1FRMM_MD);
-+
-+ return ret;
-+}
-+
-+static void __exit magician_exit(void)
-+{
-+ platform_device_unregister(magician_snd_device);
-+
-+ magician_egpio_disable(&magician_cpld, EGPIO_NR_MAGICIAN_SPK_POWER);
-+ magician_egpio_disable(&magician_cpld, EGPIO_NR_MAGICIAN_EP_POWER);
-+ magician_egpio_disable(&magician_cpld, EGPIO_NR_MAGICIAN_MIC_POWER);
-+ magician_egpio_disable(&magician_cpld, EGPIO_NR_MAGICIAN_CODEC_POWER);
-+}
-+
-+module_init(magician_init);
-+module_exit(magician_exit);
-+
-+MODULE_AUTHOR("Philipp Zabel");
-+MODULE_DESCRIPTION("ALSA SoC Magician");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/templates/template-ac97.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/templates/template-ac97.c
-@@ -0,0 +1,270 @@
-+/*
-+ * ltemplate-ac97.c -- AC97 support for the xxx chip.
-+ *
-+ * 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.
-+ */
-+
-+#include <linux/init.h>
-+#include <linux/module.h>
-+#include <linux/platform_device.h>
-+#include <linux/interrupt.h>
-+#include <linux/wait.h>
-+#include <linux/delay.h>
-+#include <linux/mutex.h>
-+
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/ac97_codec.h>
-+#include <sound/initval.h>
-+#include <sound/soc.h>
-+
-+#include <asm/irq.h>
-+#include <asm/hardware.h>
-+
-+#include "template-pcm.h"
-+
-+#define AC97_DIR \
-+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
-+
-+#define AC97_RATES \
-+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
-+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
-+
-+/* DAI description of AC97 controllers capabilities */
-+static struct snd_soc_dai_mode template_ac97_modes[] = {
-+ {
-+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
-+ .pcmrate = AC97_RATES,
-+ .pcmdir = AC97_DIR,
-+ },
-+};
-+
-+/* AC97 controlller reads codec register */
-+static unsigned short template_ac97_read(struct snd_ac97 *ac97,
-+ unsigned short reg)
-+{
-+}
-+
-+/* AC97 controller writes to codec register */
-+static void template_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
-+ unsigned short val)
-+{
-+}
-+
-+/* AC97 controller asserts a warm reset */
-+static void template_ac97_warm_reset(struct snd_ac97 *ac97)
-+{
-+}
-+
-+/* AC97 controller asserts a cold reset */
-+static void template_ac97_cold_reset(struct snd_ac97 *ac97)
-+{
-+}
-+
-+/* AC97 controller operations */
-+struct snd_ac97_bus_ops soc_ac97_ops = {
-+ .read = template_ac97_read,
-+ .write = template_ac97_write,
-+ .warm_reset = template_ac97_warm_reset,
-+ .reset = template_ac97_cold_reset,
-+};
-+EXPORT_SYMBOL_GPL(soc_ac97_ops);
-+
-+/* DMA structure describing platform specific AC97 DMA for each logical DAI */
-+static struct template_pcm_dma_params template_ac97_pcm_stereo_out = {
-+ .name = "AC97 PCM Stereo out",
-+ .dev_addr = __PREG(PCDR),
-+};
-+
-+static struct template_pcm_dma_params template_ac97_pcm_stereo_in = {
-+ .name = "AC97 PCM Stereo in",
-+ .dev_addr = __PREG(PCDR),
-+};
-+
-+static struct template_pcm_dma_params template_ac97_pcm_aux_mono_out = {
-+ .name = "AC97 Aux PCM (Slot 5) Mono out",
-+ .dev_addr = __PREG(MODR),
-+};
-+
-+static struct template_pcm_dma_params template_ac97_pcm_aux_mono_in = {
-+ .name = "AC97 Aux PCM (Slot 5) Mono in",
-+ .dev_addr = __PREG(MODR),
-+};
-+
-+static struct template_pcm_dma_params template_ac97_pcm_mic_mono_in = {
-+ .name = "AC97 Mic PCM (Slot 6) Mono in",
-+ .dev_addr = __PREG(MCDR),
-+};
-+
-+#ifdef CONFIG_PM
-+/* suspend the AC97 controller */
-+static int template_ac97_suspend(struct platform_device *pdev,
-+ struct snd_soc_cpu_dai *dai)
-+{
-+}
-+
-+/* resume the AC97 controller */
-+static int template_ac97_resume(struct platform_device *pdev,
-+ struct snd_soc_cpu_dai *dai)
-+{
-+}
-+
-+#else
-+#define template_ac97_suspend NULL
-+#define template_ac97_resume NULL
-+#endif
-+
-+/*
-+ * Probe initialises the AC97 controller. e.g.
-+ * request any IRQ's
-+ * configure GPIO's
-+ * enable any clocks
-+ */
-+static int template_ac97_probe(struct platform_device *pdev)
-+{
-+}
-+
-+/*
-+ * Free's resources setup in probe()
-+ */
-+static void template_ac97_remove(struct platform_device *pdev)
-+{
-+}
-+
-+/*
-+ * Alsa operations
-+ * Only implement the required operations for your platform.
-+ * These operations are specific to the AC97 controller and DAI only.
-+ */
-+
-+ /*
-+ * Called by ALSA when a PCM substream is opened, private data can be allocated.
-+ */
-+static int template_ac97_startup(struct snd_pcm_substream *substream)
-+{
-+}
-+
-+/*
-+ * Called by ALSA when a PCM substream is closed. Private data can be
-+ * freed here.
-+ */
-+static int template_ac97_shutdown(struct snd_pcm_substream *substream)
-+{
-+}
-+
-+/*
-+ * Called by ALSA when the PCM substream is prepared, can set format, sample
-+ * rate, etc. This function is non atomic and can be called multiple times,
-+ * it can refer to the runtime info.
-+ */
-+static int template_ac97_prepare(struct snd_pcm_substream *substream)
-+{
-+}
-+
-+/*
-+ * Called by ALSA when the hardware params are set by application. This
-+ * function can also be called multiple times and can allocate buffers
-+ * (using snd_pcm_lib_* ). It's non-atomic.
-+ */
-+static int template_ac97_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+}
-+
-+/*
-+ * Free's resources allocated by hw_params, can be called multiple times
-+ */
-+static int template_ac97_hw_free(struct snd_pcm_substream *substream)
-+{
-+}
-+
-+/*
-+ * Starts (Triggers) audio playback or capture.
-+ * Usually only needed for DMA
-+ */
-+static int template_ac97_trigger(struct snd_pcm_substream *substream, int cmd)
-+{
-+}
-+
-+/*
-+ * Define each AC97 slot grouping as a DAI.
-+ *
-+ * e.g. (below)
-+ * Slots 3 & 4 = HiFi DAI
-+ * Slot 5 = Aux playback
-+ * Slot 7 = Mic Capture
-+ *
-+ * This gives 3 logical DAI's on the 1 physical AC97 DAI.
-+ *
-+ */
-+struct snd_soc_cpu_dai template_ac97_dai[] = {
-+{
-+ .name = "template-ac97-HiFi",
-+ .id = 0,
-+ .type = SND_SOC_DAI_AC97,
-+ /* DAI driver operations - only needed on 1st logical DAI in AC97 */
-+ .probe = template_ac97_probe,
-+ .remove = template_ac97_remove,
-+ .suspend = template_ac97_suspend,
-+ .resume = template_ac97_resume,
-+ /* playback and capture stream info */
-+ .playback = {
-+ .stream_name = "AC97 Playback",
-+ .channels_min = 2,
-+ .channels_max = 2,},
-+ .capture = {
-+ .stream_name = "AC97 Capture",
-+ .channels_min = 2,
-+ .channels_max = 2,},
-+ /* alsa PCM operations */
-+ .ops = {
-+ .startup = template_ac97_startup,
-+ .shutdown = template_ac97_shutdown,
-+ .prepare = template_ac97_prepare,
-+ .trigger = template_ac97_trigger,
-+ .hw_params = template_ac97_hw_params,
-+ .hw_free = template_ac97_hw_free,},
-+ /* DAI capabilities */
-+ .caps = {
-+ .num_modes = ARRAY_SIZE(template_ac97_modes),
-+ .mode = template_ac97_modes,},
-+},
-+/* AC97 AUX playback - not supported on all controllers */
-+{
-+ .name = "template-ac97-aux",
-+ .id = 1,
-+ .type = SND_SOC_DAI_AC97,
-+ .playback = {
-+ .stream_name = "AC97 Aux Playback",
-+ .channels_min = 1,
-+ .channels_max = 1,},
-+ .capture = {
-+ .stream_name = "AC97 Aux Capture",
-+ .channels_min = 1,
-+ .channels_max = 1,},
-+ .ops = {
-+ .hw_params = template_ac97_hw_aux_params,},
-+ .caps = {
-+ .num_modes = ARRAY_SIZE(template_ac97_modes),
-+ .mode = template_ac97_modes,},
-+},
-+/* AC97 Mic capture - not supported on all controllers */
-+{
-+ .name = "template-ac97-mic",
-+ .id = 2,
-+ .type = SND_SOC_DAI_AC97,
-+ .capture = {
-+ .stream_name = "AC97 Mic Capture",
-+ .channels_min = 1,
-+ .channels_max = 1,},
-+ .ops = {
-+ .hw_params = template_ac97_hw_mic_params,},
-+ .caps = {
-+ .num_modes = ARRAY_SIZE(template_ac97_modes),
-+ .mode = template_ac97_modes,},},
-+};
-+EXPORT_SYMBOL_GPL(template_ac97_dai);
-+
-Index: linux-2.6.21-moko/sound/soc/templates/template-codec.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/templates/template-codec.c
-@@ -0,0 +1,784 @@
-+/*
-+ * template-codec.c -- Template Codec Audio driver
-+ *
-+ * 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.
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/moduleparam.h>
-+#include <linux/init.h>
-+#include <linux/delay.h>
-+#include <linux/pm.h>
-+#include <linux/i2c.h>
-+#include <linux/platform_device.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/pcm_params.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+#include <sound/initval.h>
-+
-+#include "template-codec.h"
-+
-+#define AUDIO_NAME "template-codec"
-+#define TEMPLATE_VERSION "0.1"
-+
-+/*
-+ * Debug
-+ */
-+
-+#define TEMPLATE_DEBUG 0
-+
-+#ifdef TEMPLATE_DEBUG
-+#define dbg(format, arg...) \
-+ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
-+#else
-+#define dbg(format, arg...) do {} while (0)
-+#endif
-+#define err(format, arg...) \
-+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
-+#define info(format, arg...) \
-+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
-+#define warn(format, arg...) \
-+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
-+
-+struct snd_soc_codec_device soc_codec_dev_template_codec;
-+
-+/*
-+ * template_codec register cache
-+ */
-+static const u16 template_codec_reg[TEMPLATE_CACHEREGNUM] = {
-+ 0x0097, 0x0097, 0x0079, 0x0079,
-+ 0x000a, 0x0008, 0x009f, 0x000a,
-+ 0x0000, 0x0000
-+};
-+
-+/* Codec DAI can support these hardware formats */
-+#define TEMPLATE_DAIFMT \
-+ (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_RIGHT_J | \
-+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_IB_NF | \
-+ SND_SOC_DAIFMT_IB_IF)
-+
-+/* Codec DAI supports direction */
-+#define TEMPLATE_DIR \
-+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
-+
-+/* Codec DAI supports rates */
-+#define TEMPLATE_RATES \
-+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
-+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
-+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
-+
-+/* Codec DAI supports PCM word sizes */
-+#define TEMPLATE_HIFI_BITS \
-+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
-+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
-+
-+/*
-+ * Description of supported codec DAI supported modes.
-+ */
-+static struct snd_soc_dai_mode template_codec_modes[] = {
-+ /* codec frame and clock master modes */
-+ /* 8k */
-+ {
-+ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
-+ .pcmfmt = TEMPLATE_HIFI_BITS,
-+ .pcmrate = SNDRV_PCM_RATE_8000,
-+ .pcmdir = TEMPLATE_DIR,
-+ .flags = SND_SOC_DAI_BFS_RATE,
-+ .fs = 1536,
-+ .bfs = 64,
-+ },
-+ {
-+ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
-+ .pcmfmt = TEMPLATE_HIFI_BITS,
-+ .pcmrate = SNDRV_PCM_RATE_8000,
-+ .pcmdir = TEMPLATE_DIR,
-+ .flags = SND_SOC_DAI_BFS_RATE,
-+ .fs = 2304,
-+ .bfs = 64,
-+ },
-+ {
-+ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
-+ .pcmfmt = TEMPLATE_HIFI_BITS,
-+ .pcmrate = SNDRV_PCM_RATE_8000,
-+ .pcmdir = TEMPLATE_DIR,
-+ .flags = SND_SOC_DAI_BFS_RATE,
-+ .fs = 1408,
-+ .bfs = 64,
-+ },
-+ {
-+ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
-+ .pcmfmt = TEMPLATE_HIFI_BITS,
-+ .pcmrate = SNDRV_PCM_RATE_8000,
-+ .pcmdir = TEMPLATE_DIR,
-+ .flags = SND_SOC_DAI_BFS_RATE,
-+ .fs = 2112,
-+ .bfs = 64,
-+ },
-+
-+ /* 32k */
-+ {
-+ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
-+ .pcmfmt = TEMPLATE_HIFI_BITS,
-+ .pcmrate = SNDRV_PCM_RATE_32000,
-+ .pcmdir = TEMPLATE_DIR,
-+ .flags = SND_SOC_DAI_BFS_RATE,
-+ .fs = 384,
-+ .bfs = 64,
-+ },
-+ {
-+ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
-+ .pcmfmt = TEMPLATE_HIFI_BITS,
-+ .pcmrate = SNDRV_PCM_RATE_32000,
-+ .pcmdir = TEMPLATE_DIR,
-+ .flags = SND_SOC_DAI_BFS_RATE,
-+ .fs = 576,
-+ .bfs = 64,
-+ },
-+
-+ /* 44.1k & 48k */
-+ {
-+ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
-+ .pcmfmt = TEMPLATE_HIFI_BITS,
-+ .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
-+ .pcmdir = TEMPLATE_DIR,
-+ .flags = SND_SOC_DAI_BFS_RATE,
-+ .fs = 256,
-+ .bfs = 64,
-+ },
-+ {
-+ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
-+ .pcmfmt = TEMPLATE_HIFI_BITS,
-+ .pcmrate = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
-+ .pcmdir = TEMPLATE_DIR,
-+ .flags = SND_SOC_DAI_BFS_RATE,
-+ .fs = 384,
-+ .bfs = 64,
-+ },
-+
-+ /* 88.2 & 96k */
-+ {
-+ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
-+ .pcmfmt = TEMPLATE_HIFI_BITS,
-+ .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000,
-+ .pcmdir = TEMPLATE_DIR,
-+ .flags = SND_SOC_DAI_BFS_RATE,
-+ .fs = 128,
-+ .bfs = 64,
-+ },
-+ {
-+ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
-+ .pcmfmt = TEMPLATE_HIFI_BITS,
-+ .pcmrate = SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000,
-+ .pcmdir = TEMPLATE_DIR,
-+ .flags = SND_SOC_DAI_BFS_RATE,
-+ .fs = 192,
-+ .bfs = 64,
-+ },
-+
-+ /* USB codec frame and clock master modes */
-+ /* 8k */
-+ {
-+ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
-+ .pcmfmt = TEMPLATE_HIFI_BITS,
-+ .pcmrate = SNDRV_PCM_RATE_8000,
-+ .pcmdir = TEMPLATE_DIR,
-+ .flags = SND_SOC_DAI_BFS_DIV,
-+ .fs = 1500,
-+ .bfs = SND_SOC_FSBD(1),
-+ },
-+
-+ /* 44.1k */
-+ {
-+ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
-+ .pcmfmt = TEMPLATE_HIFI_BITS,
-+ .pcmrate = SNDRV_PCM_RATE_44100,
-+ .pcmdir = TEMPLATE_DIR,
-+ .flags = SND_SOC_DAI_BFS_DIV,
-+ .fs = 272,
-+ .bfs = SND_SOC_FSBD(1),
-+ },
-+
-+ /* 48k */
-+ {
-+ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
-+ .pcmfmt = TEMPLATE_HIFI_BITS,
-+ .pcmrate = SNDRV_PCM_RATE_48000,
-+ .pcmdir = TEMPLATE_DIR,
-+ .flags = SND_SOC_DAI_BFS_DIV,
-+ .fs = 250,
-+ .bfs = SND_SOC_FSBD(1),
-+ },
-+
-+ /* 88.2k */
-+ {
-+ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
-+ .pcmfmt = TEMPLATE_HIFI_BITS,
-+ .pcmrate = SNDRV_PCM_RATE_88200,
-+ .pcmdir = TEMPLATE_DIR,
-+ .flags = SND_SOC_DAI_BFS_DIV,
-+ .fs = 136,
-+ .bfs = SND_SOC_FSBD(1),
-+ },
-+
-+ /* 96k */
-+ {
-+ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBM_CFM,
-+ .pcmfmt = TEMPLATE_HIFI_BITS,
-+ .pcmrate = SNDRV_PCM_RATE_96000,
-+ .pcmdir = TEMPLATE_DIR,
-+ .flags = SND_SOC_DAI_BFS_DIV,
-+ .fs = 125,
-+ .bfs = SND_SOC_FSBD(1),
-+ },
-+
-+ /* codec frame and clock slave modes */
-+ {
-+ .fmt = TEMPLATE_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
-+ .pcmfmt = TEMPLATE_HIFI_BITS,
-+ .pcmrate = TEMPLATE_RATES,
-+ .pcmdir = TEMPLATE_DIR,
-+ .flags = SND_SOC_DAI_BFS_DIV,
-+ .fs = SND_SOC_FS_ALL,
-+ .bfs = SND_SOC_FSB_ALL,
-+ },
-+};
-+
-+/*
-+ * read template_codec register cache
-+ */
-+static inline unsigned int template_codec_read_reg_cache(struct snd_soc_codec *codec,
-+ unsigned int reg)
-+{
-+ u16 *cache = codec->reg_cache;
-+
-+ if (reg >= TEMPLATE_CACHEREGNUM)
-+ return -1;
-+ return cache[reg];
-+}
-+
-+/*
-+ * write template_codec register cache
-+ */
-+static inline void template_codec_write_reg_cache(struct snd_soc_codec *codec,
-+ u16 reg, unsigned int value)
-+{
-+ u16 *cache = codec->reg_cache;
-+ if (reg >= TEMPLATE_CACHEREGNUM)
-+ return;
-+ cache[reg] = value;
-+}
-+
-+/*
-+ * write to the template codec register space
-+ */
-+static int template_codec_write(struct snd_soc_codec *codec, unsigned int reg,
-+ unsigned int value)
-+{
-+ u8 data[2];
-+
-+ /* format the data - codec specific */
-+ data[0] = reg;
-+ data[1] = value;
-+
-+ template_codec_write_reg_cache (codec, reg, value);
-+ if (codec->hw_write(codec->control_data, data, 2) == 2)
-+ return 0;
-+ else
-+ return -EIO;
-+}
-+
-+#define template_codec_reset(c) template_codec_write(c, TEMPLATE_RESET, 0)
-+
-+
-+/* template codec non DAPM controls */
-+static const struct snd_kcontrol_new template_codec_snd_controls[] = {
-+};
-+
-+/* add non dapm controls */
-+static int template_codec_add_controls(struct snd_soc_codec *codec)
-+{
-+ int err, i;
-+
-+ for (i = 0; i < ARRAY_SIZE(template_codec_snd_controls); i++) {
-+ if ((err = snd_ctl_add(codec->card,
-+ snd_soc_cnew(&template_codec_snd_controls[i],codec, NULL))) < 0)
-+ return err;
-+ }
-+
-+ return 0;
-+}
-+
-+/* template codec DAPM controls */
-+static const struct snd_soc_dapm_widget template_codec_dapm_widgets[] = {
-+};
-+
-+/*
-+ * template codec audio interconnectiosn between sink and source.
-+ */
-+static const char *audio_map[][3] = {
-+
-+
-+ /* terminator */
-+ {NULL, NULL, NULL},
-+};
-+
-+static int template_codec_add_widgets(struct snd_soc_codec *codec)
-+{
-+ int i;
-+
-+ for(i = 0; i < ARRAY_SIZE(template_codec_dapm_widgets); i++) {
-+ snd_soc_dapm_new_control(codec, &template_codec_dapm_widgets[i]);
-+ }
-+
-+ /* set up audio path interconnects */
-+ for(i = 0; intercon[i][0] != NULL; i++) {
-+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
-+ audio_map[i][1], audio_map[i][2]);
-+ }
-+
-+ snd_soc_dapm_new_widgets(codec);
-+ return 0;
-+}
-+
-+/*
-+ * Configures the codec SYSCLK/MCLK (system or master clock)
-+ */
-+static unsigned int template_codec_config_sysclk(struct snd_soc_codec_dai *dai,
-+ struct snd_soc_clock_info *info, unsigned int clk)
-+{
-+ dai->mclk = 0;
-+
-+ /* check that the calculated FS and rate actually match a clock from
-+ * the machine driver */
-+ if (info->fs * info->rate == clk)
-+ dai->mclk = clk;
-+
-+ return dai->mclk;
-+}
-+
-+/*
-+ * Alsa operations
-+ * Only implement the required operations for your platform.
-+ * These operations are specific to the codec only.
-+ */
-+
-+ /*
-+ * Called by ALSA when a PCM substream is opened, private data can be allocated.
-+ */
-+static int template_codec_startup(struct snd_pcm_substream *substream)
-+{
-+}
-+
-+/*
-+ * Called by ALSA when a PCM substream is closed. Private data can be
-+ * freed here.
-+ */
-+static int template_codec_shutdown(struct snd_pcm_substream *substream)
-+{
-+}
-+
-+/*
-+ * Called by ALSA when the hardware params are set by application. This
-+ * function can also be called multiple times and can allocate buffers
-+ * (using snd_pcm_lib_* ). It's non-atomic.
-+ */
-+static int template_codec_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+}
-+
-+/*
-+ * Free's resources allocated by hw_params, can be called multiple times
-+ */
-+static int template_codec_hw_free(struct snd_pcm_substream *substream)
-+{
-+}
-+
-+/*
-+ * Starts (Triggers) audio playback or capture.
-+ * Usually only needed for DMA
-+ */
-+static int template_codec_trigger(struct snd_pcm_substream *substream, int cmd)
-+{
-+}
-+
-+/*
-+ * Called by ALSA when the PCM substream is prepared, can set format, sample
-+ * rate, etc. This function is non atomic and can be called multiple times,
-+ * it can refer to the runtime info.
-+ */
-+static int template_codec_prepare(struct snd_pcm_substream *substream)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_device *socdev = rtd->socdev;
-+ struct snd_soc_codec *codec = socdev->codec;
-+
-+ /* set master/slave audio interface */
-+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
-+ case SND_SOC_DAIFMT_CBM_CFM:
-+ break;
-+ case SND_SOC_DAIFMT_CBS_CFS:
-+ break;
-+ }
-+
-+ /* interface format */
-+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
-+ case SND_SOC_DAIFMT_I2S:
-+ break;
-+ case SND_SOC_DAIFMT_RIGHT_J:
-+ break;
-+ case SND_SOC_DAIFMT_LEFT_J:
-+ break;
-+ case SND_SOC_DAIFMT_DSP_A:
-+ break;
-+ case SND_SOC_DAIFMT_DSP_B:
-+ break;
-+ }
-+
-+ /* bit size */
-+ switch (rtd->codec_dai->dai_runtime.pcmfmt) {
-+ case SNDRV_PCM_FMTBIT_S16_LE:
-+ break;
-+ case SNDRV_PCM_FMTBIT_S20_3LE:
-+ break;
-+ case SNDRV_PCM_FMTBIT_S24_LE:
-+ break;
-+ case SNDRV_PCM_FMTBIT_S32_LE:
-+ break;
-+ }
-+
-+ /* clock inversion */
-+ switch (rtd->codec_dai->dai_runtime.fmt & SND_SOC_DAIFMT_INV_MASK) {
-+ case SND_SOC_DAIFMT_NB_NF:
-+ break;
-+ case SND_SOC_DAIFMT_IB_IF:
-+ break;
-+ case SND_SOC_DAIFMT_IB_NF:
-+ break;
-+ case SND_SOC_DAIFMT_NB_IF:
-+ break;
-+ }
-+
-+ return 0;
-+}
-+
-+/*
-+ * Enable / Disable codec digital soft mute
-+ */
-+static int template_codec_mute(struct snd_soc_codec *codec,
-+ struct snd_soc_codec_dai *dai, int mute)
-+{
-+}
-+
-+/*
-+ * Codec DAPM event handler
-+ * This handles codec level DAPM events
-+ */
-+static int template_codec_dapm_event(struct snd_soc_codec *codec, int event)
-+{
-+ u16 reg = template_codec_read_reg_cache(codec, TEMPLATE_PWR) & 0xff7f;
-+
-+ switch (event) {
-+ case SNDRV_CTL_POWER_D0: /* full On */
-+ /* e.g. vref/mid, osc on, */
-+ break;
-+ case SNDRV_CTL_POWER_D1: /* partial On */
-+ case SNDRV_CTL_POWER_D2: /* partial On */
-+ break;
-+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
-+ /* everything off except vref/vmid, */
-+ break;
-+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
-+ /* everything off, dac mute, inactive */
-+ break;
-+ }
-+ codec->dapm_state = event;
-+ return 0;
-+}
-+
-+/*
-+ * Define codec DAI.
-+ */
-+struct snd_soc_codec_dai template_codec_dai = {
-+ .name = "codec xxx",
-+ /* playback and capture stream info */
-+ .playback = {
-+ .stream_name = "Playback",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ },
-+ .capture = {
-+ .stream_name = "Capture",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ },
-+ /* codec operations */
-+ .config_sysclk = template_codec_config_sysclk,
-+ .digital_mute = template_codec_mute,
-+ /* alsa PCM operations */
-+ .ops = {
-+ .startup = template_codec_startup,
-+ .shutdown = template_codec_shutdown,
-+ .prepare = template_codec_prepare,
-+ .trigger = template_codec_trigger,
-+ .hw_params = template_codec_hw_params,
-+ .hw_free = template_codec_hw_free,},
-+ /* codec capabilities */
-+ .caps = {
-+ .num_modes = ARRAY_SIZE(template_codec_modes),
-+ .mode = template_codec_modes,
-+ },
-+};
-+EXPORT_SYMBOL_GPL(template_codec_dai);
-+
-+static int template_codec_suspend(struct platform_device *pdev, pm_message_t state)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+
-+ template_codec_write(codec, TEMPLATE_ACTIVE, 0x0);
-+ template_codec_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
-+ return 0;
-+}
-+
-+static int template_codec_resume(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+ int i;
-+ u8 data[2];
-+ u16 *cache = codec->reg_cache;
-+
-+ /* Sync reg_cache with the hardware */
-+ for (i = 0; i < ARRAY_SIZE(template_codec_reg); i++) {
-+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
-+ data[1] = cache[i] & 0x00ff;
-+ codec->hw_write(codec->control_data, data, 2);
-+ }
-+ template_codec_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
-+ template_codec_dapm_event(codec, codec->suspend_dapm_state);
-+ return 0;
-+}
-+
-+/*
-+ * initialise the TEMPLATE driver
-+ * register the mixer and dsp interfaces with the kernel
-+ */
-+static int template_codec_init(struct snd_soc_device *socdev)
-+{
-+ struct snd_soc_codec *codec = socdev->codec;
-+ int reg, ret = 0;
-+
-+ codec->name = "TEMPLATE";
-+ codec->owner = THIS_MODULE;
-+ codec->read = template_codec_read_reg_cache;
-+ codec->write = template_codec_write;
-+ codec->dapm_event = template_codec_dapm_event;
-+ codec->dai = &template_codec_dai;
-+ codec->num_dai = 1;
-+ codec->reg_cache_size = ARRAY_SIZE(template_codec_reg);
-+
-+ codec->reg_cache =
-+ kzalloc(sizeof(u16) * ARRAY_SIZE(template_codec_reg), GFP_KERNEL);
-+ if (codec->reg_cache == NULL)
-+ return -ENOMEM;
-+ memcpy(codec->reg_cache,
-+ template_codec_reg, sizeof(u16) * ARRAY_SIZE(template_codec_reg));
-+ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(template_codec_reg);
-+
-+ template_codec_reset(codec);
-+
-+ /* register pcms */
-+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
-+ if (ret < 0) {
-+ kfree(codec->reg_cache);
-+ return ret;
-+ }
-+
-+ /* power on device */
-+ template_codec_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
-+
-+ /* set the update bits */
-+ reg = template_codec_read_reg_cache(codec, TEMPLATE_LOUT1V);
-+ template_codec_write(codec, TEMPLATE_LOUT1V, reg | 0x0100);
-+ reg = template_codec_read_reg_cache(codec, TEMPLATE_ROUT1V);
-+ template_codec_write(codec, TEMPLATE_ROUT1V, reg | 0x0100);
-+ reg = template_codec_read_reg_cache(codec, TEMPLATE_LINVOL);
-+ template_codec_write(codec, TEMPLATE_LINVOL, reg | 0x0100);
-+ reg = template_codec_read_reg_cache(codec, TEMPLATE_RINVOL);
-+ template_codec_write(codec, TEMPLATE_RINVOL, reg | 0x0100);
-+
-+ template_codec_add_controls(codec);
-+ template_codec_add_widgets(codec);
-+ ret = snd_soc_register_card(socdev);
-+ if (ret < 0) {
-+ snd_soc_free_pcms(socdev);
-+ snd_soc_dapm_free(socdev);
-+ }
-+
-+ return ret;
-+}
-+
-+static struct snd_soc_device *template_codec_socdev;
-+
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+
-+/*
-+ * TEMPLATE 2 wire address is determined by GPIO5
-+ * state during powerup.
-+ * low = 0x1a
-+ * high = 0x1b
-+ */
-+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
-+
-+/* Magic definition of all other variables and things */
-+I2C_CLIENT_INSMOD;
-+
-+static struct i2c_driver template_codec_i2c_driver;
-+static struct i2c_client client_template;
-+
-+/* If the i2c layer weren't so broken, we could pass this kind of data
-+ around */
-+
-+static int template_codec_codec_probe(struct i2c_adapter *adap, int addr, int kind)
-+{
-+ struct snd_soc_device *socdev = template_codec_socdev;
-+ struct template_codec_setup_data *setup = socdev->codec_data;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ struct i2c_client *i2c;
-+ int ret;
-+
-+ if (addr != setup->i2c_address)
-+ return -ENODEV;
-+
-+ client_template.adapter = adap;
-+ client_template.addr = addr;
-+
-+ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
-+ if (i2c == NULL) {
-+ kfree(codec);
-+ return -ENOMEM;
-+ }
-+ memcpy(i2c, &client_template, sizeof(struct i2c_client));
-+ i2c_set_clientdata(i2c, codec);
-+ codec->control_data = i2c;
-+
-+ ret = i2c_attach_client(i2c);
-+ if (ret < 0) {
-+ err("failed to attach codec at addr %x\n", addr);
-+ goto err;
-+ }
-+
-+ ret = template_codec_init(socdev);
-+ if (ret < 0) {
-+ err("failed to initialise TEMPLATE\n");
-+ goto err;
-+ }
-+ return ret;
-+
-+err:
-+ kfree(codec);
-+ kfree(i2c);
-+ return ret;
-+}
-+
-+static int template_codec_i2c_detach(struct i2c_client *client)
-+{
-+ struct snd_soc_codec* codec = i2c_get_clientdata(client);
-+ i2c_detach_client(client);
-+ kfree(codec->reg_cache);
-+ kfree(client);
-+ return 0;
-+}
-+
-+static int template_codec_i2c_attach(struct i2c_adapter *adap)
-+{
-+ return i2c_probe(adap, &addr_data, template_codec_codec_probe);
-+}
-+
-+/* corgi i2c codec control layer */
-+static struct i2c_driver template_codec_i2c_driver = {
-+ .driver = {
-+ .name = "TEMPLATE I2C Codec",
-+ .owner = THIS_MODULE,
-+ },
-+ .id = I2C_DRIVERID_TEMPLATE,
-+ .attach_adapter = template_codec_i2c_attach,
-+ .detach_client = template_codec_i2c_detach,
-+ .command = NULL,
-+};
-+
-+static struct i2c_client client_template = {
-+ .name = "TEMPLATE",
-+ .driver = &template_codec_i2c_driver,
-+};
-+#endif
-+
-+static int template_codec_probe(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct template_codec_setup_data *setup;
-+ struct snd_soc_codec *codec;
-+ int ret = 0;
-+
-+ info("TEMPLATE Audio Codec %s", TEMPLATE_VERSION);
-+
-+ setup = socdev->codec_data;
-+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
-+ if (codec == NULL)
-+ return -ENOMEM;
-+
-+ socdev->codec = codec;
-+ mutex_init(&codec->mutex);
-+ INIT_LIST_HEAD(&codec->dapm_widgets);
-+ INIT_LIST_HEAD(&codec->dapm_paths);
-+
-+ template_codec_socdev = socdev;
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+ if (setup->i2c_address) {
-+ normal_i2c[0] = setup->i2c_address;
-+ codec->hw_write = (hw_write_t)i2c_master_send;
-+ ret = i2c_add_driver(&template_codec_i2c_driver);
-+ if (ret != 0)
-+ printk(KERN_ERR "can't add i2c driver");
-+ }
-+#else
-+ /* Add other interfaces here */
-+#endif
-+ return ret;
-+}
-+
-+/* power down chip and remove */
-+static int template_codec_remove(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+
-+ if (codec->control_data)
-+ template_codec_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
-+
-+ snd_soc_free_pcms(socdev);
-+ snd_soc_dapm_free(socdev);
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+ i2c_del_driver(&template_codec_i2c_driver);
-+#endif
-+ kfree(codec);
-+
-+ return 0;
-+}
-+
-+/* codec device ops */
-+struct snd_soc_codec_device soc_codec_dev_template_codec = {
-+ .probe = template_codec_probe,
-+ .remove = template_codec_remove,
-+ .suspend = template_codec_suspend,
-+ .resume = template_codec_resume,
-+};
-+
-+EXPORT_SYMBOL_GPL(soc_codec_dev_template_codec);
-+
-Index: linux-2.6.21-moko/sound/soc/templates/template-i2s.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/templates/template-i2s.c
-@@ -0,0 +1,223 @@
-+/*
-+ * 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.
-+ */
-+
-+#include <linux/init.h>
-+#include <linux/module.h>
-+#include <linux/device.h>
-+#include <linux/delay.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/initval.h>
-+#include <sound/soc.h>
-+
-+#include <asm/hardware.h>
-+
-+#include "template-pcm.h"
-+
-+/* supported I2S DAI hardware formats */
-+#define TEMPLATE_I2S_DAIFMT \
-+ (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF)
-+
-+/* supported I2S direction */
-+#define TEMPLATE_I2S_DIR \
-+ (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
-+
-+/* supported I2S rates */
-+#define TEMPLATE_I2S_RATES \
-+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
-+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
-+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
-+
-+/* I2S controller DAI capabilities */
-+static struct snd_soc_dai_mode template_i2s_modes[] = {
-+ /* template I2S frame and clock master modes */
-+ {
-+ .fmt = TEMPLATE_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
-+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
-+ .pcmrate = SNDRV_PCM_RATE_8000,
-+ .pcmdir = TEMPLATE_I2S_DIR,
-+ .flags = SND_SOC_DAI_BFS_DIV,
-+ .fs = 256,
-+ .bfs = SND_SOC_FSBD(4),
-+ },
-+ {
-+ .fmt = TEMPLATE_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
-+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
-+ .pcmrate = SNDRV_PCM_RATE_11025,
-+ .pcmdir = TEMPLATE_I2S_DIR,
-+ .flags = SND_SOC_DAI_BFS_DIV,
-+ .fs = 256,
-+ .bfs = SND_SOC_FSBD(4),
-+ },
-+ {
-+ .fmt = TEMPLATE_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
-+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
-+ .pcmrate = SNDRV_PCM_RATE_16000,
-+ .pcmdir = TEMPLATE_I2S_DIR,
-+ .flags = SND_SOC_DAI_BFS_DIV,
-+ .fs = 256,
-+ .bfs = SND_SOC_FSBD(4),
-+ },
-+ {
-+ .fmt = TEMPLATE_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
-+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
-+ .pcmrate = SNDRV_PCM_RATE_22050,
-+ .pcmdir = TEMPLATE_I2S_DIR,
-+ .flags = SND_SOC_DAI_BFS_DIV,
-+ .fs = 256,
-+ .bfs = SND_SOC_FSBD(4),
-+ },
-+ {
-+ .fmt = TEMPLATE_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
-+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
-+ .pcmrate = SNDRV_PCM_RATE_44100,
-+ .pcmdir = TEMPLATE_I2S_DIR,
-+ .flags = SND_SOC_DAI_BFS_DIV,
-+ .fs = 256,
-+ .bfs = SND_SOC_FSBD(4),
-+ },
-+ {
-+ .fmt = TEMPLATE_I2S_DAIFMT | SND_SOC_DAIFMT_CBS_CFS,
-+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
-+ .pcmrate = SNDRV_PCM_RATE_48000,
-+ .pcmdir = TEMPLATE_I2S_DIR,
-+ .flags = SND_SOC_DAI_BFS_DIV,
-+ .fs = 256,
-+ .bfs = SND_SOC_FSBD(4),
-+ },
-+
-+ /* template I2S frame master and clock slave mode */
-+ {
-+ .fmt = TEMPLATE_I2S_DAIFMT | SND_SOC_DAIFMT_CBM_CFS,
-+ .pcmfmt = SNDRV_PCM_FMTBIT_S16_LE,
-+ .pcmrate = TEMPLATE_I2S_RATES,
-+ .pcmdir = TEMPLATE_I2S_DIR,
-+ .fs = SND_SOC_FS_ALL,
-+ .flags = SND_SOC_DAI_BFS_RATE,
-+ .bfs = 64,
-+ },
-+};
-+
-+/* I2S controller platform specific DMA parameters */
-+static struct template_pcm_dma_params template_i2s_pcm_stereo_out = {
-+ .name = "I2S PCM Stereo out",
-+ .dev_addr = __PREG(SADR),
-+};
-+
-+static struct template_pcm_dma_params template_i2s_pcm_stereo_in = {
-+ .name = "I2S PCM Stereo in",
-+ .dev_addr = __PREG(SADR),
-+};
-+
-+#ifdef CONFIG_PM
-+/* suspend I2S controller */
-+static int template_i2s_suspend(struct platform_device *dev,
-+ struct snd_soc_cpu_dai *dai)
-+{
-+}
-+
-+/* resume I2S controller */
-+static int template_i2s_resume(struct platform_device *pdev,
-+ struct snd_soc_cpu_dai *dai)
-+{
-+}
-+
-+#else
-+#define template_i2s_suspend NULL
-+#define template_i2s_resume NULL
-+#endif
-+
-+/* configure the I2S controllers MCLK or SYSCLK */
-+static unsigned int template_i2s_config_sysclk(struct snd_soc_cpu_dai *iface,
-+ struct snd_soc_clock_info *info, unsigned int clk)
-+{
-+}
-+
-+
-+/*
-+ * Alsa operations
-+ * Only implement the required operations for your platform.
-+ * These operations are specific to the I2S controller and DAI only.
-+ */
-+
-+ /*
-+ * Called by ALSA when a PCM substream is opened, private data can be allocated.
-+ */
-+static int template_i2s_startup(struct snd_pcm_substream *substream)
-+{
-+}
-+
-+/*
-+ * Called by ALSA when a PCM substream is closed. Private data can be
-+ * freed here.
-+ */
-+static int template_i2s_shutdown(struct snd_pcm_substream *substream)
-+{
-+}
-+
-+/*
-+ * Called by ALSA when the PCM substream is prepared, can set format, sample
-+ * rate, etc. This function is non atomic and can be called multiple times,
-+ * it can refer to the runtime info.
-+ */
-+static int template_i2s_prepare(struct snd_pcm_substream *substream)
-+{
-+}
-+
-+/*
-+ * Called by ALSA when the hardware params are set by application. This
-+ * function can also be called multiple times and can allocate buffers
-+ * (using snd_pcm_lib_* ). It's non-atomic.
-+ */
-+static int template_i2s_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+}
-+
-+/*
-+ * Free's resources allocated by hw_params, can be called multiple times
-+ */
-+static int template_i2s_hw_free(struct snd_pcm_substream *substream)
-+{
-+}
-+
-+/*
-+ * Starts (Triggers) audio playback or capture.
-+ * Usually only needed for DMA
-+ */
-+static int template_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
-+{
-+}
-+
-+
-+struct snd_soc_cpu_dai template_i2s_dai = {
-+ .name = "template-i2s",
-+ .id = 0,
-+ .type = SND_SOC_DAI_I2S,
-+ .suspend = template_i2s_suspend,
-+ .resume = template_i2s_resume,
-+ .config_sysclk = template_i2s_config_sysclk,
-+ .playback = {
-+ .channels_min = 2,
-+ .channels_max = 2,},
-+ .capture = {
-+ .channels_min = 2,
-+ .channels_max = 2,},
-+ .ops = {
-+ .startup = template_i2s_startup,
-+ .shutdown = template_i2s_shutdown,
-+ .prepare = template_i2s_prepare,
-+ .trigger = template_i2s_trigger,
-+ .hw_params = template_i2s_hw_params,
-+ .hw_free = template_i2s_hw_free,},
-+ .caps = {
-+ .num_modes = ARRAY_SIZE(template_i2s_modes),
-+ .mode = template_i2s_modes,},
-+};
-+
-+EXPORT_SYMBOL_GPL(template_i2s_dai);
-Index: linux-2.6.21-moko/sound/soc/templates/template-pcm.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/templates/template-pcm.c
-@@ -0,0 +1,166 @@
-+/*
-+ * 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.
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/init.h>
-+#include <linux/platform_device.h>
-+#include <linux/slab.h>
-+#include <linux/dma-mapping.h>
-+
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/pcm_params.h>
-+#include <sound/soc.h>
-+
-+#include <asm/dma.h>
-+#include <asm/hardware.h>
-+
-+#include "template-pcm.h"
-+
-+/* PCM hardware DMA capabilities - platform specific */
-+static const struct snd_pcm_hardware template_pcm_hardware = {
-+ .info = SNDRV_PCM_INFO_MMAP |
-+ SNDRV_PCM_INFO_MMAP_VALID |
-+ SNDRV_PCM_INFO_INTERLEAVED |
-+ SNDRV_PCM_INFO_PAUSE |
-+ SNDRV_PCM_INFO_RESUME,
-+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
-+ SNDRV_PCM_FMTBIT_S24_LE |
-+ SNDRV_PCM_FMTBIT_S32_LE,
-+ .period_bytes_min = 32,
-+ .period_bytes_max = 8192 - 32,
-+ .periods_min = 1,
-+ .periods_max = PAGE_SIZE/sizeof(pxa_dma_desc),
-+ .buffer_bytes_max = 128 * 1024,
-+ .fifo_size = 32,
-+};
-+
-+/*
-+ * Called by ALSA when the hardware params are set by application. This
-+ * function can also be called multiple times and can allocate buffers
-+ * (using snd_pcm_lib_* ). It's non-atomic.
-+ */
-+static int template_pcm_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+}
-+
-+/*
-+ * Free's resources allocated by hw_params, can be called multiple times
-+ */
-+static int template_pcm_hw_free(struct snd_pcm_substream *substream)
-+{
-+}
-+
-+/*
-+ * Called by ALSA when the PCM substream is prepared, can set format, sample
-+ * rate, etc. This function is non atomic and can be called multiple times,
-+ * it can refer to the runtime info.
-+ */
-+static int template_pcm_prepare(struct snd_pcm_substream *substream)
-+{
-+}
-+
-+/*
-+ * Starts (Triggers) audio playback or capture.
-+ * Usually only needed for DMA
-+ */
-+static int template_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-+{
-+ struct template_runtime_data *prtd = substream->runtime->private_data;
-+ int ret = 0;
-+
-+ switch (cmd) {
-+ case SNDRV_PCM_TRIGGER_START:
-+ break;
-+ case SNDRV_PCM_TRIGGER_STOP:
-+ break;
-+ case SNDRV_PCM_TRIGGER_SUSPEND:
-+ break;
-+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-+ break;
-+ case SNDRV_PCM_TRIGGER_RESUME:
-+ break;
-+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-+ break;
-+ default:
-+ ret = -EINVAL;
-+ }
-+
-+ return ret;
-+}
-+
-+/*
-+ * Returns the DMA audio frame position
-+ */
-+static snd_pcm_uframes_t
-+template_pcm_pointer(struct snd_pcm_substream *substream)
-+{
-+}
-+
-+ /*
-+ * Called by ALSA when a PCM substream is opened, private data can be allocated.
-+ */
-+static int template_pcm_open(struct snd_pcm_substream *substream)
-+{
-+}
-+
-+/*
-+ * Called by ALSA when a PCM substream is closed. Private data can be
-+ * freed here.
-+ */
-+static int template_pcm_close(struct snd_pcm_substream *substream)
-+{
-+}
-+
-+/* map DMA audio buffer into user space */
-+static int template_pcm_mmap(struct snd_pcm_substream *substream,
-+ struct vm_area_struct *vma)
-+{
-+ struct snd_pcm_runtime *runtime = substream->runtime;
-+ return dma_mmap_writecombine(substream->pcm->card->dev, vma,
-+ runtime->dma_area,
-+ runtime->dma_addr,
-+ runtime->dma_bytes);
-+}
-+
-+/* ALSA PCM operations */
-+struct snd_pcm_ops template_pcm_ops = {
-+ .open = template_pcm_open,
-+ .close = template_pcm_close,
-+ .ioctl = snd_pcm_lib_ioctl,
-+ .hw_params = template_pcm_hw_params,
-+ .hw_free = template_pcm_hw_free,
-+ .prepare = template_pcm_prepare,
-+ .trigger = template_pcm_trigger,
-+ .pointer = template_pcm_pointer,
-+ .mmap = template_pcm_mmap,
-+};
-+
-+/*
-+ * Called by ASoC core to free platform DMA.
-+ */
-+static void template_pcm_free_dma_buffers(struct snd_pcm *pcm)
-+{
-+}
-+
-+/*
-+ * Called by the ASoC core to create and initialise the platform DMA.
-+ */
-+int template_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
-+ struct snd_pcm *pcm)
-+{
-+}
-+
-+/* template audio platform */
-+struct snd_soc_platform template_soc_platform = {
-+ .name = "template-audio",
-+ .pcm_ops = &template_pcm_ops,
-+ .pcm_new = template_pcm_new,
-+ .pcm_free = template_pcm_free_dma_buffers,
-+};
-+EXPORT_SYMBOL_GPL(template_soc_platform);
-Index: linux-2.6.21-moko/sound/soc/templates/template-pcm.h
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/templates/template-pcm.h
-@@ -0,0 +1,19 @@
-+/*
-+ * 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 _TEMPLATE_PCM_H
-+#define _TEMPLATE_PCM_H
-+
-+/* platform specific structs can be declared here */
-+
-+extern struct snd_soc_cpu_dai template_ac97_dai[3];
-+extern struct snd_soc_cpu_dai template_i2s_dai;
-+
-+/* template platform data */
-+extern struct snd_soc_platform template_soc_platform;
-+extern struct snd_ac97_bus_ops tempalte_ac97_ops;
-+
-+#endif
-Index: linux-2.6.21-moko/sound/soc/templates/template-codec.h
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/templates/template-codec.h
-@@ -0,0 +1,21 @@
-+/*
-+ * 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 _TEMPLATE_H
-+#define _TEMPLATE_H
-+
-+/* TEMPLATE register space */
-+
-+#define TEMPLATE_CACHEREGNUM 10
-+
-+struct template_codec_setup_data {
-+ unsigned short i2c_address;
-+};
-+
-+extern struct snd_soc_codec_dai template_codec_dai;
-+extern struct snd_soc_codec_device soc_codec_dev_template_codec;
-+
-+#endif
-Index: linux-2.6.21-moko/sound/soc/templates/template-machine.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/templates/template-machine.c
-@@ -0,0 +1,161 @@
-+/*
-+ * 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.
-+ *
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/moduleparam.h>
-+#include <linux/timer.h>
-+#include <linux/interrupt.h>
-+#include <linux/platform_device.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+
-+#include <asm/mach-types.h>
-+
-+#include "../codecs/template-codec.h"
-+#include "template-pcm.h"
-+
-+/*
-+ * Alsa operations
-+ * Only implement the required operations for your platform.
-+ * These operations are specific to the machine only.
-+ */
-+
-+ /*
-+ * Called by ALSA when a PCM substream is opened, private data can be allocated.
-+ */
-+static int template_machine_startup(struct snd_pcm_substream *substream)
-+{
-+}
-+
-+/*
-+ * Called by ALSA when a PCM substream is closed. Private data can be
-+ * freed here.
-+ */
-+static int template_machine_shutdown(struct snd_pcm_substream *substream)
-+{
-+}
-+
-+/*
-+ * Called by ALSA when the hardware params are set by application. This
-+ * function can also be called multiple times and can allocate buffers
-+ * (using snd_pcm_lib_* ). It's non-atomic.
-+ */
-+static int template_machine_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+}
-+
-+/*
-+ * Free's resources allocated by hw_params, can be called multiple times
-+ */
-+static int template_machine_hw_free(struct snd_pcm_substream *substream)
-+{
-+}
-+
-+/* machine Alsa PCM operations */
-+static struct snd_soc_ops template_ops = {
-+ .startup = template_machine_startup,
-+ .shutdown = template_machine_shutdown,
-+ .hw_free = template_machine_hw_free,
-+ .hw_params = template_machine_hw_params,
-+};
-+
-+/* machine audio map (connections to the codec pins) */
-+static const char *audio_map[][3] = {
-+
-+ {NULL, NULL, NULL},
-+};
-+
-+/*
-+ * Initialise the machine audio subsystem.
-+ */
-+static int template_machine_init(struct snd_soc_codec *codec)
-+{
-+ /* mark unused codec pins as NC */
-+
-+ /* Add template specific controls */
-+
-+ /* Add template specific widgets */
-+
-+ /* Set up template specific audio path audio_map */
-+
-+ /* synchronise subsystem */
-+ snd_soc_dapm_sync_endpoints(codec);
-+ return 0;
-+}
-+
-+/*
-+ * Configure the clocking within the audio subsystem
-+ */
-+static unsigned int template_config_sysclk(struct snd_soc_pcm_runtime *rtd,
-+ struct snd_soc_clock_info *info)
-+{
-+}
-+
-+/* template digital audio interface glue - connects codec <--> CPU */
-+static struct snd_soc_dai_link template_dai = {
-+ .name = "Codec",
-+ .stream_name = "Stream Name",
-+ .cpu_dai = &template_i2s_dai,
-+ .codec_dai = &template_codec_dai,
-+ .init = template_machine_init,
-+ .config_sysclk = template_config_sysclk,
-+};
-+
-+/* template audio machine driver */
-+static struct snd_soc_machine snd_soc_machine_template = {
-+ .name = "Machine",
-+ .dai_link = &template_dai,
-+ .num_links = ARRAY_SIZE(template_dai),
-+ .ops = &template_ops,
-+};
-+
-+/* template audio private data */
-+static struct codec_priv_setup_data template_codec_setup = {
-+ .i2c_address = 0x1b,
-+};
-+
-+/* template audio subsystem */
-+static struct snd_soc_device template_snd_devdata = {
-+ .machine = &snd_soc_machine_template,
-+ .platform = &template_soc_platform,
-+ .codec_dev = &soc_codec_dev_wm8731,
-+ .codec_data = &template_codec_setup,
-+};
-+
-+static struct platform_device *template_snd_device;
-+
-+static int __init template_init(void)
-+{
-+ int ret;
-+
-+ template_snd_device = platform_device_alloc("soc-audio", -1);
-+ if (!template_snd_device)
-+ return -ENOMEM;
-+
-+ platform_set_drvdata(template_snd_device, &template_snd_devdata);
-+ template_snd_devdata.dev = &template_snd_device->dev;
-+ ret = platform_device_add(template_snd_device);
-+
-+ if (ret)
-+ platform_device_put(template_snd_device);
-+
-+ return ret;
-+}
-+
-+static void __exit template_exit(void)
-+{
-+ platform_device_unregister(template_snd_device);
-+}
-+
-+module_init(template_init);
-+module_exit(template_exit);
-+
-Index: linux-2.6.21-moko/sound/soc/pxa/pxa2xx-ssp.h
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/pxa/pxa2xx-ssp.h
-@@ -0,0 +1,43 @@
-+/*
-+ * linux/sound/arm/pxa2xx-ssp.h
-+ *
-+ * 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 _PXA2XX_SSP_H
-+#define _PXA2XX_SSP_H
-+
-+/* pxa2xx DAI SSP ID's */
-+#define PXA2XX_DAI_SSP1 0
-+#define PXA2XX_DAI_SSP2 1
-+#define PXA2XX_DAI_SSP3 2
-+
-+/* SSP clock sources */
-+#define PXA2XX_SSP_CLK_PLL 0
-+#define PXA2XX_SSP_CLK_EXT 1
-+#define PXA2XX_SSP_CLK_NET 2
-+#define PXA2XX_SSP_CLK_AUDIO 3
-+#define PXA2XX_SSP_CLK_NET_PLL 4
-+
-+/* SSP audio dividers */
-+#define PXA2XX_SSP_AUDIO_DIV_ACDS 0
-+#define PXA2XX_SSP_AUDIO_DIV_SCDB 1
-+#define PXA2XX_SSP_DIV_SCR 2
-+
-+/* SSP ACDS audio dividers values */
-+#define PXA2XX_SSP_CLK_AUDIO_DIV_1 0
-+#define PXA2XX_SSP_CLK_AUDIO_DIV_2 1
-+#define PXA2XX_SSP_CLK_AUDIO_DIV_4 2
-+#define PXA2XX_SSP_CLK_AUDIO_DIV_8 3
-+#define PXA2XX_SSP_CLK_AUDIO_DIV_16 4
-+#define PXA2XX_SSP_CLK_AUDIO_DIV_32 5
-+
-+/* SSP divider bypass */
-+#define PXA2XX_SSP_CLK_SCDB_4 0
-+#define PXA2XX_SSP_CLK_SCDB_1 1
-+
-+extern struct snd_soc_cpu_dai pxa_ssp_dai[3];
-+
-+#endif
-Index: linux-2.6.21-moko/sound/soc/s3c24xx/Kconfig
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/s3c24xx/Kconfig
-@@ -0,0 +1,32 @@
-+menu "SoC Audio for the Samsung S3C24XX"
-+
-+config SND_S3C24XX_SOC
-+ tristate "SoC Audio for the Samsung S3C24XX chips"
-+ depends on ARCH_S3C2410 && SND
-+ select SND_PCM
-+ help
-+ Say Y or M if you want to add support for codecs attached to
-+ the S3C24XX AC97, I2S or SSP interface. You will also need
-+ to select the audio interfaces to support below.
-+
-+config SND_S3C24XX_SOC_I2S
-+ tristate
-+
-+config SND_S3C24XX_SOC_SMDK2440
-+ tristate "SoC I2S Audio support for SMDK2440"
-+ depends on SND_S3C24XX_SOC && MACH_SMDK
-+ select SND_S3C24XX_SOC_I2S
-+ select SND_SOC_UDA1380
-+ help
-+ Say Y if you want to add support for SoC audio on SMDK2440
-+
-+config SND_S3C24XX_SOC_NEO1973_WM8753
-+ tristate "SoC I2S Audio support for NEO1973 - WM8753"
-+ depends on SND_S3C24XX_SOC && MACH_NEO1973_GTA01
-+ select SND_S3C24XX_SOC_I2S
-+ select SND_SOC_WM8753
-+ help
-+ Say Y if you want to add support for SoC audio on FIC Neo1973
-+ with the WM8753.
-+endmenu
-+
-Index: linux-2.6.21-moko/sound/soc/s3c24xx/Makefile
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/s3c24xx/Makefile
-@@ -0,0 +1,13 @@
-+# S3c24XX Platform Support
-+snd-soc-s3c24xx-objs := s3c24xx-pcm.o
-+snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o
-+
-+obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o
-+obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o
-+
-+# S3C24XX Machine Support
-+snd-soc-smdk2440-objs := smdk2440.o
-+snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o
-+
-+obj-$(CONFIG_SND_S3C24XX_SOC_SMDK2440) += snd-soc-smdk2440.o
-+obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
-Index: linux-2.6.21-moko/sound/soc/s3c24xx/s3c24xx-i2s.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/s3c24xx/s3c24xx-i2s.c
-@@ -0,0 +1,439 @@
-+/*
-+ * s3c24xx-i2s.c -- ALSA Soc Audio Layer
-+ *
-+ * (c) 2006 Wolfson Microelectronics PLC.
-+ * Graeme Gregory graeme.gregory at wolfsonmicro.com or linux at wolfsonmicro.com
-+ *
-+ * (c) 2004-2005 Simtec Electronics
-+ * http://armlinux.simtec.co.uk/
-+ * Ben Dooks <ben at simtec.co.uk>
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or (at your
-+ * option) any later version.
-+ *
-+ *
-+ * Revision history
-+ * 11th Dec 2006 Merged with Simtec driver
-+ * 10th Nov 2006 Initial version.
-+ */
-+
-+#include <linux/init.h>
-+#include <linux/module.h>
-+#include <linux/device.h>
-+#include <linux/delay.h>
-+#include <linux/clk.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/pcm_params.h>
-+#include <sound/initval.h>
-+#include <sound/soc.h>
-+
-+#include <asm/hardware.h>
-+#include <asm/io.h>
-+#include <asm/arch/regs-iis.h>
-+#include <asm/arch/regs-gpio.h>
-+#include <asm/arch/regs-clock.h>
-+#include <asm/arch/audio.h>
-+#include <asm/dma.h>
-+#include <asm/arch/dma.h>
-+
-+#include "s3c24xx-pcm.h"
-+#include "s3c24xx-i2s.h"
-+
-+#define S3C24XX_I2S_DEBUG 0
-+#if S3C24XX_I2S_DEBUG
-+#define DBG(x...) printk(KERN_DEBUG x)
-+#else
-+#define DBG(x...)
-+#endif
-+
-+static struct s3c2410_dma_client s3c24xx_dma_client_out = {
-+ .name = "I2S PCM Stereo out"
-+};
-+
-+static struct s3c2410_dma_client s3c24xx_dma_client_in = {
-+ .name = "I2S PCM Stereo in"
-+};
-+
-+static struct s3c24xx_pcm_dma_params s3c24xx_i2s_pcm_stereo_out = {
-+ .client = &s3c24xx_dma_client_out,
-+ .channel = DMACH_I2S_OUT,
-+ .dma_addr = S3C2410_PA_IIS + S3C2410_IISFIFO
-+};
-+
-+static struct s3c24xx_pcm_dma_params s3c24xx_i2s_pcm_stereo_in = {
-+ .client = &s3c24xx_dma_client_in,
-+ .channel = DMACH_I2S_IN,
-+ .dma_addr = S3C2410_PA_IIS + S3C2410_IISFIFO
-+};
-+
-+struct s3c24xx_i2s_info {
-+ void __iomem *regs;
-+ struct clk *iis_clk;
-+};
-+static struct s3c24xx_i2s_info s3c24xx_i2s;
-+
-+static void s3c24xx_snd_txctrl(int on)
-+{
-+ u32 iisfcon;
-+ u32 iiscon;
-+ u32 iismod;
-+
-+ DBG("Entered %s\n", __FUNCTION__);
-+
-+ iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
-+ iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
-+ iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
-+
-+ DBG("r: IISCON: %lx IISMOD: %lx IISFCON: %lx\n", iiscon, iismod, iisfcon);
-+
-+ if (on) {
-+ iisfcon |= S3C2410_IISFCON_TXDMA | S3C2410_IISFCON_TXENABLE;
-+ iiscon |= S3C2410_IISCON_TXDMAEN | S3C2410_IISCON_IISEN;
-+ iiscon &= ~S3C2410_IISCON_TXIDLE;
-+ iismod |= S3C2410_IISMOD_TXMODE;
-+
-+ writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
-+ writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
-+ writel(iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
-+ } else {
-+ /* note, we have to disable the FIFOs otherwise bad things
-+ * seem to happen when the DMA stops. According to the
-+ * Samsung supplied kernel, this should allow the DMA
-+ * engine and FIFOs to reset. If this isn't allowed, the
-+ * DMA engine will simply freeze randomly.
-+ */
-+
-+ iisfcon &= ~S3C2410_IISFCON_TXENABLE;
-+ iisfcon &= ~S3C2410_IISFCON_TXDMA;
-+ iiscon |= S3C2410_IISCON_TXIDLE;
-+ iiscon &= ~S3C2410_IISCON_TXDMAEN;
-+ iismod &= ~S3C2410_IISMOD_TXMODE;
-+
-+ writel(iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
-+ writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
-+ writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
-+ }
-+
-+ DBG("w: IISCON: %lx IISMOD: %lx IISFCON: %lx\n", iiscon, iismod, iisfcon);
-+}
-+
-+static void s3c24xx_snd_rxctrl(int on)
-+{
-+ u32 iisfcon;
-+ u32 iiscon;
-+ u32 iismod;
-+
-+ DBG("Entered %s\n", __FUNCTION__);
-+
-+ iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);
-+ iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
-+ iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
-+
-+ DBG("r: IISCON: %lx IISMOD: %lx IISFCON: %lx\n", iiscon, iismod, iisfcon);
-+
-+ if (on) {
-+ iisfcon |= S3C2410_IISFCON_RXDMA | S3C2410_IISFCON_RXENABLE;
-+ iiscon |= S3C2410_IISCON_RXDMAEN | S3C2410_IISCON_IISEN;
-+ iiscon &= ~S3C2410_IISCON_RXIDLE;
-+ iismod |= S3C2410_IISMOD_RXMODE;
-+
-+ writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
-+ writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
-+ writel(iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
-+ } else {
-+ /* note, we have to disable the FIFOs otherwise bad things
-+ * seem to happen when the DMA stops. According to the
-+ * Samsung supplied kernel, this should allow the DMA
-+ * engine and FIFOs to reset. If this isn't allowed, the
-+ * DMA engine will simply freeze randomly.
-+ */
-+
-+ iisfcon &= ~S3C2410_IISFCON_RXENABLE;
-+ iisfcon &= ~S3C2410_IISFCON_RXDMA;
-+ iiscon |= S3C2410_IISCON_RXIDLE;
-+ iiscon &= ~S3C2410_IISCON_RXDMAEN;
-+ iismod &= ~S3C2410_IISMOD_RXMODE;
-+
-+ writel(iisfcon, s3c24xx_i2s.regs + S3C2410_IISFCON);
-+ writel(iiscon, s3c24xx_i2s.regs + S3C2410_IISCON);
-+ writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
-+ }
-+
-+ DBG("w: IISCON: %lx IISMOD: %lx IISFCON: %lx\n", iiscon, iismod, iisfcon);
-+}
-+
-+/*
-+ * Wait for the LR signal to allow synchronisation to the L/R clock
-+ * from the codec. May only be needed for slave mode.
-+ */
-+static int s3c24xx_snd_lrsync(void)
-+{
-+ u32 iiscon;
-+ unsigned long timeout = jiffies + msecs_to_jiffies(5);
-+
-+ DBG("Entered %s\n", __FUNCTION__);
-+
-+ while (1) {
-+ iiscon = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
-+ if (iiscon & S3C2410_IISCON_LRINDEX)
-+ break;
-+
-+ if (timeout < jiffies)
-+ return -ETIMEDOUT;
-+ }
-+
-+ return 0;
-+}
-+
-+/*
-+ * Check whether CPU is the master or slave
-+ */
-+static inline int s3c24xx_snd_is_clkmaster(void)
-+{
-+ DBG("Entered %s\n", __FUNCTION__);
-+
-+ return (readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & S3C2410_IISMOD_SLAVE) ? 0:1;
-+}
-+
-+/*
-+ * Set S3C24xx I2S DAI format
-+ */
-+static int s3c24xx_i2s_set_fmt(struct snd_soc_cpu_dai *cpu_dai,
-+ unsigned int fmt)
-+{
-+ u32 iismod;
-+
-+ DBG("Entered %s\n", __FUNCTION__);
-+
-+ iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
-+ DBG("hw_params r: IISMOD: %lx \n", iismod);
-+
-+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
-+ case SND_SOC_DAIFMT_CBM_CFM:
-+ iismod |= S3C2410_IISMOD_SLAVE;
-+ break;
-+ case SND_SOC_DAIFMT_CBS_CFS:
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
-+ case SND_SOC_DAIFMT_LEFT_J:
-+ iismod |= S3C2410_IISMOD_MSB;
-+ break;
-+ case SND_SOC_DAIFMT_I2S:
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
-+ DBG("hw_params w: IISMOD: %lx \n", iismod);
-+ return 0;
-+}
-+
-+static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ u32 iismod;
-+
-+ DBG("Entered %s\n", __FUNCTION__);
-+
-+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-+ rtd->dai->cpu_dai->dma_data = &s3c24xx_i2s_pcm_stereo_out;
-+ else
-+ rtd->dai->cpu_dai->dma_data = &s3c24xx_i2s_pcm_stereo_in;
-+
-+ /* Working copies of register */
-+ iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
-+ DBG("hw_params r: IISMOD: %lx\n", iismod);
-+
-+ switch (params_format(params)) {
-+ case SNDRV_PCM_FORMAT_S8:
-+ break;
-+ case SNDRV_PCM_FORMAT_S16_LE:
-+ iismod |= S3C2410_IISMOD_16BIT;
-+ break;
-+ }
-+
-+ writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
-+ DBG("hw_params w: IISMOD: %lx\n", iismod);
-+ return 0;
-+}
-+
-+static int s3c24xx_i2s_trigger(struct snd_pcm_substream *substream, int cmd)
-+{
-+ int ret = 0;
-+
-+ DBG("Entered %s\n", __FUNCTION__);
-+
-+ switch (cmd) {
-+ case SNDRV_PCM_TRIGGER_START:
-+ case SNDRV_PCM_TRIGGER_RESUME:
-+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-+ if (!s3c24xx_snd_is_clkmaster()) {
-+ ret = s3c24xx_snd_lrsync();
-+ if (ret)
-+ goto exit_err;
-+ }
-+
-+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
-+ s3c24xx_snd_rxctrl(1);
-+ else
-+ s3c24xx_snd_txctrl(1);
-+ break;
-+ case SNDRV_PCM_TRIGGER_STOP:
-+ case SNDRV_PCM_TRIGGER_SUSPEND:
-+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
-+ s3c24xx_snd_rxctrl(0);
-+ else
-+ s3c24xx_snd_txctrl(0);
-+ break;
-+ default:
-+ ret = -EINVAL;
-+ break;
-+ }
-+
-+exit_err:
-+ return ret;
-+}
-+
-+/*
-+ * Set S3C24xx Clock source
-+ */
-+static int s3c24xx_i2s_set_sysclk(struct snd_soc_cpu_dai *cpu_dai,
-+ int clk_id, unsigned int freq, int dir)
-+{
-+ u32 iismod = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);
-+
-+ DBG("Entered %s\n", __FUNCTION__);
-+
-+ iismod &= ~S3C2440_IISMOD_MPLL;
-+
-+ switch (clk_id) {
-+ case S3C24XX_CLKSRC_PCLK:
-+ break;
-+ case S3C24XX_CLKSRC_MPLL:
-+ iismod |= S3C2440_IISMOD_MPLL;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
-+ return 0;
-+}
-+
-+/*
-+ * Set S3C24xx Clock dividers
-+ */
-+static int s3c24xx_i2s_set_clkdiv(struct snd_soc_cpu_dai *cpu_dai,
-+ int div_id, int div)
-+{
-+ u32 reg;
-+
-+ DBG("Entered %s\n", __FUNCTION__);
-+
-+ switch (div_id) {
-+ case S3C24XX_DIV_MCLK:
-+ reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~S3C2410_IISMOD_FS_MASK;
-+ writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
-+ break;
-+ case S3C24XX_DIV_BCLK:
-+ reg = readl(s3c24xx_i2s.regs + S3C2410_IISMOD) & ~(S3C2410_IISMOD_384FS);
-+ writel(reg | div, s3c24xx_i2s.regs + S3C2410_IISMOD);
-+ break;
-+ case S3C24XX_DIV_PRESCALER:
-+ writel(div, s3c24xx_i2s.regs + S3C2410_IISPSR);
-+ reg = readl(s3c24xx_i2s.regs + S3C2410_IISCON);
-+ writel(reg | S3C2410_IISCON_PSCEN, s3c24xx_i2s.regs + S3C2410_IISCON);
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ return 0;
-+}
-+
-+/*
-+ * To avoid duplicating clock code, allow machine driver to
-+ * get the clockrate from here.
-+ */
-+u32 s3c24xx_i2s_get_clockrate(void)
-+{
-+ return clk_get_rate(s3c24xx_i2s.iis_clk);
-+}
-+EXPORT_SYMBOL_GPL(s3c24xx_i2s_get_clockrate);
-+
-+static int s3c24xx_i2s_probe(struct platform_device *pdev)
-+{
-+ DBG("Entered %s\n", __FUNCTION__);
-+
-+ s3c24xx_i2s.regs = ioremap(S3C2410_PA_IIS, 0x100);
-+ if (s3c24xx_i2s.regs == NULL)
-+ return -ENXIO;
-+
-+ s3c24xx_i2s.iis_clk=clk_get(&pdev->dev, "iis");
-+ if (s3c24xx_i2s.iis_clk == NULL) {
-+ DBG("failed to get iis_clock\n");
-+ return -ENODEV;
-+ }
-+ clk_enable(s3c24xx_i2s.iis_clk);
-+
-+ /* Configure the I2S pins in correct mode */
-+ s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2410_GPE0_I2SLRCK);
-+ s3c2410_gpio_cfgpin(S3C2410_GPE1, S3C2410_GPE1_I2SSCLK);
-+ s3c2410_gpio_cfgpin(S3C2410_GPE2, S3C2410_GPE2_CDCLK);
-+ s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2410_GPE3_I2SSDI);
-+ s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2410_GPE4_I2SSDO);
-+
-+ writel(S3C2410_IISCON_IISEN, s3c24xx_i2s.regs + S3C2410_IISCON);
-+
-+ s3c24xx_snd_txctrl(0);
-+ s3c24xx_snd_rxctrl(0);
-+
-+ return 0;
-+}
-+
-+#define S3C24XX_I2S_RATES \
-+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
-+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
-+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
-+
-+struct snd_soc_cpu_dai s3c24xx_i2s_dai = {
-+ .name = "s3c24xx-i2s",
-+ .id = 0,
-+ .type = SND_SOC_DAI_I2S,
-+ .probe = s3c24xx_i2s_probe,
-+ .playback = {
-+ .channels_min = 2,
-+ .channels_max = 2,
-+ .rates = S3C24XX_I2S_RATES,
-+ .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
-+ .capture = {
-+ .channels_min = 2,
-+ .channels_max = 2,
-+ .rates = S3C24XX_I2S_RATES,
-+ .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},
-+ .ops = {
-+ .trigger = s3c24xx_i2s_trigger,
-+ .hw_params = s3c24xx_i2s_hw_params,},
-+ .dai_ops = {
-+ .set_fmt = s3c24xx_i2s_set_fmt,
-+ .set_clkdiv = s3c24xx_i2s_set_clkdiv,
-+ .set_sysclk = s3c24xx_i2s_set_sysclk,
-+ },
-+};
-+EXPORT_SYMBOL_GPL(s3c24xx_i2s_dai);
-+
-+/* Module information */
-+MODULE_AUTHOR("Ben Dooks, <ben at simtec.co.uk>");
-+MODULE_DESCRIPTION("s3c24xx I2S SoC Interface");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/s3c24xx/s3c24xx-i2s.h
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/s3c24xx/s3c24xx-i2s.h
-@@ -0,0 +1,35 @@
-+/*
-+ * s3c24xx-i2s.c -- ALSA Soc Audio Layer
-+ *
-+ * Copyright 2005 Wolfson Microelectronics PLC.
-+ * Author: Graeme Gregory
-+ * graeme.gregory at wolfsonmicro.com or linux at wolfsonmicro.com
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or (at your
-+ * option) any later version.
-+ *
-+ * Revision history
-+ * 10th Nov 2006 Initial version.
-+ */
-+
-+#ifndef S3C24XXI2S_H_
-+#define S3C24XXI2S_H_
-+
-+/* clock sources */
-+#define S3C24XX_CLKSRC_PCLK 0
-+#define S3C24XX_CLKSRC_MPLL 1
-+
-+/* Clock dividers */
-+#define S3C24XX_DIV_MCLK 0
-+#define S3C24XX_DIV_BCLK 1
-+#define S3C24XX_DIV_PRESCALER 2
-+
-+/* prescaler */
-+#define S3C24XX_PRESCALE(a,b) \
-+ (((a - 1) << S3C2410_IISPSR_INTSHIFT) | ((b - 1) << S3C2410_IISPSR_EXTSHFIT))
-+
-+u32 s3c24xx_i2s_get_clockrate(void);
-+
-+#endif /*S3C24XXI2S_H_*/
-Index: linux-2.6.21-moko/sound/soc/s3c24xx/s3c24xx-pcm.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/s3c24xx/s3c24xx-pcm.c
-@@ -0,0 +1,464 @@
-+/*
-+ * s3c24xx-pcm.c -- ALSA Soc Audio Layer
-+ *
-+ * (c) 2006 Wolfson Microelectronics PLC.
-+ * Graeme Gregory graeme.gregory at wolfsonmicro.com or linux at wolfsonmicro.com
-+ *
-+ * (c) 2004-2005 Simtec Electronics
-+ * http://armlinux.simtec.co.uk/
-+ * Ben Dooks <ben at simtec.co.uk>
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or (at your
-+ * option) any later version.
-+ *
-+ * Revision history
-+ * 11th Dec 2006 Merged with Simtec driver
-+ * 10th Nov 2006 Initial version.
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/init.h>
-+#include <linux/platform_device.h>
-+#include <linux/slab.h>
-+#include <linux/dma-mapping.h>
-+
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/pcm_params.h>
-+#include <sound/soc.h>
-+
-+#include <asm/dma.h>
-+#include <asm/io.h>
-+#include <asm/hardware.h>
-+#include <asm/arch/dma.h>
-+#include <asm/arch/audio.h>
-+
-+#include "s3c24xx-pcm.h"
-+
-+#define S3C24XX_PCM_DEBUG 0
-+#if S3C24XX_PCM_DEBUG
-+#define DBG(x...) printk(KERN_DEBUG x)
-+#else
-+#define DBG(x...)
-+#endif
-+
-+static const struct snd_pcm_hardware s3c24xx_pcm_hardware = {
-+ .info = SNDRV_PCM_INFO_INTERLEAVED |
-+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
-+ SNDRV_PCM_INFO_MMAP |
-+ SNDRV_PCM_INFO_MMAP_VALID,
-+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
-+ SNDRV_PCM_FMTBIT_U16_LE |
-+ SNDRV_PCM_FMTBIT_U8 |
-+ SNDRV_PCM_FMTBIT_S8,
-+ .channels_min = 2,
-+ .channels_max = 2,
-+ .buffer_bytes_max = 128*1024,
-+ .period_bytes_min = PAGE_SIZE,
-+ .period_bytes_max = PAGE_SIZE*2,
-+ .periods_min = 2,
-+ .periods_max = 128,
-+ .fifo_size = 32,
-+};
-+
-+struct s3c24xx_runtime_data {
-+ spinlock_t lock;
-+ int state;
-+ unsigned int dma_loaded;
-+ unsigned int dma_limit;
-+ unsigned int dma_period;
-+ dma_addr_t dma_start;
-+ dma_addr_t dma_pos;
-+ dma_addr_t dma_end;
-+ struct s3c24xx_pcm_dma_params *params;
-+};
-+
-+/* s3c24xx_pcm_enqueue
-+ *
-+ * place a dma buffer onto the queue for the dma system
-+ * to handle.
-+*/
-+static void s3c24xx_pcm_enqueue(struct snd_pcm_substream *substream)
-+{
-+ struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
-+ dma_addr_t pos = prtd->dma_pos;
-+ int ret;
-+
-+ DBG("Entered %s\n", __FUNCTION__);
-+
-+ while (prtd->dma_loaded < prtd->dma_limit) {
-+ unsigned long len = prtd->dma_period;
-+
-+ DBG("dma_loaded: %d\n",prtd->dma_loaded);
-+
-+ if ((pos + len) > prtd->dma_end) {
-+ len = prtd->dma_end - pos;
-+ DBG(KERN_DEBUG "%s: corrected dma len %ld\n",
-+ __FUNCTION__, len);
-+ }
-+
-+ ret = s3c2410_dma_enqueue(prtd->params->channel, substream, pos, len);
-+
-+ if (ret == 0) {
-+ prtd->dma_loaded++;
-+ pos += prtd->dma_period;
-+ if (pos >= prtd->dma_end)
-+ pos = prtd->dma_start;
-+ } else
-+ break;
-+ }
-+
-+ prtd->dma_pos = pos;
-+}
-+
-+static void s3c24xx_audio_buffdone(struct s3c2410_dma_chan *channel,
-+ void *dev_id, int size,
-+ enum s3c2410_dma_buffresult result)
-+{
-+ struct snd_pcm_substream *substream = dev_id;
-+ struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
-+
-+ DBG("Entered %s\n", __FUNCTION__);
-+
-+ if (result == S3C2410_RES_ABORT || result == S3C2410_RES_ERR)
-+ return;
-+
-+ if (substream)
-+ snd_pcm_period_elapsed(substream);
-+
-+ spin_lock(&prtd->lock);
-+ if (prtd->state & ST_RUNNING) {
-+ prtd->dma_loaded--;
-+ s3c24xx_pcm_enqueue(substream);
-+ }
-+
-+ spin_unlock(&prtd->lock);
-+}
-+
-+static int s3c24xx_pcm_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_pcm_runtime *runtime = substream->runtime;
-+ struct s3c24xx_runtime_data *prtd = runtime->private_data;
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct s3c24xx_pcm_dma_params *dma = rtd->dai->cpu_dai->dma_data;
-+ unsigned long totbytes = params_buffer_bytes(params);
-+ int ret=0;
-+
-+ DBG("Entered %s\n", __FUNCTION__);
-+
-+ /* return if this is a bufferless transfer e.g.
-+ * codec <--> BT codec or GSM modem -- lg FIXME */
-+ if (!dma)
-+ return 0;
-+
-+ /* prepare DMA */
-+ prtd->params = dma;
-+
-+ DBG("params %p, client %p, channel %d\n", prtd->params,
-+ prtd->params->client, prtd->params->channel);
-+
-+ ret = s3c2410_dma_request(prtd->params->channel,
-+ prtd->params->client, NULL);
-+
-+ if (ret) {
-+ DBG(KERN_ERR "failed to get dma channel\n");
-+ return ret;
-+ }
-+
-+ /* channel needs configuring for mem=>device, increment memory addr,
-+ * sync to pclk, half-word transfers to the IIS-FIFO. */
-+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-+ s3c2410_dma_devconfig(prtd->params->channel,
-+ S3C2410_DMASRC_MEM, S3C2410_DISRCC_INC |
-+ S3C2410_DISRCC_APB, prtd->params->dma_addr);
-+
-+ s3c2410_dma_config(prtd->params->channel,
-+ 2, S3C2410_DCON_SYNC_PCLK | S3C2410_DCON_HANDSHAKE);
-+ } else {
-+ s3c2410_dma_config(prtd->params->channel,
-+ 2, S3C2410_DCON_HANDSHAKE | S3C2410_DCON_SYNC_PCLK);
-+
-+ s3c2410_dma_devconfig(prtd->params->channel,
-+ S3C2410_DMASRC_HW, 0x3,
-+ prtd->params->dma_addr);
-+ }
-+
-+ s3c2410_dma_set_buffdone_fn(prtd->params->channel,
-+ s3c24xx_audio_buffdone);
-+
-+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
-+
-+ runtime->dma_bytes = totbytes;
-+
-+ spin_lock_irq(&prtd->lock);
-+ prtd->dma_loaded = 0;
-+ prtd->dma_limit = runtime->hw.periods_min;
-+ prtd->dma_period = params_period_bytes(params);
-+ prtd->dma_start = runtime->dma_addr;
-+ prtd->dma_pos = prtd->dma_start;
-+ prtd->dma_end = prtd->dma_start + totbytes;
-+ spin_unlock_irq(&prtd->lock);
-+
-+ return 0;
-+}
-+
-+static int s3c24xx_pcm_hw_free(struct snd_pcm_substream *substream)
-+{
-+ struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
-+
-+ DBG("Entered %s\n", __FUNCTION__);
-+
-+ /* TODO - do we need to ensure DMA flushed */
-+ snd_pcm_set_runtime_buffer(substream, NULL);
-+
-+ if (prtd->params) {
-+ s3c2410_dma_free(prtd->params->channel, prtd->params->client);
-+ prtd->params = NULL;
-+ }
-+
-+ return 0;
-+}
-+
-+static int s3c24xx_pcm_prepare(struct snd_pcm_substream *substream)
-+{
-+ struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
-+ int ret = 0;
-+
-+ DBG("Entered %s\n", __FUNCTION__);
-+
-+ /* return if this is a bufferless transfer e.g.
-+ * codec <--> BT codec or GSM modem -- lg FIXME */
-+ if (!prtd->params)
-+ return 0;
-+
-+ /* flush the DMA channel */
-+ s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_FLUSH);
-+ prtd->dma_loaded = 0;
-+ prtd->dma_pos = prtd->dma_start;
-+
-+ /* enqueue dma buffers */
-+ s3c24xx_pcm_enqueue(substream);
-+
-+ return ret;
-+}
-+
-+static int s3c24xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-+{
-+ struct s3c24xx_runtime_data *prtd = substream->runtime->private_data;
-+ int ret = 0;
-+
-+ DBG("Entered %s\n", __FUNCTION__);
-+
-+ spin_lock(&prtd->lock);
-+
-+ switch (cmd) {
-+ case SNDRV_PCM_TRIGGER_START:
-+ case SNDRV_PCM_TRIGGER_RESUME:
-+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-+ prtd->state |= ST_RUNNING;
-+ s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_START);
-+ s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_STARTED);
-+ break;
-+
-+ case SNDRV_PCM_TRIGGER_STOP:
-+ case SNDRV_PCM_TRIGGER_SUSPEND:
-+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-+ prtd->state &= ~ST_RUNNING;
-+ s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_STOP);
-+ break;
-+
-+ default:
-+ ret = -EINVAL;
-+ break;
-+ }
-+
-+ spin_unlock(&prtd->lock);
-+
-+ return ret;
-+}
-+
-+static snd_pcm_uframes_t s3c24xx_pcm_pointer(struct snd_pcm_substream *substream)
-+{
-+ struct snd_pcm_runtime *runtime = substream->runtime;
-+ struct s3c24xx_runtime_data *prtd = runtime->private_data;
-+ unsigned long res;
-+ dma_addr_t src, dst;
-+
-+ DBG("Entered %s\n", __FUNCTION__);
-+
-+ spin_lock(&prtd->lock);
-+ s3c2410_dma_getposition(prtd->params->channel, &src, &dst);
-+
-+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
-+ res = dst - prtd->dma_start;
-+ else
-+ res = src - prtd->dma_start;
-+
-+ spin_unlock(&prtd->lock);
-+
-+ DBG("Pointer %x %x\n",src,dst);
-+
-+ /* we seem to be getting the odd error from the pcm library due
-+ * to out-of-bounds pointers. this is maybe due to the dma engine
-+ * not having loaded the new values for the channel before being
-+ * callled... (todo - fix )
-+ */
-+
-+ if (res >= snd_pcm_lib_buffer_bytes(substream)) {
-+ if (res == snd_pcm_lib_buffer_bytes(substream))
-+ res = 0;
-+ }
-+
-+ return bytes_to_frames(substream->runtime, res);
-+}
-+
-+static int s3c24xx_pcm_open(struct snd_pcm_substream *substream)
-+{
-+ struct snd_pcm_runtime *runtime = substream->runtime;
-+ struct s3c24xx_runtime_data *prtd;
-+
-+ int ret;
-+
-+ DBG("Entered %s\n", __FUNCTION__);
-+
-+ snd_soc_set_runtime_hwparams(substream, &s3c24xx_pcm_hardware);
-+
-+ prtd = kzalloc(sizeof(struct s3c24xx_runtime_data), GFP_KERNEL);
-+ if (prtd == NULL)
-+ return -ENOMEM;
-+
-+ spin_lock_init(&prtd->lock);
-+
-+ runtime->private_data = prtd;
-+ return 0;
-+}
-+
-+static int s3c24xx_pcm_close(struct snd_pcm_substream *substream)
-+{
-+ struct snd_pcm_runtime *runtime = substream->runtime;
-+ struct s3c24xx_runtime_data *prtd = runtime->private_data;
-+
-+ DBG("Entered %s\n", __FUNCTION__);
-+
-+ if (prtd)
-+ kfree(prtd);
-+ else
-+ DBG("s3c24xx_pcm_close called with prtd == NULL\n");
-+
-+ return 0;
-+}
-+
-+static int s3c24xx_pcm_mmap(struct snd_pcm_substream *substream,
-+ struct vm_area_struct *vma)
-+{
-+ struct snd_pcm_runtime *runtime = substream->runtime;
-+
-+ DBG("Entered %s\n", __FUNCTION__);
-+
-+ return dma_mmap_writecombine(substream->pcm->card->dev, vma,
-+ runtime->dma_area,
-+ runtime->dma_addr,
-+ runtime->dma_bytes);
-+}
-+
-+static struct snd_pcm_ops s3c24xx_pcm_ops = {
-+ .open = s3c24xx_pcm_open,
-+ .close = s3c24xx_pcm_close,
-+ .ioctl = snd_pcm_lib_ioctl,
-+ .hw_params = s3c24xx_pcm_hw_params,
-+ .hw_free = s3c24xx_pcm_hw_free,
-+ .prepare = s3c24xx_pcm_prepare,
-+ .trigger = s3c24xx_pcm_trigger,
-+ .pointer = s3c24xx_pcm_pointer,
-+ .mmap = s3c24xx_pcm_mmap,
-+};
-+
-+static int s3c24xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
-+{
-+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
-+ struct snd_dma_buffer *buf = &substream->dma_buffer;
-+ size_t size = s3c24xx_pcm_hardware.buffer_bytes_max;
-+
-+ DBG("Entered %s\n", __FUNCTION__);
-+
-+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
-+ buf->dev.dev = pcm->card->dev;
-+ buf->private_data = NULL;
-+ buf->area = dma_alloc_writecombine(pcm->card->dev, size,
-+ &buf->addr, GFP_KERNEL);
-+ if (!buf->area)
-+ return -ENOMEM;
-+ buf->bytes = size;
-+ return 0;
-+}
-+
-+static void s3c24xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
-+{
-+ struct snd_pcm_substream *substream;
-+ struct snd_dma_buffer *buf;
-+ int stream;
-+
-+ DBG("Entered %s\n", __FUNCTION__);
-+
-+ for (stream = 0; stream < 2; stream++) {
-+ substream = pcm->streams[stream].substream;
-+ if (!substream)
-+ continue;
-+
-+ buf = &substream->dma_buffer;
-+ if (!buf->area)
-+ continue;
-+
-+ dma_free_writecombine(pcm->card->dev, buf->bytes,
-+ buf->area, buf->addr);
-+ buf->area = NULL;
-+ }
-+}
-+
-+static u64 s3c24xx_pcm_dmamask = DMA_32BIT_MASK;
-+
-+static int s3c24xx_pcm_new(struct snd_card *card, struct snd_soc_codec_dai *dai,
-+ struct snd_pcm *pcm)
-+{
-+ int ret = 0;
-+
-+ DBG("Entered %s\n", __FUNCTION__);
-+
-+ if (!card->dev->dma_mask)
-+ card->dev->dma_mask = &s3c24xx_pcm_dmamask;
-+ if (!card->dev->coherent_dma_mask)
-+ card->dev->coherent_dma_mask = 0xffffffff;
-+
-+ if (dai->playback.channels_min) {
-+ ret = s3c24xx_pcm_preallocate_dma_buffer(pcm,
-+ SNDRV_PCM_STREAM_PLAYBACK);
-+ if (ret)
-+ goto out;
-+ }
-+
-+ if (dai->capture.channels_min) {
-+ ret = s3c24xx_pcm_preallocate_dma_buffer(pcm,
-+ SNDRV_PCM_STREAM_CAPTURE);
-+ if (ret)
-+ goto out;
-+ }
-+ out:
-+ return ret;
-+}
-+
-+struct snd_soc_platform s3c24xx_soc_platform = {
-+ .name = "s3c24xx-audio",
-+ .pcm_ops = &s3c24xx_pcm_ops,
-+ .pcm_new = s3c24xx_pcm_new,
-+ .pcm_free = s3c24xx_pcm_free_dma_buffers,
-+};
-+
-+EXPORT_SYMBOL_GPL(s3c24xx_soc_platform);
-+
-+MODULE_AUTHOR("Ben Dooks, <ben at simtec.co.uk>");
-+MODULE_DESCRIPTION("Samsung S3C24XX PCM DMA module");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/s3c24xx/s3c24xx-pcm.h
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/s3c24xx/s3c24xx-pcm.h
-@@ -0,0 +1,32 @@
-+/*
-+ * s3c24xx-pcm.h --
-+ *
-+ * 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.
-+ *
-+ * ALSA PCM interface for the Samsung S3C24xx CPU
-+ */
-+
-+#ifndef _S3C24XX_PCM_H
-+#define _S3C24XX_PCM_H
-+
-+#define ST_RUNNING (1<<0)
-+#define ST_OPENED (1<<1)
-+
-+struct s3c24xx_pcm_dma_params {
-+ struct s3c2410_dma_client *client; /* stream identifier */
-+ int channel; /* Channel ID */
-+ dma_addr_t dma_addr;
-+};
-+
-+#define S3C24XX_DAI_I2S 0
-+
-+extern struct snd_soc_cpu_dai s3c24xx_i2s_dai;
-+
-+/* platform data */
-+extern struct snd_soc_platform s3c24xx_soc_platform;
-+extern struct snd_ac97_bus_ops s3c24xx_ac97_ops;
-+
-+#endif
-Index: linux-2.6.21-moko/sound/soc/s3c24xx/smdk2440.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/s3c24xx/smdk2440.c
-@@ -0,0 +1,318 @@
-+/*
-+ * smdk2440.c -- ALSA Soc Audio Layer
-+ *
-+ * (c) 2006 Wolfson Microelectronics PLC.
-+ * Graeme Gregory graeme.gregory at wolfsonmicro.com or linux at wolfsonmicro.com
-+ *
-+ * (c) 2004-2005 Simtec Electronics
-+ * http://armlinux.simtec.co.uk/
-+ * Ben Dooks <ben at simtec.co.uk>
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or (at your
-+ * option) any later version.
-+ *
-+ * This module is a modified version of the s3c24xx I2S driver supplied by
-+ * Ben Dooks of Simtec and rejigged to the ASoC style at Wolfson Microelectronics
-+ *
-+ * Revision history
-+ * 11th Dec 2006 Merged with Simtec driver
-+ * 10th Nov 2006 Initial version.
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/moduleparam.h>
-+#include <linux/timer.h>
-+#include <linux/interrupt.h>
-+#include <linux/platform_device.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+
-+#include <asm/mach-types.h>
-+#include <asm/hardware.h>
-+#include <asm/hardware/scoop.h>
-+#include <asm/arch/regs-iis.h>
-+#include <asm/arch/regs-clock.h>
-+#include <asm/arch/regs-gpio.h>
-+#include <asm/arch/audio.h>
-+#include <asm/io.h>
-+#include <asm/arch/spi-gpio.h>
-+#include "../codecs/uda1380.h"
-+#include "s3c24xx-pcm.h"
-+#include "s3c24xx-i2s.h"
-+
-+#define SMDK2440_DEBUG 0
-+#if SMDK2440_DEBUG
-+#define DBG(x...) printk(KERN_DEBUG x)
-+#else
-+#define DBG(x...)
-+#endif
-+
-+/* audio clock in Hz */
-+#define SMDK_CLOCK_SOURCE S3C24XX_CLKSRC_MPLL
-+#define SMDK_CRYSTAL_CLOCK 16934400
-+
-+static int smdk2440_startup(struct snd_pcm_substream *substream)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_codec *codec = rtd->socdev->codec;
-+
-+ DBG("Entered smdk2440_startup\n");
-+
-+ return 0;
-+}
-+
-+static void smdk2440_shutdown(struct snd_pcm_substream *substream)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_codec *codec = rtd->socdev->codec;
-+
-+ DBG("Entered smdk2440_shutdown\n");
-+}
-+
-+static int smdk2440_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
-+ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-+ unsigned long iis_clkrate;
-+ int div, div256, div384, diff256, diff384, bclk, mclk;
-+ int ret;
-+ unsigned int rate=params_rate(params);
-+
-+ DBG("Entered %s\n",__FUNCTION__);
-+
-+ iis_clkrate = s3c24xx_i2s_get_clockrate();
-+
-+ /* Using PCLK doesnt seem to suit audio particularly well on these cpu's
-+ */
-+
-+ div256 = iis_clkrate / (rate * 256);
-+ div384 = iis_clkrate / (rate * 384);
-+
-+ if (((iis_clkrate / div256) - (rate * 256)) <
-+ ((rate * 256) - (iis_clkrate / (div256 + 1)))) {
-+ diff256 = (iis_clkrate / div256) - (rate * 256);
-+ } else {
-+ div256++;
-+ diff256 = (iis_clkrate / div256) - (rate * 256);
-+ }
-+
-+ if (((iis_clkrate / div384) - (rate * 384)) <
-+ ((rate * 384) - (iis_clkrate / (div384 + 1)))) {
-+ diff384 = (iis_clkrate / div384) - (rate * 384);
-+ } else {
-+ div384++;
-+ diff384 = (iis_clkrate / div384) - (rate * 384);
-+ }
-+
-+ DBG("diff256 %d, diff384 %d\n", diff256, diff384);
-+
-+ if (diff256<=diff384) {
-+ DBG("Selected 256FS\n");
-+ div = div256 - 1;
-+ bclk = S3C2410_IISMOD_256FS;
-+ } else {
-+ DBG("Selected 384FS\n");
-+ div = div384 - 1;
-+ bclk = S3C2410_IISMOD_384FS;
-+ }
-+
-+ /* set codec DAI configuration */
-+ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
-+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set cpu DAI configuration */
-+ ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
-+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set the audio system clock for DAC and ADC */
-+ ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, S3C24XX_CLKSRC_PCLK,
-+ rate, SND_SOC_CLOCK_OUT);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set MCLK division for sample rate */
-+ ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, S3C2410_IISMOD_32FS );
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set BCLK division for sample rate */
-+ ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_BCLK, bclk);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set prescaler division for sample rate */
-+ ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
-+ S3C24XX_PRESCALE(div,div));
-+ if (ret < 0)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+static struct snd_soc_ops smdk2440_ops = {
-+ .startup = smdk2440_startup,
-+ .shutdown = smdk2440_shutdown,
-+ .hw_params = smdk2440_hw_params,
-+};
-+
-+/* smdk2440 machine dapm widgets */
-+static const struct snd_soc_dapm_widget smdk2440_dapm_widgets[] = {
-+SND_SOC_DAPM_HP("Headphone Jack", NULL),
-+SND_SOC_DAPM_MIC("Mic Jack", NULL),
-+SND_SOC_DAPM_LINE("Line Jack", NULL),
-+};
-+
-+/* smdk2440 machine audio map (connections to the codec pins) */
-+static const char* audio_map[][3] = {
-+ /* headphone connected to HPOUT */
-+ {"Headphone Jack", NULL, "HPOUT"},
-+
-+ /* mic is connected to MICIN (via right channel of headphone jack) */
-+ {"MICIN", NULL, "Mic Jack"},
-+ {"MICIN", NULL, "Line Jack"},
-+
-+ {NULL, NULL, NULL},
-+};
-+
-+/*
-+ * Logic for a UDA1341 as attached to SMDK2440
-+ */
-+static int smdk2440_uda1341_init(struct snd_soc_codec *codec)
-+{
-+ int i, err;
-+
-+ DBG("Staring smdk2440 init\n");
-+
-+ /* Add smdk2440 specific widgets */
-+ for(i = 0; i < ARRAY_SIZE(smdk2440_dapm_widgets); i++) {
-+ snd_soc_dapm_new_control(codec, &smdk2440_dapm_widgets[i]);
-+ }
-+
-+ /* Set up smdk2440 specific audio path audio_mapnects */
-+ for(i = 0; audio_map[i][0] != NULL; i++) {
-+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
-+ audio_map[i][1], audio_map[i][2]);
-+ }
-+
-+ snd_soc_dapm_sync_endpoints(codec);
-+
-+ DBG("Ending smdk2440 init\n");
-+
-+ return 0;
-+}
-+
-+/* s3c24xx digital audio interface glue - connects codec <--> CPU */
-+static struct snd_soc_dai_link s3c24xx_dai = {
-+ .name = "WM8731",
-+ .stream_name = "WM8731",
-+ .cpu_dai = &s3c24xx_i2s_dai,
-+ .codec_dai = uda1380_dai,
-+ .init = smdk2440_uda1341_init,
-+ .ops = &smdk2440_ops,
-+};
-+
-+/* smdk2440 audio machine driver */
-+static struct snd_soc_machine snd_soc_machine_smdk2440 = {
-+ .name = "SMDK2440",
-+ .dai_link = &s3c24xx_dai,
-+ .num_links = 1,
-+};
-+
-+static struct uda1380_setup_data smdk2440_uda1380_setup = {
-+ .i2c_address = 0x00,
-+};
-+
-+/* s3c24xx audio subsystem */
-+static struct snd_soc_device s3c24xx_snd_devdata = {
-+ .machine = &snd_soc_machine_smdk2440,
-+ .platform = &s3c24xx_soc_platform,
-+ .codec_dev = &soc_codec_dev_uda1380,
-+ .codec_data = &smdk2440_uda1380_setup,
-+};
-+
-+static struct platform_device *s3c24xx_snd_device;
-+
-+struct smdk2440_spi_device {
-+ struct device *dev;
-+};
-+
-+static struct smdk2440_spi_device smdk2440_spi_devdata = {
-+};
-+
-+struct s3c2410_spigpio_info smdk2440_spi_devinfo = {
-+ .pin_clk = S3C2410_GPF4,
-+ .pin_mosi = S3C2410_GPF5,
-+ .pin_miso = S3C2410_GPF6,
-+ //.board_size,
-+ //.board_info,
-+ .chip_select=NULL,
-+};
-+
-+static struct platform_device *smdk2440_spi_device;
-+
-+static int __init smdk2440_init(void)
-+{
-+ int ret;
-+
-+ if (!machine_is_smdk2440() && !machine_is_s3c2440()) {
-+ DBG("%d\n",machine_arch_type);
-+ DBG("Not a SMDK2440\n");
-+ return -ENODEV;
-+ }
-+
-+ s3c24xx_snd_device = platform_device_alloc("soc-audio", -1);
-+ if (!s3c24xx_snd_device) {
-+ DBG("platform_dev_alloc failed\n");
-+ return -ENOMEM;
-+ }
-+
-+ platform_set_drvdata(s3c24xx_snd_device, &s3c24xx_snd_devdata);
-+ s3c24xx_snd_devdata.dev = &s3c24xx_snd_device->dev;
-+ ret = platform_device_add(s3c24xx_snd_device);
-+
-+ if (ret)
-+ platform_device_put(s3c24xx_snd_device);
-+
-+ // Create a bitbanged SPI device
-+
-+ smdk2440_spi_device = platform_device_alloc("s3c24xx-spi-gpio",-1);
-+ if (!smdk2440_spi_device) {
-+ DBG("smdk2440_spi_device : platform_dev_alloc failed\n");
-+ return -ENOMEM;
-+ }
-+ DBG("Return Code %d\n",ret);
-+
-+ platform_set_drvdata(smdk2440_spi_device, &smdk2440_spi_devdata);
-+ smdk2440_spi_devdata.dev = &smdk2440_spi_device->dev;
-+ smdk2440_spi_devdata.dev->platform_data = &smdk2440_spi_devinfo;
-+ ret = platform_device_add(smdk2440_spi_device);
-+
-+ if (ret)
-+ platform_device_put(smdk2440_spi_device);
-+
-+ return ret;
-+}
-+
-+static void __exit smdk2440_exit(void)
-+{
-+ platform_device_unregister(s3c24xx_snd_device);
-+}
-+
-+module_init(smdk2440_init);
-+module_exit(smdk2440_exit);
-+
-+/* Module information */
-+MODULE_AUTHOR("Ben Dooks, <ben at simtec.co.uk>");
-+MODULE_DESCRIPTION("ALSA SoC SMDK2440");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/codecs/wm8956.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/codecs/wm8956.c
-@@ -0,0 +1,724 @@
-+/*
-+ * wm8956.c -- WM8956 ALSA SoC Audio driver
-+ *
-+ * Author: Liam Girdwood
-+ *
-+ * 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.
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/moduleparam.h>
-+#include <linux/init.h>
-+#include <linux/delay.h>
-+#include <linux/pm.h>
-+#include <linux/i2c.h>
-+#include <linux/platform_device.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/pcm_params.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+#include <sound/initval.h>
-+
-+#include "wm8956.h"
-+
-+#define AUDIO_NAME "wm8956"
-+#define WM8956_VERSION "0.2"
-+
-+/*
-+ * Debug
-+ */
-+
-+#define WM8956_DEBUG 0
-+
-+#ifdef WM8956_DEBUG
-+#define dbg(format, arg...) \
-+ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
-+#else
-+#define dbg(format, arg...) do {} while (0)
-+#endif
-+#define err(format, arg...) \
-+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
-+#define info(format, arg...) \
-+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
-+#define warn(format, arg...) \
-+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
-+
-+struct snd_soc_codec_device soc_codec_dev_wm8956;
-+
-+/*
-+ * wm8956 register cache
-+ * We can't read the WM8956 register space when we are
-+ * using 2 wire for device control, so we cache them instead.
-+ */
-+static const u16 wm8956_reg[WM8956_CACHEREGNUM] = {
-+ 0x0097, 0x0097, 0x0000, 0x0000,
-+ 0x0000, 0x0008, 0x0000, 0x000a,
-+ 0x01c0, 0x0000, 0x00ff, 0x00ff,
-+ 0x0000, 0x0000, 0x0000, 0x0000, //r15
-+ 0x0000, 0x007b, 0x0100, 0x0032,
-+ 0x0000, 0x00c3, 0x00c3, 0x01c0,
-+ 0x0000, 0x0000, 0x0000, 0x0000,
-+ 0x0000, 0x0000, 0x0000, 0x0000, //r31
-+ 0x0100, 0x0100, 0x0050, 0x0050,
-+ 0x0050, 0x0050, 0x0000, 0x0000,
-+ 0x0000, 0x0000, 0x0040, 0x0000,
-+ 0x0000, 0x0050, 0x0050, 0x0000, //47
-+ 0x0002, 0x0037, 0x004d, 0x0080,
-+ 0x0008, 0x0031, 0x0026, 0x00e9,
-+};
-+
-+/*
-+ * read wm8956 register cache
-+ */
-+static inline unsigned int wm8956_read_reg_cache(struct snd_soc_codec *codec,
-+ unsigned int reg)
-+{
-+ u16 *cache = codec->reg_cache;
-+ if (reg == WM8956_RESET)
-+ return 0;
-+ if (reg >= WM8956_CACHEREGNUM)
-+ return -1;
-+ return cache[reg];
-+}
-+
-+/*
-+ * write wm8956 register cache
-+ */
-+static inline void wm8956_write_reg_cache(struct snd_soc_codec *codec,
-+ u16 reg, unsigned int value)
-+{
-+ u16 *cache = codec->reg_cache;
-+ if (reg >= WM8956_CACHEREGNUM)
-+ return;
-+ cache[reg] = value;
-+}
-+
-+/*
-+ * write to the WM8956 register space
-+ */
-+static int wm8956_write(struct snd_soc_codec *codec, unsigned int reg,
-+ unsigned int value)
-+{
-+ u8 data[2];
-+
-+ /* data is
-+ * D15..D9 WM8956 register offset
-+ * D8...D0 register data
-+ */
-+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
-+ data[1] = value & 0x00ff;
-+
-+ wm8956_write_reg_cache (codec, reg, value);
-+ if (codec->hw_write(codec->control_data, data, 2) == 2)
-+ return 0;
-+ else
-+ return -EIO;
-+}
-+
-+#define wm8956_reset(c) wm8956_write(c, WM8956_RESET, 0)
-+
-+/* todo - complete enumerated controls */
-+static const char *wm8956_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
-+
-+static const struct soc_enum wm8956_enum[] = {
-+ SOC_ENUM_SINGLE(WM8956_DACCTL1, 1, 4, wm8956_deemph),
-+};
-+
-+/* to complete */
-+static const struct snd_kcontrol_new wm8956_snd_controls[] = {
-+
-+SOC_DOUBLE_R("Headphone Playback Volume", WM8956_LOUT1, WM8956_ROUT1,
-+ 0, 127, 0),
-+SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8956_LOUT1, WM8956_ROUT1,
-+ 7, 1, 0),
-+SOC_DOUBLE_R("PCM Volume", WM8956_LDAC, WM8956_RDAC,
-+ 0, 127, 0),
-+
-+SOC_ENUM("Playback De-emphasis", wm8956_enum[0]),
-+};
-+
-+/* add non dapm controls */
-+static int wm8956_add_controls(struct snd_soc_codec *codec)
-+{
-+ int err, i;
-+
-+ for (i = 0; i < ARRAY_SIZE(wm8956_snd_controls); i++) {
-+ if ((err = snd_ctl_add(codec->card,
-+ snd_soc_cnew(&wm8956_snd_controls[i],codec, NULL))) < 0)
-+ return err;
-+ }
-+
-+ return 0;
-+}
-+
-+/* Left Output Mixer */
-+static const struct snd_kcontrol_new wm8956_loutput_mixer_controls[] = {
-+SOC_DAPM_SINGLE("Left PCM Playback Switch", WM8956_LOUTMIX1, 8, 1, 0),
-+};
-+
-+/* Right Output Mixer */
-+static const struct snd_kcontrol_new wm8956_routput_mixer_controls[] = {
-+SOC_DAPM_SINGLE("Right PCM Playback Switch", WM8956_ROUTMIX2, 8, 1, 0),
-+};
-+
-+static const struct snd_soc_dapm_widget wm8956_dapm_widgets[] = {
-+SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
-+ &wm8956_loutput_mixer_controls[0],
-+ ARRAY_SIZE(wm8956_loutput_mixer_controls)),
-+SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
-+ &wm8956_loutput_mixer_controls[0],
-+ ARRAY_SIZE(wm8956_routput_mixer_controls)),
-+};
-+
-+static const char *intercon[][3] = {
-+ /* TODO */
-+ /* terminator */
-+ {NULL, NULL, NULL},
-+};
-+
-+static int wm8956_add_widgets(struct snd_soc_codec *codec)
-+{
-+ int i;
-+
-+ for(i = 0; i < ARRAY_SIZE(wm8956_dapm_widgets); i++) {
-+ snd_soc_dapm_new_control(codec, &wm8956_dapm_widgets[i]);
-+ }
-+
-+ /* set up audio path interconnects */
-+ for(i = 0; intercon[i][0] != NULL; i++) {
-+ snd_soc_dapm_connect_input(codec, intercon[i][0],
-+ intercon[i][1], intercon[i][2]);
-+ }
-+
-+ snd_soc_dapm_new_widgets(codec);
-+ return 0;
-+}
-+
-+static int wm8956_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
-+ unsigned int fmt)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 iface = 0;
-+
-+ /* set master/slave audio interface */
-+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
-+ case SND_SOC_DAIFMT_CBM_CFM:
-+ iface |= 0x0040;
-+ break;
-+ case SND_SOC_DAIFMT_CBS_CFS:
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ /* interface format */
-+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
-+ case SND_SOC_DAIFMT_I2S:
-+ iface |= 0x0002;
-+ break;
-+ case SND_SOC_DAIFMT_RIGHT_J:
-+ break;
-+ case SND_SOC_DAIFMT_LEFT_J:
-+ iface |= 0x0001;
-+ break;
-+ case SND_SOC_DAIFMT_DSP_A:
-+ iface |= 0x0003;
-+ break;
-+ case SND_SOC_DAIFMT_DSP_B:
-+ iface |= 0x0013;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ /* clock inversion */
-+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
-+ case SND_SOC_DAIFMT_NB_NF:
-+ break;
-+ case SND_SOC_DAIFMT_IB_IF:
-+ iface |= 0x0090;
-+ break;
-+ case SND_SOC_DAIFMT_IB_NF:
-+ iface |= 0x0080;
-+ break;
-+ case SND_SOC_DAIFMT_NB_IF:
-+ iface |= 0x0010;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ /* set iface */
-+ wm8956_write(codec, WM8956_IFACE1, iface);
-+ return 0;
-+}
-+
-+static int wm8956_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_device *socdev = rtd->socdev;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ u16 iface = wm8956_read_reg_cache(codec, WM8956_IFACE1) & 0xfff3;
-+
-+ /* bit size */
-+ switch (params_format(params)) {
-+ case SNDRV_PCM_FORMAT_S16_LE:
-+ break;
-+ case SNDRV_PCM_FORMAT_S20_3LE:
-+ iface |= 0x0004;
-+ break;
-+ case SNDRV_PCM_FORMAT_S24_LE:
-+ iface |= 0x0008;
-+ break;
-+ }
-+
-+ /* set iface */
-+ wm8956_write(codec, WM8956_IFACE1, iface);
-+ return 0;
-+}
-+
-+static int wm8956_mute(struct snd_soc_codec_dai *dai, int mute)
-+{
-+ struct snd_soc_codec *codec = dai->codec;
-+ u16 mute_reg = wm8956_read_reg_cache(codec, WM8956_DACCTL1) & 0xfff7;
-+
-+ if (mute)
-+ wm8956_write(codec, WM8956_DACCTL1, mute_reg | 0x8);
-+ else
-+ wm8956_write(codec, WM8956_DACCTL1, mute_reg);
-+ return 0;
-+}
-+
-+static int wm8956_dapm_event(struct snd_soc_codec *codec, int event)
-+{
-+#if 0
-+ switch (event) {
-+ case SNDRV_CTL_POWER_D0: /* full On */
-+ /* vref/mid, osc on, dac unmute */
-+
-+ break;
-+ case SNDRV_CTL_POWER_D1: /* partial On */
-+ case SNDRV_CTL_POWER_D2: /* partial On */
-+ break;
-+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
-+ /* everything off except vref/vmid, */
-+ break;
-+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
-+ /* everything off, dac mute, inactive */
-+ break;
-+ }
-+#endif
-+ // tmp
-+ wm8956_write(codec, WM8956_POWER1, 0xffff);
-+ wm8956_write(codec, WM8956_POWER2, 0xffff);
-+ wm8956_write(codec, WM8956_POWER3, 0xffff);
-+ codec->dapm_state = event;
-+ return 0;
-+}
-+
-+/* PLL divisors */
-+struct _pll_div {
-+ u32 pre_div:1;
-+ u32 n:4;
-+ u32 k:24;
-+};
-+
-+static struct _pll_div pll_div;
-+
-+/* The size in bits of the pll divide multiplied by 10
-+ * to allow rounding later */
-+#define FIXED_PLL_SIZE ((1 << 24) * 10)
-+
-+static void pll_factors(unsigned int target, unsigned int source)
-+{
-+ unsigned long long Kpart;
-+ unsigned int K, Ndiv, Nmod;
-+
-+ Ndiv = target / source;
-+ if (Ndiv < 6) {
-+ source >>= 1;
-+ pll_div.pre_div = 1;
-+ Ndiv = target / source;
-+ } else
-+ pll_div.pre_div = 0;
-+
-+ if ((Ndiv < 6) || (Ndiv > 12))
-+ printk(KERN_WARNING
-+ "WM8956 N value outwith recommended range! N = %d\n",Ndiv);
-+
-+ pll_div.n = Ndiv;
-+ Nmod = target % source;
-+ Kpart = FIXED_PLL_SIZE * (long long)Nmod;
-+
-+ do_div(Kpart, source);
-+
-+ K = Kpart & 0xFFFFFFFF;
-+
-+ /* Check if we need to round */
-+ if ((K % 10) >= 5)
-+ K += 5;
-+
-+ /* Move down to proper range now rounding is done */
-+ K /= 10;
-+
-+ pll_div.k = K;
-+}
-+
-+static int wm8956_set_dai_pll(struct snd_soc_codec_dai *codec_dai,
-+ int pll_id, unsigned int freq_in, unsigned int freq_out)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 reg;
-+ int found = 0;
-+#if 0
-+ if (freq_in == 0 || freq_out == 0) {
-+ /* disable the pll */
-+ /* turn PLL power off */
-+ }
-+#endif
-+
-+ pll_factors(freq_out * 8, freq_in);
-+
-+ if (!found)
-+ return -EINVAL;
-+
-+ reg = wm8956_read_reg_cache(codec, WM8956_PLLN) & 0x1e0;
-+ wm8956_write(codec, WM8956_PLLN, reg | (pll_div.pre_div << 4)
-+ | pll_div.n);
-+ wm8956_write(codec, WM8956_PLLK1, pll_div.k >> 16 );
-+ wm8956_write(codec, WM8956_PLLK2, (pll_div.k >> 8) & 0xff);
-+ wm8956_write(codec, WM8956_PLLK3, pll_div.k &0xff);
-+ wm8956_write(codec, WM8956_CLOCK1, 4);
-+
-+ return 0;
-+}
-+
-+static int wm8956_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
-+ int div_id, int div)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 reg;
-+
-+ switch (div_id) {
-+ case WM8956_SYSCLKSEL:
-+ reg = wm8956_read_reg_cache(codec, WM8956_CLOCK1) & 0x1fe;
-+ wm8956_write(codec, WM8956_CLOCK1, reg | div);
-+ break;
-+ case WM8956_SYSCLKDIV:
-+ reg = wm8956_read_reg_cache(codec, WM8956_CLOCK1) & 0x1f9;
-+ wm8956_write(codec, WM8956_CLOCK1, reg | div);
-+ break;
-+ case WM8956_DACDIV:
-+ reg = wm8956_read_reg_cache(codec, WM8956_CLOCK1) & 0x1c7;
-+ wm8956_write(codec, WM8956_CLOCK1, reg | div);
-+ break;
-+ case WM8956_OPCLKDIV:
-+ reg = wm8956_read_reg_cache(codec, WM8956_PLLN) & 0x03f;
-+ wm8956_write(codec, WM8956_PLLN, reg | div);
-+ break;
-+ case WM8956_DCLKDIV:
-+ reg = wm8956_read_reg_cache(codec, WM8956_CLOCK2) & 0x03f;
-+ wm8956_write(codec, WM8956_CLOCK2, reg | div);
-+ break;
-+ case WM8956_TOCLKSEL:
-+ reg = wm8956_read_reg_cache(codec, WM8956_ADDCTL1) & 0x1fd;
-+ wm8956_write(codec, WM8956_ADDCTL1, reg | div);
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ return 0;
-+}
-+
-+#define WM8956_RATES \
-+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
-+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
-+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
-+
-+#define WM8956_FORMATS \
-+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
-+ SNDRV_PCM_FMTBIT_S24_LE)
-+
-+struct snd_soc_codec_dai wm8956_dai = {
-+ .name = "WM8956",
-+ .playback = {
-+ .stream_name = "Playback",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = WM8956_RATES,
-+ .formats = WM8956_FORMATS,},
-+ .capture = {
-+ .stream_name = "Capture",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = WM8956_RATES,
-+ .formats = WM8956_FORMATS,},
-+ .ops = {
-+ .hw_params = wm8956_hw_params,
-+ },
-+ .dai_ops = {
-+ .digital_mute = wm8956_mute,
-+ .set_fmt = wm8956_set_dai_fmt,
-+ .set_clkdiv = wm8956_set_dai_clkdiv,
-+ .set_pll = wm8956_set_dai_pll,
-+ },
-+};
-+EXPORT_SYMBOL_GPL(wm8956_dai);
-+
-+
-+/* To complete PM */
-+static int wm8956_suspend(struct platform_device *pdev, pm_message_t state)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+
-+ wm8956_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
-+ return 0;
-+}
-+
-+static int wm8956_resume(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+ int i;
-+ u8 data[2];
-+ u16 *cache = codec->reg_cache;
-+
-+ /* Sync reg_cache with the hardware */
-+ for (i = 0; i < ARRAY_SIZE(wm8956_reg); i++) {
-+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
-+ data[1] = cache[i] & 0x00ff;
-+ codec->hw_write(codec->control_data, data, 2);
-+ }
-+ wm8956_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
-+ wm8956_dapm_event(codec, codec->suspend_dapm_state);
-+ return 0;
-+}
-+
-+/*
-+ * initialise the WM8956 driver
-+ * register the mixer and dsp interfaces with the kernel
-+ */
-+static int wm8956_init(struct snd_soc_device *socdev)
-+{
-+ struct snd_soc_codec *codec = socdev->codec;
-+ int reg, ret = 0;
-+
-+ codec->name = "WM8956";
-+ codec->owner = THIS_MODULE;
-+ codec->read = wm8956_read_reg_cache;
-+ codec->write = wm8956_write;
-+ codec->dapm_event = wm8956_dapm_event;
-+ codec->dai = &wm8956_dai;
-+ codec->num_dai = 1;
-+ codec->reg_cache_size = ARRAY_SIZE(wm8956_reg);
-+
-+ codec->reg_cache =
-+ kzalloc(sizeof(u16) * ARRAY_SIZE(wm8956_reg), GFP_KERNEL);
-+ if (codec->reg_cache == NULL)
-+ return -ENOMEM;
-+ memcpy(codec->reg_cache,
-+ wm8956_reg, sizeof(u16) * ARRAY_SIZE(wm8956_reg));
-+ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8956_reg);
-+
-+ wm8956_reset(codec);
-+
-+ /* register pcms */
-+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
-+ if (ret < 0) {
-+ printk(KERN_ERR "wm8956: failed to create pcms\n");
-+ goto pcm_err;
-+ }
-+
-+ /* power on device */
-+ wm8956_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
-+
-+ /* set the update bits */
-+ reg = wm8956_read_reg_cache(codec, WM8956_LOUT1);
-+ wm8956_write(codec, WM8956_LOUT1, reg | 0x0100);
-+ reg = wm8956_read_reg_cache(codec, WM8956_ROUT1);
-+ wm8956_write(codec, WM8956_ROUT1, reg | 0x0100);
-+
-+ wm8956_add_controls(codec);
-+ wm8956_add_widgets(codec);
-+ ret = snd_soc_register_card(socdev);
-+ if (ret < 0) {
-+ printk(KERN_ERR "wm8956: failed to register card\n");
-+ goto card_err;
-+ }
-+ return ret;
-+
-+card_err:
-+ snd_soc_free_pcms(socdev);
-+ snd_soc_dapm_free(socdev);
-+pcm_err:
-+ kfree(codec->reg_cache);
-+ return ret;
-+}
-+
-+static struct snd_soc_device *wm8956_socdev;
-+
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+
-+/*
-+ * WM8956 2 wire address is 0x1a
-+ */
-+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
-+
-+/* Magic definition of all other variables and things */
-+I2C_CLIENT_INSMOD;
-+
-+static struct i2c_driver wm8956_i2c_driver;
-+static struct i2c_client client_template;
-+
-+/* If the i2c layer weren't so broken, we could pass this kind of data
-+ around */
-+
-+static int wm8956_codec_probe(struct i2c_adapter *adap, int addr, int kind)
-+{
-+ struct snd_soc_device *socdev = wm8956_socdev;
-+ struct wm8956_setup_data *setup = socdev->codec_data;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ struct i2c_client *i2c;
-+ int ret;
-+
-+ if (addr != setup->i2c_address)
-+ return -ENODEV;
-+
-+ client_template.adapter = adap;
-+ client_template.addr = addr;
-+
-+ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
-+ if (i2c == NULL) {
-+ kfree(codec);
-+ return -ENOMEM;
-+ }
-+ memcpy(i2c, &client_template, sizeof(struct i2c_client));
-+ i2c_set_clientdata(i2c, codec);
-+ codec->control_data = i2c;
-+
-+ ret = i2c_attach_client(i2c);
-+ if (ret < 0) {
-+ err("failed to attach codec at addr %x\n", addr);
-+ goto err;
-+ }
-+
-+ ret = wm8956_init(socdev);
-+ if (ret < 0) {
-+ err("failed to initialise WM8956\n");
-+ goto err;
-+ }
-+ return ret;
-+
-+err:
-+ kfree(codec);
-+ kfree(i2c);
-+ return ret;
-+}
-+
-+static int wm8956_i2c_detach(struct i2c_client *client)
-+{
-+ struct snd_soc_codec* codec = i2c_get_clientdata(client);
-+ i2c_detach_client(client);
-+ kfree(codec->reg_cache);
-+ kfree(client);
-+ return 0;
-+}
-+
-+static int wm8956_i2c_attach(struct i2c_adapter *adap)
-+{
-+ return i2c_probe(adap, &addr_data, wm8956_codec_probe);
-+}
-+
-+// tmp
-+#define I2C_DRIVERID_WM8956 0xfefe
-+
-+/* corgi i2c codec control layer */
-+static struct i2c_driver wm8956_i2c_driver = {
-+ .driver = {
-+ .name = "WM8956 I2C Codec",
-+ .owner = THIS_MODULE,
-+ },
-+ .id = I2C_DRIVERID_WM8956,
-+ .attach_adapter = wm8956_i2c_attach,
-+ .detach_client = wm8956_i2c_detach,
-+ .command = NULL,
-+};
-+
-+static struct i2c_client client_template = {
-+ .name = "WM8956",
-+ .driver = &wm8956_i2c_driver,
-+};
-+#endif
-+
-+static int wm8956_probe(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct wm8956_setup_data *setup;
-+ struct snd_soc_codec *codec;
-+ int ret = 0;
-+
-+ info("WM8956 Audio Codec %s", WM8956_VERSION);
-+
-+ setup = socdev->codec_data;
-+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
-+ if (codec == NULL)
-+ return -ENOMEM;
-+
-+ socdev->codec = codec;
-+ mutex_init(&codec->mutex);
-+ INIT_LIST_HEAD(&codec->dapm_widgets);
-+ INIT_LIST_HEAD(&codec->dapm_paths);
-+
-+ wm8956_socdev = socdev;
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+ if (setup->i2c_address) {
-+ normal_i2c[0] = setup->i2c_address;
-+ codec->hw_write = (hw_write_t)i2c_master_send;
-+ ret = i2c_add_driver(&wm8956_i2c_driver);
-+ if (ret != 0)
-+ printk(KERN_ERR "can't add i2c driver");
-+ }
-+#else
-+ /* Add other interfaces here */
-+#endif
-+ return ret;
-+}
-+
-+/* power down chip */
-+static int wm8956_remove(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+
-+ if (codec->control_data)
-+ wm8956_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
-+
-+ snd_soc_free_pcms(socdev);
-+ snd_soc_dapm_free(socdev);
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+ i2c_del_driver(&wm8956_i2c_driver);
-+#endif
-+ kfree(codec);
-+
-+ return 0;
-+}
-+
-+struct snd_soc_codec_device soc_codec_dev_wm8956 = {
-+ .probe = wm8956_probe,
-+ .remove = wm8956_remove,
-+ .suspend = wm8956_suspend,
-+ .resume = wm8956_resume,
-+};
-+
-+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8956);
-+
-+MODULE_DESCRIPTION("ASoC WM8956 driver");
-+MODULE_AUTHOR("Liam Girdwood");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/codecs/wm8956.h
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/codecs/wm8956.h
-@@ -0,0 +1,116 @@
-+/*
-+ * wm8956.h -- WM8956 Soc Audio driver
-+ *
-+ * 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 _WM8956_H
-+#define _WM8956_H
-+
-+/* WM8956 register space */
-+
-+
-+#define WM8956_CACHEREGNUM 56
-+
-+#define WM8956_LINVOL 0x0
-+#define WM8956_RINVOL 0x1
-+#define WM8956_LOUT1 0x2
-+#define WM8956_ROUT1 0x3
-+#define WM8956_CLOCK1 0x4
-+#define WM8956_DACCTL1 0x5
-+#define WM8956_DACCTL2 0x6
-+#define WM8956_IFACE1 0x7
-+#define WM8956_CLOCK2 0x8
-+#define WM8956_IFACE2 0x9
-+#define WM8956_LDAC 0xa
-+#define WM8956_RDAC 0xb
-+
-+#define WM8956_RESET 0xf
-+#define WM8956_3D 0x10
-+
-+#define WM8956_ADDCTL1 0x17
-+#define WM8956_ADDCTL2 0x18
-+#define WM8956_POWER1 0x19
-+#define WM8956_POWER2 0x1a
-+#define WM8956_ADDCTL3 0x1b
-+#define WM8956_APOP1 0x1c
-+#define WM8956_APOP2 0x1d
-+
-+#define WM8956_LINPATH 0x20
-+#define WM8956_RINPATH 0x21
-+#define WM8956_LOUTMIX1 0x22
-+
-+#define WM8956_ROUTMIX2 0x25
-+#define WM8956_MONOMIX1 0x26
-+#define WM8956_MONOMIX2 0x27
-+#define WM8956_LOUT2 0x28
-+#define WM8956_ROUT2 0x29
-+#define WM8956_MONO 0x2a
-+#define WM8956_INBMIX1 0x2b
-+#define WM8956_INBMIX2 0x2c
-+#define WM8956_BYPASS1 0x2d
-+#define WM8956_BYPASS2 0x2e
-+#define WM8956_POWER3 0x2f
-+#define WM8956_ADDCTL4 0x30
-+#define WM8956_CLASSD1 0x31
-+
-+#define WM8956_CLASSD3 0x33
-+#define WM8956_PLLN 0x34
-+#define WM8956_PLLK1 0x35
-+#define WM8956_PLLK2 0x36
-+#define WM8956_PLLK3 0x37
-+
-+
-+/*
-+ * WM8956 Clock dividers
-+ */
-+#define WM8956_SYSCLKDIV 0
-+#define WM8956_DACDIV 1
-+#define WM8956_OPCLKDIV 2
-+#define WM8956_DCLKDIV 3
-+#define WM8956_TOCLKSEL 4
-+#define WM8956_SYSCLKSEL 5
-+
-+#define WM8956_SYSCLK_DIV_1 (0 << 1)
-+#define WM8956_SYSCLK_DIV_2 (2 << 1)
-+
-+#define WM8956_SYSCLK_MCLK (0 << 0)
-+#define WM8956_SYSCLK_PLL (1 << 0)
-+
-+#define WM8956_DAC_DIV_1 (0 << 3)
-+#define WM8956_DAC_DIV_1_5 (1 << 3)
-+#define WM8956_DAC_DIV_2 (2 << 3)
-+#define WM8956_DAC_DIV_3 (3 << 3)
-+#define WM8956_DAC_DIV_4 (4 << 3)
-+#define WM8956_DAC_DIV_5_5 (5 << 3)
-+#define WM8956_DAC_DIV_6 (6 << 3)
-+
-+#define WM8956_DCLK_DIV_1_5 (0 << 6)
-+#define WM8956_DCLK_DIV_2 (1 << 6)
-+#define WM8956_DCLK_DIV_3 (2 << 6)
-+#define WM8956_DCLK_DIV_4 (3 << 6)
-+#define WM8956_DCLK_DIV_6 (4 << 6)
-+#define WM8956_DCLK_DIV_8 (5 << 6)
-+#define WM8956_DCLK_DIV_12 (6 << 6)
-+#define WM8956_DCLK_DIV_16 (7 << 6)
-+
-+#define WM8956_TOCLK_F19 (0 << 1)
-+#define WM8956_TOCLK_F21 (1 << 1)
-+
-+#define WM8956_OPCLK_DIV_1 (0 << 0)
-+#define WM8956_OPCLK_DIV_2 (1 << 0)
-+#define WM8956_OPCLK_DIV_3 (2 << 0)
-+#define WM8956_OPCLK_DIV_4 (3 << 0)
-+#define WM8956_OPCLK_DIV_5_5 (4 << 0)
-+#define WM8956_OPCLK_DIV_6 (5 << 0)
-+
-+struct wm8956_setup_data {
-+ unsigned short i2c_address;
-+};
-+
-+extern struct snd_soc_codec_dai wm8956_dai;
-+extern struct snd_soc_codec_device soc_codec_dev_wm8956;
-+
-+#endif
-Index: linux-2.6.21-moko/sound/soc/codecs/wm8960.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/codecs/wm8960.c
-@@ -0,0 +1,766 @@
-+/*
-+ * wm8960.c -- WM8960 ALSA SoC Audio driver
-+ *
-+ * Author: Liam Girdwood
-+ *
-+ * 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.
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/moduleparam.h>
-+#include <linux/init.h>
-+#include <linux/delay.h>
-+#include <linux/pm.h>
-+#include <linux/i2c.h>
-+#include <linux/platform_device.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/pcm_params.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+#include <sound/initval.h>
-+
-+#include "wm8960.h"
-+
-+#define AUDIO_NAME "wm8960"
-+#define WM8960_VERSION "0.1"
-+
-+/*
-+ * Debug
-+ */
-+
-+#define WM8960_DEBUG 0
-+
-+#ifdef WM8960_DEBUG
-+#define dbg(format, arg...) \
-+ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
-+#else
-+#define dbg(format, arg...) do {} while (0)
-+#endif
-+#define err(format, arg...) \
-+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
-+#define info(format, arg...) \
-+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
-+#define warn(format, arg...) \
-+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
-+
-+struct snd_soc_codec_device soc_codec_dev_wm8960;
-+
-+/*
-+ * wm8960 register cache
-+ * We can't read the WM8960 register space when we are
-+ * using 2 wire for device control, so we cache them instead.
-+ */
-+static const u16 wm8960_reg[WM8960_CACHEREGNUM] = {
-+ 0x0097, 0x0097, 0x0000, 0x0000,
-+ 0x0000, 0x0008, 0x0000, 0x000a,
-+ 0x01c0, 0x0000, 0x00ff, 0x00ff,
-+ 0x0000, 0x0000, 0x0000, 0x0000, //r15
-+ 0x0000, 0x007b, 0x0100, 0x0032,
-+ 0x0000, 0x00c3, 0x00c3, 0x01c0,
-+ 0x0000, 0x0000, 0x0000, 0x0000,
-+ 0x0000, 0x0000, 0x0000, 0x0000, //r31
-+ 0x0100, 0x0100, 0x0050, 0x0050,
-+ 0x0050, 0x0050, 0x0000, 0x0000,
-+ 0x0000, 0x0000, 0x0040, 0x0000,
-+ 0x0000, 0x0050, 0x0050, 0x0000, //47
-+ 0x0002, 0x0037, 0x004d, 0x0080,
-+ 0x0008, 0x0031, 0x0026, 0x00e9,
-+};
-+
-+/*
-+ * read wm8960 register cache
-+ */
-+static inline unsigned int wm8960_read_reg_cache(struct snd_soc_codec *codec,
-+ unsigned int reg)
-+{
-+ u16 *cache = codec->reg_cache;
-+ if (reg == WM8960_RESET)
-+ return 0;
-+ if (reg >= WM8960_CACHEREGNUM)
-+ return -1;
-+ return cache[reg];
-+}
-+
-+/*
-+ * write wm8960 register cache
-+ */
-+static inline void wm8960_write_reg_cache(struct snd_soc_codec *codec,
-+ u16 reg, unsigned int value)
-+{
-+ u16 *cache = codec->reg_cache;
-+ if (reg >= WM8960_CACHEREGNUM)
-+ return;
-+ cache[reg] = value;
-+}
-+
-+/*
-+ * write to the WM8960 register space
-+ */
-+static int wm8960_write(struct snd_soc_codec *codec, unsigned int reg,
-+ unsigned int value)
-+{
-+ u8 data[2];
-+
-+ /* data is
-+ * D15..D9 WM8960 register offset
-+ * D8...D0 register data
-+ */
-+ data[0] = (reg << 1) | ((value >> 8) & 0x0001);
-+ data[1] = value & 0x00ff;
-+
-+ wm8960_write_reg_cache (codec, reg, value);
-+ if (codec->hw_write(codec->control_data, data, 2) == 2)
-+ return 0;
-+ else
-+ return -EIO;
-+}
-+
-+#define wm8960_reset(c) wm8960_write(c, WM8960_RESET, 0)
-+
-+/* enumerated controls */
-+static const char *wm8960_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
-+static const char *wm8960_polarity[] = {"No Inversion", "Left Inverted",
-+ "Right Inverted", "Stereo Inversion"};
-+static const char *wm8960_3d_upper_cutoff[] = {"High", "Low"};
-+static const char *wm8960_3d_lower_cutoff[] = {"Low", "High"};
-+static const char *wm8960_alcfunc[] = {"Off", "Right", "Left", "Stereo"};
-+static const char *wm8960_alcmode[] = {"ALC", "Limiter"};
-+
-+static const struct soc_enum wm8960_enum[] = {
-+ SOC_ENUM_SINGLE(WM8960_DACCTL1, 1, 4, wm8960_deemph),
-+ SOC_ENUM_SINGLE(WM8960_DACCTL1, 5, 4, wm8960_polarity),
-+ SOC_ENUM_SINGLE(WM8960_DACCTL2, 5, 4, wm8960_polarity),
-+ SOC_ENUM_SINGLE(WM8960_3D, 6, 2, wm8960_3d_upper_cutoff),
-+ SOC_ENUM_SINGLE(WM8960_3D, 5, 2, wm8960_3d_lower_cutoff),
-+ SOC_ENUM_SINGLE(WM8960_ALC1, 7, 4, wm8960_alcfunc),
-+ SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode),
-+};
-+
-+/* to complete */
-+static const struct snd_kcontrol_new wm8960_snd_controls[] = {
-+SOC_DOUBLE_R("Capture Volume", WM8960_LINVOL, WM8960_RINVOL,
-+ 0, 63, 0),
-+SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL,
-+ 6, 1, 0),
-+SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL,
-+ 7, 1, 0),
-+SOC_DOUBLE_R("Headphone Playback Volume", WM8960_LOUT1, WM8960_ROUT1,
-+ 0, 127, 0),
-+SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8960_LOUT1, WM8960_ROUT1,
-+ 7, 1, 0),
-+SOC_SINGLE("PCM Playback -6dB Switch", WM8960_DACCTL1, 7, 1, 0),
-+SOC_ENUM("ADC Polarity", wm8960_enum[1]),
-+SOC_ENUM("Playback De-emphasis", wm8960_enum[0]),
-+SOC_SINGLE("ADC High Pass Filter Switch", WM8960_DACCTL1, 0, 1, 0),
-+
-+SOC_ENUM("DAC Polarity", wm8960_enum[2]),
-+
-+SOC_DOUBLE_R("PCM Volume", WM8960_LDAC, WM8960_RDAC,
-+ 0, 127, 0),
-+
-+SOC_ENUM("3D Filter Upper Cut-Off", wm8960_enum[3]),
-+SOC_ENUM("3D Filter Lower Cut-Off", wm8960_enum[4]),
-+SOC_SINGLE("3D Volume", WM8960_3D, 1, 15, 0),
-+SOC_SINGLE("3D Switch", WM8960_3D, 0, 1, 0),
-+
-+SOC_ENUM("ALC Function", wm8960_enum[5]),
-+SOC_SINGLE("ALC Max Gain", WM8960_ALC1, 4, 7, 0),
-+SOC_SINGLE("ALC Target", WM8960_ALC1, 0, 15, 1),
-+SOC_SINGLE("ALC Min Gain", WM8960_ALC2, 4, 7, 0),
-+SOC_SINGLE("ALC Hold Time", WM8960_ALC2, 0, 15, 0),
-+SOC_ENUM("ALC Mode", wm8960_enum[6]),
-+SOC_SINGLE("ALC Decay", WM8960_ALC3, 4, 15, 0),
-+SOC_SINGLE("ALC Attack", WM8960_ALC3, 0, 15, 0),
-+
-+SOC_SINGLE("Noise Gate Threshold", WM8960_NOISEG, 3, 31, 0),
-+SOC_SINGLE("Noise Gate Switch", WM8960_NOISEG, 0, 1, 0),
-+
-+SOC_DOUBLE_R("ADC PCM Capture Volume", WM8960_LINPATH, WM8960_RINPATH,
-+ 0, 127, 0),
-+};
-+
-+/* add non dapm controls */
-+static int wm8960_add_controls(struct snd_soc_codec *codec)
-+{
-+ int err, i;
-+
-+ for (i = 0; i < ARRAY_SIZE(wm8960_snd_controls); i++) {
-+ if ((err = snd_ctl_add(codec->card,
-+ snd_soc_cnew(&wm8960_snd_controls[i],codec, NULL))) < 0)
-+ return err;
-+ }
-+
-+ return 0;
-+}
-+
-+/* Left Output Mixer */
-+static const struct snd_kcontrol_new wm8960_loutput_mixer_controls[] = {
-+SOC_DAPM_SINGLE("Left PCM Playback Switch", WM8960_LOUTMIX1, 8, 1, 0),
-+};
-+
-+/* Right Output Mixer */
-+static const struct snd_kcontrol_new wm8960_routput_mixer_controls[] = {
-+SOC_DAPM_SINGLE("Right PCM Playback Switch", WM8960_ROUTMIX2, 8, 1, 0),
-+};
-+
-+static const struct snd_soc_dapm_widget wm8960_dapm_widgets[] = {
-+SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
-+ &wm8960_loutput_mixer_controls[0],
-+ ARRAY_SIZE(wm8960_loutput_mixer_controls)),
-+SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
-+ &wm8960_loutput_mixer_controls[0],
-+ ARRAY_SIZE(wm8960_routput_mixer_controls)),
-+};
-+
-+static const char *intercon[][3] = {
-+ /* TODO */
-+ /* terminator */
-+ {NULL, NULL, NULL},
-+};
-+
-+static int wm8960_add_widgets(struct snd_soc_codec *codec)
-+{
-+ int i;
-+
-+ for(i = 0; i < ARRAY_SIZE(wm8960_dapm_widgets); i++) {
-+ snd_soc_dapm_new_control(codec, &wm8960_dapm_widgets[i]);
-+ }
-+
-+ /* set up audio path interconnects */
-+ for(i = 0; intercon[i][0] != NULL; i++) {
-+ snd_soc_dapm_connect_input(codec, intercon[i][0],
-+ intercon[i][1], intercon[i][2]);
-+ }
-+
-+ snd_soc_dapm_new_widgets(codec);
-+ return 0;
-+}
-+
-+static int wm8960_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
-+ unsigned int fmt)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 iface = 0;
-+
-+ /* set master/slave audio interface */
-+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
-+ case SND_SOC_DAIFMT_CBM_CFM:
-+ iface |= 0x0040;
-+ break;
-+ case SND_SOC_DAIFMT_CBS_CFS:
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ /* interface format */
-+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
-+ case SND_SOC_DAIFMT_I2S:
-+ iface |= 0x0002;
-+ break;
-+ case SND_SOC_DAIFMT_RIGHT_J:
-+ break;
-+ case SND_SOC_DAIFMT_LEFT_J:
-+ iface |= 0x0001;
-+ break;
-+ case SND_SOC_DAIFMT_DSP_A:
-+ iface |= 0x0003;
-+ break;
-+ case SND_SOC_DAIFMT_DSP_B:
-+ iface |= 0x0013;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ /* clock inversion */
-+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
-+ case SND_SOC_DAIFMT_NB_NF:
-+ break;
-+ case SND_SOC_DAIFMT_IB_IF:
-+ iface |= 0x0090;
-+ break;
-+ case SND_SOC_DAIFMT_IB_NF:
-+ iface |= 0x0080;
-+ break;
-+ case SND_SOC_DAIFMT_NB_IF:
-+ iface |= 0x0010;
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ /* set iface */
-+ wm8960_write(codec, WM8960_IFACE1, iface);
-+ return 0;
-+}
-+
-+static int wm8960_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_device *socdev = rtd->socdev;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ u16 iface = wm8960_read_reg_cache(codec, WM8960_IFACE1) & 0xfff3;
-+
-+ /* bit size */
-+ switch (params_format(params)) {
-+ case SNDRV_PCM_FORMAT_S16_LE:
-+ break;
-+ case SNDRV_PCM_FORMAT_S20_3LE:
-+ iface |= 0x0004;
-+ break;
-+ case SNDRV_PCM_FORMAT_S24_LE:
-+ iface |= 0x0008;
-+ break;
-+ }
-+
-+ /* set iface */
-+ wm8960_write(codec, WM8960_IFACE1, iface);
-+ return 0;
-+}
-+
-+static int wm8960_mute(struct snd_soc_codec_dai *dai, int mute)
-+{
-+ struct snd_soc_codec *codec = dai->codec;
-+ u16 mute_reg = wm8960_read_reg_cache(codec, WM8960_DACCTL1) & 0xfff7;
-+
-+ if (mute)
-+ wm8960_write(codec, WM8960_DACCTL1, mute_reg | 0x8);
-+ else
-+ wm8960_write(codec, WM8960_DACCTL1, mute_reg);
-+ return 0;
-+}
-+
-+static int wm8960_dapm_event(struct snd_soc_codec *codec, int event)
-+{
-+#if 0
-+ switch (event) {
-+ case SNDRV_CTL_POWER_D0: /* full On */
-+ /* vref/mid, osc on, dac unmute */
-+
-+ break;
-+ case SNDRV_CTL_POWER_D1: /* partial On */
-+ case SNDRV_CTL_POWER_D2: /* partial On */
-+ break;
-+ case SNDRV_CTL_POWER_D3hot: /* Off, with power */
-+ /* everything off except vref/vmid, */
-+ break;
-+ case SNDRV_CTL_POWER_D3cold: /* Off, without power */
-+ /* everything off, dac mute, inactive */
-+ break;
-+ }
-+#endif
-+ // tmp
-+ wm8960_write(codec, WM8960_POWER1, 0xffff);
-+ wm8960_write(codec, WM8960_POWER2, 0xffff);
-+ wm8960_write(codec, WM8960_POWER3, 0xffff);
-+ codec->dapm_state = event;
-+ return 0;
-+}
-+
-+/* PLL divisors */
-+struct _pll_div {
-+ u32 pre_div:1;
-+ u32 n:4;
-+ u32 k:24;
-+};
-+
-+static struct _pll_div pll_div;
-+
-+/* The size in bits of the pll divide multiplied by 10
-+ * to allow rounding later */
-+#define FIXED_PLL_SIZE ((1 << 24) * 10)
-+
-+static void pll_factors(unsigned int target, unsigned int source)
-+{
-+ unsigned long long Kpart;
-+ unsigned int K, Ndiv, Nmod;
-+
-+ Ndiv = target / source;
-+ if (Ndiv < 6) {
-+ source >>= 1;
-+ pll_div.pre_div = 1;
-+ Ndiv = target / source;
-+ } else
-+ pll_div.pre_div = 0;
-+
-+ if ((Ndiv < 6) || (Ndiv > 12))
-+ printk(KERN_WARNING
-+ "WM8960 N value outwith recommended range! N = %d\n",Ndiv);
-+
-+ pll_div.n = Ndiv;
-+ Nmod = target % source;
-+ Kpart = FIXED_PLL_SIZE * (long long)Nmod;
-+
-+ do_div(Kpart, source);
-+
-+ K = Kpart & 0xFFFFFFFF;
-+
-+ /* Check if we need to round */
-+ if ((K % 10) >= 5)
-+ K += 5;
-+
-+ /* Move down to proper range now rounding is done */
-+ K /= 10;
-+
-+ pll_div.k = K;
-+}
-+
-+static int wm8960_set_dai_pll(struct snd_soc_codec_dai *codec_dai,
-+ int pll_id, unsigned int freq_in, unsigned int freq_out)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 reg;
-+ int found = 0;
-+#if 0
-+ if (freq_in == 0 || freq_out == 0) {
-+ /* disable the pll */
-+ /* turn PLL power off */
-+ }
-+#endif
-+
-+ pll_factors(freq_out * 8, freq_in);
-+
-+ if (!found)
-+ return -EINVAL;
-+
-+ reg = wm8960_read_reg_cache(codec, WM8960_PLLN) & 0x1e0;
-+ wm8960_write(codec, WM8960_PLLN, reg | (pll_div.pre_div << 4)
-+ | pll_div.n);
-+ wm8960_write(codec, WM8960_PLLK1, pll_div.k >> 16 );
-+ wm8960_write(codec, WM8960_PLLK2, (pll_div.k >> 8) & 0xff);
-+ wm8960_write(codec, WM8960_PLLK3, pll_div.k &0xff);
-+ wm8960_write(codec, WM8960_CLOCK1, 4);
-+
-+ return 0;
-+}
-+
-+static int wm8960_set_dai_clkdiv(struct snd_soc_codec_dai *codec_dai,
-+ int div_id, int div)
-+{
-+ struct snd_soc_codec *codec = codec_dai->codec;
-+ u16 reg;
-+
-+ switch (div_id) {
-+ case WM8960_SYSCLKSEL:
-+ reg = wm8960_read_reg_cache(codec, WM8960_CLOCK1) & 0x1fe;
-+ wm8960_write(codec, WM8960_CLOCK1, reg | div);
-+ break;
-+ case WM8960_SYSCLKDIV:
-+ reg = wm8960_read_reg_cache(codec, WM8960_CLOCK1) & 0x1f9;
-+ wm8960_write(codec, WM8960_CLOCK1, reg | div);
-+ break;
-+ case WM8960_DACDIV:
-+ reg = wm8960_read_reg_cache(codec, WM8960_CLOCK1) & 0x1c7;
-+ wm8960_write(codec, WM8960_CLOCK1, reg | div);
-+ break;
-+ case WM8960_OPCLKDIV:
-+ reg = wm8960_read_reg_cache(codec, WM8960_PLLN) & 0x03f;
-+ wm8960_write(codec, WM8960_PLLN, reg | div);
-+ break;
-+ case WM8960_DCLKDIV:
-+ reg = wm8960_read_reg_cache(codec, WM8960_CLOCK2) & 0x03f;
-+ wm8960_write(codec, WM8960_CLOCK2, reg | div);
-+ break;
-+ case WM8960_TOCLKSEL:
-+ reg = wm8960_read_reg_cache(codec, WM8960_ADDCTL1) & 0x1fd;
-+ wm8960_write(codec, WM8960_ADDCTL1, reg | div);
-+ break;
-+ default:
-+ return -EINVAL;
-+ }
-+
-+ return 0;
-+}
-+
-+#define WM8960_RATES \
-+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
-+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
-+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
-+
-+#define WM8960_FORMATS \
-+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
-+ SNDRV_PCM_FMTBIT_S24_LE)
-+
-+struct snd_soc_codec_dai wm8960_dai = {
-+ .name = "WM8960",
-+ .playback = {
-+ .stream_name = "Playback",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = WM8960_RATES,
-+ .formats = WM8960_FORMATS,},
-+ .capture = {
-+ .stream_name = "Capture",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = WM8960_RATES,
-+ .formats = WM8960_FORMATS,},
-+ .ops = {
-+ .hw_params = wm8960_hw_params,
-+ },
-+ .dai_ops = {
-+ .digital_mute = wm8960_mute,
-+ .set_fmt = wm8960_set_dai_fmt,
-+ .set_clkdiv = wm8960_set_dai_clkdiv,
-+ .set_pll = wm8960_set_dai_pll,
-+ },
-+};
-+EXPORT_SYMBOL_GPL(wm8960_dai);
-+
-+
-+/* To complete PM */
-+static int wm8960_suspend(struct platform_device *pdev, pm_message_t state)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+
-+ wm8960_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
-+ return 0;
-+}
-+
-+static int wm8960_resume(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+ int i;
-+ u8 data[2];
-+ u16 *cache = codec->reg_cache;
-+
-+ /* Sync reg_cache with the hardware */
-+ for (i = 0; i < ARRAY_SIZE(wm8960_reg); i++) {
-+ data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
-+ data[1] = cache[i] & 0x00ff;
-+ codec->hw_write(codec->control_data, data, 2);
-+ }
-+ wm8960_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
-+ wm8960_dapm_event(codec, codec->suspend_dapm_state);
-+ return 0;
-+}
-+
-+/*
-+ * initialise the WM8960 driver
-+ * register the mixer and dsp interfaces with the kernel
-+ */
-+static int wm8960_init(struct snd_soc_device *socdev)
-+{
-+ struct snd_soc_codec *codec = socdev->codec;
-+ int reg, ret = 0;
-+
-+ codec->name = "WM8960";
-+ codec->owner = THIS_MODULE;
-+ codec->read = wm8960_read_reg_cache;
-+ codec->write = wm8960_write;
-+ codec->dapm_event = wm8960_dapm_event;
-+ codec->dai = &wm8960_dai;
-+ codec->num_dai = 1;
-+ codec->reg_cache_size = ARRAY_SIZE(wm8960_reg);
-+
-+ codec->reg_cache =
-+ kzalloc(sizeof(u16) * ARRAY_SIZE(wm8960_reg), GFP_KERNEL);
-+ if (codec->reg_cache == NULL)
-+ return -ENOMEM;
-+ memcpy(codec->reg_cache,
-+ wm8960_reg, sizeof(u16) * ARRAY_SIZE(wm8960_reg));
-+ codec->reg_cache_size = sizeof(u16) * ARRAY_SIZE(wm8960_reg);
-+
-+ wm8960_reset(codec);
-+
-+ /* register pcms */
-+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
-+ if (ret < 0) {
-+ printk(KERN_ERR "wm8960: failed to create pcms\n");
-+ goto pcm_err;
-+ }
-+
-+ /* power on device */
-+ wm8960_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
-+
-+ /* set the update bits */
-+ reg = wm8960_read_reg_cache(codec, WM8960_LOUT1);
-+ wm8960_write(codec, WM8960_LOUT1, reg | 0x0100);
-+ reg = wm8960_read_reg_cache(codec, WM8960_ROUT1);
-+ wm8960_write(codec, WM8960_ROUT1, reg | 0x0100);
-+
-+ wm8960_add_controls(codec);
-+ wm8960_add_widgets(codec);
-+ ret = snd_soc_register_card(socdev);
-+ if (ret < 0) {
-+ printk(KERN_ERR "wm8960: failed to register card\n");
-+ goto card_err;
-+ }
-+ return ret;
-+
-+card_err:
-+ snd_soc_free_pcms(socdev);
-+ snd_soc_dapm_free(socdev);
-+pcm_err:
-+ kfree(codec->reg_cache);
-+ return ret;
-+}
-+
-+static struct snd_soc_device *wm8960_socdev;
-+
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+
-+/*
-+ * WM8960 2 wire address is 0x1a
-+ */
-+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
-+
-+/* Magic definition of all other variables and things */
-+I2C_CLIENT_INSMOD;
-+
-+static struct i2c_driver wm8960_i2c_driver;
-+static struct i2c_client client_template;
-+
-+/* If the i2c layer weren't so broken, we could pass this kind of data
-+ around */
-+
-+static int wm8960_codec_probe(struct i2c_adapter *adap, int addr, int kind)
-+{
-+ struct snd_soc_device *socdev = wm8960_socdev;
-+ struct wm8960_setup_data *setup = socdev->codec_data;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ struct i2c_client *i2c;
-+ int ret;
-+
-+ if (addr != setup->i2c_address)
-+ return -ENODEV;
-+
-+ client_template.adapter = adap;
-+ client_template.addr = addr;
-+
-+ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
-+ if (i2c == NULL) {
-+ kfree(codec);
-+ return -ENOMEM;
-+ }
-+ memcpy(i2c, &client_template, sizeof(struct i2c_client));
-+ i2c_set_clientdata(i2c, codec);
-+ codec->control_data = i2c;
-+
-+ ret = i2c_attach_client(i2c);
-+ if (ret < 0) {
-+ err("failed to attach codec at addr %x\n", addr);
-+ goto err;
-+ }
-+
-+ ret = wm8960_init(socdev);
-+ if (ret < 0) {
-+ err("failed to initialise WM8960\n");
-+ goto err;
-+ }
-+ return ret;
-+
-+err:
-+ kfree(codec);
-+ kfree(i2c);
-+ return ret;
-+}
-+
-+static int wm8960_i2c_detach(struct i2c_client *client)
-+{
-+ struct snd_soc_codec* codec = i2c_get_clientdata(client);
-+ i2c_detach_client(client);
-+ kfree(codec->reg_cache);
-+ kfree(client);
-+ return 0;
-+}
-+
-+static int wm8960_i2c_attach(struct i2c_adapter *adap)
-+{
-+ return i2c_probe(adap, &addr_data, wm8960_codec_probe);
-+}
-+
-+// tmp
-+#define I2C_DRIVERID_WM8960 0xfefe
-+
-+/* corgi i2c codec control layer */
-+static struct i2c_driver wm8960_i2c_driver = {
-+ .driver = {
-+ .name = "WM8960 I2C Codec",
-+ .owner = THIS_MODULE,
-+ },
-+ .id = I2C_DRIVERID_WM8960,
-+ .attach_adapter = wm8960_i2c_attach,
-+ .detach_client = wm8960_i2c_detach,
-+ .command = NULL,
-+};
-+
-+static struct i2c_client client_template = {
-+ .name = "WM8960",
-+ .driver = &wm8960_i2c_driver,
-+};
-+#endif
-+
-+static int wm8960_probe(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct wm8960_setup_data *setup;
-+ struct snd_soc_codec *codec;
-+ int ret = 0;
-+
-+ info("WM8960 Audio Codec %s", WM8960_VERSION);
-+
-+ setup = socdev->codec_data;
-+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
-+ if (codec == NULL)
-+ return -ENOMEM;
-+
-+ socdev->codec = codec;
-+ mutex_init(&codec->mutex);
-+ INIT_LIST_HEAD(&codec->dapm_widgets);
-+ INIT_LIST_HEAD(&codec->dapm_paths);
-+
-+ wm8960_socdev = socdev;
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+ if (setup->i2c_address) {
-+ normal_i2c[0] = setup->i2c_address;
-+ codec->hw_write = (hw_write_t)i2c_master_send;
-+ ret = i2c_add_driver(&wm8960_i2c_driver);
-+ if (ret != 0)
-+ printk(KERN_ERR "can't add i2c driver");
-+ }
-+#else
-+ /* Add other interfaces here */
-+#endif
-+ return ret;
-+}
-+
-+/* power down chip */
-+static int wm8960_remove(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+
-+ if (codec->control_data)
-+ wm8960_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
-+
-+ snd_soc_free_pcms(socdev);
-+ snd_soc_dapm_free(socdev);
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+ i2c_del_driver(&wm8960_i2c_driver);
-+#endif
-+ kfree(codec);
-+
-+ return 0;
-+}
-+
-+struct snd_soc_codec_device soc_codec_dev_wm8960 = {
-+ .probe = wm8960_probe,
-+ .remove = wm8960_remove,
-+ .suspend = wm8960_suspend,
-+ .resume = wm8960_resume,
-+};
-+
-+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8960);
-+
-+MODULE_DESCRIPTION("ASoC WM8960 driver");
-+MODULE_AUTHOR("Liam Girdwood");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/codecs/wm8960.h
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/codecs/wm8960.h
-@@ -0,0 +1,121 @@
-+/*
-+ * wm8960.h -- WM8960 Soc Audio driver
-+ *
-+ * 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 _WM8960_H
-+#define _WM8960_H
-+
-+/* WM8960 register space */
-+
-+
-+#define WM8960_CACHEREGNUM 56
-+
-+#define WM8960_LINVOL 0x0
-+#define WM8960_RINVOL 0x1
-+#define WM8960_LOUT1 0x2
-+#define WM8960_ROUT1 0x3
-+#define WM8960_CLOCK1 0x4
-+#define WM8960_DACCTL1 0x5
-+#define WM8960_DACCTL2 0x6
-+#define WM8960_IFACE1 0x7
-+#define WM8960_CLOCK2 0x8
-+#define WM8960_IFACE2 0x9
-+#define WM8960_LDAC 0xa
-+#define WM8960_RDAC 0xb
-+
-+#define WM8960_RESET 0xf
-+#define WM8960_3D 0x10
-+#define WM8960_ALC1 0x11
-+#define WM8960_ALC2 0x12
-+#define WM8960_ALC3 0x13
-+#define WM8960_NOISEG 0x14
-+#define WM8960_LADC 0x15
-+#define WM8960_RADC 0x16
-+#define WM8960_ADDCTL1 0x17
-+#define WM8960_ADDCTL2 0x18
-+#define WM8960_POWER1 0x19
-+#define WM8960_POWER2 0x1a
-+#define WM8960_ADDCTL3 0x1b
-+#define WM8960_APOP1 0x1c
-+#define WM8960_APOP2 0x1d
-+
-+#define WM8960_LINPATH 0x20
-+#define WM8960_RINPATH 0x21
-+#define WM8960_LOUTMIX1 0x22
-+
-+#define WM8960_ROUTMIX2 0x25
-+#define WM8960_MONOMIX1 0x26
-+#define WM8960_MONOMIX2 0x27
-+#define WM8960_LOUT2 0x28
-+#define WM8960_ROUT2 0x29
-+#define WM8960_MONO 0x2a
-+#define WM8960_INBMIX1 0x2b
-+#define WM8960_INBMIX2 0x2c
-+#define WM8960_BYPASS1 0x2d
-+#define WM8960_BYPASS2 0x2e
-+#define WM8960_POWER3 0x2f
-+#define WM8960_ADDCTL4 0x30
-+#define WM8960_CLASSD1 0x31
-+
-+#define WM8960_CLASSD3 0x33
-+#define WM8960_PLLN 0x34
-+#define WM8960_PLLK1 0x35
-+#define WM8960_PLLK2 0x36
-+#define WM8960_PLLK3 0x37
-+
-+
-+/*
-+ * WM8960 Clock dividers
-+ */
-+#define WM8960_SYSCLKDIV 0
-+#define WM8960_DACDIV 1
-+#define WM8960_OPCLKDIV 2
-+#define WM8960_DCLKDIV 3
-+#define WM8960_TOCLKSEL 4
-+#define WM8960_SYSCLKSEL 5
-+
-+#define WM8960_SYSCLK_DIV_1 (0 << 1)
-+#define WM8960_SYSCLK_DIV_2 (2 << 1)
-+
-+#define WM8960_SYSCLK_MCLK (0 << 0)
-+#define WM8960_SYSCLK_PLL (1 << 0)
-+
-+#define WM8960_DAC_DIV_1 (0 << 3)
-+#define WM8960_DAC_DIV_1_5 (1 << 3)
-+#define WM8960_DAC_DIV_2 (2 << 3)
-+#define WM8960_DAC_DIV_3 (3 << 3)
-+#define WM8960_DAC_DIV_4 (4 << 3)
-+#define WM8960_DAC_DIV_5_5 (5 << 3)
-+#define WM8960_DAC_DIV_6 (6 << 3)
-+
-+#define WM8960_DCLK_DIV_1_5 (0 << 6)
-+#define WM8960_DCLK_DIV_2 (1 << 6)
-+#define WM8960_DCLK_DIV_3 (2 << 6)
-+#define WM8960_DCLK_DIV_4 (3 << 6)
-+#define WM8960_DCLK_DIV_6 (4 << 6)
-+#define WM8960_DCLK_DIV_8 (5 << 6)
-+#define WM8960_DCLK_DIV_12 (6 << 6)
-+#define WM8960_DCLK_DIV_16 (7 << 6)
-+
-+#define WM8960_TOCLK_F19 (0 << 1)
-+#define WM8960_TOCLK_F21 (1 << 1)
-+
-+#define WM8960_OPCLK_DIV_1 (0 << 0)
-+#define WM8960_OPCLK_DIV_2 (1 << 0)
-+#define WM8960_OPCLK_DIV_3 (2 << 0)
-+#define WM8960_OPCLK_DIV_4 (3 << 0)
-+#define WM8960_OPCLK_DIV_5_5 (4 << 0)
-+#define WM8960_OPCLK_DIV_6 (5 << 0)
-+
-+struct wm8960_setup_data {
-+ unsigned short i2c_address;
-+};
-+
-+extern struct snd_soc_codec_dai wm8960_dai;
-+extern struct snd_soc_codec_device soc_codec_dev_wm8960;
-+
-+#endif
-Index: linux-2.6.21-moko/sound/soc/s3c24xx/smdk2440_wm8956.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/s3c24xx/smdk2440_wm8956.c
-@@ -0,0 +1,335 @@
-+/*
-+ * smdk2440.c -- ALSA Soc Audio Layer
-+ *
-+ * (c) 2006 Wolfson Microelectronics PLC.
-+ * Graeme Gregory graeme.gregory at wolfsonmicro.com or linux at wolfsonmicro.com
-+ *
-+ * (c) 2004-2005 Simtec Electronics
-+ * http://armlinux.simtec.co.uk/
-+ * Ben Dooks <ben at simtec.co.uk>
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or (at your
-+ * option) any later version.
-+ *
-+ * This module is a modified version of the s3c24xx I2S driver supplied by
-+ * Ben Dooks of Simtec and rejigged to the ASoC style at Wolfson Microelectronics
-+ *
-+ * Revision history
-+ * 11th Dec 2006 Merged with Simtec driver
-+ * 10th Nov 2006 Initial version.
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/moduleparam.h>
-+#include <linux/timer.h>
-+#include <linux/interrupt.h>
-+#include <linux/platform_device.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+
-+#include <asm/mach-types.h>
-+#include <asm/hardware/scoop.h>
-+#include <asm/arch/regs-iis.h>
-+#include <asm/arch/regs-clock.h>
-+#include <asm/arch/regs-gpio.h>
-+#include <asm/arch/hardware.h>
-+#include <asm/arch/audio.h>
-+#include <asm/io.h>
-+#include <asm/arch/spi-gpio.h>
-+#include "../codecs/wm8956.h"
-+#include "s3c24xx-pcm.h"
-+#include "s3c24xx-i2s.h"
-+
-+#define SMDK2440_DEBUG 0
-+#if SMDK2440_DEBUG
-+#define DBG(x...) printk(KERN_DEBUG x)
-+#else
-+#define DBG(x...)
-+#endif
-+
-+/* audio clock in Hz */
-+#define SMDK_CLOCK_SOURCE S3C24XX_CLKSRC_MPLL
-+#define SMDK_CRYSTAL_CLOCK 12000000
-+
-+static int smdk2440_startup(struct snd_pcm_substream *substream)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_codec *codec = rtd->socdev->codec;
-+
-+ DBG("Entered %s\n",__FUNCTION__);
-+
-+ return 0;
-+}
-+
-+static int smdk2440_shutdown(struct snd_pcm_substream *substream)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_codec *codec = rtd->socdev->codec;
-+
-+ DBG("Entered %s\n",__FUNCTION__);
-+
-+ return 0;
-+}
-+
-+static int smdk2440_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
-+ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-+ int bclk, mclk;
-+ int ret;
-+ int pll;
-+ int div=0,sysclkdiv=0;
-+ unsigned int rate = params_rate(params);
-+
-+ DBG("Entered %s\n",__FUNCTION__);
-+
-+ /* Work out the pll dividers */
-+ switch(rate)
-+ {
-+ case 8000:
-+ case 16000:
-+ case 32000:
-+ case 48000:
-+ pll=12288000;
-+ break;
-+ case 96000:
-+ pll=24576000;
-+ break;
-+ case 11025:
-+ case 22050:
-+ case 44100:
-+ pll=11289600;
-+ break;
-+ case 88200:
-+ pll=22579200;
-+ break;
-+ default:
-+ pll=12288000;
-+ }
-+
-+ /* Work out the DAV Div */
-+ switch(rate)
-+ {
-+ case 96000:
-+ case 88200:
-+ case 48000:
-+ case 44100:
-+ div=0;
-+ break;
-+ case 32000:
-+ div=1;
-+ break;
-+ case 22050;
-+ div=2;
-+ break;
-+ case 16000:
-+ div=1;
-+ sysclkdiv=2;
-+ break;
-+ case 11025:
-+ div=4;
-+ break;
-+ case 8000:
-+ div=6;
-+ break;
-+ }
-+
-+ /* set codec DAI configuration */
-+ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
-+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = codec_dai->dai_ops.set_pll(codec_dai, 0, SMDK_CRYSTAL_CLOCK, pll);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8956_SYSCLKDIV, sysclkdiv);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8956_DACDIV, div);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set cpu DAI configuration */
-+ ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
-+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set the audio system clock for DAC and ADC */
-+ /* 12Mhz crystal for this example */
-+ ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, S3C24XX_CLKSRC_MPLL,
-+ SMDK_CRYSTAL_CLOCK, SND_SOC_CLOCK_OUT);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set MCLK division for sample rate */
-+ ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK, S3C2410_IISMOD_32FS );
-+ if (ret < 0)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+static struct snd_soc_ops smdk2440_ops = {
-+ .startup = smdk2440_startup,
-+ .shutdown = smdk2440_shutdown,
-+ .hw_params = smdk2440_hw_params,
-+};
-+
-+/* smdk2440 machine dapm widgets */
-+static const struct snd_soc_dapm_widget smdk2440_dapm_widgets[] = {
-+SND_SOC_DAPM_HP("Headphone Jack", NULL),
-+SND_SOC_DAPM_MIC("Mic Jack", NULL),
-+SND_SOC_DAPM_LINE("Line Jack", NULL),
-+};
-+
-+/* smdk2440 machine audio map (connections to the codec pins) */
-+static const char* audio_map[][3] = {
-+ /* headphone connected to HPOUT */
-+ {"Headphone Jack", NULL, "HPOUT"},
-+ {"MICIN", NULL, "Mic Jack"},
-+ {"MICIN", NULL, "Line Jack"},
-+
-+ {NULL, NULL, NULL},
-+};
-+
-+/*
-+ * Logic for a wm8956 as attached to SMDK2440
-+ */
-+static int smdk2440_wm8956_init(struct snd_soc_codec *codec)
-+{
-+ int i, err;
-+
-+ DBG("Entered %s\n",__FUNCTION__);
-+
-+
-+ /* Add smdk2440 specific widgets */
-+ for(i = 0; i < ARRAY_SIZE(smdk2440_dapm_widgets); i++) {
-+ snd_soc_dapm_new_control(codec, &smdk2440_dapm_widgets[i]);
-+ }
-+
-+ /* Set up smdk2440 specific audio path audio_mapnects */
-+ for(i = 0; audio_map[i][0] != NULL; i++) {
-+ snd_soc_dapm_connect_input(codec, audio_map[i][0],
-+ audio_map[i][1], audio_map[i][2]);
-+ }
-+
-+ snd_soc_dapm_sync_endpoints(codec);
-+
-+ return 0;
-+}
-+
-+/* s3c24xx digital audio interface glue - connects codec <--> CPU */
-+static struct snd_soc_dai_link s3c24xx_dai = {
-+ .name = "WM8731",
-+ .stream_name = "WM8731",
-+ .cpu_dai = &s3c24xx_i2s_dai,
-+ .codec_dai = &wm8956_dai,
-+ .init = smdk2440_wm8956_init,
-+ .ops = &smdk2440_ops,
-+};
-+
-+/* smdk2440 audio machine driver */
-+static struct snd_soc_machine snd_soc_machine_smdk2440 = {
-+ .name = "SMDK2440",
-+ .dai_link = &s3c24xx_dai,
-+ .num_links = 1,
-+};
-+
-+static struct wm8956_setup_data smdk2440_wm8956_setup = {
-+ .i2c_address = 0x00,
-+};
-+
-+/* s3c24xx audio subsystem */
-+static struct snd_soc_device s3c24xx_snd_devdata = {
-+ .machine = &snd_soc_machine_smdk2440,
-+ .platform = &s3c24xx_soc_platform,
-+ .codec_dev = &soc_codec_dev_wm8956,
-+ .codec_data = &smdk2440_wm8956_setup,
-+};
-+
-+static struct platform_device *s3c24xx_snd_device;
-+
-+struct smdk2440_spi_device {
-+ struct device *dev;
-+};
-+
-+static struct smdk2440_spi_device smdk2440_spi_devdata = {
-+};
-+
-+struct s3c2410_spigpio_info smdk2440_spi_devinfo = {
-+ .pin_clk = S3C2410_GPF4,
-+ .pin_mosi = S3C2410_GPF5,
-+ .pin_miso = S3C2410_GPF6,
-+ //.board_size,
-+ //.board_info,
-+ .chip_select=NULL,
-+};
-+
-+static struct platform_device *smdk2440_spi_device;
-+
-+static int __init smdk2440_init(void)
-+{
-+ int ret;
-+
-+ if (!machine_is_smdk2440() && !machine_is_s3c2440()) {
-+ DBG("%d\n",machine_arch_type);
-+ DBG("Not a SMDK2440\n");
-+ return -ENODEV;
-+ }
-+
-+ s3c24xx_snd_device = platform_device_alloc("soc-audio", -1);
-+ if (!s3c24xx_snd_device) {
-+ DBG("platform_dev_alloc failed\n");
-+ return -ENOMEM;
-+ }
-+
-+ platform_set_drvdata(s3c24xx_snd_device, &s3c24xx_snd_devdata);
-+ s3c24xx_snd_devdata.dev = &s3c24xx_snd_device->dev;
-+ ret = platform_device_add(s3c24xx_snd_device);
-+
-+ if (ret)
-+ platform_device_put(s3c24xx_snd_device);
-+
-+ // Create a bitbanged SPI device
-+
-+ smdk2440_spi_device = platform_device_alloc("s3c24xx-spi-gpio",-1);
-+ if (!smdk2440_spi_device) {
-+ DBG("smdk2440_spi_device : platform_dev_alloc failed\n");
-+ return -ENOMEM;
-+ }
-+ DBG("Return Code %d\n",ret);
-+
-+ platform_set_drvdata(smdk2440_spi_device, &smdk2440_spi_devdata);
-+ smdk2440_spi_devdata.dev = &smdk2440_spi_device->dev;
-+ smdk2440_spi_devdata.dev->platform_data = &smdk2440_spi_devinfo;
-+ ret = platform_device_add(smdk2440_spi_device);
-+
-+ if (ret)
-+ platform_device_put(smdk2440_spi_device);
-+
-+ return ret;
-+}
-+
-+static void __exit smdk2440_exit(void)
-+{
-+ platform_device_unregister(s3c24xx_snd_device);
-+}
-+
-+module_init(smdk2440_init);
-+module_exit(smdk2440_exit);
-+
-+/* Module information */
-+MODULE_AUTHOR("Ben Dooks, <ben at simtec.co.uk>");
-+MODULE_DESCRIPTION("ALSA SoC SMDK2440");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/imx/imx-ssi.h
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/imx/imx-ssi.h
-@@ -0,0 +1,28 @@
-+/*
-+ * 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 _IMX_SSI_H
-+#define _IMX_SSI_H
-+
-+/* pxa2xx DAI SSP ID's */
-+#define IMX_DAI_SSI1 0
-+#define IMX_DAI_SSI2 1
-+
-+/* SSI clock sources */
-+#define IMX_SSP_SYS_CLK 0
-+
-+/* SSI audio dividers */
-+#define IMX_SSI_DIV_2 0
-+#define IMX_SSI_DIV_PSR 1
-+#define IMX_SSI_DIV_PM 2
-+
-+/* SSI Div 2 */
-+#define IMX_SSI_DIV_2_OFF ~SSI_STCCR_DIV2
-+#define IMX_SSI_DIV_2_ON SSI_STCCR_DIV2
-+
-+extern struct snd_soc_cpu_dai imx_ssi_pcm_dai[2];
-+
-+#endif
-Index: linux-2.6.21-moko/sound/soc/imx/mx31ads_wm8753.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/imx/mx31ads_wm8753.c
-@@ -0,0 +1,400 @@
-+/*
-+ * mx31ads_wm8753.c -- SoC audio for mx31ads
-+ *
-+ * Copyright 2005 Wolfson Microelectronics PLC.
-+ * Author: Liam Girdwood
-+ * liam.girdwood at wolfsonmicro.com or linux at wolfsonmicro.com
-+ *
-+ * mx31ads audio amplifier code taken from arch/arm/mach-pxa/mx31ads.c
-+ * Copyright: MontaVista Software Inc.
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or (at your
-+ * option) any later version.
-+ *
-+ * Revision history
-+ * 30th Oct 2005 Initial version.
-+ *
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/moduleparam.h>
-+#include <linux/device.h>
-+#include <linux/i2c.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+
-+#include <asm/hardware.h>
-+
-+#include "../codecs/wm8753.h"
-+#include "imx31-pcm.h"
-+#include "imx-ssi.h"
-+
-+static struct snd_soc_machine mx31ads;
-+
-+static int mx31ads_hifi_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
-+ unsigned int pll_out = 0, bclk = 0, fmt = 0;
-+ int ret = 0;
-+
-+ /*
-+ * The WM8753 is better at generating accurate audio clocks than the
-+ * MX31 SSI controller, so we will use it as master when we can.
-+ */
-+ switch (params_rate(params)) {
-+ case 8000:
-+ case 16000:
-+ fmt = SND_SOC_DAIFMT_CBS_CFS;
-+ pll_out = 12288000;
-+ break;
-+ case 48000:
-+ fmt = SND_SOC_DAIFMT_CBM_CFS;
-+ bclk = WM8753_BCLK_DIV_4;
-+ pll_out = 12288000;
-+ break;
-+ case 96000:
-+ fmt = SND_SOC_DAIFMT_CBM_CFS;
-+ bclk = WM8753_BCLK_DIV_2;
-+ pll_out = 12288000;
-+ break;
-+ case 11025:
-+ fmt = SND_SOC_DAIFMT_CBM_CFS;
-+ bclk = WM8753_BCLK_DIV_16;
-+ pll_out = 11289600;
-+ break;
-+ case 22050:
-+ fmt = SND_SOC_DAIFMT_CBM_CFS;
-+ bclk = WM8753_BCLK_DIV_8;
-+ pll_out = 11289600;
-+ break;
-+ case 44100:
-+ fmt = SND_SOC_DAIFMT_CBM_CFS;
-+ bclk = WM8753_BCLK_DIV_4;
-+ pll_out = 11289600;
-+ break;
-+ case 88200:
-+ fmt = SND_SOC_DAIFMT_CBM_CFS;
-+ bclk = WM8753_BCLK_DIV_2;
-+ pll_out = 11289600;
-+ break;
-+ }
-+
-+ /* set codec DAI configuration */
-+ ret = codec_dai->dai_ops.set_fmt(codec_dai,
-+ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | fmt);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set cpu DAI configuration */
-+ ret = cpu_dai->dai_ops.set_fmt(cpu_dai,
-+ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | fmt);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set the codec system clock for DAC and ADC */
-+ ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_MCLK, pll_out,
-+ SND_SOC_CLOCK_IN);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set the SSI system clock as input (unused) */
-+ ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0,
-+ SND_SOC_CLOCK_IN);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set codec BCLK division for sample rate */
-+ ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_BCLKDIV, bclk);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* codec PLL input is 13 MHz */
-+ ret = codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1, 13000000, pll_out);
-+ if (ret < 0)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+static int mx31ads_hifi_hw_free(struct snd_pcm_substream *substream)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-+
-+ /* disable the PLL */
-+ return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1, 0, 0);
-+}
-+
-+/*
-+ * mx31ads WM8753 HiFi DAI opserations.
-+ */
-+static struct snd_soc_ops mx31ads_hifi_ops = {
-+ .hw_params = mx31ads_hifi_hw_params,
-+ .hw_free = mx31ads_hifi_hw_free,
-+};
-+
-+static int mx31ads_voice_startup(struct snd_pcm_substream *substream)
-+{
-+ return 0;
-+}
-+
-+static void mx31ads_voice_shutdown(struct snd_pcm_substream *substream)
-+{
-+}
-+
-+static int mx31ads_voice_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
-+ unsigned int pll_out = 0, bclk = 0, pcmdiv = 0;
-+ int ret = 0;
-+
-+ /*
-+ * The WM8753 is far better at generating accurate audio clocks than the
-+ * pxa2xx SSP controller, so we will use it as master when we can.
-+ */
-+ switch (params_rate(params)) {
-+ case 8000:
-+ pll_out = 12288000;
-+ pcmdiv = WM8753_PCM_DIV_6; /* 2.048 MHz */
-+ bclk = WM8753_VXCLK_DIV_8; /* 256kHz */
-+ break;
-+ case 16000:
-+ pll_out = 12288000;
-+ pcmdiv = WM8753_PCM_DIV_3; /* 4.096 MHz */
-+ bclk = WM8753_VXCLK_DIV_8; /* 512kHz */
-+ break;
-+ case 48000:
-+ pll_out = 12288000;
-+ pcmdiv = WM8753_PCM_DIV_1; /* 12.288 MHz */
-+ bclk = WM8753_VXCLK_DIV_8; /* 1.536 MHz */
-+ break;
-+ case 11025:
-+ pll_out = 11289600;
-+ pcmdiv = WM8753_PCM_DIV_4; /* 11.2896 MHz */
-+ bclk = WM8753_VXCLK_DIV_8; /* 352.8 kHz */
-+ break;
-+ case 22050:
-+ pll_out = 11289600;
-+ pcmdiv = WM8753_PCM_DIV_2; /* 11.2896 MHz */
-+ bclk = WM8753_VXCLK_DIV_8; /* 705.6 kHz */
-+ break;
-+ case 44100:
-+ pll_out = 11289600;
-+ pcmdiv = WM8753_PCM_DIV_1; /* 11.2896 MHz */
-+ bclk = WM8753_VXCLK_DIV_8; /* 1.4112 MHz */
-+ break;
-+ }
-+
-+ /* set codec DAI configuration */
-+ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A |
-+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set cpu DAI configuration */
-+ ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
-+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set the codec system clock for DAC and ADC */
-+ ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_PCMCLK, pll_out,
-+ SND_SOC_CLOCK_IN);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set the SSP system clock as input (unused) */
-+// ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_SSP_CLK_PLL, 0,
-+// SND_SOC_CLOCK_IN);
-+// if (ret < 0)
-+// return ret;
-+
-+ /* set codec BCLK division for sample rate */
-+ ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_VXCLKDIV, bclk);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set codec PCM division for sample rate */
-+ ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* codec PLL input is 13 MHz */
-+ ret = codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL2, 13000000, pll_out);
-+ if (ret < 0)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+static int mx31ads_voice_hw_free(struct snd_pcm_substream *substream)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-+
-+ /* disable the PLL */
-+ return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL2, 0, 0);
-+}
-+
-+static struct snd_soc_ops mx31ads_voice_ops = {
-+ .startup = mx31ads_voice_startup,
-+ .shutdown = mx31ads_voice_shutdown,
-+ .hw_params = mx31ads_voice_hw_params,
-+ .hw_free = mx31ads_voice_hw_free,
-+};
-+
-+static int mx31ads_suspend(struct platform_device *pdev, pm_message_t state)
-+{
-+ return 0;
-+}
-+
-+static int mx31ads_resume(struct platform_device *pdev)
-+{
-+ return 0;
-+}
-+
-+static int mx31ads_probe(struct platform_device *pdev)
-+{
-+ return 0;
-+}
-+
-+static int mx31ads_remove(struct platform_device *pdev)
-+{
-+ return 0;
-+}
-+
-+/* example machine audio_mapnections */
-+static const char* audio_map[][3] = {
-+
-+ /* mic is connected to mic1 - with bias */
-+ {"MIC1", NULL, "Mic Bias"},
-+ {"MIC1N", NULL, "Mic Bias"},
-+ {"Mic Bias", NULL, "Mic1 Jack"},
-+ {"Mic Bias", NULL, "Mic1 Jack"},
-+
-+ {"ACIN", NULL, "ACOP"},
-+ {NULL, NULL, NULL},
-+};
-+
-+/* headphone detect support on my board */
-+static const char * hp_pol[] = {"Headphone", "Speaker"};
-+static const struct soc_enum wm8753_enum =
-+ SOC_ENUM_SINGLE(WM8753_OUTCTL, 1, 2, hp_pol);
-+
-+static const struct snd_kcontrol_new wm8753_mx31ads_controls[] = {
-+ SOC_SINGLE("Headphone Detect Switch", WM8753_OUTCTL, 6, 1, 0),
-+ SOC_ENUM("Headphone Detect Polarity", wm8753_enum),
-+};
-+
-+/*
-+ * This is an example machine initialisation for a wm8753 connected to a
-+ * mx31ads II. It is missing logic to detect hp/mic insertions and logic
-+ * to re-route the audio in such an event.
-+ */
-+static int mx31ads_wm8753_init(struct snd_soc_codec *codec)
-+{
-+ int i, err;
-+
-+ /* set up mx31ads codec pins */
-+ snd_soc_dapm_set_endpoint(codec, "RXP", 0);
-+ snd_soc_dapm_set_endpoint(codec, "RXN", 0);
-+ snd_soc_dapm_set_endpoint(codec, "MIC2", 0);
-+
-+ /* add mx31ads specific controls */
-+ for (i = 0; i < ARRAY_SIZE(wm8753_mx31ads_controls); i++) {
-+ if ((err = snd_ctl_add(codec->card,
-+ snd_soc_cnew(&wm8753_mx31ads_controls[i],codec, NULL))) < 0)
-+ return err;
-+ }
-+
-+ /* set up mx31ads specific audio path audio_mapnects */
-+ for(i = 0; audio_map[i][0] != NULL; i++) {
-+ snd_soc_dapm_connect_input(codec, audio_map[i][0], audio_map[i][1], audio_map[i][2]);
-+ }
-+
-+ snd_soc_dapm_sync_endpoints(codec);
-+ return 0;
-+}
-+
-+static struct snd_soc_dai_link mx31ads_dai[] = {
-+{ /* Hifi Playback - for similatious use with voice below */
-+ .name = "WM8753",
-+ .stream_name = "WM8753 HiFi",
-+ .cpu_dai = &imx_ssi_pcm_dai[0],
-+ .codec_dai = &wm8753_dai[WM8753_DAI_HIFI],
-+ .init = mx31ads_wm8753_init,
-+ .ops = &mx31ads_hifi_ops,
-+},
-+//{ /* Voice via BT */
-+// .name = "Bluetooth",
-+// .stream_name = "Voice",
-+// .cpu_dai = &pxa_ssp_dai[1],
-+// .codec_dai = &wm8753_dai[WM8753_DAI_VOICE],
-+// .ops = &mx31ads_voice_ops,
-+//},
-+};
-+
-+static struct snd_soc_machine mx31ads = {
-+ .name = "mx31ads",
-+ .probe = mx31ads_probe,
-+ .remove = mx31ads_remove,
-+ .suspend_pre = mx31ads_suspend,
-+ .resume_post = mx31ads_resume,
-+ .dai_link = mx31ads_dai,
-+ .num_links = ARRAY_SIZE(mx31ads_dai),
-+};
-+
-+static struct wm8753_setup_data mx31ads_wm8753_setup = {
-+ .i2c_address = 0x1a,
-+};
-+
-+static struct snd_soc_device mx31ads_snd_devdata = {
-+ .machine = &mx31ads,
-+ .platform = &mxc_soc_platform,
-+ .codec_dev = &soc_codec_dev_wm8753,
-+ .codec_data = &mx31ads_wm8753_setup,
-+};
-+
-+static struct platform_device *mx31ads_snd_device;
-+
-+static int __init mx31ads_init(void)
-+{
-+ int ret;
-+
-+ mx31ads_snd_device = platform_device_alloc("soc-audio", -1);
-+ if (!mx31ads_snd_device)
-+ return -ENOMEM;
-+
-+ platform_set_drvdata(mx31ads_snd_device, &mx31ads_snd_devdata);
-+ mx31ads_snd_devdata.dev = &mx31ads_snd_device->dev;
-+ ret = platform_device_add(mx31ads_snd_device);
-+
-+ if (ret)
-+ platform_device_put(mx31ads_snd_device);
-+
-+ return ret;
-+}
-+
-+static void __exit mx31ads_exit(void)
-+{
-+ platform_device_unregister(mx31ads_snd_device);
-+}
-+
-+module_init(mx31ads_init);
-+module_exit(mx31ads_exit);
-+
-+/* Module information */
-+MODULE_AUTHOR("Liam Girdwood, liam.girdwood at wolfsonmicro.com, www.wolfsonmicro.com");
-+MODULE_DESCRIPTION("ALSA SoC WM8753 mx31ads");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/s3c24xx/lm4857.h
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/s3c24xx/lm4857.h
-@@ -0,0 +1,15 @@
-+#ifndef LM4857_H_
-+#define LM4857_H_
-+
-+/* The register offsets in the cache array */
-+#define LM4857_MVOL 0
-+#define LM4857_LVOL 1
-+#define LM4857_RVOL 2
-+#define LM4857_CTRL 3
-+
-+/* the shifts required to set these bits */
-+#define LM4857_3D 5
-+#define LM4857_WAKEUP 5
-+#define LM4857_EPGAIN 4
-+
-+#endif /*LM4857_H_*/
-Index: linux-2.6.21-moko/sound/soc/codecs/tlv320.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/codecs/tlv320.c
-@@ -0,0 +1,609 @@
-+/*
-+ * tlv320.c -- TLV 320 ALSA Soc Audio driver
-+ *
-+ * Copyright 2005 Wolfson Microelectronics PLC.
-+ * Copyright 2006 Atlab srl.
-+ *
-+ * Authors: Liam Girdwood <liam.girdwood at wolfsonmicro.com>
-+ * Nicola Perrino <nicola.perrino at atlab.it>
-+ *
-+ * 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.
-+ *
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/moduleparam.h>
-+#include <linux/version.h>
-+#include <linux/kernel.h>
-+#include <linux/init.h>
-+#include <linux/delay.h>
-+#include <linux/pm.h>
-+#include <linux/i2c.h>
-+#include <linux/platform_device.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/pcm_params.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+#include <sound/initval.h>
-+
-+#include "tlv320.h"
-+
-+#define AUDIO_NAME "tlv320"
-+#define TLV320_VERSION "0.1"
-+
-+/*
-+ * Debug
-+ */
-+
-+//#define TLV320_DEBUG 0
-+
-+#ifdef TLV320_DEBUG
-+#define dbg(format, arg...) \
-+ printk(KERN_DEBUG AUDIO_NAME ": " format "\n" , ## arg)
-+#else
-+#define dbg(format, arg...) do {} while (0)
-+#endif
-+#define err(format, arg...) \
-+ printk(KERN_ERR AUDIO_NAME ": " format "\n" , ## arg)
-+#define info(format, arg...) \
-+ printk(KERN_INFO AUDIO_NAME ": " format "\n" , ## arg)
-+#define warn(format, arg...) \
-+ printk(KERN_WARNING AUDIO_NAME ": " format "\n" , ## arg)
-+
-+
-+#define TLV320_VOICE_RATES \
-+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 | \
-+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
-+ SNDRV_PCM_RATE_48000)
-+
-+
-+#define TLV320_VOICE_BITS \
-+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
-+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
-+
-+
-+static int caps_charge = 2000;
-+static int setting = 1;
-+module_param(caps_charge, int, 0);
-+module_param(setting, int, 0);
-+MODULE_PARM_DESC(caps_charge, "TLV320 cap charge time (msecs)");
-+
-+static struct workqueue_struct *tlv320_workq = NULL;
-+//static struct work_struct tlv320_dapm_work;
-+
-+/* codec private data */
-+struct tlv320_priv {
-+ unsigned int sysclk;
-+ unsigned int pcmclk;
-+};
-+
-+
-+#ifdef TLV320AIC24K
-+/* ADDR table */
-+static const unsigned char tlv320_reg_addr[] = {
-+ 0x00, /* CONTROL REG 0 No Operation */
-+ 0x01, /* CONTROL REG 1 */
-+ 0x02, /* CONTROL REG 2 */
-+ 0x03, /* CONTROL REG 3A */
-+ 0x03, /* CONTROL REG 3B */
-+ 0x03, /* CONTROL REG 3C */
-+ 0x03, /* CONTROL REG 3D */
-+ 0x04, /* CONTROL REG 4 */
-+ 0x04, /* CONTROL REG 4 Bis */
-+ 0x05, /* CONTROL REG 5A */
-+ 0x05, /* CONTROL REG 5B */
-+ 0x05, /* CONTROL REG 5C */
-+ 0x05, /* CONTROL REG 5D */
-+ 0x06, /* CONTROL REG 6A */
-+ 0x06, /* CONTROL REG 6B */
-+};
-+
-+/*
-+ * DATA case digital SET1:
-+ * SSP -> DAC -> OUT
-+ * IN -> ADC -> SSP
-+ * IN = HNSI (MIC)
-+ * OUT = HDSO (SPKG)
-+ * Usage: playback, capture streams
-+ */
-+static const unsigned char tlv320_reg_data_init_set1[] = {
-+ 0x00, /* CONTROL REG 0 No Operation */
-+ 0x49, /* CONTROL REG 1 */
-+ 0x20, /* CONTROL REG 2 */
-+ 0x01, /* CONTROL REG 3A */
-+ 0x40, /* CONTROL REG 3B */
-+ 0x81, /* CONTROL REG 3C */
-+ 0xc0, /* CONTROL REG 3D */
-+ 0x02,//0x42(16khz),//0x10, /* CONTROL REG 4 */
-+ 0x88,//0x90, /* CONTROL REG 4 Bis */
-+ 0x00, /* CONTROL REG 5A */
-+ 0x40,//(0dB) /* CONTROL REG 5B */
-+ 0xbf, /* CONTROL REG 5C */
-+ 0xc0, /* CONTROL REG 5D */
-+ 0x02,//(HNSI) /* CONTROL REG 6A */
-+ 0x81 //(HDSO) /* CONTROL REG 6B */
-+};
-+
-+/*
-+ * DATA case digital SET2:
-+ * SSP -> DAC -> OUT
-+ * IN -> ADC -> SSP
-+ * IN = HDSI (PHONE IN)
-+ * OUT = HNSO (PHONE OUT)
-+ * Usage: playback, capture streams
-+ */
-+static const unsigned char tlv320_reg_data_init_set2[] = {
-+ 0x00, /* CONTROL REG 0 No Operation */
-+ 0x49, /* CONTROL REG 1 */
-+ 0x20, /* CONTROL REG 2 */
-+ 0x01, /* CONTROL REG 3A */
-+ 0x40, /* CONTROL REG 3B */
-+ 0x81, /* CONTROL REG 3C */
-+ 0xc0, /* CONTROL REG 3D */
-+ 0x02,//0x42(16khz),//0x10, /* CONTROL REG 4 */
-+ 0x88,//0x90, /* CONTROL REG 4 Bis */
-+ 0x00, /* CONTROL REG 5A */
-+ 0x52,//(-27dB) /* CONTROL REG 5B */
-+ 0xbf, /* CONTROL REG 5C */
-+ 0xc0, /* CONTROL REG 5D */
-+ 0x01,//(PHONE IN) /* CONTROL REG 6A */
-+ 0x82 //(PHONE OUT) /* CONTROL REG 6B */
-+};
-+
-+/*
-+ * DATA case analog:
-+ * ADC, DAC, SSP off
-+ * Headset input to output (HDSI2O -> 1)
-+ * Handset input to output (HNSI2O -> 1)
-+ * Usage: room monitor
-+ */
-+static const unsigned char tlv320_reg_data_init_set3[] = {
-+ 0x00, /* CONTROL REG 0 No Operation */
-+ 0x08, /* CONTROL REG 1 */
-+ 0x20, /* CONTROL REG 2 */
-+ 0x11, /* CONTROL REG 3A */
-+ 0x40, /* CONTROL REG 3B */
-+ 0x80, /* CONTROL REG 3C */
-+ 0xc0, /* CONTROL REG 3D */
-+ 0x00, /* CONTROL REG 4 */
-+ 0x00, /* CONTROL REG 5A */
-+ 0x40, /* CONTROL REG 5B */
-+ 0x80, /* CONTROL REG 5C */
-+ 0xc0, /* CONTROL REG 5D */
-+ 0x60, /* CONTROL REG 6A */
-+ 0x80 /* CONTROL REG 6B */
-+};
-+
-+#else // TLV320AIC14k
-+
-+/* ADDR table */
-+static const unsigned char tlv320_reg_addr[] = {
-+ 0x00, /* CONTROL REG 0 No Operation */
-+ 0x01, /* CONTROL REG 1 */
-+ 0x02, /* CONTROL REG 2 */
-+ 0x03, /* CONTROL REG 3 */
-+ 0x04, /* CONTROL REG 4 */
-+ 0x04, /* CONTROL REG 4 Bis */
-+ 0x05, /* CONTROL REG 5A */
-+ 0x05, /* CONTROL REG 5B */
-+ 0x05, /* CONTROL REG 5C */
-+ 0x05, /* CONTROL REG 5D */
-+ 0x06 /* CONTROL REG 6 */
-+};
-+
-+/*
-+ * DATA case digital:
-+ * SSP -> DAC -> OUT
-+ * IN -> ADC -> SSP
-+ * Usage: playback, capture streams
-+ */
-+static const unsigned char tlv320_reg_data_init_set1[] = {
-+ 0x00, /* CONTROL REG 0 No Operation */
-+ 0x41, /* CONTROL REG 1 */
-+ 0x20, /* CONTROL REG 2 */
-+ 0x09, /* CONTROL REG 3 */
-+ 0x02,//0x42(16khz),//0x10, /* CONTROL REG 4 */
-+ 0x88,//0x90, /* CONTROL REG 4 Bis */
-+ 0x2A, /* CONTROL REG 5A */
-+ 0x6A, /* CONTROL REG 5B */
-+ 0xbc, /* CONTROL REG 5C */
-+ 0xc0, /* CONTROL REG 5D */
-+ 0x00 /* CONTROL REG 6 */
-+};
-+#endif
-+/*
-+ * read tlv320 register cache
-+ */
-+static inline unsigned int tlv320_read_reg_cache(struct snd_soc_codec *codec,
-+ unsigned int reg)
-+{
-+ u8 *cache = codec->reg_cache;
-+ if (reg > ARRAY_SIZE(tlv320_reg_addr))
-+ return -1;
-+ return cache[reg];
-+}
-+
-+/*
-+ * write tlv320 register cache
-+ */
-+static inline void tlv320_write_reg_cache(struct snd_soc_codec *codec,
-+ unsigned int reg, unsigned int value)
-+{
-+ u8 *cache = codec->reg_cache;
-+ if (reg > ARRAY_SIZE(tlv320_reg_addr))
-+ return;
-+ cache[reg] = value;
-+}
-+
-+/*
-+ * read tlv320
-+ */
-+static int tlv320_read (struct snd_soc_codec *codec, u8 reg)
-+{
-+ return i2c_smbus_read_byte_data(codec->control_data, reg);
-+}
-+
-+/*
-+ * write tlv320
-+ */
-+static int tlv320_write(struct snd_soc_codec *codec, unsigned int reg,
-+ unsigned int value)
-+{
-+ if (tlv320_reg_addr[reg] > 0x06)
-+ return -1;
-+
-+ tlv320_write_reg_cache (codec, reg, value);
-+
-+ return i2c_smbus_write_byte_data(codec->control_data, tlv320_reg_addr[reg], value);
-+}
-+
-+
-+/*
-+ * write block tlv320
-+ */
-+static int tlv320_write_block (struct snd_soc_codec *codec,
-+ const u8 *data, unsigned int len)
-+{
-+ int ret = -1;
-+ int i;
-+
-+ for (i=0; i<len; i++) {
-+ dbg("addr = 0x%02x, data = 0x%02x", tlv320_reg_addr[i], data[i]);
-+ if ((ret = tlv320_write(codec, i, data[i])) < 0)
-+ break;
-+ }
-+
-+ return ret;
-+}
-+
-+
-+static int tlv320_set_dai_fmt(struct snd_soc_codec_dai *codec_dai,
-+ unsigned int fmt)
-+{
-+ dbg("tlv320_set_dai_fmt enter");
-+ return 0;
-+}
-+
-+/*
-+ * Set PCM DAI bit size and sample rate.
-+ */
-+static int tlv320_pcm_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ dbg("tlv320_pcm_hw_params enter");
-+ return 0;
-+}
-+
-+
-+static int tlv320_config_pcm_sysclk(struct snd_soc_codec_dai *codec_dai,
-+ int clk_id, unsigned int freq, int dir)
-+{
-+ dbg("tlv320_config_pcm_sysclk enter");
-+ return 0;
-+}
-+
-+
-+/*
-+ * Voice over PCM DAI
-+ */
-+struct snd_soc_codec_dai tlv320_dai[] = {
-+{ .name = "TLV320 Voice",
-+ .id = 1,
-+ .playback = {
-+ .stream_name = "Voice Playback",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = TLV320_VOICE_RATES,
-+ .formats = TLV320_VOICE_BITS,},
-+ .capture = {
-+ .stream_name = "Voice Capture",
-+ .channels_min = 1,
-+ .channels_max = 2,
-+ .rates = TLV320_VOICE_RATES,
-+ .formats = TLV320_VOICE_BITS,},
-+ .ops = {
-+ .hw_params = tlv320_pcm_hw_params,},
-+ .dai_ops = {
-+ .digital_mute = NULL,
-+ .set_fmt = tlv320_set_dai_fmt,
-+ .set_clkdiv = NULL,
-+ .set_pll = NULL,
-+ .set_sysclk = tlv320_config_pcm_sysclk,
-+ },
-+},
-+
-+
-+};
-+EXPORT_SYMBOL_GPL(tlv320_dai);
-+
-+
-+static void tlv320_work(struct work_struct *work)
-+{
-+#if 0
-+ struct snd_soc_codec *codec =
-+ container_of(work, struct snd_soc_codec, delayed_work.work);
-+ //wm8753_dapm_event(codec, codec->dapm_state);
-+#endif
-+}
-+
-+/*
-+ * initialise the TLV320 driver
-+ * register the mixer and dsp interfaces with the kernel
-+ */
-+static int tlv320_init(struct snd_soc_device *socdev)
-+{
-+ struct snd_soc_codec *codec = socdev->codec;
-+ int ret = 0;
-+
-+ codec->name = "TLV320";
-+ codec->owner = THIS_MODULE;
-+ codec->read = tlv320_read_reg_cache;
-+ codec->write = tlv320_write;
-+ codec->dai = tlv320_dai;
-+ codec->num_dai = ARRAY_SIZE(tlv320_dai);
-+ codec->reg_cache_size = ARRAY_SIZE(tlv320_reg_addr);
-+
-+ codec->reg_cache =
-+ kzalloc(sizeof(u8) * ARRAY_SIZE(tlv320_reg_addr), GFP_KERNEL);
-+ if (codec->reg_cache == NULL)
-+ return -ENOMEM;
-+ memcpy(codec->reg_cache, tlv320_reg_addr,
-+ sizeof(u8) * ARRAY_SIZE(tlv320_reg_addr));
-+ codec->reg_cache_size = sizeof(u8) * ARRAY_SIZE(tlv320_reg_addr);
-+
-+ /* register pcms */
-+ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
-+ if (ret < 0) {
-+ kfree(codec->reg_cache);
-+ return ret;
-+ }
-+
-+ queue_delayed_work(tlv320_workq,
-+ &codec->delayed_work, msecs_to_jiffies(caps_charge));
-+
-+ ret = snd_soc_register_card(socdev);
-+ if (ret < 0) {
-+ snd_soc_free_pcms(socdev);
-+ snd_soc_dapm_free(socdev);
-+ }
-+
-+ return ret;
-+}
-+
-+/* If the i2c layer weren't so broken, we could pass this kind of data
-+ around */
-+static struct snd_soc_device *tlv320_socdev;
-+
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+
-+#define I2C_DRIVERID_TLV320 0xfefe /* liam - need a proper id */
-+
-+static unsigned short normal_i2c[] = { 0, I2C_CLIENT_END };
-+
-+/* Magic definition of all other variables and things */
-+I2C_CLIENT_INSMOD;
-+
-+static struct i2c_driver tlv320_i2c_driver;
-+static struct i2c_client client_template;
-+
-+static int tlv320_codec_probe(struct i2c_adapter *adap, int addr, int kind)
-+{
-+ struct snd_soc_device *socdev = tlv320_socdev;
-+ struct tlv320_setup_data *setup = socdev->codec_data;
-+ struct snd_soc_codec *codec = socdev->codec;
-+ struct i2c_client *i2c;
-+ int ret, len;
-+ const unsigned char *data;
-+
-+ if (addr != setup->i2c_address)
-+ return -ENODEV;
-+
-+ client_template.adapter = adap;
-+ client_template.addr = addr;
-+
-+ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
-+ if (i2c == NULL){
-+ kfree(codec);
-+ return -ENOMEM;
-+ }
-+ memcpy(i2c, &client_template, sizeof(struct i2c_client));
-+ i2c_set_clientdata(i2c, codec);
-+ codec->control_data = i2c;
-+
-+ ret = i2c_attach_client(i2c);
-+ if (ret < 0) {
-+ err("failed to attach codec at addr %x\n", addr);
-+ goto err;
-+ }
-+
-+ ret = tlv320_init(socdev);
-+ if (ret < 0) {
-+ err("failed to initialise TLV320\n");
-+ goto err;
-+ }
-+
-+ switch(setting) {
-+ case 1:
-+ data = tlv320_reg_data_init_set1;
-+ len = sizeof(tlv320_reg_data_init_set1);
-+ break;
-+ case 2:
-+ data = tlv320_reg_data_init_set2;
-+ len = sizeof(tlv320_reg_data_init_set2);
-+ break;
-+ case 3:
-+ data = tlv320_reg_data_init_set3;
-+ len = sizeof(tlv320_reg_data_init_set3);
-+ break;
-+ default:
-+ data = tlv320_reg_data_init_set1;
-+ len = sizeof(tlv320_reg_data_init_set1);
-+ break;
-+ }
-+
-+ ret = tlv320_write_block(codec, data, len);
-+
-+ if (ret < 0) {
-+ err("attach error: init status %d\n", ret);
-+ } else {
-+ info("attach: chip tlv320 at address 0x%02x",
-+ tlv320_read(codec, 0x02) << 1);
-+ }
-+
-+ //tlv320_write(codec, CODEC_REG6B, 0x80);
-+#if 0
-+ int value;
-+ int i;
-+
-+ for (i=0; i<len; i++) {
-+ value = tlv320_read(codec, tlv320_reg_addr[i]);
-+ dbg("read addr = 0x%02x, data = 0x%02x", tlv320_reg_addr[i], value);
-+ mdelay(10);
-+ }
-+
-+#endif
-+
-+
-+ return ret;
-+
-+err:
-+ kfree(codec);
-+ kfree(i2c);
-+ return ret;
-+}
-+
-+static int tlv320_i2c_detach(struct i2c_client *client)
-+{
-+ struct snd_soc_codec *codec = i2c_get_clientdata(client);
-+ i2c_detach_client(client);
-+ kfree(codec->reg_cache);
-+ kfree(client);
-+ return 0;
-+}
-+
-+static int tlv320_i2c_attach(struct i2c_adapter *adap)
-+{
-+ return i2c_probe(adap, &addr_data, tlv320_codec_probe);
-+}
-+
-+/* tlv320 i2c codec control layer */
-+static struct i2c_driver tlv320_i2c_driver = {
-+ .driver = {
-+ .name = "tlv320 I2C Codec",
-+ .owner = THIS_MODULE,
-+ },
-+ .id = I2C_DRIVERID_TLV320,
-+ .attach_adapter = tlv320_i2c_attach,
-+ .detach_client = tlv320_i2c_detach,
-+ .command = NULL,
-+};
-+
-+static struct i2c_client client_template = {
-+ .name = "tlv320",
-+ .driver = &tlv320_i2c_driver,
-+};
-+#endif
-+
-+static int tlv320_probe(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct tlv320_setup_data *setup;
-+ struct snd_soc_codec *codec;
-+ int ret = 0;
-+ struct tlv320_priv *tlv320;
-+
-+ info("TLV320 Audio Codec %s", TLV320_VERSION);
-+
-+ setup = socdev->codec_data;
-+ codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
-+ if (codec == NULL)
-+ return -ENOMEM;
-+
-+ tlv320 = kzalloc(sizeof(struct tlv320_priv), GFP_KERNEL);
-+ if (tlv320 == NULL) {
-+ kfree(codec);
-+ return -ENOMEM;
-+ }
-+
-+ codec->private_data = tlv320;
-+
-+ socdev->codec = codec;
-+ mutex_init(&codec->mutex);
-+ INIT_LIST_HEAD(&codec->dapm_widgets);
-+ INIT_LIST_HEAD(&codec->dapm_paths);
-+ tlv320_socdev = socdev;
-+
-+ INIT_DELAYED_WORK(&codec->delayed_work, tlv320_work);
-+ tlv320_workq = create_workqueue("tlv320");
-+ if (tlv320_workq == NULL) {
-+ kfree(codec);
-+ return -ENOMEM;
-+ }
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+ if (setup->i2c_address) {
-+ normal_i2c[0] = setup->i2c_address;
-+ codec->hw_write = (hw_write_t)i2c_master_send;
-+ ret = i2c_add_driver(&tlv320_i2c_driver);
-+ if (ret != 0)
-+ printk(KERN_ERR "can't add i2c driver");
-+ }
-+#else
-+ /* Add other interfaces here */
-+#endif
-+ return ret;
-+}
-+
-+/* power down chip */
-+static int tlv320_remove(struct platform_device *pdev)
-+{
-+ struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-+ struct snd_soc_codec *codec = socdev->codec;
-+
-+ if (tlv320_workq)
-+ destroy_workqueue(tlv320_workq);
-+ snd_soc_free_pcms(socdev);
-+ snd_soc_dapm_free(socdev);
-+#if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
-+ i2c_del_driver(&tlv320_i2c_driver);
-+#endif
-+ kfree(codec->private_data);
-+ kfree(codec);
-+
-+ return 0;
-+}
-+
-+struct snd_soc_codec_device soc_codec_dev_tlv320 = {
-+ .probe = tlv320_probe,
-+ .remove = tlv320_remove,
-+};
-+
-+EXPORT_SYMBOL_GPL(soc_codec_dev_tlv320);
-+
-+MODULE_DESCRIPTION("ASoC TLV320 driver");
-+MODULE_AUTHOR("Nicola Perrino");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/codecs/tlv320.h
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/codecs/tlv320.h
-@@ -0,0 +1,111 @@
-+/*
-+ * tlv320.h -- TLV 320 ALSA Soc Audio driver
-+ *
-+ * Copyright 2005 Wolfson Microelectronics PLC.
-+ * Copyright 2006 Atlab srl.
-+ *
-+ * Authors: Liam Girdwood <liam.girdwood at wolfsonmicro.com>
-+ * Nicola Perrino <nicola.perrino at atlab.it>
-+ *
-+ * 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.
-+ *
-+ */
-+
-+#ifndef _TLV320_H
-+#define _TLV320_H
-+
-+#define TLV320AIC24K
-+
-+
-+/* TLV320 register space */
-+#define CODEC_NOOP 0x00
-+#define CODEC_REG1 0x01
-+#define CODEC_REG2 0x02
-+#define CODEC_REG3A 0x03
-+#define CODEC_REG3B 0x04
-+#define CODEC_REG3C 0x05
-+#define CODEC_REG3D 0x06
-+#define CODEC_REG4A 0x07
-+#define CODEC_REG4B 0x08
-+#define CODEC_REG5A 0x09
-+#define CODEC_REG5B 0x0a
-+#define CODEC_REG5C 0x0b
-+#define CODEC_REG5D 0x0c
-+#define CODEC_REG6A 0x0d
-+#define CODEC_REG6B 0x0e
-+
-+
-+// Control Register 1
-+#define REG1_CONTINUOUS 0x40
-+#define REG1_IIR_EN 0x20
-+#define REG1_MIC_BIAS_235 0x08
-+#define REG1_ANALOG_LOOP_BACK 0x04
-+#define REG1_DIGITAL_LOOP_BACK 0x02
-+#define REG1_DAC16 0x01
-+
-+// Control Register 2
-+#define REG2_TURBO_EN 0x80
-+#define REG2_FIR_BYPASS 0x40
-+#define REG2_GPIO 0x02
-+#define REG2_GPIO_1 0x06
-+
-+// Control Register 3A
-+#define REG3_PWDN_ALL 0x30
-+#define REG3_PWDN_ADC 0x10
-+#define REG3_PWDN_DAC 0x20
-+#define REG3_SW_RESET 0x08
-+#define REG3_SAMPLING_FACTOR1 0x01
-+#define REG3_SAMPLING_FACTOR2 0x02
-+
-+// Control Register 3B
-+#define REG3_8KBP_EN 0x60
-+#define REG3_MUTE_OUTP1 0x42
-+#define REG3_MUTE_OUTP2 0x48
-+#define REG3_MUTE_OUTP3 0x44
-+
-+// Control Register 4
-+#define REG4_FSDIV_M 0x85 //M=5
-+#define REG4_FSDIV_NP 0x08 //N=1, P=8
-+//#define REG4_FSDIV_NP 0x01 //N=1, P=8
-+#define REG4_FSDIV_NP1 0x02 //N=16, P=2
-+
-+// Control Register 5
-+#define REG5A_ADC_GAIN 0x02 //3dB
-+#define REG5A_ADC_MUTE 0x0f //Mute
-+#define REG5B_DAC_GAIN 0x42 //-3dB
-+#define REG5B_DAC_MUTE 0x4f //Mute
-+#define REG5C_SIDETONE_MUTE 0xBF
-+
-+// Control Register 6
-+#define REG6A_AIC24A_CH1_IN 0x08 //INP1 to ADC
-+#define REG6B_AIC24A_CH1_OUT 0x82 //OUTP2 to DAC
-+#define REG6A_AIC24A_CH2_IN 0x02 //INP2 to ADC
-+#define REG6B_AIC24A_CH2_OUT 0x81 //OUTP3 to DAC
-+
-+/* clock inputs */
-+#define TLV320_MCLK 0
-+#define TLV320_PCMCLK 1
-+
-+
-+struct tlv320_setup_data {
-+ unsigned short i2c_address;
-+};
-+
-+/* DAI ifmodes */
-+/* mode 1 IFMODE = 00 */
-+#define TLV320_DAI_MODE1_VOICE 0
-+#define TLV320_DAI_MODE1_HIFI 1
-+/* mode 2 IFMODE = 01 */
-+#define TLV320_DAI_MODE2_VOICE 2
-+/* mode 3 IFMODE = 10 */
-+#define TLV320_DAI_MODE3_HIFI 3
-+/* mode 4 IFMODE = 11 */
-+#define TLV320_DAI_MODE4_HIFI 4
-+
-+extern struct snd_soc_codec_dai tlv320_dai[5];
-+extern struct snd_soc_codec_device soc_codec_dev_tlv320;
-+
-+#endif
-Index: linux-2.6.21-moko/sound/soc/pxa/amesom_tlv320.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/pxa/amesom_tlv320.c
-@@ -0,0 +1,211 @@
-+/*
-+ * amesom_tlv320.c -- SoC audio for Amesom
-+ *
-+ * Copyright 2005 Wolfson Microelectronics PLC.
-+ * Copyright 2006 Atlab srl.
-+ *
-+ * Authors: Liam Girdwood <liam.girdwood at wolfsonmicro.com>
-+ * Nicola Perrino <nicola.perrino at atlab.it>
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or (at your
-+ * option) any later version.
-+ *
-+ * Revision history
-+ * 5th Dec 2006 Initial version.
-+ *
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/moduleparam.h>
-+#include <linux/device.h>
-+#include <linux/i2c.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+
-+#include <asm/hardware.h>
-+#include <asm/arch/pxa-regs.h>
-+#include <asm/arch/audio.h>
-+
-+#include "../codecs/tlv320.h"
-+#include "pxa2xx-pcm.h"
-+#include "pxa2xx-i2s.h"
-+#include "pxa2xx-ssp.h"
-+
-+
-+/*
-+ * SSP2 GPIO's
-+ */
-+
-+#define GPIO11_SSP2RX_MD (11 | GPIO_ALT_FN_2_IN)
-+#define GPIO13_SSP2TX_MD (13 | GPIO_ALT_FN_1_OUT)
-+#define GPIO50_SSP2CLKS_MD (50 | GPIO_ALT_FN_3_IN)
-+#define GPIO14_SSP2FRMS_MD (14 | GPIO_ALT_FN_2_IN)
-+#define GPIO50_SSP2CLKM_MD (50 | GPIO_ALT_FN_3_OUT)
-+#define GPIO14_SSP2FRMM_MD (14 | GPIO_ALT_FN_2_OUT)
-+
-+
-+static struct snd_soc_machine amesom;
-+
-+
-+static int amesom_probe(struct platform_device *pdev)
-+{
-+ return 0;
-+}
-+
-+static int amesom_remove(struct platform_device *pdev)
-+{
-+ return 0;
-+}
-+
-+static int tlv320_voice_startup(struct snd_pcm_substream *substream)
-+{
-+ return 0;
-+}
-+
-+static void tlv320_voice_shutdown(struct snd_pcm_substream *substream)
-+{
-+ return;
-+}
-+
-+/*
-+ * Tlv320 uses SSP port for playback.
-+ */
-+static int tlv320_voice_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
-+ int ret = 0;
-+
-+ //printk("tlv320_voice_hw_params enter\n");
-+ switch(params_rate(params)) {
-+ case 8000:
-+ //printk("tlv320_voice_hw_params 8000\n");
-+ break;
-+ case 16000:
-+ //printk("tlv320_voice_hw_params 16000\n");
-+ break;
-+ default:
-+ break;
-+ }
-+
-+ // CODEC MASTER, SSP SLAVE
-+
-+ /* set codec DAI configuration */
-+ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_MSB |
-+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set cpu DAI configuration */
-+ ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_MSB |
-+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set the SSP system clock as input (unused) */
-+ ret = cpu_dai->dai_ops.set_sysclk(cpu_dai, PXA2XX_SSP_CLK_NET_PLL, 0,
-+ SND_SOC_CLOCK_IN);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set SSP slots */
-+ //ret = cpu_dai->dai_ops.set_tdm_slot(cpu_dai, 0x1, slots);
-+ ret = cpu_dai->dai_ops.set_tdm_slot(cpu_dai, 0x3, 1);
-+ if (ret < 0)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+static int tlv320_voice_hw_free(struct snd_pcm_substream *substream)
-+{
-+ return 0;
-+}
-+
-+static struct snd_soc_ops tlv320_voice_ops = {
-+ .startup = tlv320_voice_startup,
-+ .shutdown = tlv320_voice_shutdown,
-+ .hw_params = tlv320_voice_hw_params,
-+ .hw_free = tlv320_voice_hw_free,
-+};
-+
-+
-+static struct snd_soc_dai_link amesom_dai[] = {
-+{
-+ .name = "TLV320",
-+ .stream_name = "TLV320 Voice",
-+ .cpu_dai = &pxa_ssp_dai[PXA2XX_DAI_SSP2],
-+ .codec_dai = &tlv320_dai[TLV320_DAI_MODE1_VOICE],
-+ .ops = &tlv320_voice_ops,
-+},
-+};
-+
-+static struct snd_soc_machine amesom = {
-+ .name = "Amesom",
-+ .probe = amesom_probe,
-+ .remove = amesom_remove,
-+ .dai_link = amesom_dai,
-+ .num_links = ARRAY_SIZE(amesom_dai),
-+};
-+
-+static struct tlv320_setup_data amesom_tlv320_setup = {
-+#ifdef TLV320AIC24K //codec2
-+ .i2c_address = 0x41,
-+#else // TLV320AIC14k
-+ .i2c_address = 0x40,
-+#endif
-+};
-+
-+static struct snd_soc_device amesom_snd_devdata = {
-+ .machine = &amesom,
-+ .platform = &pxa2xx_soc_platform,
-+ .codec_dev = &soc_codec_dev_tlv320,
-+ .codec_data = &amesom_tlv320_setup,
-+};
-+
-+static struct platform_device *amesom_snd_device;
-+
-+static int __init amesom_init(void)
-+{
-+ int ret;
-+
-+ amesom_snd_device = platform_device_alloc("soc-audio", -1);
-+ if (!amesom_snd_device)
-+ return -ENOMEM;
-+
-+ platform_set_drvdata(amesom_snd_device, &amesom_snd_devdata);
-+ amesom_snd_devdata.dev = &amesom_snd_device->dev;
-+ ret = platform_device_add(amesom_snd_device);
-+
-+ if (ret)
-+ platform_device_put(amesom_snd_device);
-+
-+
-+ /* SSP port 2 slave */
-+ pxa_gpio_mode(GPIO11_SSP2RX_MD);
-+ pxa_gpio_mode(GPIO13_SSP2TX_MD);
-+ pxa_gpio_mode(GPIO50_SSP2CLKS_MD);
-+ pxa_gpio_mode(GPIO14_SSP2FRMS_MD);
-+
-+ return ret;
-+}
-+
-+static void __exit amesom_exit(void)
-+{
-+ platform_device_unregister(amesom_snd_device);
-+}
-+
-+module_init(amesom_init);
-+module_exit(amesom_exit);
-+
-+/* Module information */
-+MODULE_AUTHOR("Nicola Perrino");
-+MODULE_DESCRIPTION("ALSA SoC TLV320 Amesom");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/s3c24xx/neo1973_wm8753.c
-===================================================================
---- /dev/null
-+++ linux-2.6.21-moko/sound/soc/s3c24xx/neo1973_wm8753.c
-@@ -0,0 +1,686 @@
-+/*
-+ * neo1973_wm8753.c -- SoC audio for Neo1973
-+ *
-+ * Copyright 2007 Wolfson Microelectronics PLC.
-+ * Author: Graeme Gregory
-+ * graeme.gregory at wolfsonmicro.com or linux at wolfsonmicro.com
-+ *
-+ * This program is free software; you can redistribute it and/or modify it
-+ * under the terms of the GNU General Public License as published by the
-+ * Free Software Foundation; either version 2 of the License, or (at your
-+ * option) any later version.
-+ *
-+ * Revision history
-+ * 20th Jan 2007 Initial version.
-+ * 05th Feb 2007 Rename all to Neo1973
-+ *
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/moduleparam.h>
-+#include <linux/timer.h>
-+#include <linux/interrupt.h>
-+#include <linux/platform_device.h>
-+#include <linux/i2c.h>
-+#include <sound/driver.h>
-+#include <sound/core.h>
-+#include <sound/pcm.h>
-+#include <sound/soc.h>
-+#include <sound/soc-dapm.h>
-+
-+#include <asm/mach-types.h>
-+#include <asm/hardware/scoop.h>
-+#include <asm/arch/regs-iis.h>
-+#include <asm/arch/regs-clock.h>
-+#include <asm/arch/regs-gpio.h>
-+#include <asm/arch/hardware.h>
-+#include <asm/arch/audio.h>
-+#include <asm/io.h>
-+#include <asm/arch/spi-gpio.h>
-+#include "../codecs/wm8753.h"
-+#include "lm4857.h"
-+#include "s3c24xx-pcm.h"
-+#include "s3c24xx-i2s.h"
-+
-+#define NEO1973_DEBUG 0
-+#if NEO1973_DEBUG
-+#define DBG(x...) printk(KERN_DEBUG x)
-+#else
-+#define DBG(x...)
-+#endif
-+
-+/* define the scenarios */
-+#define NEO_AUDIO_OFF 0
-+#define NEO_GSM_CALL_AUDIO_HANDSET 1
-+#define NEO_GSM_CALL_AUDIO_HEADSET 2
-+#define NEO_GSM_CALL_AUDIO_BLUETOOTH 3
-+#define NEO_STEREO_TO_SPEAKERS 4
-+#define NEO_STEREO_TO_HEADPHONES 5
-+#define NEO_CAPTURE_HANDSET 6
-+#define NEO_CAPTURE_HEADSET 7
-+#define NEO_CAPTURE_BLUETOOTH 8
-+
-+static struct snd_soc_machine neo1973;
-+static struct i2c_client *i2c;
-+
-+static int neo1973_hifi_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-+ struct snd_soc_cpu_dai *cpu_dai = rtd->dai->cpu_dai;
-+ unsigned int pll_out = 0, bclk = 0;
-+ int ret = 0;
-+ unsigned long iis_clkrate;
-+
-+ iis_clkrate = s3c24xx_i2s_get_clockrate();
-+
-+ switch (params_rate(params)) {
-+ case 8000:
-+ case 16000:
-+ pll_out = 12288000;
-+ break;
-+ case 48000:
-+ bclk = WM8753_BCLK_DIV_4;
-+ pll_out = 12288000;
-+ break;
-+ case 96000:
-+ bclk = WM8753_BCLK_DIV_2;
-+ pll_out = 12288000;
-+ break;
-+ case 11025:
-+ bclk = WM8753_BCLK_DIV_16;
-+ pll_out = 11289600;
-+ break;
-+ case 22050:
-+ bclk = WM8753_BCLK_DIV_8;
-+ pll_out = 11289600;
-+ break;
-+ case 44100:
-+ bclk = WM8753_BCLK_DIV_4;
-+ pll_out = 11289600;
-+ break;
-+ case 88200:
-+ bclk = WM8753_BCLK_DIV_2;
-+ pll_out = 11289600;
-+ break;
-+ }
-+
-+ /* set codec DAI configuration */
-+ ret = codec_dai->dai_ops.set_fmt(codec_dai,
-+ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set cpu DAI configuration */
-+ ret = cpu_dai->dai_ops.set_fmt(cpu_dai,
-+ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set the codec system clock for DAC and ADC */
-+ ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_MCLK, pll_out,
-+ SND_SOC_CLOCK_IN);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set MCLK division for sample rate */
-+ ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
-+ S3C2410_IISMOD_32FS );
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set codec BCLK division for sample rate */
-+ ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_BCLKDIV, bclk);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set prescaler division for sample rate */
-+ ret = cpu_dai->dai_ops.set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
-+ S3C24XX_PRESCALE(4,4));
-+ if (ret < 0)
-+ return ret;
-+
-+ /* codec PLL input is PCLK/4 */
-+ ret = codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1, iis_clkrate/4,
-+ pll_out);
-+ if (ret < 0)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+static int neo1973_hifi_hw_free(struct snd_pcm_substream *substream)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-+
-+ /* disable the PLL */
-+ return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL1, 0, 0);
-+}
-+
-+/*
-+ * Neo1973 WM8753 HiFi DAI opserations.
-+ */
-+static struct snd_soc_ops neo1973_hifi_ops = {
-+ .hw_params = neo1973_hifi_hw_params,
-+ .hw_free = neo1973_hifi_hw_free,
-+};
-+
-+static int neo1973_voice_hw_params(struct snd_pcm_substream *substream,
-+ struct snd_pcm_hw_params *params)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-+ unsigned int pcmdiv = 0;
-+ int ret = 0;
-+ unsigned long iis_clkrate;
-+
-+ /* todo: gg where is sysclk coming from for voice ?? */
-+ iis_clkrate = s3c24xx_i2s_get_clockrate();
-+
-+ if (params_rate(params) != 8000)
-+ return -EINVAL;
-+ if(params_channels(params) != 1)
-+ return -EINVAL;
-+
-+ pcmdiv = WM8753_PCM_DIV_6; /* 2.048 MHz */
-+
-+ /* todo: gg check mode (DSP_B) against CSR datasheet */
-+ /* set codec DAI configuration */
-+ ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B |
-+ SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set the codec system clock for DAC and ADC */
-+ ret = codec_dai->dai_ops.set_sysclk(codec_dai, WM8753_PCMCLK, 12288000,
-+ SND_SOC_CLOCK_IN);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* set codec PCM division for sample rate */
-+ ret = codec_dai->dai_ops.set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv);
-+ if (ret < 0)
-+ return ret;
-+
-+ /* configue and enable PLL for 12.288MHz output */
-+ ret = codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL2, iis_clkrate/4,
-+ 12288000);
-+ if (ret < 0)
-+ return ret;
-+
-+ return 0;
-+}
-+
-+static int neo1973_voice_hw_free(struct snd_pcm_substream *substream)
-+{
-+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
-+ struct snd_soc_codec_dai *codec_dai = rtd->dai->codec_dai;
-+
-+ /* disable the PLL */
-+ return codec_dai->dai_ops.set_pll(codec_dai, WM8753_PLL2, 0, 0);
-+}
-+
-+static struct snd_soc_ops neo1973_voice_ops = {
-+ .hw_params = neo1973_voice_hw_params,
-+ .hw_free = neo1973_voice_hw_free,
-+};
-+
-+static int neo1973_scenario = 0;
-+
-+static int neo1973_get_scenario(struct snd_kcontrol *kcontrol,
-+ struct snd_ctl_elem_value *ucontrol)
-+{
-+ ucontrol->value.integer.value[0] = neo1973_scenario;
-+ return 0;
-+}
-+
-+static int set_scenario_endpoints(struct snd_soc_codec *codec, int scenario)
-+{
-+ switch(neo1973_scenario) {
-+ case NEO_AUDIO_OFF:
-+ snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
-+ snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
-+ snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
-+ snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
-+ snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
-+ break;
-+ case NEO_GSM_CALL_AUDIO_HANDSET:
-+ snd_soc_dapm_set_endpoint(codec, "Audio Out", 1);
-+ snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
-+ snd_soc_dapm_set_endpoint(codec, "GSM Line In", 1);
-+ snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
-+ snd_soc_dapm_set_endpoint(codec, "Call Mic", 1);
-+ break;
-+ case NEO_GSM_CALL_AUDIO_HEADSET:
-+ snd_soc_dapm_set_endpoint(codec, "Audio Out", 1);
-+ snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
-+ snd_soc_dapm_set_endpoint(codec, "GSM Line In", 1);
-+ snd_soc_dapm_set_endpoint(codec, "Headset Mic", 1);
-+ snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
-+ break;
-+ case NEO_GSM_CALL_AUDIO_BLUETOOTH:
-+ snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
-+ snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1);
-+ snd_soc_dapm_set_endpoint(codec, "GSM Line In", 1);
-+ snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
-+ snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
-+ break;
-+ case NEO_STEREO_TO_SPEAKERS:
-+ snd_soc_dapm_set_endpoint(codec, "Audio Out", 1);
-+ snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
-+ snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
-+ snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
-+ snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
-+ break;
-+ case NEO_STEREO_TO_HEADPHONES:
-+ snd_soc_dapm_set_endpoint(codec, "Audio Out", 1);
-+ snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
-+ snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
-+ snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
-+ snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
-+ break;
-+ case NEO_CAPTURE_HANDSET:
-+ snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
-+ snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
-+ snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
-+ snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
-+ snd_soc_dapm_set_endpoint(codec, "Call Mic", 1);
-+ break;
-+ case NEO_CAPTURE_HEADSET:
-+ snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
-+ snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
-+ snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
-+ snd_soc_dapm_set_endpoint(codec, "Headset Mic", 1);
-+ snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
-+ break;
-+ case NEO_CAPTURE_BLUETOOTH:
-+ snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
-+ snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
-+ snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
-+ snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
-+ snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
-+ break;
-+ default:
-+ snd_soc_dapm_set_endpoint(codec, "Audio Out", 0);
-+ snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0);
-+ snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0);
-+ snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0);
-+ snd_soc_dapm_set_endpoint(codec, "Call Mic", 0);
-+ }
-+
-+ snd_soc_dapm_sync_endpoints(codec);
-+
-+ return 0;
-+}
-+
-+static int neo1973_set_scenario(struct snd_kcontrol *kcontrol,
-+ struct snd_ctl_elem_value *ucontrol)
-+{
-+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-+
-+ if (neo1973_scenario == ucontrol->value.integer.value[0])
-+ return 0;
-+
-+ neo1973_scenario = ucontrol->value.integer.value[0];
-+
-+ set_scenario_endpoints(codec, neo1973_scenario);
-+
-+ return 1;
-+}
-+
-+static u8 lm4857_regs[4] = {0x00, 0x40, 0x80, 0xC0};
-+
-+static void lm4857_write_regs( void )
-+{
-+ if( i2c_master_send(i2c, lm4857_regs, 4) != 4)
-+ printk(KERN_WARNING "lm4857: i2c write failed\n");
-+}
-+
-+static int lm4857_get_reg(struct snd_kcontrol *kcontrol,
-+ struct snd_ctl_elem_value *ucontrol)
-+{
-+ int reg=kcontrol->private_value & 0xFF;
-+ int shift = (kcontrol->private_value >> 8) & 0x0F;
-+ int mask = (kcontrol->private_value >> 16) & 0xFF;
-+
-+ ucontrol->value.integer.value[0] = (lm4857_regs[reg] >> shift) & mask;
-+
-+ return 0;
-+}
-+
-+static int lm4857_set_reg(struct snd_kcontrol *kcontrol,
-+ struct snd_ctl_elem_value *ucontrol)
-+{
-+ int reg = kcontrol->private_value & 0xFF;
-+ int shift = (kcontrol->private_value >> 8) & 0x0F;
-+ int mask = (kcontrol->private_value >> 16) & 0xFF;
-+
-+ if (((lm4857_regs[reg] >> shift ) & mask) ==
-+ ucontrol->value.integer.value[0])
-+ return 0;
-+
-+ lm4857_regs[reg] &= ~ (mask << shift);
-+ lm4857_regs[reg] |= ucontrol->value.integer.value[0] << shift;
-+
-+ lm4857_write_regs();
-+ return 1;
-+}
-+
-+static int lm4857_get_mode(struct snd_kcontrol *kcontrol,
-+ struct snd_ctl_elem_value *ucontrol)
-+{
-+ u8 value = lm4857_regs[LM4857_CTRL] & 0x0F;
-+
-+ if (value)
-+ value -= 5;
-+
-+ ucontrol->value.integer.value[0] = value;
-+ return 0;
-+}
-+
-+static int lm4857_set_mode(struct snd_kcontrol *kcontrol,
-+ struct snd_ctl_elem_value *ucontrol)
-+{
-+ u8 value = ucontrol->value.integer.value[0];
-+
-+ if (value)
-+ value += 5;
-+
-+ if ((lm4857_regs[LM4857_CTRL] & 0x0F) == value)
-+ return 0;
-+
-+ lm4857_regs[LM4857_CTRL] &= 0xF0;
-+ lm4857_regs[LM4857_CTRL] |= value;
-+
-+ lm4857_write_regs();
-+ return 1;
-+}
-+
-+static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = {
-+ SND_SOC_DAPM_LINE("Audio Out", NULL),
-+ SND_SOC_DAPM_LINE("GSM Line Out", NULL),
-+ SND_SOC_DAPM_LINE("GSM Line In", NULL),
-+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
-+ SND_SOC_DAPM_MIC("Call Mic", NULL),
-+};
-+
-+
-+/* example machine audio_mapnections */
-+static const char* audio_map[][3] = {
-+
-+ /* Connections to the lm4857 amp */
-+ {"Audio Out", NULL, "LOUT1"},
-+ {"Audio Out", NULL, "ROUT1"},
-+
-+ /* Connections to the GSM Module */
-+ {"GSM Line Out", NULL, "MONO1"},
-+ {"GSM Line Out", NULL, "MONO2"},
-+ {"RXP", NULL, "GSM Line In"},
-+ {"RXN", NULL, "GSM Line In"},
-+
-+ /* Connections to Headset */
-+ {"MIC1", NULL, "Mic Bias"},
-+ {"Mic Bias", NULL, "Headset Mic"},
-+
-+ /* Call Mic */
-+ {"MIC2", NULL, "Mic Bias"},
-+ {"MIC2N", NULL, "Mic Bias"},
-+ {"Mic Bias", NULL, "Call Mic"},
-+
-+ /* Connect the ALC pins */
-+ {"ACIN", NULL, "ACOP"},
-+
-+ {NULL, NULL, NULL},
-+};
-+
-+static const char *lm4857_mode[] = {
-+ "Off",
-+ "Call Speaker",
-+ "Stereo Speakers",
-+ "Stereo Speakers + Headphones",
-+ "Headphones"
-+};
-+
-+static const struct soc_enum lm4857_mode_enum[] = {
-+ SOC_ENUM_SINGLE_EXT(5, lm4857_mode),
-+};
-+
-+static const char *neo_scenarios[] = {
-+ "Off",
-+ "GSM Handset",
-+ "GSM Headset",
-+ "GSM Bluetooth",
-+ "Speakers",
-+ "Headphones",
-+ "Capture Handset",
-+ "Capture Headset",
-+ "Capture Bluetooth"
-+};
-+
-+static const struct soc_enum neo_scenario_enum[] = {
-+ SOC_ENUM_SINGLE_EXT(9,neo_scenarios),
-+};
-+
-+static const struct snd_kcontrol_new wm8753_neo1973_controls[] = {
-+ SOC_SINGLE_EXT("Amp Left Playback Volume", LM4857_LVOL, 0, 31, 0,
-+ lm4857_get_reg, lm4857_set_reg),
-+ SOC_SINGLE_EXT("Amp Right Playback Volume", LM4857_RVOL, 0, 31, 0,
-+ lm4857_get_reg, lm4857_set_reg),
-+ SOC_SINGLE_EXT("Amp Mono Playback Volume", LM4857_MVOL, 0, 31, 0,
-+ lm4857_get_reg, lm4857_set_reg),
-+ SOC_ENUM_EXT("Amp Mode", lm4857_mode_enum[0],
-+ lm4857_get_mode, lm4857_set_mode),
-+ SOC_ENUM_EXT("Neo Mode", neo_scenario_enum[0],
-+ neo1973_get_scenario, neo1973_set_scenario),
-+ SOC_SINGLE_EXT("Amp Spk 3D Playback Switch", LM4857_LVOL, 5, 1, 0,
-+ lm4857_get_reg, lm4857_set_reg),
-+ SOC_SINGLE_EXT("Amp HP 3d Playback Switch", LM4857_RVOL, 5, 1, 0,
-+ lm4857_get_reg, lm4857_set_reg),
-+ SOC_SINGLE_EXT("Amp Fast Wakeup Playback Switch", LM4857_CTRL, 5, 1, 0,
-+ lm4857_get_reg, lm4857_set_reg),
-+ SOC_SINGLE_EXT("Amp Earpiece 6dB Playback Switch", LM4857_CTRL, 4, 1, 0,
-+ lm4857_get_reg, lm4857_set_reg),
-+};
-+
-+/*
-+ * This is an example machine initialisation for a wm8753 connected to a
-+ * neo1973 II. It is missing logic to detect hp/mic insertions and logic
-+ * to re-route the audio in such an event.
-+ */
-+static int neo1973_wm8753_init(struct snd_soc_codec *codec)
-+{
-+ int i, err;
-+
-+ /* set up NC codec pins */
-+ snd_soc_dapm_set_endpoint(codec, "LOUT2", 0);
-+ snd_soc_dapm_set_endpoint(codec, "ROUT2", 0);
-+ snd_soc_dapm_set_endpoint(codec, "OUT3", 0);
-+ snd_soc_dapm_set_endpoint(codec, "OUT4", 0);
-+ snd_soc_dapm_set_endpoint(codec, "LINE1", 0);
-+ snd_soc_dapm_set_endpoint(codec, "LINE2", 0);
-+
-+
-+ /* set endpoints to default mode */
-+ set_scenario_endpoints(codec, NEO_AUDIO_OFF);
-+
-+ /* Add neo1973 specific widgets */
-+ for(i = 0; i < ARRAY_SIZE(wm8753_dapm_widgets); i++)
-+ snd_soc_dapm_new_control(codec, &wm8753_dapm_widgets[i]);
-+
-+ /* add neo1973 specific controls */
-+ for (i = 0; i < ARRAY_SIZE(wm8753_neo1973_controls); i++) {
-+ if ((err = snd_ctl_add(codec->card,
-+ snd_soc_cnew(&wm8753_neo1973_controls[i],codec, NULL))) < 0)
-+ return err;
-+ }
-+
-+ /* set up neo1973 specific audio path audio_mapnects */
-+ for(i = 0; audio_map[i][0] != NULL; i++) {
-+ snd_soc_dapm_connect_input(codec, audio_map[i][0], audio_map[i][1],
-+ audio_map[i][2]);
-+ }
-+
-+ snd_soc_dapm_sync_endpoints(codec);
-+ return 0;
-+}
-+
-+/*
-+ * BT Codec DAI
-+ */
-+static struct snd_soc_cpu_dai bt_dai =
-+{ .name = "Bluetooth",
-+ .id = 0,
-+ .type = SND_SOC_DAI_PCM,
-+ .playback = {
-+ .channels_min = 1,
-+ .channels_max = 1,
-+ .rates = SNDRV_PCM_RATE_8000,
-+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
-+ .capture = {
-+ .channels_min = 1,
-+ .channels_max = 1,
-+ .rates = SNDRV_PCM_RATE_8000,
-+ .formats = SNDRV_PCM_FMTBIT_S16_LE,},
-+};
-+
-+static struct snd_soc_dai_link neo1973_dai[] = {
-+{ /* Hifi Playback - for similatious use with voice below */
-+ .name = "WM8753",
-+ .stream_name = "WM8753 HiFi",
-+ .cpu_dai = &s3c24xx_i2s_dai,
-+ .codec_dai = &wm8753_dai[WM8753_DAI_HIFI],
-+ .init = neo1973_wm8753_init,
-+ .ops = &neo1973_hifi_ops,
-+},
-+{ /* Voice via BT */
-+ .name = "Bluetooth",
-+ .stream_name = "Voice",
-+ .cpu_dai = &bt_dai,
-+ .codec_dai = &wm8753_dai[WM8753_DAI_VOICE],
-+ .ops = &neo1973_voice_ops,
-+},
-+};
-+
-+static struct snd_soc_machine neo1973 = {
-+ .name = "neo1973",
-+ .dai_link = neo1973_dai,
-+ .num_links = ARRAY_SIZE(neo1973_dai),
-+};
-+
-+static struct wm8753_setup_data neo1973_wm8753_setup = {
-+ .i2c_address = 0x1a,
-+};
-+
-+static struct snd_soc_device neo1973_snd_devdata = {
-+ .machine = &neo1973,
-+ .platform = &s3c24xx_soc_platform,
-+ .codec_dev = &soc_codec_dev_wm8753,
-+ .codec_data = &neo1973_wm8753_setup,
-+};
-+
-+static struct i2c_client client_template;
-+
-+static unsigned short normal_i2c[] = { 0x7C, I2C_CLIENT_END };
-+
-+/* Magic definition of all other variables and things */
-+I2C_CLIENT_INSMOD;
-+
-+static int lm4857_amp_probe(struct i2c_adapter *adap, int addr, int kind)
-+{
-+ int ret;
-+
-+ client_template.adapter = adap;
-+ client_template.addr = addr;
-+
-+ DBG("Entering %s\n", __FUNCTION__);
-+
-+ i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
-+ if (i2c == NULL){
-+ return -ENOMEM;
-+ }
-+ memcpy(i2c, &client_template, sizeof(struct i2c_client));
-+
-+ ret = i2c_attach_client(i2c);
-+ if (ret < 0) {
-+ DBG("failed to attach codec at addr %x\n", addr);
-+ goto exit_err;
-+ }
-+
-+ lm4857_write_regs();
-+
-+ return ret;
-+
-+exit_err:
-+ kfree(i2c);
-+ return ret;
-+}
-+
-+static int lm4857_i2c_detach(struct i2c_client *client)
-+{
-+ i2c_detach_client(client);
-+ kfree(client);
-+ return 0;
-+}
-+
-+static int lm4857_i2c_attach(struct i2c_adapter *adap)
-+{
-+ return i2c_probe(adap, &addr_data, lm4857_amp_probe);
-+}
-+
-+#define I2C_DRIVERID_LM4857 0xA5A5 /* liam - need a proper id */
-+
-+/* corgi i2c codec control layer */
-+static struct i2c_driver lm4857_i2c_driver = {
-+ .driver = {
-+ .name = "LM4857 I2C Amp",
-+ .owner = THIS_MODULE,
-+ },
-+ .id = I2C_DRIVERID_LM4857,
-+ .attach_adapter = lm4857_i2c_attach,
-+ .detach_client = lm4857_i2c_detach,
-+ .command = NULL,
-+};
-+
-+static struct i2c_client client_template = {
-+ .name = "LM4857",
-+ .driver = &lm4857_i2c_driver,
-+};
-+
-+static struct platform_device *neo1973_snd_device;
-+
-+static int __init neo1973_init(void)
-+{
-+ int ret;
-+
-+ neo1973_snd_device = platform_device_alloc("soc-audio", -1);
-+ if (!neo1973_snd_device)
-+ return -ENOMEM;
-+
-+ platform_set_drvdata(neo1973_snd_device, &neo1973_snd_devdata);
-+ neo1973_snd_devdata.dev = &neo1973_snd_device->dev;
-+ ret = platform_device_add(neo1973_snd_device);
-+
-+ if (ret)
-+ platform_device_put(neo1973_snd_device);
-+
-+ ret = i2c_add_driver(&lm4857_i2c_driver);
-+ if (ret != 0)
-+ printk(KERN_ERR "can't add i2c driver");
-+
-+ return ret;
-+}
-+
-+static void __exit neo1973_exit(void)
-+{
-+ platform_device_unregister(neo1973_snd_device);
-+}
-+
-+module_init(neo1973_init);
-+module_exit(neo1973_exit);
-+
-+/* Module information */
-+MODULE_AUTHOR("Graeme Gregory, graeme.gregory at wolfsonmicro.com, www.wolfsonmicro.com");
-+MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973");
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.21-moko/sound/soc/Kconfig
-===================================================================
---- linux-2.6.21-moko.orig/sound/soc/Kconfig
-+++ linux-2.6.21-moko/sound/soc/Kconfig
-@@ -25,7 +25,9 @@
- menu "SoC Platforms"
- depends on SND_SOC
- source "sound/soc/at91/Kconfig"
-+source "sound/soc/imx/Kconfig"
- source "sound/soc/pxa/Kconfig"
-+source "sound/soc/s3c24xx/Kconfig"
- endmenu
-
- # Supported codecs
-Index: linux-2.6.21-moko/sound/soc/Makefile
-===================================================================
---- linux-2.6.21-moko.orig/sound/soc/Makefile
-+++ linux-2.6.21-moko/sound/soc/Makefile
-@@ -1,4 +1,4 @@
- snd-soc-core-objs := soc-core.o soc-dapm.o
-
- obj-$(CONFIG_SND_SOC) += snd-soc-core.o
--obj-$(CONFIG_SND_SOC) += codecs/ at91/ pxa/
-+obj-$(CONFIG_SND_SOC) += codecs/ at91/ imx/ pxa/ s3c24xx/
-Index: linux-2.6.21-moko/sound/soc/codecs/Kconfig
-===================================================================
---- linux-2.6.21-moko.orig/sound/soc/codecs/Kconfig
-+++ linux-2.6.21-moko/sound/soc/codecs/Kconfig
-@@ -2,6 +2,14 @@
- tristate
- depends on SND_SOC
-
-+config SND_SOC_WM8510
-+ tristate
-+ depends on SND_SOC
-+
-+config SND_SOC_WM8711
-+ tristate
-+ depends on SND_SOC
-+
- config SND_SOC_WM8731
- tristate
- depends on SND_SOC
-@@ -10,6 +18,46 @@
- tristate
- depends on SND_SOC
-
-+config SND_SOC_WM8753
-+ tristate
-+ depends on SND_SOC
-+
-+config SND_SOC_WM8772
-+ tristate
-+ depends on SND_SOC
-+
-+config SND_SOC_WM8956
-+ tristate
-+ depends on SND_SOC
-+
-+config SND_SOC_WM8960
-+ tristate
-+ depends on SND_SOC
-+
-+config SND_SOC_WM8971
-+ tristate
-+ depends on SND_SOC
-+
-+config SND_SOC_WM8974
-+ tristate
-+ depends on SND_SOC
-+
-+config SND_SOC_WM8976
-+ tristate
-+ depends on SND_SOC
-+
-+config SND_SOC_WM8980
-+ tristate
-+ depends on SND_SOC
-+
-+config SND_SOC_UDA1380
-+ tristate
-+ depends on SND_SOC
-+
- config SND_SOC_WM9712
- tristate
- depends on SND_SOC
-+
-+config SND_SOC_WM9713
-+ tristate
-+ depends on SND_SOC
-Index: linux-2.6.21-moko/sound/soc/codecs/Makefile
-===================================================================
---- linux-2.6.21-moko.orig/sound/soc/codecs/Makefile
-+++ linux-2.6.21-moko/sound/soc/codecs/Makefile
-@@ -1,9 +1,33 @@
- snd-soc-ac97-objs := ac97.o
-+snd-soc-wm8711-objs := wm8711.o
-+snd-soc-wm8510-objs := wm8510.o
- snd-soc-wm8731-objs := wm8731.o
- snd-soc-wm8750-objs := wm8750.o
-+snd-soc-wm8753-objs := wm8753.o
-+snd-soc-wm8772-objs := wm8772.o
-+snd-soc-wm8956-objs := wm8956.o
-+snd-soc-wm8960-objs := wm8960.o
-+snd-soc-wm8971-objs := wm8971.o
-+snd-soc-wm8974-objs := wm8974.o
-+snd-soc-wm8976-objs := wm8976.o
-+snd-soc-wm8980-objs := wm8980.o
-+snd-soc-uda1380-objs := uda1380.o
- snd-soc-wm9712-objs := wm9712.o
-+snd-soc-wm9713-objs := wm9713.o
-
- obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
-+obj-$(CONFIG_SND_SOC_WM8711) += snd-soc-wm8711.o
-+obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o
- obj-$(CONFIG_SND_SOC_WM8731) += snd-soc-wm8731.o
- obj-$(CONFIG_SND_SOC_WM8750) += snd-soc-wm8750.o
-+obj-$(CONFIG_SND_SOC_WM8753) += snd-soc-wm8753.o
-+obj-$(CONFIG_SND_SOC_WM8772) += snd-soc-wm8772.o
-+obj-$(CONFIG_SND_SOC_WM8956) += snd-soc-wm8956.o
-+obj-$(CONFIG_SND_SOC_WM8960) += snd-soc-wm8960.o
-+obj-$(CONFIG_SND_SOC_WM8971) += snd-soc-wm8971.o
-+obj-$(CONFIG_SND_SOC_WM8974) += snd-soc-wm8974.o
-+obj-$(CONFIG_SND_SOC_WM8976) += snd-soc-wm8976.o
-+obj-$(CONFIG_SND_SOC_WM8980) += snd-soc-wm8980.o
-+obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
- obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
-+obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
Modified: branches/src/target/kernel/2.6.22.x/patches/gta01-core.patch
===================================================================
--- branches/src/target/kernel/2.6.22.x/patches/gta01-core.patch 2007-07-26 14:40:11 UTC (rev 2397)
+++ branches/src/target/kernel/2.6.22.x/patches/gta01-core.patch 2007-07-26 15:24:28 UTC (rev 2398)
@@ -1,10 +1,8 @@
This patch adds support for the FIC GTA01 machine type to the ARM port of
the linux kernel.
-Index: linux-2.6.21.3-moko/arch/arm/mach-s3c2410/Kconfig
-===================================================================
---- linux-2.6.21.3-moko.orig/arch/arm/mach-s3c2410/Kconfig
-+++ linux-2.6.21.3-moko/arch/arm/mach-s3c2410/Kconfig
+--- linux-2.6.22.1.orig/arch/arm/mach-s3c2410/Kconfig
++++ linux-2.6.22.1/arch/arm/mach-s3c2410/Kconfig
@@ -109,5 +109,12 @@
help
Say Y here if you are using the Armzone QT2410
@@ -18,20 +16,16 @@
+
endmenu
-Index: linux-2.6.21.3-moko/arch/arm/mach-s3c2410/Makefile
-===================================================================
---- linux-2.6.21.3-moko.orig/arch/arm/mach-s3c2410/Makefile
-+++ linux-2.6.21.3-moko/arch/arm/mach-s3c2410/Makefile
+--- linux-2.6.22.1.orig/arch/arm/mach-s3c2410/Makefile
++++ linux-2.6.22.1/arch/arm/mach-s3c2410/Makefile
@@ -29,3 +29,4 @@
obj-$(CONFIG_BAST_PC104_IRQ) += bast-irq.o
obj-$(CONFIG_MACH_VR1000) += mach-vr1000.o usb-simtec.o
obj-$(CONFIG_MACH_QT2410) += mach-qt2410.o
+obj-$(CONFIG_MACH_NEO1973_GTA01)+= mach-gta01.o
-Index: linux-2.6.21.3-moko/arch/arm/mach-s3c2410/mach-gta01.c
-===================================================================
--- /dev/null
-+++ linux-2.6.21.3-moko/arch/arm/mach-s3c2410/mach-gta01.c
-@@ -0,0 +1,759 @@
++++ linux-2.6.22.1/arch/arm/mach-s3c2410/mach-gta01.c
+@@ -0,0 +1,747 @@
+/*
+ * linux/arch/arm/mach-s3c2410/mach-gta01.c
+ *
@@ -69,7 +63,6 @@
+#include <linux/serial_core.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_bitbang.h>
-+#include <linux/mmc/protocol.h>
+#include <linux/mmc/host.h>
+
+#include <linux/mtd/mtd.h>
@@ -371,11 +364,6 @@
+ &s3c_device_ts,
+};
+
-+static struct s3c24xx_board gta01_board __initdata = {
-+ .devices = gta01_devices,
-+ .devices_count = ARRAY_SIZE(gta01_devices)
-+};
-+
+static struct mtd_partition gta01_nand_part[] = {
+ [0] = {
+ .name = "U-Boot",
@@ -426,14 +414,7 @@
+};
+
+static unsigned int mmc_millivolts[] = {
-+ [MMC_VDD_145_150] = 1500,
-+ [MMC_VDD_150_155] = 1500,
-+ [MMC_VDD_155_160] = 1600,
-+ [MMC_VDD_160_165] = 1600,
-+ [MMC_VDD_165_170] = 1700,
-+ [MMC_VDD_17_18] = 1800,
-+ [MMC_VDD_18_19] = 1900,
-+ [MMC_VDD_19_20] = 2000,
++ [MMC_VDD_165_195] = 2000, // TODO is this correct?
+ [MMC_VDD_20_21] = 2100,
+ [MMC_VDD_21_22] = 2200,
+ [MMC_VDD_22_23] = 2300,
@@ -707,7 +688,6 @@
+ s3c24xx_init_io(gta01_iodesc, ARRAY_SIZE(gta01_iodesc));
+ s3c24xx_init_clocks(12*1000*1000);
+ s3c24xx_init_uarts(gta01_uartcfgs, ARRAY_SIZE(gta01_uartcfgs));
-+ s3c24xx_set_board(>a01_board);
+}
+
+static irqreturn_t gta01_modem_irq(int irq, void *param)
@@ -772,6 +752,8 @@
+ platform_device_register(>a01_pmu_dev);
+ platform_device_register(>a01_led_dev);
+
++ platform_add_devices(gta01_devices, ARRAY_SIZE(gta01_devices));
++
+ s3c2410_pm_init();
+
+ set_irq_type(GTA01_IRQ_MODEM, IRQT_RISING);
@@ -791,10 +773,8 @@
+MACHINE_END
+
+
-Index: linux-2.6.21.3-moko/include/asm-arm/arch-s3c2410/gta01.h
-===================================================================
--- /dev/null
-+++ linux-2.6.21.3-moko/include/asm-arm/arch-s3c2410/gta01.h
++++ linux-2.6.22.1/include/asm-arm/arch-s3c2410/gta01.h
@@ -0,0 +1,70 @@
+#ifndef _GTA01_H
+#define _GTA01_H
Modified: branches/src/target/kernel/2.6.22.x/patches/gta01-no_nand_partitions.patch
===================================================================
--- branches/src/target/kernel/2.6.22.x/patches/gta01-no_nand_partitions.patch 2007-07-26 14:40:11 UTC (rev 2397)
+++ branches/src/target/kernel/2.6.22.x/patches/gta01-no_nand_partitions.patch 2007-07-26 15:24:28 UTC (rev 2398)
@@ -1,9 +1,7 @@
-Index: linux-2.6.20/arch/arm/mach-s3c2410/mach-gta01.c
-===================================================================
---- linux-2.6.20.orig/arch/arm/mach-s3c2410/mach-gta01.c 2007-02-15 16:28:11.000000000 +0100
-+++ linux-2.6.20/arch/arm/mach-s3c2410/mach-gta01.c 2007-02-15 16:28:11.000000000 +0100
-@@ -186,40 +186,10 @@
- .devices_count = ARRAY_SIZE(gta01_devices)
+--- linux-2.6.22.1.orig/arch/arm/mach-s3c2410/mach-gta01.c
++++ linux-2.6.22.1/arch/arm/mach-s3c2410/mach-gta01.c
+@@ -336,40 +336,10 @@
+ &s3c_device_ts,
};
-static struct mtd_partition gta01_nand_part[] = {
@@ -44,11 +42,9 @@
},
};
-Index: linux-2.6.20/drivers/mtd/nand/s3c2410.c
-===================================================================
---- linux-2.6.20.orig/drivers/mtd/nand/s3c2410.c 2007-02-15 16:28:11.000000000 +0100
-+++ linux-2.6.20/drivers/mtd/nand/s3c2410.c 2007-02-15 16:28:11.000000000 +0100
-@@ -471,17 +471,31 @@
+--- linux-2.6.22.1.orig/drivers/mtd/nand/s3c2410.c
++++ linux-2.6.22.1/drivers/mtd/nand/s3c2410.c
+@@ -547,17 +547,31 @@
}
#ifdef CONFIG_MTD_PARTITIONS
Modified: branches/src/target/kernel/2.6.22.x/patches/gta01-pcf50606.patch
===================================================================
--- branches/src/target/kernel/2.6.22.x/patches/gta01-pcf50606.patch 2007-07-26 14:40:11 UTC (rev 2397)
+++ branches/src/target/kernel/2.6.22.x/patches/gta01-pcf50606.patch 2007-07-26 15:24:28 UTC (rev 2398)
@@ -1,9 +1,7 @@
This is the PCF50606 power management unit driver for FIC GTA01
-Index: linux-2.6.21.3-moko/drivers/i2c/chips/pcf50606.c
-===================================================================
--- /dev/null
-+++ linux-2.6.21.3-moko/drivers/i2c/chips/pcf50606.c
++++ linux-2.6.22.1/drivers/i2c/chips/pcf50606.c
@@ -0,0 +1,1929 @@
+/* Philips PCF50606 Power Management Unit (PMU) driver
+ *
@@ -608,7 +606,7 @@
+ if (int1 & PCF50606_INT1_SECOND) {
+ DEBUGPC("SECOND ");
+ if (pcf->flags & PCF50606_F_RTC_SECOND)
-+ rtc_update_irq(&pcf->rtc->class_dev, 1,
++ rtc_update_irq(pcf->rtc, 1,
+ RTC_PF | RTC_IRQF);
+
+ if (pcf->onkey_seconds >= 0 &&
@@ -628,7 +626,7 @@
+ if (int1 & PCF50606_INT1_ALARM) {
+ DEBUGPC("ALARM ");
+ if (pcf->pdata->used_features & PCF50606_FEAT_RTC)
-+ rtc_update_irq(&pcf->rtc->class_dev, 1,
++ rtc_update_irq(pcf->rtc, 1,
+ RTC_AF | RTC_IRQF);
+ }
+
@@ -1934,10 +1932,8 @@
+
+module_init(pcf50606_init);
+module_exit(pcf50606_exit);
-Index: linux-2.6.21.3-moko/drivers/i2c/chips/pcf50606.h
-===================================================================
--- /dev/null
-+++ linux-2.6.21.3-moko/drivers/i2c/chips/pcf50606.h
++++ linux-2.6.22.1/drivers/i2c/chips/pcf50606.h
@@ -0,0 +1,302 @@
+#ifndef _PCF50606_H
+#define _PCF50606_H
@@ -2241,11 +2237,9 @@
+
+#endif /* _PCF50606_H */
+
-Index: linux-2.6.21.3-moko/drivers/i2c/chips/Kconfig
-===================================================================
---- linux-2.6.21.3-moko.orig/drivers/i2c/chips/Kconfig
-+++ linux-2.6.21.3-moko/drivers/i2c/chips/Kconfig
-@@ -36,6 +36,17 @@
+--- linux-2.6.22.1.orig/drivers/i2c/chips/Kconfig
++++ linux-2.6.22.1/drivers/i2c/chips/Kconfig
+@@ -35,6 +35,17 @@
This driver can also be built as a module. If so, the module
will be called eeprom.
@@ -2262,11 +2256,9 @@
+
config SENSORS_PCF8574
tristate "Philips PCF8574 and PCF8574A"
- depends on I2C && EXPERIMENTAL
-Index: linux-2.6.21.3-moko/drivers/i2c/chips/Makefile
-===================================================================
---- linux-2.6.21.3-moko.orig/drivers/i2c/chips/Makefile
-+++ linux-2.6.21.3-moko/drivers/i2c/chips/Makefile
+ depends on EXPERIMENTAL
+--- linux-2.6.22.1.orig/drivers/i2c/chips/Makefile
++++ linux-2.6.22.1/drivers/i2c/chips/Makefile
@@ -8,6 +8,7 @@
obj-$(CONFIG_SENSORS_MAX6875) += max6875.o
obj-$(CONFIG_SENSORS_M41T00) += m41t00.o
@@ -2275,11 +2267,9 @@
obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o
-Index: linux-2.6.21.3-moko/include/linux/i2c-id.h
-===================================================================
---- linux-2.6.21.3-moko.orig/include/linux/i2c-id.h
-+++ linux-2.6.21.3-moko/include/linux/i2c-id.h
-@@ -159,6 +159,7 @@
+--- linux-2.6.22.1.orig/include/linux/i2c-id.h
++++ linux-2.6.22.1/include/linux/i2c-id.h
+@@ -160,6 +160,7 @@
#define I2C_DRIVERID_FSCHER 1046
#define I2C_DRIVERID_W83L785TS 1047
#define I2C_DRIVERID_OV7670 1048 /* Omnivision 7670 camera */
@@ -2287,10 +2277,8 @@
/*
* ---- Adapter types ----------------------------------------------------
-Index: linux-2.6.21.3-moko/include/linux/pcf50606.h
-===================================================================
--- /dev/null
-+++ linux-2.6.21.3-moko/include/linux/pcf50606.h
++++ linux-2.6.22.1/include/linux/pcf50606.h
@@ -0,0 +1,104 @@
+#ifndef _LINUX_PCF50606_H
+#define _LINUX_PCF50606_H
Modified: branches/src/target/kernel/2.6.22.x/patches/gta02-core.patch
===================================================================
--- branches/src/target/kernel/2.6.22.x/patches/gta02-core.patch 2007-07-26 14:40:11 UTC (rev 2397)
+++ branches/src/target/kernel/2.6.22.x/patches/gta02-core.patch 2007-07-26 15:24:28 UTC (rev 2398)
@@ -1,7 +1,5 @@
-Index: linux-2.6.21.3-moko/arch/arm/mach-s3c2440/mach-gta02.c
-===================================================================
--- /dev/null
-+++ linux-2.6.21.3-moko/arch/arm/mach-s3c2440/mach-gta02.c
++++ linux-2.6.22.1/arch/arm/mach-s3c2440/mach-gta02.c
@@ -0,0 +1,625 @@
+/*
+ * linux/arch/arm/mach-s3c2440/mach-gta02.c
@@ -628,10 +626,8 @@
+ .init_machine = gta02_machine_init,
+ .timer = &s3c24xx_timer,
+MACHINE_END
-Index: linux-2.6.21.3-moko/include/asm-arm/arch-s3c2410/gta02.h
-===================================================================
--- /dev/null
-+++ linux-2.6.21.3-moko/include/asm-arm/arch-s3c2410/gta02.h
++++ linux-2.6.22.1/include/asm-arm/arch-s3c2410/gta02.h
@@ -0,0 +1,24 @@
+#ifndef _GTA02_H
+#define _GTA02_H
@@ -657,10 +653,8 @@
+#define GTA02_IRQ_PCF50633 IRQ_EINT9
+
+#endif /* _GTA02_H */
-Index: linux-2.6.21.3-moko/arch/arm/mach-s3c2440/Kconfig
-===================================================================
---- linux-2.6.21.3-moko.orig/arch/arm/mach-s3c2440/Kconfig
-+++ linux-2.6.21.3-moko/arch/arm/mach-s3c2440/Kconfig
+--- linux-2.6.22.1.orig/arch/arm/mach-s3c2440/Kconfig
++++ linux-2.6.22.1/arch/arm/mach-s3c2440/Kconfig
@@ -73,5 +73,12 @@
help
Say Y here if you are using the FIC Neo1973 GSM Phone
@@ -674,19 +668,15 @@
+
endmenu
-Index: linux-2.6.21.3-moko/arch/arm/mach-s3c2440/Makefile
-===================================================================
---- linux-2.6.21.3-moko.orig/arch/arm/mach-s3c2440/Makefile
-+++ linux-2.6.21.3-moko/arch/arm/mach-s3c2440/Makefile
+--- linux-2.6.22.1.orig/arch/arm/mach-s3c2440/Makefile
++++ linux-2.6.22.1/arch/arm/mach-s3c2440/Makefile
@@ -22,3 +22,4 @@
obj-$(CONFIG_ARCH_S3C2440) += mach-smdk2440.o
obj-$(CONFIG_MACH_NEXCODER_2440) += mach-nexcoder.o
obj-$(CONFIG_MACH_HXD8) += mach-hxd8.o
+obj-$(CONFIG_MACH_NEO1973_GTA02) += mach-gta02.o
-Index: linux-2.6.21.3-moko/arch/arm/common/gta01_pm_bt.c
-===================================================================
---- linux-2.6.21.3-moko.orig/arch/arm/common/gta01_pm_bt.c
-+++ linux-2.6.21.3-moko/arch/arm/common/gta01_pm_bt.c
+--- linux-2.6.22.1.orig/arch/arm/common/gta01_pm_bt.c
++++ linux-2.6.22.1/arch/arm/common/gta01_pm_bt.c
@@ -19,6 +19,7 @@
#include <linux/pcf50606.h>
@@ -774,10 +764,8 @@
/* we pull reset to low to make sure that the chip doesn't
* drain power through the reset line */
s3c2410_gpio_setpin(GTA01_GPIO_BT_EN, 0);
-Index: linux-2.6.21.3-moko/arch/arm/common/gta01_pm_gsm.c
-===================================================================
---- linux-2.6.21.3-moko.orig/arch/arm/common/gta01_pm_gsm.c
-+++ linux-2.6.21.3-moko/arch/arm/common/gta01_pm_gsm.c
+--- linux-2.6.22.1.orig/arch/arm/common/gta01_pm_gsm.c
++++ linux-2.6.22.1/arch/arm/common/gta01_pm_gsm.c
@@ -19,7 +19,9 @@
#include <linux/errno.h>
@@ -820,16 +808,16 @@
}
} else if (!strcmp(attr->attr.name, "reset")) {
s3c2410_gpio_setpin(GTA01_GPIO_MODEM_RST, on);
-@@ -157,6 +161,9 @@
- case GTA01Bv4_SYSTEM_REV:
+@@ -158,6 +162,9 @@
gta01_gsm.gpio_ngsm_en = GTA01Bv2_GPIO_nGSM_EN;
s3c2410_gpio_setpin(GTA01v3_GPIO_nGSM_EN, 0);
-+ break;
+ break;
+ case GTA02v1_SYSTEM_REV:
+ gta01_gsm.gpio_ngsm_en = 0;
- break;
++ break;
default:
dev_warn(&pdev->dev, "Unknown GTA01 Revision 0x%x, "
+ "some PM features not available!!!\n",
@@ -175,9 +182,13 @@
break;
}
Modified: branches/src/target/kernel/2.6.22.x/patches/hxd8-core.patch
===================================================================
--- branches/src/target/kernel/2.6.22.x/patches/hxd8-core.patch 2007-07-26 14:40:11 UTC (rev 2397)
+++ branches/src/target/kernel/2.6.22.x/patches/hxd8-core.patch 2007-07-26 15:24:28 UTC (rev 2398)
@@ -1,8 +1,6 @@
This patch adds another machine, the FIC HXD8
-Index: linux-2.6.21-moko/arch/arm/mach-s3c2440/Kconfig
-===================================================================
---- linux-2.6.21-moko.orig/arch/arm/mach-s3c2440/Kconfig
-+++ linux-2.6.21-moko/arch/arm/mach-s3c2440/Kconfig
+--- linux-2.6.22.1.orig/arch/arm/mach-s3c2440/Kconfig
++++ linux-2.6.22.1/arch/arm/mach-s3c2440/Kconfig
@@ -66,6 +66,12 @@
default y if ARCH_S3C2440
select CPU_S3C2440
@@ -16,20 +14,16 @@
endmenu
-Index: linux-2.6.21-moko/arch/arm/mach-s3c2440/Makefile
-===================================================================
---- linux-2.6.21-moko.orig/arch/arm/mach-s3c2440/Makefile
-+++ linux-2.6.21-moko/arch/arm/mach-s3c2440/Makefile
+--- linux-2.6.22.1.orig/arch/arm/mach-s3c2440/Makefile
++++ linux-2.6.22.1/arch/arm/mach-s3c2440/Makefile
@@ -21,3 +21,4 @@
obj-$(CONFIG_MACH_RX3715) += mach-rx3715.o
obj-$(CONFIG_ARCH_S3C2440) += mach-smdk2440.o
obj-$(CONFIG_MACH_NEXCODER_2440) += mach-nexcoder.o
+obj-$(CONFIG_MACH_HXD8) += mach-hxd8.o
-Index: linux-2.6.21-moko/arch/arm/mach-s3c2440/mach-hxd8.c
-===================================================================
--- /dev/null
-+++ linux-2.6.21-moko/arch/arm/mach-s3c2440/mach-hxd8.c
-@@ -0,0 +1,403 @@
++++ linux-2.6.22.1/arch/arm/mach-s3c2440/mach-hxd8.c
+@@ -0,0 +1,399 @@
+/* linux/arch/arm/mach-s3c2440/mach-hxd8.c
+ *
+ * S3C2440 Machine Support for the FIC HXD8
@@ -64,7 +58,7 @@
+#include <linux/workqueue.h>
+#include <linux/serial_core.h>
+#include <linux/platform_device.h>
-+#include <linux/mmc/protocol.h>
++#include <linux/mmc/host.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
@@ -391,17 +385,11 @@
+ &s3c_device_ts,
+};
+
-+static struct s3c24xx_board hxd8_board __initdata = {
-+ .devices = hxd8_devices,
-+ .devices_count = ARRAY_SIZE(hxd8_devices)
-+};
-+
+static void __init hxd8_map_io(void)
+{
+ s3c24xx_init_io(hxd8_iodesc, ARRAY_SIZE(hxd8_iodesc));
+ s3c24xx_init_clocks(16934400);
+ s3c24xx_init_uarts(hxd8_uartcfgs, ARRAY_SIZE(hxd8_uartcfgs));
-+ s3c24xx_set_board(&hxd8_board);
+}
+
+static void __init hxd8_machine_init(void)
@@ -419,6 +407,8 @@
+
+ platform_device_register(&hxd8_pmu_dev);
+
++ platform_add_devices(hxd8_devices, ARRAY_SIZE(hxd8_devices));
++
+ s3c2410_pm_init();
+}
+
@@ -433,10 +423,8 @@
+ .init_machine = hxd8_machine_init,
+ .timer = &s3c24xx_timer,
+MACHINE_END
-Index: linux-2.6.21-moko/include/asm-arm/arch-s3c2440/hxd8.h
-===================================================================
--- /dev/null
-+++ linux-2.6.21-moko/include/asm-arm/arch-s3c2440/hxd8.h
++++ linux-2.6.22.1/include/asm-arm/arch-s3c2440/hxd8.h
@@ -0,0 +1,16 @@
+#ifndef _HXD8_H
+#define _HXD8_H
Modified: branches/src/target/kernel/2.6.22.x/patches/qt2410-s3c_mci-pdata.patch
===================================================================
--- branches/src/target/kernel/2.6.22.x/patches/qt2410-s3c_mci-pdata.patch 2007-07-26 14:40:11 UTC (rev 2397)
+++ branches/src/target/kernel/2.6.22.x/patches/qt2410-s3c_mci-pdata.patch 2007-07-26 15:24:28 UTC (rev 2398)
@@ -1,21 +1,28 @@
This patch adds platform data to support the SD/MMC slot of the Armzone
S3C2410 development board.
-Index: linux-2.6.20/arch/arm/mach-s3c2410/mach-qt2410.c
-===================================================================
---- linux-2.6.20.orig/arch/arm/mach-s3c2410/mach-qt2410.c 2007-02-15 17:07:38.000000000 +0100
-+++ linux-2.6.20/arch/arm/mach-s3c2410/mach-qt2410.c 2007-02-15 17:08:16.000000000 +0100
-@@ -56,6 +57,7 @@
- #include <asm/arch/ts.h>
+--- linux-2.6.22.1.orig/arch/arm/mach-s3c2410/mach-qt2410.c
++++ linux-2.6.22.1/arch/arm/mach-s3c2410/mach-qt2410.c
+@@ -33,6 +33,8 @@
+ #include <linux/spi/spi.h>
+ #include <linux/spi/spi_bitbang.h>
+
++#include <linux/mmc/host.h>
++
+ #include <linux/mtd/mtd.h>
+ #include <linux/mtd/nand.h>
+ #include <linux/mtd/nand_ecc.h>
+@@ -55,6 +57,7 @@
+ #include <asm/arch/udc.h>
#include <asm/arch/spi.h>
#include <asm/arch/spi-gpio.h>
+#include <asm/arch/mci.h>
- #include "devs.h"
- #include "cpu.h"
-@@ -475,6 +477,13 @@
- __setup("tft=", tft_setup);
+ #include <asm/plat-s3c24xx/common-smdk.h>
+ #include <asm/plat-s3c24xx/devs.h>
+@@ -395,6 +398,13 @@
+ __setup("tft=", qt2410_tft_setup);
+static struct s3c24xx_mci_pdata qt2410_mmc_cfg = {
+ .gpio_wprotect = S3C2410_GPH8,
@@ -27,9 +34,9 @@
static void __init qt2410_map_io(void)
{
s3c24xx_init_io(qt2410_iodesc, ARRAY_SIZE(qt2410_iodesc));
-@@ -498,6 +507,7 @@
- s3c2410_gpio_setpin(S3C2410_GPF7, 1);
-
+@@ -405,6 +415,7 @@
+ static void __init qt2410_machine_init(void)
+ {
s3c_device_nand.dev.platform_data = &qt2410_nand_info;
+ s3c_device_sdi.dev.platform_data = &qt2410_mmc_cfg;
Deleted: branches/src/target/kernel/2.6.22.x/patches/s3c2410-usb-switch.patch
===================================================================
--- branches/src/target/kernel/2.6.22.x/patches/s3c2410-usb-switch.patch 2007-07-26 14:40:11 UTC (rev 2397)
+++ branches/src/target/kernel/2.6.22.x/patches/s3c2410-usb-switch.patch 2007-07-26 15:24:28 UTC (rev 2398)
@@ -1,113 +0,0 @@
-Support switching of USB mode between host and device port
-
-Index: linux-2.6.20.4/arch/arm/mach-s3c2410/usb-modeswitch.c
-===================================================================
---- /dev/null 1970-01-01 00:00:00.000000000 +0000
-+++ linux-2.6.20.4/arch/arm/mach-s3c2410/usb-modeswitch.c 2007-04-01 12:36:49.000000000 +0200
-@@ -0,0 +1,33 @@
-+/*
-+ * linux/arch/arm/mach-s3c2410/usb-modeswitch.c
-+ *
-+ * S3C2410 USB Host/Device switch
-+ *
-+ * Copyright (C) 2007 by OpenMoko, Inc.
-+ * Author: Harald Welte <laforge at openmoko.org>
-+ * All rights reserved.
-+ *
-+ * This program is free software; you can redistribute it and/or
-+ * modify it under the terms of the GNU General Public License as
-+ * published by the Free Software Foundation; 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
-+ *
-+ */
-+
-+void s3c2410_usb_enable_host(int on)
-+{
-+ s3c2410_modify_misccr(S3C2410_MISCCR_HSBHOST,
-+ on ? S3C2410_MISCCR_USBHOST : 0);
-+}
-+
-+
-Index: linux-2.6.20.4/drivers/usb/host/ohci-s3c2410.c
-===================================================================
---- linux-2.6.20.4.orig/drivers/usb/host/ohci-s3c2410.c 2007-04-01 12:33:01.000000000 +0200
-+++ linux-2.6.20.4/drivers/usb/host/ohci-s3c2410.c 2007-04-01 12:36:49.000000000 +0200
-@@ -24,6 +24,7 @@
-
- #include <asm/hardware.h>
- #include <asm/arch/usb-control.h>
-+#include <asm/arch/regs-gpio.h>
-
- #define valid_port(idx) ((idx) == 1 || (idx) == 2)
-
-@@ -308,6 +309,36 @@
- local_irq_restore(flags);
- }
-
-+/* switching of USB pads */
-+static ssize_t show_usb_mode(struct device *dev, struct device_attribute *attr,
-+ char *buf)
-+{
-+ if (__raw_readl(S3C24XX_MISCCR) & S3C2410_MISCCR_USBHOST)
-+ return sprintf(buf, "host");
-+
-+ return sprintf(buf, "device");
-+}
-+
-+static ssize_t set_usb_mode(struct device *dev, struct device_attribute *attr,
-+ const char *buf, size_t count)
-+{
-+ if (!strcmp(buf, "host")) {
-+ s3c2410_modify_misccr(S3C2410_MISCCR_USBHOST,
-+ S3C2410_MISCCR_USBHOST);
-+ /* FIXME:
-+ * - call machine-specific disable-pullup function i
-+ * - enable +Vbus (if hardware supports it)
-+ */
-+ } else if (!strcmp(buf, "device")) {
-+ s3c2410_modify_misccr(S3C2410_MISCCR_USBHOST, 0);
-+ } else
-+ return -EINVAL;
-+
-+ return count;
-+}
-+
-+static DEVICE_ATTR(usb_mode, S_IRUGO | S_IWUSR, show_usb_mode, set_usb_mode);
-+
- /* may be called without controller electrically present */
- /* may be called with controller, bus, and devices active */
-
-@@ -325,6 +356,7 @@
- static void
- usb_hcd_s3c2410_remove (struct usb_hcd *hcd, struct platform_device *dev)
- {
-+ device_remove_file(&dev->dev, &dev_attr_usb_mode);
- usb_remove_hcd(hcd);
- s3c2410_stop_hc(dev);
- iounmap(hcd->regs);
-@@ -392,8 +424,15 @@
- if (retval != 0)
- goto err_ioremap;
-
-+ retval = device_create_file(&dev->dev, &dev_attr_usb_mode);
-+ if (retval != 0)
-+ goto err_hcd;
-+
- return 0;
-
-+ err_hcd:
-+ usb_remove_hcd(hcd);
-+
- err_ioremap:
- s3c2410_stop_hc(dev);
- iounmap(hcd->regs);
Deleted: branches/src/target/kernel/2.6.22.x/patches/s3c2410_udc-vbus_draw_pdata.patch
===================================================================
--- branches/src/target/kernel/2.6.22.x/patches/s3c2410_udc-vbus_draw_pdata.patch 2007-07-26 14:40:11 UTC (rev 2397)
+++ branches/src/target/kernel/2.6.22.x/patches/s3c2410_udc-vbus_draw_pdata.patch 2007-07-26 15:24:28 UTC (rev 2398)
@@ -1,47 +0,0 @@
-Introduce a platform_device (machine) specific callback function
-which gets called when the amount of power we can draw from Vbus
-has changed.
-
-Index: linux-2.6.21-moko/drivers/usb/gadget/s3c2410_udc.c
-===================================================================
---- linux-2.6.21-moko.orig/drivers/usb/gadget/s3c2410_udc.c
-+++ linux-2.6.21-moko/drivers/usb/gadget/s3c2410_udc.c
-@@ -1404,12 +1404,25 @@
- return IRQ_HANDLED;
- }
-
-+static int s3c2410_vbus_draw(struct usb_gadget *_gadget, unsigned int ma)
-+{
-+ struct s3c2410_udc *udc;
-+
-+ dprintk(DEBUG_NORMAL, "s3c2410_vbus_draw()\n");
-+
-+ if (udc_info && udc_info->vbus_draw)
-+ return udc_info->vbus_draw(ma);
-+ else
-+ return 0;
-+}
-+
- static const struct usb_gadget_ops s3c2410_ops = {
- .get_frame = s3c2410_g_get_frame,
- .wakeup = s3c2410_wakeup,
- .set_selfpowered = s3c2410_set_selfpowered,
- .pullup = s3c2410_pullup,
- .vbus_session = s3c2410_udc_vbus_session,
-+ .vbus_draw = s3c2410_vbus_draw,
- };
-
- /*------------------------- gadget driver handling---------------------------*/
-Index: linux-2.6.21-moko/include/asm-arm/arch-s3c2410/udc.h
-===================================================================
---- linux-2.6.21-moko.orig/include/asm-arm/arch-s3c2410/udc.h
-+++ linux-2.6.21-moko/include/asm-arm/arch-s3c2410/udc.h
-@@ -26,7 +26,7 @@
-
- struct s3c2410_udc_mach_info {
- void (*udc_command)(enum s3c2410_udc_cmd_e);
-- void (*vbus_draw)(unsigned int ma);
-+ int (*vbus_draw)(unsigned int ma);
- unsigned int vbus_pin;
- unsigned char vbus_pin_inverted;
- };
Deleted: branches/src/target/kernel/2.6.22.x/patches/s3c2410_udc.patch
===================================================================
--- branches/src/target/kernel/2.6.22.x/patches/s3c2410_udc.patch 2007-07-26 14:40:11 UTC (rev 2397)
+++ branches/src/target/kernel/2.6.22.x/patches/s3c2410_udc.patch 2007-07-26 15:24:28 UTC (rev 2398)
@@ -1,2150 +0,0 @@
-Index: linux-2.6.20/arch/arm/mach-s3c2410/s3c2410.c
-===================================================================
---- linux-2.6.20.orig/arch/arm/mach-s3c2410/s3c2410.c 2007-02-04 19:44:54.000000000 +0100
-+++ linux-2.6.20/arch/arm/mach-s3c2410/s3c2410.c 2007-02-15 14:55:33.000000000 +0100
-@@ -39,6 +39,7 @@
- /* Initial IO mappings */
-
- static struct map_desc s3c2410_iodesc[] __initdata = {
-+ IODESC_ENT(USBDEV),
- IODESC_ENT(CLKPWR),
- IODESC_ENT(LCD),
- IODESC_ENT(TIMER),
-Index: linux-2.6.20/drivers/usb/gadget/Kconfig
-===================================================================
---- linux-2.6.20.orig/drivers/usb/gadget/Kconfig 2007-02-04 19:44:54.000000000 +0100
-+++ linux-2.6.20/drivers/usb/gadget/Kconfig 2007-02-15 14:54:49.000000000 +0100
-@@ -187,6 +187,25 @@
-
- Select this only if your OMAP board has a Mini-AB connector.
-
-+config USB_GADGET_S3C2410
-+ boolean "S3C2410"
-+ depends on ARCH_S3C2410
-+ help
-+ Samsung's S3C2410 is an ARM-4 processor with an integrated
-+ full speed USB 1.1 device controller.
-+ It has 4 configurable endpoints, as well as endpoint
-+ zero (for control transfers).
-+
-+config USB_S3C2410
-+ tristate
-+ depends on USB_GADGET_S3C2410
-+ default USB_GADGET
-+ select USB_GADGET_SELECTED
-+
-+config USB_S3C2410_DEBUG
-+ boolean "S3C2410 udc debug messages"
-+ depends on USB_GADGET_S3C2410
-+
- config USB_GADGET_AT91
- boolean "AT91 USB Device Port"
- depends on ARCH_AT91
-Index: linux-2.6.20/drivers/usb/gadget/Makefile
-===================================================================
---- linux-2.6.20.orig/drivers/usb/gadget/Makefile 2007-02-04 19:44:54.000000000 +0100
-+++ linux-2.6.20/drivers/usb/gadget/Makefile 2007-02-15 14:54:49.000000000 +0100
-@@ -7,6 +7,7 @@
- obj-$(CONFIG_USB_GOKU) += goku_udc.o
- obj-$(CONFIG_USB_OMAP) += omap_udc.o
- obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o
-+obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o
- obj-$(CONFIG_USB_AT91) += at91_udc.o
-
- #
-Index: linux-2.6.20/drivers/usb/gadget/s3c2410_udc.c
-===================================================================
---- /dev/null 1970-01-01 00:00:00.000000000 +0000
-+++ linux-2.6.20/drivers/usb/gadget/s3c2410_udc.c 2007-02-15 14:54:49.000000000 +0100
-@@ -0,0 +1,1897 @@
-+/*
-+ * linux/drivers/usb/gadget/s3c2410_udc.c
-+ * Samsung on-chip full speed USB device controllers
-+ *
-+ * Copyright (C) 2004-2006 Herbert Pötzl - Arnaud Patard
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation; either version 2 of the License, or
-+ * (at your option) any later version.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ * GNU General Public License for more details.
-+ *
-+ * You should have received a copy of the GNU General Public License
-+ * along with this program; if not, write to the Free Software
-+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-+ *
-+ */
-+#include <linux/module.h>
-+#include <linux/kernel.h>
-+#include <linux/delay.h>
-+#include <linux/ioport.h>
-+#include <linux/sched.h>
-+#include <linux/slab.h>
-+#include <linux/smp_lock.h>
-+#include <linux/errno.h>
-+#include <linux/init.h>
-+#include <linux/timer.h>
-+#include <linux/list.h>
-+#include <linux/interrupt.h>
-+#include <linux/platform_device.h>
-+#include <linux/version.h>
-+#include <linux/clk.h>
-+
-+#include <linux/usb.h>
-+#include <linux/usb_gadget.h>
-+
-+#include <asm/byteorder.h>
-+#include <asm/io.h>
-+#include <asm/irq.h>
-+#include <asm/system.h>
-+#include <asm/unaligned.h>
-+#include <asm/arch/irqs.h>
-+
-+#include <asm/arch/hardware.h>
-+#include <asm/arch/regs-clock.h>
-+#include <asm/arch/regs-gpio.h>
-+#include <asm/arch/regs-udc.h>
-+#include <asm/arch/udc.h>
-+
-+#include <asm/mach-types.h>
-+
-+#include "s3c2410_udc.h"
-+
-+#define ENABLE_SYSFS
-+
-+#define DRIVER_DESC "S3C2410 USB Device Controller Gadget"
-+#define DRIVER_VERSION "30 Apr 2006"
-+#define DRIVER_AUTHOR "Herbert Pötzl <herbert at 13thfloor.at>, Arnaud Patard <arnaud.patard at rtp-net.org>"
-+
-+static const char gadget_name [] = "s3c2410_udc";
-+static const char driver_desc [] = DRIVER_DESC;
-+
-+static struct s3c2410_udc *the_controller;
-+static struct clk *udc_clock;
-+static struct clk *usb_bus_clock;
-+static void __iomem *base_addr;
-+static u64 rsrc_start;
-+static u64 rsrc_len;
-+
-+static inline u32 udc_readl(u32 reg)
-+{
-+ return readl(base_addr+reg);
-+}
-+static inline void udc_writel(u32 value, u32 reg)
-+{
-+ writel(value,base_addr+reg);
-+}
-+
-+static struct s3c2410_udc_mach_info *udc_info;
-+
-+/*************************** DEBUG FUNCTION ***************************/
-+#define DEBUG_NORMAL 1
-+#define DEBUG_VERBOSE 2
-+
-+#ifdef CONFIG_USB_S3C2410_DEBUG
-+#define USB_S3C2410_DEBUG_LEVEL 1
-+
-+static uint32_t s3c2410_ticks=0;
-+
-+static int dprintk(int level, const char *fmt, ...)
-+{
-+ static char printk_buf[1024];
-+ static long prevticks;
-+ static int invocation;
-+ va_list args;
-+ int len;
-+
-+ if (level > USB_S3C2410_DEBUG_LEVEL)
-+ return 0;
-+
-+ if (s3c2410_ticks != prevticks) {
-+ prevticks = s3c2410_ticks;
-+ invocation = 0;
-+ }
-+
-+ len = scnprintf(printk_buf, \
-+ sizeof(printk_buf), "%1lu.%02d USB: ", \
-+ prevticks, invocation++);
-+
-+ va_start(args, fmt);
-+ len = vscnprintf(printk_buf+len, \
-+ sizeof(printk_buf)-len, fmt, args);
-+ va_end(args);
-+
-+ return printk("%s", printk_buf);
-+}
-+#else
-+static int dprintk(int level, const char *fmt, ...) { return 0; }
-+#endif
-+#ifdef ENABLE_SYSFS
-+static ssize_t s3c2410udc_regs_show(struct device *dev, struct device_attribute *attr, char *buf)
-+{
-+ u32 addr_reg,pwr_reg,ep_int_reg,usb_int_reg;
-+ u32 ep_int_en_reg, usb_int_en_reg, ep0_csr;
-+ u32 ep1_i_csr1,ep1_i_csr2,ep1_o_csr1,ep1_o_csr2;
-+ u32 ep2_i_csr1,ep2_i_csr2,ep2_o_csr1,ep2_o_csr2;
-+
-+ addr_reg = udc_readl(S3C2410_UDC_FUNC_ADDR_REG);
-+ pwr_reg = udc_readl(S3C2410_UDC_PWR_REG);
-+ ep_int_reg = udc_readl(S3C2410_UDC_EP_INT_REG);
-+ usb_int_reg = udc_readl(S3C2410_UDC_USB_INT_REG);
-+ ep_int_en_reg = udc_readl(S3C2410_UDC_EP_INT_EN_REG);
-+ usb_int_en_reg = udc_readl(S3C2410_UDC_USB_INT_EN_REG);
-+ udc_writel(0, S3C2410_UDC_INDEX_REG);
-+ ep0_csr = udc_readl(S3C2410_UDC_IN_CSR1_REG);
-+ udc_writel(1, S3C2410_UDC_INDEX_REG);
-+ ep1_i_csr1 = udc_readl(S3C2410_UDC_IN_CSR1_REG);
-+ ep1_i_csr2 = udc_readl(S3C2410_UDC_IN_CSR2_REG);
-+ ep1_o_csr1 = udc_readl(S3C2410_UDC_IN_CSR1_REG);
-+ ep1_o_csr2 = udc_readl(S3C2410_UDC_IN_CSR2_REG);
-+ udc_writel(2, S3C2410_UDC_INDEX_REG);
-+ ep2_i_csr1 = udc_readl(S3C2410_UDC_IN_CSR1_REG);
-+ ep2_i_csr2 = udc_readl(S3C2410_UDC_IN_CSR2_REG);
-+ ep2_o_csr1 = udc_readl(S3C2410_UDC_IN_CSR1_REG);
-+ ep2_o_csr2 = udc_readl(S3C2410_UDC_IN_CSR2_REG);
-+
-+
-+ return snprintf(buf, PAGE_SIZE, \
-+ "FUNC_ADDR_REG : 0x%04X\n" \
-+ "PWR_REG : 0x%04X\n" \
-+ "EP_INT_REG : 0x%04X\n" \
-+ "USB_INT_REG : 0x%04X\n" \
-+ "EP_INT_EN_REG : 0x%04X\n" \
-+ "USB_INT_EN_REG : 0x%04X\n" \
-+ "EP0_CSR : 0x%04X\n" \
-+ "EP1_I_CSR1 : 0x%04X\n" \
-+ "EP1_I_CSR2 : 0x%04X\n" \
-+ "EP1_O_CSR1 : 0x%04X\n" \
-+ "EP1_O_CSR2 : 0x%04X\n" \
-+ "EP2_I_CSR1 : 0x%04X\n" \
-+ "EP2_I_CSR2 : 0x%04X\n" \
-+ "EP2_O_CSR1 : 0x%04X\n" \
-+ "EP2_O_CSR2 : 0x%04X\n", \
-+ addr_reg,pwr_reg,ep_int_reg,usb_int_reg, \
-+ ep_int_en_reg, usb_int_en_reg, ep0_csr, \
-+ ep1_i_csr1,ep1_i_csr2,ep1_o_csr1,ep1_o_csr2, \
-+ ep2_i_csr1,ep2_i_csr2,ep2_o_csr1,ep2_o_csr2 \
-+ );
-+}
-+
-+static DEVICE_ATTR(regs, 0444,
-+ s3c2410udc_regs_show,
-+ NULL);
-+#endif
-+/*------------------------- I/O ----------------------------------*/
-+static void done(struct s3c2410_ep *ep, struct s3c2410_request *req, int status);
-+static void nuke (struct s3c2410_udc *udc, struct s3c2410_ep *ep, int status)
-+{
-+ /* Sanity check */
-+ if (&ep->queue != NULL)
-+ while (!list_empty (&ep->queue)) {
-+ struct s3c2410_request *req;
-+ req = list_entry (ep->queue.next, struct s3c2410_request, queue);
-+ done(ep,req,status);
-+ }
-+}
-+
-+/*
-+ * done
-+ */
-+static void done(struct s3c2410_ep *ep, struct s3c2410_request *req, int status)
-+{
-+ unsigned halted = ep->halted;
-+
-+ list_del_init(&req->queue);
-+
-+ if (likely (req->req.status == -EINPROGRESS))
-+ req->req.status = status;
-+ else
-+ status = req->req.status;
-+
-+ ep->halted = 1;
-+ req->req.complete(&ep->ep, &req->req);
-+ ep->halted = halted;
-+}
-+
-+static inline void clear_ep_state (struct s3c2410_udc *dev)
-+{
-+ unsigned i;
-+
-+ /* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint
-+ * fifos, and pending transactions mustn't be continued in any case.
-+ */
-+ for (i = 1; i < S3C2410_ENDPOINTS; i++)
-+ nuke(dev, &dev->ep[i], -ECONNABORTED);
-+}
-+
-+static inline int fifo_count_out(void)
-+{
-+ int tmp;
-+
-+ tmp = udc_readl(S3C2410_UDC_OUT_FIFO_CNT2_REG) << 8;
-+ tmp |= udc_readl(S3C2410_UDC_OUT_FIFO_CNT1_REG);
-+
-+ return tmp & 0xffff;
-+}
-+
-+/*
-+ * write_packet
-+ */
-+static inline int
-+write_packet(int fifo, struct s3c2410_request *req, unsigned max)
-+{
-+ unsigned len;
-+ u8 *buf;
-+
-+ buf = req->req.buf + req->req.actual;
-+ prefetch(buf);
-+
-+ len = min(req->req.length - req->req.actual, max);
-+ dprintk(DEBUG_VERBOSE, "write_packet %d %d %d ",req->req.actual,req->req.length,len);
-+ req->req.actual += len;
-+ dprintk(DEBUG_VERBOSE, "%d\n",req->req.actual);
-+
-+ writesb(base_addr+fifo, buf, len);
-+ return len;
-+}
-+
-+static void udc_reinit(struct s3c2410_udc *dev);
-+
-+/*
-+ * write_fifo
-+ *
-+ * return: 0 = still running, 1 = completed, negative = errno
-+ */
-+static int write_fifo(struct s3c2410_ep *ep, struct s3c2410_request *req)
-+{
-+ unsigned count;
-+ int is_last;
-+ u32 idx;
-+ int fifo_reg;
-+ u32 ep_csr;
-+
-+
-+ switch(ep->bEndpointAddress&0x7F)
-+ {
-+ default:
-+ case 0: idx = 0;
-+ fifo_reg = S3C2410_UDC_EP0_FIFO_REG;
-+ break;
-+ case 1:
-+ idx = 1;
-+ fifo_reg = S3C2410_UDC_EP1_FIFO_REG;
-+ break;
-+ case 2:
-+ idx = 2;
-+ fifo_reg = S3C2410_UDC_EP2_FIFO_REG;
-+ break;
-+
-+ case 3:
-+ idx = 3;
-+ fifo_reg = S3C2410_UDC_EP3_FIFO_REG;
-+ break;
-+
-+ case 4:
-+ idx = 4;
-+ fifo_reg = S3C2410_UDC_EP4_FIFO_REG;
-+ break;
-+ }
-+
-+ count = write_packet(fifo_reg, req, ep->ep.maxpacket);
-+
-+ /* last packet is often short (sometimes a zlp) */
-+ if (count != ep->ep.maxpacket)
-+ is_last = 1;
-+ else if (req->req.length != req->req.actual || req->req.zero)
-+ is_last = 0;
-+ else
-+ is_last = 2;
-+
-+ /* Only ep0 debug messages are interesting */
-+ if (!idx)
-+ dprintk(DEBUG_NORMAL, "Written ep%d %d.%d of %d b [last %d,z %d]\n",idx,count,req->req.actual,req->req.length,is_last,req->req.zero);
-+
-+ if (is_last)
-+ {
-+ /* The order is important. It prevents to send 2 packet at the same time
-+ **/
-+ if (!idx)
-+ {
-+ /* If we got a reset signal, no need to say 'data sent' */
-+ if (! (udc_readl(S3C2410_UDC_USB_INT_REG) & S3C2410_UDC_USBINT_RESET))
-+ set_ep0_de_in(base_addr);
-+ ep->dev->ep0state=EP0_IDLE;
-+ }
-+ else
-+ {
-+ udc_writel(idx, S3C2410_UDC_INDEX_REG);
-+ ep_csr=udc_readl(S3C2410_UDC_IN_CSR1_REG);
-+ udc_writel(idx, S3C2410_UDC_INDEX_REG);
-+ udc_writel(ep_csr|S3C2410_UDC_ICSR1_PKTRDY,S3C2410_UDC_IN_CSR1_REG);
-+ }
-+ done(ep, req, 0);
-+ is_last=1;
-+ }
-+ else
-+ {
-+ if (!idx)
-+ {
-+ /* If we got a reset signal, no need to say 'data sent' */
-+ if (! (udc_readl(S3C2410_UDC_USB_INT_REG) & S3C2410_UDC_USBINT_RESET))
-+ set_ep0_ipr(base_addr);
-+ }
-+ else
-+ {
-+ udc_writel(idx, S3C2410_UDC_INDEX_REG);
-+ ep_csr=udc_readl(S3C2410_UDC_IN_CSR1_REG);
-+ udc_writel(idx, S3C2410_UDC_INDEX_REG);
-+ udc_writel(ep_csr|S3C2410_UDC_ICSR1_PKTRDY,S3C2410_UDC_IN_CSR1_REG);
-+ }
-+ }
-+
-+
-+ return is_last;
-+}
-+
-+static inline int
-+read_packet(int fifo, u8 *buf, struct s3c2410_request *req, unsigned avail)
-+{
-+ unsigned len;
-+
-+ len = min(req->req.length - req->req.actual, avail);
-+ req->req.actual += len;
-+
-+ readsb(fifo + base_addr, buf, len);
-+ return len;
-+}
-+
-+/*
-+ * return: 0 = still running, 1 = queue empty, negative = errno
-+ */
-+static int read_fifo(struct s3c2410_ep *ep, struct s3c2410_request *req)
-+{
-+ u8 *buf;
-+ u32 ep_csr;
-+ unsigned bufferspace;
-+ int is_last=1;
-+ unsigned avail;
-+ int fifo_count = 0;
-+ u32 idx;
-+ int fifo_reg;
-+
-+
-+ switch(ep->bEndpointAddress&0x7F)
-+ {
-+ default:
-+ case 0: idx = 0;
-+ fifo_reg = S3C2410_UDC_EP0_FIFO_REG;
-+ break;
-+ case 1:
-+ idx = 1;
-+ fifo_reg = S3C2410_UDC_EP1_FIFO_REG;
-+ break;
-+ case 2:
-+ idx = 2;
-+ fifo_reg = S3C2410_UDC_EP2_FIFO_REG;
-+ break;
-+
-+ case 3:
-+ idx = 3;
-+ fifo_reg = S3C2410_UDC_EP3_FIFO_REG;
-+ break;
-+
-+ case 4:
-+ idx = 4;
-+ fifo_reg = S3C2410_UDC_EP4_FIFO_REG;
-+ break;
-+
-+ }
-+
-+ if (!req->req.length) {
-+ return 1;
-+ }
-+
-+ buf = req->req.buf + req->req.actual;
-+ bufferspace = req->req.length - req->req.actual;
-+ if (!bufferspace)
-+ {
-+ dprintk(DEBUG_NORMAL, "read_fifo: Buffer full !!\n");
-+ return -1;
-+ }
-+
-+ udc_writel(idx, S3C2410_UDC_INDEX_REG);
-+
-+ fifo_count = fifo_count_out();
-+ dprintk(DEBUG_NORMAL, "fifo_read fifo count : %d\n",fifo_count);
-+
-+ if (fifo_count > ep->ep.maxpacket)
-+ avail = ep->ep.maxpacket;
-+ else
-+ avail = fifo_count;
-+
-+ fifo_count=read_packet(fifo_reg,buf,req,avail);
-+
-+ /* checking this with ep0 is not accurate as we already
-+ * read a control request
-+ **/
-+ if (idx && fifo_count < ep->ep.maxpacket) {
-+ is_last = 1;
-+ /* overflowed this request? flush extra data */
-+ if (fifo_count != avail) {
-+ req->req.status = -EOVERFLOW;
-+ }
-+ } else {
-+ if (req->req.length <= req->req.actual)
-+ is_last = 1;
-+ else
-+ is_last = 0;
-+ }
-+
-+ udc_writel(idx, S3C2410_UDC_INDEX_REG);
-+ fifo_count = fifo_count_out();
-+
-+ /* Only ep0 debug messages are interesting */
-+ if (!idx)
-+ dprintk(DEBUG_VERBOSE, "fifo_read fifo count : %d [last %d]\n",fifo_count,is_last);
-+
-+
-+ if (is_last) {
-+ if (!idx)
-+ {
-+ set_ep0_de_out(base_addr);
-+ ep->dev->ep0state=EP0_IDLE;
-+ }
-+ else
-+ {
-+ udc_writel(idx, S3C2410_UDC_INDEX_REG);
-+ ep_csr=udc_readl(S3C2410_UDC_OUT_CSR1_REG);
-+ udc_writel(idx, S3C2410_UDC_INDEX_REG);
-+ udc_writel(ep_csr&~S3C2410_UDC_OCSR1_PKTRDY,S3C2410_UDC_OUT_CSR1_REG);
-+ }
-+ done(ep, req, 0);
-+ if (!list_empty(&ep->queue))
-+ {
-+ is_last=0;
-+ req = container_of(ep->queue.next,
-+ struct s3c2410_request, queue);
-+ }
-+ else
-+ is_last=1;
-+
-+ }
-+ else
-+ {
-+ if (!idx)
-+ {
-+ clear_ep0_opr(base_addr);
-+ }
-+ else
-+ {
-+ udc_writel(idx, S3C2410_UDC_INDEX_REG);
-+ ep_csr=udc_readl(S3C2410_UDC_OUT_CSR1_REG);
-+ udc_writel(idx, S3C2410_UDC_INDEX_REG);
-+ udc_writel(ep_csr&~S3C2410_UDC_OCSR1_PKTRDY,S3C2410_UDC_OUT_CSR1_REG);
-+ }
-+ }
-+
-+
-+ return is_last;
-+}
-+
-+
-+static int
-+read_fifo_crq(struct usb_ctrlrequest *crq)
-+{
-+ int bytes_read = 0;
-+ int fifo_count = 0;
-+ int i;
-+
-+
-+ unsigned char *pOut = (unsigned char*)crq;
-+
-+ udc_writel(0, S3C2410_UDC_INDEX_REG);
-+
-+ fifo_count = fifo_count_out();
-+
-+ dprintk(DEBUG_NORMAL, "read_fifo_crq(): fifo_count=%d\n", fifo_count );
-+
-+ fifo_count = sizeof(struct usb_ctrlrequest);
-+ while( fifo_count-- ) {
-+ i = 0;
-+
-+ do {
-+ *pOut = (unsigned char)udc_readl(S3C2410_UDC_EP0_FIFO_REG);
-+ i++;
-+ } while((fifo_count_out() != fifo_count) && (i < 10));
-+
-+ if ( i == 10 ) {
-+ dprintk(DEBUG_NORMAL, "read_fifo(): read failure\n");
-+ }
-+
-+ pOut++;
-+ bytes_read++;
-+ }
-+
-+ dprintk(DEBUG_VERBOSE, "read_fifo_crq: len=%d %02x:%02x {%x,%x,%x}\n",
-+ bytes_read, crq->bRequest, crq->bRequestType,
-+ crq->wValue, crq->wIndex, crq->wLength);
-+
-+ return bytes_read;
-+}
-+static int s3c2410_get_status(struct s3c2410_udc *dev, struct usb_ctrlrequest *crq)
-+{
-+ u16 status = 0;
-+ u8 ep_num = crq->wIndex & 0x7F;
-+ u8 is_in = crq->wIndex & USB_DIR_IN;
-+
-+ switch(crq->bRequestType & USB_RECIP_MASK) {
-+ case USB_RECIP_INTERFACE:
-+ break;
-+ case USB_RECIP_DEVICE:
-+ status = dev->devstatus;
-+ break;
-+ case USB_RECIP_ENDPOINT:
-+ if (ep_num>4 || crq->wLength > 2)
-+ return 1;
-+ if (!ep_num) {
-+ udc_writel(0, S3C2410_UDC_INDEX_REG);
-+ status = udc_readl(S3C2410_UDC_IN_CSR1_REG);
-+ status = ( (status & S3C2410_UDC_EP0_CSR_SENDSTL) == S3C2410_UDC_EP0_CSR_SENDSTL);
-+ }
-+ else {
-+ udc_writel(ep_num, S3C2410_UDC_INDEX_REG);
-+ if (is_in) {
-+ status = udc_readl(S3C2410_UDC_IN_CSR1_REG);
-+ status = ( (status & S3C2410_UDC_ICSR1_SENTSTL) == S3C2410_UDC_ICSR1_SENTSTL);
-+ }
-+ else {
-+ status = udc_readl(S3C2410_UDC_OUT_CSR1_REG);
-+ status = ( (status & S3C2410_UDC_OCSR1_SENTSTL) == S3C2410_UDC_OCSR1_SENTSTL);
-+ }
-+ }
-+
-+ break;
-+ default:
-+ return 1;
-+ }
-+
-+ /* Seems to be needed to get it working. ouch :( */
-+ udelay(0x20);
-+ udc_writel(status&0xFF,S3C2410_UDC_EP0_FIFO_REG);
-+ udc_writel(status>>8,S3C2410_UDC_EP0_FIFO_REG);
-+ set_ep0_de_in(base_addr);
-+
-+ return 0;
-+}
-+/*------------------------- usb state machine -------------------------------*/
-+static void handle_ep0(struct s3c2410_udc *dev)
-+{
-+ u32 ep0csr;
-+ struct s3c2410_ep *ep = &dev->ep [0];
-+ struct s3c2410_request *req;
-+ struct usb_ctrlrequest crq;
-+
-+ if (list_empty(&ep->queue))
-+ req = NULL;
-+ else
-+ req = list_entry(ep->queue.next, struct s3c2410_request, queue);
-+
-+
-+ udc_writel(0, S3C2410_UDC_INDEX_REG);
-+ ep0csr = udc_readl(S3C2410_UDC_IN_CSR1_REG);
-+ dprintk(DEBUG_NORMAL,"ep0csr %x ep0state %s\n",ep0csr,ep0states[dev->ep0state]);
-+
-+ /* clear stall status */
-+ if (ep0csr & S3C2410_UDC_EP0_CSR_SENTSTL) {
-+ /* FIXME */
-+ nuke(dev, ep, -EPIPE);
-+ dprintk(DEBUG_NORMAL, "... clear SENT_STALL ...\n");
-+ clear_ep0_sst(base_addr);
-+ dev->ep0state = EP0_IDLE;
-+ return;
-+ }
-+
-+ /* clear setup end */
-+ if (ep0csr & S3C2410_UDC_EP0_CSR_SE
-+ /* && dev->ep0state != EP0_IDLE */) {
-+ dprintk(DEBUG_NORMAL, "... serviced SETUP_END ...\n");
-+ nuke(dev, ep, 0);
-+ clear_ep0_se(base_addr);
-+ dev->ep0state = EP0_IDLE;
-+ }
-+
-+
-+ switch (dev->ep0state) {
-+ case EP0_IDLE:
-+ /* start control request? */
-+ if (ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY) {
-+ int len, ret, tmp;
-+
-+ nuke (dev, ep, -EPROTO);
-+
-+ len = read_fifo_crq(&crq);
-+ if (len != sizeof(crq)) {
-+ dprintk(DEBUG_NORMAL, "setup begin: fifo READ ERROR"
-+ " wanted %d bytes got %d. Stalling out...\n",
-+ sizeof(crq), len);
-+ set_ep0_ss(base_addr);
-+ return;
-+ }
-+
-+ dprintk(DEBUG_NORMAL, "bRequest = %d bRequestType %d wLength = %d\n", crq.bRequest,crq.bRequestType, crq.wLength);
-+
-+
-+ /* cope with automagic for some standard requests. */
-+ dev->req_std = (crq.bRequestType & USB_TYPE_MASK)
-+ == USB_TYPE_STANDARD;
-+ dev->req_config = 0;
-+ dev->req_pending = 1;
-+ switch (crq.bRequest) {
-+ case USB_REQ_SET_CONFIGURATION:
-+ dprintk(DEBUG_NORMAL, "USB_REQ_SET_CONFIGURATION ... \n");
-+ if (crq.bRequestType == USB_RECIP_DEVICE) {
-+config_change:
-+ dev->req_config = 1;
-+/* clear_ep_state(dev);*/
-+ set_ep0_de_out(base_addr);
-+ }
-+ break;
-+ case USB_REQ_SET_INTERFACE:
-+ dprintk(DEBUG_NORMAL, "USB_REQ_SET_INTERFACE ... \n");
-+ if (crq.bRequestType == USB_RECIP_INTERFACE) {
-+ goto config_change;
-+ }
-+ break;
-+
-+ case USB_REQ_SET_ADDRESS:
-+ dprintk(DEBUG_NORMAL, "USB_REQ_SET_ADDRESS ... \n");
-+ if (crq.bRequestType == USB_RECIP_DEVICE) {
-+ tmp = crq.wValue & 0x7F;
-+ dev->address = tmp;
-+ udc_writel((tmp | 0x80), S3C2410_UDC_FUNC_ADDR_REG);
-+ set_ep0_de_out(base_addr);
-+ return;
-+ }
-+ break;
-+
-+ case USB_REQ_GET_STATUS:
-+ dprintk(DEBUG_NORMAL, "USB_REQ_GET_STATUS ... \n");
-+ clear_ep0_opr(base_addr);
-+ if (!s3c2410_get_status(dev, &crq)) {
-+ return;
-+ }
-+ break;
-+
-+ default:
-+ clear_ep0_opr(base_addr);
-+ break;
-+ }
-+
-+ if (crq.bRequestType & USB_DIR_IN)
-+ dev->ep0state = EP0_IN_DATA_PHASE;
-+ else
-+ dev->ep0state = EP0_OUT_DATA_PHASE;
-+ ret = dev->driver->setup(&dev->gadget, &crq);
-+ if (ret < 0) {
-+ if (dev->req_config) {
-+ dprintk(DEBUG_NORMAL, "config change %02x fail %d?\n",
-+ crq.bRequest, ret);
-+ return;
-+ }
-+ if (ret == -EOPNOTSUPP)
-+ dprintk(DEBUG_NORMAL, "Operation not supported\n");
-+ else
-+ dprintk(DEBUG_NORMAL, "dev->driver->setup failed. (%d)\n",ret);
-+
-+ set_ep0_ss(base_addr);
-+ set_ep0_de_out(base_addr);
-+ dev->ep0state = EP0_IDLE;
-+ /* deferred i/o == no response yet */
-+ } else if (dev->req_pending) {
-+ dprintk(DEBUG_VERBOSE, "dev->req_pending... what now?\n");
-+ dev->req_pending=0;
-+ }
-+ dprintk(DEBUG_VERBOSE, "ep0state %s\n",ep0states[dev->ep0state]);
-+ }
-+ break;
-+ case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */
-+ dprintk(DEBUG_NORMAL, "EP0_IN_DATA_PHASE ... what now?\n");
-+ if (!(ep0csr & 2) && req)
-+ {
-+ write_fifo(ep, req);
-+ }
-+ break;
-+ case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */
-+ dprintk(DEBUG_NORMAL, "EP0_OUT_DATA_PHASE ... what now?\n");
-+ if ((ep0csr & 1) && req ) {
-+ read_fifo(ep,req);
-+ }
-+ break;
-+ case EP0_END_XFER:
-+ dprintk(DEBUG_NORMAL, "EP0_END_XFER ... what now?\n");
-+ dev->ep0state=EP0_IDLE;
-+ break;
-+ case EP0_STALL:
-+ dprintk(DEBUG_NORMAL, "EP0_STALL ... what now?\n");
-+ dev->ep0state=EP0_IDLE;
-+ break;
-+ }
-+}
-+/*
-+ * handle_ep - Manage I/O endpoints
-+ */
-+static void handle_ep(struct s3c2410_ep *ep)
-+{
-+ struct s3c2410_request *req;
-+ int is_in = ep->bEndpointAddress & USB_DIR_IN;
-+ u32 ep_csr1;
-+ u32 idx;
-+
-+ if (likely (!list_empty(&ep->queue)))
-+ req = list_entry(ep->queue.next,
-+ struct s3c2410_request, queue);
-+ else
-+ req = NULL;
-+
-+ idx = (u32)(ep->bEndpointAddress&0x7F);
-+
-+ if (is_in) {
-+ udc_writel(idx, S3C2410_UDC_INDEX_REG);
-+ ep_csr1 = udc_readl(S3C2410_UDC_IN_CSR1_REG);
-+ dprintk(DEBUG_VERBOSE, "ep%01d write csr:%02x %d\n",idx,ep_csr1,req ? 1 : 0);
-+
-+ if (ep_csr1 & S3C2410_UDC_ICSR1_SENTSTL)
-+ {
-+ dprintk(DEBUG_VERBOSE, "st\n");
-+ udc_writel(idx, S3C2410_UDC_INDEX_REG);
-+ udc_writel(0x00,S3C2410_UDC_IN_CSR1_REG);
-+ return;
-+ }
-+
-+ if (!(ep_csr1 & S3C2410_UDC_ICSR1_PKTRDY) && req)
-+ {
-+ write_fifo(ep,req);
-+ }
-+ }
-+ else {
-+ udc_writel(idx, S3C2410_UDC_INDEX_REG);
-+ ep_csr1 = udc_readl(S3C2410_UDC_OUT_CSR1_REG);
-+ dprintk(DEBUG_VERBOSE, "ep%01d read csr:%02x\n",idx,ep_csr1);
-+
-+ if (ep_csr1 & S3C2410_UDC_OCSR1_SENTSTL)
-+ {
-+ udc_writel(idx, S3C2410_UDC_INDEX_REG);
-+ udc_writel(0x00,S3C2410_UDC_OUT_CSR1_REG);
-+ return;
-+ }
-+ if( (ep_csr1 & S3C2410_UDC_OCSR1_PKTRDY) && req)
-+ {
-+ read_fifo(ep,req);
-+ }
-+ }
-+}
-+
-+#include <asm/arch/regs-irq.h>
-+/*
-+ * s3c2410_udc_irq - interrupt handler
-+ */
-+static irqreturn_t
-+s3c2410_udc_irq(int irq, void *_dev)
-+{
-+ struct s3c2410_udc *dev = _dev;
-+ int usb_status;
-+ int usbd_status;
-+ int pwr_reg;
-+ int ep0csr;
-+ int i;
-+ u32 idx;
-+ unsigned long flags;
-+
-+ spin_lock_irqsave(&dev->lock,flags);
-+
-+ /* Driver connected ? */
-+ if (!dev->driver) {
-+ /* Clear interrupts */
-+ udc_writel( \
-+ udc_readl(S3C2410_UDC_USB_INT_REG), \
-+ S3C2410_UDC_USB_INT_REG \
-+ );
-+ udc_writel( \
-+ udc_readl(S3C2410_UDC_EP_INT_REG), \
-+ S3C2410_UDC_EP_INT_REG \
-+ );
-+ }
-+
-+ /* Save index */
-+ idx = udc_readl(S3C2410_UDC_INDEX_REG);
-+
-+ /* Read status registers */
-+ usb_status = udc_readl(S3C2410_UDC_USB_INT_REG);
-+ usbd_status = udc_readl(S3C2410_UDC_EP_INT_REG);
-+ pwr_reg = udc_readl(S3C2410_UDC_PWR_REG);
-+
-+ S3C2410_UDC_SETIX(base_addr,EP0);
-+ ep0csr = udc_readl(S3C2410_UDC_IN_CSR1_REG);
-+
-+ // dprintk(DEBUG_NORMAL, "usbs=%02x, usbds=%02x, pwr=%02x ep0csr=%02x\n", usb_status, usbd_status, pwr_reg,ep0csr);
-+
-+ /*
-+ * Now, handle interrupts. There's two types :
-+ * - Reset, Resume, Suspend coming -> usb_int_reg
-+ * - EP -> ep_int_reg
-+ */
-+
-+ /* RESET */
-+ if (usb_status & S3C2410_UDC_USBINT_RESET )
-+ {
-+ /* two kind of reset :
-+ * - reset start -> pwr reg = 8
-+ * - reset end -> pwr reg = 0
-+ **/
-+ dprintk(DEBUG_NORMAL, "USB reset csr %x pwr %x\n",ep0csr,pwr_reg);
-+ dev->gadget.speed = USB_SPEED_UNKNOWN;
-+ udc_writel(0x00, S3C2410_UDC_INDEX_REG);
-+ udc_writel((dev->ep[0].ep.maxpacket&0x7ff)>>3,S3C2410_UDC_MAXP_REG);
-+ dev->address = 0;
-+
-+ dev->ep0state = EP0_IDLE;
-+ dev->gadget.speed = USB_SPEED_FULL;
-+
-+ /* clear interrupt */
-+ udc_writel(S3C2410_UDC_USBINT_RESET,
-+ S3C2410_UDC_USB_INT_REG);
-+
-+ udc_writel(idx,S3C2410_UDC_INDEX_REG);
-+ spin_unlock_irqrestore(&dev->lock,flags);
-+ return IRQ_HANDLED;
-+ }
-+
-+ /* RESUME */
-+ if (usb_status & S3C2410_UDC_USBINT_RESUME)
-+ {
-+ dprintk(DEBUG_NORMAL, "USB resume\n");
-+
-+ /* clear interrupt */
-+ udc_writel(S3C2410_UDC_USBINT_RESUME,
-+ S3C2410_UDC_USB_INT_REG);
-+ if (dev->gadget.speed != USB_SPEED_UNKNOWN
-+ && dev->driver
-+ && dev->driver->resume)
-+ dev->driver->resume(&dev->gadget);
-+ }
-+
-+ /* SUSPEND */
-+ if (usb_status & S3C2410_UDC_USBINT_SUSPEND)
-+ {
-+ dprintk(DEBUG_NORMAL, "USB suspend\n");
-+
-+ /* clear interrupt */
-+ udc_writel(S3C2410_UDC_USBINT_SUSPEND,
-+ S3C2410_UDC_USB_INT_REG);
-+
-+ if (dev->gadget.speed != USB_SPEED_UNKNOWN
-+ && dev->driver
-+ && dev->driver->suspend)
-+ dev->driver->suspend(&dev->gadget);
-+
-+ dev->ep0state = EP0_IDLE;
-+ }
-+
-+ /* EP */
-+ /* control traffic */
-+ /* check on ep0csr != 0 is not a good idea as clearing in_pkt_ready
-+ * generate an interrupt
-+ */
-+ if (usbd_status & S3C2410_UDC_INT_EP0)
-+ {
-+ dprintk(DEBUG_VERBOSE, "USB ep0 irq\n");
-+ /* Clear the interrupt bit by setting it to 1 */
-+ udc_writel(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_REG);
-+ handle_ep0(dev);
-+ }
-+ /* endpoint data transfers */
-+ for (i = 1; i < S3C2410_ENDPOINTS; i++) {
-+ u32 tmp = 1 << i;
-+ if (usbd_status & tmp) {
-+ dprintk(DEBUG_VERBOSE, "USB ep%d irq\n", i);
-+
-+ /* Clear the interrupt bit by setting it to 1 */
-+ udc_writel(tmp, S3C2410_UDC_EP_INT_REG);
-+ handle_ep(&dev->ep[i]);
-+ }
-+ }
-+
-+
-+ dprintk(DEBUG_VERBOSE,"irq: %d done.\n", irq);
-+
-+ /* Restore old index */
-+ udc_writel(idx,S3C2410_UDC_INDEX_REG);
-+
-+ spin_unlock_irqrestore(&dev->lock,flags);
-+
-+ return IRQ_HANDLED;
-+}
-+/*------------------------- s3c2410_ep_ops ----------------------------------*/
-+
-+/*
-+ * s3c2410_ep_enable
-+ */
-+static int
-+s3c2410_ep_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
-+{
-+ struct s3c2410_udc *dev;
-+ struct s3c2410_ep *ep;
-+ u32 max, tmp;
-+ unsigned long flags;
-+ u32 csr1,csr2;
-+ u32 int_en_reg;
-+
-+
-+ ep = container_of (_ep, struct s3c2410_ep, ep);
-+ if (!_ep || !desc || ep->desc || _ep->name == ep0name
-+ || desc->bDescriptorType != USB_DT_ENDPOINT)
-+ return -EINVAL;
-+ dev = ep->dev;
-+ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
-+ return -ESHUTDOWN;
-+
-+ max = le16_to_cpu (desc->wMaxPacketSize) & 0x1fff;
-+
-+ local_irq_save (flags);
-+ _ep->maxpacket = max & 0x7ff;
-+ ep->desc = desc;
-+ ep->halted = 0;
-+ ep->bEndpointAddress = desc->bEndpointAddress;
-+
-+ /* set max packet */
-+ udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
-+ udc_writel(max>>3,S3C2410_UDC_MAXP_REG);
-+
-+
-+ /* set type, direction, address; reset fifo counters */
-+ if (desc->bEndpointAddress & USB_DIR_IN)
-+ {
-+ csr1 = S3C2410_UDC_ICSR1_FFLUSH|S3C2410_UDC_ICSR1_CLRDT;
-+ csr2 = S3C2410_UDC_ICSR2_MODEIN|S3C2410_UDC_ICSR2_DMAIEN;
-+
-+ udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
-+ udc_writel(csr1,S3C2410_UDC_IN_CSR1_REG);
-+ udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
-+ udc_writel(csr2,S3C2410_UDC_IN_CSR2_REG);
-+ }
-+ else
-+ {
-+ /* don't flush he in fifo or there will be an interrupt for that
-+ * endpoint */
-+ csr1 = S3C2410_UDC_ICSR1_CLRDT;
-+ csr2 = S3C2410_UDC_ICSR2_DMAIEN;
-+
-+ udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
-+ udc_writel(csr1,S3C2410_UDC_IN_CSR1_REG);
-+ udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
-+ udc_writel(csr2,S3C2410_UDC_IN_CSR2_REG);
-+
-+ csr1 = S3C2410_UDC_OCSR1_FFLUSH | S3C2410_UDC_OCSR1_CLRDT;
-+ csr2 = S3C2410_UDC_OCSR2_DMAIEN;
-+
-+ udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
-+ udc_writel(csr1,S3C2410_UDC_OUT_CSR1_REG);
-+ udc_writel(ep->num, S3C2410_UDC_INDEX_REG);
-+ udc_writel(csr2,S3C2410_UDC_OUT_CSR2_REG);
-+ }
-+
-+
-+ /* enable irqs */
-+ int_en_reg = udc_readl(S3C2410_UDC_EP_INT_EN_REG);
-+ udc_writel(int_en_reg | (1<<ep->num),S3C2410_UDC_EP_INT_EN_REG);
-+
-+
-+ /* print some debug message */
-+ tmp = desc->bEndpointAddress;
-+ dprintk (DEBUG_NORMAL, "enable %s(%d) ep%x%s-blk max %02x\n",
-+ _ep->name,ep->num, tmp, desc->bEndpointAddress & USB_DIR_IN ? "in" : "out", max);
-+
-+ local_irq_restore (flags);
-+
-+ return 0;
-+}
-+
-+/*
-+ * s3c2410_ep_disable
-+ */
-+static int s3c2410_ep_disable (struct usb_ep *_ep)
-+{
-+ struct s3c2410_ep *ep = container_of(_ep, struct s3c2410_ep, ep);
-+ unsigned long flags;
-+ u32 int_en_reg;
-+
-+
-+ if (!_ep || !ep->desc) {
-+ dprintk(DEBUG_NORMAL, "%s not enabled\n",
-+ _ep ? ep->ep.name : NULL);
-+ return -EINVAL;
-+ }
-+
-+ local_irq_save(flags);
-+
-+ printk(KERN_ERR "ep_disable: %s\n",_ep->name);
-+
-+ ep->desc = NULL;
-+ ep->halted = 1;
-+
-+ nuke (ep->dev, ep, -ESHUTDOWN);
-+
-+ /* disable irqs */
-+ int_en_reg = udc_readl(S3C2410_UDC_EP_INT_EN_REG);
-+ udc_writel(int_en_reg & ~(1<<ep->num),S3C2410_UDC_EP_INT_EN_REG);
-+
-+ local_irq_restore(flags);
-+
-+ dprintk(DEBUG_NORMAL, "%s disabled\n", _ep->name);
-+
-+ return 0;
-+}
-+
-+/*
-+ * s3c2410_alloc_request
-+ */
-+static struct usb_request *
-+s3c2410_alloc_request (struct usb_ep *_ep, gfp_t mem_flags)
-+{
-+ struct s3c2410_ep *ep;
-+ struct s3c2410_request *req;
-+
-+ dprintk(DEBUG_VERBOSE,"s3c2410_alloc_request(ep=%p,flags=%d)\n", _ep, mem_flags);
-+
-+ ep = container_of (_ep, struct s3c2410_ep, ep);
-+ if (!_ep)
-+ return NULL;
-+
-+ req = kzalloc (sizeof *req, mem_flags);
-+ if (!req)
-+ return NULL;
-+ INIT_LIST_HEAD (&req->queue);
-+ return &req->req;
-+}
-+
-+/*
-+ * s3c2410_free_request
-+ */
-+static void
-+s3c2410_free_request (struct usb_ep *_ep, struct usb_request *_req)
-+{
-+ struct s3c2410_ep *ep;
-+ struct s3c2410_request *req;
-+
-+ dprintk(DEBUG_VERBOSE, "s3c2410_free_request(ep=%p,req=%p)\n", _ep, _req);
-+
-+ ep = container_of (_ep, struct s3c2410_ep, ep);
-+ if (!ep || !_req || (!ep->desc && _ep->name != ep0name))
-+ return;
-+
-+ req = container_of (_req, struct s3c2410_request, req);
-+ WARN_ON (!list_empty (&req->queue));
-+ kfree (req);
-+}
-+
-+/*
-+ * s3c2410_alloc_buffer
-+ */
-+static void *
-+s3c2410_alloc_buffer (
-+ struct usb_ep *_ep,
-+ unsigned bytes,
-+ dma_addr_t *dma,
-+ gfp_t mem_flags)
-+{
-+ char *retval;
-+
-+ dprintk(DEBUG_VERBOSE,"s3c2410_alloc_buffer()\n");
-+
-+ if (!the_controller->driver)
-+ return NULL;
-+ retval = kmalloc (bytes, mem_flags);
-+ *dma = (dma_addr_t) retval;
-+ return retval;
-+}
-+
-+/*
-+ * s3c2410_free_buffer
-+ */
-+static void
-+s3c2410_free_buffer (
-+ struct usb_ep *_ep,
-+ void *buf,
-+ dma_addr_t dma,
-+ unsigned bytes)
-+{
-+ dprintk(DEBUG_VERBOSE, "s3c2410_free_buffer()\n");
-+
-+ if (bytes)
-+ kfree (buf);
-+}
-+
-+/*
-+ * s3c2410_queue
-+ */
-+static int
-+s3c2410_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
-+{
-+ struct s3c2410_request *req;
-+ struct s3c2410_ep *ep;
-+ struct s3c2410_udc *dev;
-+ u32 ep_csr=0;
-+ int fifo_count=0;
-+ unsigned long flags;
-+
-+
-+ ep = container_of(_ep, struct s3c2410_ep, ep);
-+ if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) {
-+ dprintk(DEBUG_NORMAL, "s3c2410_queue: inval 2\n");
-+ return -EINVAL;
-+ }
-+
-+ dev = ep->dev;
-+ if (unlikely (!dev->driver
-+ || dev->gadget.speed == USB_SPEED_UNKNOWN)) {
-+ return -ESHUTDOWN;
-+ }
-+
-+ local_irq_save (flags);
-+
-+ req = container_of(_req, struct s3c2410_request, req);
-+ if (unlikely (!_req || !_req->complete || !_req->buf
-+ || !list_empty(&req->queue))) {
-+ if (!_req)
-+ dprintk(DEBUG_NORMAL, "s3c2410_queue: 1 X X X\n");
-+ else
-+ {
-+ dprintk(DEBUG_NORMAL, "s3c2410_queue: 0 %01d %01d %01d\n",!_req->complete,!_req->buf, !list_empty(&req->queue));
-+ }
-+ local_irq_restore(flags);
-+ return -EINVAL;
-+ }
-+
-+ _req->status = -EINPROGRESS;
-+ _req->actual = 0;
-+
-+ dprintk(DEBUG_VERBOSE,"s3c2410_queue: ep%x len %d\n",ep->bEndpointAddress,_req->length);
-+
-+ if (ep->bEndpointAddress) {
-+ udc_writel(ep->bEndpointAddress&0x7F,S3C2410_UDC_INDEX_REG);
-+ ep_csr = udc_readl(ep->bEndpointAddress&USB_DIR_IN ? S3C2410_UDC_IN_CSR1_REG : S3C2410_UDC_OUT_CSR1_REG);
-+ fifo_count=fifo_count_out();
-+ }
-+ else {
-+ udc_writel(0,S3C2410_UDC_INDEX_REG);
-+ ep_csr = udc_readl(S3C2410_UDC_IN_CSR1_REG);
-+ }
-+ /* kickstart this i/o queue? */
-+ if (list_empty(&ep->queue) && !ep->halted) {
-+ if (ep->bEndpointAddress == 0 /* ep0 */) {
-+ switch (dev->ep0state) {
-+ case EP0_IN_DATA_PHASE:
-+ if (write_fifo(ep, req)) {
-+ dev->ep0state = EP0_IDLE;
-+ req = NULL;
-+ }
-+ break;
-+
-+ case EP0_OUT_DATA_PHASE:
-+ if ( (!_req->length) || ((ep_csr & 1) && read_fifo(ep,req))) {
-+ dev->ep0state = EP0_IDLE;
-+ req = NULL;
-+ }
-+ break;
-+
-+ default:
-+ local_irq_restore(flags);
-+ return -EL2HLT;
-+ }
-+ }
-+ else if ((ep->bEndpointAddress & USB_DIR_IN) != 0
-+ && (!(ep_csr&S3C2410_UDC_OCSR1_PKTRDY)) && write_fifo(ep, req)) {
-+ req = NULL;
-+ } else if ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY) && fifo_count && read_fifo(ep, req)) {
-+ req = NULL;
-+ }
-+
-+ }
-+
-+ /* pio or dma irq handler advances the queue. */
-+ if (likely (req != 0))
-+ list_add_tail(&req->queue, &ep->queue);
-+
-+ local_irq_restore(flags);
-+
-+ dprintk(DEBUG_VERBOSE, "s3c2410_queue normal end\n");
-+ return 0;
-+}
-+
-+/*
-+ * s3c2410_dequeue
-+ */
-+static int s3c2410_dequeue (struct usb_ep *_ep, struct usb_request *_req)
-+{
-+ struct s3c2410_ep *ep;
-+ struct s3c2410_udc *udc;
-+ int retval = -EINVAL;
-+ unsigned long flags;
-+ struct s3c2410_request *req = NULL;
-+
-+ dprintk(DEBUG_VERBOSE,"s3c2410_dequeue(ep=%p,req=%p)\n", _ep, _req);
-+
-+ if (!the_controller->driver)
-+ return -ESHUTDOWN;
-+
-+ if (!_ep || !_req)
-+ return retval;
-+ ep = container_of (_ep, struct s3c2410_ep, ep);
-+ udc = container_of (ep->gadget, struct s3c2410_udc, gadget);
-+
-+ local_irq_save (flags);
-+
-+ list_for_each_entry (req, &ep->queue, queue) {
-+ if (&req->req == _req) {
-+ list_del_init (&req->queue);
-+ _req->status = -ECONNRESET;
-+ retval = 0;
-+ break;
-+ }
-+ }
-+
-+ if (retval == 0) {
-+ dprintk(DEBUG_VERBOSE, "dequeued req %p from %s, len %d buf %p\n",
-+ req, _ep->name, _req->length, _req->buf);
-+
-+ done(ep, req, -ECONNRESET);
-+ }
-+ local_irq_restore (flags);
-+
-+ return retval;
-+}
-+
-+
-+/*
-+ * s3c2410_set_halt
-+ */
-+static int
-+s3c2410_set_halt (struct usb_ep *_ep, int value)
-+{
-+ return 0;
-+}
-+
-+
-+static const struct usb_ep_ops s3c2410_ep_ops = {
-+ .enable = s3c2410_ep_enable,
-+ .disable = s3c2410_ep_disable,
-+
-+ .alloc_request = s3c2410_alloc_request,
-+ .free_request = s3c2410_free_request,
-+
-+ .alloc_buffer = s3c2410_alloc_buffer,
-+ .free_buffer = s3c2410_free_buffer,
-+
-+ .queue = s3c2410_queue,
-+ .dequeue = s3c2410_dequeue,
-+
-+ .set_halt = s3c2410_set_halt,
-+};
-+
-+/*------------------------- usb_gadget_ops ----------------------------------*/
-+
-+/*
-+ * s3c2410_g_get_frame
-+ */
-+static int s3c2410_g_get_frame (struct usb_gadget *_gadget)
-+{
-+ int tmp;
-+
-+ dprintk(DEBUG_VERBOSE,"s3c2410_g_get_frame()\n");
-+
-+ tmp = udc_readl(S3C2410_UDC_FRAME_NUM2_REG) << 8;
-+ tmp |= udc_readl(S3C2410_UDC_FRAME_NUM1_REG);
-+
-+ return tmp & 0xffff;
-+}
-+
-+/*
-+ * s3c2410_wakeup
-+ */
-+static int s3c2410_wakeup (struct usb_gadget *_gadget)
-+{
-+
-+ dprintk(DEBUG_NORMAL,"s3c2410_wakeup()\n");
-+
-+ return 0;
-+}
-+
-+/*
-+ * s3c2410_set_selfpowered
-+ */
-+static int s3c2410_set_selfpowered (struct usb_gadget *_gadget, int value)
-+{
-+ struct s3c2410_udc *udc;
-+
-+ dprintk(DEBUG_NORMAL, "s3c2410_set_selfpowered()\n");
-+
-+ udc = container_of (_gadget, struct s3c2410_udc, gadget);
-+
-+ if (value)
-+ udc->devstatus |= (1 << USB_DEVICE_SELF_POWERED);
-+ else
-+ udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
-+
-+ return 0;
-+}
-+
-+static void udc_disable(struct s3c2410_udc *dev);
-+static void udc_enable(struct s3c2410_udc *dev);
-+
-+static int pull_up (struct s3c2410_udc *udc, int is_on)
-+{
-+ dprintk(DEBUG_NORMAL, "pull_up()\n");
-+
-+ if (udc_info && udc_info->udc_command) {
-+ if (is_on)
-+ udc_enable(udc);
-+ else {
-+ if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
-+ if (udc->driver && udc->driver->disconnect)
-+ udc->driver->disconnect(&udc->gadget);
-+
-+ }
-+ udc_disable(udc);
-+ }
-+ }
-+ else
-+ return -EOPNOTSUPP;
-+
-+ return 0;
-+}
-+
-+static int s3c2410_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
-+{
-+ struct s3c2410_udc *udc;
-+
-+ dprintk(DEBUG_NORMAL, "s3c2410_udc_vbus_session()\n");
-+ udc = container_of (_gadget, struct s3c2410_udc, gadget);
-+
-+ udc->vbus = (is_active != 0);
-+ pull_up(udc, is_active);
-+ return 0;
-+}
-+
-+static int s3c2410_pullup (struct usb_gadget *_gadget, int is_on)
-+{
-+ struct s3c2410_udc *udc;
-+
-+ dprintk(DEBUG_NORMAL, "s3c2410_pullup()\n");
-+ udc = container_of (_gadget, struct s3c2410_udc, gadget);
-+ is_on = !!is_on;
-+ pull_up(udc, is_on);
-+ return 0;
-+}
-+
-+static irqreturn_t s3c2410_udc_vbus_irq(int irq, void *_dev)
-+{
-+ struct s3c2410_udc *dev = _dev;
-+ unsigned int value;
-+
-+ dprintk(DEBUG_NORMAL, "s3c2410_udc_vbus_irq()\n");
-+ value = s3c2410_gpio_getpin(udc_info->vbus_pin);
-+ if (udc_info->vbus_pin_inverted)
-+ value = !value;
-+
-+ if (value != dev->vbus)
-+ s3c2410_udc_vbus_session(&dev->gadget, value);
-+
-+ return IRQ_HANDLED;
-+}
-+
-+static const struct usb_gadget_ops s3c2410_ops = {
-+ .get_frame = s3c2410_g_get_frame,
-+ .wakeup = s3c2410_wakeup,
-+ .set_selfpowered = s3c2410_set_selfpowered,
-+ .pullup = s3c2410_pullup,
-+ .vbus_session = s3c2410_udc_vbus_session,
-+};
-+
-+/*------------------------- gadget driver handling---------------------------*/
-+/*
-+ * udc_disable
-+ */
-+static void udc_disable(struct s3c2410_udc *dev)
-+{
-+ dprintk(DEBUG_NORMAL, "udc_disable called\n");
-+
-+ /* Disable all interrupts */
-+ udc_writel(0x00, S3C2410_UDC_USB_INT_EN_REG);
-+ udc_writel(0x00, S3C2410_UDC_EP_INT_EN_REG);
-+
-+ /* Clear the interrupt registers */
-+ udc_writel( S3C2410_UDC_USBINT_RESET | \
-+ S3C2410_UDC_USBINT_RESUME | \
-+ S3C2410_UDC_USBINT_SUSPEND, \
-+ S3C2410_UDC_USB_INT_REG);
-+ udc_writel( 0x1F, S3C2410_UDC_EP_INT_REG);
-+
-+
-+ /* Good bye, cruel world */
-+ if (udc_info && udc_info->udc_command)
-+ udc_info->udc_command(S3C2410_UDC_P_DISABLE);
-+
-+ /* Set address to 0 */
-+ /*udc_writel( 0x80, S3C2410_UDC_FUNC_ADDR_REG);*/
-+
-+ /* Set speed to unknown */
-+ dev->gadget.speed = USB_SPEED_UNKNOWN;
-+}
-+/*
-+ * udc_reinit
-+ */
-+static void udc_reinit(struct s3c2410_udc *dev)
-+{
-+ u32 i;
-+
-+ /* device/ep0 records init */
-+ INIT_LIST_HEAD (&dev->gadget.ep_list);
-+ INIT_LIST_HEAD (&dev->gadget.ep0->ep_list);
-+ dev->ep0state = EP0_IDLE;
-+
-+
-+ for (i = 0; i < S3C2410_ENDPOINTS; i++) {
-+ struct s3c2410_ep *ep = &dev->ep[i];
-+
-+ if (i != 0)
-+ list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list);
-+
-+ ep->dev = dev;
-+ ep->desc = NULL;
-+ ep->halted = 0;
-+ INIT_LIST_HEAD (&ep->queue);
-+ }
-+}
-+
-+/*
-+ * udc_enable
-+ */
-+static void udc_enable(struct s3c2410_udc *dev)
-+{
-+ int i;
-+
-+ dprintk(DEBUG_NORMAL, "udc_enable called\n");
-+
-+ /* dev->gadget.speed = USB_SPEED_UNKNOWN; */
-+ dev->gadget.speed = USB_SPEED_FULL;
-+
-+ /* Set MAXP for all endpoints */
-+ for (i = 0; i < S3C2410_ENDPOINTS; i++) {
-+
-+ udc_writel(i, S3C2410_UDC_INDEX_REG);
-+ udc_writel((dev->ep[i].ep.maxpacket&0x7ff)>>3,S3C2410_UDC_MAXP_REG);
-+ }
-+
-+ /* Set default power state */
-+ udc_writel(DEFAULT_POWER_STATE, S3C2410_UDC_PWR_REG);
-+
-+ /* Enable reset and suspend interrupt interrupts */
-+ udc_writel(1<<2 | 1<<0 ,S3C2410_UDC_USB_INT_EN_REG);
-+
-+ /* Enable ep0 interrupt */
-+ udc_writel(0x01,S3C2410_UDC_EP_INT_EN_REG);
-+
-+ /* time to say "hello, world" */
-+ if (udc_info && udc_info->udc_command)
-+ udc_info->udc_command(S3C2410_UDC_P_ENABLE);
-+}
-+
-+
-+/*
-+ * nop_release
-+ */
-+static void nop_release (struct device *dev)
-+{
-+ dprintk(DEBUG_NORMAL, "%s %s\n", __FUNCTION__, dev->bus_id);
-+}
-+/*
-+ * usb_gadget_register_driver
-+ */
-+int
-+usb_gadget_register_driver (struct usb_gadget_driver *driver)
-+{
-+ struct s3c2410_udc *udc = the_controller;
-+ int retval;
-+
-+ dprintk(DEBUG_NORMAL, "usb_gadget_register_driver() '%s'\n",
-+ driver->driver.name);
-+
-+ /* Sanity checks */
-+ if (!udc)
-+ return -ENODEV;
-+ if (udc->driver)
-+ return -EBUSY;
-+ if (!driver->bind || !driver->setup
-+ || driver->speed != USB_SPEED_FULL) {
-+ printk(KERN_ERR "Invalid driver : bind %p setup %p speed %d\n",
-+ driver->bind, driver->setup, driver->speed);
-+ return -EINVAL;
-+ }
-+#if defined(MODULE)
-+ if (!driver->unbind) {
-+ printk(KERN_ERR "Invalid driver : no unbind method\n");
-+ return -EINVAL;
-+ }
-+#endif
-+
-+ /* Hook the driver */
-+ udc->driver = driver;
-+ udc->gadget.dev.driver = &driver->driver;
-+
-+ /*Bind the driver */
-+ device_add(&udc->gadget.dev);
-+ dprintk(DEBUG_NORMAL, "binding gadget driver '%s'\n", driver->driver.name);
-+ if ((retval = driver->bind (&udc->gadget)) != 0) {
-+ device_del(&udc->gadget.dev);
-+ udc->driver = NULL;
-+ udc->gadget.dev.driver = NULL;
-+ return retval;
-+ }
-+
-+ /* driver->driver.bus = 0; */
-+
-+ /* Enable udc */
-+ udc_enable(udc);
-+
-+ return 0;
-+}
-+
-+
-+/*
-+ * usb_gadget_unregister_driver
-+ */
-+int
-+usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
-+{
-+ struct s3c2410_udc *udc = the_controller;
-+
-+ if (!udc)
-+ return -ENODEV;
-+ if (!driver || driver != udc->driver)
-+ return -EINVAL;
-+
-+ dprintk(DEBUG_NORMAL,"usb_gadget_register_driver() '%s'\n",
-+ driver->driver.name);
-+
-+ if (driver->disconnect)
-+ driver->disconnect(&udc->gadget);
-+
-+ if (driver->unbind)
-+ driver->unbind (&udc->gadget);
-+
-+ device_del(&udc->gadget.dev);
-+ udc->driver = NULL;
-+
-+ /* Disable udc */
-+ udc_disable(udc);
-+
-+ return 0;
-+}
-+
-+/*---------------------------------------------------------------------------*/
-+static struct s3c2410_udc memory = {
-+ .gadget = {
-+ .ops = &s3c2410_ops,
-+ .ep0 = &memory.ep[0].ep,
-+ .name = gadget_name,
-+ .dev = {
-+ .bus_id = "gadget",
-+ .release = nop_release,
-+ },
-+ },
-+
-+ /* control endpoint */
-+ .ep[0] = {
-+ .num = 0,
-+ .ep = {
-+ .name = ep0name,
-+ .ops = &s3c2410_ep_ops,
-+ .maxpacket = EP0_FIFO_SIZE,
-+ },
-+ .dev = &memory,
-+ },
-+
-+ /* first group of endpoints */
-+ .ep[1] = {
-+ .num = 1,
-+ .ep = {
-+ .name = "ep1-bulk",
-+ .ops = &s3c2410_ep_ops,
-+ .maxpacket = EP_FIFO_SIZE,
-+ },
-+ .dev = &memory,
-+ .fifo_size = EP_FIFO_SIZE,
-+ .bEndpointAddress = 1,
-+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
-+ },
-+ .ep[2] = {
-+ .num = 2,
-+ .ep = {
-+ .name = "ep2-bulk",
-+ .ops = &s3c2410_ep_ops,
-+ .maxpacket = EP_FIFO_SIZE,
-+ },
-+ .dev = &memory,
-+ .fifo_size = EP_FIFO_SIZE,
-+ .bEndpointAddress = 2,
-+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
-+ },
-+ .ep[3] = {
-+ .num = 3,
-+ .ep = {
-+ .name = "ep3-bulk",
-+ .ops = &s3c2410_ep_ops,
-+ .maxpacket = EP_FIFO_SIZE,
-+ },
-+ .dev = &memory,
-+ .fifo_size = EP_FIFO_SIZE,
-+ .bEndpointAddress = 3,
-+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
-+ },
-+ .ep[4] = {
-+ .num = 4,
-+ .ep = {
-+ .name = "ep4-bulk",
-+ .ops = &s3c2410_ep_ops,
-+ .maxpacket = EP_FIFO_SIZE,
-+ },
-+ .dev = &memory,
-+ .fifo_size = EP_FIFO_SIZE,
-+ .bEndpointAddress = 4,
-+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
-+ }
-+
-+};
-+
-+/*
-+ * probe - binds to the platform device
-+ */
-+static int s3c2410_udc_probe(struct platform_device *pdev)
-+{
-+ struct s3c2410_udc *udc = &memory;
-+ int retval;
-+ unsigned int irq;
-+
-+ dprintk(DEBUG_NORMAL,"s3c2410_udc_probe\n");
-+
-+ usb_bus_clock = clk_get(NULL, "usb-bus");
-+ if (IS_ERR(usb_bus_clock)) {
-+ printk(KERN_INFO "failed to get usb bus clock source\n");
-+ return PTR_ERR(usb_bus_clock);
-+ }
-+
-+ clk_enable(usb_bus_clock);
-+
-+ udc_clock = clk_get(NULL, "usb-device");
-+ if (IS_ERR(udc_clock)) {
-+ printk(KERN_INFO "failed to get udc clock source\n");
-+ return PTR_ERR(udc_clock);
-+ }
-+
-+ clk_enable(udc_clock);
-+
-+ mdelay(10);
-+
-+ dprintk(DEBUG_VERBOSE, "got and enabled clocks\n");
-+
-+ if (strncmp(pdev->name, "s3c2440", 7) == 0) {
-+ printk("Detected S3C2440 - increasing FIFO to 128 bytes\n");
-+ memory.ep[1].fifo_size = S3C2440_EP_FIFO_SIZE;
-+ memory.ep[2].fifo_size = S3C2440_EP_FIFO_SIZE;
-+ memory.ep[3].fifo_size = S3C2440_EP_FIFO_SIZE;
-+ memory.ep[4].fifo_size = S3C2440_EP_FIFO_SIZE;
-+ }
-+
-+ spin_lock_init (&udc->lock);
-+ udc_info = pdev->dev.platform_data;
-+
-+ rsrc_start = S3C2410_PA_USBDEV;
-+ rsrc_len = S3C24XX_SZ_USBDEV;
-+
-+ if (!request_mem_region(rsrc_start, rsrc_len, gadget_name))
-+ return -EBUSY;
-+
-+ base_addr = ioremap(rsrc_start, rsrc_len);
-+ if (!base_addr) {
-+ retval = -ENOMEM;
-+ goto err_mem;
-+ }
-+
-+ device_initialize(&udc->gadget.dev);
-+ udc->gadget.dev.parent = &pdev->dev;
-+ udc->gadget.dev.dma_mask = pdev->dev.dma_mask;
-+
-+ the_controller = udc;
-+ platform_set_drvdata(pdev, udc);
-+
-+ udc_disable(udc);
-+ udc_reinit(udc);
-+
-+ /* irq setup after old hardware state is cleaned up */
-+ retval = request_irq(IRQ_USBD, s3c2410_udc_irq,
-+ IRQF_DISABLED, gadget_name, udc);
-+
-+ if (retval != 0) {
-+ printk(KERN_ERR "%s: can't get irq %i, err %d\n",
-+ gadget_name, IRQ_USBD, retval);
-+ retval = -EBUSY;
-+ goto err_map;
-+ }
-+
-+ dprintk(DEBUG_VERBOSE, "%s: got irq %i\n", gadget_name, IRQ_USBD);
-+
-+ if (udc_info && udc_info->vbus_pin > 0) {
-+ irq = s3c2410_gpio_getirq(udc_info->vbus_pin);
-+ retval = request_irq(irq, s3c2410_udc_vbus_irq,
-+ IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
-+ gadget_name, udc);
-+
-+ if (retval != 0) {
-+ printk(KERN_ERR "%s: can't get vbus irq %i, err %d\n",
-+ gadget_name, irq, retval);
-+ retval = -EBUSY;
-+ goto err_int;
-+ }
-+
-+ dprintk(DEBUG_VERBOSE, "%s: got irq %i\n", gadget_name, irq);
-+ }
-+ else {
-+ udc->vbus = 1;
-+ }
-+
-+#ifdef ENABLE_SYSFS
-+ /* create device files */
-+ device_create_file(&pdev->dev, &dev_attr_regs);
-+#endif
-+ return 0;
-+err_int:
-+ free_irq(IRQ_USBD, udc);
-+err_map:
-+ iounmap(base_addr);
-+err_mem:
-+ release_mem_region(rsrc_start, rsrc_len);
-+
-+ return retval;
-+}
-+
-+/*
-+ * s3c2410_udc_remove
-+ */
-+static int s3c2410_udc_remove(struct platform_device *pdev)
-+{
-+ struct s3c2410_udc *udc = platform_get_drvdata(pdev);
-+ unsigned int irq;
-+
-+ dprintk(DEBUG_NORMAL, "s3c2410_udc_remove\n");
-+ usb_gadget_unregister_driver(udc->driver);
-+
-+ if (udc_info && udc_info->vbus_pin > 0) {
-+ irq = s3c2410_gpio_getirq(udc_info->vbus_pin);
-+ free_irq(irq, udc);
-+ }
-+
-+ free_irq(IRQ_USBD, udc);
-+
-+ iounmap(base_addr);
-+ release_mem_region(rsrc_start, rsrc_len);
-+
-+ platform_set_drvdata(pdev, NULL);
-+
-+ if (!IS_ERR(udc_clock) && udc_clock != NULL) {
-+ clk_disable(udc_clock);
-+ clk_put(udc_clock);
-+ udc_clock = NULL;
-+ }
-+
-+ if (!IS_ERR(usb_bus_clock) && usb_bus_clock != NULL) {
-+ clk_disable(usb_bus_clock);
-+ clk_put(usb_bus_clock);
-+ usb_bus_clock = NULL;
-+ }
-+
-+ return 0;
-+}
-+
-+#ifdef CONFIG_PM
-+static int s3c2410_udc_suspend(struct platform_device *pdev, pm_message_t message)
-+{
-+ struct s3c2410_udc *udc = platform_get_drvdata(pdev);
-+
-+ if (udc_info && udc_info->udc_command)
-+ udc_info->udc_command(S3C2410_UDC_P_DISABLE);
-+
-+ return 0;
-+}
-+
-+static int s3c2410_udc_resume(struct platform_device *pdev)
-+{
-+ struct s3c2410_udc *udc = platform_get_drvdata(pdev);
-+
-+ if (udc_info && udc_info->udc_command)
-+ udc_info->udc_command(S3C2410_UDC_P_ENABLE);
-+
-+ return 0;
-+}
-+#else
-+#define s3c2410_udc_suspend NULL
-+#define s3c2410_udc_resume NULL
-+#endif
-+
-+
-+static struct platform_driver udc_driver_2410 = {
-+ .driver = {
-+ .name = "s3c2410-usbgadget",
-+ .owner = THIS_MODULE,
-+ },
-+ .probe = s3c2410_udc_probe,
-+ .remove = s3c2410_udc_remove,
-+ .suspend = s3c2410_udc_suspend,
-+ .resume = s3c2410_udc_resume,
-+};
-+
-+static struct platform_driver udc_driver_2440 = {
-+ .driver = {
-+ .name = "s3c2440-usbgadget",
-+ .owner = THIS_MODULE,
-+ },
-+ .probe = s3c2410_udc_probe,
-+ .remove = s3c2410_udc_remove,
-+ .suspend = s3c2410_udc_suspend,
-+ .resume = s3c2410_udc_resume,
-+};
-+
-+static int __init udc_init(void)
-+{
-+ int retval;
-+
-+ dprintk(DEBUG_NORMAL, "%s: version %s\n", gadget_name, DRIVER_VERSION);
-+
-+ retval = platform_driver_register(&udc_driver_2410);
-+ if (retval)
-+ return retval;
-+
-+ return platform_driver_register(&udc_driver_2440);
-+}
-+
-+static void __exit udc_exit(void)
-+{
-+ platform_driver_unregister(&udc_driver_2410);
-+ platform_driver_unregister(&udc_driver_2440);
-+}
-+
-+
-+EXPORT_SYMBOL (usb_gadget_unregister_driver);
-+EXPORT_SYMBOL (usb_gadget_register_driver);
-+
-+module_init(udc_init);
-+module_exit(udc_exit);
-+
-+MODULE_AUTHOR(DRIVER_AUTHOR);
-+MODULE_DESCRIPTION(DRIVER_DESC);
-+MODULE_VERSION(DRIVER_VERSION);
-+MODULE_LICENSE("GPL");
-Index: linux-2.6.20/drivers/usb/gadget/s3c2410_udc.h
-===================================================================
---- /dev/null 1970-01-01 00:00:00.000000000 +0000
-+++ linux-2.6.20/drivers/usb/gadget/s3c2410_udc.h 2007-02-15 14:54:49.000000000 +0100
-@@ -0,0 +1,188 @@
-+#ifndef _S3C2410_UDC_H
-+#define _S3C2410_UDC_H
-+
-+struct s3c2410_ep {
-+ struct list_head queue;
-+ unsigned long last_io; /* jiffies timestamp */
-+ struct usb_gadget *gadget;
-+ struct s3c2410_udc *dev;
-+ const struct usb_endpoint_descriptor *desc;
-+ struct usb_ep ep;
-+ u8 num;
-+
-+ unsigned short fifo_size;
-+ u8 bEndpointAddress;
-+ u8 bmAttributes;
-+
-+ unsigned halted : 1;
-+ unsigned already_seen : 1;
-+ unsigned setup_stage : 1;
-+};
-+
-+
-+/* Warning : ep0 has a fifo of 16 bytes */
-+/* Don't try to set 32 or 64 */
-+#define EP0_FIFO_SIZE 16
-+#define EP_FIFO_SIZE 64
-+#define DEFAULT_POWER_STATE 0x00
-+
-+#define S3C2440_EP_FIFO_SIZE 128
-+
-+static const char ep0name [] = "ep0";
-+
-+static const char *const ep_name[] = {
-+ ep0name, /* everyone has ep0 */
-+ /* s3c2410 four bidirectional bulk endpoints */
-+ "ep1-bulk", "ep2-bulk", "ep3-bulk", "ep4-bulk",
-+};
-+
-+#define S3C2410_ENDPOINTS ARRAY_SIZE(ep_name)
-+
-+struct s3c2410_request {
-+ struct list_head queue; /* ep's requests */
-+ struct usb_request req;
-+};
-+
-+enum ep0_state {
-+ EP0_IDLE,
-+ EP0_IN_DATA_PHASE,
-+ EP0_OUT_DATA_PHASE,
-+ EP0_END_XFER,
-+ EP0_STALL,
-+};
-+
-+static const char *ep0states[]= {
-+ "EP0_IDLE",
-+ "EP0_IN_DATA_PHASE",
-+ "EP0_OUT_DATA_PHASE",
-+ "EP0_END_XFER",
-+ "EP0_STALL",
-+};
-+
-+struct s3c2410_udc {
-+ spinlock_t lock;
-+
-+ struct s3c2410_ep ep[S3C2410_ENDPOINTS];
-+ int address;
-+ struct usb_gadget gadget;
-+ struct usb_gadget_driver *driver;
-+ struct s3c2410_request fifo_req;
-+ u8 fifo_buf[EP_FIFO_SIZE];
-+ u16 devstatus;
-+
-+ u32 port_status;
-+ int ep0state;
-+
-+ unsigned got_irq : 1;
-+
-+ unsigned req_std : 1;
-+ unsigned req_config : 1;
-+ unsigned req_pending : 1;
-+ u8 vbus;
-+};
-+
-+/****************** MACROS ******************/
-+/* #define BIT_MASK BIT_MASK*/
-+#define BIT_MASK 0xFF
-+
-+#define maskb(base,v,m,a) \
-+ writeb((readb(base+a) & ~(m))|((v)&(m)), (base+a))
-+
-+#define maskw(base,v,m,a) \
-+ writew((readw(base+a) & ~(m))|((v)&(m)), (base+a))
-+
-+#define maskl(base,v,m,a) \
-+ writel((readl(base+a) & ~(m))|((v)&(m)), (base+a))
-+
-+#define clear_ep0_sst(base) do { \
-+ S3C2410_UDC_SETIX(base,EP0); \
-+ writel(0x00, base+S3C2410_UDC_EP0_CSR_REG); \
-+} while(0)
-+
-+#define clear_ep0_se(base) do { \
-+ S3C2410_UDC_SETIX(base,EP0); \
-+ maskl(base,S3C2410_UDC_EP0_CSR_SSE, \
-+ BIT_MASK, S3C2410_UDC_EP0_CSR_REG); \
-+} while(0)
-+
-+#define clear_ep0_opr(base) do { \
-+ S3C2410_UDC_SETIX(base,EP0); \
-+ maskl(base,S3C2410_UDC_EP0_CSR_SOPKTRDY, \
-+ BIT_MASK, S3C2410_UDC_EP0_CSR_REG); \
-+} while(0)
-+
-+#define set_ep0_ipr(base) do { \
-+ S3C2410_UDC_SETIX(base,EP0); \
-+ maskl(base,S3C2410_UDC_EP0_CSR_IPKRDY, \
-+ BIT_MASK, S3C2410_UDC_EP0_CSR_REG); \
-+} while(0)
-+
-+#define set_ep0_de(base) do { \
-+ S3C2410_UDC_SETIX(base,EP0); \
-+ maskl(base,S3C2410_UDC_EP0_CSR_DE, \
-+ BIT_MASK, S3C2410_UDC_EP0_CSR_REG); \
-+} while(0)
-+
-+#if 0
-+#define set_ep0_ss(base) do { \
-+ S3C2410_UDC_SETIX(base,EP0); \
-+ maskl(base,S3C2410_UDC_EP0_CSR_SENDSTL|S3C2410_UDC_EP0_CSR_SOPKTRDY, \
-+ BIT_MASK, S3C2410_UDC_EP0_CSR_REG); \
-+} while(0)
-+#else
-+#define set_ep0_ss(base) do { \
-+ S3C2410_UDC_SETIX(base,EP0); \
-+ maskl(base,S3C2410_UDC_EP0_CSR_SENDSTL, \
-+ BIT_MASK, S3C2410_UDC_EP0_CSR_REG); \
-+} while(0)
-+#endif
-+
-+#define set_ep0_de_out(base) do { \
-+ S3C2410_UDC_SETIX(base,EP0); \
-+ maskl(base,(S3C2410_UDC_EP0_CSR_SOPKTRDY \
-+ | S3C2410_UDC_EP0_CSR_DE), \
-+ BIT_MASK, S3C2410_UDC_EP0_CSR_REG); \
-+} while(0)
-+
-+#define set_ep0_sse_out(base) do { \
-+ S3C2410_UDC_SETIX(base,EP0); \
-+ maskl(base,(S3C2410_UDC_EP0_CSR_SOPKTRDY \
-+ | S3C2410_UDC_EP0_CSR_SSE), \
-+ BIT_MASK, S3C2410_UDC_EP0_CSR_REG); \
-+} while(0)
-+
-+#define set_ep0_de_in(base) do { \
-+ S3C2410_UDC_SETIX(base,EP0); \
-+ maskl(base,(S3C2410_UDC_EP0_CSR_IPKRDY \
-+ | S3C2410_UDC_EP0_CSR_DE), \
-+ BIT_MASK, S3C2410_UDC_EP0_CSR_REG); \
-+} while(0)
-+
-+
-+
-+#define clear_stall_ep1_out(base) do { \
-+ S3C2410_UDC_SETIX(base,EP1); \
-+ orl(0,base+S3C2410_UDC_OUT_CSR1_REG); \
-+} while(0)
-+
-+
-+#define clear_stall_ep2_out(base) do { \
-+ S3C2410_UDC_SETIX(base,EP2); \
-+ orl(0, base+S3C2410_UDC_OUT_CSR1_REG); \
-+} while(0)
-+
-+
-+#define clear_stall_ep3_out(base) do { \
-+ S3C2410_UDC_SETIX(base,EP3); \
-+ orl(0,base+S3C2410_UDC_OUT_CSR1_REG); \
-+} while(0)
-+
-+
-+#define clear_stall_ep4_out(base) do { \
-+ S3C2410_UDC_SETIX(base,EP4); \
-+ orl(0, base+S3C2410_UDC_OUT_CSR1_REG); \
-+} while(0)
-+
-+#endif
-+
-+
-
Added: branches/src/target/kernel/2.6.22.x/patches/s3c2410_udc_from_upstream.patch
===================================================================
--- branches/src/target/kernel/2.6.22.x/patches/s3c2410_udc_from_upstream.patch 2007-07-26 14:40:11 UTC (rev 2397)
+++ branches/src/target/kernel/2.6.22.x/patches/s3c2410_udc_from_upstream.patch 2007-07-26 15:24:28 UTC (rev 2398)
@@ -0,0 +1,2265 @@
+From: Arnaud Patard <arnaud.patard at rtp-net.org>
+Date: Thu, 7 Jun 2007 04:05:49 +0000 (-0700)
+Subject: USB Gadget driver for Samsung s3c2410 ARM SoC
+X-Git-Tag: v2.6.23-rc1~1083^2~86
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=3fc154b6b8134b98bb94d60cad9a46ec1ffbe372;hp=7a4eb7fd50d4df99fc1f623e6d90680d9fca3d82
+
+USB Gadget driver for Samsung s3c2410 ARM SoC
+
+This patch adds the support for the Usb Device Controller on Samsung
+S3C24xx SoCs. This driver passes all tests from testusb (including #13)
+and has been tested on S3C2410, S3C24212, and S3C2440 SoCs.
+
+Whitespace updates, minor cleanups by David
+
+Signed-off-by: Arnaud Patard <arnaud.patard at rtp-net.org>
+Signed-off-by: Ben Dooks <ben-linux at fluff.org>
+Cc: Herbert Pötzl <herbert at 13thfloor.at>
+Signed-off-by: David Brownell <dbrownell at users.sourceforge.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh at suse.de>
+---
+
+diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
+index 0576888..74eaa7d 100644
+--- a/drivers/usb/gadget/Kconfig
++++ b/drivers/usb/gadget/Kconfig
+@@ -208,6 +208,27 @@ config USB_OTG
+
+ Select this only if your OMAP board has a Mini-AB connector.
+
++config USB_GADGET_S3C2410
++ boolean "S3C2410 USB Device Controller"
++ depends on ARCH_S3C2410
++ help
++ Samsung's S3C2410 is an ARM-4 processor with an integrated
++ full speed USB 1.1 device controller. It has 4 configurable
++ endpoints, as well as endpoint zero (for control transfers).
++
++ This driver has been tested on the S3C2410, S3C2412, and
++ S3C2440 processors.
++
++config USB_S3C2410
++ tristate
++ depends on USB_GADGET_S3C2410
++ default USB_GADGET
++ select USB_GADGET_SELECTED
++
++config USB_S3C2410_DEBUG
++ boolean "S3C2410 udc debug messages"
++ depends on USB_GADGET_S3C2410
++
+ config USB_GADGET_AT91
+ boolean "AT91 USB Device Port"
+ depends on ARCH_AT91 && !ARCH_AT91SAM9RL
+diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
+index 2d41e84..bff2783 100644
+--- a/drivers/usb/gadget/Makefile
++++ b/drivers/usb/gadget/Makefile
+@@ -7,6 +7,7 @@ obj-$(CONFIG_USB_PXA2XX) += pxa2xx_udc.o
+ obj-$(CONFIG_USB_GOKU) += goku_udc.o
+ obj-$(CONFIG_USB_OMAP) += omap_udc.o
+ obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o
++obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o
+ obj-$(CONFIG_USB_AT91) += at91_udc.o
+ obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o
+ obj-$(CONFIG_USB_M66592) += m66592-udc.o
+diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c
+new file mode 100644
+index 0000000..d60748a
+--- /dev/null
++++ b/drivers/usb/gadget/s3c2410_udc.c
+@@ -0,0 +1,2078 @@
++/*
++ * linux/drivers/usb/gadget/s3c2410_udc.c
++ *
++ * Samsung S3C24xx series on-chip full speed USB device controllers
++ *
++ * Copyright (C) 2004-2007 Herbert Pötzl - Arnaud Patard
++ * Additional cleanups by Ben Dooks <ben-linux at fluff.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/ioport.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/smp_lock.h>
++#include <linux/errno.h>
++#include <linux/init.h>
++#include <linux/timer.h>
++#include <linux/list.h>
++#include <linux/interrupt.h>
++#include <linux/platform_device.h>
++#include <linux/version.h>
++#include <linux/clk.h>
++
++#include <linux/debugfs.h>
++#include <linux/seq_file.h>
++
++#include <linux/usb.h>
++#include <linux/usb_gadget.h>
++
++#include <asm/byteorder.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/system.h>
++#include <asm/unaligned.h>
++#include <asm/arch/irqs.h>
++
++#include <asm/arch/hardware.h>
++#include <asm/arch/regs-clock.h>
++#include <asm/arch/regs-gpio.h>
++#include <asm/arch/regs-udc.h>
++#include <asm/arch/udc.h>
++
++#include <asm/mach-types.h>
++
++#include "s3c2410_udc.h"
++
++#define DRIVER_DESC "S3C2410 USB Device Controller Gadget"
++#define DRIVER_VERSION "29 Apr 2007"
++#define DRIVER_AUTHOR "Herbert Pötzl <herbert at 13thfloor.at>, " \
++ "Arnaud Patard <arnaud.patard at rtp-net.org>"
++
++static const char gadget_name[] = "s3c2410_udc";
++static const char driver_desc[] = DRIVER_DESC;
++
++static struct s3c2410_udc *the_controller;
++static struct clk *udc_clock;
++static struct clk *usb_bus_clock;
++static void __iomem *base_addr;
++static u64 rsrc_start;
++static u64 rsrc_len;
++static struct dentry *s3c2410_udc_debugfs_root;
++
++static inline u32 udc_read(u32 reg)
++{
++ return readb(base_addr + reg);
++}
++
++static inline void udc_write(u32 value, u32 reg)
++{
++ writeb(value, base_addr + reg);
++}
++
++static inline void udc_writeb(void __iomem *base, u32 value, u32 reg)
++{
++ writeb(value, base + reg);
++}
++
++static struct s3c2410_udc_mach_info *udc_info;
++
++/*************************** DEBUG FUNCTION ***************************/
++#define DEBUG_NORMAL 1
++#define DEBUG_VERBOSE 2
++
++#ifdef CONFIG_USB_S3C2410_DEBUG
++#define USB_S3C2410_DEBUG_LEVEL 0
++
++static uint32_t s3c2410_ticks = 0;
++
++static int dprintk(int level, const char *fmt, ...)
++{
++ static char printk_buf[1024];
++ static long prevticks;
++ static int invocation;
++ va_list args;
++ int len;
++
++ if (level > USB_S3C2410_DEBUG_LEVEL)
++ return 0;
++
++ if (s3c2410_ticks != prevticks) {
++ prevticks = s3c2410_ticks;
++ invocation = 0;
++ }
++
++ len = scnprintf(printk_buf,
++ sizeof(printk_buf), "%1lu.%02d USB: ",
++ prevticks, invocation++);
++
++ va_start(args, fmt);
++ len = vscnprintf(printk_buf+len,
++ sizeof(printk_buf)-len, fmt, args);
++ va_end(args);
++
++ return printk(KERN_DEBUG "%s", printk_buf);
++}
++#else
++static int dprintk(int level, const char *fmt, ...)
++{
++ return 0;
++}
++#endif
++static int s3c2410_udc_debugfs_seq_show(struct seq_file *m, void *p)
++{
++ u32 addr_reg,pwr_reg,ep_int_reg,usb_int_reg;
++ u32 ep_int_en_reg, usb_int_en_reg, ep0_csr;
++ u32 ep1_i_csr1,ep1_i_csr2,ep1_o_csr1,ep1_o_csr2;
++ u32 ep2_i_csr1,ep2_i_csr2,ep2_o_csr1,ep2_o_csr2;
++
++ addr_reg = udc_read(S3C2410_UDC_FUNC_ADDR_REG);
++ pwr_reg = udc_read(S3C2410_UDC_PWR_REG);
++ ep_int_reg = udc_read(S3C2410_UDC_EP_INT_REG);
++ usb_int_reg = udc_read(S3C2410_UDC_USB_INT_REG);
++ ep_int_en_reg = udc_read(S3C2410_UDC_EP_INT_EN_REG);
++ usb_int_en_reg = udc_read(S3C2410_UDC_USB_INT_EN_REG);
++ udc_write(0, S3C2410_UDC_INDEX_REG);
++ ep0_csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
++ udc_write(1, S3C2410_UDC_INDEX_REG);
++ ep1_i_csr1 = udc_read(S3C2410_UDC_IN_CSR1_REG);
++ ep1_i_csr2 = udc_read(S3C2410_UDC_IN_CSR2_REG);
++ ep1_o_csr1 = udc_read(S3C2410_UDC_IN_CSR1_REG);
++ ep1_o_csr2 = udc_read(S3C2410_UDC_IN_CSR2_REG);
++ udc_write(2, S3C2410_UDC_INDEX_REG);
++ ep2_i_csr1 = udc_read(S3C2410_UDC_IN_CSR1_REG);
++ ep2_i_csr2 = udc_read(S3C2410_UDC_IN_CSR2_REG);
++ ep2_o_csr1 = udc_read(S3C2410_UDC_IN_CSR1_REG);
++ ep2_o_csr2 = udc_read(S3C2410_UDC_IN_CSR2_REG);
++
++ seq_printf(m, "FUNC_ADDR_REG : 0x%04X\n"
++ "PWR_REG : 0x%04X\n"
++ "EP_INT_REG : 0x%04X\n"
++ "USB_INT_REG : 0x%04X\n"
++ "EP_INT_EN_REG : 0x%04X\n"
++ "USB_INT_EN_REG : 0x%04X\n"
++ "EP0_CSR : 0x%04X\n"
++ "EP1_I_CSR1 : 0x%04X\n"
++ "EP1_I_CSR2 : 0x%04X\n"
++ "EP1_O_CSR1 : 0x%04X\n"
++ "EP1_O_CSR2 : 0x%04X\n"
++ "EP2_I_CSR1 : 0x%04X\n"
++ "EP2_I_CSR2 : 0x%04X\n"
++ "EP2_O_CSR1 : 0x%04X\n"
++ "EP2_O_CSR2 : 0x%04X\n",
++ addr_reg,pwr_reg,ep_int_reg,usb_int_reg,
++ ep_int_en_reg, usb_int_en_reg, ep0_csr,
++ ep1_i_csr1,ep1_i_csr2,ep1_o_csr1,ep1_o_csr2,
++ ep2_i_csr1,ep2_i_csr2,ep2_o_csr1,ep2_o_csr2
++ );
++
++ return 0;
++}
++
++static int s3c2410_udc_debugfs_fops_open(struct inode *inode,
++ struct file *file)
++{
++ return single_open(file, s3c2410_udc_debugfs_seq_show, NULL);
++}
++
++static const struct file_operations s3c2410_udc_debugfs_fops = {
++ .open = s3c2410_udc_debugfs_fops_open,
++ .read = seq_read,
++ .llseek = seq_lseek,
++ .release = single_release,
++ .owner = THIS_MODULE,
++};
++
++/* io macros */
++
++static inline void s3c2410_udc_clear_ep0_opr(void __iomem *base)
++{
++ udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
++ udc_writeb(base, S3C2410_UDC_EP0_CSR_SOPKTRDY,
++ S3C2410_UDC_EP0_CSR_REG);
++}
++
++static inline void s3c2410_udc_clear_ep0_sst(void __iomem *base)
++{
++ udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
++ writeb(0x00, base + S3C2410_UDC_EP0_CSR_REG);
++}
++
++static inline void s3c2410_udc_clear_ep0_se(void __iomem *base)
++{
++ udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
++ udc_writeb(base, S3C2410_UDC_EP0_CSR_SSE, S3C2410_UDC_EP0_CSR_REG);
++}
++
++static inline void s3c2410_udc_set_ep0_ipr(void __iomem *base)
++{
++ udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
++ udc_writeb(base, S3C2410_UDC_EP0_CSR_IPKRDY, S3C2410_UDC_EP0_CSR_REG);
++}
++
++static inline void s3c2410_udc_set_ep0_de(void __iomem *base)
++{
++ udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
++ udc_writeb(base, S3C2410_UDC_EP0_CSR_DE, S3C2410_UDC_EP0_CSR_REG);
++}
++
++inline void s3c2410_udc_set_ep0_ss(void __iomem *b)
++{
++ udc_writeb(b, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
++ udc_writeb(b, S3C2410_UDC_EP0_CSR_SENDSTL, S3C2410_UDC_EP0_CSR_REG);
++}
++
++static inline void s3c2410_udc_set_ep0_de_out(void __iomem *base)
++{
++ udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
++
++ udc_writeb(base,(S3C2410_UDC_EP0_CSR_SOPKTRDY
++ | S3C2410_UDC_EP0_CSR_DE),
++ S3C2410_UDC_EP0_CSR_REG);
++}
++
++static inline void s3c2410_udc_set_ep0_sse_out(void __iomem *base)
++{
++ udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
++ udc_writeb(base, (S3C2410_UDC_EP0_CSR_SOPKTRDY
++ | S3C2410_UDC_EP0_CSR_SSE),
++ S3C2410_UDC_EP0_CSR_REG);
++}
++
++static inline void s3c2410_udc_set_ep0_de_in(void __iomem *base)
++{
++ udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
++ udc_writeb(base, (S3C2410_UDC_EP0_CSR_IPKRDY
++ | S3C2410_UDC_EP0_CSR_DE),
++ S3C2410_UDC_EP0_CSR_REG);
++}
++
++/*------------------------- I/O ----------------------------------*/
++
++/*
++ * s3c2410_udc_done
++ */
++static void s3c2410_udc_done(struct s3c2410_ep *ep,
++ struct s3c2410_request *req, int status)
++{
++ unsigned halted = ep->halted;
++
++ list_del_init(&req->queue);
++
++ if (likely (req->req.status == -EINPROGRESS))
++ req->req.status = status;
++ else
++ status = req->req.status;
++
++ ep->halted = 1;
++ req->req.complete(&ep->ep, &req->req);
++ ep->halted = halted;
++}
++
++static void s3c2410_udc_nuke(struct s3c2410_udc *udc,
++ struct s3c2410_ep *ep, int status)
++{
++ /* Sanity check */
++ if (&ep->queue == NULL)
++ return;
++
++ while (!list_empty (&ep->queue)) {
++ struct s3c2410_request *req;
++ req = list_entry (ep->queue.next, struct s3c2410_request,
++ queue);
++ s3c2410_udc_done(ep, req, status);
++ }
++}
++
++static inline void s3c2410_udc_clear_ep_state(struct s3c2410_udc *dev)
++{
++ unsigned i;
++
++ /* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint
++ * fifos, and pending transactions mustn't be continued in any case.
++ */
++
++ for (i = 1; i < S3C2410_ENDPOINTS; i++)
++ s3c2410_udc_nuke(dev, &dev->ep[i], -ECONNABORTED);
++}
++
++static inline int s3c2410_udc_fifo_count_out(void)
++{
++ int tmp;
++
++ tmp = udc_read(S3C2410_UDC_OUT_FIFO_CNT2_REG) << 8;
++ tmp |= udc_read(S3C2410_UDC_OUT_FIFO_CNT1_REG);
++ return tmp;
++}
++
++/*
++ * s3c2410_udc_write_packet
++ */
++static inline int s3c2410_udc_write_packet(int fifo,
++ struct s3c2410_request *req,
++ unsigned max)
++{
++ unsigned len = min(req->req.length - req->req.actual, max);
++ u8 *buf = req->req.buf + req->req.actual;
++
++ prefetch(buf);
++
++ dprintk(DEBUG_VERBOSE, "%s %d %d %d %d\n", __func__,
++ req->req.actual, req->req.length, len, req->req.actual + len);
++
++ req->req.actual += len;
++
++ udelay(5);
++ writesb(base_addr + fifo, buf, len);
++ return len;
++}
++
++/*
++ * s3c2410_udc_write_fifo
++ *
++ * return: 0 = still running, 1 = completed, negative = errno
++ */
++static int s3c2410_udc_write_fifo(struct s3c2410_ep *ep,
++ struct s3c2410_request *req)
++{
++ unsigned count;
++ int is_last;
++ u32 idx;
++ int fifo_reg;
++ u32 ep_csr;
++
++ idx = ep->bEndpointAddress & 0x7F;
++ switch (idx) {
++ default:
++ idx = 0;
++ case 0:
++ fifo_reg = S3C2410_UDC_EP0_FIFO_REG;
++ break;
++ case 1:
++ fifo_reg = S3C2410_UDC_EP1_FIFO_REG;
++ break;
++ case 2:
++ fifo_reg = S3C2410_UDC_EP2_FIFO_REG;
++ break;
++ case 3:
++ fifo_reg = S3C2410_UDC_EP3_FIFO_REG;
++ break;
++ case 4:
++ fifo_reg = S3C2410_UDC_EP4_FIFO_REG;
++ break;
++ }
++
++ count = s3c2410_udc_write_packet(fifo_reg, req, ep->ep.maxpacket);
++
++ /* last packet is often short (sometimes a zlp) */
++ if (count != ep->ep.maxpacket)
++ is_last = 1;
++ else if (req->req.length != req->req.actual || req->req.zero)
++ is_last = 0;
++ else
++ is_last = 2;
++
++ /* Only ep0 debug messages are interesting */
++ if (idx == 0)
++ dprintk(DEBUG_NORMAL,
++ "Written ep%d %d.%d of %d b [last %d,z %d]\n",
++ idx, count, req->req.actual, req->req.length,
++ is_last, req->req.zero);
++
++ if (is_last) {
++ /* The order is important. It prevents sending 2 packets
++ * at the same time */
++
++ if (idx == 0) {
++ /* Reset signal => no need to say 'data sent' */
++ if (! (udc_read(S3C2410_UDC_USB_INT_REG)
++ & S3C2410_UDC_USBINT_RESET))
++ s3c2410_udc_set_ep0_de_in(base_addr);
++ ep->dev->ep0state=EP0_IDLE;
++ } else {
++ udc_write(idx, S3C2410_UDC_INDEX_REG);
++ ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
++ udc_write(idx, S3C2410_UDC_INDEX_REG);
++ udc_write(ep_csr | S3C2410_UDC_ICSR1_PKTRDY,
++ S3C2410_UDC_IN_CSR1_REG);
++ }
++
++ s3c2410_udc_done(ep, req, 0);
++ is_last = 1;
++ } else {
++ if (idx == 0) {
++ /* Reset signal => no need to say 'data sent' */
++ if (! (udc_read(S3C2410_UDC_USB_INT_REG)
++ & S3C2410_UDC_USBINT_RESET))
++ s3c2410_udc_set_ep0_ipr(base_addr);
++ } else {
++ udc_write(idx, S3C2410_UDC_INDEX_REG);
++ ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
++ udc_write(idx, S3C2410_UDC_INDEX_REG);
++ udc_write(ep_csr | S3C2410_UDC_ICSR1_PKTRDY,
++ S3C2410_UDC_IN_CSR1_REG);
++ }
++ }
++
++ return is_last;
++}
++
++static inline int s3c2410_udc_read_packet(int fifo, u8 *buf,
++ struct s3c2410_request *req, unsigned avail)
++{
++ unsigned len;
++
++ len = min(req->req.length - req->req.actual, avail);
++ req->req.actual += len;
++
++ readsb(fifo + base_addr, buf, len);
++ return len;
++}
++
++/*
++ * return: 0 = still running, 1 = queue empty, negative = errno
++ */
++static int s3c2410_udc_read_fifo(struct s3c2410_ep *ep,
++ struct s3c2410_request *req)
++{
++ u8 *buf;
++ u32 ep_csr;
++ unsigned bufferspace;
++ int is_last=1;
++ unsigned avail;
++ int fifo_count = 0;
++ u32 idx;
++ int fifo_reg;
++
++ idx = ep->bEndpointAddress & 0x7F;
++
++ switch (idx) {
++ default:
++ idx = 0;
++ case 0:
++ fifo_reg = S3C2410_UDC_EP0_FIFO_REG;
++ break;
++ case 1:
++ fifo_reg = S3C2410_UDC_EP1_FIFO_REG;
++ break;
++ case 2:
++ fifo_reg = S3C2410_UDC_EP2_FIFO_REG;
++ break;
++ case 3:
++ fifo_reg = S3C2410_UDC_EP3_FIFO_REG;
++ break;
++ case 4:
++ fifo_reg = S3C2410_UDC_EP4_FIFO_REG;
++ break;
++ }
++
++ if (!req->req.length)
++ return 1;
++
++ buf = req->req.buf + req->req.actual;
++ bufferspace = req->req.length - req->req.actual;
++ if (!bufferspace) {
++ dprintk(DEBUG_NORMAL, "%s: buffer full!\n", __func__);
++ return -1;
++ }
++
++ udc_write(idx, S3C2410_UDC_INDEX_REG);
++
++ fifo_count = s3c2410_udc_fifo_count_out();
++ dprintk(DEBUG_NORMAL, "%s fifo count : %d\n", __func__, fifo_count);
++
++ if (fifo_count > ep->ep.maxpacket)
++ avail = ep->ep.maxpacket;
++ else
++ avail = fifo_count;
++
++ fifo_count = s3c2410_udc_read_packet(fifo_reg, buf, req, avail);
++
++ /* checking this with ep0 is not accurate as we already
++ * read a control request
++ **/
++ if (idx != 0 && fifo_count < ep->ep.maxpacket) {
++ is_last = 1;
++ /* overflowed this request? flush extra data */
++ if (fifo_count != avail)
++ req->req.status = -EOVERFLOW;
++ } else {
++ is_last = (req->req.length <= req->req.actual) ? 1 : 0;
++ }
++
++ udc_write(idx, S3C2410_UDC_INDEX_REG);
++ fifo_count = s3c2410_udc_fifo_count_out();
++
++ /* Only ep0 debug messages are interesting */
++ if (idx == 0)
++ dprintk(DEBUG_VERBOSE, "%s fifo count : %d [last %d]\n",
++ __func__, fifo_count,is_last);
++
++ if (is_last) {
++ if (idx == 0) {
++ s3c2410_udc_set_ep0_de_out(base_addr);
++ ep->dev->ep0state = EP0_IDLE;
++ } else {
++ udc_write(idx, S3C2410_UDC_INDEX_REG);
++ ep_csr = udc_read(S3C2410_UDC_OUT_CSR1_REG);
++ udc_write(idx, S3C2410_UDC_INDEX_REG);
++ udc_write(ep_csr & ~S3C2410_UDC_OCSR1_PKTRDY,
++ S3C2410_UDC_OUT_CSR1_REG);
++ }
++
++ s3c2410_udc_done(ep, req, 0);
++ } else {
++ if (idx == 0) {
++ s3c2410_udc_clear_ep0_opr(base_addr);
++ } else {
++ udc_write(idx, S3C2410_UDC_INDEX_REG);
++ ep_csr = udc_read(S3C2410_UDC_OUT_CSR1_REG);
++ udc_write(idx, S3C2410_UDC_INDEX_REG);
++ udc_write(ep_csr & ~S3C2410_UDC_OCSR1_PKTRDY,
++ S3C2410_UDC_OUT_CSR1_REG);
++ }
++ }
++
++ return is_last;
++}
++
++static int s3c2410_udc_read_fifo_crq(struct usb_ctrlrequest *crq)
++{
++ unsigned char *outbuf = (unsigned char*)crq;
++ int bytes_read = 0;
++
++ udc_write(0, S3C2410_UDC_INDEX_REG);
++
++ bytes_read = s3c2410_udc_fifo_count_out();
++
++ dprintk(DEBUG_NORMAL, "%s: fifo_count=%d\n", __func__, bytes_read);
++
++ if (bytes_read > sizeof(struct usb_ctrlrequest))
++ bytes_read = sizeof(struct usb_ctrlrequest);
++
++ readsb(S3C2410_UDC_EP0_FIFO_REG + base_addr, outbuf, bytes_read);
++
++ dprintk(DEBUG_VERBOSE, "%s: len=%d %02x:%02x {%x,%x,%x}\n", __func__,
++ bytes_read, crq->bRequest, crq->bRequestType,
++ crq->wValue, crq->wIndex, crq->wLength);
++
++ return bytes_read;
++}
++
++static int s3c2410_udc_get_status(struct s3c2410_udc *dev,
++ struct usb_ctrlrequest *crq)
++{
++ u16 status = 0;
++ u8 ep_num = crq->wIndex & 0x7F;
++ u8 is_in = crq->wIndex & USB_DIR_IN;
++
++ switch (crq->bRequestType & USB_RECIP_MASK) {
++ case USB_RECIP_INTERFACE:
++ break;
++
++ case USB_RECIP_DEVICE:
++ status = dev->devstatus;
++ break;
++
++ case USB_RECIP_ENDPOINT:
++ if (ep_num > 4 || crq->wLength > 2)
++ return 1;
++
++ if (ep_num == 0) {
++ udc_write(0, S3C2410_UDC_INDEX_REG);
++ status = udc_read(S3C2410_UDC_IN_CSR1_REG);
++ status = status & S3C2410_UDC_EP0_CSR_SENDSTL;
++ } else {
++ udc_write(ep_num, S3C2410_UDC_INDEX_REG);
++ if (is_in) {
++ status = udc_read(S3C2410_UDC_IN_CSR1_REG);
++ status = status & S3C2410_UDC_ICSR1_SENDSTL;
++ } else {
++ status = udc_read(S3C2410_UDC_OUT_CSR1_REG);
++ status = status & S3C2410_UDC_OCSR1_SENDSTL;
++ }
++ }
++
++ status = status ? 1 : 0;
++ break;
++
++ default:
++ return 1;
++ }
++
++ /* Seems to be needed to get it working. ouch :( */
++ udelay(5);
++ udc_write(status & 0xFF, S3C2410_UDC_EP0_FIFO_REG);
++ udc_write(status >> 8, S3C2410_UDC_EP0_FIFO_REG);
++ s3c2410_udc_set_ep0_de_in(base_addr);
++
++ return 0;
++}
++/*------------------------- usb state machine -------------------------------*/
++static int s3c2410_udc_set_halt(struct usb_ep *_ep, int value);
++
++static void s3c2410_udc_handle_ep0_idle(struct s3c2410_udc *dev,
++ struct s3c2410_ep *ep,
++ struct usb_ctrlrequest *crq,
++ u32 ep0csr)
++{
++ int len, ret, tmp;
++
++ /* start control request? */
++ if (!(ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY))
++ return;
++
++ s3c2410_udc_nuke(dev, ep, -EPROTO);
++
++ len = s3c2410_udc_read_fifo_crq(crq);
++ if (len != sizeof(*crq)) {
++ dprintk(DEBUG_NORMAL, "setup begin: fifo READ ERROR"
++ " wanted %d bytes got %d. Stalling out...\n",
++ sizeof(*crq), len);
++ s3c2410_udc_set_ep0_ss(base_addr);
++ return;
++ }
++
++ dprintk(DEBUG_NORMAL, "bRequest = %d bRequestType %d wLength = %d\n",
++ crq->bRequest, crq->bRequestType, crq->wLength);
++
++ /* cope with automagic for some standard requests. */
++ dev->req_std = (crq->bRequestType & USB_TYPE_MASK)
++ == USB_TYPE_STANDARD;
++ dev->req_config = 0;
++ dev->req_pending = 1;
++
++ switch (crq->bRequest) {
++ case USB_REQ_SET_CONFIGURATION:
++ dprintk(DEBUG_NORMAL, "USB_REQ_SET_CONFIGURATION ... \n");
++
++ if (crq->bRequestType == USB_RECIP_DEVICE) {
++ dev->req_config = 1;
++ s3c2410_udc_set_ep0_de_out(base_addr);
++ }
++ break;
++
++ case USB_REQ_SET_INTERFACE:
++ dprintk(DEBUG_NORMAL, "USB_REQ_SET_INTERFACE ... \n");
++
++ if (crq->bRequestType == USB_RECIP_INTERFACE) {
++ dev->req_config = 1;
++ s3c2410_udc_set_ep0_de_out(base_addr);
++ }
++ break;
++
++ case USB_REQ_SET_ADDRESS:
++ dprintk(DEBUG_NORMAL, "USB_REQ_SET_ADDRESS ... \n");
++
++ if (crq->bRequestType == USB_RECIP_DEVICE) {
++ tmp = crq->wValue & 0x7F;
++ dev->address = tmp;
++ udc_write((tmp | S3C2410_UDC_FUNCADDR_UPDATE),
++ S3C2410_UDC_FUNC_ADDR_REG);
++ s3c2410_udc_set_ep0_de_out(base_addr);
++ return;
++ }
++ break;
++
++ case USB_REQ_GET_STATUS:
++ dprintk(DEBUG_NORMAL, "USB_REQ_GET_STATUS ... \n");
++ s3c2410_udc_clear_ep0_opr(base_addr);
++
++ if (dev->req_std) {
++ if (!s3c2410_udc_get_status(dev, crq)) {
++ return;
++ }
++ }
++ break;
++
++ case USB_REQ_CLEAR_FEATURE:
++ s3c2410_udc_clear_ep0_opr(base_addr);
++
++ if (crq->bRequestType != USB_RECIP_ENDPOINT)
++ break;
++
++ if (crq->wValue != USB_ENDPOINT_HALT || crq->wLength != 0)
++ break;
++
++ s3c2410_udc_set_halt(&dev->ep[crq->wIndex & 0x7f].ep, 0);
++ s3c2410_udc_set_ep0_de_out(base_addr);
++ return;
++
++ case USB_REQ_SET_FEATURE:
++ s3c2410_udc_clear_ep0_opr(base_addr);
++
++ if (crq->bRequestType != USB_RECIP_ENDPOINT)
++ break;
++
++ if (crq->wValue != USB_ENDPOINT_HALT || crq->wLength != 0)
++ break;
++
++ s3c2410_udc_set_halt(&dev->ep[crq->wIndex & 0x7f].ep, 1);
++ s3c2410_udc_set_ep0_de_out(base_addr);
++ return;
++
++ default:
++ s3c2410_udc_clear_ep0_opr(base_addr);
++ break;
++ }
++
++ if (crq->bRequestType & USB_DIR_IN)
++ dev->ep0state = EP0_IN_DATA_PHASE;
++ else
++ dev->ep0state = EP0_OUT_DATA_PHASE;
++
++ ret = dev->driver->setup(&dev->gadget, crq);
++ if (ret < 0) {
++ if (dev->req_config) {
++ dprintk(DEBUG_NORMAL, "config change %02x fail %d?\n",
++ crq->bRequest, ret);
++ return;
++ }
++
++ if (ret == -EOPNOTSUPP)
++ dprintk(DEBUG_NORMAL, "Operation not supported\n");
++ else
++ dprintk(DEBUG_NORMAL,
++ "dev->driver->setup failed. (%d)\n", ret);
++
++ udelay(5);
++ s3c2410_udc_set_ep0_ss(base_addr);
++ s3c2410_udc_set_ep0_de_out(base_addr);
++ dev->ep0state = EP0_IDLE;
++ /* deferred i/o == no response yet */
++ } else if (dev->req_pending) {
++ dprintk(DEBUG_VERBOSE, "dev->req_pending... what now?\n");
++ dev->req_pending=0;
++ }
++
++ dprintk(DEBUG_VERBOSE, "ep0state %s\n", ep0states[dev->ep0state]);
++}
++
++static void s3c2410_udc_handle_ep0(struct s3c2410_udc *dev)
++{
++ u32 ep0csr;
++ struct s3c2410_ep *ep = &dev->ep[0];
++ struct s3c2410_request *req;
++ struct usb_ctrlrequest crq;
++
++ if (list_empty(&ep->queue))
++ req = NULL;
++ else
++ req = list_entry(ep->queue.next, struct s3c2410_request, queue);
++
++ /* We make the assumption that S3C2410_UDC_IN_CSR1_REG equal to
++ * S3C2410_UDC_EP0_CSR_REG when index is zero */
++
++ udc_write(0, S3C2410_UDC_INDEX_REG);
++ ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
++
++ dprintk(DEBUG_NORMAL, "ep0csr %x ep0state %s\n",
++ ep0csr, ep0states[dev->ep0state]);
++
++ /* clear stall status */
++ if (ep0csr & S3C2410_UDC_EP0_CSR_SENTSTL) {
++ s3c2410_udc_nuke(dev, ep, -EPIPE);
++ dprintk(DEBUG_NORMAL, "... clear SENT_STALL ...\n");
++ s3c2410_udc_clear_ep0_sst(base_addr);
++ dev->ep0state = EP0_IDLE;
++ return;
++ }
++
++ /* clear setup end */
++ if (ep0csr & S3C2410_UDC_EP0_CSR_SE) {
++ dprintk(DEBUG_NORMAL, "... serviced SETUP_END ...\n");
++ s3c2410_udc_nuke(dev, ep, 0);
++ s3c2410_udc_clear_ep0_se(base_addr);
++ dev->ep0state = EP0_IDLE;
++ }
++
++ switch (dev->ep0state) {
++ case EP0_IDLE:
++ s3c2410_udc_handle_ep0_idle(dev, ep, &crq, ep0csr);
++ break;
++
++ case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */
++ dprintk(DEBUG_NORMAL, "EP0_IN_DATA_PHASE ... what now?\n");
++ if (!(ep0csr & S3C2410_UDC_EP0_CSR_IPKRDY) && req) {
++ s3c2410_udc_write_fifo(ep, req);
++ }
++ break;
++
++ case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */
++ dprintk(DEBUG_NORMAL, "EP0_OUT_DATA_PHASE ... what now?\n");
++ if ((ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY) && req ) {
++ s3c2410_udc_read_fifo(ep,req);
++ }
++ break;
++
++ case EP0_END_XFER:
++ dprintk(DEBUG_NORMAL, "EP0_END_XFER ... what now?\n");
++ dev->ep0state = EP0_IDLE;
++ break;
++
++ case EP0_STALL:
++ dprintk(DEBUG_NORMAL, "EP0_STALL ... what now?\n");
++ dev->ep0state = EP0_IDLE;
++ break;
++ }
++}
++
++/*
++ * handle_ep - Manage I/O endpoints
++ */
++
++static void s3c2410_udc_handle_ep(struct s3c2410_ep *ep)
++{
++ struct s3c2410_request *req;
++ int is_in = ep->bEndpointAddress & USB_DIR_IN;
++ u32 ep_csr1;
++ u32 idx;
++
++ if (likely (!list_empty(&ep->queue)))
++ req = list_entry(ep->queue.next,
++ struct s3c2410_request, queue);
++ else
++ req = NULL;
++
++ idx = ep->bEndpointAddress & 0x7F;
++
++ if (is_in) {
++ udc_write(idx, S3C2410_UDC_INDEX_REG);
++ ep_csr1 = udc_read(S3C2410_UDC_IN_CSR1_REG);
++ dprintk(DEBUG_VERBOSE, "ep%01d write csr:%02x %d\n",
++ idx, ep_csr1, req ? 1 : 0);
++
++ if (ep_csr1 & S3C2410_UDC_ICSR1_SENTSTL) {
++ dprintk(DEBUG_VERBOSE, "st\n");
++ udc_write(idx, S3C2410_UDC_INDEX_REG);
++ udc_write(ep_csr1 & ~S3C2410_UDC_ICSR1_SENTSTL,
++ S3C2410_UDC_IN_CSR1_REG);
++ return;
++ }
++
++ if (!(ep_csr1 & S3C2410_UDC_ICSR1_PKTRDY) && req) {
++ s3c2410_udc_write_fifo(ep,req);
++ }
++ } else {
++ udc_write(idx, S3C2410_UDC_INDEX_REG);
++ ep_csr1 = udc_read(S3C2410_UDC_OUT_CSR1_REG);
++ dprintk(DEBUG_VERBOSE, "ep%01d rd csr:%02x\n", idx, ep_csr1);
++
++ if (ep_csr1 & S3C2410_UDC_OCSR1_SENTSTL) {
++ udc_write(idx, S3C2410_UDC_INDEX_REG);
++ udc_write(ep_csr1 & ~S3C2410_UDC_OCSR1_SENTSTL,
++ S3C2410_UDC_OUT_CSR1_REG);
++ return;
++ }
++
++ if ((ep_csr1 & S3C2410_UDC_OCSR1_PKTRDY) && req) {
++ s3c2410_udc_read_fifo(ep,req);
++ }
++ }
++}
++
++#include <asm/arch/regs-irq.h>
++
++/*
++ * s3c2410_udc_irq - interrupt handler
++ */
++static irqreturn_t s3c2410_udc_irq(int irq, void *_dev)
++{
++ struct s3c2410_udc *dev = _dev;
++ int usb_status;
++ int usbd_status;
++ int pwr_reg;
++ int ep0csr;
++ int i;
++ u32 idx;
++ unsigned long flags;
++
++ spin_lock_irqsave(&dev->lock, flags);
++
++ /* Driver connected ? */
++ if (!dev->driver) {
++ /* Clear interrupts */
++ udc_write(udc_read(S3C2410_UDC_USB_INT_REG),
++ S3C2410_UDC_USB_INT_REG);
++ udc_write(udc_read(S3C2410_UDC_EP_INT_REG),
++ S3C2410_UDC_EP_INT_REG);
++ }
++
++ /* Save index */
++ idx = udc_read(S3C2410_UDC_INDEX_REG);
++
++ /* Read status registers */
++ usb_status = udc_read(S3C2410_UDC_USB_INT_REG);
++ usbd_status = udc_read(S3C2410_UDC_EP_INT_REG);
++ pwr_reg = udc_read(S3C2410_UDC_PWR_REG);
++
++ udc_writeb(base_addr, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
++ ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
++
++ dprintk(DEBUG_NORMAL, "usbs=%02x, usbds=%02x, pwr=%02x ep0csr=%02x\n",
++ usb_status, usbd_status, pwr_reg, ep0csr);
++
++ /*
++ * Now, handle interrupts. There's two types :
++ * - Reset, Resume, Suspend coming -> usb_int_reg
++ * - EP -> ep_int_reg
++ */
++
++ /* RESET */
++ if (usb_status & S3C2410_UDC_USBINT_RESET) {
++ /* two kind of reset :
++ * - reset start -> pwr reg = 8
++ * - reset end -> pwr reg = 0
++ **/
++ dprintk(DEBUG_NORMAL, "USB reset csr %x pwr %x\n",
++ ep0csr, pwr_reg);
++
++ dev->gadget.speed = USB_SPEED_UNKNOWN;
++ udc_write(0x00, S3C2410_UDC_INDEX_REG);
++ udc_write((dev->ep[0].ep.maxpacket & 0x7ff) >> 3,
++ S3C2410_UDC_MAXP_REG);
++ dev->address = 0;
++
++ dev->ep0state = EP0_IDLE;
++ dev->gadget.speed = USB_SPEED_FULL;
++
++ /* clear interrupt */
++ udc_write(S3C2410_UDC_USBINT_RESET,
++ S3C2410_UDC_USB_INT_REG);
++
++ udc_write(idx, S3C2410_UDC_INDEX_REG);
++ spin_unlock_irqrestore(&dev->lock, flags);
++ return IRQ_HANDLED;
++ }
++
++ /* RESUME */
++ if (usb_status & S3C2410_UDC_USBINT_RESUME) {
++ dprintk(DEBUG_NORMAL, "USB resume\n");
++
++ /* clear interrupt */
++ udc_write(S3C2410_UDC_USBINT_RESUME,
++ S3C2410_UDC_USB_INT_REG);
++
++ if (dev->gadget.speed != USB_SPEED_UNKNOWN
++ && dev->driver
++ && dev->driver->resume)
++ dev->driver->resume(&dev->gadget);
++ }
++
++ /* SUSPEND */
++ if (usb_status & S3C2410_UDC_USBINT_SUSPEND) {
++ dprintk(DEBUG_NORMAL, "USB suspend\n");
++
++ /* clear interrupt */
++ udc_write(S3C2410_UDC_USBINT_SUSPEND,
++ S3C2410_UDC_USB_INT_REG);
++
++ if (dev->gadget.speed != USB_SPEED_UNKNOWN
++ && dev->driver
++ && dev->driver->suspend)
++ dev->driver->suspend(&dev->gadget);
++
++ dev->ep0state = EP0_IDLE;
++ }
++
++ /* EP */
++ /* control traffic */
++ /* check on ep0csr != 0 is not a good idea as clearing in_pkt_ready
++ * generate an interrupt
++ */
++ if (usbd_status & S3C2410_UDC_INT_EP0) {
++ dprintk(DEBUG_VERBOSE, "USB ep0 irq\n");
++ /* Clear the interrupt bit by setting it to 1 */
++ udc_write(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_REG);
++ s3c2410_udc_handle_ep0(dev);
++ }
++
++ /* endpoint data transfers */
++ for (i = 1; i < S3C2410_ENDPOINTS; i++) {
++ u32 tmp = 1 << i;
++ if (usbd_status & tmp) {
++ dprintk(DEBUG_VERBOSE, "USB ep%d irq\n", i);
++
++ /* Clear the interrupt bit by setting it to 1 */
++ udc_write(tmp, S3C2410_UDC_EP_INT_REG);
++ s3c2410_udc_handle_ep(&dev->ep[i]);
++ }
++ }
++
++ dprintk(DEBUG_VERBOSE, "irq: %d s3c2410_udc_done.\n", irq);
++
++ /* Restore old index */
++ udc_write(idx, S3C2410_UDC_INDEX_REG);
++
++ spin_unlock_irqrestore(&dev->lock, flags);
++
++ return IRQ_HANDLED;
++}
++/*------------------------- s3c2410_ep_ops ----------------------------------*/
++
++static inline struct s3c2410_ep *to_s3c2410_ep(struct usb_ep *ep)
++{
++ return container_of(ep, struct s3c2410_ep, ep);
++}
++
++static inline struct s3c2410_udc *to_s3c2410_udc(struct usb_gadget *gadget)
++{
++ return container_of(gadget, struct s3c2410_udc, gadget);
++}
++
++static inline struct s3c2410_request *to_s3c2410_req(struct usb_request *req)
++{
++ return container_of(req, struct s3c2410_request, req);
++}
++
++/*
++ * s3c2410_udc_ep_enable
++ */
++static int s3c2410_udc_ep_enable(struct usb_ep *_ep,
++ const struct usb_endpoint_descriptor *desc)
++{
++ struct s3c2410_udc *dev;
++ struct s3c2410_ep *ep;
++ u32 max, tmp;
++ unsigned long flags;
++ u32 csr1,csr2;
++ u32 int_en_reg;
++
++ ep = to_s3c2410_ep(_ep);
++
++ if (!_ep || !desc || ep->desc
++ || _ep->name == ep0name
++ || desc->bDescriptorType != USB_DT_ENDPOINT)
++ return -EINVAL;
++
++ dev = ep->dev;
++ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
++ return -ESHUTDOWN;
++
++ max = le16_to_cpu(desc->wMaxPacketSize) & 0x1fff;
++
++ local_irq_save (flags);
++ _ep->maxpacket = max & 0x7ff;
++ ep->desc = desc;
++ ep->halted = 0;
++ ep->bEndpointAddress = desc->bEndpointAddress;
++
++ /* set max packet */
++ udc_write(ep->num, S3C2410_UDC_INDEX_REG);
++ udc_write(max >> 3, S3C2410_UDC_MAXP_REG);
++
++ /* set type, direction, address; reset fifo counters */
++ if (desc->bEndpointAddress & USB_DIR_IN) {
++ csr1 = S3C2410_UDC_ICSR1_FFLUSH|S3C2410_UDC_ICSR1_CLRDT;
++ csr2 = S3C2410_UDC_ICSR2_MODEIN|S3C2410_UDC_ICSR2_DMAIEN;
++
++ udc_write(ep->num, S3C2410_UDC_INDEX_REG);
++ udc_write(csr1, S3C2410_UDC_IN_CSR1_REG);
++ udc_write(ep->num, S3C2410_UDC_INDEX_REG);
++ udc_write(csr2, S3C2410_UDC_IN_CSR2_REG);
++ } else {
++ /* don't flush in fifo or it will cause endpoint interrupt */
++ csr1 = S3C2410_UDC_ICSR1_CLRDT;
++ csr2 = S3C2410_UDC_ICSR2_DMAIEN;
++
++ udc_write(ep->num, S3C2410_UDC_INDEX_REG);
++ udc_write(csr1, S3C2410_UDC_IN_CSR1_REG);
++ udc_write(ep->num, S3C2410_UDC_INDEX_REG);
++ udc_write(csr2, S3C2410_UDC_IN_CSR2_REG);
++
++ csr1 = S3C2410_UDC_OCSR1_FFLUSH | S3C2410_UDC_OCSR1_CLRDT;
++ csr2 = S3C2410_UDC_OCSR2_DMAIEN;
++
++ udc_write(ep->num, S3C2410_UDC_INDEX_REG);
++ udc_write(csr1, S3C2410_UDC_OUT_CSR1_REG);
++ udc_write(ep->num, S3C2410_UDC_INDEX_REG);
++ udc_write(csr2, S3C2410_UDC_OUT_CSR2_REG);
++ }
++
++ /* enable irqs */
++ int_en_reg = udc_read(S3C2410_UDC_EP_INT_EN_REG);
++ udc_write(int_en_reg | (1 << ep->num), S3C2410_UDC_EP_INT_EN_REG);
++
++ /* print some debug message */
++ tmp = desc->bEndpointAddress;
++ dprintk (DEBUG_NORMAL, "enable %s(%d) ep%x%s-blk max %02x\n",
++ _ep->name,ep->num, tmp,
++ desc->bEndpointAddress & USB_DIR_IN ? "in" : "out", max);
++
++ local_irq_restore (flags);
++ s3c2410_udc_set_halt(_ep, 0);
++
++ return 0;
++}
++
++/*
++ * s3c2410_udc_ep_disable
++ */
++static int s3c2410_udc_ep_disable(struct usb_ep *_ep)
++{
++ struct s3c2410_ep *ep = to_s3c2410_ep(_ep);
++ unsigned long flags;
++ u32 int_en_reg;
++
++ if (!_ep || !ep->desc) {
++ dprintk(DEBUG_NORMAL, "%s not enabled\n",
++ _ep ? ep->ep.name : NULL);
++ return -EINVAL;
++ }
++
++ local_irq_save(flags);
++
++ dprintk(DEBUG_NORMAL, "ep_disable: %s\n", _ep->name);
++
++ ep->desc = NULL;
++ ep->halted = 1;
++
++ s3c2410_udc_nuke (ep->dev, ep, -ESHUTDOWN);
++
++ /* disable irqs */
++ int_en_reg = udc_read(S3C2410_UDC_EP_INT_EN_REG);
++ udc_write(int_en_reg & ~(1<<ep->num), S3C2410_UDC_EP_INT_EN_REG);
++
++ local_irq_restore(flags);
++
++ dprintk(DEBUG_NORMAL, "%s disabled\n", _ep->name);
++
++ return 0;
++}
++
++/*
++ * s3c2410_udc_alloc_request
++ */
++static struct usb_request *
++s3c2410_udc_alloc_request(struct usb_ep *_ep, gfp_t mem_flags)
++{
++ struct s3c2410_request *req;
++
++ dprintk(DEBUG_VERBOSE,"%s(%p,%d)\n", __func__, _ep, mem_flags);
++
++ if (!_ep)
++ return NULL;
++
++ req = kzalloc (sizeof(struct s3c2410_request), mem_flags);
++ if (!req)
++ return NULL;
++
++ INIT_LIST_HEAD (&req->queue);
++ return &req->req;
++}
++
++/*
++ * s3c2410_udc_free_request
++ */
++static void
++s3c2410_udc_free_request(struct usb_ep *_ep, struct usb_request *_req)
++{
++ struct s3c2410_ep *ep = to_s3c2410_ep(_ep);
++ struct s3c2410_request *req = to_s3c2410_req(_req);
++
++ dprintk(DEBUG_VERBOSE, "%s(%p,%p)\n", __func__, _ep, _req);
++
++ if (!ep || !_req || (!ep->desc && _ep->name != ep0name))
++ return;
++
++ WARN_ON (!list_empty (&req->queue));
++ kfree(req);
++}
++
++/*
++ * s3c2410_udc_alloc_buffer
++ */
++static void *s3c2410_udc_alloc_buffer(struct usb_ep *_ep,
++ unsigned bytes, dma_addr_t *dma, gfp_t mem_flags)
++{
++ char *retval;
++
++ dprintk(DEBUG_VERBOSE, "%s()\n", __func__);
++
++ if (!the_controller->driver)
++ return NULL;
++
++ retval = kmalloc (bytes, mem_flags);
++ *dma = (dma_addr_t) retval;
++ return retval;
++}
++
++/*
++ * s3c2410_udc_free_buffer
++ */
++static void s3c2410_udc_free_buffer (struct usb_ep *_ep, void *buf,
++ dma_addr_t dma, unsigned bytes)
++{
++ dprintk(DEBUG_VERBOSE, "%s()\n", __func__);
++
++ if (bytes)
++ kfree (buf);
++}
++
++/*
++ * s3c2410_udc_queue
++ */
++static int s3c2410_udc_queue(struct usb_ep *_ep, struct usb_request *_req,
++ gfp_t gfp_flags)
++{
++ struct s3c2410_request *req = to_s3c2410_req(_req);
++ struct s3c2410_ep *ep = to_s3c2410_ep(_ep);
++ struct s3c2410_udc *dev;
++ u32 ep_csr = 0;
++ int fifo_count = 0;
++ unsigned long flags;
++
++ if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) {
++ dprintk(DEBUG_NORMAL, "%s: invalid args\n", __func__);
++ return -EINVAL;
++ }
++
++ dev = ep->dev;
++ if (unlikely (!dev->driver
++ || dev->gadget.speed == USB_SPEED_UNKNOWN)) {
++ return -ESHUTDOWN;
++ }
++
++ local_irq_save (flags);
++
++ if (unlikely(!_req || !_req->complete
++ || !_req->buf || !list_empty(&req->queue))) {
++ if (!_req)
++ dprintk(DEBUG_NORMAL, "%s: 1 X X X\n", __func__);
++ else {
++ dprintk(DEBUG_NORMAL, "%s: 0 %01d %01d %01d\n",
++ __func__, !_req->complete,!_req->buf,
++ !list_empty(&req->queue));
++ }
++
++ local_irq_restore(flags);
++ return -EINVAL;
++ }
++
++ _req->status = -EINPROGRESS;
++ _req->actual = 0;
++
++ dprintk(DEBUG_VERBOSE, "%s: ep%x len %d\n",
++ __func__, ep->bEndpointAddress, _req->length);
++
++ if (ep->bEndpointAddress) {
++ udc_write(ep->bEndpointAddress & 0x7F, S3C2410_UDC_INDEX_REG);
++
++ ep_csr = udc_read((ep->bEndpointAddress & USB_DIR_IN)
++ ? S3C2410_UDC_IN_CSR1_REG
++ : S3C2410_UDC_OUT_CSR1_REG);
++ fifo_count = s3c2410_udc_fifo_count_out();
++ } else {
++ udc_write(0, S3C2410_UDC_INDEX_REG);
++ ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
++ fifo_count = s3c2410_udc_fifo_count_out();
++ }
++
++ /* kickstart this i/o queue? */
++ if (list_empty(&ep->queue) && !ep->halted) {
++ if (ep->bEndpointAddress == 0 /* ep0 */) {
++ switch (dev->ep0state) {
++ case EP0_IN_DATA_PHASE:
++ if (!(ep_csr&S3C2410_UDC_EP0_CSR_IPKRDY)
++ && s3c2410_udc_write_fifo(ep,
++ req)) {
++ dev->ep0state = EP0_IDLE;
++ req = NULL;
++ }
++ break;
++
++ case EP0_OUT_DATA_PHASE:
++ if ((!_req->length)
++ || ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY)
++ && s3c2410_udc_read_fifo(ep,
++ req))) {
++ dev->ep0state = EP0_IDLE;
++ req = NULL;
++ }
++ break;
++
++ default:
++ local_irq_restore(flags);
++ return -EL2HLT;
++ }
++ } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0
++ && (!(ep_csr&S3C2410_UDC_OCSR1_PKTRDY))
++ && s3c2410_udc_write_fifo(ep, req)) {
++ req = NULL;
++ } else if ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY)
++ && fifo_count
++ && s3c2410_udc_read_fifo(ep, req)) {
++ req = NULL;
++ }
++ }
++
++ /* pio or dma irq handler advances the queue. */
++ if (likely (req != 0))
++ list_add_tail(&req->queue, &ep->queue);
++
++ local_irq_restore(flags);
++
++ dprintk(DEBUG_VERBOSE, "%s ok\n", __func__);
++ return 0;
++}
++
++/*
++ * s3c2410_udc_dequeue
++ */
++static int s3c2410_udc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
++{
++ struct s3c2410_ep *ep = to_s3c2410_ep(_ep);
++ struct s3c2410_udc *udc;
++ int retval = -EINVAL;
++ unsigned long flags;
++ struct s3c2410_request *req = NULL;
++
++ dprintk(DEBUG_VERBOSE, "%s(%p,%p)\n", __func__, _ep, _req);
++
++ if (!the_controller->driver)
++ return -ESHUTDOWN;
++
++ if (!_ep || !_req)
++ return retval;
++
++ udc = to_s3c2410_udc(ep->gadget);
++
++ local_irq_save (flags);
++
++ list_for_each_entry (req, &ep->queue, queue) {
++ if (&req->req == _req) {
++ list_del_init (&req->queue);
++ _req->status = -ECONNRESET;
++ retval = 0;
++ break;
++ }
++ }
++
++ if (retval == 0) {
++ dprintk(DEBUG_VERBOSE,
++ "dequeued req %p from %s, len %d buf %p\n",
++ req, _ep->name, _req->length, _req->buf);
++
++ s3c2410_udc_done(ep, req, -ECONNRESET);
++ }
++
++ local_irq_restore (flags);
++ return retval;
++}
++
++/*
++ * s3c2410_udc_set_halt
++ */
++static int s3c2410_udc_set_halt(struct usb_ep *_ep, int value)
++{
++ struct s3c2410_ep *ep = to_s3c2410_ep(_ep);
++ u32 ep_csr = 0;
++ unsigned long flags;
++ u32 idx;
++
++ if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) {
++ dprintk(DEBUG_NORMAL, "%s: inval 2\n", __func__);
++ return -EINVAL;
++ }
++
++ local_irq_save (flags);
++
++ idx = ep->bEndpointAddress & 0x7F;
++
++ if (idx == 0) {
++ s3c2410_udc_set_ep0_ss(base_addr);
++ s3c2410_udc_set_ep0_de_out(base_addr);
++ } else {
++ udc_write(idx, S3C2410_UDC_INDEX_REG);
++ ep_csr = udc_read((ep->bEndpointAddress &USB_DIR_IN)
++ ? S3C2410_UDC_IN_CSR1_REG
++ : S3C2410_UDC_OUT_CSR1_REG);
++
++ if ((ep->bEndpointAddress & USB_DIR_IN) != 0) {
++ if (value)
++ udc_write(ep_csr | S3C2410_UDC_ICSR1_SENDSTL,
++ S3C2410_UDC_IN_CSR1_REG);
++ else {
++ ep_csr &= ~S3C2410_UDC_ICSR1_SENDSTL;
++ udc_write(ep_csr, S3C2410_UDC_IN_CSR1_REG);
++ ep_csr |= S3C2410_UDC_ICSR1_CLRDT;
++ udc_write(ep_csr, S3C2410_UDC_IN_CSR1_REG);
++ }
++ } else {
++ if (value)
++ udc_write(ep_csr | S3C2410_UDC_OCSR1_SENDSTL,
++ S3C2410_UDC_OUT_CSR1_REG);
++ else {
++ ep_csr &= ~S3C2410_UDC_OCSR1_SENDSTL;
++ udc_write(ep_csr, S3C2410_UDC_OUT_CSR1_REG);
++ ep_csr |= S3C2410_UDC_OCSR1_CLRDT;
++ udc_write(ep_csr, S3C2410_UDC_OUT_CSR1_REG);
++ }
++ }
++ }
++
++ ep->halted = value ? 1 : 0;
++ local_irq_restore (flags);
++
++ return 0;
++}
++
++static const struct usb_ep_ops s3c2410_ep_ops = {
++ .enable = s3c2410_udc_ep_enable,
++ .disable = s3c2410_udc_ep_disable,
++
++ .alloc_request = s3c2410_udc_alloc_request,
++ .free_request = s3c2410_udc_free_request,
++
++ .alloc_buffer = s3c2410_udc_alloc_buffer,
++ .free_buffer = s3c2410_udc_free_buffer,
++
++ .queue = s3c2410_udc_queue,
++ .dequeue = s3c2410_udc_dequeue,
++
++ .set_halt = s3c2410_udc_set_halt,
++};
++
++/*------------------------- usb_gadget_ops ----------------------------------*/
++
++/*
++ * s3c2410_udc_get_frame
++ */
++static int s3c2410_udc_get_frame(struct usb_gadget *_gadget)
++{
++ int tmp;
++
++ dprintk(DEBUG_VERBOSE, "%s()\n", __func__);
++
++ tmp = udc_read(S3C2410_UDC_FRAME_NUM2_REG) << 8;
++ tmp |= udc_read(S3C2410_UDC_FRAME_NUM1_REG);
++ return tmp;
++}
++
++/*
++ * s3c2410_udc_wakeup
++ */
++static int s3c2410_udc_wakeup(struct usb_gadget *_gadget)
++{
++ dprintk(DEBUG_NORMAL, "%s()\n", __func__);
++ return 0;
++}
++
++/*
++ * s3c2410_udc_set_selfpowered
++ */
++static int s3c2410_udc_set_selfpowered(struct usb_gadget *gadget, int value)
++{
++ struct s3c2410_udc *udc = to_s3c2410_udc(gadget);
++
++ dprintk(DEBUG_NORMAL, "%s()\n", __func__);
++
++ if (value)
++ udc->devstatus |= (1 << USB_DEVICE_SELF_POWERED);
++ else
++ udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
++
++ return 0;
++}
++
++static void s3c2410_udc_disable(struct s3c2410_udc *dev);
++static void s3c2410_udc_enable(struct s3c2410_udc *dev);
++
++static int s3c2410_udc_set_pullup(struct s3c2410_udc *udc, int is_on)
++{
++ dprintk(DEBUG_NORMAL, "%s()\n", __func__);
++
++ if (udc_info && udc_info->udc_command) {
++ if (is_on)
++ s3c2410_udc_enable(udc);
++ else {
++ if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
++ if (udc->driver && udc->driver->disconnect)
++ udc->driver->disconnect(&udc->gadget);
++
++ }
++ s3c2410_udc_disable(udc);
++ }
++ }
++ else
++ return -EOPNOTSUPP;
++
++ return 0;
++}
++
++static int s3c2410_udc_vbus_session(struct usb_gadget *gadget, int is_active)
++{
++ struct s3c2410_udc *udc = to_s3c2410_udc(gadget);
++
++ dprintk(DEBUG_NORMAL, "%s()\n", __func__);
++
++ udc->vbus = (is_active != 0);
++ s3c2410_udc_set_pullup(udc, is_active);
++ return 0;
++}
++
++static int s3c2410_udc_pullup(struct usb_gadget *gadget, int is_on)
++{
++ struct s3c2410_udc *udc = to_s3c2410_udc(gadget);
++
++ dprintk(DEBUG_NORMAL, "%s()\n", __func__);
++
++ s3c2410_udc_set_pullup(udc, is_on ? 0 : 1);
++ return 0;
++}
++
++static irqreturn_t s3c2410_udc_vbus_irq(int irq, void *_dev)
++{
++ struct s3c2410_udc *dev = _dev;
++ unsigned int value;
++
++ dprintk(DEBUG_NORMAL, "%s()\n", __func__);
++ value = s3c2410_gpio_getpin(udc_info->vbus_pin);
++
++ if (udc_info->vbus_pin_inverted)
++ value = !value;
++
++ if (value != dev->vbus)
++ s3c2410_udc_vbus_session(&dev->gadget, value);
++
++ return IRQ_HANDLED;
++}
++
++static int s3c2410_vbus_draw(struct usb_gadget *_gadget, unsigned ma)
++{
++ dprintk(DEBUG_NORMAL, "%s()\n", __func__);
++
++ if (udc_info && udc_info->vbus_draw) {
++ udc_info->vbus_draw(ma);
++ return 0;
++ }
++
++ return -ENOTSUPP;
++}
++
++static const struct usb_gadget_ops s3c2410_ops = {
++ .get_frame = s3c2410_udc_get_frame,
++ .wakeup = s3c2410_udc_wakeup,
++ .set_selfpowered = s3c2410_udc_set_selfpowered,
++ .pullup = s3c2410_udc_pullup,
++ .vbus_session = s3c2410_udc_vbus_session,
++ .vbus_draw = s3c2410_vbus_draw,
++};
++
++/*------------------------- gadget driver handling---------------------------*/
++/*
++ * s3c2410_udc_disable
++ */
++static void s3c2410_udc_disable(struct s3c2410_udc *dev)
++{
++ dprintk(DEBUG_NORMAL, "%s()\n", __func__);
++
++ /* Disable all interrupts */
++ udc_write(0x00, S3C2410_UDC_USB_INT_EN_REG);
++ udc_write(0x00, S3C2410_UDC_EP_INT_EN_REG);
++
++ /* Clear the interrupt registers */
++ udc_write(S3C2410_UDC_USBINT_RESET
++ | S3C2410_UDC_USBINT_RESUME
++ | S3C2410_UDC_USBINT_SUSPEND,
++ S3C2410_UDC_USB_INT_REG);
++
++ udc_write(0x1F, S3C2410_UDC_EP_INT_REG);
++
++ /* Good bye, cruel world */
++ if (udc_info && udc_info->udc_command)
++ udc_info->udc_command(S3C2410_UDC_P_DISABLE);
++
++ /* Set speed to unknown */
++ dev->gadget.speed = USB_SPEED_UNKNOWN;
++}
++
++/*
++ * s3c2410_udc_reinit
++ */
++static void s3c2410_udc_reinit(struct s3c2410_udc *dev)
++{
++ u32 i;
++
++ /* device/ep0 records init */
++ INIT_LIST_HEAD (&dev->gadget.ep_list);
++ INIT_LIST_HEAD (&dev->gadget.ep0->ep_list);
++ dev->ep0state = EP0_IDLE;
++
++ for (i = 0; i < S3C2410_ENDPOINTS; i++) {
++ struct s3c2410_ep *ep = &dev->ep[i];
++
++ if (i != 0)
++ list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list);
++
++ ep->dev = dev;
++ ep->desc = NULL;
++ ep->halted = 0;
++ INIT_LIST_HEAD (&ep->queue);
++ }
++}
++
++/*
++ * s3c2410_udc_enable
++ */
++static void s3c2410_udc_enable(struct s3c2410_udc *dev)
++{
++ int i;
++
++ dprintk(DEBUG_NORMAL, "s3c2410_udc_enable called\n");
++
++ /* dev->gadget.speed = USB_SPEED_UNKNOWN; */
++ dev->gadget.speed = USB_SPEED_FULL;
++
++ /* Set MAXP for all endpoints */
++ for (i = 0; i < S3C2410_ENDPOINTS; i++) {
++ udc_write(i, S3C2410_UDC_INDEX_REG);
++ udc_write((dev->ep[i].ep.maxpacket & 0x7ff) >> 3,
++ S3C2410_UDC_MAXP_REG);
++ }
++
++ /* Set default power state */
++ udc_write(DEFAULT_POWER_STATE, S3C2410_UDC_PWR_REG);
++
++ /* Enable reset and suspend interrupt interrupts */
++ udc_write(S3C2410_UDC_USBINT_RESET | S3C2410_UDC_USBINT_SUSPEND,
++ S3C2410_UDC_USB_INT_EN_REG);
++
++ /* Enable ep0 interrupt */
++ udc_write(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_EN_REG);
++
++ /* time to say "hello, world" */
++ if (udc_info && udc_info->udc_command)
++ udc_info->udc_command(S3C2410_UDC_P_ENABLE);
++}
++
++/*
++ * usb_gadget_register_driver
++ */
++int usb_gadget_register_driver(struct usb_gadget_driver *driver)
++{
++ struct s3c2410_udc *udc = the_controller;
++ int retval;
++
++ dprintk(DEBUG_NORMAL, "usb_gadget_register_driver() '%s'\n",
++ driver->driver.name);
++
++ /* Sanity checks */
++ if (!udc)
++ return -ENODEV;
++
++ if (udc->driver)
++ return -EBUSY;
++
++ if (!driver->bind || !driver->setup
++ || driver->speed != USB_SPEED_FULL) {
++ printk(KERN_ERR "Invalid driver: bind %p setup %p speed %d\n",
++ driver->bind, driver->setup, driver->speed);
++ return -EINVAL;
++ }
++#if defined(MODULE)
++ if (!driver->unbind) {
++ printk(KERN_ERR "Invalid driver: no unbind method\n");
++ return -EINVAL;
++ }
++#endif
++
++ /* Hook the driver */
++ udc->driver = driver;
++ udc->gadget.dev.driver = &driver->driver;
++
++ /* Bind the driver */
++ if ((retval = device_add(&udc->gadget.dev)) != 0) {
++ printk(KERN_ERR "Error in device_add() : %d\n",retval);
++ goto register_error;
++ }
++
++ dprintk(DEBUG_NORMAL, "binding gadget driver '%s'\n",
++ driver->driver.name);
++
++ if ((retval = driver->bind (&udc->gadget)) != 0) {
++ device_del(&udc->gadget.dev);
++ goto register_error;
++ }
++
++ /* Enable udc */
++ s3c2410_udc_enable(udc);
++
++ return 0;
++
++register_error:
++ udc->driver = NULL;
++ udc->gadget.dev.driver = NULL;
++ return retval;
++}
++
++/*
++ * usb_gadget_unregister_driver
++ */
++int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
++{
++ struct s3c2410_udc *udc = the_controller;
++
++ if (!udc)
++ return -ENODEV;
++
++ if (!driver || driver != udc->driver || !driver->unbind)
++ return -EINVAL;
++
++ dprintk(DEBUG_NORMAL,"usb_gadget_register_driver() '%s'\n",
++ driver->driver.name);
++
++ if (driver->disconnect)
++ driver->disconnect(&udc->gadget);
++
++ device_del(&udc->gadget.dev);
++ udc->driver = NULL;
++
++ /* Disable udc */
++ s3c2410_udc_disable(udc);
++
++ return 0;
++}
++
++/*---------------------------------------------------------------------------*/
++static struct s3c2410_udc memory = {
++ .gadget = {
++ .ops = &s3c2410_ops,
++ .ep0 = &memory.ep[0].ep,
++ .name = gadget_name,
++ .dev = {
++ .bus_id = "gadget",
++ },
++ },
++
++ /* control endpoint */
++ .ep[0] = {
++ .num = 0,
++ .ep = {
++ .name = ep0name,
++ .ops = &s3c2410_ep_ops,
++ .maxpacket = EP0_FIFO_SIZE,
++ },
++ .dev = &memory,
++ },
++
++ /* first group of endpoints */
++ .ep[1] = {
++ .num = 1,
++ .ep = {
++ .name = "ep1-bulk",
++ .ops = &s3c2410_ep_ops,
++ .maxpacket = EP_FIFO_SIZE,
++ },
++ .dev = &memory,
++ .fifo_size = EP_FIFO_SIZE,
++ .bEndpointAddress = 1,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ },
++ .ep[2] = {
++ .num = 2,
++ .ep = {
++ .name = "ep2-bulk",
++ .ops = &s3c2410_ep_ops,
++ .maxpacket = EP_FIFO_SIZE,
++ },
++ .dev = &memory,
++ .fifo_size = EP_FIFO_SIZE,
++ .bEndpointAddress = 2,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ },
++ .ep[3] = {
++ .num = 3,
++ .ep = {
++ .name = "ep3-bulk",
++ .ops = &s3c2410_ep_ops,
++ .maxpacket = EP_FIFO_SIZE,
++ },
++ .dev = &memory,
++ .fifo_size = EP_FIFO_SIZE,
++ .bEndpointAddress = 3,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ },
++ .ep[4] = {
++ .num = 4,
++ .ep = {
++ .name = "ep4-bulk",
++ .ops = &s3c2410_ep_ops,
++ .maxpacket = EP_FIFO_SIZE,
++ },
++ .dev = &memory,
++ .fifo_size = EP_FIFO_SIZE,
++ .bEndpointAddress = 4,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ }
++
++};
++
++/*
++ * probe - binds to the platform device
++ */
++static int s3c2410_udc_probe(struct platform_device *pdev)
++{
++ struct s3c2410_udc *udc = &memory;
++ struct device *dev = &pdev->dev;
++ int retval;
++ unsigned int irq;
++
++ dev_dbg(dev, "%s()\n", __func__);
++
++ usb_bus_clock = clk_get(NULL, "usb-bus-gadget");
++ if (IS_ERR(usb_bus_clock)) {
++ dev_err(dev, "failed to get usb bus clock source\n");
++ return PTR_ERR(usb_bus_clock);
++ }
++
++ clk_enable(usb_bus_clock);
++
++ udc_clock = clk_get(NULL, "usb-device");
++ if (IS_ERR(udc_clock)) {
++ dev_err(dev, "failed to get udc clock source\n");
++ return PTR_ERR(udc_clock);
++ }
++
++ clk_enable(udc_clock);
++
++ mdelay(10);
++
++ dev_dbg(dev, "got and enabled clocks\n");
++
++ if (strncmp(pdev->name, "s3c2440", 7) == 0) {
++ dev_info(dev, "S3C2440: increasing FIFO to 128 bytes\n");
++ memory.ep[1].fifo_size = S3C2440_EP_FIFO_SIZE;
++ memory.ep[2].fifo_size = S3C2440_EP_FIFO_SIZE;
++ memory.ep[3].fifo_size = S3C2440_EP_FIFO_SIZE;
++ memory.ep[4].fifo_size = S3C2440_EP_FIFO_SIZE;
++ }
++
++ spin_lock_init (&udc->lock);
++ udc_info = pdev->dev.platform_data;
++
++ rsrc_start = S3C2410_PA_USBDEV;
++ rsrc_len = S3C24XX_SZ_USBDEV;
++
++ if (!request_mem_region(rsrc_start, rsrc_len, gadget_name))
++ return -EBUSY;
++
++ base_addr = ioremap(rsrc_start, rsrc_len);
++ if (!base_addr) {
++ retval = -ENOMEM;
++ goto err_mem;
++ }
++
++ device_initialize(&udc->gadget.dev);
++ udc->gadget.dev.parent = &pdev->dev;
++ udc->gadget.dev.dma_mask = pdev->dev.dma_mask;
++
++ the_controller = udc;
++ platform_set_drvdata(pdev, udc);
++
++ s3c2410_udc_disable(udc);
++ s3c2410_udc_reinit(udc);
++
++ /* irq setup after old hardware state is cleaned up */
++ retval = request_irq(IRQ_USBD, s3c2410_udc_irq,
++ IRQF_DISABLED, gadget_name, udc);
++
++ if (retval != 0) {
++ dev_err(dev, "cannot get irq %i, err %d\n", IRQ_USBD, retval);
++ retval = -EBUSY;
++ goto err_map;
++ }
++
++ dev_dbg(dev, "got irq %i\n", IRQ_USBD);
++
++ if (udc_info && udc_info->vbus_pin > 0) {
++ irq = s3c2410_gpio_getirq(udc_info->vbus_pin);
++ retval = request_irq(irq, s3c2410_udc_vbus_irq,
++ IRQF_DISABLED | IRQF_TRIGGER_RISING
++ | IRQF_TRIGGER_FALLING,
++ gadget_name, udc);
++
++ if (retval != 0) {
++ dev_err(dev, "can't get vbus irq %i, err %d\n",
++ irq, retval);
++ retval = -EBUSY;
++ goto err_int;
++ }
++
++ dev_dbg(dev, "got irq %i\n", irq);
++ } else {
++ udc->vbus = 1;
++ }
++
++ if (s3c2410_udc_debugfs_root) {
++ udc->regs_info = debugfs_create_file("registers", S_IRUGO,
++ s3c2410_udc_debugfs_root,
++ udc, &s3c2410_udc_debugfs_fops);
++ if (IS_ERR(udc->regs_info)) {
++ dev_warn(dev, "debugfs file creation failed %ld\n",
++ PTR_ERR(udc->regs_info));
++ udc->regs_info = NULL;
++ }
++ }
++
++ dev_dbg(dev, "probe ok\n");
++
++ return 0;
++
++err_int:
++ free_irq(IRQ_USBD, udc);
++err_map:
++ iounmap(base_addr);
++err_mem:
++ release_mem_region(rsrc_start, rsrc_len);
++
++ return retval;
++}
++
++/*
++ * s3c2410_udc_remove
++ */
++static int s3c2410_udc_remove(struct platform_device *pdev)
++{
++ struct s3c2410_udc *udc = platform_get_drvdata(pdev);
++ unsigned int irq;
++
++ dev_dbg(&pdev->dev, "%s()\n", __func__);
++ if (udc->driver)
++ return -EBUSY;
++
++ debugfs_remove(udc->regs_info);
++
++ if (udc_info && udc_info->vbus_pin > 0) {
++ irq = s3c2410_gpio_getirq(udc_info->vbus_pin);
++ free_irq(irq, udc);
++ }
++
++ free_irq(IRQ_USBD, udc);
++
++ iounmap(base_addr);
++ release_mem_region(rsrc_start, rsrc_len);
++
++ platform_set_drvdata(pdev, NULL);
++
++ if (!IS_ERR(udc_clock) && udc_clock != NULL) {
++ clk_disable(udc_clock);
++ clk_put(udc_clock);
++ udc_clock = NULL;
++ }
++
++ if (!IS_ERR(usb_bus_clock) && usb_bus_clock != NULL) {
++ clk_disable(usb_bus_clock);
++ clk_put(usb_bus_clock);
++ usb_bus_clock = NULL;
++ }
++
++ dev_dbg(&pdev->dev, "%s: remove ok\n", __func__);
++ return 0;
++}
++
++#ifdef CONFIG_PM
++static int s3c2410_udc_suspend(struct platform_device *pdev, pm_message_t message)
++{
++ if (udc_info && udc_info->udc_command)
++ udc_info->udc_command(S3C2410_UDC_P_DISABLE);
++
++ return 0;
++}
++
++static int s3c2410_udc_resume(struct platform_device *pdev)
++{
++ if (udc_info && udc_info->udc_command)
++ udc_info->udc_command(S3C2410_UDC_P_ENABLE);
++
++ return 0;
++}
++#else
++#define s3c2410_udc_suspend NULL
++#define s3c2410_udc_resume NULL
++#endif
++
++static struct platform_driver udc_driver_2410 = {
++ .driver = {
++ .name = "s3c2410-usbgadget",
++ .owner = THIS_MODULE,
++ },
++ .probe = s3c2410_udc_probe,
++ .remove = s3c2410_udc_remove,
++ .suspend = s3c2410_udc_suspend,
++ .resume = s3c2410_udc_resume,
++};
++
++static struct platform_driver udc_driver_2440 = {
++ .driver = {
++ .name = "s3c2440-usbgadget",
++ .owner = THIS_MODULE,
++ },
++ .probe = s3c2410_udc_probe,
++ .remove = s3c2410_udc_remove,
++ .suspend = s3c2410_udc_suspend,
++ .resume = s3c2410_udc_resume,
++};
++
++static int __init udc_init(void)
++{
++ int retval;
++
++ dprintk(DEBUG_NORMAL, "%s: version %s\n", gadget_name, DRIVER_VERSION);
++
++ s3c2410_udc_debugfs_root = debugfs_create_dir(gadget_name, NULL);
++ if (IS_ERR(s3c2410_udc_debugfs_root)) {
++ printk(KERN_ERR "%s: debugfs dir creation failed %ld\n",
++ gadget_name, PTR_ERR(s3c2410_udc_debugfs_root));
++ s3c2410_udc_debugfs_root = NULL;
++ }
++
++ retval = platform_driver_register(&udc_driver_2410);
++ if (retval)
++ goto err;
++
++ retval = platform_driver_register(&udc_driver_2440);
++ if (retval)
++ goto err;
++
++ return 0;
++
++err:
++ debugfs_remove(s3c2410_udc_debugfs_root);
++ return retval;
++}
++
++static void __exit udc_exit(void)
++{
++ platform_driver_unregister(&udc_driver_2410);
++ platform_driver_unregister(&udc_driver_2440);
++ debugfs_remove(s3c2410_udc_debugfs_root);
++}
++
++EXPORT_SYMBOL(usb_gadget_unregister_driver);
++EXPORT_SYMBOL(usb_gadget_register_driver);
++
++module_init(udc_init);
++module_exit(udc_exit);
++
++MODULE_AUTHOR(DRIVER_AUTHOR);
++MODULE_DESCRIPTION(DRIVER_DESC);
++MODULE_VERSION(DRIVER_VERSION);
++MODULE_LICENSE("GPL");
+diff --git a/drivers/usb/gadget/s3c2410_udc.h b/drivers/usb/gadget/s3c2410_udc.h
+new file mode 100644
+index 0000000..9e0bece
+--- /dev/null
++++ b/drivers/usb/gadget/s3c2410_udc.h
+@@ -0,0 +1,110 @@
++/*
++ * linux/drivers/usb/gadget/s3c2410_udc.h
++ * Samsung on-chip full speed USB device controllers
++ *
++ * Copyright (C) 2004-2007 Herbert Pötzl - Arnaud Patard
++ * Additional cleanups by Ben Dooks <ben-linux at fluff.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#ifndef _S3C2410_UDC_H
++#define _S3C2410_UDC_H
++
++struct s3c2410_ep {
++ struct list_head queue;
++ unsigned long last_io; /* jiffies timestamp */
++ struct usb_gadget *gadget;
++ struct s3c2410_udc *dev;
++ const struct usb_endpoint_descriptor *desc;
++ struct usb_ep ep;
++ u8 num;
++
++ unsigned short fifo_size;
++ u8 bEndpointAddress;
++ u8 bmAttributes;
++
++ unsigned halted : 1;
++ unsigned already_seen : 1;
++ unsigned setup_stage : 1;
++};
++
++
++/* Warning : ep0 has a fifo of 16 bytes */
++/* Don't try to set 32 or 64 */
++/* also testusb 14 fails wit 16 but is */
++/* fine with 8 */
++#define EP0_FIFO_SIZE 8
++#define EP_FIFO_SIZE 64
++#define DEFAULT_POWER_STATE 0x00
++
++#define S3C2440_EP_FIFO_SIZE 128
++
++static const char ep0name [] = "ep0";
++
++static const char *const ep_name[] = {
++ ep0name, /* everyone has ep0 */
++ /* s3c2410 four bidirectional bulk endpoints */
++ "ep1-bulk", "ep2-bulk", "ep3-bulk", "ep4-bulk",
++};
++
++#define S3C2410_ENDPOINTS ARRAY_SIZE(ep_name)
++
++struct s3c2410_request {
++ struct list_head queue; /* ep's requests */
++ struct usb_request req;
++};
++
++enum ep0_state {
++ EP0_IDLE,
++ EP0_IN_DATA_PHASE,
++ EP0_OUT_DATA_PHASE,
++ EP0_END_XFER,
++ EP0_STALL,
++};
++
++static const char *ep0states[]= {
++ "EP0_IDLE",
++ "EP0_IN_DATA_PHASE",
++ "EP0_OUT_DATA_PHASE",
++ "EP0_END_XFER",
++ "EP0_STALL",
++};
++
++struct s3c2410_udc {
++ spinlock_t lock;
++
++ struct s3c2410_ep ep[S3C2410_ENDPOINTS];
++ int address;
++ struct usb_gadget gadget;
++ struct usb_gadget_driver *driver;
++ struct s3c2410_request fifo_req;
++ u8 fifo_buf[EP_FIFO_SIZE];
++ u16 devstatus;
++
++ u32 port_status;
++ int ep0state;
++
++ unsigned got_irq : 1;
++
++ unsigned req_std : 1;
++ unsigned req_config : 1;
++ unsigned req_pending : 1;
++ u8 vbus;
++ struct dentry *regs_info;
++};
++
++#endif
Modified: branches/src/target/kernel/2.6.22.x/patches/s3c_mci.patch
===================================================================
--- branches/src/target/kernel/2.6.22.x/patches/s3c_mci.patch 2007-07-26 14:40:11 UTC (rev 2397)
+++ branches/src/target/kernel/2.6.22.x/patches/s3c_mci.patch 2007-07-26 15:24:28 UTC (rev 2398)
@@ -1,31 +1,8 @@
This is the latest S3C MMC/SD driver by Thomas Kleffel
-Index: linux-2.6.21.3-moko/drivers/mmc/Kconfig
-===================================================================
---- linux-2.6.21.3-moko.orig/drivers/mmc/Kconfig
-+++ linux-2.6.21.3-moko/drivers/mmc/Kconfig
-@@ -125,4 +125,16 @@
- To compile this driver as a module, choose M here: the
- module will be called tifm_sd.
-
-+config MMC_S3C
-+ tristate "Samsung S3C SD/MMC Card Interface support"
-+ depends on ARCH_S3C2410 && MMC
-+ help
-+ This selects a driver for the MCI interface found in
-+ Samsung's S3C2410, S3C2412, S3C2440, S3C2442 CPUs.
-+ If you have a board based on one of those and a MMC/SD
-+ slot, say Y or M here.
-+
-+ If unsure, say N.
-+
-+
- endmenu
-Index: linux-2.6.21.3-moko/drivers/mmc/mmc_debug.c
-===================================================================
--- /dev/null
-+++ linux-2.6.21.3-moko/drivers/mmc/mmc_debug.c
-@@ -0,0 +1,59 @@
++++ linux-2.6.22.1/drivers/mmc/mmc_debug.c
+@@ -0,0 +1,60 @@
+/*
+ * linux/drivers/mmc/mmc_debug.c
+ *
@@ -39,6 +16,7 @@
+ *
+ */
+
++#include <linux/mmc/core.h>
+#include <linux/mmc/mmc.h>
+#include "mmc_debug.h"
+
@@ -85,10 +63,8 @@
+ }
+}
+EXPORT_SYMBOL(mmc_err2str);
-Index: linux-2.6.21.3-moko/drivers/mmc/mmc_debug.h
-===================================================================
--- /dev/null
-+++ linux-2.6.21.3-moko/drivers/mmc/mmc_debug.h
++++ linux-2.6.22.1/drivers/mmc/mmc_debug.h
@@ -0,0 +1,7 @@
+#ifndef MMC_DEBUG_H
+#define MMC_DEBUG_H
@@ -97,11 +73,120 @@
+char *mmc_err2str(int err);
+
+#endif /* MMC_DEBUG_H */
-Index: linux-2.6.21.3-moko/drivers/mmc/s3cmci.c
-===================================================================
+--- linux-2.6.22.1.orig/include/asm-arm/arch-s3c2410/regs-sdi.h
++++ linux-2.6.22.1/include/asm-arm/arch-s3c2410/regs-sdi.h
+@@ -28,9 +28,15 @@
+ #define S3C2410_SDIDCNT (0x30)
+ #define S3C2410_SDIDSTA (0x34)
+ #define S3C2410_SDIFSTA (0x38)
++
+ #define S3C2410_SDIDATA (0x3C)
+ #define S3C2410_SDIIMSK (0x40)
+
++#define S3C2440_SDIDATA (0x40)
++#define S3C2440_SDIIMSK (0x3C)
++
++#define S3C2440_SDICON_SDRESET (1<<8)
++#define S3C2440_SDICON_MMCCLOCK (1<<5)
+ #define S3C2410_SDICON_BYTEORDER (1<<4)
+ #define S3C2410_SDICON_SDIOIRQ (1<<3)
+ #define S3C2410_SDICON_RWAITEN (1<<2)
+@@ -42,7 +48,8 @@
+ #define S3C2410_SDICMDCON_LONGRSP (1<<10)
+ #define S3C2410_SDICMDCON_WAITRSP (1<<9)
+ #define S3C2410_SDICMDCON_CMDSTART (1<<8)
+-#define S3C2410_SDICMDCON_INDEX (0xff)
++#define S3C2410_SDICMDCON_SENDERHOST (1<<6)
++#define S3C2410_SDICMDCON_INDEX (0x3f)
+
+ #define S3C2410_SDICMDSTAT_CRCFAIL (1<<12)
+ #define S3C2410_SDICMDSTAT_CMDSENT (1<<11)
+@@ -51,6 +58,9 @@
+ #define S3C2410_SDICMDSTAT_XFERING (1<<8)
+ #define S3C2410_SDICMDSTAT_INDEX (0xff)
+
++#define S3C2440_SDIDCON_DS_BYTE (0<<22)
++#define S3C2440_SDIDCON_DS_HALFWORD (1<<22)
++#define S3C2440_SDIDCON_DS_WORD (2<<22)
+ #define S3C2410_SDIDCON_IRQPERIOD (1<<21)
+ #define S3C2410_SDIDCON_TXAFTERRESP (1<<20)
+ #define S3C2410_SDIDCON_RXAFTERCMD (1<<19)
+@@ -59,6 +69,7 @@
+ #define S3C2410_SDIDCON_WIDEBUS (1<<16)
+ #define S3C2410_SDIDCON_DMAEN (1<<15)
+ #define S3C2410_SDIDCON_STOP (1<<14)
++#define S3C2440_SDIDCON_DATSTART (1<<14)
+ #define S3C2410_SDIDCON_DATMODE (3<<12)
+ #define S3C2410_SDIDCON_BLKNUM (0x7ff)
+
+@@ -68,6 +79,7 @@
+ #define S3C2410_SDIDCON_XFER_RXSTART (2<<12)
+ #define S3C2410_SDIDCON_XFER_TXSTART (3<<12)
+
++#define S3C2410_SDIDCON_BLKNUM_MASK (0xFFF)
+ #define S3C2410_SDIDCNT_BLKNUM_SHIFT (12)
+
+ #define S3C2410_SDIDSTA_RDYWAITREQ (1<<10)
+@@ -82,10 +94,12 @@
+ #define S3C2410_SDIDSTA_TXDATAON (1<<1)
+ #define S3C2410_SDIDSTA_RXDATAON (1<<0)
+
++#define S3C2440_SDIFSTA_FIFORESET (1<<16)
++#define S3C2440_SDIFSTA_FIFOFAIL (3<<14) /* 3 is correct (2 bits) */
+ #define S3C2410_SDIFSTA_TFDET (1<<13)
+ #define S3C2410_SDIFSTA_RFDET (1<<12)
+-#define S3C2410_SDIFSTA_TXHALF (1<<11)
+-#define S3C2410_SDIFSTA_TXEMPTY (1<<10)
++#define S3C2410_SDIFSTA_TFHALF (1<<11)
++#define S3C2410_SDIFSTA_TFEMPTY (1<<10)
+ #define S3C2410_SDIFSTA_RFLAST (1<<9)
+ #define S3C2410_SDIFSTA_RFFULL (1<<8)
+ #define S3C2410_SDIFSTA_RFHALF (1<<7)
+--- linux-2.6.22.1.orig/drivers/mmc/Makefile
++++ linux-2.6.22.1/drivers/mmc/Makefile
+@@ -4,6 +4,7 @@
+
+ ifeq ($(CONFIG_MMC_DEBUG),y)
+ EXTRA_CFLAGS += -DDEBUG
++ obj-$(CONFIG_MMC) += mmc_debug.o
+ endif
+
+ obj-$(CONFIG_MMC) += core/
+--- linux-2.6.22.1.orig/drivers/mmc/host/Makefile
++++ linux-2.6.22.1/drivers/mmc/host/Makefile
+@@ -15,4 +15,5 @@
+ obj-$(CONFIG_MMC_OMAP) += omap.o
+ obj-$(CONFIG_MMC_AT91) += at91_mci.o
+ obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o
++obj-$(CONFIG_MMC_S3C) += s3cmci.o
+
+--- linux-2.6.22.1.orig/include/linux/mmc/core.h
++++ linux-2.6.22.1/include/linux/mmc/core.h
+@@ -54,12 +54,15 @@
+ unsigned int retries; /* max number of retries */
+ unsigned int error; /* command error */
+
+-#define MMC_ERR_NONE 0
+-#define MMC_ERR_TIMEOUT 1
+-#define MMC_ERR_BADCRC 2
+-#define MMC_ERR_FIFO 3
+-#define MMC_ERR_FAILED 4
+-#define MMC_ERR_INVALID 5
++#define MMC_ERR_NONE 0
++#define MMC_ERR_TIMEOUT 1
++#define MMC_ERR_BADCRC 2
++#define MMC_ERR_FIFO 3
++#define MMC_ERR_DMA 4
++#define MMC_ERR_BUSY 5
++#define MMC_ERR_FAILED 6
++#define MMC_ERR_INVALID 7
++#define MMC_ERR_CANCELED 8
+
+ struct mmc_data *data; /* data segment associated with cmd */
+ struct mmc_request *mrq; /* associated request */
--- /dev/null
-+++ linux-2.6.21.3-moko/drivers/mmc/s3cmci.c
-@@ -0,0 +1,1341 @@
++++ linux-2.6.22.1/drivers/mmc/host/s3cmci.c
+@@ -0,0 +1,1340 @@
+/*
+ * linux/drivers/mmc/s3cmci.h - Samsung S3C MCI driver
+ *
@@ -116,7 +201,6 @@
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <linux/mmc/host.h>
-+#include <linux/mmc/protocol.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+
@@ -127,7 +211,7 @@
+#include <asm/arch/regs-sdi.h>
+#include <asm/arch/regs-gpio.h>
+
-+#include "mmc_debug.h"
++#include "../mmc_debug.h"
+#include "s3cmci.h"
+
+#define DRIVER_NAME "s3c-mci"
@@ -1443,10 +1527,8 @@
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Thomas Kleffel <tk at maintech.de>");
+
-Index: linux-2.6.21.3-moko/drivers/mmc/s3cmci.h
-===================================================================
--- /dev/null
-+++ linux-2.6.21.3-moko/drivers/mmc/s3cmci.h
++++ linux-2.6.22.1/drivers/mmc/host/s3cmci.h
@@ -0,0 +1,71 @@
+/*
+ * linux/drivers/mmc/s3cmci.h - Samsung S3C MCI driver
@@ -1519,117 +1601,3 @@
+ unsigned int ccnt, dcnt;
+ struct tasklet_struct pio_tasklet;
+};
-Index: linux-2.6.21.3-moko/include/asm-arm/arch-s3c2410/regs-sdi.h
-===================================================================
---- linux-2.6.21.3-moko.orig/include/asm-arm/arch-s3c2410/regs-sdi.h
-+++ linux-2.6.21.3-moko/include/asm-arm/arch-s3c2410/regs-sdi.h
-@@ -28,9 +28,15 @@
- #define S3C2410_SDIDCNT (0x30)
- #define S3C2410_SDIDSTA (0x34)
- #define S3C2410_SDIFSTA (0x38)
-+
- #define S3C2410_SDIDATA (0x3C)
- #define S3C2410_SDIIMSK (0x40)
-
-+#define S3C2440_SDIDATA (0x40)
-+#define S3C2440_SDIIMSK (0x3C)
-+
-+#define S3C2440_SDICON_SDRESET (1<<8)
-+#define S3C2440_SDICON_MMCCLOCK (1<<5)
- #define S3C2410_SDICON_BYTEORDER (1<<4)
- #define S3C2410_SDICON_SDIOIRQ (1<<3)
- #define S3C2410_SDICON_RWAITEN (1<<2)
-@@ -42,7 +48,8 @@
- #define S3C2410_SDICMDCON_LONGRSP (1<<10)
- #define S3C2410_SDICMDCON_WAITRSP (1<<9)
- #define S3C2410_SDICMDCON_CMDSTART (1<<8)
--#define S3C2410_SDICMDCON_INDEX (0xff)
-+#define S3C2410_SDICMDCON_SENDERHOST (1<<6)
-+#define S3C2410_SDICMDCON_INDEX (0x3f)
-
- #define S3C2410_SDICMDSTAT_CRCFAIL (1<<12)
- #define S3C2410_SDICMDSTAT_CMDSENT (1<<11)
-@@ -51,6 +58,9 @@
- #define S3C2410_SDICMDSTAT_XFERING (1<<8)
- #define S3C2410_SDICMDSTAT_INDEX (0xff)
-
-+#define S3C2440_SDIDCON_DS_BYTE (0<<22)
-+#define S3C2440_SDIDCON_DS_HALFWORD (1<<22)
-+#define S3C2440_SDIDCON_DS_WORD (2<<22)
- #define S3C2410_SDIDCON_IRQPERIOD (1<<21)
- #define S3C2410_SDIDCON_TXAFTERRESP (1<<20)
- #define S3C2410_SDIDCON_RXAFTERCMD (1<<19)
-@@ -59,6 +69,7 @@
- #define S3C2410_SDIDCON_WIDEBUS (1<<16)
- #define S3C2410_SDIDCON_DMAEN (1<<15)
- #define S3C2410_SDIDCON_STOP (1<<14)
-+#define S3C2440_SDIDCON_DATSTART (1<<14)
- #define S3C2410_SDIDCON_DATMODE (3<<12)
- #define S3C2410_SDIDCON_BLKNUM (0x7ff)
-
-@@ -68,6 +79,7 @@
- #define S3C2410_SDIDCON_XFER_RXSTART (2<<12)
- #define S3C2410_SDIDCON_XFER_TXSTART (3<<12)
-
-+#define S3C2410_SDIDCON_BLKNUM_MASK (0xFFF)
- #define S3C2410_SDIDCNT_BLKNUM_SHIFT (12)
-
- #define S3C2410_SDIDSTA_RDYWAITREQ (1<<10)
-@@ -82,10 +94,12 @@
- #define S3C2410_SDIDSTA_TXDATAON (1<<1)
- #define S3C2410_SDIDSTA_RXDATAON (1<<0)
-
-+#define S3C2440_SDIFSTA_FIFORESET (1<<16)
-+#define S3C2440_SDIFSTA_FIFOFAIL (3<<14) /* 3 is correct (2 bits) */
- #define S3C2410_SDIFSTA_TFDET (1<<13)
- #define S3C2410_SDIFSTA_RFDET (1<<12)
--#define S3C2410_SDIFSTA_TXHALF (1<<11)
--#define S3C2410_SDIFSTA_TXEMPTY (1<<10)
-+#define S3C2410_SDIFSTA_TFHALF (1<<11)
-+#define S3C2410_SDIFSTA_TFEMPTY (1<<10)
- #define S3C2410_SDIFSTA_RFLAST (1<<9)
- #define S3C2410_SDIFSTA_RFFULL (1<<8)
- #define S3C2410_SDIFSTA_RFHALF (1<<7)
-Index: linux-2.6.21.3-moko/include/linux/mmc/mmc.h
-===================================================================
---- linux-2.6.21.3-moko.orig/include/linux/mmc/mmc.h
-+++ linux-2.6.21.3-moko/include/linux/mmc/mmc.h
-@@ -55,12 +55,15 @@
- unsigned int retries; /* max number of retries */
- unsigned int error; /* command error */
-
--#define MMC_ERR_NONE 0
--#define MMC_ERR_TIMEOUT 1
--#define MMC_ERR_BADCRC 2
--#define MMC_ERR_FIFO 3
--#define MMC_ERR_FAILED 4
--#define MMC_ERR_INVALID 5
-+#define MMC_ERR_NONE 0
-+#define MMC_ERR_TIMEOUT 1
-+#define MMC_ERR_BADCRC 2
-+#define MMC_ERR_FIFO 3
-+#define MMC_ERR_DMA 4
-+#define MMC_ERR_BUSY 5
-+#define MMC_ERR_FAILED 6
-+#define MMC_ERR_INVALID 7
-+#define MMC_ERR_CANCELED 8
-
- struct mmc_data *data; /* data segment associated with cmd */
- struct mmc_request *mrq; /* associated request */
-Index: linux-2.6.21.3-moko/drivers/mmc/Makefile
-===================================================================
---- linux-2.6.21.3-moko.orig/drivers/mmc/Makefile
-+++ linux-2.6.21.3-moko/drivers/mmc/Makefile
-@@ -24,10 +24,12 @@
- obj-$(CONFIG_MMC_OMAP) += omap.o
- obj-$(CONFIG_MMC_AT91) += at91_mci.o
- obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o
-+obj-$(CONFIG_MMC_S3C) += s3cmci.o
-
- mmc_core-y := mmc.o mmc_sysfs.o
- mmc_core-$(CONFIG_BLOCK) += mmc_queue.o
-
- ifeq ($(CONFIG_MMC_DEBUG),y)
-+obj-$(CONFIG_MMC) += mmc_debug.o
- EXTRA_CFLAGS += -DDEBUG
- endif
Modified: branches/src/target/kernel/2.6.22.x/patches/s3c_mci_platform.patch
===================================================================
--- branches/src/target/kernel/2.6.22.x/patches/s3c_mci_platform.patch 2007-07-26 14:40:11 UTC (rev 2397)
+++ branches/src/target/kernel/2.6.22.x/patches/s3c_mci_platform.patch 2007-07-26 15:24:28 UTC (rev 2398)
@@ -1,19 +1,17 @@
This patch adds platform data support to the s3mci driver. This allows
flexible board-specific configuration of set_power, card detect and read only
pins.
-Index: linux-2.6.20.4/drivers/mmc/s3cmci.c
-===================================================================
---- linux-2.6.20.4.orig/drivers/mmc/s3cmci.c 2007-04-02 20:37:47.000000000 +0200
-+++ linux-2.6.20.4/drivers/mmc/s3cmci.c 2007-04-02 20:55:02.000000000 +0200
-@@ -22,6 +22,7 @@
+--- linux-2.6.22.1.orig/drivers/mmc/host/s3cmci.c
++++ linux-2.6.22.1/drivers/mmc/host/s3cmci.c
+@@ -21,6 +21,7 @@
#include <asm/io.h>
#include <asm/arch/regs-sdi.h>
#include <asm/arch/regs-gpio.h>
+#include <asm/arch/mci.h>
- #include "mmc_debug.h"
+ #include "../mmc_debug.h"
#include "s3cmci.h"
-@@ -1012,6 +1013,9 @@
+@@ -1011,6 +1012,9 @@
s3c2410_gpio_cfgpin(S3C2410_GPE9, S3C2410_GPE9_SDDAT2);
s3c2410_gpio_cfgpin(S3C2410_GPE10, S3C2410_GPE10_SDDAT3);
@@ -23,7 +21,7 @@
if (!host->is2440)
mci_con|=S3C2410_SDICON_FIFORESET;
-@@ -1022,6 +1026,9 @@
+@@ -1021,6 +1025,9 @@
s3c2410_gpio_setpin(S3C2410_GPE5, 0);
s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_OUTP);
@@ -33,7 +31,7 @@
if (host->is2440)
mci_con|=S3C2440_SDICON_SDRESET;
-@@ -1075,9 +1082,26 @@
+@@ -1074,9 +1081,26 @@
writel(con, host->base + S3C2410_SDICON);
}
@@ -60,7 +58,7 @@
};
static int s3cmci_probe(struct platform_device *pdev, int is2440)
-@@ -1097,6 +1121,12 @@
+@@ -1096,6 +1120,12 @@
host->mmc = mmc;
host->pdev = pdev;
@@ -73,7 +71,7 @@
spin_lock_init(&host->complete_lock);
tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);
if (is2440) {
-@@ -1115,7 +1145,8 @@
+@@ -1114,7 +1144,8 @@
host->pio_active = XFER_NONE;
host->dma = S3CMCI_DMA;
@@ -83,7 +81,7 @@
host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!host->mem) {
-@@ -1157,7 +1188,7 @@
+@@ -1156,7 +1187,7 @@
disable_irq(host->irq);
@@ -92,7 +90,7 @@
set_irq_type(host->irq_cd, IRQT_BOTHEDGE);
if (request_irq(host->irq_cd, s3cmci_irq_cd, 0, DRIVER_NAME, host)) {
-@@ -1168,6 +1199,10 @@
+@@ -1167,6 +1198,10 @@
goto probe_free_irq;
}
@@ -103,7 +101,7 @@
if (s3c2410_dma_request(S3CMCI_DMA, &s3cmci_dma_client, NULL)) {
dev_err(&pdev->dev, "unable to get DMA channel.\n");
ret = -EBUSY;
-@@ -1190,7 +1225,7 @@
+@@ -1189,7 +1224,7 @@
host->clk_rate = clk_get_rate(host->clk);
mmc->ops = &s3cmci_ops;
@@ -112,10 +110,8 @@
mmc->caps = MMC_CAP_4_BIT_DATA;
mmc->f_min = host->clk_rate / (host->clk_div * 256);
mmc->f_max = host->clk_rate / host->clk_div;
-Index: linux-2.6.20.4/drivers/mmc/s3cmci.h
-===================================================================
---- linux-2.6.20.4.orig/drivers/mmc/s3cmci.h 2007-04-02 20:37:44.000000000 +0200
-+++ linux-2.6.20.4/drivers/mmc/s3cmci.h 2007-04-02 20:37:48.000000000 +0200
+--- linux-2.6.22.1.orig/drivers/mmc/host/s3cmci.h
++++ linux-2.6.22.1/drivers/mmc/host/s3cmci.h
@@ -22,6 +22,7 @@
struct s3cmci_host {
@@ -124,10 +120,8 @@
struct mmc_host *mmc;
struct resource *mem;
struct clk *clk;
-Index: linux-2.6.20.4/include/asm-arm/arch-s3c2410/mci.h
-===================================================================
---- /dev/null 1970-01-01 00:00:00.000000000 +0000
-+++ linux-2.6.20.4/include/asm-arm/arch-s3c2410/mci.h 2007-04-02 20:54:16.000000000 +0200
+--- /dev/null
++++ linux-2.6.22.1/include/asm-arm/arch-s3c2410/mci.h
@@ -0,0 +1,12 @@
+#ifndef _ARCH_MCI_H
+#define _ARCH_MCI_H
Modified: branches/src/target/kernel/2.6.22.x/patches/s3cmci-dma-free.patch
===================================================================
--- branches/src/target/kernel/2.6.22.x/patches/s3cmci-dma-free.patch 2007-07-26 14:40:11 UTC (rev 2397)
+++ branches/src/target/kernel/2.6.22.x/patches/s3cmci-dma-free.patch 2007-07-26 15:24:28 UTC (rev 2398)
@@ -1,8 +1,6 @@
-Index: linux-2.6.17.14-fic4.test/drivers/mmc/s3cmci.c
-===================================================================
---- linux-2.6.17.14-fic4.test.orig/drivers/mmc/s3cmci.c 2007-01-24 12:17:52.000000000 +0100
-+++ linux-2.6.17.14-fic4.test/drivers/mmc/s3cmci.c 2007-01-24 12:17:57.000000000 +0100
-@@ -1247,6 +1247,7 @@
+--- linux-2.6.22.1.orig/drivers/mmc/host/s3cmci.c
++++ linux-2.6.22.1/drivers/mmc/host/s3cmci.c
+@@ -1248,6 +1248,7 @@
mmc_remove_host(mmc);
clk_disable(host->clk);
clk_put(host->clk);
Modified: branches/src/target/kernel/2.6.22.x/patches/s3cmci-stop-fix.patch
===================================================================
--- branches/src/target/kernel/2.6.22.x/patches/s3cmci-stop-fix.patch 2007-07-26 14:40:11 UTC (rev 2397)
+++ branches/src/target/kernel/2.6.22.x/patches/s3cmci-stop-fix.patch 2007-07-26 15:24:28 UTC (rev 2398)
@@ -3,11 +3,9 @@
Signed-off-by: Harald Welte <laforge at openmoko.org>
-Index: linux-2.6.17.14-fic4.test/drivers/mmc/s3cmci.c
-===================================================================
---- linux-2.6.17.14-fic4.test.orig/drivers/mmc/s3cmci.c 2007-02-07 20:39:12.000000000 +0100
-+++ linux-2.6.17.14-fic4.test/drivers/mmc/s3cmci.c 2007-02-10 02:31:13.000000000 +0100
-@@ -667,7 +667,7 @@
+--- linux-2.6.22.1.orig/drivers/mmc/host/s3cmci.c
++++ linux-2.6.22.1/drivers/mmc/host/s3cmci.c
+@@ -665,7 +665,7 @@
#endif
//Cleanup controller
writel(0, host->base + S3C2410_SDICMDARG);
@@ -16,7 +14,7 @@
writel(0, host->base + S3C2410_SDICMDCON);
writel(0, host->base + host->sdiimsk);
-@@ -794,7 +794,7 @@
+@@ -792,7 +792,7 @@
dbg(host, dbg_err,
"mci_setup_data() transfer stillin progress.\n");
Modified: branches/src/target/kernel/2.6.22.x/patches/s3cmci_dbg.patch
===================================================================
--- branches/src/target/kernel/2.6.22.x/patches/s3cmci_dbg.patch 2007-07-26 14:40:11 UTC (rev 2397)
+++ branches/src/target/kernel/2.6.22.x/patches/s3cmci_dbg.patch 2007-07-26 15:24:28 UTC (rev 2398)
@@ -1,10 +1,8 @@
This patch is a workaround of some S3C2410 MMC chip bug
-Index: linux-2.6.17.14-fic4.test/drivers/mmc/s3cmci.c
-===================================================================
---- linux-2.6.17.14-fic4.test.orig/drivers/mmc/s3cmci.c 2007-01-24 12:17:51.000000000 +0100
-+++ linux-2.6.17.14-fic4.test/drivers/mmc/s3cmci.c 2007-01-24 12:17:52.000000000 +0100
-@@ -445,11 +445,17 @@
+--- linux-2.6.22.1.orig/drivers/mmc/host/s3cmci.c
++++ linux-2.6.22.1/drivers/mmc/host/s3cmci.c
+@@ -444,11 +444,17 @@
if (mci_csta & S3C2410_SDICMDSTAT_CRCFAIL) {
if (cmd->flags & MMC_RSP_CRC) {
Modified: branches/src/target/kernel/2.6.22.x/patches/series
===================================================================
--- branches/src/target/kernel/2.6.22.x/patches/series 2007-07-26 14:40:11 UTC (rev 2397)
+++ branches/src/target/kernel/2.6.22.x/patches/series 2007-07-26 15:24:28 UTC (rev 2398)
@@ -1,17 +1,13 @@
-asoc.patch
-asoc-asm_hardware_h.patch
-asoc-platform-hw_init-pcm_emulation-fix.patch
yaffs2-20070304.patch
i2c-permit_invalid_addrs.patch
pm-debug_less_verbose.patch
g_ether-highpower.patch
g_ether-vendor_product.patch
s3c2410_serial-nodebug.patch
-s3c2410_udc.patch
+s3c2410_udc_from_upstream.patch
s3c2410_touchscreen.patch
s3c2410-bbt.patch
-udc-nomodule-misccr.patch
-s3c2410_udc-vbus_draw_pdata.patch
+#udc-nomodule-misccr.patch
s3c_mci.patch
s3cmci_dbg.patch
s3cmci-dma-free.patch
@@ -40,3 +36,6 @@
smedia-glamo.patch
s3c24xx-nand-largepage.patch
gta02-core.patch
+alsa-2.6.23-rc1-commit.diff
+asoc-platform-hw_init-pcm_emulation-fix.patch
+asoc-kconfig-fix.patch
Deleted: branches/src/target/kernel/2.6.22.x/patches/series.old
===================================================================
--- branches/src/target/kernel/2.6.22.x/patches/series.old 2007-07-26 14:40:11 UTC (rev 2397)
+++ branches/src/target/kernel/2.6.22.x/patches/series.old 2007-07-26 15:24:28 UTC (rev 2398)
@@ -1,17 +0,0 @@
-qt2410-base.patch
-qt2410-cs8900.patch
-mmc.patch
-udc.patch
-mmc-get_ro.patch
-mmc-sdipre_limit.patch
-mmc-fixdmalockup.patch
-mmc-fixdebugp.patch
-qt2410-biglcd.patch
-qt2410-touchscreen.patch
-s3c2410_serial-nodebug.patch
-2.6.17-arm-usb_gadget-ether-link.patch
-2.6.17-s3c2410-spi-mode23.patch
-gta01-core.patch
-gta01-jbt6k74.patch
-gta01-backlight.patch
-gta01-pcf50606.patch
Modified: branches/src/target/kernel/2.6.22.x/patches/smedia-glamo.patch
===================================================================
--- branches/src/target/kernel/2.6.22.x/patches/smedia-glamo.patch 2007-07-26 14:40:11 UTC (rev 2397)
+++ branches/src/target/kernel/2.6.22.x/patches/smedia-glamo.patch 2007-07-26 15:24:28 UTC (rev 2398)
@@ -1,10 +1,8 @@
-Index: linux-2.6.21.3-moko/drivers/video/Kconfig
-===================================================================
---- linux-2.6.21.3-moko.orig/drivers/video/Kconfig
-+++ linux-2.6.21.3-moko/drivers/video/Kconfig
-@@ -1633,6 +1633,38 @@
- The default value can be overridden on the kernel command line
- using the "ps3fb" option (e.g. "ps3fb=9M");
+--- linux-2.6.22.1.orig/drivers/video/Kconfig
++++ linux-2.6.22.1/drivers/video/Kconfig
+@@ -1820,6 +1820,38 @@
+ framebuffer. ML300 carries a 640*480 LCD display on the board,
+ ML403 uses a standard DB15 VGA connector.
+config GLAMO
+ bool
@@ -41,22 +39,18 @@
config FB_VIRTUAL
tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)"
depends on FB
-Index: linux-2.6.21.3-moko/drivers/video/Makefile
-===================================================================
---- linux-2.6.21.3-moko.orig/drivers/video/Makefile
-+++ linux-2.6.21.3-moko/drivers/video/Makefile
-@@ -99,6 +99,7 @@
- obj-$(CONFIG_FB_IBM_GXT4500) += gxt4500.o
+--- linux-2.6.22.1.orig/drivers/video/Makefile
++++ linux-2.6.22.1/drivers/video/Makefile
+@@ -113,6 +113,7 @@
obj-$(CONFIG_FB_PS3) += ps3fb.o
obj-$(CONFIG_FB_SM501) += sm501fb.o
+ obj-$(CONFIG_FB_XILINX) += xilinxfb.o
+obj-$(CONFIG_GLAMO) += glamo/
# Platform or fallback drivers go here
obj-$(CONFIG_FB_VESA) += vesafb.o
-Index: linux-2.6.21.3-moko/drivers/video/glamo/Makefile
-===================================================================
--- /dev/null
-+++ linux-2.6.21.3-moko/drivers/video/glamo/Makefile
++++ linux-2.6.22.1/drivers/video/glamo/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for the Smedia Glamo framebuffer driver
@@ -69,10 +63,8 @@
+obj-$(CONFIG_FB_GLAMO) += glamo-fb.o
+obj-$(CONFIG_FB_GLAMO_SPI) += glamo-lcm-spi.o
+
-Index: linux-2.6.21.3-moko/drivers/video/glamo/glamo-regs.h
-===================================================================
--- /dev/null
-+++ linux-2.6.21.3-moko/drivers/video/glamo/glamo-regs.h
++++ linux-2.6.22.1/drivers/video/glamo/glamo-regs.h
@@ -0,0 +1,466 @@
+#ifndef _GLAMO_REGS_H
+#define _GLAMO_REGS_H
@@ -540,10 +532,8 @@
+};
+
+#endif /* _GLAMO_REGS_H */
-Index: linux-2.6.21.3-moko/drivers/video/glamo/glamo-core.c
-===================================================================
--- /dev/null
-+++ linux-2.6.21.3-moko/drivers/video/glamo/glamo-core.c
++++ linux-2.6.22.1/drivers/video/glamo/glamo-core.c
@@ -0,0 +1,1017 @@
+/* Smedia Glamo 336x/337x driver
+ *
@@ -1562,10 +1552,8 @@
+MODULE_AUTHOR("Harald Welte <laforge at openmoko.org>");
+MODULE_DESCRIPTION("Smedia Glamo 336x/337x core/resource driver");
+MODULE_LICENSE("GPL");
-Index: linux-2.6.21.3-moko/include/asm-arm/arch-s3c2410/irqs.h
-===================================================================
---- linux-2.6.21.3-moko.orig/include/asm-arm/arch-s3c2410/irqs.h
-+++ linux-2.6.21.3-moko/include/asm-arm/arch-s3c2410/irqs.h
+--- linux-2.6.22.1.orig/include/asm-arm/arch-s3c2410/irqs.h
++++ linux-2.6.22.1/include/asm-arm/arch-s3c2410/irqs.h
@@ -148,9 +148,37 @@
#define IRQ_S3C2443_AC97 S3C2410_IRQSUB(28)
@@ -1606,10 +1594,8 @@
+#define IRQ_GLAMO_RISC IRQ_GLAMO(8)
+
#endif /* __ASM_ARCH_IRQ_H */
-Index: linux-2.6.21.3-moko/drivers/video/glamo/glamo-fb.c
-===================================================================
--- /dev/null
-+++ linux-2.6.21.3-moko/drivers/video/glamo/glamo-fb.c
++++ linux-2.6.22.1/drivers/video/glamo/glamo-fb.c
@@ -0,0 +1,547 @@
+/* Smedia Glamo 336x/337x driver
+ *
@@ -2158,10 +2144,8 @@
+MODULE_AUTHOR("Harald Welte <laforge at openmoko.org>");
+MODULE_DESCRIPTION("Smedia Glamo 336x/337x framebuffer driver");
+MODULE_LICENSE("GPL");
-Index: linux-2.6.21.3-moko/include/linux/glamofb.h
-===================================================================
--- /dev/null
-+++ linux-2.6.21.3-moko/include/linux/glamofb.h
++++ linux-2.6.22.1/include/linux/glamofb.h
@@ -0,0 +1,29 @@
+#ifndef _LINUX_GLAMOFB_H
+#define _LINUX_GLAMOFB_H
@@ -2192,10 +2176,8 @@
+int glamofb_cmd_write(struct glamofb_handle *gfb, u_int16_t val);
+
+#endif
-Index: linux-2.6.21.3-moko/include/linux/spi/glamo.h
-===================================================================
--- /dev/null
-+++ linux-2.6.21.3-moko/include/linux/spi/glamo.h
++++ linux-2.6.22.1/include/linux/spi/glamo.h
@@ -0,0 +1,28 @@
+#ifndef __GLAMO_SPI_H
+#define __GLAMO_SPI_H
@@ -2225,10 +2207,8 @@
+
+
+#endif
-Index: linux-2.6.21.3-moko/drivers/video/glamo/glamo-core.h
-===================================================================
--- /dev/null
-+++ linux-2.6.21.3-moko/drivers/video/glamo/glamo-core.h
++++ linux-2.6.22.1/drivers/video/glamo/glamo-core.h
@@ -0,0 +1,51 @@
+#ifndef __GLAMO_CORE_H
+#define __GLAMO_CORE_H
@@ -2281,10 +2261,8 @@
+void glamo_engine_reset(struct glamo_core *glamo, enum glamo_engine engine);
+
+#endif /* __GLAMO_CORE_H */
-Index: linux-2.6.21.3-moko/drivers/video/glamo/glamo-gpio.c
-===================================================================
--- /dev/null
-+++ linux-2.6.21.3-moko/drivers/video/glamo/glamo-gpio.c
++++ linux-2.6.22.1/drivers/video/glamo/glamo-gpio.c
@@ -0,0 +1,62 @@
+
+#include <linux/kernel.h>
@@ -2348,10 +2326,8 @@
+}
+EXPORT_SYMBOL(glamo_gpio_cfgpin);
+
-Index: linux-2.6.21.3-moko/drivers/video/glamo/glamo-lcm-spi.c
-===================================================================
--- /dev/null
-+++ linux-2.6.21.3-moko/drivers/video/glamo/glamo-lcm-spi.c
++++ linux-2.6.22.1/drivers/video/glamo/glamo-lcm-spi.c
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2007 OpenMoko, Inc.
@@ -2594,10 +2570,8 @@
+MODULE_DESCRIPTION("Smedia Glamo 336x/337x LCM serial command SPI Driver");
+MODULE_AUTHOR("Harald Welte <laforge at openmoko.org>")
+MODULE_LICENSE("GPL");
-Index: linux-2.6.21.3-moko/drivers/video/glamo/glamo-spi-gpio.c
-===================================================================
--- /dev/null
-+++ linux-2.6.21.3-moko/drivers/video/glamo/glamo-spi-gpio.c
++++ linux-2.6.22.1/drivers/video/glamo/glamo-spi-gpio.c
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2007 OpenMoko, Inc.
@@ -2849,10 +2823,8 @@
+MODULE_DESCRIPTION("Smedia Glamo 336x/337x LCM serial command SPI Driver");
+MODULE_AUTHOR("Harald Welte <laforge at openmoko.org>")
+MODULE_LICENSE("GPL");
-Index: linux-2.6.21.3-moko/include/linux/glamo-gpio.h
-===================================================================
--- /dev/null
-+++ linux-2.6.21.3-moko/include/linux/glamo-gpio.h
++++ linux-2.6.22.1/include/linux/glamo-gpio.h
@@ -0,0 +1,99 @@
+#ifndef __GLAMO_GPIO_H
+#define __GLAMO_GPIO_H
More information about the commitlog
mailing list