]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ALSA: SB X-Fi driver merge
authorWai Yew CHAY <wychay@ctl.creative.com>
Thu, 14 May 2009 06:05:58 +0000 (08:05 +0200)
committerTakashi Iwai <tiwai@suse.de>
Thu, 14 May 2009 06:24:10 +0000 (08:24 +0200)
The Sound Blaster X-Fi driver supports Creative solutions based on
20K1 and 20K2 chipsets.

Supported hardware :

Creative Sound Blaster X-Fi Titanium Fatal1ty® Champion Series
Creative Sound Blaster X-Fi Titanium Fatal1ty Professional Series
Creative Sound Blaster X-Fi Titanium Professional Audio
Creative Sound Blaster X-Fi Titanium
Creative Sound Blaster X-Fi Elite Pro
Creative Sound Blaster X-Fi Platinum
Creative Sound Blaster X-Fi Fatal1ty
Creative Sound Blaster X-Fi XtremeGamer
Creative Sound Blaster X-Fi XtremeMusic

Current release features:

* ALSA PCM Playback
* ALSA Record
* ALSA Mixer

Note:

* External I/O modules detection not included.

Signed-off-by: Wai Yew CHAY <wychay@ctl.creative.com>
Singed-off-by: Ryan RICHARDS <ryan_richards@creativelabs.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
32 files changed:
Documentation/sound/alsa/ALSA-Configuration.txt
sound/pci/Kconfig
sound/pci/Makefile
sound/pci/ctxfi/Makefile [new file with mode: 0644]
sound/pci/ctxfi/ct20k1reg.h [new file with mode: 0644]
sound/pci/ctxfi/ct20k2reg.h [new file with mode: 0644]
sound/pci/ctxfi/ctamixer.c [new file with mode: 0644]
sound/pci/ctxfi/ctamixer.h [new file with mode: 0644]
sound/pci/ctxfi/ctatc.c [new file with mode: 0644]
sound/pci/ctxfi/ctatc.h [new file with mode: 0644]
sound/pci/ctxfi/ctdaio.c [new file with mode: 0644]
sound/pci/ctxfi/ctdaio.h [new file with mode: 0644]
sound/pci/ctxfi/ctdrv.h [new file with mode: 0644]
sound/pci/ctxfi/cthardware.c [new file with mode: 0644]
sound/pci/ctxfi/cthardware.h [new file with mode: 0644]
sound/pci/ctxfi/cthw20k1.c [new file with mode: 0644]
sound/pci/ctxfi/cthw20k1.h [new file with mode: 0644]
sound/pci/ctxfi/cthw20k2.c [new file with mode: 0644]
sound/pci/ctxfi/cthw20k2.h [new file with mode: 0644]
sound/pci/ctxfi/ctimap.c [new file with mode: 0644]
sound/pci/ctxfi/ctimap.h [new file with mode: 0644]
sound/pci/ctxfi/ctmixer.c [new file with mode: 0644]
sound/pci/ctxfi/ctmixer.h [new file with mode: 0644]
sound/pci/ctxfi/ctpcm.c [new file with mode: 0644]
sound/pci/ctxfi/ctpcm.h [new file with mode: 0644]
sound/pci/ctxfi/ctresource.c [new file with mode: 0644]
sound/pci/ctxfi/ctresource.h [new file with mode: 0644]
sound/pci/ctxfi/ctsrc.c [new file with mode: 0644]
sound/pci/ctxfi/ctsrc.h [new file with mode: 0644]
sound/pci/ctxfi/ctvmem.c [new file with mode: 0644]
sound/pci/ctxfi/ctvmem.h [new file with mode: 0644]
sound/pci/ctxfi/xfi.c [new file with mode: 0644]

index 012858d2b11935aa711e4cf9d2d8fbe637403813..c903db869dd6582de23a8b053282da65e7afc91c 100644 (file)
@@ -460,6 +460,25 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     The power-management is supported.
 
+  Module snd-ctxfi
+  ----------------
+
+    Module for Creative Sound Blaster X-Fi boards (20k1 / 20k2 chips)
+       * Creative Sound Blaster X-Fi Titanium Fatal1ty Champion Series
+       * Creative Sound Blaster X-Fi Titanium Fatal1ty Professional Series
+       * Creative Sound Blaster X-Fi Titanium Professional Audio
+       * Creative Sound Blaster X-Fi Titanium
+       * Creative Sound Blaster X-Fi Elite Pro
+       * Creative Sound Blaster X-Fi Platinum
+       * Creative Sound Blaster X-Fi Fatal1ty
+       * Creative Sound Blaster X-Fi XtremeGamer
+       * Creative Sound Blaster X-Fi XtremeMusic
+
+    reference_rate     - reference sample rate, 44100 or 48000 (default)
+    multiple           - multiple to ref. sample rate, 1 or 2 (default)
+
+    This module supports multiple cards.
+
   Module snd-darla20
   ------------------
 
index 93422e3a3f0cc2f642a5a30cfd09ee89cbb55a43..3a7640feaf92b6162c4a7d803b4bad653e7cd67b 100644 (file)
@@ -275,6 +275,16 @@ config SND_CS5535AUDIO
          To compile this driver as a module, choose M here: the module
          will be called snd-cs5535audio.
 
+config SND_CTXFI
+       tristate "Creative Sound Blaster X-Fi"
+       select SND_PCM
+       help
+         If you want to use soundcards based on Creative Sound Blastr X-Fi
+         boards with 20k1 or 20k2 chips, say Y here.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-ctxfi.
+
 config SND_DARLA20
        tristate "(Echoaudio) Darla20"
        select FW_LOADER
index 65b25d221cd2795135f265dcbb05d47f1dfc8a8f..6a1281ec01e3958df5fdde2f7ff9e88173149f3c 100644 (file)
@@ -59,6 +59,7 @@ obj-$(CONFIG_SND) += \
        ali5451/ \
        au88x0/ \
        aw2/ \
+       ctxfi/ \
        ca0106/ \
        cs46xx/ \
        cs5535audio/ \
diff --git a/sound/pci/ctxfi/Makefile b/sound/pci/ctxfi/Makefile
new file mode 100644 (file)
index 0000000..2904323
--- /dev/null
@@ -0,0 +1,5 @@
+snd-ctxfi-objs := xfi.o ctatc.o ctvmem.o ctpcm.o ctmixer.o ctresource.o \
+       ctsrc.o ctamixer.o ctdaio.o ctimap.o cthardware.o \
+       cthw20k2.o cthw20k1.o
+
+obj-$(CONFIG_SND_CTXFI) += snd-ctxfi.o
diff --git a/sound/pci/ctxfi/ct20k1reg.h b/sound/pci/ctxfi/ct20k1reg.h
new file mode 100644 (file)
index 0000000..c62e677
--- /dev/null
@@ -0,0 +1,634 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ */
+
+#ifndef CT20K1REG_H
+#define CT20k1REG_H
+
+/* 20k1 registers */
+#define        DSPXRAM_START                   0x000000
+#define        DSPXRAM_END                     0x013FFC
+#define        DSPAXRAM_START                  0x020000
+#define        DSPAXRAM_END                    0x023FFC
+#define        DSPYRAM_START                   0x040000
+#define        DSPYRAM_END                     0x04FFFC
+#define        DSPAYRAM_START                  0x020000
+#define        DSPAYRAM_END                    0x063FFC
+#define        DSPMICRO_START                  0x080000
+#define        DSPMICRO_END                    0x0B3FFC
+#define        DSP0IO_START                    0x100000
+#define        DSP0IO_END                      0x101FFC
+#define        AUDIORINGIPDSP0_START           0x100000
+#define        AUDIORINGIPDSP0_END             0x1003FC
+#define        AUDIORINGOPDSP0_START           0x100400
+#define        AUDIORINGOPDSP0_END             0x1007FC
+#define        AUDPARARINGIODSP0_START         0x100800
+#define        AUDPARARINGIODSP0_END           0x100BFC
+#define        DSP0LOCALHWREG_START            0x100C00
+#define        DSP0LOCALHWREG_END              0x100C3C
+#define        DSP0XYRAMAGINDEX_START          0x100C40
+#define        DSP0XYRAMAGINDEX_END            0x100C5C
+#define        DSP0XYRAMAGMDFR_START           0x100C60
+#define        DSP0XYRAMAGMDFR_END             0x100C7C
+#define        DSP0INTCONTLVEC_START           0x100C80
+#define        DSP0INTCONTLVEC_END             0x100CD8
+#define        INTCONTLGLOBALREG_START         0x100D1C
+#define        INTCONTLGLOBALREG_END           0x100D3C
+#define                HOSTINTFPORTADDRCONTDSP0        0x100D40
+#define                HOSTINTFPORTDATADSP0            0x100D44
+#define                TIME0PERENBDSP0                 0x100D60
+#define                TIME0COUNTERDSP0                0x100D64
+#define                TIME1PERENBDSP0                 0x100D68
+#define                TIME1COUNTERDSP0                0x100D6C
+#define                TIME2PERENBDSP0                 0x100D70
+#define                TIME2COUNTERDSP0                0x100D74
+#define                TIME3PERENBDSP0                 0x100D78
+#define                TIME3COUNTERDSP0                0x100D7C
+#define        XRAMINDOPERREFNOUP_STARTDSP0    0x100D80
+#define        XRAMINDOPERREFNOUP_ENDDSP0      0x100D9C
+#define        XRAMINDOPERREFUP_STARTDSP0      0x100DA0
+#define        XRAMINDOPERREFUP_ENDDSP0        0x100DBC
+#define        YRAMINDOPERREFNOUP_STARTDSP0    0x100DC0
+#define        YRAMINDOPERREFNOUP_ENDDSP0      0x100DDC
+#define        YRAMINDOPERREFUP_STARTDSP0      0x100DE0
+#define        YRAMINDOPERREFUP_ENDDSP0        0x100DFC
+#define        DSP0CONDCODE                    0x100E00
+#define        DSP0STACKFLAG                   0x100E04
+#define        DSP0PROGCOUNTSTACKPTREG         0x100E08
+#define        DSP0PROGCOUNTSTACKDATAREG       0x100E0C
+#define        DSP0CURLOOPADDRREG              0x100E10
+#define        DSP0CURLOOPCOUNT                0x100E14
+#define        DSP0TOPLOOPCOUNTSTACK           0x100E18
+#define        DSP0TOPLOOPADDRSTACK            0x100E1C
+#define        DSP0LOOPSTACKPTR                0x100E20
+#define        DSP0STASSTACKDATAREG            0x100E24
+#define        DSP0STASSTACKPTR                0x100E28
+#define        DSP0PROGCOUNT                   0x100E2C
+#define        GLOBDSPDEBGREG                  0x100E30
+#define        GLOBDSPBREPTRREG                0x100E30
+#define        DSP0XYRAMBASE_START             0x100EA0
+#define        DSP0XYRAMBASE_END               0x100EBC
+#define        DSP0XYRAMLENG_START             0x100EC0
+#define        DSP0XYRAMLENG_END               0x100EDC
+#define                SEMAPHOREREGDSP0                0x100EE0
+#define                DSP0INTCONTMASKREG              0x100EE4
+#define                DSP0INTCONTPENDREG              0x100EE8
+#define                DSP0INTCONTSERVINT              0x100EEC
+#define                DSPINTCONTEXTINTMODREG          0x100EEC
+#define                GPIODSP0                        0x100EFC
+#define        DMADSPBASEADDRREG_STARTDSP0     0x100F00
+#define        DMADSPBASEADDRREG_ENDDSP0       0x100F1C
+#define        DMAHOSTBASEADDRREG_STARTDSP0    0x100F20
+#define        DMAHOSTBASEADDRREG_ENDDSP0      0x100F3C
+#define        DMADSPCURADDRREG_STARTDSP0      0x100F40
+#define        DMADSPCURADDRREG_ENDDSP0        0x100F5C
+#define        DMAHOSTCURADDRREG_STARTDSP0     0x100F60
+#define        DMAHOSTCURADDRREG_ENDDSP0       0x100F7C
+#define        DMATANXCOUNTREG_STARTDSP0       0x100F80
+#define        DMATANXCOUNTREG_ENDDSP0         0x100F9C
+#define        DMATIMEBUGREG_STARTDSP0         0x100FA0
+#define        DMATIMEBUGREG_ENDDSP0           0x100FAC
+#define        DMACNTLMODFREG_STARTDSP0        0x100FA0
+#define        DMACNTLMODFREG_ENDDSP0          0x100FAC
+
+#define        DMAGLOBSTATSREGDSP0             0x100FEC
+#define        DSP0XGPRAM_START                0x101000
+#define        DSP0XGPRAM_END                  0x1017FC
+#define        DSP0YGPRAM_START                0x101800
+#define        DSP0YGPRAM_END                  0x101FFC
+
+
+
+
+#define        AUDIORINGIPDSP1_START           0x102000
+#define        AUDIORINGIPDSP1_END             0x1023FC
+#define        AUDIORINGOPDSP1_START           0x102400
+#define        AUDIORINGOPDSP1_END             0x1027FC
+#define        AUDPARARINGIODSP1_START         0x102800
+#define        AUDPARARINGIODSP1_END           0x102BFC
+#define        DSP1LOCALHWREG_START            0x102C00
+#define        DSP1LOCALHWREG_END              0x102C3C
+#define        DSP1XYRAMAGINDEX_START          0x102C40
+#define        DSP1XYRAMAGINDEX_END            0x102C5C
+#define        DSP1XYRAMAGMDFR_START           0x102C60
+#define        DSP1XYRAMAGMDFR_END             0x102C7C
+#define        DSP1INTCONTLVEC_START           0x102C80
+#define        DSP1INTCONTLVEC_END             0x102CD8
+#define                HOSTINTFPORTADDRCONTDSP1        0x102D40
+#define                HOSTINTFPORTDATADSP1            0x102D44
+#define                TIME0PERENBDSP1                 0x102D60
+#define                TIME0COUNTERDSP1                0x102D64
+#define                TIME1PERENBDSP1                 0x102D68
+#define                TIME1COUNTERDSP1                0x102D6C
+#define                TIME2PERENBDSP1                 0x102D70
+#define                TIME2COUNTERDSP1                0x102D74
+#define                TIME3PERENBDSP1                 0x102D78
+#define                TIME3COUNTERDSP1                0x102D7C
+#define        XRAMINDOPERREFNOUP_STARTDSP1    0x102D80
+#define        XRAMINDOPERREFNOUP_ENDDSP1      0x102D9C
+#define        XRAMINDOPERREFUP_STARTDSP1      0x102DA0
+#define        XRAMINDOPERREFUP_ENDDSP1        0x102DBC
+#define        YRAMINDOPERREFNOUP_STARTDSP1    0x102DC0
+#define        YRAMINDOPERREFNOUP_ENDDSP1      0x102DDC
+#define        YRAMINDOPERREFUP_STARTDSP1      0x102DE0
+#define        YRAMINDOPERREFUP_ENDDSP1        0x102DFC
+
+#define        DSP1CONDCODE                    0x102E00
+#define        DSP1STACKFLAG                   0x102E04
+#define        DSP1PROGCOUNTSTACKPTREG         0x102E08
+#define        DSP1PROGCOUNTSTACKDATAREG       0x102E0C
+#define        DSP1CURLOOPADDRREG              0x102E10
+#define        DSP1CURLOOPCOUNT                0x102E14
+#define        DSP1TOPLOOPCOUNTSTACK           0x102E18
+#define        DSP1TOPLOOPADDRSTACK            0x102E1C
+#define        DSP1LOOPSTACKPTR                0x102E20
+#define        DSP1STASSTACKDATAREG            0x102E24
+#define        DSP1STASSTACKPTR                0x102E28
+#define        DSP1PROGCOUNT                   0x102E2C
+#define        DSP1XYRAMBASE_START             0x102EA0
+#define        DSP1XYRAMBASE_END               0x102EBC
+#define        DSP1XYRAMLENG_START             0x102EC0
+#define        DSP1XYRAMLENG_END               0x102EDC
+#define                SEMAPHOREREGDSP1                0x102EE0
+#define                DSP1INTCONTMASKREG              0x102EE4
+#define                DSP1INTCONTPENDREG              0x102EE8
+#define                DSP1INTCONTSERVINT              0x102EEC
+#define                GPIODSP1                        0x102EFC
+#define        DMADSPBASEADDRREG_STARTDSP1     0x102F00
+#define        DMADSPBASEADDRREG_ENDDSP1       0x102F1C
+#define        DMAHOSTBASEADDRREG_STARTDSP1    0x102F20
+#define        DMAHOSTBASEADDRREG_ENDDSP1      0x102F3C
+#define        DMADSPCURADDRREG_STARTDSP1      0x102F40
+#define        DMADSPCURADDRREG_ENDDSP1        0x102F5C
+#define        DMAHOSTCURADDRREG_STARTDSP1     0x102F60
+#define        DMAHOSTCURADDRREG_ENDDSP1       0x102F7C
+#define        DMATANXCOUNTREG_STARTDSP1       0x102F80
+#define        DMATANXCOUNTREG_ENDDSP1         0x102F9C
+#define        DMATIMEBUGREG_STARTDSP1         0x102FA0
+#define        DMATIMEBUGREG_ENDDSP1           0x102FAC
+#define        DMACNTLMODFREG_STARTDSP1        0x102FA0
+#define        DMACNTLMODFREG_ENDDSP1          0x102FAC
+
+#define        DMAGLOBSTATSREGDSP1             0x102FEC
+#define        DSP1XGPRAM_START                0x103000
+#define        DSP1XGPRAM_END                  0x1033FC
+#define        DSP1YGPRAM_START                0x103400
+#define        DSP1YGPRAM_END                  0x1037FC
+
+
+
+#define        AUDIORINGIPDSP2_START           0x104000
+#define        AUDIORINGIPDSP2_END             0x1043FC
+#define        AUDIORINGOPDSP2_START           0x104400
+#define        AUDIORINGOPDSP2_END             0x1047FC
+#define        AUDPARARINGIODSP2_START         0x104800
+#define        AUDPARARINGIODSP2_END           0x104BFC
+#define        DSP2LOCALHWREG_START            0x104C00
+#define        DSP2LOCALHWREG_END              0x104C3C
+#define        DSP2XYRAMAGINDEX_START          0x104C40
+#define        DSP2XYRAMAGINDEX_END            0x104C5C
+#define        DSP2XYRAMAGMDFR_START           0x104C60
+#define        DSP2XYRAMAGMDFR_END             0x104C7C
+#define        DSP2INTCONTLVEC_START           0x104C80
+#define        DSP2INTCONTLVEC_END             0x104CD8
+#define                HOSTINTFPORTADDRCONTDSP2        0x104D40
+#define                HOSTINTFPORTDATADSP2            0x104D44
+#define                TIME0PERENBDSP2                 0x104D60
+#define                TIME0COUNTERDSP2                0x104D64
+#define                TIME1PERENBDSP2                 0x104D68
+#define                TIME1COUNTERDSP2                0x104D6C
+#define                TIME2PERENBDSP2                 0x104D70
+#define                TIME2COUNTERDSP2                0x104D74
+#define                TIME3PERENBDSP2                 0x104D78
+#define                TIME3COUNTERDSP2                0x104D7C
+#define        XRAMINDOPERREFNOUP_STARTDSP2    0x104D80
+#define        XRAMINDOPERREFNOUP_ENDDSP2      0x104D9C
+#define        XRAMINDOPERREFUP_STARTDSP2      0x104DA0
+#define        XRAMINDOPERREFUP_ENDDSP2        0x104DBC
+#define        YRAMINDOPERREFNOUP_STARTDSP2    0x104DC0
+#define        YRAMINDOPERREFNOUP_ENDDSP2      0x104DDC
+#define        YRAMINDOPERREFUP_STARTDSP2      0x104DE0
+#define        YRAMINDOPERREFUP_ENDDSP2        0x104DFC
+#define        DSP2CONDCODE                    0x104E00
+#define        DSP2STACKFLAG                   0x104E04
+#define        DSP2PROGCOUNTSTACKPTREG         0x104E08
+#define        DSP2PROGCOUNTSTACKDATAREG       0x104E0C
+#define        DSP2CURLOOPADDRREG              0x104E10
+#define        DSP2CURLOOPCOUNT                0x104E14
+#define        DSP2TOPLOOPCOUNTSTACK           0x104E18
+#define        DSP2TOPLOOPADDRSTACK            0x104E1C
+#define        DSP2LOOPSTACKPTR                0x104E20
+#define        DSP2STASSTACKDATAREG            0x104E24
+#define        DSP2STASSTACKPTR                0x104E28
+#define        DSP2PROGCOUNT                   0x104E2C
+#define        DSP2XYRAMBASE_START             0x104EA0
+#define        DSP2XYRAMBASE_END               0x104EBC
+#define        DSP2XYRAMLENG_START             0x104EC0
+#define        DSP2XYRAMLENG_END               0x104EDC
+#define                SEMAPHOREREGDSP2                0x104EE0
+#define                DSP2INTCONTMASKREG              0x104EE4
+#define                DSP2INTCONTPENDREG              0x104EE8
+#define                DSP2INTCONTSERVINT              0x104EEC
+#define                GPIODSP2                        0x104EFC
+#define        DMADSPBASEADDRREG_STARTDSP2     0x104F00
+#define        DMADSPBASEADDRREG_ENDDSP2       0x104F1C
+#define        DMAHOSTBASEADDRREG_STARTDSP2    0x104F20
+#define        DMAHOSTBASEADDRREG_ENDDSP2      0x104F3C
+#define        DMADSPCURADDRREG_STARTDSP2      0x104F40
+#define        DMADSPCURADDRREG_ENDDSP2        0x104F5C
+#define        DMAHOSTCURADDRREG_STARTDSP2     0x104F60
+#define        DMAHOSTCURADDRREG_ENDDSP2       0x104F7C
+#define        DMATANXCOUNTREG_STARTDSP2       0x104F80
+#define        DMATANXCOUNTREG_ENDDSP2         0x104F9C
+#define        DMATIMEBUGREG_STARTDSP2         0x104FA0
+#define        DMATIMEBUGREG_ENDDSP2           0x104FAC
+#define        DMACNTLMODFREG_STARTDSP2        0x104FA0
+#define        DMACNTLMODFREG_ENDDSP2          0x104FAC
+
+#define        DMAGLOBSTATSREGDSP2             0x104FEC
+#define        DSP2XGPRAM_START                0x105000
+#define        DSP2XGPRAM_END                  0x1051FC
+#define        DSP2YGPRAM_START                0x105800
+#define        DSP2YGPRAM_END                  0x1059FC
+
+
+
+#define        AUDIORINGIPDSP3_START           0x106000
+#define        AUDIORINGIPDSP3_END             0x1063FC
+#define        AUDIORINGOPDSP3_START           0x106400
+#define        AUDIORINGOPDSP3_END             0x1067FC
+#define        AUDPARARINGIODSP3_START         0x106800
+#define        AUDPARARINGIODSP3_END           0x106BFC
+#define        DSP3LOCALHWREG_START            0x106C00
+#define        DSP3LOCALHWREG_END              0x106C3C
+#define        DSP3XYRAMAGINDEX_START          0x106C40
+#define        DSP3XYRAMAGINDEX_END            0x106C5C
+#define        DSP3XYRAMAGMDFR_START           0x106C60
+#define        DSP3XYRAMAGMDFR_END             0x106C7C
+#define        DSP3INTCONTLVEC_START           0x106C80
+#define        DSP3INTCONTLVEC_END             0x106CD8
+#define                HOSTINTFPORTADDRCONTDSP3        0x106D40
+#define                HOSTINTFPORTDATADSP3            0x106D44
+#define                TIME0PERENBDSP3                 0x106D60
+#define                TIME0COUNTERDSP3                0x106D64
+#define                TIME1PERENBDSP3                 0x106D68
+#define                TIME1COUNTERDSP3                0x106D6C
+#define                TIME2PERENBDSP3                 0x106D70
+#define                TIME2COUNTERDSP3                0x106D74
+#define                TIME3PERENBDSP3                 0x106D78
+#define                TIME3COUNTERDSP3                0x106D7C
+#define        XRAMINDOPERREFNOUP_STARTDSP3    0x106D80
+#define        XRAMINDOPERREFNOUP_ENDDSP3      0x106D9C
+#define        XRAMINDOPERREFUP_STARTDSP3      0x106DA0
+#define        XRAMINDOPERREFUP_ENDDSP3        0x106DBC
+#define        YRAMINDOPERREFNOUP_STARTDSP3    0x106DC0
+#define        YRAMINDOPERREFNOUP_ENDDSP3      0x106DDC
+#define        YRAMINDOPERREFUP_STARTDSP3      0x106DE0
+#define        YRAMINDOPERREFUP_ENDDSP3        0x100DFC
+
+#define        DSP3CONDCODE                    0x106E00
+#define        DSP3STACKFLAG                   0x106E04
+#define        DSP3PROGCOUNTSTACKPTREG         0x106E08
+#define        DSP3PROGCOUNTSTACKDATAREG       0x106E0C
+#define        DSP3CURLOOPADDRREG              0x106E10
+#define        DSP3CURLOOPCOUNT                0x106E14
+#define        DSP3TOPLOOPCOUNTSTACK           0x106E18
+#define        DSP3TOPLOOPADDRSTACK            0x106E1C
+#define        DSP3LOOPSTACKPTR                0x106E20
+#define        DSP3STASSTACKDATAREG            0x106E24
+#define        DSP3STASSTACKPTR                0x106E28
+#define        DSP3PROGCOUNT                   0x106E2C
+#define        DSP3XYRAMBASE_START             0x106EA0
+#define        DSP3XYRAMBASE_END               0x106EBC
+#define        DSP3XYRAMLENG_START             0x106EC0
+#define        DSP3XYRAMLENG_END               0x106EDC
+#define                SEMAPHOREREGDSP3                0x106EE0
+#define                DSP3INTCONTMASKREG              0x106EE4
+#define                DSP3INTCONTPENDREG              0x106EE8
+#define                DSP3INTCONTSERVINT              0x106EEC
+#define                GPIODSP3                        0x106EFC
+#define        DMADSPBASEADDRREG_STARTDSP3     0x106F00
+#define        DMADSPBASEADDRREG_ENDDSP3       0x106F1C
+#define        DMAHOSTBASEADDRREG_STARTDSP3    0x106F20
+#define        DMAHOSTBASEADDRREG_ENDDSP3      0x106F3C
+#define        DMADSPCURADDRREG_STARTDSP3      0x106F40
+#define        DMADSPCURADDRREG_ENDDSP3        0x106F5C
+#define        DMAHOSTCURADDRREG_STARTDSP3     0x106F60
+#define        DMAHOSTCURADDRREG_ENDDSP3       0x106F7C
+#define        DMATANXCOUNTREG_STARTDSP3       0x106F80
+#define        DMATANXCOUNTREG_ENDDSP3         0x106F9C
+#define        DMATIMEBUGREG_STARTDSP3         0x106FA0
+#define        DMATIMEBUGREG_ENDDSP3           0x106FAC
+#define        DMACNTLMODFREG_STARTDSP3        0x106FA0
+#define        DMACNTLMODFREG_ENDDSP3          0x106FAC
+
+#define        DMAGLOBSTATSREGDSP3             0x106FEC
+#define        DSP3XGPRAM_START                0x107000
+#define        DSP3XGPRAM_END                  0x1071FC
+#define        DSP3YGPRAM_START                0x107800
+#define        DSP3YGPRAM_END                  0x1079FC
+
+/* end of DSP reg definitions */
+
+#define        DSPAIMAP_START                  0x108000
+#define        DSPAIMAP_END                    0x1083FC
+#define        DSPPIMAP_START                  0x108400
+#define        DSPPIMAP_END                    0x1087FC
+#define        DSPPOMAP_START                  0x108800
+#define        DSPPOMAP_END                    0x108BFC
+#define        DSPPOCTL                        0x108C00
+#define        TKCTL_START                     0x110000
+#define        TKCTL_END                       0x110FFC
+#define        TKCC_START                      0x111000
+#define        TKCC_END                        0x111FFC
+#define        TKIMAP_START                    0x112000
+#define        TKIMAP_END                      0x112FFC
+#define                TKDCTR16                        0x113000
+#define                TKPB16                          0x113004
+#define                TKBS16                          0x113008
+#define                TKDCTR32                        0x11300C
+#define                TKPB32                          0x113010
+#define                TKBS32                          0x113014
+#define                ICDCTR16                        0x113018
+#define                ITBS16                          0x11301C
+#define                ICDCTR32                        0x113020
+#define                ITBS32                          0x113024
+#define                ITSTART                         0x113028
+#define                TKSQ                            0x11302C
+
+#define                TKSCCTL_START                   0x114000
+#define                TKSCCTL_END                     0x11403C
+#define                TKSCADR_START                   0x114100
+#define                TKSCADR_END                     0x11413C
+#define                TKSCDATAX_START                 0x114800
+#define                TKSCDATAX_END                   0x1149FC
+#define                TKPCDATAX_START                 0x120000
+#define                TKPCDATAX_END                   0x12FFFC
+
+#define                MALSA                           0x130000
+#define                MAPPHA                          0x130004
+#define                MAPPLA                          0x130008
+#define                MALSB                           0x130010
+#define                MAPPHB                          0x130014
+#define                MAPPLB                          0x130018
+
+#define        TANSPORTMAPABREGS_START         0x130020
+#define        TANSPORTMAPABREGS_END           0x13A2FC
+
+#define                PTPAHX                          0x13B000
+#define                PTPALX                          0x13B004
+
+#define                TANSPPAGETABLEPHYADDR015_START  0x13B008
+#define                TANSPPAGETABLEPHYADDR015_END    0x13B07C
+#define                TRNQADRX_START                  0x13B100
+#define                TRNQADRX_END                    0x13B13C
+#define                TRNQTIMX_START                  0x13B200
+#define                TRNQTIMX_END                    0x13B23C
+#define                TRNQAPARMX_START                0x13B300
+#define                TRNQAPARMX_END                  0x13B33C
+
+#define                TRNQCNT                         0x13B400
+#define                TRNCTL                          0x13B404
+#define                TRNIS                           0x13B408
+#define                TRNCURTS                        0x13B40C
+
+#define                AMOP_START                      0x140000
+#define                AMOPLO                          0x140000
+#define                AMOPHI                          0x140004
+#define                AMOP_END                        0x147FFC
+#define                PMOP_START                      0x148000
+#define                PMOPLO                          0x148000
+#define                PMOPHI                          0x148004
+#define                PMOP_END                        0x14FFFC
+#define                PCURR_START                     0x150000
+#define                PCURR_END                       0x153FFC
+#define                PTRAG_START                     0x154000
+#define                PTRAG_END                       0x157FFC
+#define                PSR_START                       0x158000
+#define                PSR_END                         0x15BFFC
+
+#define                PFSTAT4SEG_START                0x160000
+#define                PFSTAT4SEG_END                  0x160BFC
+#define                PFSTAT2SEG_START                0x160C00
+#define                PFSTAT2SEG_END                  0x1617FC
+#define                PFTARG4SEG_START                0x164000
+#define                PFTARG4SEG_END                  0x164BFC
+#define                PFTARG2SEG_START                0x164C00
+#define                PFTARG2SEG_END                  0x1657FC
+#define                PFSR4SEG_START                  0x168000
+#define                PFSR4SEG_END                    0x168BFC
+#define                PFSR2SEG_START                  0x168C00
+#define                PFSR2SEG_END                    0x1697FC
+#define                PCURRMS4SEG_START               0x16C000
+#define                PCURRMS4SEG_END                 0x16CCFC
+#define                PCURRMS2SEG_START               0x16CC00
+#define                PCURRMS2SEG_END                 0x16D7FC
+#define                PTARGMS4SEG_START               0x170000
+#define                PTARGMS4SEG_END                 0x172FFC
+#define                PTARGMS2SEG_START               0x173000
+#define                PTARGMS2SEG_END                 0x1747FC
+#define                PSRMS4SEG_START                 0x170000
+#define                PSRMS4SEG_END                   0x172FFC
+#define                PSRMS2SEG_START                 0x173000
+#define                PSRMS2SEG_END                   0x1747FC
+
+#define                PRING_LO_START                  0x190000
+#define                PRING_LO_END                    0x193FFC
+#define                PRING_HI_START                  0x194000
+#define                PRING_HI_END                    0x197FFC
+#define                PRING_LO_HI_START               0x198000
+#define                PRING_LO_HI                     0x198000
+#define                PRING_LO_HI_END                 0x19BFFC
+
+#define                PINTFIFO                        0x1A0000
+#define                SRCCTL                          0x1B0000
+#define                SRCCCR                          0x1B0004
+#define                SRCIMAP                         0x1B0008
+#define                SRCODDC                         0x1B000C
+#define                SRCCA                           0x1B0010
+#define                SRCCF                           0x1B0014
+#define                SRCSA                           0x1B0018
+#define                SRCLA                           0x1B001C
+#define                SRCCTLSWR                       0x1B0020
+
+/* SRC HERE */
+#define                SRCALBA                         0x1B002C
+#define                SRCMCTL                         0x1B012C
+#define                SRCCERR                         0x1B022C
+#define                SRCITB                          0x1B032C
+#define                SRCIPM                          0x1B082C
+#define                SRCIP                           0x1B102C
+#define                SRCENBSTAT                      0x1B202C
+#define                SRCENBLO                        0x1B212C
+#define                SRCENBHI                        0x1B222C
+#define                SRCENBS                         0x1B232C
+#define                SRCENB                          0x1B282C
+#define                SRCENB07                        0x1B282C
+#define                SRCENBS07                       0x1B302C
+
+#define                SRCDN0Z                         0x1B0030
+#define                SRCDN0Z0                        0x1B0030
+#define                SRCDN0Z1                        0x1B0034
+#define                SRCDN0Z2                        0x1B0038
+#define                SRCDN0Z3                        0x1B003C
+#define                SRCDN1Z                         0x1B0040
+#define                SRCDN1Z0                        0x1B0040
+#define                SRCDN1Z1                        0x1B0044
+#define                SRCDN1Z2                        0x1B0048
+#define                SRCDN1Z3                        0x1B004C
+#define                SRCDN1Z4                        0x1B0050
+#define                SRCDN1Z5                        0x1B0054
+#define                SRCDN1Z6                        0x1B0058
+#define                SRCDN1Z7                        0x1B005C
+#define                SRCUPZ                          0x1B0060
+#define                SRCUPZ0                         0x1B0060
+#define                SRCUPZ1                         0x1B0064
+#define                SRCUPZ2                         0x1B0068
+#define                SRCUPZ3                         0x1B006C
+#define                SRCUPZ4                         0x1B0070
+#define                SRCUPZ5                         0x1B0074
+#define                SRCUPZ6                         0x1B0078
+#define                SRCUPZ7                         0x1B007C
+#define                SRCCD0                          0x1B0080
+#define                SRCCD1                          0x1B0084
+#define                SRCCD2                          0x1B0088
+#define                SRCCD3                          0x1B008C
+#define                SRCCD4                          0x1B0090
+#define                SRCCD5                          0x1B0094
+#define                SRCCD6                          0x1B0098
+#define                SRCCD7                          0x1B009C
+#define                SRCCD8                          0x1B00A0
+#define                SRCCD9                          0x1B00A4
+#define                SRCCDA                          0x1B00A8
+#define                SRCCDB                          0x1B00AC
+#define                SRCCDC                          0x1B00B0
+#define                SRCCDD                          0x1B00B4
+#define                SRCCDE                          0x1B00B8
+#define                SRCCDF                          0x1B00BC
+#define                SRCCD10                         0x1B00C0
+#define                SRCCD11                         0x1B00C4
+#define                SRCCD12                         0x1B00C8
+#define                SRCCD13                         0x1B00CC
+#define                SRCCD14                         0x1B00D0
+#define                SRCCD15                         0x1B00D4
+#define                SRCCD16                         0x1B00D8
+#define                SRCCD17                         0x1B00DC
+#define                SRCCD18                         0x1B00E0
+#define                SRCCD19                         0x1B00E4
+#define                SRCCD1A                         0x1B00E8
+#define                SRCCD1B                         0x1B00EC
+#define                SRCCD1C                         0x1B00F0
+#define                SRCCD1D                         0x1B00F4
+#define                SRCCD1E                         0x1B00F8
+#define                SRCCD1F                         0x1B00FC
+
+#define                SRCCONTRBLOCK_START             0x1B0100
+#define                SRCCONTRBLOCK_END               0x1BFFFC
+#define                FILTOP_START    0x1C0000
+#define                FILTOP_END      0x1C05FC
+#define                FILTIMAP_START  0x1C0800
+#define                FILTIMAP_END    0x1C0DFC
+#define                FILTZ1_START    0x1C1000
+#define                FILTZ1_END      0x1C15FC
+#define                FILTZ2_START    0x1C1800
+#define                FILTZ2_END      0x1C1DFC
+#define                DAOIMAP_START   0x1C5000
+#define                DAOIMAP         0x1C5000
+#define                DAOIMAP_END     0x1C5124
+
+#define                AC97D           0x1C5400
+#define                AC97A           0x1C5404
+#define                AC97CTL         0x1C5408
+#define                I2SCTL          0x1C5420
+
+#define                SPOS            0x1C5440
+#define                SPOSA           0x1C5440
+#define                SPOSB           0x1C5444
+#define                SPOSC           0x1C5448
+#define                SPOSD           0x1C544C
+
+#define                SPISA           0x1C5450
+#define                SPISB           0x1C5454
+#define                SPISC           0x1C5458
+#define                SPISD           0x1C545C
+
+#define                SPFSCTL         0x1C5460
+
+#define                SPFS0           0x1C5468
+#define                SPFS1           0x1C546C
+#define                SPFS2           0x1C5470
+#define                SPFS3           0x1C5474
+#define                SPFS4           0x1C5478
+#define                SPFS5           0x1C547C
+
+#define                SPOCTL          0x1C5480
+#define                SPICTL          0x1C5484
+#define                SPISTS          0x1C5488
+#define                SPINTP          0x1C548C
+#define                SPINTE          0x1C5490
+#define                SPUTCTLAB       0x1C5494
+#define                SPUTCTLCD       0x1C5498
+
+#define                SRTSPA          0x1C54C0
+#define                SRTSPB          0x1C54C4
+#define                SRTSPC          0x1C54C8
+#define                SRTSPD          0x1C54CC
+
+#define                SRTSCTL         0x1C54D0
+#define                SRTSCTLA        0x1C54D0
+#define                SRTSCTLB        0x1C54D4
+#define                SRTSCTLC        0x1C54D8
+#define                SRTSCTLD        0x1C54DC
+
+#define                SRTI2S          0x1C54E0
+#define                SRTICTL         0x1C54F0
+
+#define                WC              0x1C6000
+#define                TIMR            0x1C6004
+
+#define                GIP             0x1C6010
+#define                GIE             0x1C6014
+#define                DIE             0x1C6018
+#define                DIC             0x1C601C
+#define                GPIO            0x1C6020
+#define                GPIOCTL         0x1C6024
+#define                GPIP            0x1C6028
+#define                GPIE            0x1C602C
+#define                DSPINT0         0x1C6030
+#define                DSPEIOC         0x1C6034
+#define                MUADAT          0x1C6040
+#define                MUACMD          0x1C6044
+#define        MUASTAT         0x1C6044
+#define                MUBDAT          0x1C6048
+#define                MUBCMD          0x1C604C
+#define                MUBSTAT         0x1C604C
+#define                UARTCMA         0x1C6050
+#define                UARTCMB         0x1C6054
+#define                UARTIP          0x1C6058
+#define                UARTIE          0x1C605C
+#define                PLLCTL          0x1C6060
+#define                PLLDCD          0x1C6064
+#define                GCTL            0x1C6070
+#define                ID0             0x1C6080
+#define                ID1             0x1C6084
+#define                ID2             0x1C6088
+#define                ID3             0x1C608C
+#define                SDRCTL          0x1C7000
+
+
+#define I2SA_L    0x0L
+#define I2SA_R    0x1L
+#define I2SB_L    0x8L
+#define I2SB_R    0x9L
+#define I2SC_L    0x10L
+#define I2SC_R    0x11L
+#define I2SD_L    0x18L
+#define I2SD_R    0x19L
+
+#endif /* CT20K1REG_H */
+
+
diff --git a/sound/pci/ctxfi/ct20k2reg.h b/sound/pci/ctxfi/ct20k2reg.h
new file mode 100644 (file)
index 0000000..2d07986
--- /dev/null
@@ -0,0 +1,85 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ */
+
+#ifndef _20K2REGISTERS_H_
+#define _20K2REGISTERS_H_
+
+
+/* Timer Registers */
+#define TIMER_TIMR          0x1B7004
+#define INTERRUPT_GIP       0x1B7010
+#define INTERRUPT_GIE       0x1B7014
+
+/* I2C Registers */
+#define I2C_IF_ADDRESS   0x1B9000
+#define I2C_IF_WDATA     0x1B9004
+#define I2C_IF_RDATA     0x1B9008
+#define I2C_IF_STATUS    0x1B900C
+#define I2C_IF_WLOCK     0x1B9010
+
+/* Global Control Registers */
+#define GLOBAL_CNTL_GCTL    0x1B7090
+
+/* PLL Registers */
+#define PLL_CTL                0x1B7080
+#define PLL_STAT               0x1B7084
+#define PLL_ENB                        0x1B7088
+
+/* SRC Registers */
+#define SRC_CTL             0x1A0000 /* 0x1A0000 + (256 * Chn) */
+#define SRC_CCR             0x1A0004 /* 0x1A0004 + (256 * Chn) */
+#define SRC_IMAP            0x1A0008 /* 0x1A0008 + (256 * Chn) */
+#define SRC_CA              0x1A0010 /* 0x1A0010 + (256 * Chn) */
+#define SRC_CF              0x1A0014 /* 0x1A0014 + (256 * Chn) */
+#define SRC_SA              0x1A0018 /* 0x1A0018 + (256 * Chn) */
+#define SRC_LA              0x1A001C /* 0x1A001C + (256 * Chn) */
+#define SRC_CTLSWR         0x1A0020 /* 0x1A0020 + (256 * Chn) */
+#define SRC_CD             0x1A0080 /* 0x1A0080 + (256 * Chn) + (4 * Regn) */
+#define SRC_MCTL               0x1A012C
+#define SRC_IP                 0x1A102C /* 0x1A102C + (256 * Regn) */
+#define SRC_ENB                        0x1A282C /* 0x1A282C + (256 * Regn) */
+#define SRC_ENBSTAT            0x1A202C
+#define SRC_ENBSA              0x1A232C
+#define SRC_DN0Z               0x1A0030
+#define SRC_DN1Z               0x1A0040
+#define SRC_UPZ                        0x1A0060
+
+/* GPIO Registers */
+#define GPIO_DATA           0x1B7020
+#define GPIO_CTRL           0x1B7024
+
+/* Virtual memory registers */
+#define VMEM_PTPAL          0x1C6300 /* 0x1C6300 + (16 * Chn) */
+#define VMEM_PTPAH          0x1C6304 /* 0x1C6304 + (16 * Chn) */
+#define VMEM_CTL            0x1C7000
+
+/* Transport Registers */
+#define TRANSPORT_ENB       0x1B6000
+#define TRANSPORT_CTL       0x1B6004
+#define TRANSPORT_INT       0x1B6008
+
+/* Audio IO */
+#define AUDIO_IO_AIM        0x1B5000 /* 0x1B5000 + (0x04 * Chn) */
+#define AUDIO_IO_TX_CTL     0x1B5400 /* 0x1B5400 + (0x40 * Chn) */
+#define AUDIO_IO_TX_CSTAT_L 0x1B5408 /* 0x1B5408 + (0x40 * Chn) */
+#define AUDIO_IO_TX_CSTAT_H 0x1B540C /* 0x1B540C + (0x40 * Chn) */
+#define AUDIO_IO_RX_CTL     0x1B5410 /* 0x1B5410 + (0x40 * Chn) */
+#define AUDIO_IO_RX_SRT_CTL 0x1B5420 /* 0x1B5420 + (0x40 * Chn) */
+#define AUDIO_IO_MCLK       0x1B5600
+#define AUDIO_IO_TX_BLRCLK  0x1B5604
+#define AUDIO_IO_RX_BLRCLK  0x1B5608
+
+/* Mixer */
+#define MIXER_AMOPLO           0x130000 /* 0x130000 + (8 * Chn) [4095 : 0] */
+#define MIXER_AMOPHI           0x130004 /* 0x130004 + (8 * Chn) [4095 : 0] */
+#define MIXER_PRING_LO_HI      0x188000 /* 0x188000 + (4 * Chn) [4095 : 0] */
+#define MIXER_PMOPLO           0x138000 /* 0x138000 + (8 * Chn) [4095 : 0] */
+#define MIXER_PMOPHI           0x138004 /* 0x138004 + (8 * Chn) [4095 : 0] */
+#define MIXER_AR_ENABLE                0x19000C
+
+#endif
diff --git a/sound/pci/ctxfi/ctamixer.c b/sound/pci/ctxfi/ctamixer.c
new file mode 100644 (file)
index 0000000..119791a
--- /dev/null
@@ -0,0 +1,488 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       ctamixer.c
+ *
+ * @Brief
+ * This file contains the implementation of the Audio Mixer
+ * resource management object.
+ *
+ * @Author     Liu Chun
+ * @Date       May 21 2008
+ *
+ */
+
+#include "ctamixer.h"
+#include "cthardware.h"
+#include <linux/slab.h>
+
+#define AMIXER_RESOURCE_NUM    256
+#define SUM_RESOURCE_NUM       256
+
+#define AMIXER_Y_IMMEDIATE     1
+
+#define BLANK_SLOT             4094
+
+static int amixer_master(struct rsc *rsc)
+{
+       rsc->conj = 0;
+       return rsc->idx = container_of(rsc, struct amixer, rsc)->idx[0];
+}
+
+static int amixer_next_conj(struct rsc *rsc)
+{
+       rsc->conj++;
+       return container_of(rsc, struct amixer, rsc)->idx[rsc->conj];
+}
+
+static int amixer_index(const struct rsc *rsc)
+{
+       return container_of(rsc, struct amixer, rsc)->idx[rsc->conj];
+}
+
+static int amixer_output_slot(const struct rsc *rsc)
+{
+       return (amixer_index(rsc) << 4) + 0x4;
+}
+
+static struct rsc_ops amixer_basic_rsc_ops = {
+       .master         = amixer_master,
+       .next_conj      = amixer_next_conj,
+       .index          = amixer_index,
+       .output_slot    = amixer_output_slot,
+};
+
+static int amixer_set_input(struct amixer *amixer, struct rsc *rsc)
+{
+       struct hw *hw = NULL;
+
+       hw = (struct hw *)amixer->rsc.hw;
+       hw->amixer_set_mode(amixer->rsc.ctrl_blk, AMIXER_Y_IMMEDIATE);
+       amixer->input = rsc;
+       if (NULL == rsc)
+               hw->amixer_set_x(amixer->rsc.ctrl_blk, BLANK_SLOT);
+       else
+               hw->amixer_set_x(amixer->rsc.ctrl_blk,
+                                       rsc->ops->output_slot(rsc));
+
+       return 0;
+}
+
+/* y is a 14-bit immediate constant */
+static int amixer_set_y(struct amixer *amixer, unsigned int y)
+{
+       struct hw *hw = NULL;
+
+       hw = (struct hw *)amixer->rsc.hw;
+       hw->amixer_set_y(amixer->rsc.ctrl_blk, y);
+
+       return 0;
+}
+
+static int amixer_set_invalid_squash(struct amixer *amixer, unsigned int iv)
+{
+       struct hw *hw = NULL;
+
+       hw = (struct hw *)amixer->rsc.hw;
+       hw->amixer_set_iv(amixer->rsc.ctrl_blk, iv);
+
+       return 0;
+}
+
+static int amixer_set_sum(struct amixer *amixer, struct sum *sum)
+{
+       struct hw *hw = NULL;
+
+       hw = (struct hw *)amixer->rsc.hw;
+       amixer->sum = sum;
+       if (NULL == sum) {
+               hw->amixer_set_se(amixer->rsc.ctrl_blk, 0);
+       } else {
+               hw->amixer_set_se(amixer->rsc.ctrl_blk, 1);
+               hw->amixer_set_sadr(amixer->rsc.ctrl_blk,
+                                       sum->rsc.ops->index(&sum->rsc));
+       }
+
+       return 0;
+}
+
+static int amixer_commit_write(struct amixer *amixer)
+{
+       struct hw *hw = NULL;
+       unsigned int index = 0;
+       int i = 0;
+       struct rsc *input = NULL;
+       struct sum *sum = NULL;
+
+       hw = (struct hw *)amixer->rsc.hw;
+       input = amixer->input;
+       sum = amixer->sum;
+
+       /* Program master and conjugate resources */
+       amixer->rsc.ops->master(&amixer->rsc);
+       if (NULL != input)
+               input->ops->master(input);
+
+       if (NULL != sum)
+               sum->rsc.ops->master(&sum->rsc);
+
+       for (i = 0; i < amixer->rsc.msr; i++) {
+               hw->amixer_set_dirty_all(amixer->rsc.ctrl_blk);
+               if (NULL != input) {
+                       hw->amixer_set_x(amixer->rsc.ctrl_blk,
+                                               input->ops->output_slot(input));
+                       input->ops->next_conj(input);
+               }
+               if (NULL != sum) {
+                       hw->amixer_set_sadr(amixer->rsc.ctrl_blk,
+                                               sum->rsc.ops->index(&sum->rsc));
+                       sum->rsc.ops->next_conj(&sum->rsc);
+               }
+               index = amixer->rsc.ops->output_slot(&amixer->rsc);
+               hw->amixer_commit_write(hw, index, amixer->rsc.ctrl_blk);
+               amixer->rsc.ops->next_conj(&amixer->rsc);
+       }
+       amixer->rsc.ops->master(&amixer->rsc);
+       if (NULL != input)
+               input->ops->master(input);
+
+       if (NULL != sum)
+               sum->rsc.ops->master(&sum->rsc);
+
+       return 0;
+}
+
+static int amixer_commit_raw_write(struct amixer *amixer)
+{
+       struct hw *hw = NULL;
+       unsigned int index = 0;
+
+       hw = (struct hw *)amixer->rsc.hw;
+       index = amixer->rsc.ops->output_slot(&amixer->rsc);
+       hw->amixer_commit_write(hw, index, amixer->rsc.ctrl_blk);
+
+       return 0;
+}
+
+static int amixer_get_y(struct amixer *amixer)
+{
+       struct hw *hw = NULL;
+
+       hw = (struct hw *)amixer->rsc.hw;
+       return hw->amixer_get_y(amixer->rsc.ctrl_blk);
+}
+
+static int amixer_setup(struct amixer *amixer, struct rsc *input,
+                       unsigned int scale, struct sum *sum)
+{
+       amixer_set_input(amixer, input);
+       amixer_set_y(amixer, scale);
+       amixer_set_sum(amixer, sum);
+       amixer_commit_write(amixer);
+       return 0;
+}
+
+static struct amixer_rsc_ops amixer_ops = {
+       .set_input              = amixer_set_input,
+       .set_invalid_squash     = amixer_set_invalid_squash,
+       .set_scale              = amixer_set_y,
+       .set_sum                = amixer_set_sum,
+       .commit_write           = amixer_commit_write,
+       .commit_raw_write       = amixer_commit_raw_write,
+       .setup                  = amixer_setup,
+       .get_scale              = amixer_get_y,
+};
+
+static int amixer_rsc_init(struct amixer *amixer,
+                          const struct amixer_desc *desc,
+                          struct amixer_mgr *mgr)
+{
+       int err = 0;
+
+       err = rsc_init(&amixer->rsc, amixer->idx[0],
+                       AMIXER, desc->msr, mgr->mgr.hw);
+       if (err)
+               return err;
+
+       /* Set amixer specific operations */
+       amixer->rsc.ops = &amixer_basic_rsc_ops;
+       amixer->ops = &amixer_ops;
+       amixer->input = NULL;
+       amixer->sum = NULL;
+
+       amixer_setup(amixer, NULL, 0, NULL);
+
+       return 0;
+}
+
+static int amixer_rsc_uninit(struct amixer *amixer)
+{
+       amixer_setup(amixer, NULL, 0, NULL);
+       rsc_uninit(&amixer->rsc);
+       amixer->ops = NULL;
+       amixer->input = NULL;
+       amixer->sum = NULL;
+       return 0;
+}
+
+static int get_amixer_rsc(struct amixer_mgr *mgr,
+                         const struct amixer_desc *desc,
+                         struct amixer **ramixer)
+{
+       int err = 0, i = 0;
+       unsigned int idx = 0;
+       struct amixer *amixer = NULL;
+       unsigned long flags;
+
+       *ramixer = NULL;
+
+       /* Allocate mem for amixer resource */
+       amixer = kzalloc(sizeof(*amixer), GFP_KERNEL);
+       if (NULL == amixer) {
+               err = -ENOMEM;
+               return err;
+       }
+
+       /* Check whether there are sufficient
+        * amixer resources to meet request. */
+       spin_lock_irqsave(&mgr->mgr_lock, flags);
+       for (i = 0; i < desc->msr; i++) {
+               err = mgr_get_resource(&mgr->mgr, 1, &idx);
+               if (err)
+                       break;
+
+               amixer->idx[i] = idx;
+       }
+       spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+       if (err) {
+               printk(KERN_ERR "Can't meet AMIXER resource request!\n");
+               goto error;
+       }
+
+       err = amixer_rsc_init(amixer, desc, mgr);
+       if (err)
+               goto error;
+
+       *ramixer = amixer;
+
+       return 0;
+
+error:
+       spin_lock_irqsave(&mgr->mgr_lock, flags);
+       for (i--; i >= 0; i--)
+               mgr_put_resource(&mgr->mgr, 1, amixer->idx[i]);
+
+       spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+       kfree(amixer);
+       return err;
+}
+
+static int put_amixer_rsc(struct amixer_mgr *mgr, struct amixer *amixer)
+{
+       unsigned long flags;
+       int i = 0;
+
+       spin_lock_irqsave(&mgr->mgr_lock, flags);
+       for (i = 0; i < amixer->rsc.msr; i++)
+               mgr_put_resource(&mgr->mgr, 1, amixer->idx[i]);
+
+       spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+       amixer_rsc_uninit(amixer);
+       kfree(amixer);
+
+       return 0;
+}
+
+int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr)
+{
+       int err = 0;
+       struct amixer_mgr *amixer_mgr;
+
+       *ramixer_mgr = NULL;
+       amixer_mgr = kzalloc(sizeof(*amixer_mgr), GFP_KERNEL);
+       if (NULL == amixer_mgr)
+               return -ENOMEM;
+
+       err = rsc_mgr_init(&amixer_mgr->mgr, AMIXER, AMIXER_RESOURCE_NUM, hw);
+       if (err)
+               goto error;
+
+       spin_lock_init(&amixer_mgr->mgr_lock);
+
+       amixer_mgr->get_amixer = get_amixer_rsc;
+       amixer_mgr->put_amixer = put_amixer_rsc;
+
+       *ramixer_mgr = amixer_mgr;
+
+       return 0;
+
+error:
+       kfree(amixer_mgr);
+       return err;
+}
+
+int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr)
+{
+       rsc_mgr_uninit(&amixer_mgr->mgr);
+       kfree(amixer_mgr);
+       return 0;
+}
+
+/* SUM resource management */
+
+static int sum_master(struct rsc *rsc)
+{
+       rsc->conj = 0;
+       return rsc->idx = container_of(rsc, struct sum, rsc)->idx[0];
+}
+
+static int sum_next_conj(struct rsc *rsc)
+{
+       rsc->conj++;
+       return container_of(rsc, struct sum, rsc)->idx[rsc->conj];
+}
+
+static int sum_index(const struct rsc *rsc)
+{
+       return container_of(rsc, struct sum, rsc)->idx[rsc->conj];
+}
+
+static int sum_output_slot(const struct rsc *rsc)
+{
+       return (sum_index(rsc) << 4) + 0xc;
+}
+
+static struct rsc_ops sum_basic_rsc_ops = {
+       .master         = sum_master,
+       .next_conj      = sum_next_conj,
+       .index          = sum_index,
+       .output_slot    = sum_output_slot,
+};
+
+static int sum_rsc_init(struct sum *sum,
+                       const struct sum_desc *desc,
+                       struct sum_mgr *mgr)
+{
+       int err = 0;
+
+       err = rsc_init(&sum->rsc, sum->idx[0], SUM, desc->msr, mgr->mgr.hw);
+       if (err)
+               return err;
+
+       sum->rsc.ops = &sum_basic_rsc_ops;
+
+       return 0;
+}
+
+static int sum_rsc_uninit(struct sum *sum)
+{
+       rsc_uninit(&sum->rsc);
+       return 0;
+}
+
+static int get_sum_rsc(struct sum_mgr *mgr,
+                      const struct sum_desc *desc,
+                      struct sum **rsum)
+{
+       int err = 0, i = 0;
+       unsigned int idx = 0;
+       struct sum *sum = NULL;
+       unsigned long flags;
+
+       *rsum = NULL;
+
+       /* Allocate mem for sum resource */
+       sum = kzalloc(sizeof(*sum), GFP_KERNEL);
+       if (NULL == sum) {
+               err = -ENOMEM;
+               return err;
+       }
+
+       /* Check whether there are sufficient sum resources to meet request. */
+       spin_lock_irqsave(&mgr->mgr_lock, flags);
+       for (i = 0; i < desc->msr; i++) {
+               err = mgr_get_resource(&mgr->mgr, 1, &idx);
+               if (err)
+                       break;
+
+               sum->idx[i] = idx;
+       }
+       spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+       if (err) {
+               printk(KERN_ERR "Can't meet SUM resource request!\n");
+               goto error;
+       }
+
+       err = sum_rsc_init(sum, desc, mgr);
+       if (err)
+               goto error;
+
+       *rsum = sum;
+
+       return 0;
+
+error:
+       spin_lock_irqsave(&mgr->mgr_lock, flags);
+       for (i--; i >= 0; i--)
+               mgr_put_resource(&mgr->mgr, 1, sum->idx[i]);
+
+       spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+       kfree(sum);
+       return err;
+}
+
+static int put_sum_rsc(struct sum_mgr *mgr, struct sum *sum)
+{
+       unsigned long flags;
+       int i = 0;
+
+       spin_lock_irqsave(&mgr->mgr_lock, flags);
+       for (i = 0; i < sum->rsc.msr; i++)
+               mgr_put_resource(&mgr->mgr, 1, sum->idx[i]);
+
+       spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+       sum_rsc_uninit(sum);
+       kfree(sum);
+
+       return 0;
+}
+
+int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr)
+{
+       int err = 0;
+       struct sum_mgr *sum_mgr;
+
+       *rsum_mgr = NULL;
+       sum_mgr = kzalloc(sizeof(*sum_mgr), GFP_KERNEL);
+       if (NULL == sum_mgr)
+               return -ENOMEM;
+
+       err = rsc_mgr_init(&sum_mgr->mgr, SUM, SUM_RESOURCE_NUM, hw);
+       if (err)
+               goto error;
+
+       spin_lock_init(&sum_mgr->mgr_lock);
+
+       sum_mgr->get_sum = get_sum_rsc;
+       sum_mgr->put_sum = put_sum_rsc;
+
+       *rsum_mgr = sum_mgr;
+
+       return 0;
+
+error:
+       kfree(sum_mgr);
+       return err;
+}
+
+int sum_mgr_destroy(struct sum_mgr *sum_mgr)
+{
+       rsc_mgr_uninit(&sum_mgr->mgr);
+       kfree(sum_mgr);
+       return 0;
+}
+
diff --git a/sound/pci/ctxfi/ctamixer.h b/sound/pci/ctxfi/ctamixer.h
new file mode 100644 (file)
index 0000000..cc49e5a
--- /dev/null
@@ -0,0 +1,96 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       ctamixer.h
+ *
+ * @Brief
+ * This file contains the definition of the Audio Mixer
+ * resource management object.
+ *
+ * @Author     Liu Chun
+ * @Date       May 21 2008
+ *
+ */
+
+#ifndef CTAMIXER_H
+#define CTAMIXER_H
+
+#include "ctresource.h"
+#include <linux/spinlock.h>
+
+/* Define the descriptor of a summation node resource */
+struct sum {
+       struct rsc rsc;         /* Basic resource info */
+       unsigned char idx[8];
+};
+
+/* Define sum resource request description info */
+struct sum_desc {
+       unsigned int msr;
+};
+
+struct sum_mgr {
+       struct rsc_mgr mgr;     /* Basic resource manager info */
+       spinlock_t mgr_lock;
+
+        /* request one sum resource */
+       int (*get_sum)(struct sum_mgr *mgr,
+                       const struct sum_desc *desc, struct sum **rsum);
+       /* return one sum resource */
+       int (*put_sum)(struct sum_mgr *mgr, struct sum *sum);
+};
+
+/* Constructor and destructor of daio resource manager */
+int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr);
+int sum_mgr_destroy(struct sum_mgr *sum_mgr);
+
+/* Define the descriptor of a amixer resource */
+struct amixer_rsc_ops;
+
+struct amixer {
+       struct rsc rsc;         /* Basic resource info */
+       unsigned char idx[8];
+       struct rsc *input;      /* pointer to a resource acting as source */
+       struct sum *sum;        /* Put amixer output to this summation node */
+       struct amixer_rsc_ops *ops;     /* AMixer specific operations */
+};
+
+struct amixer_rsc_ops {
+       int (*set_input)(struct amixer *amixer, struct rsc *rsc);
+       int (*set_scale)(struct amixer *amixer, unsigned int scale);
+       int (*set_invalid_squash)(struct amixer *amixer, unsigned int iv);
+       int (*set_sum)(struct amixer *amixer, struct sum *sum);
+       int (*commit_write)(struct amixer *amixer);
+       /* Only for interleaved recording */
+       int (*commit_raw_write)(struct amixer *amixer);
+       int (*setup)(struct amixer *amixer, struct rsc *input,
+                       unsigned int scale, struct sum *sum);
+       int (*get_scale)(struct amixer *amixer);
+};
+
+/* Define amixer resource request description info */
+struct amixer_desc {
+       unsigned int msr;
+};
+
+struct amixer_mgr {
+       struct rsc_mgr mgr;     /* Basic resource manager info */
+       spinlock_t mgr_lock;
+
+        /* request one amixer resource */
+       int (*get_amixer)(struct amixer_mgr *mgr,
+                         const struct amixer_desc *desc,
+                         struct amixer **ramixer);
+       /* return one amixer resource */
+       int (*put_amixer)(struct amixer_mgr *mgr, struct amixer *amixer);
+};
+
+/* Constructor and destructor of amixer resource manager */
+int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr);
+int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr);
+
+#endif /* CTAMIXER_H */
diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c
new file mode 100644 (file)
index 0000000..5f35374
--- /dev/null
@@ -0,0 +1,1605 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File    ctatc.c
+ *
+ * @Brief
+ * This file contains the implementation of the device resource management
+ * object.
+ *
+ * @Author Liu Chun
+ * @Date Mar 28 2008
+ */
+
+#include "ctatc.h"
+#include "ctpcm.h"
+#include "ctmixer.h"
+#include "ctdrv.h"
+#include "cthardware.h"
+#include "ctsrc.h"
+#include "ctamixer.h"
+#include "ctdaio.h"
+#include <linux/delay.h>
+#include <sound/pcm.h>
+#include <sound/control.h>
+#include <sound/asoundef.h>
+
+#define MONO_SUM_SCALE 0x19a8  /* 2^(-0.5) in 14-bit floating format */
+#define DAIONUM                7
+#define MAX_MULTI_CHN  8
+
+#define IEC958_DEFAULT_CON ((IEC958_AES0_NONAUDIO \
+                           | IEC958_AES0_CON_NOT_COPYRIGHT) \
+                           | ((IEC958_AES1_CON_MIXER \
+                           | IEC958_AES1_CON_ORIGINAL) << 8) \
+                           | (0x10 << 16) \
+                           | ((IEC958_AES3_CON_FS_48000) << 24))
+
+static const struct ct_atc_chip_sub_details atc_sub_details[NUM_CTCARDS] = {
+       [CTSB0760] = {.subsys = PCI_SUBSYS_CREATIVE_SB0760,
+                     .nm_model = "SB076x"},
+       [CTHENDRIX] = {.subsys = PCI_SUBSYS_CREATIVE_HENDRIX,
+                     .nm_model = "Hendrix"},
+       [CTSB08801] = {.subsys = PCI_SUBSYS_CREATIVE_SB08801,
+                     .nm_model = "SB0880"},
+       [CTSB08802] = {.subsys = PCI_SUBSYS_CREATIVE_SB08802,
+                     .nm_model = "SB0880"},
+       [CTSB08803] = {.subsys = PCI_SUBSYS_CREATIVE_SB08803,
+                     .nm_model = "SB0880"}
+};
+
+static struct ct_atc_chip_details atc_chip_details[] = {
+       {.vendor = PCI_VENDOR_CREATIVE, .device = PCI_DEVICE_CREATIVE_20K1,
+        .sub_details = NULL,
+        .nm_card = "X-Fi 20k1"},
+       {.vendor = PCI_VENDOR_CREATIVE, .device = PCI_DEVICE_CREATIVE_20K2,
+        .sub_details = atc_sub_details,
+        .nm_card = "X-Fi 20k2"},
+       {} /* terminator */
+};
+
+static struct {
+       int (*create)(struct ct_atc *atc,
+                       enum CTALSADEVS device, const char *device_name);
+       int (*destroy)(void *alsa_dev);
+       const char *public_name;
+} alsa_dev_funcs[NUM_CTALSADEVS] = {
+       [FRONT]         = { .create = ct_alsa_pcm_create,
+                           .destroy = NULL,
+                           .public_name = "Front/WaveIn"},
+       [REAR]          = { .create = ct_alsa_pcm_create,
+                           .destroy = NULL,
+                           .public_name = "Rear"},
+       [CLFE]          = { .create = ct_alsa_pcm_create,
+                           .destroy = NULL,
+                           .public_name = "Center/LFE"},
+       [SURROUND]      = { .create = ct_alsa_pcm_create,
+                           .destroy = NULL,
+                           .public_name = "Surround"},
+       [IEC958]        = { .create = ct_alsa_pcm_create,
+                           .destroy = NULL,
+                           .public_name = "IEC958 Non-audio"},
+
+       [MIXER]         = { .create = ct_alsa_mix_create,
+                           .destroy = NULL,
+                           .public_name = "Mixer"}
+};
+
+typedef int (*create_t)(void *, void **);
+typedef int (*destroy_t)(void *);
+
+static struct {
+       int (*create)(void *hw, void **rmgr);
+       int (*destroy)(void *mgr);
+} rsc_mgr_funcs[NUM_RSCTYP] = {
+       [SRC]           = { .create     = (create_t)src_mgr_create,
+                           .destroy    = (destroy_t)src_mgr_destroy    },
+       [SRCIMP]        = { .create     = (create_t)srcimp_mgr_create,
+                           .destroy    = (destroy_t)srcimp_mgr_destroy },
+       [AMIXER]        = { .create     = (create_t)amixer_mgr_create,
+                           .destroy    = (destroy_t)amixer_mgr_destroy },
+       [SUM]           = { .create     = (create_t)sum_mgr_create,
+                           .destroy    = (destroy_t)sum_mgr_destroy    },
+       [DAIO]          = { .create     = (create_t)daio_mgr_create,
+                           .destroy    = (destroy_t)daio_mgr_destroy   }
+};
+
+static int
+atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm);
+
+/* *
+ * Only mono and interleaved modes are supported now.
+ * Always allocates a contiguous channel block.
+ * */
+
+static int ct_map_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+       unsigned long flags;
+       struct snd_pcm_runtime *runtime;
+       struct ct_vm *vm;
+
+       if (NULL == apcm->substream)
+               return 0;
+
+       runtime = apcm->substream->runtime;
+       vm = atc->vm;
+
+       spin_lock_irqsave(&atc->vm_lock, flags);
+       apcm->vm_block = vm->map(vm, runtime->dma_area, runtime->dma_bytes);
+       spin_unlock_irqrestore(&atc->vm_lock, flags);
+
+       if (NULL == apcm->vm_block)
+               return -ENOENT;
+
+       return 0;
+}
+
+static void ct_unmap_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+       unsigned long flags;
+       struct ct_vm *vm;
+
+       if (NULL == apcm->vm_block)
+               return;
+
+       vm = atc->vm;
+
+       spin_lock_irqsave(&atc->vm_lock, flags);
+       vm->unmap(vm, apcm->vm_block);
+       spin_unlock_irqrestore(&atc->vm_lock, flags);
+
+       apcm->vm_block = NULL;
+}
+
+static unsigned long atc_get_ptp_phys(struct ct_atc *atc, int index)
+{
+       struct ct_vm *vm;
+       void *kvirt_addr;
+       unsigned long phys_addr;
+       unsigned long flags;
+
+       spin_lock_irqsave(&atc->vm_lock, flags);
+       vm = atc->vm;
+       kvirt_addr = vm->get_ptp_virt(vm, index);
+       if (kvirt_addr == NULL)
+               phys_addr = (~0UL);
+       else
+               phys_addr = virt_to_phys(kvirt_addr);
+
+       spin_unlock_irqrestore(&atc->vm_lock, flags);
+
+       return phys_addr;
+}
+
+static unsigned int convert_format(snd_pcm_format_t snd_format)
+{
+       switch (snd_format) {
+       case SNDRV_PCM_FORMAT_U8:
+       case SNDRV_PCM_FORMAT_S8:
+               return SRC_SF_U8;
+       case SNDRV_PCM_FORMAT_S16_LE:
+       case SNDRV_PCM_FORMAT_U16_LE:
+               return SRC_SF_S16;
+       case SNDRV_PCM_FORMAT_S24_3LE:
+               return SRC_SF_S24;
+       case SNDRV_PCM_FORMAT_S24_LE:
+       case SNDRV_PCM_FORMAT_S32_LE:
+               return SRC_SF_S32;
+       default:
+               printk(KERN_ERR "not recognized snd format is %d \n",
+                       snd_format);
+               return SRC_SF_S16;
+       }
+}
+
+static unsigned int
+atc_get_pitch(unsigned int input_rate, unsigned int output_rate)
+{
+       unsigned int pitch = 0;
+       int b = 0;
+
+       /* get pitch and convert to fixed-point 8.24 format. */
+       pitch = (input_rate / output_rate) << 24;
+       input_rate %= output_rate;
+       input_rate /= 100;
+       output_rate /= 100;
+       for (b = 31; ((b >= 0) && !(input_rate >> b)); )
+               b--;
+
+       if (b >= 0) {
+               input_rate <<= (31 - b);
+               input_rate /= output_rate;
+               b = 24 - (31 - b);
+               if (b >= 0)
+                       input_rate <<= b;
+               else
+                       input_rate >>= -b;
+
+               pitch |= input_rate;
+       }
+
+       return pitch;
+}
+
+static int select_rom(unsigned int pitch)
+{
+       if ((pitch > 0x00428f5c) && (pitch < 0x01b851ec)) {
+               /* 0.26 <= pitch <= 1.72 */
+               return 1;
+       } else if ((0x01d66666 == pitch) || (0x01d66667 == pitch)) {
+               /* pitch == 1.8375 */
+               return 2;
+       } else if (0x02000000 == pitch) {
+               /* pitch == 2 */
+               return 3;
+       } else if ((pitch >= 0x0) && (pitch <= 0x08000000)) {
+               /* 0 <= pitch <= 8 */
+               return 0;
+       } else {
+               return -ENOENT;
+       }
+}
+
+static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+       struct src_mgr *src_mgr = atc->rsc_mgrs[SRC];
+       struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER];
+       struct src_desc desc = {0};
+       struct amixer_desc mix_dsc = {0};
+       struct src *src = NULL;
+       struct amixer *amixer = NULL;
+       int err = 0;
+       int n_amixer = apcm->substream->runtime->channels, i = 0;
+       int device = apcm->substream->pcm->device;
+       unsigned int pitch = 0;
+       unsigned long flags;
+
+       if (NULL != apcm->src) {
+               /* Prepared pcm playback */
+               return 0;
+       }
+
+       /* Get SRC resource */
+       desc.multi = apcm->substream->runtime->channels;
+       desc.msr = atc->msr;
+       desc.mode = MEMRD;
+       err = src_mgr->get_src(src_mgr, &desc, (struct src **)&apcm->src);
+       if (err)
+               goto error1;
+
+       pitch = atc_get_pitch(apcm->substream->runtime->rate,
+                                               (atc->rsr * atc->msr));
+       src = apcm->src;
+       src->ops->set_pitch(src, pitch);
+       src->ops->set_rom(src, select_rom(pitch));
+       src->ops->set_sf(src, convert_format(apcm->substream->runtime->format));
+       src->ops->set_pm(src, (src->ops->next_interleave(src) != NULL));
+
+       /* Get AMIXER resource */
+       n_amixer = (n_amixer < 2) ? 2 : n_amixer;
+       apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL);
+       if (NULL == apcm->amixers) {
+               err = -ENOMEM;
+               goto error1;
+       }
+       mix_dsc.msr = atc->msr;
+       for (i = 0, apcm->n_amixer = 0; i < n_amixer; i++) {
+               err = amixer_mgr->get_amixer(amixer_mgr, &mix_dsc,
+                                       (struct amixer **)&apcm->amixers[i]);
+               if (err)
+                       goto error1;
+
+               apcm->n_amixer++;
+       }
+
+       /* Set up device virtual mem map */
+       err = ct_map_audio_buffer(atc, apcm);
+       if (err < 0)
+               goto error1;
+
+       /* Connect resources */
+       src = apcm->src;
+       for (i = 0; i < n_amixer; i++) {
+               amixer = apcm->amixers[i];
+               spin_lock_irqsave(&atc->atc_lock, flags);
+               amixer->ops->setup(amixer, &src->rsc,
+                                       INIT_VOL, atc->pcm[i+device*2]);
+               spin_unlock_irqrestore(&atc->atc_lock, flags);
+               src = src->ops->next_interleave(src);
+               if (NULL == src)
+                       src = apcm->src;
+       }
+
+       return 0;
+
+error1:
+       atc_pcm_release_resources(atc, apcm);
+       return err;
+}
+
+static int
+atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+       struct src_mgr *src_mgr = atc->rsc_mgrs[SRC];
+       struct srcimp_mgr *srcimp_mgr = atc->rsc_mgrs[SRCIMP];
+       struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER];
+       struct sum_mgr *sum_mgr = atc->rsc_mgrs[SUM];
+       struct srcimp *srcimp = NULL;
+       int i = 0;
+
+       if (NULL != apcm->srcimps) {
+               for (i = 0; i < apcm->n_srcimp; i++) {
+                       srcimp = apcm->srcimps[i];
+                       srcimp->ops->unmap(srcimp);
+                       srcimp_mgr->put_srcimp(srcimp_mgr, srcimp);
+                       apcm->srcimps[i] = NULL;
+               }
+               kfree(apcm->srcimps);
+               apcm->srcimps = NULL;
+       }
+
+       if (NULL != apcm->srccs) {
+               for (i = 0; i < apcm->n_srcc; i++) {
+                       src_mgr->put_src(src_mgr, apcm->srccs[i]);
+                       apcm->srccs[i] = NULL;
+               }
+               kfree(apcm->srccs);
+               apcm->srccs = NULL;
+       }
+
+       if (NULL != apcm->amixers) {
+               for (i = 0; i < apcm->n_amixer; i++) {
+                       amixer_mgr->put_amixer(amixer_mgr, apcm->amixers[i]);
+                       apcm->amixers[i] = NULL;
+               }
+               kfree(apcm->amixers);
+               apcm->amixers = NULL;
+       }
+
+       if (NULL != apcm->mono) {
+               sum_mgr->put_sum(sum_mgr, apcm->mono);
+               apcm->mono = NULL;
+       }
+
+       if (NULL != apcm->src) {
+               src_mgr->put_src(src_mgr, apcm->src);
+               apcm->src = NULL;
+       }
+
+       if (NULL != apcm->vm_block) {
+               /* Undo device virtual mem map */
+               ct_unmap_audio_buffer(atc, apcm);
+               apcm->vm_block = NULL;
+       }
+
+       return 0;
+}
+
+static int atc_pcm_playback_start(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+       unsigned int max_cisz = 0;
+       struct src *src = apcm->src;
+
+       max_cisz = src->multi * src->rsc.msr;
+       max_cisz = 0x80 * (max_cisz < 8 ? max_cisz : 8);
+
+       src->ops->set_sa(src, apcm->vm_block->addr);
+       src->ops->set_la(src, apcm->vm_block->addr + apcm->vm_block->size);
+       src->ops->set_ca(src, apcm->vm_block->addr + max_cisz);
+       src->ops->set_cisz(src, max_cisz);
+
+       src->ops->set_bm(src, 1);
+       src->ops->set_state(src, SRC_STATE_INIT);
+       src->ops->commit_write(src);
+
+       return 0;
+}
+
+static int atc_pcm_stop(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+       struct src *src = NULL;
+       int i = 0;
+
+       src = apcm->src;
+       src->ops->set_bm(src, 0);
+       src->ops->set_state(src, SRC_STATE_OFF);
+       src->ops->commit_write(src);
+
+       if (NULL != apcm->srccs) {
+               for (i = 0; i < apcm->n_srcc; i++) {
+                       src = apcm->srccs[i];
+                       src->ops->set_bm(src, 0);
+                       src->ops->set_state(src, SRC_STATE_OFF);
+                       src->ops->commit_write(src);
+               }
+       }
+
+       apcm->started = 0;
+
+       return 0;
+}
+
+static int
+atc_pcm_playback_position(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+       struct src *src = apcm->src;
+       u32 size = 0, max_cisz = 0;
+       int position = 0;
+
+       position = src->ops->get_ca(src);
+
+       size = apcm->vm_block->size;
+       max_cisz = src->multi * src->rsc.msr;
+       max_cisz = 128 * (max_cisz < 8 ? max_cisz : 8);
+
+       return (position + size - max_cisz - apcm->vm_block->addr) % size;
+}
+
+struct src_node_conf_t {
+       unsigned int pitch;
+       unsigned int msr:8;
+       unsigned int mix_msr:8;
+       unsigned int imp_msr:8;
+       unsigned int vo:1;
+};
+
+static void setup_src_node_conf(struct ct_atc *atc, struct ct_atc_pcm *apcm,
+                               struct src_node_conf_t *conf, int *n_srcc)
+{
+       unsigned int pitch = 0;
+
+       /* get pitch and convert to fixed-point 8.24 format. */
+       pitch = atc_get_pitch((atc->rsr * atc->msr),
+                               apcm->substream->runtime->rate);
+       *n_srcc = 0;
+
+       if (1 == atc->msr) {
+               *n_srcc = apcm->substream->runtime->channels;
+               conf[0].pitch = pitch;
+               conf[0].mix_msr = conf[0].imp_msr = conf[0].msr = 1;
+               conf[0].vo = 1;
+       } else if (2 == atc->msr) {
+               if (0x8000000 < pitch) {
+                       /* Need two-stage SRCs, SRCIMPs and
+                        * AMIXERs for converting format */
+                       conf[0].pitch = (atc->msr << 24);
+                       conf[0].msr = conf[0].mix_msr = 1;
+                       conf[0].imp_msr = atc->msr;
+                       conf[0].vo = 0;
+                       conf[1].pitch = atc_get_pitch(atc->rsr,
+                                       apcm->substream->runtime->rate);
+                       conf[1].msr = conf[1].mix_msr = conf[1].imp_msr = 1;
+                       conf[1].vo = 1;
+                       *n_srcc = apcm->substream->runtime->channels * 2;
+               } else if (0x1000000 < pitch) {
+                       /* Need one-stage SRCs, SRCIMPs and
+                        * AMIXERs for converting format */
+                       conf[0].pitch = pitch;
+                       conf[0].msr = conf[0].mix_msr
+                                   = conf[0].imp_msr = atc->msr;
+                       conf[0].vo = 1;
+                       *n_srcc = apcm->substream->runtime->channels;
+               }
+       }
+}
+
+static int
+atc_pcm_capture_get_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+       struct src_mgr *src_mgr = atc->rsc_mgrs[SRC];
+       struct srcimp_mgr *srcimp_mgr = atc->rsc_mgrs[SRCIMP];
+       struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER];
+       struct sum_mgr *sum_mgr = atc->rsc_mgrs[SUM];
+       struct src_desc src_dsc = {0};
+       struct src *src = NULL;
+       struct srcimp_desc srcimp_dsc = {0};
+       struct srcimp *srcimp = NULL;
+       struct amixer_desc mix_dsc = {0};
+       struct sum_desc sum_dsc = {0};
+       unsigned int pitch = 0;
+       int multi = 0, err = 0, i = 0;
+       int n_srcimp = 0, n_amixer = 0, n_srcc = 0, n_sum = 0;
+       struct src_node_conf_t src_node_conf[2] = {{0} };
+
+       /* The numbers of converting SRCs and SRCIMPs should be determined
+        * by pitch value. */
+
+       multi = apcm->substream->runtime->channels;
+
+       /* get pitch and convert to fixed-point 8.24 format. */
+       pitch = atc_get_pitch((atc->rsr * atc->msr),
+                               apcm->substream->runtime->rate);
+
+       setup_src_node_conf(atc, apcm, src_node_conf, &n_srcc);
+       n_sum = (1 == multi) ? 1 : 0;
+       n_amixer += n_sum * 2 + n_srcc;
+       n_srcimp += n_srcc;
+       if ((multi > 1) && (0x8000000 >= pitch)) {
+               /* Need extra AMIXERs and SRCIMPs for special treatment
+                * of interleaved recording of conjugate channels */
+               n_amixer += multi * atc->msr;
+               n_srcimp += multi * atc->msr;
+       } else {
+               n_srcimp += multi;
+       }
+
+       if (n_srcc) {
+               apcm->srccs = kzalloc(sizeof(void *)*n_srcc, GFP_KERNEL);
+               if (NULL == apcm->srccs)
+                       return -ENOMEM;
+       }
+       if (n_amixer) {
+               apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL);
+               if (NULL == apcm->amixers) {
+                       err = -ENOMEM;
+                       goto error1;
+               }
+       }
+       apcm->srcimps = kzalloc(sizeof(void *)*n_srcimp, GFP_KERNEL);
+       if (NULL == apcm->srcimps) {
+               err = -ENOMEM;
+               goto error1;
+       }
+
+       /* Allocate SRCs for sample rate conversion if needed */
+       src_dsc.multi = 1;
+       src_dsc.mode = ARCRW;
+       for (i = 0, apcm->n_srcc = 0; i < n_srcc; i++) {
+               src_dsc.msr = src_node_conf[i/multi].msr;
+               err = src_mgr->get_src(src_mgr, &src_dsc,
+                                       (struct src **)&apcm->srccs[i]);
+               if (err)
+                       goto error1;
+
+               src = apcm->srccs[i];
+               pitch = src_node_conf[i/multi].pitch;
+               src->ops->set_pitch(src, pitch);
+               src->ops->set_rom(src, select_rom(pitch));
+               src->ops->set_vo(src, src_node_conf[i/multi].vo);
+
+               apcm->n_srcc++;
+       }
+
+       /* Allocate AMIXERs for routing SRCs of conversion if needed */
+       for (i = 0, apcm->n_amixer = 0; i < n_amixer; i++) {
+               if (i < (n_sum*2))
+                       mix_dsc.msr = atc->msr;
+               else if (i < (n_sum*2+n_srcc))
+                       mix_dsc.msr = src_node_conf[(i-n_sum*2)/multi].mix_msr;
+               else
+                       mix_dsc.msr = 1;
+
+               err = amixer_mgr->get_amixer(amixer_mgr, &mix_dsc,
+                                       (struct amixer **)&apcm->amixers[i]);
+               if (err)
+                       goto error1;
+
+               apcm->n_amixer++;
+       }
+
+       /* Allocate a SUM resource to mix all input channels together */
+       sum_dsc.msr = atc->msr;
+       err = sum_mgr->get_sum(sum_mgr, &sum_dsc, (struct sum **)&apcm->mono);
+       if (err)
+               goto error1;
+
+       pitch = atc_get_pitch((atc->rsr * atc->msr),
+                               apcm->substream->runtime->rate);
+       /* Allocate SRCIMP resources */
+       for (i = 0, apcm->n_srcimp = 0; i < n_srcimp; i++) {
+               if (i < (n_srcc))
+                       srcimp_dsc.msr = src_node_conf[i/multi].imp_msr;
+               else if (1 == multi)
+                       srcimp_dsc.msr = (pitch <= 0x8000000) ? atc->msr : 1;
+               else
+                       srcimp_dsc.msr = 1;
+
+               err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc, &srcimp);
+               if (err)
+                       goto error1;
+
+               apcm->srcimps[i] = srcimp;
+               apcm->n_srcimp++;
+       }
+
+       /* Allocate a SRC for writing data to host memory */
+       src_dsc.multi = apcm->substream->runtime->channels;
+       src_dsc.msr = 1;
+       src_dsc.mode = MEMWR;
+       err = src_mgr->get_src(src_mgr, &src_dsc, (struct src **)&apcm->src);
+       if (err)
+               goto error1;
+
+       src = apcm->src;
+       src->ops->set_pitch(src, pitch);
+
+       /* Set up device virtual mem map */
+       err = ct_map_audio_buffer(atc, apcm);
+       if (err < 0)
+               goto error1;
+
+       return 0;
+
+error1:
+       atc_pcm_release_resources(atc, apcm);
+       return err;
+}
+
+static int atc_pcm_capture_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+       struct src *src = NULL;
+       struct amixer *amixer = NULL;
+       struct srcimp *srcimp = NULL;
+       struct ct_mixer *mixer = atc->mixer;
+       struct sum *mono = NULL;
+       struct rsc *out_ports[8] = {NULL};
+       int err = 0, i = 0, j = 0, n_sum = 0, multi = 0;
+       unsigned int pitch = 0;
+       int mix_base = 0, imp_base = 0;
+
+       if (NULL != apcm->src) {
+               /* Prepared pcm capture */
+               return 0;
+       }
+
+       /* Get needed resources. */
+       err = atc_pcm_capture_get_resources(atc, apcm);
+       if (err)
+               return err;
+
+       /* Connect resources */
+       mixer->get_output_ports(mixer, MIX_PCMO_FRONT,
+                               &out_ports[0], &out_ports[1]);
+
+       multi = apcm->substream->runtime->channels;
+       if (1 == multi) {
+               mono = apcm->mono;
+               for (i = 0; i < 2; i++) {
+                       amixer = apcm->amixers[i];
+                       amixer->ops->setup(amixer, out_ports[i],
+                                               MONO_SUM_SCALE, mono);
+               }
+               out_ports[0] = &mono->rsc;
+               n_sum = 1;
+               mix_base = n_sum * 2;
+       }
+
+       for (i = 0; i < apcm->n_srcc; i++) {
+               src = apcm->srccs[i];
+               srcimp = apcm->srcimps[imp_base+i];
+               amixer = apcm->amixers[mix_base+i];
+               srcimp->ops->map(srcimp, src, out_ports[i%multi]);
+               amixer->ops->setup(amixer, &src->rsc, INIT_VOL, NULL);
+               out_ports[i%multi] = &amixer->rsc;
+       }
+
+       pitch = atc_get_pitch((atc->rsr * atc->msr),
+                               apcm->substream->runtime->rate);
+
+       if ((multi > 1) && (pitch <= 0x8000000)) {
+               /* Special connection for interleaved
+                * recording with conjugate channels */
+               for (i = 0; i < multi; i++) {
+                       out_ports[i]->ops->master(out_ports[i]);
+                       for (j = 0; j < atc->msr; j++) {
+                               amixer = apcm->amixers[apcm->n_srcc+j*multi+i];
+                               amixer->ops->set_input(amixer, out_ports[i]);
+                               amixer->ops->set_scale(amixer, INIT_VOL);
+                               amixer->ops->set_sum(amixer, NULL);
+                               amixer->ops->commit_raw_write(amixer);
+                               out_ports[i]->ops->next_conj(out_ports[i]);
+
+                               srcimp = apcm->srcimps[apcm->n_srcc+j*multi+i];
+                               srcimp->ops->map(srcimp, apcm->src,
+                                                       &amixer->rsc);
+                       }
+               }
+       } else {
+               for (i = 0; i < multi; i++) {
+                       srcimp = apcm->srcimps[apcm->n_srcc+i];
+                       srcimp->ops->map(srcimp, apcm->src, out_ports[i]);
+               }
+       }
+
+       return 0;
+}
+
+static int atc_pcm_capture_start(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+       struct src *src = NULL;
+       struct src_mgr *src_mgr = atc->rsc_mgrs[SRC];
+       int i = 0, multi = 0;
+
+       if (apcm->started)
+               return 0;
+
+       apcm->started = 1;
+       multi = apcm->substream->runtime->channels;
+       /* Set up converting SRCs */
+       for (i = 0; i < apcm->n_srcc; i++) {
+               src = apcm->srccs[i];
+               src->ops->set_pm(src, ((i%multi) != (multi-1)));
+               src_mgr->src_disable(src_mgr, src);
+       }
+
+       /*  Set up recording SRC */
+       src = apcm->src;
+       src->ops->set_sf(src, convert_format(apcm->substream->runtime->format));
+       src->ops->set_sa(src, apcm->vm_block->addr);
+       src->ops->set_la(src, apcm->vm_block->addr + apcm->vm_block->size);
+       src->ops->set_ca(src, apcm->vm_block->addr);
+       src_mgr->src_disable(src_mgr, src);
+
+       /* Disable relevant SRCs firstly */
+       src_mgr->commit_write(src_mgr);
+
+       /* Enable SRCs respectively */
+       for (i = 0; i < apcm->n_srcc; i++) {
+               src = apcm->srccs[i];
+               src->ops->set_state(src, SRC_STATE_RUN);
+               src->ops->commit_write(src);
+               src_mgr->src_enable_s(src_mgr, src);
+       }
+       src = apcm->src;
+       src->ops->set_bm(src, 1);
+       src->ops->set_state(src, SRC_STATE_RUN);
+       src->ops->commit_write(src);
+       src_mgr->src_enable_s(src_mgr, src);
+
+       /* Enable relevant SRCs synchronously */
+       src_mgr->commit_write(src_mgr);
+
+       return 0;
+}
+
+static int
+atc_pcm_capture_position(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+       struct src *src = apcm->src;
+
+       return src->ops->get_ca(src) - apcm->vm_block->addr;
+}
+
+static int spdif_passthru_playback_get_resources(struct ct_atc *atc,
+                                                struct ct_atc_pcm *apcm)
+{
+       struct src_mgr *src_mgr = atc->rsc_mgrs[SRC];
+       struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER];
+       struct src_desc desc = {0};
+       struct amixer_desc mix_dsc = {0};
+       struct src *src = NULL;
+       int err = 0;
+       int n_amixer = apcm->substream->runtime->channels, i = 0;
+       unsigned int pitch = 0, rsr = atc->pll_rate;
+
+       /* Get SRC resource */
+       desc.multi = apcm->substream->runtime->channels;
+       desc.msr = 1;
+       while (apcm->substream->runtime->rate > (rsr * desc.msr))
+               desc.msr <<= 1;
+
+       desc.mode = MEMRD;
+       err = src_mgr->get_src(src_mgr, &desc, (struct src **)&apcm->src);
+       if (err)
+               goto error1;
+
+       pitch = atc_get_pitch(apcm->substream->runtime->rate, (rsr * desc.msr));
+       src = apcm->src;
+       src->ops->set_pitch(src, pitch);
+       src->ops->set_rom(src, select_rom(pitch));
+       src->ops->set_sf(src, convert_format(apcm->substream->runtime->format));
+       src->ops->set_pm(src, (src->ops->next_interleave(src) != NULL));
+       src->ops->set_bp(src, 1);
+
+       /* Get AMIXER resource */
+       n_amixer = (n_amixer < 2) ? 2 : n_amixer;
+       apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL);
+       if (NULL == apcm->amixers) {
+               err = -ENOMEM;
+               goto error1;
+       }
+       mix_dsc.msr = desc.msr;
+       for (i = 0, apcm->n_amixer = 0; i < n_amixer; i++) {
+               err = amixer_mgr->get_amixer(amixer_mgr, &mix_dsc,
+                                       (struct amixer **)&apcm->amixers[i]);
+               if (err)
+                       goto error1;
+
+               apcm->n_amixer++;
+       }
+
+       /* Set up device virtual mem map */
+       err = ct_map_audio_buffer(atc, apcm);
+       if (err < 0)
+               goto error1;
+
+       return 0;
+
+error1:
+       atc_pcm_release_resources(atc, apcm);
+       return err;
+}
+
+static int
+spdif_passthru_playback_setup(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+       struct dao *dao = container_of(atc->daios[SPDIFOO], struct dao, daio);
+       unsigned long flags;
+       unsigned int rate = apcm->substream->runtime->rate;
+       unsigned int status = 0;
+       int err = 0;
+       unsigned char iec958_con_fs = 0;
+
+       switch (rate) {
+       case 48000:
+               iec958_con_fs = IEC958_AES3_CON_FS_48000;
+               break;
+       case 44100:
+               iec958_con_fs = IEC958_AES3_CON_FS_44100;
+               break;
+       case 32000:
+               iec958_con_fs = IEC958_AES3_CON_FS_32000;
+               break;
+       default:
+               return -ENOENT;
+       }
+
+       spin_lock_irqsave(&atc->atc_lock, flags);
+       dao->ops->get_spos(dao, &status);
+       if (((status >> 24) & IEC958_AES3_CON_FS) != iec958_con_fs) {
+               status &= ((~IEC958_AES3_CON_FS) << 24);
+               status |= (iec958_con_fs << 24);
+               dao->ops->set_spos(dao, status);
+               dao->ops->commit_write(dao);
+       }
+       if ((rate != atc->pll_rate) && (32000 != rate)) {
+               err = ((struct hw *)atc->hw)->pll_init(atc->hw, rate);
+               atc->pll_rate = err ? 0 : rate;
+       }
+       spin_unlock_irqrestore(&atc->atc_lock, flags);
+
+       return err;
+}
+
+static int
+spdif_passthru_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+       struct src *src = NULL;
+       struct amixer *amixer = NULL;
+       struct dao *dao = NULL;
+       int err = 0;
+       int i = 0;
+       unsigned long flags;
+
+       if (NULL != apcm->src)
+               return 0;
+
+       /* Configure SPDIFOO and PLL to passthrough mode;
+        * determine pll_rate. */
+       err = spdif_passthru_playback_setup(atc, apcm);
+       if (err)
+               return err;
+
+       /* Get needed resources. */
+       err = spdif_passthru_playback_get_resources(atc, apcm);
+       if (err)
+               return err;
+
+       /* Connect resources */
+       src = apcm->src;
+       for (i = 0; i < apcm->n_amixer; i++) {
+               amixer = apcm->amixers[i];
+               amixer->ops->setup(amixer, &src->rsc, INIT_VOL, NULL);
+               src = src->ops->next_interleave(src);
+               if (NULL == src)
+                       src = apcm->src;
+       }
+       /* Connect to SPDIFOO */
+       spin_lock_irqsave(&atc->atc_lock, flags);
+       dao = container_of(atc->daios[SPDIFOO], struct dao, daio);
+       amixer = apcm->amixers[0];
+       dao->ops->set_left_input(dao, &amixer->rsc);
+       amixer = apcm->amixers[1];
+       dao->ops->set_right_input(dao, &amixer->rsc);
+       spin_unlock_irqrestore(&atc->atc_lock, flags);
+
+       return 0;
+}
+
+static int atc_select_line_in(struct ct_atc *atc)
+{
+       struct hw *hw = atc->hw;
+       struct ct_mixer *mixer = atc->mixer;
+       struct src *src = NULL;
+
+       if (hw->is_adc_source_selected(hw, ADC_LINEIN))
+               return 0;
+
+       mixer->set_input_left(mixer, MIX_MIC_IN, NULL);
+       mixer->set_input_right(mixer, MIX_MIC_IN, NULL);
+
+       hw->select_adc_source(hw, ADC_LINEIN);
+
+       src = atc->srcs[2];
+       mixer->set_input_left(mixer, MIX_LINE_IN, &src->rsc);
+       src = atc->srcs[3];
+       mixer->set_input_right(mixer, MIX_LINE_IN, &src->rsc);
+
+       return 0;
+}
+
+static int atc_select_mic_in(struct ct_atc *atc)
+{
+       struct hw *hw = atc->hw;
+       struct ct_mixer *mixer = atc->mixer;
+       struct src *src = NULL;
+
+       if (hw->is_adc_source_selected(hw, ADC_MICIN))
+               return 0;
+
+       mixer->set_input_left(mixer, MIX_LINE_IN, NULL);
+       mixer->set_input_right(mixer, MIX_LINE_IN, NULL);
+
+       hw->select_adc_source(hw, ADC_MICIN);
+
+       src = atc->srcs[2];
+       mixer->set_input_left(mixer, MIX_MIC_IN, &src->rsc);
+       src = atc->srcs[3];
+       mixer->set_input_right(mixer, MIX_MIC_IN, &src->rsc);
+
+       return 0;
+}
+
+static int atc_have_digit_io_switch(struct ct_atc *atc)
+{
+       struct hw *hw = atc->hw;
+
+       return hw->have_digit_io_switch(hw);
+}
+
+static int atc_select_digit_io(struct ct_atc *atc)
+{
+       struct hw *hw = atc->hw;
+
+       if (hw->is_adc_source_selected(hw, ADC_NONE))
+               return 0;
+
+       hw->select_adc_source(hw, ADC_NONE);
+
+       return 0;
+}
+
+static int atc_daio_unmute(struct ct_atc *atc, unsigned char state, int type)
+{
+       struct daio_mgr *daio_mgr = atc->rsc_mgrs[DAIO];
+
+       if (state)
+               daio_mgr->daio_enable(daio_mgr, atc->daios[type]);
+       else
+               daio_mgr->daio_disable(daio_mgr, atc->daios[type]);
+
+       daio_mgr->commit_write(daio_mgr);
+
+       return 0;
+}
+
+static int
+atc_dao_get_status(struct ct_atc *atc, unsigned int *status, int type)
+{
+       struct dao *dao = container_of(atc->daios[type], struct dao, daio);
+       return dao->ops->get_spos(dao, status);
+}
+
+static int
+atc_dao_set_status(struct ct_atc *atc, unsigned int status, int type)
+{
+       struct dao *dao = container_of(atc->daios[type], struct dao, daio);
+
+       dao->ops->set_spos(dao, status);
+       dao->ops->commit_write(dao);
+       return 0;
+}
+
+static int atc_line_front_unmute(struct ct_atc *atc, unsigned char state)
+{
+       return atc_daio_unmute(atc, state, LINEO1);
+}
+
+static int atc_line_surround_unmute(struct ct_atc *atc, unsigned char state)
+{
+       return atc_daio_unmute(atc, state, LINEO4);
+}
+
+static int atc_line_clfe_unmute(struct ct_atc *atc, unsigned char state)
+{
+       return atc_daio_unmute(atc, state, LINEO3);
+}
+
+static int atc_line_rear_unmute(struct ct_atc *atc, unsigned char state)
+{
+       return atc_daio_unmute(atc, state, LINEO2);
+}
+
+static int atc_line_in_unmute(struct ct_atc *atc, unsigned char state)
+{
+       return atc_daio_unmute(atc, state, LINEIM);
+}
+
+static int atc_spdif_out_unmute(struct ct_atc *atc, unsigned char state)
+{
+       return atc_daio_unmute(atc, state, SPDIFOO);
+}
+
+static int atc_spdif_in_unmute(struct ct_atc *atc, unsigned char state)
+{
+       return atc_daio_unmute(atc, state, SPDIFIO);
+}
+
+static int atc_spdif_out_get_status(struct ct_atc *atc, unsigned int *status)
+{
+       return atc_dao_get_status(atc, status, SPDIFOO);
+}
+
+static int atc_spdif_out_set_status(struct ct_atc *atc, unsigned int status)
+{
+       return atc_dao_set_status(atc, status, SPDIFOO);
+}
+
+static int atc_spdif_out_passthru(struct ct_atc *atc, unsigned char state)
+{
+       unsigned long flags;
+       struct dao_desc da_dsc = {0};
+       struct dao *dao = NULL;
+       int err = 0;
+       struct ct_mixer *mixer = atc->mixer;
+       struct rsc *rscs[2] = {NULL};
+       unsigned int spos = 0;
+
+       spin_lock_irqsave(&atc->atc_lock, flags);
+       dao = container_of(atc->daios[SPDIFOO], struct dao, daio);
+       da_dsc.msr = state ? 1 : atc->msr;
+       da_dsc.passthru = state ? 1 : 0;
+       err = dao->ops->reinit(dao, &da_dsc);
+       if (state) {
+               spos = IEC958_DEFAULT_CON;
+       } else {
+               mixer->get_output_ports(mixer, MIX_SPDIF_OUT,
+                                       &rscs[0], &rscs[1]);
+               dao->ops->set_left_input(dao, rscs[0]);
+               dao->ops->set_right_input(dao, rscs[1]);
+               /* Restore PLL to atc->rsr if needed. */
+               if (atc->pll_rate != atc->rsr) {
+                       err = ((struct hw *)atc->hw)->pll_init(atc->hw,
+                                                              atc->rsr);
+                       atc->pll_rate = err ? 0 : atc->rsr;
+               }
+       }
+       dao->ops->set_spos(dao, spos);
+       dao->ops->commit_write(dao);
+       spin_unlock_irqrestore(&atc->atc_lock, flags);
+
+       return err;
+}
+
+static int ct_atc_destroy(struct ct_atc *atc)
+{
+       struct daio_mgr *daio_mgr = NULL;
+       struct dao *dao = NULL;
+       struct dai *dai = NULL;
+       struct daio *daio = NULL;
+       struct sum_mgr *sum_mgr = NULL;
+       struct src_mgr *src_mgr = NULL;
+       struct srcimp_mgr *srcimp_mgr = NULL;
+       struct srcimp *srcimp = NULL;
+       struct ct_mixer *mixer = NULL;
+       int i = 0;
+
+       if (NULL == atc)
+               return 0;
+
+       /* Stop hardware and disable all interrupts */
+       if (NULL != atc->hw)
+               ((struct hw *)atc->hw)->card_stop(atc->hw);
+
+       /* Destroy internal mixer objects */
+       if (NULL != atc->mixer) {
+               mixer = atc->mixer;
+               mixer->set_input_left(mixer, MIX_LINE_IN, NULL);
+               mixer->set_input_right(mixer, MIX_LINE_IN, NULL);
+               mixer->set_input_left(mixer, MIX_MIC_IN, NULL);
+               mixer->set_input_right(mixer, MIX_MIC_IN, NULL);
+               mixer->set_input_left(mixer, MIX_SPDIF_IN, NULL);
+               mixer->set_input_right(mixer, MIX_SPDIF_IN, NULL);
+               ct_mixer_destroy(atc->mixer);
+       }
+
+       if (NULL != atc->daios) {
+               daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO];
+               for (i = 0; i < atc->n_daio; i++) {
+                       daio = atc->daios[i];
+                       if (daio->type < LINEIM) {
+                               dao = container_of(daio, struct dao, daio);
+                               dao->ops->clear_left_input(dao);
+                               dao->ops->clear_right_input(dao);
+                       } else {
+                               dai = container_of(daio, struct dai, daio);
+                               /* some thing to do for dai ... */
+                       }
+                       daio_mgr->put_daio(daio_mgr, daio);
+               }
+               kfree(atc->daios);
+       }
+
+       if (NULL != atc->pcm) {
+               sum_mgr = atc->rsc_mgrs[SUM];
+               for (i = 0; i < atc->n_pcm; i++)
+                       sum_mgr->put_sum(sum_mgr, atc->pcm[i]);
+
+               kfree(atc->pcm);
+       }
+
+       if (NULL != atc->srcs) {
+               src_mgr = atc->rsc_mgrs[SRC];
+               for (i = 0; i < atc->n_src; i++)
+                       src_mgr->put_src(src_mgr, atc->srcs[i]);
+
+               kfree(atc->srcs);
+       }
+
+       if (NULL != atc->srcimps) {
+               srcimp_mgr = atc->rsc_mgrs[SRCIMP];
+               for (i = 0; i < atc->n_srcimp; i++) {
+                       srcimp = atc->srcimps[i];
+                       srcimp->ops->unmap(srcimp);
+                       srcimp_mgr->put_srcimp(srcimp_mgr, atc->srcimps[i]);
+               }
+               kfree(atc->srcimps);
+       }
+
+       for (i = 0; i < NUM_RSCTYP; i++) {
+               if ((NULL != rsc_mgr_funcs[i].destroy) &&
+                   (NULL != atc->rsc_mgrs[i]))
+                       rsc_mgr_funcs[i].destroy(atc->rsc_mgrs[i]);
+
+       }
+
+       if (NULL != atc->hw)
+               destroy_hw_obj((struct hw *)atc->hw);
+
+       /* Destroy device virtual memory manager object */
+       if (NULL != atc->vm) {
+               ct_vm_destroy(atc->vm);
+               atc->vm = NULL;
+       }
+
+       kfree(atc);
+
+       return 0;
+}
+
+static int atc_dev_free(struct snd_device *dev)
+{
+       struct ct_atc *atc = dev->device_data;
+       return ct_atc_destroy(atc);
+}
+
+static int atc_identify_card(struct ct_atc *atc)
+{
+       u16 subsys = 0;
+       u8 revision = 0;
+       struct pci_dev *pci = atc->pci;
+       const struct ct_atc_chip_details *d;
+       enum CTCARDS i;
+
+       pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsys);
+       pci_read_config_byte(pci, PCI_REVISION_ID, &revision);
+       atc->chip_details = NULL;
+       atc->model = NUM_CTCARDS;
+       for (d = atc_chip_details; d->vendor; d++) {
+               if (d->vendor != pci->vendor || d->device != pci->device)
+                       continue;
+
+               if (NULL == d->sub_details) {
+                       atc->chip_details = d;
+                       break;
+               }
+               for (i = 0; i < NUM_CTCARDS; i++) {
+                       if ((d->sub_details[i].subsys == subsys) ||
+                           (((subsys & 0x6000) == 0x6000) &&
+                           ((d->sub_details[i].subsys & 0x6000) == 0x6000))) {
+                               atc->model = i;
+                               break;
+                       }
+               }
+               if (i >= NUM_CTCARDS)
+                       continue;
+
+               atc->chip_details = d;
+               break;
+               /* not take revision into consideration now */
+       }
+       if (!d->vendor)
+               return -ENOENT;
+
+       return 0;
+}
+
+static int ct_create_alsa_devs(struct ct_atc *atc)
+{
+       enum CTALSADEVS i;
+       struct hw *hw = atc->hw;
+       int err;
+
+       switch (hw->get_chip_type(hw)) {
+       case ATC20K1:
+               alsa_dev_funcs[MIXER].public_name = "20K1";
+               break;
+       case ATC20K2:
+               alsa_dev_funcs[MIXER].public_name = "20K2";
+               break;
+       default:
+               alsa_dev_funcs[MIXER].public_name = "Unknown";
+               break;
+       }
+
+       for (i = 0; i < NUM_CTALSADEVS; i++) {
+               if (NULL == alsa_dev_funcs[i].create)
+                       continue;
+
+               err = alsa_dev_funcs[i].create(atc, i,
+                               alsa_dev_funcs[i].public_name);
+               if (err) {
+                       printk(KERN_ERR "Creating alsa device %d failed!\n", i);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+static int atc_create_hw_devs(struct ct_atc *atc)
+{
+       struct hw *hw = NULL;
+       struct card_conf info = {0};
+       int i = 0, err = 0;
+
+       err = create_hw_obj(atc->pci, &hw);
+       if (err) {
+               printk(KERN_ERR "Failed to create hw obj!!!\n");
+               return err;
+       }
+       atc->hw = hw;
+
+       /* Initialize card hardware. */
+       info.rsr = atc->rsr;
+       info.msr = atc->msr;
+       info.vm_pgt_phys = atc_get_ptp_phys(atc, 0);
+       err = hw->card_init(hw, &info);
+       if (err < 0)
+               return err;
+
+       for (i = 0; i < NUM_RSCTYP; i++) {
+               if (NULL == rsc_mgr_funcs[i].create)
+                       continue;
+
+               err = rsc_mgr_funcs[i].create(atc->hw, &atc->rsc_mgrs[i]);
+               if (err) {
+                       printk(KERN_ERR "Failed to create rsc_mgr %d!!!\n", i);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+static int atc_get_resources(struct ct_atc *atc)
+{
+       struct daio_desc da_desc = {0};
+       struct daio_mgr *daio_mgr = NULL;
+       struct src_desc src_dsc = {0};
+       struct src_mgr *src_mgr = NULL;
+       struct srcimp_desc srcimp_dsc = {0};
+       struct srcimp_mgr *srcimp_mgr = NULL;
+       struct sum_desc sum_dsc = {0};
+       struct sum_mgr *sum_mgr = NULL;
+       int err = 0, i = 0;
+       unsigned short subsys_id = 0;
+
+       atc->daios = kzalloc(sizeof(void *)*(DAIONUM), GFP_KERNEL);
+       if (NULL == atc->daios)
+               return -ENOMEM;
+
+       atc->srcs = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL);
+       if (NULL == atc->srcs)
+               return -ENOMEM;
+
+       atc->srcimps = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL);
+       if (NULL == atc->srcimps)
+               return -ENOMEM;
+
+       atc->pcm = kzalloc(sizeof(void *)*(2*4), GFP_KERNEL);
+       if (NULL == atc->pcm)
+               return -ENOMEM;
+
+       daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO];
+       da_desc.msr = atc->msr;
+       for (i = 0, atc->n_daio = 0; i < DAIONUM-1; i++) {
+               da_desc.type = i;
+               err = daio_mgr->get_daio(daio_mgr, &da_desc,
+                                       (struct daio **)&atc->daios[i]);
+               if (err) {
+                       printk(KERN_ERR "Failed to get DAIO "
+                                       "resource %d!!!\n", i);
+                       return err;
+               }
+               atc->n_daio++;
+       }
+       pci_read_config_word(atc->pci, PCI_SUBSYSTEM_ID, &subsys_id);
+       if ((subsys_id == 0x0029) || (subsys_id == 0x0031)) {
+               /* SB073x cards */
+               da_desc.type = SPDIFI1;
+       } else {
+               da_desc.type = SPDIFIO;
+       }
+       err = daio_mgr->get_daio(daio_mgr, &da_desc,
+                               (struct daio **)&atc->daios[i]);
+       if (err) {
+               printk(KERN_ERR "Failed to get S/PDIF-in resource!!!\n");
+               return err;
+       }
+       atc->n_daio++;
+
+       src_mgr = atc->rsc_mgrs[SRC];
+       src_dsc.multi = 1;
+       src_dsc.msr = atc->msr;
+       src_dsc.mode = ARCRW;
+       for (i = 0, atc->n_src = 0; i < (2*2); i++) {
+               err = src_mgr->get_src(src_mgr, &src_dsc,
+                                       (struct src **)&atc->srcs[i]);
+               if (err)
+                       return err;
+
+               atc->n_src++;
+       }
+
+       srcimp_mgr = atc->rsc_mgrs[SRCIMP];
+       srcimp_dsc.msr = 8; /* SRCIMPs for S/PDIFIn SRT */
+       for (i = 0, atc->n_srcimp = 0; i < (2*1); i++) {
+               err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc,
+                                       (struct srcimp **)&atc->srcimps[i]);
+               if (err)
+                       return err;
+
+               atc->n_srcimp++;
+       }
+       srcimp_dsc.msr = 8; /* SRCIMPs for LINE/MICIn SRT */
+       for (i = 0; i < (2*1); i++) {
+               err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc,
+                               (struct srcimp **)&atc->srcimps[2*1+i]);
+               if (err)
+                       return err;
+
+               atc->n_srcimp++;
+       }
+
+       sum_mgr = atc->rsc_mgrs[SUM];
+       sum_dsc.msr = atc->msr;
+       for (i = 0, atc->n_pcm = 0; i < (2*4); i++) {
+               err = sum_mgr->get_sum(sum_mgr, &sum_dsc,
+                                       (struct sum **)&atc->pcm[i]);
+               if (err)
+                       return err;
+
+               atc->n_pcm++;
+       }
+
+       err = ct_mixer_create(atc, (struct ct_mixer **)&atc->mixer);
+       if (err) {
+               printk(KERN_ERR "Failed to create mixer obj!!!\n");
+               return err;
+       }
+
+       return 0;
+}
+
+static void
+atc_connect_dai(struct src_mgr *src_mgr, struct dai *dai,
+               struct src **srcs, struct srcimp **srcimps)
+{
+       struct rsc *rscs[2] = {NULL};
+       struct src *src = NULL;
+       struct srcimp *srcimp = NULL;
+       int i = 0;
+
+       rscs[0] = &dai->daio.rscl;
+       rscs[1] = &dai->daio.rscr;
+       for (i = 0; i < 2; i++) {
+               src = srcs[i];
+               srcimp = srcimps[i];
+               srcimp->ops->map(srcimp, src, rscs[i]);
+               src_mgr->src_disable(src_mgr, src);
+       }
+
+       src_mgr->commit_write(src_mgr); /* Actually disable SRCs */
+
+       src = srcs[0];
+       src->ops->set_pm(src, 1);
+       for (i = 0; i < 2; i++) {
+               src = srcs[i];
+               src->ops->set_state(src, SRC_STATE_RUN);
+               src->ops->commit_write(src);
+               src_mgr->src_enable_s(src_mgr, src);
+       }
+
+       dai->ops->set_srt_srcl(dai, &(srcs[0]->rsc));
+       dai->ops->set_srt_srcr(dai, &(srcs[1]->rsc));
+
+       dai->ops->set_enb_src(dai, 1);
+       dai->ops->set_enb_srt(dai, 1);
+       dai->ops->commit_write(dai);
+
+       src_mgr->commit_write(src_mgr); /* Synchronously enable SRCs */
+}
+
+static void atc_connect_resources(struct ct_atc *atc)
+{
+       struct dai *dai = NULL;
+       struct dao *dao = NULL;
+       struct src *src = NULL;
+       struct sum *sum = NULL;
+       struct ct_mixer *mixer = NULL;
+       struct rsc *rscs[2] = {NULL};
+       int i = 0, j = 0;
+
+       mixer = atc->mixer;
+
+       for (i = MIX_WAVE_FRONT, j = LINEO1; i <= MIX_SPDIF_OUT; i++, j++) {
+               mixer->get_output_ports(mixer, i, &rscs[0], &rscs[1]);
+               dao = container_of(atc->daios[j], struct dao, daio);
+               dao->ops->set_left_input(dao, rscs[0]);
+               dao->ops->set_right_input(dao, rscs[1]);
+       }
+
+       dai = container_of(atc->daios[LINEIM], struct dai, daio);
+       atc_connect_dai(atc->rsc_mgrs[SRC], dai,
+                       (struct src **)&atc->srcs[2],
+                       (struct srcimp **)&atc->srcimps[2]);
+       src = atc->srcs[2];
+       mixer->set_input_left(mixer, MIX_LINE_IN, &src->rsc);
+       src = atc->srcs[3];
+       mixer->set_input_right(mixer, MIX_LINE_IN, &src->rsc);
+
+       dai = container_of(atc->daios[SPDIFIO], struct dai, daio);
+       atc_connect_dai(atc->rsc_mgrs[SRC], dai,
+                       (struct src **)&atc->srcs[0],
+                       (struct srcimp **)&atc->srcimps[0]);
+
+       src = atc->srcs[0];
+       mixer->set_input_left(mixer, MIX_SPDIF_IN, &src->rsc);
+       src = atc->srcs[1];
+       mixer->set_input_right(mixer, MIX_SPDIF_IN, &src->rsc);
+
+       for (i = MIX_PCMI_FRONT, j = 0; i <= MIX_PCMI_SURROUND; i++, j += 2) {
+               sum = atc->pcm[j];
+               mixer->set_input_left(mixer, i, &sum->rsc);
+               sum = atc->pcm[j+1];
+               mixer->set_input_right(mixer, i, &sum->rsc);
+       }
+}
+
+static void atc_set_ops(struct ct_atc *atc)
+{
+       /* Set operations */
+       atc->map_audio_buffer = ct_map_audio_buffer;
+       atc->unmap_audio_buffer = ct_unmap_audio_buffer;
+       atc->pcm_playback_prepare = atc_pcm_playback_prepare;
+       atc->pcm_release_resources = atc_pcm_release_resources;
+       atc->pcm_playback_start = atc_pcm_playback_start;
+       atc->pcm_playback_stop = atc_pcm_stop;
+       atc->pcm_playback_position = atc_pcm_playback_position;
+       atc->pcm_capture_prepare = atc_pcm_capture_prepare;
+       atc->pcm_capture_start = atc_pcm_capture_start;
+       atc->pcm_capture_stop = atc_pcm_stop;
+       atc->pcm_capture_position = atc_pcm_capture_position;
+       atc->spdif_passthru_playback_prepare = spdif_passthru_playback_prepare;
+       atc->get_ptp_phys = atc_get_ptp_phys;
+       atc->select_line_in = atc_select_line_in;
+       atc->select_mic_in = atc_select_mic_in;
+       atc->select_digit_io = atc_select_digit_io;
+       atc->line_front_unmute = atc_line_front_unmute;
+       atc->line_surround_unmute = atc_line_surround_unmute;
+       atc->line_clfe_unmute = atc_line_clfe_unmute;
+       atc->line_rear_unmute = atc_line_rear_unmute;
+       atc->line_in_unmute = atc_line_in_unmute;
+       atc->spdif_out_unmute = atc_spdif_out_unmute;
+       atc->spdif_in_unmute = atc_spdif_in_unmute;
+       atc->spdif_out_get_status = atc_spdif_out_get_status;
+       atc->spdif_out_set_status = atc_spdif_out_set_status;
+       atc->spdif_out_passthru = atc_spdif_out_passthru;
+       atc->have_digit_io_switch = atc_have_digit_io_switch;
+}
+
+/**
+ *  ct_atc_create - create and initialize a hardware manager
+ *  @card: corresponding alsa card object
+ *  @pci: corresponding kernel pci device object
+ *  @ratc: return created object address in it
+ *
+ *  Creates and initializes a hardware manager.
+ *
+ *  Creates kmallocated ct_atc structure. Initializes hardware.
+ *  Returns 0 if suceeds, or negative error code if fails.
+ */
+
+int ct_atc_create(struct snd_card *card, struct pci_dev *pci,
+                 unsigned int rsr, unsigned int msr, struct ct_atc **ratc)
+{
+       struct ct_atc *atc = NULL;
+       static struct snd_device_ops ops = {
+               .dev_free = atc_dev_free,
+       };
+       int err = 0;
+
+       *ratc = NULL;
+
+       atc = kzalloc(sizeof(*atc), GFP_KERNEL);
+       if (NULL == atc)
+               return -ENOMEM;
+
+       atc->card = card;
+       atc->pci = pci;
+       atc->rsr = rsr;
+       atc->msr = msr;
+
+       /* Set operations */
+       atc_set_ops(atc);
+
+       spin_lock_init(&atc->atc_lock);
+       spin_lock_init(&atc->vm_lock);
+
+       /* Find card model */
+       err = atc_identify_card(atc);
+       if (err < 0) {
+               printk(KERN_ERR "ctatc: Card not recognised\n");
+               goto error1;
+       }
+
+       /* Set up device virtual memory management object */
+       err = ct_vm_create(&atc->vm);
+       if (err < 0)
+               goto error1;
+
+       /* Create all atc hw devices */
+       err = atc_create_hw_devs(atc);
+       if (err < 0)
+               goto error1;
+
+       /* Get resources */
+       err = atc_get_resources(atc);
+       if (err < 0)
+               goto error1;
+
+       /* Build topology */
+       atc_connect_resources(atc);
+
+       atc->create_alsa_devs = ct_create_alsa_devs;
+
+       err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, atc, &ops);
+       if (err < 0)
+               goto error1;
+
+       snd_card_set_dev(card, &pci->dev);
+
+       *ratc = atc;
+       return 0;
+
+error1:
+       ct_atc_destroy(atc);
+       printk(KERN_ERR "Something wrong!!!\n");
+       return err;
+}
+
diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h
new file mode 100644 (file)
index 0000000..286c993
--- /dev/null
@@ -0,0 +1,155 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       ctatc.h
+ *
+ * @Brief
+ * This file contains the definition of the device resource management object.
+ *
+ * @Author     Liu Chun
+ * @Date       Mar 28 2008
+ *
+ */
+
+#ifndef CTATC_H
+#define CTATC_H
+
+#include <linux/types.h>
+#include <linux/spinlock_types.h>
+#include <linux/pci.h>
+#include <linux/timer.h>
+#include <sound/core.h>
+
+#include "ctvmem.h"
+#include "ctresource.h"
+
+enum CTALSADEVS {              /* Types of alsa devices */
+       FRONT,
+       REAR,
+       CLFE,
+       SURROUND,
+       IEC958,
+       MIXER,
+       NUM_CTALSADEVS          /* This should always be the last */
+};
+
+enum CTCARDS {
+       CTSB0760,
+       CTHENDRIX,
+       CTSB08801,
+       CTSB08802,
+       CTSB08803,
+       NUM_CTCARDS             /* This should always be the last */
+};
+
+struct ct_atc_chip_sub_details {
+       u16 subsys;
+       const char *nm_model;
+};
+
+struct ct_atc_chip_details {
+       u16 vendor;
+       u16 device;
+       const struct ct_atc_chip_sub_details *sub_details;
+       const char *nm_card;
+};
+
+struct ct_atc;
+
+/* alsa pcm stream descriptor */
+struct ct_atc_pcm {
+       struct snd_pcm_substream *substream;
+       void (*interrupt)(struct ct_atc_pcm *apcm);
+       unsigned int started:1;
+       unsigned int stop_timer:1;
+       struct timer_list timer;
+       spinlock_t timer_lock;
+       unsigned int position;
+
+       /* Only mono and interleaved modes are supported now. */
+       struct ct_vm_block *vm_block;
+       void *src;              /* SRC for interacting with host memory */
+       void **srccs;           /* SRCs for sample rate conversion */
+       void **srcimps;         /* SRC Input Mappers */
+       void **amixers;         /* AMIXERs for routing converted data */
+       void *mono;             /* A SUM resource for mixing chs to one */
+       unsigned char n_srcc;   /* Number of converting SRCs */
+       unsigned char n_srcimp; /* Number of SRC Input Mappers */
+       unsigned char n_amixer; /* Number of AMIXERs */
+};
+
+/* Chip resource management object */
+struct ct_atc {
+       struct pci_dev *pci;
+       struct snd_card *card;
+       unsigned int rsr; /* reference sample rate in Hz */
+       unsigned int msr; /* master sample rate in rsr */
+       unsigned int pll_rate; /* current rate of Phase Lock Loop */
+
+       const struct ct_atc_chip_details *chip_details;
+       enum CTCARDS model;
+       /* Create all alsa devices */
+       int (*create_alsa_devs)(struct ct_atc *atc);
+
+       struct ct_vm *vm; /* device virtual memory manager for this card */
+       int (*map_audio_buffer)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
+       void (*unmap_audio_buffer)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
+       unsigned long (*get_ptp_phys)(struct ct_atc *atc, int index);
+
+       spinlock_t atc_lock;
+       spinlock_t vm_lock;
+
+       int (*pcm_playback_prepare)(struct ct_atc *atc,
+                                   struct ct_atc_pcm *apcm);
+       int (*pcm_playback_start)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
+       int (*pcm_playback_stop)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
+       int (*pcm_playback_position)(struct ct_atc *atc,
+                                    struct ct_atc_pcm *apcm);
+       int (*spdif_passthru_playback_prepare)(struct ct_atc *atc,
+                                              struct ct_atc_pcm *apcm);
+       int (*pcm_capture_prepare)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
+       int (*pcm_capture_start)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
+       int (*pcm_capture_stop)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
+       int (*pcm_capture_position)(struct ct_atc *atc,
+                                   struct ct_atc_pcm *apcm);
+       int (*pcm_release_resources)(struct ct_atc *atc,
+                                    struct ct_atc_pcm *apcm);
+       int (*select_line_in)(struct ct_atc *atc);
+       int (*select_mic_in)(struct ct_atc *atc);
+       int (*select_digit_io)(struct ct_atc *atc);
+       int (*line_front_unmute)(struct ct_atc *atc, unsigned char state);
+       int (*line_surround_unmute)(struct ct_atc *atc, unsigned char state);
+       int (*line_clfe_unmute)(struct ct_atc *atc, unsigned char state);
+       int (*line_rear_unmute)(struct ct_atc *atc, unsigned char state);
+       int (*line_in_unmute)(struct ct_atc *atc, unsigned char state);
+       int (*spdif_out_unmute)(struct ct_atc *atc, unsigned char state);
+       int (*spdif_in_unmute)(struct ct_atc *atc, unsigned char state);
+       int (*spdif_out_get_status)(struct ct_atc *atc, unsigned int *status);
+       int (*spdif_out_set_status)(struct ct_atc *atc, unsigned int status);
+       int (*spdif_out_passthru)(struct ct_atc *atc, unsigned char state);
+       int (*have_digit_io_switch)(struct ct_atc *atc);
+
+       /* Don't touch! Used for internal object. */
+       void *rsc_mgrs[NUM_RSCTYP]; /* chip resource managers */
+       void *mixer;            /* internal mixer object */
+       void *hw;               /* chip specific hardware access object */
+       void **daios;           /* digital audio io resources */
+       void **pcm;             /* SUMs for collecting all pcm stream */
+       void **srcs;            /* Sample Rate Converters for input signal */
+       void **srcimps;         /* input mappers for SRCs */
+       unsigned char n_daio;
+       unsigned char n_src;
+       unsigned char n_srcimp;
+       unsigned char n_pcm;
+};
+
+
+int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci,
+                           unsigned int rsr, unsigned int msr,
+                           struct ct_atc **ratc);
+
+#endif /* CTATC_H */
diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c
new file mode 100644 (file)
index 0000000..a2aea39
--- /dev/null
@@ -0,0 +1,769 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       ctdaio.c
+ *
+ * @Brief
+ * This file contains the implementation of Digital Audio Input Output
+ * resource management object.
+ *
+ * @Author     Liu Chun
+ * @Date       May 23 2008
+ *
+ */
+
+#include "ctdaio.h"
+#include "cthardware.h"
+#include "ctimap.h"
+#include <linux/slab.h>
+#include <linux/kernel.h>
+
+#define DAIO_RESOURCE_NUM      NUM_DAIOTYP
+#define DAIO_OUT_MAX           SPDIFOO
+
+union daio_usage {
+       struct {
+               unsigned short lineo1:1;
+               unsigned short lineo2:1;
+               unsigned short lineo3:1;
+               unsigned short lineo4:1;
+               unsigned short spdifoo:1;
+               unsigned short lineim:1;
+               unsigned short spdifio:1;
+               unsigned short spdifi1:1;
+       } bf;
+       unsigned short data;
+};
+
+struct daio_rsc_idx {
+       unsigned short left;
+       unsigned short right;
+};
+
+struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = {
+       [LINEO1] = {.left = 0x00, .right = 0x01},
+       [LINEO2] = {.left = 0x18, .right = 0x19},
+       [LINEO3] = {.left = 0x08, .right = 0x09},
+       [LINEO4] = {.left = 0x10, .right = 0x11},
+       [LINEIM] = {.left = 0x1b5, .right = 0x1bd},
+       [SPDIFOO] = {.left = 0x20, .right = 0x21},
+       [SPDIFIO] = {.left = 0x15, .right = 0x1d},
+       [SPDIFI1] = {.left = 0x95, .right = 0x9d},
+};
+
+struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = {
+       [LINEO1] = {.left = 0x40, .right = 0x41},
+       [LINEO2] = {.left = 0x70, .right = 0x71},
+       [LINEO3] = {.left = 0x50, .right = 0x51},
+       [LINEO4] = {.left = 0x60, .right = 0x61},
+       [LINEIM] = {.left = 0x45, .right = 0xc5},
+       [SPDIFOO] = {.left = 0x00, .right = 0x01},
+       [SPDIFIO] = {.left = 0x05, .right = 0x85},
+};
+
+static int daio_master(struct rsc *rsc)
+{
+       /* Actually, this is not the resource index of DAIO.
+        * For DAO, it is the input mapper index. And, for DAI,
+        * it is the output time-slot index. */
+       return rsc->conj = rsc->idx;
+}
+
+static int daio_index(const struct rsc *rsc)
+{
+       return rsc->conj;
+}
+
+static int daio_out_next_conj(struct rsc *rsc)
+{
+       return rsc->conj += 2;
+}
+
+static int daio_in_next_conj_20k1(struct rsc *rsc)
+{
+       return rsc->conj += 0x200;
+}
+
+static int daio_in_next_conj_20k2(struct rsc *rsc)
+{
+       return rsc->conj += 0x100;
+}
+
+static struct rsc_ops daio_out_rsc_ops = {
+       .master         = daio_master,
+       .next_conj      = daio_out_next_conj,
+       .index          = daio_index,
+       .output_slot    = NULL,
+};
+
+static struct rsc_ops daio_in_rsc_ops_20k1 = {
+       .master         = daio_master,
+       .next_conj      = daio_in_next_conj_20k1,
+       .index          = NULL,
+       .output_slot    = daio_index,
+};
+
+static struct rsc_ops daio_in_rsc_ops_20k2 = {
+       .master         = daio_master,
+       .next_conj      = daio_in_next_conj_20k2,
+       .index          = NULL,
+       .output_slot    = daio_index,
+};
+
+static unsigned int daio_device_index(enum DAIOTYP type, struct hw *hw)
+{
+       switch (hw->get_chip_type(hw)) {
+       case ATC20K1:
+               switch (type) {
+               case SPDIFOO:   return 0;
+               case SPDIFIO:   return 0;
+               case SPDIFI1:   return 1;
+               case LINEO1:    return 4;
+               case LINEO2:    return 7;
+               case LINEO3:    return 5;
+               case LINEO4:    return 6;
+               case LINEIM:    return 7;
+               default:        return -EINVAL;
+               }
+       case ATC20K2:
+               switch (type) {
+               case SPDIFOO:   return 0;
+               case SPDIFIO:   return 0;
+               case LINEO1:    return 4;
+               case LINEO2:    return 7;
+               case LINEO3:    return 5;
+               case LINEO4:    return 6;
+               case LINEIM:    return 4;
+               default:        return -EINVAL;
+               }
+       default:
+               return -EINVAL;
+       }
+}
+
+static int dao_rsc_reinit(struct dao *dao, const struct dao_desc *desc);
+
+static int dao_spdif_get_spos(struct dao *dao, unsigned int *spos)
+{
+       ((struct hw *)dao->hw)->dao_get_spos(dao->ctrl_blk, spos);
+       return 0;
+}
+
+static int dao_spdif_set_spos(struct dao *dao, unsigned int spos)
+{
+       ((struct hw *)dao->hw)->dao_set_spos(dao->ctrl_blk, spos);
+       return 0;
+}
+
+static int dao_commit_write(struct dao *dao)
+{
+       ((struct hw *)dao->hw)->dao_commit_write(dao->hw,
+               daio_device_index(dao->daio.type, dao->hw), dao->ctrl_blk);
+       return 0;
+}
+
+static int dao_set_left_input(struct dao *dao, struct rsc *input)
+{
+       struct imapper *entry = NULL;
+       struct daio *daio = &dao->daio;
+       int i = 0;
+
+       entry = kzalloc((sizeof(*entry) * daio->rscl.msr), GFP_KERNEL);
+       if (NULL == entry)
+               return -ENOMEM;
+
+       /* Program master and conjugate resources */
+       input->ops->master(input);
+       daio->rscl.ops->master(&daio->rscl);
+       for (i = 0; i < daio->rscl.msr; i++, entry++) {
+               entry->slot = input->ops->output_slot(input);
+               entry->user = entry->addr = daio->rscl.ops->index(&daio->rscl);
+               dao->mgr->imap_add(dao->mgr, entry);
+               dao->imappers[i] = entry;
+
+               input->ops->next_conj(input);
+               daio->rscl.ops->next_conj(&daio->rscl);
+       }
+       input->ops->master(input);
+       daio->rscl.ops->master(&daio->rscl);
+
+       return 0;
+}
+
+static int dao_set_right_input(struct dao *dao, struct rsc *input)
+{
+       struct imapper *entry = NULL;
+       struct daio *daio = &dao->daio;
+       int i = 0;
+
+       entry = kzalloc((sizeof(*entry) * daio->rscr.msr), GFP_KERNEL);
+       if (NULL == entry)
+               return -ENOMEM;
+
+       /* Program master and conjugate resources */
+       input->ops->master(input);
+       daio->rscr.ops->master(&daio->rscr);
+       for (i = 0; i < daio->rscr.msr; i++, entry++) {
+               entry->slot = input->ops->output_slot(input);
+               entry->user = entry->addr = daio->rscr.ops->index(&daio->rscr);
+               dao->mgr->imap_add(dao->mgr, entry);
+               dao->imappers[daio->rscl.msr + i] = entry;
+
+               input->ops->next_conj(input);
+               daio->rscr.ops->next_conj(&daio->rscr);
+       }
+       input->ops->master(input);
+       daio->rscr.ops->master(&daio->rscr);
+
+       return 0;
+}
+
+static int dao_clear_left_input(struct dao *dao)
+{
+       struct imapper *entry = NULL;
+       struct daio *daio = &dao->daio;
+       int i = 0;
+
+       if (NULL == dao->imappers[0])
+               return 0;
+
+       entry = dao->imappers[0];
+       dao->mgr->imap_delete(dao->mgr, entry);
+       /* Program conjugate resources */
+       for (i = 1; i < daio->rscl.msr; i++) {
+               entry = dao->imappers[i];
+               dao->mgr->imap_delete(dao->mgr, entry);
+               dao->imappers[i] = NULL;
+       }
+
+       kfree(dao->imappers[0]);
+       dao->imappers[0] = NULL;
+
+       return 0;
+}
+
+static int dao_clear_right_input(struct dao *dao)
+{
+       struct imapper *entry = NULL;
+       struct daio *daio = &dao->daio;
+       int i = 0;
+
+       if (NULL == dao->imappers[daio->rscl.msr])
+               return 0;
+
+       entry = dao->imappers[daio->rscl.msr];
+       dao->mgr->imap_delete(dao->mgr, entry);
+       /* Program conjugate resources */
+       for (i = 1; i < daio->rscr.msr; i++) {
+               entry = dao->imappers[daio->rscl.msr + i];
+               dao->mgr->imap_delete(dao->mgr, entry);
+               dao->imappers[daio->rscl.msr + i] = NULL;
+       }
+
+       kfree(dao->imappers[daio->rscl.msr]);
+       dao->imappers[daio->rscl.msr] = NULL;
+
+       return 0;
+}
+
+static struct dao_rsc_ops dao_ops = {
+       .set_spos               = dao_spdif_set_spos,
+       .commit_write           = dao_commit_write,
+       .get_spos               = dao_spdif_get_spos,
+       .reinit                 = dao_rsc_reinit,
+       .set_left_input         = dao_set_left_input,
+       .set_right_input        = dao_set_right_input,
+       .clear_left_input       = dao_clear_left_input,
+       .clear_right_input      = dao_clear_right_input,
+};
+
+static int dai_set_srt_srcl(struct dai *dai, struct rsc *src)
+{
+       src->ops->master(src);
+       ((struct hw *)dai->hw)->dai_srt_set_srcm(dai->ctrl_blk,
+                                               src->ops->index(src));
+       return 0;
+}
+
+static int dai_set_srt_srcr(struct dai *dai, struct rsc *src)
+{
+       src->ops->master(src);
+       ((struct hw *)dai->hw)->dai_srt_set_srco(dai->ctrl_blk,
+                                               src->ops->index(src));
+       return 0;
+}
+
+static int dai_set_srt_msr(struct dai *dai, unsigned int msr)
+{
+       unsigned int rsr = 0;
+
+       for (rsr = 0; msr > 1; msr >>= 1)
+               rsr++;
+
+       ((struct hw *)dai->hw)->dai_srt_set_rsr(dai->ctrl_blk, rsr);
+       return 0;
+}
+
+static int dai_set_enb_src(struct dai *dai, unsigned int enb)
+{
+       ((struct hw *)dai->hw)->dai_srt_set_ec(dai->ctrl_blk, enb);
+       return 0;
+}
+
+static int dai_set_enb_srt(struct dai *dai, unsigned int enb)
+{
+       ((struct hw *)dai->hw)->dai_srt_set_et(dai->ctrl_blk, enb);
+       return 0;
+}
+
+static int dai_commit_write(struct dai *dai)
+{
+       ((struct hw *)dai->hw)->dai_commit_write(dai->hw,
+               daio_device_index(dai->daio.type, dai->hw), dai->ctrl_blk);
+       return 0;
+}
+
+static struct dai_rsc_ops dai_ops = {
+       .set_srt_srcl           = dai_set_srt_srcl,
+       .set_srt_srcr           = dai_set_srt_srcr,
+       .set_srt_msr            = dai_set_srt_msr,
+       .set_enb_src            = dai_set_enb_src,
+       .set_enb_srt            = dai_set_enb_srt,
+       .commit_write           = dai_commit_write,
+};
+
+static int daio_rsc_init(struct daio *daio,
+                        const struct daio_desc *desc,
+                        void *hw)
+{
+       int err = 0;
+       unsigned int idx_l = 0, idx_r = 0;
+
+       switch (((struct hw *)hw)->get_chip_type(hw)) {
+       case ATC20K1:
+               idx_l = idx_20k1[desc->type].left;
+               idx_r = idx_20k1[desc->type].right;
+               break;
+       case ATC20K2:
+               idx_l = idx_20k2[desc->type].left;
+               idx_r = idx_20k2[desc->type].right;
+               break;
+       default:
+               return -EINVAL;
+       }
+       err = rsc_init(&daio->rscl, idx_l, DAIO, desc->msr, hw);
+       if (err)
+               return err;
+
+       err = rsc_init(&daio->rscr, idx_r, DAIO, desc->msr, hw);
+       if (err)
+               goto error1;
+
+       /* Set daio->rscl/r->ops to daio specific ones */
+       if (desc->type <= DAIO_OUT_MAX) {
+               daio->rscl.ops = daio->rscr.ops = &daio_out_rsc_ops;
+       } else {
+               switch (((struct hw *)hw)->get_chip_type(hw)) {
+               case ATC20K1:
+                       daio->rscl.ops = daio->rscr.ops = &daio_in_rsc_ops_20k1;
+                       break;
+               case ATC20K2:
+                       daio->rscl.ops = daio->rscr.ops = &daio_in_rsc_ops_20k2;
+                       break;
+               default:
+                       break;
+               }
+       }
+       daio->type = desc->type;
+
+       return 0;
+
+error1:
+       rsc_uninit(&daio->rscl);
+       return err;
+}
+
+static int daio_rsc_uninit(struct daio *daio)
+{
+       rsc_uninit(&daio->rscl);
+       rsc_uninit(&daio->rscr);
+
+       return 0;
+}
+
+static int dao_rsc_init(struct dao *dao,
+                       const struct daio_desc *desc,
+                       struct daio_mgr *mgr)
+{
+       struct hw *hw = mgr->mgr.hw;
+       unsigned int conf = 0;
+       int err = 0;
+
+       err = daio_rsc_init(&dao->daio, desc, mgr->mgr.hw);
+       if (err)
+               return err;
+
+       dao->imappers = kzalloc(sizeof(void *)*desc->msr*2, GFP_KERNEL);
+       if (NULL == dao->imappers) {
+               err = -ENOMEM;
+               goto error1;
+       }
+       dao->ops = &dao_ops;
+       dao->mgr = mgr;
+       dao->hw = hw;
+       err = hw->dao_get_ctrl_blk(&dao->ctrl_blk);
+       if (err)
+               goto error2;
+
+       hw->daio_mgr_dsb_dao(mgr->mgr.ctrl_blk,
+                       daio_device_index(dao->daio.type, hw));
+       hw->daio_mgr_commit_write(hw, mgr->mgr.ctrl_blk);
+
+       conf |= (desc->msr & 0x7) | (desc->passthru << 3);
+       hw->daio_mgr_dao_init(mgr->mgr.ctrl_blk,
+                       daio_device_index(dao->daio.type, hw), conf);
+       hw->daio_mgr_enb_dao(mgr->mgr.ctrl_blk,
+                       daio_device_index(dao->daio.type, hw));
+       hw->daio_mgr_commit_write(hw, mgr->mgr.ctrl_blk);
+
+       return 0;
+
+error2:
+       kfree(dao->imappers);
+       dao->imappers = NULL;
+error1:
+       daio_rsc_uninit(&dao->daio);
+       return err;
+}
+
+static int dao_rsc_uninit(struct dao *dao)
+{
+       if (NULL != dao->imappers) {
+               if (NULL != dao->imappers[0])
+                       dao_clear_left_input(dao);
+
+               if (NULL != dao->imappers[dao->daio.rscl.msr])
+                       dao_clear_right_input(dao);
+
+               kfree(dao->imappers);
+               dao->imappers = NULL;
+       }
+       ((struct hw *)dao->hw)->dao_put_ctrl_blk(dao->ctrl_blk);
+       dao->hw = dao->ctrl_blk = NULL;
+       daio_rsc_uninit(&dao->daio);
+
+       return 0;
+}
+
+static int dao_rsc_reinit(struct dao *dao, const struct dao_desc *desc)
+{
+       struct daio_mgr *mgr = dao->mgr;
+       struct daio_desc dsc = {0};
+
+       dsc.type = dao->daio.type;
+       dsc.msr = desc->msr;
+       dsc.passthru = desc->passthru;
+       dao_rsc_uninit(dao);
+       return dao_rsc_init(dao, &dsc, mgr);
+}
+
+static int dai_rsc_init(struct dai *dai,
+                       const struct daio_desc *desc,
+                       struct daio_mgr *mgr)
+{
+       int err = 0;
+       struct hw *hw = mgr->mgr.hw;
+       unsigned int rsr = 0, msr = 0;
+
+       err = daio_rsc_init(&dai->daio, desc, mgr->mgr.hw);
+       if (err)
+               return err;
+
+       dai->ops = &dai_ops;
+       dai->hw = mgr->mgr.hw;
+       err = hw->dai_get_ctrl_blk(&dai->ctrl_blk);
+       if (err)
+               goto error1;
+
+       for (rsr = 0, msr = desc->msr; msr > 1; msr >>= 1)
+               rsr++;
+
+       hw->dai_srt_set_rsr(dai->ctrl_blk, rsr);
+       hw->dai_srt_set_drat(dai->ctrl_blk, 0);
+       /* default to disabling control of a SRC */
+       hw->dai_srt_set_ec(dai->ctrl_blk, 0);
+       hw->dai_srt_set_et(dai->ctrl_blk, 0); /* default to disabling SRT */
+       hw->dai_commit_write(hw,
+               daio_device_index(dai->daio.type, dai->hw), dai->ctrl_blk);
+
+       return 0;
+
+error1:
+       daio_rsc_uninit(&dai->daio);
+       return err;
+}
+
+static int dai_rsc_uninit(struct dai *dai)
+{
+       ((struct hw *)dai->hw)->dai_put_ctrl_blk(dai->ctrl_blk);
+       dai->hw = dai->ctrl_blk = NULL;
+       daio_rsc_uninit(&dai->daio);
+       return 0;
+}
+
+static int daio_mgr_get_rsc(struct rsc_mgr *mgr, enum DAIOTYP type)
+{
+       if (((union daio_usage *)mgr->rscs)->data & (0x1 << type))
+               return -ENOENT;
+
+       ((union daio_usage *)mgr->rscs)->data |= (0x1 << type);
+
+       return 0;
+}
+
+static int daio_mgr_put_rsc(struct rsc_mgr *mgr, enum DAIOTYP type)
+{
+       ((union daio_usage *)mgr->rscs)->data &= ~(0x1 << type);
+
+       return 0;
+}
+
+static int get_daio_rsc(struct daio_mgr *mgr,
+                       const struct daio_desc *desc,
+                       struct daio **rdaio)
+{
+       int err = 0;
+       struct dai *dai = NULL;
+       struct dao *dao = NULL;
+       unsigned long flags;
+
+       *rdaio = NULL;
+
+       /* Check whether there are sufficient daio resources to meet request. */
+       spin_lock_irqsave(&mgr->mgr_lock, flags);
+       err = daio_mgr_get_rsc(&mgr->mgr, desc->type);
+       spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+       if (err) {
+               printk(KERN_ERR "Can't meet DAIO resource request!\n");
+               return err;
+       }
+
+       /* Allocate mem for daio resource */
+       if (desc->type <= DAIO_OUT_MAX) {
+               dao = kzalloc(sizeof(*dao), GFP_KERNEL);
+               if (NULL == dao) {
+                       err = -ENOMEM;
+                       goto error;
+               }
+               err = dao_rsc_init(dao, desc, mgr);
+               if (err)
+                       goto error;
+
+               *rdaio = &dao->daio;
+       } else {
+               dai = kzalloc(sizeof(*dai), GFP_KERNEL);
+               if (NULL == dai) {
+                       err = -ENOMEM;
+                       goto error;
+               }
+               err = dai_rsc_init(dai, desc, mgr);
+               if (err)
+                       goto error;
+
+               *rdaio = &dai->daio;
+       }
+
+       mgr->daio_enable(mgr, *rdaio);
+       mgr->commit_write(mgr);
+
+       return 0;
+
+error:
+       if (NULL != dao)
+               kfree(dao);
+       else if (NULL != dai)
+               kfree(dai);
+
+       spin_lock_irqsave(&mgr->mgr_lock, flags);
+       daio_mgr_put_rsc(&mgr->mgr, desc->type);
+       spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+       return err;
+}
+
+static int put_daio_rsc(struct daio_mgr *mgr, struct daio *daio)
+{
+       unsigned long flags;
+
+       mgr->daio_disable(mgr, daio);
+       mgr->commit_write(mgr);
+
+       spin_lock_irqsave(&mgr->mgr_lock, flags);
+       daio_mgr_put_rsc(&mgr->mgr, daio->type);
+       spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+
+       if (daio->type <= DAIO_OUT_MAX) {
+               dao_rsc_uninit(container_of(daio, struct dao, daio));
+               kfree(container_of(daio, struct dao, daio));
+       } else {
+               dai_rsc_uninit(container_of(daio, struct dai, daio));
+               kfree(container_of(daio, struct dai, daio));
+       }
+
+       return 0;
+}
+
+static int daio_mgr_enb_daio(struct daio_mgr *mgr, struct daio *daio)
+{
+       struct hw *hw = mgr->mgr.hw;
+
+       if (DAIO_OUT_MAX >= daio->type) {
+               hw->daio_mgr_enb_dao(mgr->mgr.ctrl_blk,
+                               daio_device_index(daio->type, hw));
+       } else {
+               hw->daio_mgr_enb_dai(mgr->mgr.ctrl_blk,
+                               daio_device_index(daio->type, hw));
+       }
+       return 0;
+}
+
+static int daio_mgr_dsb_daio(struct daio_mgr *mgr, struct daio *daio)
+{
+       struct hw *hw = mgr->mgr.hw;
+
+       if (DAIO_OUT_MAX >= daio->type) {
+               hw->daio_mgr_dsb_dao(mgr->mgr.ctrl_blk,
+                               daio_device_index(daio->type, hw));
+       } else {
+               hw->daio_mgr_dsb_dai(mgr->mgr.ctrl_blk,
+                               daio_device_index(daio->type, hw));
+       }
+       return 0;
+}
+
+static int daio_map_op(void *data, struct imapper *entry)
+{
+       struct rsc_mgr *mgr = &((struct daio_mgr *)data)->mgr;
+       struct hw *hw = mgr->hw;
+
+       hw->daio_mgr_set_imaparc(mgr->ctrl_blk, entry->slot);
+       hw->daio_mgr_set_imapnxt(mgr->ctrl_blk, entry->next);
+       hw->daio_mgr_set_imapaddr(mgr->ctrl_blk, entry->addr);
+       hw->daio_mgr_commit_write(mgr->hw, mgr->ctrl_blk);
+
+       return 0;
+}
+
+static int daio_imap_add(struct daio_mgr *mgr, struct imapper *entry)
+{
+       unsigned long flags;
+       int err = 0;
+
+       spin_lock_irqsave(&mgr->imap_lock, flags);
+       if ((0 == entry->addr) && (mgr->init_imap_added)) {
+               input_mapper_delete(&mgr->imappers, mgr->init_imap,
+                                                       daio_map_op, mgr);
+               mgr->init_imap_added = 0;
+       }
+       err = input_mapper_add(&mgr->imappers, entry, daio_map_op, mgr);
+       spin_unlock_irqrestore(&mgr->imap_lock, flags);
+
+       return err;
+}
+
+static int daio_imap_delete(struct daio_mgr *mgr, struct imapper *entry)
+{
+       unsigned long flags;
+       int err = 0;
+
+       spin_lock_irqsave(&mgr->imap_lock, flags);
+       err = input_mapper_delete(&mgr->imappers, entry, daio_map_op, mgr);
+       if (list_empty(&mgr->imappers)) {
+               input_mapper_add(&mgr->imappers, mgr->init_imap,
+                                                       daio_map_op, mgr);
+               mgr->init_imap_added = 1;
+       }
+       spin_unlock_irqrestore(&mgr->imap_lock, flags);
+
+       return err;
+}
+
+static int daio_mgr_commit_write(struct daio_mgr *mgr)
+{
+       struct hw *hw = mgr->mgr.hw;
+
+       hw->daio_mgr_commit_write(hw, mgr->mgr.ctrl_blk);
+       return 0;
+}
+
+int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr)
+{
+       int err = 0, i = 0;
+       struct daio_mgr *daio_mgr;
+       struct imapper *entry;
+
+       *rdaio_mgr = NULL;
+       daio_mgr = kzalloc(sizeof(*daio_mgr), GFP_KERNEL);
+       if (NULL == daio_mgr)
+               return -ENOMEM;
+
+       err = rsc_mgr_init(&daio_mgr->mgr, DAIO, DAIO_RESOURCE_NUM, hw);
+       if (err)
+               goto error1;
+
+       spin_lock_init(&daio_mgr->mgr_lock);
+       spin_lock_init(&daio_mgr->imap_lock);
+       INIT_LIST_HEAD(&daio_mgr->imappers);
+       entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+       if (NULL == entry) {
+               err = -ENOMEM;
+               goto error2;
+       }
+       entry->slot = entry->addr = entry->next = entry->user = 0;
+       list_add(&entry->list, &daio_mgr->imappers);
+       daio_mgr->init_imap = entry;
+       daio_mgr->init_imap_added = 1;
+
+       daio_mgr->get_daio = get_daio_rsc;
+       daio_mgr->put_daio = put_daio_rsc;
+       daio_mgr->daio_enable = daio_mgr_enb_daio;
+       daio_mgr->daio_disable = daio_mgr_dsb_daio;
+       daio_mgr->imap_add = daio_imap_add;
+       daio_mgr->imap_delete = daio_imap_delete;
+       daio_mgr->commit_write = daio_mgr_commit_write;
+
+       for (i = 0; i < 8; i++) {
+               ((struct hw *)hw)->daio_mgr_dsb_dao(daio_mgr->mgr.ctrl_blk, i);
+               ((struct hw *)hw)->daio_mgr_dsb_dai(daio_mgr->mgr.ctrl_blk, i);
+       }
+       ((struct hw *)hw)->daio_mgr_commit_write(hw, daio_mgr->mgr.ctrl_blk);
+
+       *rdaio_mgr = daio_mgr;
+
+       return 0;
+
+error2:
+       rsc_mgr_uninit(&daio_mgr->mgr);
+error1:
+       kfree(daio_mgr);
+       return err;
+}
+
+int daio_mgr_destroy(struct daio_mgr *daio_mgr)
+{
+       unsigned long flags;
+
+       /* free daio input mapper list */
+       spin_lock_irqsave(&daio_mgr->imap_lock, flags);
+       free_input_mapper_list(&daio_mgr->imappers);
+       spin_unlock_irqrestore(&daio_mgr->imap_lock, flags);
+
+       rsc_mgr_uninit(&daio_mgr->mgr);
+       kfree(daio_mgr);
+
+       return 0;
+}
+
diff --git a/sound/pci/ctxfi/ctdaio.h b/sound/pci/ctxfi/ctdaio.h
new file mode 100644 (file)
index 0000000..0f52ce5
--- /dev/null
@@ -0,0 +1,122 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       ctdaio.h
+ *
+ * @Brief
+ * This file contains the definition of Digital Audio Input Output
+ * resource management object.
+ *
+ * @Author     Liu Chun
+ * @Date       May 23 2008
+ *
+ */
+
+#ifndef CTDAIO_H
+#define CTDAIO_H
+
+#include "ctresource.h"
+#include "ctimap.h"
+#include <linux/spinlock.h>
+#include <linux/list.h>
+
+/* Define the descriptor of a daio resource */
+enum DAIOTYP {
+       LINEO1,
+       LINEO2,
+       LINEO3,
+       LINEO4,
+       SPDIFOO,        /* S/PDIF Out (Flexijack/Optical) */
+       LINEIM,
+       SPDIFIO,        /* S/PDIF In (Flexijack/Optical) on the card */
+       SPDIFI1,        /* S/PDIF In on internal Drive Bay */
+       NUM_DAIOTYP
+};
+
+struct dao_rsc_ops;
+struct dai_rsc_ops;
+struct daio_mgr;
+
+struct daio {
+       struct rsc rscl;        /* Basic resource info for left TX/RX */
+       struct rsc rscr;        /* Basic resource info for right TX/RX */
+       enum DAIOTYP type;
+};
+
+struct dao {
+       struct daio daio;
+       struct dao_rsc_ops *ops;        /* DAO specific operations */
+       struct imapper **imappers;
+       struct daio_mgr *mgr;
+       void *hw;
+       void *ctrl_blk;
+};
+
+struct dai {
+       struct daio daio;
+       struct dai_rsc_ops *ops;        /* DAI specific operations */
+       void *hw;
+       void *ctrl_blk;
+};
+
+struct dao_desc {
+       unsigned int msr:4;
+       unsigned int passthru:1;
+};
+
+struct dao_rsc_ops {
+       int (*set_spos)(struct dao *dao, unsigned int spos);
+       int (*commit_write)(struct dao *dao);
+       int (*get_spos)(struct dao *dao, unsigned int *spos);
+       int (*reinit)(struct dao *dao, const struct dao_desc *desc);
+       int (*set_left_input)(struct dao *dao, struct rsc *input);
+       int (*set_right_input)(struct dao *dao, struct rsc *input);
+       int (*clear_left_input)(struct dao *dao);
+       int (*clear_right_input)(struct dao *dao);
+};
+
+struct dai_rsc_ops {
+       int (*set_srt_srcl)(struct dai *dai, struct rsc *src);
+       int (*set_srt_srcr)(struct dai *dai, struct rsc *src);
+       int (*set_srt_msr)(struct dai *dai, unsigned int msr);
+       int (*set_enb_src)(struct dai *dai, unsigned int enb);
+       int (*set_enb_srt)(struct dai *dai, unsigned int enb);
+       int (*commit_write)(struct dai *dai);
+};
+
+/* Define daio resource request description info */
+struct daio_desc {
+       unsigned int type:4;
+       unsigned int msr:4;
+       unsigned int passthru:1;
+};
+
+struct daio_mgr {
+       struct rsc_mgr mgr;     /* Basic resource manager info */
+       spinlock_t mgr_lock;
+       spinlock_t imap_lock;
+       struct list_head imappers;
+       struct imapper *init_imap;
+       unsigned int init_imap_added;
+
+        /* request one daio resource */
+       int (*get_daio)(struct daio_mgr *mgr,
+                       const struct daio_desc *desc, struct daio **rdaio);
+       /* return one daio resource */
+       int (*put_daio)(struct daio_mgr *mgr, struct daio *daio);
+       int (*daio_enable)(struct daio_mgr *mgr, struct daio *daio);
+       int (*daio_disable)(struct daio_mgr *mgr, struct daio *daio);
+       int (*imap_add)(struct daio_mgr *mgr, struct imapper *entry);
+       int (*imap_delete)(struct daio_mgr *mgr, struct imapper *entry);
+       int (*commit_write)(struct daio_mgr *mgr);
+};
+
+/* Constructor and destructor of daio resource manager */
+int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr);
+int daio_mgr_destroy(struct daio_mgr *daio_mgr);
+
+#endif /* CTDAIO_H */
diff --git a/sound/pci/ctxfi/ctdrv.h b/sound/pci/ctxfi/ctdrv.h
new file mode 100644 (file)
index 0000000..f776a44
--- /dev/null
@@ -0,0 +1,30 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @file    ctdrv.h
+ *
+ * @breaf
+ * This file contains the definition of card IDs supported by this driver.
+ *
+ * @author Liu Chun
+ *
+ */
+
+#ifndef CTDRV_H
+#define CTDRV_H
+
+#define PCI_VENDOR_CREATIVE            0x1102
+#define PCI_DEVICE_CREATIVE_20K1       0x0005
+#define PCI_DEVICE_CREATIVE_20K2       0x000B
+#define PCI_SUBVENDOR_CREATIVE         0x1102
+#define PCI_SUBSYS_CREATIVE_SB0760     0x0024
+#define PCI_SUBSYS_CREATIVE_SB08801    0x0041
+#define PCI_SUBSYS_CREATIVE_SB08802    0x0042
+#define PCI_SUBSYS_CREATIVE_SB08803    0x0043
+#define PCI_SUBSYS_CREATIVE_HENDRIX    0x6000
+
+#endif /* CTDRV_H */
diff --git a/sound/pci/ctxfi/cthardware.c b/sound/pci/ctxfi/cthardware.c
new file mode 100644 (file)
index 0000000..8e58860
--- /dev/null
@@ -0,0 +1,108 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       cthardware.c
+ *
+ * @Brief
+ * This file contains the implementation of hardware access methord.
+ *
+ * @Author     Liu Chun
+ * @Date       Jun 26 2008
+ *
+ */
+
+#include "cthardware.h"
+#include "cthw20k1.h"
+#include "cthw20k2.h"
+#include <linux/bug.h>
+
+static enum CHIPTYP get_chip_type(struct hw *hw)
+{
+       enum CHIPTYP type = ATCNONE;
+
+       switch (hw->pci->device) {
+       case 0x0005:    /* 20k1 device */
+               type = ATC20K1;
+               break;
+       case 0x000B:    /* 20k2 device */
+               type = ATC20K2;
+               break;
+       default:
+               type = ATCNONE;
+               break;
+       }
+
+       return type;
+}
+
+int create_hw_obj(struct pci_dev *pci, struct hw **rhw)
+{
+       int err = 0;
+
+       switch (pci->device) {
+       case 0x0005:    /* 20k1 device */
+               err = create_20k1_hw_obj(rhw);
+               break;
+       case 0x000B:    /* 20k2 device */
+               err = create_20k2_hw_obj(rhw);
+               break;
+       default:
+               err = -ENODEV;
+               break;
+       }
+       if (err)
+               return err;
+
+       (*rhw)->pci = pci;
+       (*rhw)->get_chip_type = get_chip_type;
+
+       return 0;
+}
+
+int destroy_hw_obj(struct hw *hw)
+{
+       int err = 0;
+
+       switch (hw->pci->device) {
+       case 0x0005:    /* 20k1 device */
+               err = destroy_20k1_hw_obj(hw);
+               break;
+       case 0x000B:    /* 20k2 device */
+               err = destroy_20k2_hw_obj(hw);
+               break;
+       default:
+               err = -ENODEV;
+               break;
+       }
+
+       return err;
+}
+
+unsigned int get_field(unsigned int data, unsigned int field)
+{
+       int i;
+
+       BUG_ON(!field);
+       /* @field should always be greater than 0 */
+       for (i = 0; !(field & (1 << i)); )
+               i++;
+
+       return (data & field) >> i;
+}
+
+void set_field(unsigned int *data, unsigned int field, unsigned int value)
+{
+       int i;
+
+       BUG_ON(!field);
+       /* @field should always be greater than 0 */
+       for (i = 0; !(field & (1 << i)); )
+               i++;
+
+       *data = (*data & (~field)) | ((value << i) & field);
+}
+
diff --git a/sound/pci/ctxfi/cthardware.h b/sound/pci/ctxfi/cthardware.h
new file mode 100644 (file)
index 0000000..b0512df
--- /dev/null
@@ -0,0 +1,160 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       cthardware.h
+ *
+ * @Brief
+ * This file contains the definition of hardware access methord.
+ *
+ * @Author     Liu Chun
+ * @Date       May 13 2008
+ *
+ */
+
+#ifndef CTHARDWARE_H
+#define CTHARDWARE_H
+
+#include <linux/types.h>
+#include <linux/pci.h>
+
+enum CHIPTYP {
+       ATC20K1,
+       ATC20K2,
+       ATCNONE
+};
+
+/* Type of input source for ADC */
+enum ADCSRC{
+       ADC_MICIN,
+       ADC_LINEIN,
+       ADC_VIDEO,
+       ADC_AUX,
+       ADC_NONE        /* Switch to digital input */
+};
+
+struct card_conf {
+       /* device virtual mem page table page physical addr
+        * (supporting one page table page now) */
+       unsigned long vm_pgt_phys;
+       unsigned int rsr;       /* reference sample rate in Hzs*/
+       unsigned int msr;       /* master sample rate in rsrs */
+};
+
+struct hw {
+       int (*card_init)(struct hw *hw, struct card_conf *info);
+       int (*card_stop)(struct hw *hw);
+       int (*pll_init)(struct hw *hw, unsigned int rsr);
+       enum CHIPTYP (*get_chip_type)(struct hw *hw);
+       int (*is_adc_source_selected)(struct hw *hw, enum ADCSRC source);
+       int (*select_adc_source)(struct hw *hw, enum ADCSRC source);
+       int (*have_digit_io_switch)(struct hw *hw);
+
+       /* SRC operations */
+       int (*src_rsc_get_ctrl_blk)(void **rblk);
+       int (*src_rsc_put_ctrl_blk)(void *blk);
+       int (*src_set_state)(void *blk, unsigned int state);
+       int (*src_set_bm)(void *blk, unsigned int bm);
+       int (*src_set_rsr)(void *blk, unsigned int rsr);
+       int (*src_set_sf)(void *blk, unsigned int sf);
+       int (*src_set_wr)(void *blk, unsigned int wr);
+       int (*src_set_pm)(void *blk, unsigned int pm);
+       int (*src_set_rom)(void *blk, unsigned int rom);
+       int (*src_set_vo)(void *blk, unsigned int vo);
+       int (*src_set_st)(void *blk, unsigned int st);
+       int (*src_set_ie)(void *blk, unsigned int ie);
+       int (*src_set_ilsz)(void *blk, unsigned int ilsz);
+       int (*src_set_bp)(void *blk, unsigned int bp);
+       int (*src_set_cisz)(void *blk, unsigned int cisz);
+       int (*src_set_ca)(void *blk, unsigned int ca);
+       int (*src_set_sa)(void *blk, unsigned int sa);
+       int (*src_set_la)(void *blk, unsigned int la);
+       int (*src_set_pitch)(void *blk, unsigned int pitch);
+       int (*src_set_clear_zbufs)(void *blk, unsigned int clear);
+       int (*src_set_dirty)(void *blk, unsigned int flags);
+       int (*src_set_dirty_all)(void *blk);
+       int (*src_commit_write)(struct hw *hw, unsigned int idx, void *blk);
+       int (*src_get_ca)(struct hw *hw, unsigned int idx, void *blk);
+       unsigned int (*src_get_dirty)(void *blk);
+       unsigned int (*src_dirty_conj_mask)(void);
+       int (*src_mgr_get_ctrl_blk)(void **rblk);
+       int (*src_mgr_put_ctrl_blk)(void *blk);
+       /* syncly enable src @idx */
+       int (*src_mgr_enbs_src)(void *blk, unsigned int idx);
+       /* enable src @idx */
+       int (*src_mgr_enb_src)(void *blk, unsigned int idx);
+       /* disable src @idx */
+       int (*src_mgr_dsb_src)(void *blk, unsigned int idx);
+       int (*src_mgr_commit_write)(struct hw *hw, void *blk);
+
+       /* SRC Input Mapper operations */
+       int (*srcimp_mgr_get_ctrl_blk)(void **rblk);
+       int (*srcimp_mgr_put_ctrl_blk)(void *blk);
+       int (*srcimp_mgr_set_imaparc)(void *blk, unsigned int slot);
+       int (*srcimp_mgr_set_imapuser)(void *blk, unsigned int user);
+       int (*srcimp_mgr_set_imapnxt)(void *blk, unsigned int next);
+       int (*srcimp_mgr_set_imapaddr)(void *blk, unsigned int addr);
+       int (*srcimp_mgr_commit_write)(struct hw *hw, void *blk);
+
+       /* AMIXER operations */
+       int (*amixer_rsc_get_ctrl_blk)(void **rblk);
+       int (*amixer_rsc_put_ctrl_blk)(void *blk);
+       int (*amixer_mgr_get_ctrl_blk)(void **rblk);
+       int (*amixer_mgr_put_ctrl_blk)(void *blk);
+       int (*amixer_set_mode)(void *blk, unsigned int mode);
+       int (*amixer_set_iv)(void *blk, unsigned int iv);
+       int (*amixer_set_x)(void *blk, unsigned int x);
+       int (*amixer_set_y)(void *blk, unsigned int y);
+       int (*amixer_set_sadr)(void *blk, unsigned int sadr);
+       int (*amixer_set_se)(void *blk, unsigned int se);
+       int (*amixer_set_dirty)(void *blk, unsigned int flags);
+       int (*amixer_set_dirty_all)(void *blk);
+       int (*amixer_commit_write)(struct hw *hw, unsigned int idx, void *blk);
+       int (*amixer_get_y)(void *blk);
+       unsigned int (*amixer_get_dirty)(void *blk);
+
+       /* DAIO operations */
+       int (*dai_get_ctrl_blk)(void **rblk);
+       int (*dai_put_ctrl_blk)(void *blk);
+       int (*dai_srt_set_srco)(void *blk, unsigned int src);
+       int (*dai_srt_set_srcm)(void *blk, unsigned int src);
+       int (*dai_srt_set_rsr)(void *blk, unsigned int rsr);
+       int (*dai_srt_set_drat)(void *blk, unsigned int drat);
+       int (*dai_srt_set_ec)(void *blk, unsigned int ec);
+       int (*dai_srt_set_et)(void *blk, unsigned int et);
+       int (*dai_commit_write)(struct hw *hw, unsigned int idx, void *blk);
+       int (*dao_get_ctrl_blk)(void **rblk);
+       int (*dao_put_ctrl_blk)(void *blk);
+       int (*dao_set_spos)(void *blk, unsigned int spos);
+       int (*dao_commit_write)(struct hw *hw, unsigned int idx, void *blk);
+       int (*dao_get_spos)(void *blk, unsigned int *spos);
+
+       int (*daio_mgr_get_ctrl_blk)(struct hw *hw, void **rblk);
+       int (*daio_mgr_put_ctrl_blk)(void *blk);
+       int (*daio_mgr_enb_dai)(void *blk, unsigned int idx);
+       int (*daio_mgr_dsb_dai)(void *blk, unsigned int idx);
+       int (*daio_mgr_enb_dao)(void *blk, unsigned int idx);
+       int (*daio_mgr_dsb_dao)(void *blk, unsigned int idx);
+       int (*daio_mgr_dao_init)(void *blk, unsigned int idx,
+                                               unsigned int conf);
+       int (*daio_mgr_set_imaparc)(void *blk, unsigned int slot);
+       int (*daio_mgr_set_imapnxt)(void *blk, unsigned int next);
+       int (*daio_mgr_set_imapaddr)(void *blk, unsigned int addr);
+       int (*daio_mgr_commit_write)(struct hw *hw, void *blk);
+
+       struct pci_dev *pci;    /* the pci kernel structure of this card */
+       int irq;
+       unsigned long io_base;
+       unsigned long mem_base;
+};
+
+int create_hw_obj(struct pci_dev *pci, struct hw **rhw);
+int destroy_hw_obj(struct hw *hw);
+
+unsigned int get_field(unsigned int data, unsigned int field);
+void set_field(unsigned int *data, unsigned int field, unsigned int value);
+
+#endif /* CTHARDWARE_H */
diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c
new file mode 100644 (file)
index 0000000..53572d9
--- /dev/null
@@ -0,0 +1,2230 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       cthw20k1.c
+ *
+ * @Brief
+ * This file contains the implementation of hardware access methord for 20k1.
+ *
+ * @Author     Liu Chun
+ * @Date       Jun 24 2008
+ *
+ */
+
+#include "cthw20k1.h"
+#include "ct20k1reg.h"
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/string.h>
+#include <linux/spinlock.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+
+#define CT_XFI_DMA_MASK                DMA_BIT_MASK(32) /* 32 bits */
+
+struct hw20k1 {
+       struct hw hw;
+       spinlock_t reg_20k1_lock;
+       spinlock_t reg_pci_lock;
+};
+
+static u32 hw_read_20kx(struct hw *hw, u32 reg);
+static void hw_write_20kx(struct hw *hw, u32 reg, u32 data);
+static u32 hw_read_pci(struct hw *hw, u32 reg);
+static void hw_write_pci(struct hw *hw, u32 reg, u32 data);
+
+/*
+ * Type definition block.
+ * The layout of control structures can be directly applied on 20k2 chip.
+ */
+
+/*
+ * SRC control block definitions.
+ */
+
+/* SRC resource control block */
+#define SRCCTL_STATE   0x00000007
+#define SRCCTL_BM      0x00000008
+#define SRCCTL_RSR     0x00000030
+#define SRCCTL_SF      0x000001C0
+#define SRCCTL_WR      0x00000200
+#define SRCCTL_PM      0x00000400
+#define SRCCTL_ROM     0x00001800
+#define SRCCTL_VO      0x00002000
+#define SRCCTL_ST      0x00004000
+#define SRCCTL_IE      0x00008000
+#define SRCCTL_ILSZ    0x000F0000
+#define SRCCTL_BP      0x00100000
+
+#define SRCCCR_CISZ    0x000007FF
+#define SRCCCR_CWA     0x001FF800
+#define SRCCCR_D       0x00200000
+#define SRCCCR_RS      0x01C00000
+#define SRCCCR_NAL     0x3E000000
+#define SRCCCR_RA      0xC0000000
+
+#define SRCCA_CA       0x03FFFFFF
+#define SRCCA_RS       0x1C000000
+#define SRCCA_NAL      0xE0000000
+
+#define SRCSA_SA       0x03FFFFFF
+
+#define SRCLA_LA       0x03FFFFFF
+
+/* Mixer Parameter Ring ram Low and Hight register.
+ * Fixed-point value in 8.24 format for parameter channel */
+#define MPRLH_PITCH    0xFFFFFFFF
+
+/* SRC resource register dirty flags */
+union src_dirty {
+       struct {
+               u16 ctl:1;
+               u16 ccr:1;
+               u16 sa:1;
+               u16 la:1;
+               u16 ca:1;
+               u16 mpr:1;
+               u16 czbfs:1;    /* Clear Z-Buffers */
+               u16 rsv:9;
+       } bf;
+       u16 data;
+};
+
+struct src_rsc_ctrl_blk {
+       unsigned int    ctl;
+       unsigned int    ccr;
+       unsigned int    ca;
+       unsigned int    sa;
+       unsigned int    la;
+       unsigned int    mpr;
+       union src_dirty dirty;
+};
+
+/* SRC manager control block */
+union src_mgr_dirty {
+       struct {
+               u16 enb0:1;
+               u16 enb1:1;
+               u16 enb2:1;
+               u16 enb3:1;
+               u16 enb4:1;
+               u16 enb5:1;
+               u16 enb6:1;
+               u16 enb7:1;
+               u16 enbsa:1;
+               u16 rsv:7;
+       } bf;
+       u16 data;
+};
+
+struct src_mgr_ctrl_blk {
+       unsigned int            enbsa;
+       unsigned int            enb[8];
+       union src_mgr_dirty     dirty;
+};
+
+/* SRCIMP manager control block */
+#define SRCAIM_ARC     0x00000FFF
+#define SRCAIM_NXT     0x00FF0000
+#define SRCAIM_SRC     0xFF000000
+
+struct srcimap {
+       unsigned int srcaim;
+       unsigned int idx;
+};
+
+/* SRCIMP manager register dirty flags */
+union srcimp_mgr_dirty {
+       struct {
+               u16 srcimap:1;
+               u16 rsv:15;
+       } bf;
+       u16 data;
+};
+
+struct srcimp_mgr_ctrl_blk {
+       struct srcimap          srcimap;
+       union srcimp_mgr_dirty  dirty;
+};
+
+/*
+ * Function implementation block.
+ */
+
+static int src_get_rsc_ctrl_blk(void **rblk)
+{
+       struct src_rsc_ctrl_blk *blk;
+
+       *rblk = NULL;
+       blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+       if (NULL == blk)
+               return -ENOMEM;
+
+       *rblk = blk;
+
+       return 0;
+}
+
+static int src_put_rsc_ctrl_blk(void *blk)
+{
+       kfree((struct src_rsc_ctrl_blk *)blk);
+
+       return 0;
+}
+
+static int src_set_state(void *blk, unsigned int state)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_STATE, state);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_bm(void *blk, unsigned int bm)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_BM, bm);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_rsr(void *blk, unsigned int rsr)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_RSR, rsr);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_sf(void *blk, unsigned int sf)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_SF, sf);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_wr(void *blk, unsigned int wr)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_WR, wr);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_pm(void *blk, unsigned int pm)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_PM, pm);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_rom(void *blk, unsigned int rom)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_ROM, rom);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_vo(void *blk, unsigned int vo)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_VO, vo);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_st(void *blk, unsigned int st)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_ST, st);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_ie(void *blk, unsigned int ie)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_IE, ie);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_ilsz(void *blk, unsigned int ilsz)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_ILSZ, ilsz);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_bp(void *blk, unsigned int bp)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_BP, bp);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_cisz(void *blk, unsigned int cisz)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ccr, SRCCCR_CISZ, cisz);
+       ctl->dirty.bf.ccr = 1;
+       return 0;
+}
+
+static int src_set_ca(void *blk, unsigned int ca)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ca, SRCCA_CA, ca);
+       ctl->dirty.bf.ca = 1;
+       return 0;
+}
+
+static int src_set_sa(void *blk, unsigned int sa)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->sa, SRCSA_SA, sa);
+       ctl->dirty.bf.sa = 1;
+       return 0;
+}
+
+static int src_set_la(void *blk, unsigned int la)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->la, SRCLA_LA, la);
+       ctl->dirty.bf.la = 1;
+       return 0;
+}
+
+static int src_set_pitch(void *blk, unsigned int pitch)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->mpr, MPRLH_PITCH, pitch);
+       ctl->dirty.bf.mpr = 1;
+       return 0;
+}
+
+static int src_set_clear_zbufs(void *blk, unsigned int clear)
+{
+       ((struct src_rsc_ctrl_blk *)blk)->dirty.bf.czbfs = (clear ? 1 : 0);
+       return 0;
+}
+
+static int src_set_dirty(void *blk, unsigned int flags)
+{
+       ((struct src_rsc_ctrl_blk *)blk)->dirty.data = (flags & 0xffff);
+       return 0;
+}
+
+static int src_set_dirty_all(void *blk)
+{
+       ((struct src_rsc_ctrl_blk *)blk)->dirty.data = ~(0x0);
+       return 0;
+}
+
+#define AR_SLOT_SIZE           4096
+#define AR_SLOT_BLOCK_SIZE     16
+#define AR_PTS_PITCH           6
+#define AR_PARAM_SRC_OFFSET    0x60
+
+static unsigned int src_param_pitch_mixer(unsigned int src_idx)
+{
+       return ((src_idx << 4) + AR_PTS_PITCH + AR_SLOT_SIZE
+                       - AR_PARAM_SRC_OFFSET) % AR_SLOT_SIZE;
+
+}
+
+static int src_commit_write(struct hw *hw, unsigned int idx, void *blk)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+       int i = 0;
+
+       if (ctl->dirty.bf.czbfs) {
+               /* Clear Z-Buffer registers */
+               for (i = 0; i < 8; i++)
+                       hw_write_20kx(hw, SRCUPZ+idx*0x100+i*0x4, 0);
+
+               for (i = 0; i < 4; i++)
+                       hw_write_20kx(hw, SRCDN0Z+idx*0x100+i*0x4, 0);
+
+               for (i = 0; i < 8; i++)
+                       hw_write_20kx(hw, SRCDN1Z+idx*0x100+i*0x4, 0);
+
+               ctl->dirty.bf.czbfs = 0;
+       }
+       if (ctl->dirty.bf.mpr) {
+               /* Take the parameter mixer resource in the same group as that
+                * the idx src is in for simplicity. Unlike src, all conjugate
+                * parameter mixer resources must be programmed for
+                * corresponding conjugate src resources. */
+               unsigned int pm_idx = src_param_pitch_mixer(idx);
+               hw_write_20kx(hw, PRING_LO_HI+4*pm_idx, ctl->mpr);
+               hw_write_20kx(hw, PMOPLO+8*pm_idx, 0x3);
+               hw_write_20kx(hw, PMOPHI+8*pm_idx, 0x0);
+               ctl->dirty.bf.mpr = 0;
+       }
+       if (ctl->dirty.bf.sa) {
+               hw_write_20kx(hw, SRCSA+idx*0x100, ctl->sa);
+               ctl->dirty.bf.sa = 0;
+       }
+       if (ctl->dirty.bf.la) {
+               hw_write_20kx(hw, SRCLA+idx*0x100, ctl->la);
+               ctl->dirty.bf.la = 0;
+       }
+       if (ctl->dirty.bf.ca) {
+               hw_write_20kx(hw, SRCCA+idx*0x100, ctl->ca);
+               ctl->dirty.bf.ca = 0;
+       }
+
+       /* Write srccf register */
+       hw_write_20kx(hw, SRCCF+idx*0x100, 0x0);
+
+       if (ctl->dirty.bf.ccr) {
+               hw_write_20kx(hw, SRCCCR+idx*0x100, ctl->ccr);
+               ctl->dirty.bf.ccr = 0;
+       }
+       if (ctl->dirty.bf.ctl) {
+               hw_write_20kx(hw, SRCCTL+idx*0x100, ctl->ctl);
+               ctl->dirty.bf.ctl = 0;
+       }
+
+       return 0;
+}
+
+static int src_get_ca(struct hw *hw, unsigned int idx, void *blk)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       ctl->ca = hw_read_20kx(hw, SRCCA+idx*0x100);
+       ctl->dirty.bf.ca = 0;
+
+       return get_field(ctl->ca, SRCCA_CA);
+}
+
+static unsigned int src_get_dirty(void *blk)
+{
+       return ((struct src_rsc_ctrl_blk *)blk)->dirty.data;
+}
+
+static unsigned int src_dirty_conj_mask(void)
+{
+       return 0x20;
+}
+
+static int src_mgr_enbs_src(void *blk, unsigned int idx)
+{
+       ((struct src_mgr_ctrl_blk *)blk)->enbsa = ~(0x0);
+       ((struct src_mgr_ctrl_blk *)blk)->dirty.bf.enbsa = 1;
+       ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] |= (0x1 << (idx%32));
+       return 0;
+}
+
+static int src_mgr_enb_src(void *blk, unsigned int idx)
+{
+       ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] |= (0x1 << (idx%32));
+       ((struct src_mgr_ctrl_blk *)blk)->dirty.data |= (0x1 << (idx/32));
+       return 0;
+}
+
+static int src_mgr_dsb_src(void *blk, unsigned int idx)
+{
+       ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] &= ~(0x1 << (idx%32));
+       ((struct src_mgr_ctrl_blk *)blk)->dirty.data |= (0x1 << (idx/32));
+       return 0;
+}
+
+static int src_mgr_commit_write(struct hw *hw, void *blk)
+{
+       struct src_mgr_ctrl_blk *ctl = blk;
+       int i = 0;
+       unsigned int ret = 0;
+
+       if (ctl->dirty.bf.enbsa) {
+               do {
+                       ret = hw_read_20kx(hw, SRCENBSTAT);
+               } while (ret & 0x1);
+               hw_write_20kx(hw, SRCENBS, ctl->enbsa);
+               ctl->dirty.bf.enbsa = 0;
+       }
+       for (i = 0; i < 8; i++) {
+               if ((ctl->dirty.data & (0x1 << i))) {
+                       hw_write_20kx(hw, SRCENB+(i*0x100), ctl->enb[i]);
+                       ctl->dirty.data &= ~(0x1 << i);
+               }
+       }
+
+       return 0;
+}
+
+static int src_mgr_get_ctrl_blk(void **rblk)
+{
+       struct src_mgr_ctrl_blk *blk;
+
+       *rblk = NULL;
+       blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+       if (NULL == blk)
+               return -ENOMEM;
+
+       *rblk = blk;
+
+       return 0;
+}
+
+static int src_mgr_put_ctrl_blk(void *blk)
+{
+       kfree((struct src_mgr_ctrl_blk *)blk);
+
+       return 0;
+}
+
+static int srcimp_mgr_get_ctrl_blk(void **rblk)
+{
+       struct srcimp_mgr_ctrl_blk *blk;
+
+       *rblk = NULL;
+       blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+       if (NULL == blk)
+               return -ENOMEM;
+
+       *rblk = blk;
+
+       return 0;
+}
+
+static int srcimp_mgr_put_ctrl_blk(void *blk)
+{
+       kfree((struct srcimp_mgr_ctrl_blk *)blk);
+
+       return 0;
+}
+
+static int srcimp_mgr_set_imaparc(void *blk, unsigned int slot)
+{
+       struct srcimp_mgr_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srcimap.srcaim, SRCAIM_ARC, slot);
+       ctl->dirty.bf.srcimap = 1;
+       return 0;
+}
+
+static int srcimp_mgr_set_imapuser(void *blk, unsigned int user)
+{
+       struct srcimp_mgr_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srcimap.srcaim, SRCAIM_SRC, user);
+       ctl->dirty.bf.srcimap = 1;
+       return 0;
+}
+
+static int srcimp_mgr_set_imapnxt(void *blk, unsigned int next)
+{
+       struct srcimp_mgr_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srcimap.srcaim, SRCAIM_NXT, next);
+       ctl->dirty.bf.srcimap = 1;
+       return 0;
+}
+
+static int srcimp_mgr_set_imapaddr(void *blk, unsigned int addr)
+{
+       struct srcimp_mgr_ctrl_blk *ctl = blk;
+
+       ctl->srcimap.idx = addr;
+       ctl->dirty.bf.srcimap = 1;
+       return 0;
+}
+
+static int srcimp_mgr_commit_write(struct hw *hw, void *blk)
+{
+       struct srcimp_mgr_ctrl_blk *ctl = blk;
+
+       if (ctl->dirty.bf.srcimap) {
+               hw_write_20kx(hw, SRCIMAP+ctl->srcimap.idx*0x100,
+                                               ctl->srcimap.srcaim);
+               ctl->dirty.bf.srcimap = 0;
+       }
+
+       return 0;
+}
+
+/*
+ * AMIXER control block definitions.
+ */
+
+#define AMOPLO_M       0x00000003
+#define AMOPLO_X       0x0003FFF0
+#define AMOPLO_Y       0xFFFC0000
+
+#define AMOPHI_SADR    0x000000FF
+#define AMOPHI_SE      0x80000000
+
+/* AMIXER resource register dirty flags */
+union amixer_dirty {
+       struct {
+               u16 amoplo:1;
+               u16 amophi:1;
+               u16 rsv:14;
+       } bf;
+       u16 data;
+};
+
+/* AMIXER resource control block */
+struct amixer_rsc_ctrl_blk {
+       unsigned int            amoplo;
+       unsigned int            amophi;
+       union amixer_dirty      dirty;
+};
+
+static int amixer_set_mode(void *blk, unsigned int mode)
+{
+       struct amixer_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->amoplo, AMOPLO_M, mode);
+       ctl->dirty.bf.amoplo = 1;
+       return 0;
+}
+
+static int amixer_set_iv(void *blk, unsigned int iv)
+{
+       /* 20k1 amixer does not have this field */
+       return 0;
+}
+
+static int amixer_set_x(void *blk, unsigned int x)
+{
+       struct amixer_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->amoplo, AMOPLO_X, x);
+       ctl->dirty.bf.amoplo = 1;
+       return 0;
+}
+
+static int amixer_set_y(void *blk, unsigned int y)
+{
+       struct amixer_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->amoplo, AMOPLO_Y, y);
+       ctl->dirty.bf.amoplo = 1;
+       return 0;
+}
+
+static int amixer_set_sadr(void *blk, unsigned int sadr)
+{
+       struct amixer_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->amophi, AMOPHI_SADR, sadr);
+       ctl->dirty.bf.amophi = 1;
+       return 0;
+}
+
+static int amixer_set_se(void *blk, unsigned int se)
+{
+       struct amixer_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->amophi, AMOPHI_SE, se);
+       ctl->dirty.bf.amophi = 1;
+       return 0;
+}
+
+static int amixer_set_dirty(void *blk, unsigned int flags)
+{
+       ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data = (flags & 0xffff);
+       return 0;
+}
+
+static int amixer_set_dirty_all(void *blk)
+{
+       ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data = ~(0x0);
+       return 0;
+}
+
+static int amixer_commit_write(struct hw *hw, unsigned int idx, void *blk)
+{
+       struct amixer_rsc_ctrl_blk *ctl = blk;
+
+       if (ctl->dirty.bf.amoplo || ctl->dirty.bf.amophi) {
+               hw_write_20kx(hw, AMOPLO+idx*8, ctl->amoplo);
+               ctl->dirty.bf.amoplo = 0;
+               hw_write_20kx(hw, AMOPHI+idx*8, ctl->amophi);
+               ctl->dirty.bf.amophi = 0;
+       }
+
+       return 0;
+}
+
+static int amixer_get_y(void *blk)
+{
+       struct amixer_rsc_ctrl_blk *ctl = blk;
+
+       return get_field(ctl->amoplo, AMOPLO_Y);
+}
+
+static unsigned int amixer_get_dirty(void *blk)
+{
+       return ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data;
+}
+
+static int amixer_rsc_get_ctrl_blk(void **rblk)
+{
+       struct amixer_rsc_ctrl_blk *blk;
+
+       *rblk = NULL;
+       blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+       if (NULL == blk)
+               return -ENOMEM;
+
+       *rblk = blk;
+
+       return 0;
+}
+
+static int amixer_rsc_put_ctrl_blk(void *blk)
+{
+       kfree((struct amixer_rsc_ctrl_blk *)blk);
+
+       return 0;
+}
+
+static int amixer_mgr_get_ctrl_blk(void **rblk)
+{
+       /*amixer_mgr_ctrl_blk_t *blk;*/
+
+       *rblk = NULL;
+       /*blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+       if (NULL == blk)
+               return -ENOMEM;
+
+       *rblk = blk;*/
+
+       return 0;
+}
+
+static int amixer_mgr_put_ctrl_blk(void *blk)
+{
+       /*kfree((amixer_mgr_ctrl_blk_t *)blk);*/
+
+       return 0;
+}
+
+/*
+ * DAIO control block definitions.
+ */
+
+/* Receiver Sample Rate Tracker Control register */
+#define SRTCTL_SRCR    0x000000FF
+#define SRTCTL_SRCL    0x0000FF00
+#define SRTCTL_RSR     0x00030000
+#define SRTCTL_DRAT    0x000C0000
+#define SRTCTL_RLE     0x10000000
+#define SRTCTL_RLP     0x20000000
+#define SRTCTL_EC      0x40000000
+#define SRTCTL_ET      0x80000000
+
+/* DAIO Receiver register dirty flags */
+union dai_dirty {
+       struct {
+               u16 srtctl:1;
+               u16 rsv:15;
+       } bf;
+       u16 data;
+};
+
+/* DAIO Receiver control block */
+struct dai_ctrl_blk {
+       unsigned int    srtctl;
+       union dai_dirty dirty;
+};
+
+/* S/PDIF Transmitter register dirty flags */
+union dao_dirty {
+       struct {
+               u16 spos:1;
+               u16 rsv:15;
+       } bf;
+       u16 data;
+};
+
+/* S/PDIF Transmitter control block */
+struct dao_ctrl_blk {
+       unsigned int    spos; /* S/PDIF Output Channel Status Register */
+       union dao_dirty dirty;
+};
+
+/* Audio Input Mapper RAM */
+#define AIM_ARC                0x00000FFF
+#define AIM_NXT                0x007F0000
+
+struct daoimap {
+       unsigned int aim;
+       unsigned int idx;
+};
+
+/* I2S Transmitter/Receiver Control register */
+#define I2SCTL_EA      0x00000004
+#define I2SCTL_EI      0x00000010
+
+/* S/PDIF Transmitter Control register */
+#define SPOCTL_OE      0x00000001
+#define SPOCTL_OS      0x0000000E
+#define SPOCTL_RIV     0x00000010
+#define SPOCTL_LIV     0x00000020
+#define SPOCTL_SR      0x000000C0
+
+/* S/PDIF Receiver Control register */
+#define SPICTL_EN      0x00000001
+#define SPICTL_I24     0x00000002
+#define SPICTL_IB      0x00000004
+#define SPICTL_SM      0x00000008
+#define SPICTL_VM      0x00000010
+
+/* DAIO manager register dirty flags */
+union daio_mgr_dirty {
+       struct {
+               u32 i2soctl:4;
+               u32 i2sictl:4;
+               u32 spoctl:4;
+               u32 spictl:4;
+               u32 daoimap:1;
+               u32 rsv:15;
+       } bf;
+       u32 data;
+};
+
+/* DAIO manager control block */
+struct daio_mgr_ctrl_blk {
+       unsigned int            i2sctl;
+       unsigned int            spoctl;
+       unsigned int            spictl;
+       struct daoimap          daoimap;
+       union daio_mgr_dirty    dirty;
+};
+
+static int dai_srt_set_srcr(void *blk, unsigned int src)
+{
+       struct dai_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srtctl, SRTCTL_SRCR, src);
+       ctl->dirty.bf.srtctl = 1;
+       return 0;
+}
+
+static int dai_srt_set_srcl(void *blk, unsigned int src)
+{
+       struct dai_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srtctl, SRTCTL_SRCL, src);
+       ctl->dirty.bf.srtctl = 1;
+       return 0;
+}
+
+static int dai_srt_set_rsr(void *blk, unsigned int rsr)
+{
+       struct dai_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srtctl, SRTCTL_RSR, rsr);
+       ctl->dirty.bf.srtctl = 1;
+       return 0;
+}
+
+static int dai_srt_set_drat(void *blk, unsigned int drat)
+{
+       struct dai_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srtctl, SRTCTL_DRAT, drat);
+       ctl->dirty.bf.srtctl = 1;
+       return 0;
+}
+
+static int dai_srt_set_ec(void *blk, unsigned int ec)
+{
+       struct dai_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srtctl, SRTCTL_EC, ec ? 1 : 0);
+       ctl->dirty.bf.srtctl = 1;
+       return 0;
+}
+
+static int dai_srt_set_et(void *blk, unsigned int et)
+{
+       struct dai_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srtctl, SRTCTL_ET, et ? 1 : 0);
+       ctl->dirty.bf.srtctl = 1;
+       return 0;
+}
+
+static int dai_commit_write(struct hw *hw, unsigned int idx, void *blk)
+{
+       struct dai_ctrl_blk *ctl = blk;
+
+       if (ctl->dirty.bf.srtctl) {
+               if (idx < 4) {
+                       /* S/PDIF SRTs */
+                       hw_write_20kx(hw, SRTSCTL+0x4*idx, ctl->srtctl);
+               } else {
+                       /* I2S SRT */
+                       hw_write_20kx(hw, SRTICTL, ctl->srtctl);
+               }
+               ctl->dirty.bf.srtctl = 0;
+       }
+
+       return 0;
+}
+
+static int dai_get_ctrl_blk(void **rblk)
+{
+       struct dai_ctrl_blk *blk;
+
+       *rblk = NULL;
+       blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+       if (NULL == blk)
+               return -ENOMEM;
+
+       *rblk = blk;
+
+       return 0;
+}
+
+static int dai_put_ctrl_blk(void *blk)
+{
+       kfree((struct dai_ctrl_blk *)blk);
+
+       return 0;
+}
+
+static int dao_set_spos(void *blk, unsigned int spos)
+{
+       ((struct dao_ctrl_blk *)blk)->spos = spos;
+       ((struct dao_ctrl_blk *)blk)->dirty.bf.spos = 1;
+       return 0;
+}
+
+static int dao_commit_write(struct hw *hw, unsigned int idx, void *blk)
+{
+       struct dao_ctrl_blk *ctl = blk;
+
+       if (ctl->dirty.bf.spos) {
+               if (idx < 4) {
+                       /* S/PDIF SPOSx */
+                       hw_write_20kx(hw, SPOS+0x4*idx, ctl->spos);
+               }
+               ctl->dirty.bf.spos = 0;
+       }
+
+       return 0;
+}
+
+static int dao_get_spos(void *blk, unsigned int *spos)
+{
+       *spos = ((struct dao_ctrl_blk *)blk)->spos;
+       return 0;
+}
+
+static int dao_get_ctrl_blk(void **rblk)
+{
+       struct dao_ctrl_blk *blk;
+
+       *rblk = NULL;
+       blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+       if (NULL == blk)
+               return -ENOMEM;
+
+       *rblk = blk;
+
+       return 0;
+}
+
+static int dao_put_ctrl_blk(void *blk)
+{
+       kfree((struct dao_ctrl_blk *)blk);
+
+       return 0;
+}
+
+static int daio_mgr_enb_dai(void *blk, unsigned int idx)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+
+       if (idx < 4) {
+               /* S/PDIF input */
+               set_field(&ctl->spictl, SPICTL_EN << (idx*8), 1);
+               ctl->dirty.bf.spictl |= (0x1 << idx);
+       } else {
+               /* I2S input */
+               idx %= 4;
+               set_field(&ctl->i2sctl, I2SCTL_EI << (idx*8), 1);
+               ctl->dirty.bf.i2sictl |= (0x1 << idx);
+       }
+       return 0;
+}
+
+static int daio_mgr_dsb_dai(void *blk, unsigned int idx)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+
+       if (idx < 4) {
+               /* S/PDIF input */
+               set_field(&ctl->spictl, SPICTL_EN << (idx*8), 0);
+               ctl->dirty.bf.spictl |= (0x1 << idx);
+       } else {
+               /* I2S input */
+               idx %= 4;
+               set_field(&ctl->i2sctl, I2SCTL_EI << (idx*8), 0);
+               ctl->dirty.bf.i2sictl |= (0x1 << idx);
+       }
+       return 0;
+}
+
+static int daio_mgr_enb_dao(void *blk, unsigned int idx)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+
+       if (idx < 4) {
+               /* S/PDIF output */
+               set_field(&ctl->spoctl, SPOCTL_OE << (idx*8), 1);
+               ctl->dirty.bf.spoctl |= (0x1 << idx);
+       } else {
+               /* I2S output */
+               idx %= 4;
+               set_field(&ctl->i2sctl, I2SCTL_EA << (idx*8), 1);
+               ctl->dirty.bf.i2soctl |= (0x1 << idx);
+       }
+       return 0;
+}
+
+static int daio_mgr_dsb_dao(void *blk, unsigned int idx)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+
+       if (idx < 4) {
+               /* S/PDIF output */
+               set_field(&ctl->spoctl, SPOCTL_OE << (idx*8), 0);
+               ctl->dirty.bf.spoctl |= (0x1 << idx);
+       } else {
+               /* I2S output */
+               idx %= 4;
+               set_field(&ctl->i2sctl, I2SCTL_EA << (idx*8), 0);
+               ctl->dirty.bf.i2soctl |= (0x1 << idx);
+       }
+       return 0;
+}
+
+static int daio_mgr_dao_init(void *blk, unsigned int idx, unsigned int conf)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+
+       if (idx < 4) {
+               /* S/PDIF output */
+               switch ((conf & 0x7)) {
+               case 0:
+                       set_field(&ctl->spoctl, SPOCTL_SR << (idx*8), 3);
+                       break; /* CDIF */
+               case 1:
+                       set_field(&ctl->spoctl, SPOCTL_SR << (idx*8), 0);
+                       break;
+               case 2:
+                       set_field(&ctl->spoctl, SPOCTL_SR << (idx*8), 1);
+                       break;
+               case 4:
+                       set_field(&ctl->spoctl, SPOCTL_SR << (idx*8), 2);
+                       break;
+               default:
+                       break;
+               }
+               set_field(&ctl->spoctl, SPOCTL_LIV << (idx*8),
+                         (conf >> 4) & 0x1); /* Non-audio */
+               set_field(&ctl->spoctl, SPOCTL_RIV << (idx*8),
+                         (conf >> 4) & 0x1); /* Non-audio */
+               set_field(&ctl->spoctl, SPOCTL_OS << (idx*8),
+                         ((conf >> 3) & 0x1) ? 2 : 2); /* Raw */
+
+               ctl->dirty.bf.spoctl |= (0x1 << idx);
+       } else {
+               /* I2S output */
+               /*idx %= 4; */
+       }
+       return 0;
+}
+
+static int daio_mgr_set_imaparc(void *blk, unsigned int slot)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->daoimap.aim, AIM_ARC, slot);
+       ctl->dirty.bf.daoimap = 1;
+       return 0;
+}
+
+static int daio_mgr_set_imapnxt(void *blk, unsigned int next)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->daoimap.aim, AIM_NXT, next);
+       ctl->dirty.bf.daoimap = 1;
+       return 0;
+}
+
+static int daio_mgr_set_imapaddr(void *blk, unsigned int addr)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+
+       ctl->daoimap.idx = addr;
+       ctl->dirty.bf.daoimap = 1;
+       return 0;
+}
+
+static int daio_mgr_commit_write(struct hw *hw, void *blk)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+       int i = 0;
+
+       if (ctl->dirty.bf.i2sictl || ctl->dirty.bf.i2soctl) {
+               for (i = 0; i < 4; i++) {
+                       if ((ctl->dirty.bf.i2sictl & (0x1 << i)))
+                               ctl->dirty.bf.i2sictl &= ~(0x1 << i);
+
+                       if ((ctl->dirty.bf.i2soctl & (0x1 << i)))
+                               ctl->dirty.bf.i2soctl &= ~(0x1 << i);
+               }
+               hw_write_20kx(hw, I2SCTL, ctl->i2sctl);
+               mdelay(1);
+       }
+       if (ctl->dirty.bf.spoctl) {
+               for (i = 0; i < 4; i++) {
+                       if ((ctl->dirty.bf.spoctl & (0x1 << i)))
+                               ctl->dirty.bf.spoctl &= ~(0x1 << i);
+               }
+               hw_write_20kx(hw, SPOCTL, ctl->spoctl);
+               mdelay(1);
+       }
+       if (ctl->dirty.bf.spictl) {
+               for (i = 0; i < 4; i++) {
+                       if ((ctl->dirty.bf.spictl & (0x1 << i)))
+                               ctl->dirty.bf.spictl &= ~(0x1 << i);
+               }
+               hw_write_20kx(hw, SPICTL, ctl->spictl);
+               mdelay(1);
+       }
+       if (ctl->dirty.bf.daoimap) {
+               hw_write_20kx(hw, DAOIMAP+ctl->daoimap.idx*4,
+                                       ctl->daoimap.aim);
+               ctl->dirty.bf.daoimap = 0;
+       }
+
+       return 0;
+}
+
+static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk)
+{
+       struct daio_mgr_ctrl_blk *blk;
+
+       *rblk = NULL;
+       blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+       if (NULL == blk)
+               return -ENOMEM;
+
+       blk->i2sctl = hw_read_20kx(hw, I2SCTL);
+       blk->spoctl = hw_read_20kx(hw, SPOCTL);
+       blk->spictl = hw_read_20kx(hw, SPICTL);
+
+       *rblk = blk;
+
+       return 0;
+}
+
+static int daio_mgr_put_ctrl_blk(void *blk)
+{
+       kfree((struct daio_mgr_ctrl_blk *)blk);
+
+       return 0;
+}
+
+/* Card hardware initialization block */
+struct dac_conf {
+       unsigned int msr; /* master sample rate in rsrs */
+};
+
+struct adc_conf {
+       unsigned int msr;       /* master sample rate in rsrs */
+       unsigned char input;    /* the input source of ADC */
+       unsigned char mic20db;  /* boost mic by 20db if input is microphone */
+};
+
+struct daio_conf {
+       unsigned int msr; /* master sample rate in rsrs */
+};
+
+struct trn_conf {
+       unsigned long vm_pgt_phys;
+};
+
+static int hw_daio_init(struct hw *hw, const struct daio_conf *info)
+{
+       u32 i2sorg = 0;
+       u32 spdorg = 0;
+
+       /* Read I2S CTL.  Keep original value. */
+       /*i2sorg = hw_read_20kx(hw, I2SCTL);*/
+       i2sorg = 0x94040404; /* enable all audio out and I2S-D input */
+       /* Program I2S with proper master sample rate and enable
+        * the correct I2S channel. */
+       i2sorg &= 0xfffffffc;
+
+       /* Enable S/PDIF-out-A in fixed 24-bit data
+        * format and default to 48kHz. */
+       /* Disable all before doing any changes. */
+       hw_write_20kx(hw, SPOCTL, 0x0);
+       spdorg = 0x05;
+
+       switch (info->msr) {
+       case 1:
+               i2sorg |= 1;
+               spdorg |= (0x0 << 6);
+               break;
+       case 2:
+               i2sorg |= 2;
+               spdorg |= (0x1 << 6);
+               break;
+       case 4:
+               i2sorg |= 3;
+               spdorg |= (0x2 << 6);
+               break;
+       default:
+               i2sorg |= 1;
+               break;
+       }
+
+       hw_write_20kx(hw, I2SCTL, i2sorg);
+       hw_write_20kx(hw, SPOCTL, spdorg);
+
+       /* Enable S/PDIF-in-A in fixed 24-bit data format. */
+       /* Disable all before doing any changes. */
+       hw_write_20kx(hw, SPICTL, 0x0);
+       mdelay(1);
+       spdorg = 0x0a0a0a0a;
+       hw_write_20kx(hw, SPICTL, spdorg);
+       mdelay(1);
+
+       return 0;
+}
+
+/* TRANSPORT operations */
+static int hw_trn_init(struct hw *hw, const struct trn_conf *info)
+{
+       u32 trnctl = 0;
+       unsigned long ptp_phys_low = 0, ptp_phys_high = 0;
+
+       /* Set up device page table */
+       if ((~0UL) == info->vm_pgt_phys) {
+               printk(KERN_ERR "Wrong device page table page address!\n");
+               return -1;
+       }
+
+       trnctl = 0x13;  /* 32-bit, 4k-size page */
+#if BITS_PER_LONG == 64
+       ptp_phys_low = info->vm_pgt_phys & ((1UL<<32)-1);
+       ptp_phys_high = (info->vm_pgt_phys>>32) & ((1UL<<32)-1);
+       trnctl |= (1<<2);
+#elif BITS_PER_LONG == 32
+       ptp_phys_low = info->vm_pgt_phys & (~0UL);
+       ptp_phys_high = 0;
+#else
+#      error "Unknown BITS_PER_LONG!"
+#endif
+#if PAGE_SIZE == 8192
+       trnctl |= (1<<5);
+#endif
+       hw_write_20kx(hw, PTPALX, ptp_phys_low);
+       hw_write_20kx(hw, PTPAHX, ptp_phys_high);
+       hw_write_20kx(hw, TRNCTL, trnctl);
+       hw_write_20kx(hw, TRNIS, 0x200c01); /* realy needed? */
+
+       return 0;
+}
+
+/* Card initialization */
+#define GCTL_EAC       0x00000001
+#define GCTL_EAI       0x00000002
+#define GCTL_BEP       0x00000004
+#define GCTL_BES       0x00000008
+#define GCTL_DSP       0x00000010
+#define GCTL_DBP       0x00000020
+#define GCTL_ABP       0x00000040
+#define GCTL_TBP       0x00000080
+#define GCTL_SBP       0x00000100
+#define GCTL_FBP       0x00000200
+#define GCTL_XA                0x00000400
+#define GCTL_ET                0x00000800
+#define GCTL_PR                0x00001000
+#define GCTL_MRL       0x00002000
+#define GCTL_SDE       0x00004000
+#define GCTL_SDI       0x00008000
+#define GCTL_SM                0x00010000
+#define GCTL_SR                0x00020000
+#define GCTL_SD                0x00040000
+#define GCTL_SE                0x00080000
+#define GCTL_AID       0x00100000
+
+static int hw_pll_init(struct hw *hw, unsigned int rsr)
+{
+       unsigned int pllctl;
+       int i = 0;
+
+       pllctl = (48000 == rsr) ? 0x1480a001 : 0x1480a731;
+       for (i = 0; i < 3; i++) {
+               if (hw_read_20kx(hw, PLLCTL) == pllctl)
+                       break;
+
+               hw_write_20kx(hw, PLLCTL, pllctl);
+               mdelay(40);
+       }
+       if (i >= 3) {
+               printk(KERN_ALERT "PLL initialization failed!!!\n");
+               return -EBUSY;
+       }
+
+       return 0;
+}
+
+static int hw_auto_init(struct hw *hw)
+{
+       unsigned int gctl;
+       int i;
+
+       gctl = hw_read_20kx(hw, GCTL);
+       set_field(&gctl, GCTL_EAI, 0);
+       hw_write_20kx(hw, GCTL, gctl);
+       set_field(&gctl, GCTL_EAI, 1);
+       hw_write_20kx(hw, GCTL, gctl);
+       mdelay(10);
+       for (i = 0; i < 400000; i++) {
+               gctl = hw_read_20kx(hw, GCTL);
+               if (get_field(gctl, GCTL_AID))
+                       break;
+       }
+       if (!get_field(gctl, GCTL_AID)) {
+               printk(KERN_ALERT "Card Auto-init failed!!!\n");
+               return -EBUSY;
+       }
+
+       return 0;
+}
+
+static int i2c_unlock(struct hw *hw)
+{
+       if ((hw_read_pci(hw, 0xcc) & 0xff) == 0xaa)
+               return 0;
+
+       hw_write_pci(hw, 0xcc, 0x8c);
+       hw_write_pci(hw, 0xcc, 0x0e);
+       if ((hw_read_pci(hw, 0xcc) & 0xff) == 0xaa)
+               return 0;
+
+       hw_write_pci(hw, 0xcc, 0xee);
+       hw_write_pci(hw, 0xcc, 0xaa);
+       if ((hw_read_pci(hw, 0xcc) & 0xff) == 0xaa)
+               return 0;
+
+       return -1;
+}
+
+static void i2c_lock(struct hw *hw)
+{
+       if ((hw_read_pci(hw, 0xcc) & 0xff) == 0xaa)
+               hw_write_pci(hw, 0xcc, 0x00);
+}
+
+static void i2c_write(struct hw *hw, u32 device, u32 addr, u32 data)
+{
+       unsigned int ret = 0;
+
+       do {
+               ret = hw_read_pci(hw, 0xEC);
+       } while (!(ret & 0x800000));
+       hw_write_pci(hw, 0xE0, device);
+       hw_write_pci(hw, 0xE4, (data << 8) | (addr & 0xff));
+}
+
+/* DAC operations */
+
+static int hw_reset_dac(struct hw *hw)
+{
+       u32 i = 0;
+       u16 gpioorg = 0;
+       unsigned int ret = 0;
+
+       if (i2c_unlock(hw))
+               return -1;
+
+       do {
+               ret = hw_read_pci(hw, 0xEC);
+       } while (!(ret & 0x800000));
+       hw_write_pci(hw, 0xEC, 0x05);  /* write to i2c status control */
+
+       /* To be effective, need to reset the DAC twice. */
+       for (i = 0; i < 2;  i++) {
+               /* set gpio */
+               mdelay(100);
+               gpioorg = (u16)hw_read_20kx(hw, GPIO);
+               gpioorg &= 0xfffd;
+               hw_write_20kx(hw, GPIO, gpioorg);
+               mdelay(1);
+               hw_write_20kx(hw, GPIO, gpioorg | 0x2);
+       }
+
+       i2c_write(hw, 0x00180080, 0x01, 0x80);
+       i2c_write(hw, 0x00180080, 0x02, 0x10);
+
+       i2c_lock(hw);
+
+       return 0;
+}
+
+static int hw_dac_init(struct hw *hw, const struct dac_conf *info)
+{
+       u32 data = 0;
+       u16 gpioorg = 0;
+       u16 subsys_id = 0;
+       unsigned int ret = 0;
+
+       pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id);
+       if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) {
+               /* SB055x, unmute outputs */
+               gpioorg = (u16)hw_read_20kx(hw, GPIO);
+               gpioorg &= 0xffbf;      /* set GPIO6 to low */
+               gpioorg |= 2;           /* set GPIO1 to high */
+               hw_write_20kx(hw, GPIO, gpioorg);
+               return 0;
+       }
+
+       /* mute outputs */
+       gpioorg = (u16)hw_read_20kx(hw, GPIO);
+       gpioorg &= 0xffbf;
+       hw_write_20kx(hw, GPIO, gpioorg);
+
+       hw_reset_dac(hw);
+
+       if (i2c_unlock(hw))
+               return -1;
+
+       hw_write_pci(hw, 0xEC, 0x05);  /* write to i2c status control */
+       do {
+               ret = hw_read_pci(hw, 0xEC);
+       } while (!(ret & 0x800000));
+
+       switch (info->msr) {
+       case 1:
+               data = 0x24;
+               break;
+       case 2:
+               data = 0x25;
+               break;
+       case 4:
+               data = 0x26;
+               break;
+       default:
+               data = 0x24;
+               break;
+       }
+
+       i2c_write(hw, 0x00180080, 0x06, data);
+       i2c_write(hw, 0x00180080, 0x09, data);
+       i2c_write(hw, 0x00180080, 0x0c, data);
+       i2c_write(hw, 0x00180080, 0x0f, data);
+
+       i2c_lock(hw);
+
+       /* unmute outputs */
+       gpioorg = (u16)hw_read_20kx(hw, GPIO);
+       gpioorg = gpioorg | 0x40;
+       hw_write_20kx(hw, GPIO, gpioorg);
+
+       return 0;
+}
+
+/* ADC operations */
+
+static int is_adc_input_selected_SB055x(struct hw *hw, enum ADCSRC type)
+{
+       u32 data = 0;
+       return data;
+}
+
+static int is_adc_input_selected_SBx(struct hw *hw, enum ADCSRC type)
+{
+       u32 data = 0;
+
+       data = hw_read_20kx(hw, GPIO);
+       switch (type) {
+       case ADC_MICIN:
+               data = ((data & (0x1<<7)) && (data & (0x1<<8)));
+               break;
+       case ADC_LINEIN:
+               data = (!(data & (0x1<<7)) && (data & (0x1<<8)));
+               break;
+       case ADC_NONE: /* Digital I/O */
+               data = (!(data & (0x1<<8)));
+               break;
+       default:
+               data = 0;
+       }
+       return data;
+}
+
+static int is_adc_input_selected_hendrix(struct hw *hw, enum ADCSRC type)
+{
+       u32 data = 0;
+
+       data = hw_read_20kx(hw, GPIO);
+       switch (type) {
+       case ADC_MICIN:
+               data = (data & (0x1 << 7)) ? 1 : 0;
+               break;
+       case ADC_LINEIN:
+               data = (data & (0x1 << 7)) ? 0 : 1;
+               break;
+       default:
+               data = 0;
+       }
+       return data;
+}
+
+static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type)
+{
+       u16 subsys_id = 0;
+
+       pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id);
+       if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) {
+               /* SB055x cards */
+               return is_adc_input_selected_SB055x(hw, type);
+       } else if ((subsys_id == 0x0029) || (subsys_id == 0x0031)) {
+               /* SB073x cards */
+               return is_adc_input_selected_hendrix(hw, type);
+       } else if ((subsys_id & 0xf000) == 0x6000) {
+               /* Vista compatible cards */
+               return is_adc_input_selected_hendrix(hw, type);
+       } else {
+               return is_adc_input_selected_SBx(hw, type);
+       }
+}
+
+static int
+adc_input_select_SB055x(struct hw *hw, enum ADCSRC type, unsigned char boost)
+{
+       u32 data = 0;
+
+       /*
+        * check and set the following GPIO bits accordingly
+        * ADC_Gain             = GPIO2
+        * DRM_off              = GPIO3
+        * Mic_Pwr_on           = GPIO7
+        * Digital_IO_Sel       = GPIO8
+        * Mic_Sw               = GPIO9
+        * Aux/MicLine_Sw       = GPIO12
+        */
+       data = hw_read_20kx(hw, GPIO);
+       data &= 0xec73;
+       switch (type) {
+       case ADC_MICIN:
+               data |= (0x1<<7) | (0x1<<8) | (0x1<<9) ;
+               data |= boost ? (0x1<<2) : 0;
+               break;
+       case ADC_LINEIN:
+               data |= (0x1<<8);
+               break;
+       case ADC_AUX:
+               data |= (0x1<<8) | (0x1<<12);
+               break;
+       case ADC_NONE:
+               data |= (0x1<<12);  /* set to digital */
+               break;
+       default:
+               return -1;
+       }
+
+       hw_write_20kx(hw, GPIO, data);
+
+       return 0;
+}
+
+
+static int
+adc_input_select_SBx(struct hw *hw, enum ADCSRC type, unsigned char boost)
+{
+       u32 data = 0;
+       u32 i2c_data = 0;
+       unsigned int ret = 0;
+
+       if (i2c_unlock(hw))
+               return -1;
+
+       do {
+               ret = hw_read_pci(hw, 0xEC);
+       } while (!(ret & 0x800000)); /* i2c ready poll */
+       /* set i2c access mode as Direct Control */
+       hw_write_pci(hw, 0xEC, 0x05);
+
+       data = hw_read_20kx(hw, GPIO);
+       switch (type) {
+       case ADC_MICIN:
+               data |= ((0x1 << 7) | (0x1 << 8));
+               i2c_data = 0x1;  /* Mic-in */
+               break;
+       case ADC_LINEIN:
+               data &= ~(0x1 << 7);
+               data |= (0x1 << 8);
+               i2c_data = 0x2; /* Line-in */
+               break;
+       case ADC_NONE:
+               data &= ~(0x1 << 8);
+               i2c_data = 0x0; /* set to Digital */
+               break;
+       default:
+               i2c_lock(hw);
+               return -1;
+       }
+       hw_write_20kx(hw, GPIO, data);
+       i2c_write(hw, 0x001a0080, 0x2a, i2c_data);
+       if (boost) {
+               i2c_write(hw, 0x001a0080, 0x1c, 0xe7); /* +12dB boost */
+               i2c_write(hw, 0x001a0080, 0x1e, 0xe7); /* +12dB boost */
+       } else {
+               i2c_write(hw, 0x001a0080, 0x1c, 0xcf); /* No boost */
+               i2c_write(hw, 0x001a0080, 0x1e, 0xcf); /* No boost */
+       }
+
+       i2c_lock(hw);
+
+       return 0;
+}
+
+static int
+adc_input_select_hendrix(struct hw *hw, enum ADCSRC type, unsigned char boost)
+{
+       u32 data = 0;
+       u32 i2c_data = 0;
+       unsigned int ret = 0;
+
+       if (i2c_unlock(hw))
+               return -1;
+
+       do {
+               ret = hw_read_pci(hw, 0xEC);
+       } while (!(ret & 0x800000)); /* i2c ready poll */
+       /* set i2c access mode as Direct Control */
+       hw_write_pci(hw, 0xEC, 0x05);
+
+       data = hw_read_20kx(hw, GPIO);
+       switch (type) {
+       case ADC_MICIN:
+               data |= (0x1 << 7);
+               i2c_data = 0x1;  /* Mic-in */
+               break;
+       case ADC_LINEIN:
+               data &= ~(0x1 << 7);
+               i2c_data = 0x2; /* Line-in */
+               break;
+       default:
+               i2c_lock(hw);
+               return -1;
+       }
+       hw_write_20kx(hw, GPIO, data);
+       i2c_write(hw, 0x001a0080, 0x2a, i2c_data);
+       if (boost) {
+               i2c_write(hw, 0x001a0080, 0x1c, 0xe7); /* +12dB boost */
+               i2c_write(hw, 0x001a0080, 0x1e, 0xe7); /* +12dB boost */
+       } else {
+               i2c_write(hw, 0x001a0080, 0x1c, 0xcf); /* No boost */
+               i2c_write(hw, 0x001a0080, 0x1e, 0xcf); /* No boost */
+       }
+
+       i2c_lock(hw);
+
+       return 0;
+}
+
+static int hw_adc_input_select(struct hw *hw, enum ADCSRC type)
+{
+       u16 subsys_id = 0;
+
+       pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id);
+       if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) {
+               /* SB055x cards */
+               return adc_input_select_SB055x(hw, type, (ADC_MICIN == type));
+       } else if ((subsys_id == 0x0029) || (subsys_id == 0x0031)) {
+               /* SB073x cards */
+               return adc_input_select_hendrix(hw, type, (ADC_MICIN == type));
+       } else if ((subsys_id & 0xf000) == 0x6000) {
+               /* Vista compatible cards */
+               return adc_input_select_hendrix(hw, type, (ADC_MICIN == type));
+       } else {
+               return adc_input_select_SBx(hw, type, (ADC_MICIN == type));
+       }
+}
+
+static int adc_init_SB055x(struct hw *hw, int input, int mic20db)
+{
+       return adc_input_select_SB055x(hw, input, mic20db);
+}
+
+static int adc_init_SBx(struct hw *hw, int input, int mic20db)
+{
+       u16 gpioorg;
+       u16 input_source;
+       u32 adcdata = 0;
+       unsigned int ret = 0;
+
+       input_source = 0x100;  /* default to analog */
+       switch (input) {
+       case ADC_MICIN:
+               adcdata = 0x1;
+               input_source = 0x180;  /* set GPIO7 to select Mic */
+               break;
+       case ADC_LINEIN:
+               adcdata = 0x2;
+               break;
+       case ADC_VIDEO:
+               adcdata = 0x4;
+               break;
+       case ADC_AUX:
+               adcdata = 0x8;
+               break;
+       case ADC_NONE:
+               adcdata = 0x0;
+               input_source = 0x0;  /* set to Digital */
+               break;
+       default:
+               break;
+       }
+
+       if (i2c_unlock(hw))
+               return -1;
+
+       do {
+               ret = hw_read_pci(hw, 0xEC);
+       } while (!(ret & 0x800000)); /* i2c ready poll */
+       hw_write_pci(hw, 0xEC, 0x05);  /* write to i2c status control */
+
+       i2c_write(hw, 0x001a0080, 0x0e, 0x08);
+       i2c_write(hw, 0x001a0080, 0x18, 0x0a);
+       i2c_write(hw, 0x001a0080, 0x28, 0x86);
+       i2c_write(hw, 0x001a0080, 0x2a, adcdata);
+
+       if (mic20db) {
+               i2c_write(hw, 0x001a0080, 0x1c, 0xf7);
+               i2c_write(hw, 0x001a0080, 0x1e, 0xf7);
+       } else {
+               i2c_write(hw, 0x001a0080, 0x1c, 0xcf);
+               i2c_write(hw, 0x001a0080, 0x1e, 0xcf);
+       }
+
+       if (!(hw_read_20kx(hw, ID0) & 0x100))
+               i2c_write(hw, 0x001a0080, 0x16, 0x26);
+
+       i2c_lock(hw);
+
+       gpioorg = (u16)hw_read_20kx(hw,  GPIO);
+       gpioorg &= 0xfe7f;
+       gpioorg |= input_source;
+       hw_write_20kx(hw, GPIO, gpioorg);
+
+       return 0;
+}
+
+static int hw_adc_init(struct hw *hw, const struct adc_conf *info)
+{
+       int err = 0;
+       u16 subsys_id = 0;
+
+       pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id);
+       if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) {
+               /* Sb055x card */
+               err = adc_init_SB055x(hw, info->input, info->mic20db);
+       } else {
+               err = adc_init_SBx(hw, info->input, info->mic20db);
+       }
+
+       return err;
+}
+
+static int hw_have_digit_io_switch(struct hw *hw)
+{
+       u16 subsys_id = 0;
+
+       pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id);
+       /* SB073x and Vista compatible cards have no digit IO switch */
+       return !((subsys_id == 0x0029) || (subsys_id == 0x0031)
+                               || ((subsys_id & 0xf000) == 0x6000));
+}
+
+#define UAA_CFG_PWRSTATUS      0x44
+#define UAA_CFG_SPACE_FLAG     0xA0
+#define UAA_CORE_CHANGE                0x3FFC
+static int uaa_to_xfi(struct pci_dev *pci)
+{
+       unsigned int bar0, bar1, bar2, bar3, bar4, bar5;
+       unsigned int cmd, irq, cl_size, l_timer, pwr;
+       unsigned int CTLA, CTLZ, CTLL, CTLX, CTL_, CTLF, CTLi;
+       unsigned int is_uaa = 0;
+       unsigned int data[4] = {0};
+       unsigned int io_base;
+       void *mem_base;
+       int i = 0;
+
+       /* By default, Hendrix card UAA Bar0 should be using memory... */
+       io_base = pci_resource_start(pci, 0);
+       mem_base = ioremap(io_base, pci_resource_len(pci, 0));
+       if (NULL == mem_base)
+               return -ENOENT;
+
+       CTLX = ___constant_swab32(*((unsigned int *)"CTLX"));
+       CTL_ = ___constant_swab32(*((unsigned int *)"CTL-"));
+       CTLF = ___constant_swab32(*((unsigned int *)"CTLF"));
+       CTLi = ___constant_swab32(*((unsigned int *)"CTLi"));
+       CTLA = ___constant_swab32(*((unsigned int *)"CTLA"));
+       CTLZ = ___constant_swab32(*((unsigned int *)"CTLZ"));
+       CTLL = ___constant_swab32(*((unsigned int *)"CTLL"));
+
+       /* Read current mode from Mode Change Register */
+       for (i = 0; i < 4; i++)
+               data[i] = readl(mem_base + UAA_CORE_CHANGE);
+
+       /* Determine current mode... */
+       if (data[0] == CTLA) {
+               is_uaa = ((data[1] == CTLZ && data[2] == CTLL
+                         && data[3] == CTLA) || (data[1] == CTLA
+                         && data[2] == CTLZ && data[3] == CTLL));
+       } else if (data[0] == CTLZ) {
+               is_uaa = (data[1] == CTLL
+                               && data[2] == CTLA && data[3] == CTLA);
+       } else if (data[0] == CTLL) {
+               is_uaa = (data[1] == CTLA
+                               && data[2] == CTLA && data[3] == CTLZ);
+       } else {
+               is_uaa = 0;
+       }
+
+       if (!is_uaa) {
+               /* Not in UAA mode currently. Return directly. */
+               iounmap(mem_base);
+               return 0;
+       }
+
+       pci_read_config_dword(pci, PCI_BASE_ADDRESS_0, &bar0);
+       pci_read_config_dword(pci, PCI_BASE_ADDRESS_1, &bar1);
+       pci_read_config_dword(pci, PCI_BASE_ADDRESS_2, &bar2);
+       pci_read_config_dword(pci, PCI_BASE_ADDRESS_3, &bar3);
+       pci_read_config_dword(pci, PCI_BASE_ADDRESS_4, &bar4);
+       pci_read_config_dword(pci, PCI_BASE_ADDRESS_5, &bar5);
+       pci_read_config_dword(pci, PCI_INTERRUPT_LINE, &irq);
+       pci_read_config_dword(pci, PCI_CACHE_LINE_SIZE, &cl_size);
+       pci_read_config_dword(pci, PCI_LATENCY_TIMER, &l_timer);
+       pci_read_config_dword(pci, UAA_CFG_PWRSTATUS, &pwr);
+       pci_read_config_dword(pci, PCI_COMMAND, &cmd);
+
+       /* Set up X-Fi core PCI configuration space. */
+       /* Switch to X-Fi config space with BAR0 exposed. */
+       pci_write_config_dword(pci, UAA_CFG_SPACE_FLAG, 0x87654321);
+       /* Copy UAA's BAR5 into X-Fi BAR0 */
+       pci_write_config_dword(pci, PCI_BASE_ADDRESS_0, bar5);
+       /* Switch to X-Fi config space without BAR0 exposed. */
+       pci_write_config_dword(pci, UAA_CFG_SPACE_FLAG, 0x12345678);
+       pci_write_config_dword(pci, PCI_BASE_ADDRESS_1, bar1);
+       pci_write_config_dword(pci, PCI_BASE_ADDRESS_2, bar2);
+       pci_write_config_dword(pci, PCI_BASE_ADDRESS_3, bar3);
+       pci_write_config_dword(pci, PCI_BASE_ADDRESS_4, bar4);
+       pci_write_config_dword(pci, PCI_INTERRUPT_LINE, irq);
+       pci_write_config_dword(pci, PCI_CACHE_LINE_SIZE, cl_size);
+       pci_write_config_dword(pci, PCI_LATENCY_TIMER, l_timer);
+       pci_write_config_dword(pci, UAA_CFG_PWRSTATUS, pwr);
+       pci_write_config_dword(pci, PCI_COMMAND, cmd);
+
+       /* Switch to X-Fi mode */
+       writel(CTLX, (mem_base + UAA_CORE_CHANGE));
+       writel(CTL_, (mem_base + UAA_CORE_CHANGE));
+       writel(CTLF, (mem_base + UAA_CORE_CHANGE));
+       writel(CTLi, (mem_base + UAA_CORE_CHANGE));
+
+       iounmap(mem_base);
+
+       return 0;
+}
+
+static int hw_card_start(struct hw *hw)
+{
+       int err = 0;
+       struct pci_dev *pci = hw->pci;
+       u16 subsys_id = 0;
+       unsigned int dma_mask = 0;
+
+       err = pci_enable_device(pci);
+       if (err < 0)
+               return err;
+
+       /* Set DMA transfer mask */
+       dma_mask = CT_XFI_DMA_MASK;
+       if (pci_set_dma_mask(pci, dma_mask) < 0 ||
+           pci_set_consistent_dma_mask(pci, dma_mask) < 0) {
+               printk(KERN_ERR "architecture does not support PCI "
+                               "busmaster DMA with mask 0x%x\n", dma_mask);
+               err = -ENXIO;
+               goto error1;
+       }
+
+       err = pci_request_regions(pci, "XFi");
+       if (err < 0)
+               goto error1;
+
+       /* Switch to X-Fi mode from UAA mode if neeeded */
+       pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsys_id);
+       if ((0x5 == pci->device) && (0x6000 == (subsys_id & 0x6000))) {
+               err = uaa_to_xfi(pci);
+               if (err)
+                       goto error2;
+
+               hw->io_base = pci_resource_start(pci, 5);
+       } else {
+               hw->io_base = pci_resource_start(pci, 0);
+       }
+
+       /*if ((err = request_irq(pci->irq, ct_atc_interrupt, IRQF_SHARED,
+                               atc->chip_details->nm_card, hw))) {
+               goto error2;
+       }
+       hw->irq = pci->irq;
+       */
+
+       pci_set_master(pci);
+
+       return 0;
+
+error2:
+       pci_release_regions(pci);
+       hw->io_base = 0;
+error1:
+       pci_disable_device(pci);
+       return err;
+}
+
+static int hw_card_stop(struct hw *hw)
+{
+       /* TODO: Disable interrupt and so on... */
+       return 0;
+}
+
+static int hw_card_shutdown(struct hw *hw)
+{
+       if (hw->irq >= 0)
+               free_irq(hw->irq, hw);
+
+       hw->irq = -1;
+
+       if (NULL != ((void *)hw->mem_base))
+               iounmap((void *)hw->mem_base);
+
+       hw->mem_base = (unsigned long)NULL;
+
+       if (hw->io_base)
+               pci_release_regions(hw->pci);
+
+       hw->io_base = 0;
+
+       pci_disable_device(hw->pci);
+
+       return 0;
+}
+
+static int hw_card_init(struct hw *hw, struct card_conf *info)
+{
+       int err;
+       unsigned int gctl;
+       u16 subsys_id = 0;
+       u32 data = 0;
+       struct dac_conf dac_info = {0};
+       struct adc_conf adc_info = {0};
+       struct daio_conf daio_info = {0};
+       struct trn_conf trn_info = {0};
+
+       /* Get PCI io port base address and do Hendrix switch if needed. */
+       if (!hw->io_base) {
+               err = hw_card_start(hw);
+               if (err)
+                       return err;
+       }
+
+       /* PLL init */
+       err = hw_pll_init(hw, info->rsr);
+       if (err < 0)
+               return err;
+
+       /* kick off auto-init */
+       err = hw_auto_init(hw);
+       if (err < 0)
+               return err;
+
+       /* Enable audio ring */
+       gctl = hw_read_20kx(hw, GCTL);
+       set_field(&gctl, GCTL_EAC, 1);
+       set_field(&gctl, GCTL_DBP, 1);
+       set_field(&gctl, GCTL_TBP, 1);
+       set_field(&gctl, GCTL_FBP, 1);
+       set_field(&gctl, GCTL_ET, 1);
+       hw_write_20kx(hw, GCTL, gctl);
+       mdelay(10);
+
+       /* Reset all global pending interrupts */
+       hw_write_20kx(hw, GIE, 0);
+       /* Reset all SRC pending interrupts */
+       hw_write_20kx(hw, SRCIP, 0);
+       mdelay(30);
+
+       pci_read_config_word(hw->pci, PCI_SUBSYSTEM_ID, &subsys_id);
+       /* Detect the card ID and configure GPIO accordingly. */
+       if ((subsys_id == 0x0022) || (subsys_id == 0x002F)) {
+               /* SB055x cards */
+               hw_write_20kx(hw, GPIOCTL, 0x13fe);
+       } else if ((subsys_id == 0x0029) || (subsys_id == 0x0031)) {
+               /* SB073x cards */
+               hw_write_20kx(hw, GPIOCTL, 0x00e6);
+       } else if ((subsys_id & 0xf000) == 0x6000) {
+               /* Vista compatible cards */
+               hw_write_20kx(hw, GPIOCTL, 0x00c2);
+       } else {
+               hw_write_20kx(hw, GPIOCTL, 0x01e6);
+       }
+
+       trn_info.vm_pgt_phys = info->vm_pgt_phys;
+       err = hw_trn_init(hw, &trn_info);
+       if (err < 0)
+               return err;
+
+       daio_info.msr = info->msr;
+       err = hw_daio_init(hw, &daio_info);
+       if (err < 0)
+               return err;
+
+       dac_info.msr = info->msr;
+       err = hw_dac_init(hw, &dac_info);
+       if (err < 0)
+               return err;
+
+       adc_info.msr = info->msr;
+       adc_info.input = ADC_LINEIN;
+       adc_info.mic20db = 0;
+       err = hw_adc_init(hw, &adc_info);
+       if (err < 0)
+               return err;
+
+       data = hw_read_20kx(hw, SRCMCTL);
+       data |= 0x1; /* Enables input from the audio ring */
+       hw_write_20kx(hw, SRCMCTL, data);
+
+       return 0;
+}
+
+static u32 hw_read_20kx(struct hw *hw, u32 reg)
+{
+       u32 value;
+       unsigned long flags;
+
+       spin_lock_irqsave(
+               &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags);
+       outl(reg, hw->io_base + 0x0);
+       value = inl(hw->io_base + 0x4);
+       spin_unlock_irqrestore(
+               &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags);
+
+       return value;
+}
+
+static void hw_write_20kx(struct hw *hw, u32 reg, u32 data)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(
+               &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags);
+       outl(reg, hw->io_base + 0x0);
+       outl(data, hw->io_base + 0x4);
+       spin_unlock_irqrestore(
+               &container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags);
+
+}
+
+static u32 hw_read_pci(struct hw *hw, u32 reg)
+{
+       u32 value;
+       unsigned long flags;
+
+       spin_lock_irqsave(
+               &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags);
+       outl(reg, hw->io_base + 0x10);
+       value = inl(hw->io_base + 0x14);
+       spin_unlock_irqrestore(
+               &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags);
+
+       return value;
+}
+
+static void hw_write_pci(struct hw *hw, u32 reg, u32 data)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(
+               &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags);
+       outl(reg, hw->io_base + 0x10);
+       outl(data, hw->io_base + 0x14);
+       spin_unlock_irqrestore(
+               &container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags);
+}
+
+int create_20k1_hw_obj(struct hw **rhw)
+{
+       struct hw *hw;
+       struct hw20k1 *hw20k1;
+
+       *rhw = NULL;
+       hw20k1 = kzalloc(sizeof(*hw20k1), GFP_KERNEL);
+       if (NULL == hw20k1)
+               return -ENOMEM;
+
+       spin_lock_init(&hw20k1->reg_20k1_lock);
+       spin_lock_init(&hw20k1->reg_pci_lock);
+
+       hw = &hw20k1->hw;
+
+       hw->io_base = 0;
+       hw->mem_base = (unsigned long)NULL;
+       hw->irq = -1;
+
+       hw->card_init = hw_card_init;
+       hw->card_stop = hw_card_stop;
+       hw->pll_init = hw_pll_init;
+       hw->is_adc_source_selected = hw_is_adc_input_selected;
+       hw->select_adc_source = hw_adc_input_select;
+       hw->have_digit_io_switch = hw_have_digit_io_switch;
+
+       hw->src_rsc_get_ctrl_blk = src_get_rsc_ctrl_blk;
+       hw->src_rsc_put_ctrl_blk = src_put_rsc_ctrl_blk;
+       hw->src_mgr_get_ctrl_blk = src_mgr_get_ctrl_blk;
+       hw->src_mgr_put_ctrl_blk = src_mgr_put_ctrl_blk;
+       hw->src_set_state = src_set_state;
+       hw->src_set_bm = src_set_bm;
+       hw->src_set_rsr = src_set_rsr;
+       hw->src_set_sf = src_set_sf;
+       hw->src_set_wr = src_set_wr;
+       hw->src_set_pm = src_set_pm;
+       hw->src_set_rom = src_set_rom;
+       hw->src_set_vo = src_set_vo;
+       hw->src_set_st = src_set_st;
+       hw->src_set_ie = src_set_ie;
+       hw->src_set_ilsz = src_set_ilsz;
+       hw->src_set_bp = src_set_bp;
+       hw->src_set_cisz = src_set_cisz;
+       hw->src_set_ca = src_set_ca;
+       hw->src_set_sa = src_set_sa;
+       hw->src_set_la = src_set_la;
+       hw->src_set_pitch = src_set_pitch;
+       hw->src_set_dirty = src_set_dirty;
+       hw->src_set_clear_zbufs = src_set_clear_zbufs;
+       hw->src_set_dirty_all = src_set_dirty_all;
+       hw->src_commit_write = src_commit_write;
+       hw->src_get_ca = src_get_ca;
+       hw->src_get_dirty = src_get_dirty;
+       hw->src_dirty_conj_mask = src_dirty_conj_mask;
+       hw->src_mgr_enbs_src = src_mgr_enbs_src;
+       hw->src_mgr_enb_src = src_mgr_enb_src;
+       hw->src_mgr_dsb_src = src_mgr_dsb_src;
+       hw->src_mgr_commit_write = src_mgr_commit_write;
+
+       hw->srcimp_mgr_get_ctrl_blk = srcimp_mgr_get_ctrl_blk;
+       hw->srcimp_mgr_put_ctrl_blk = srcimp_mgr_put_ctrl_blk;
+       hw->srcimp_mgr_set_imaparc = srcimp_mgr_set_imaparc;
+       hw->srcimp_mgr_set_imapuser = srcimp_mgr_set_imapuser;
+       hw->srcimp_mgr_set_imapnxt = srcimp_mgr_set_imapnxt;
+       hw->srcimp_mgr_set_imapaddr = srcimp_mgr_set_imapaddr;
+       hw->srcimp_mgr_commit_write = srcimp_mgr_commit_write;
+
+       hw->amixer_rsc_get_ctrl_blk = amixer_rsc_get_ctrl_blk;
+       hw->amixer_rsc_put_ctrl_blk = amixer_rsc_put_ctrl_blk;
+       hw->amixer_mgr_get_ctrl_blk = amixer_mgr_get_ctrl_blk;
+       hw->amixer_mgr_put_ctrl_blk = amixer_mgr_put_ctrl_blk;
+       hw->amixer_set_mode = amixer_set_mode;
+       hw->amixer_set_iv = amixer_set_iv;
+       hw->amixer_set_x = amixer_set_x;
+       hw->amixer_set_y = amixer_set_y;
+       hw->amixer_set_sadr = amixer_set_sadr;
+       hw->amixer_set_se = amixer_set_se;
+       hw->amixer_set_dirty = amixer_set_dirty;
+       hw->amixer_set_dirty_all = amixer_set_dirty_all;
+       hw->amixer_commit_write = amixer_commit_write;
+       hw->amixer_get_y = amixer_get_y;
+       hw->amixer_get_dirty = amixer_get_dirty;
+
+       hw->dai_get_ctrl_blk = dai_get_ctrl_blk;
+       hw->dai_put_ctrl_blk = dai_put_ctrl_blk;
+       hw->dai_srt_set_srco = dai_srt_set_srcr;
+       hw->dai_srt_set_srcm = dai_srt_set_srcl;
+       hw->dai_srt_set_rsr = dai_srt_set_rsr;
+       hw->dai_srt_set_drat = dai_srt_set_drat;
+       hw->dai_srt_set_ec = dai_srt_set_ec;
+       hw->dai_srt_set_et = dai_srt_set_et;
+       hw->dai_commit_write = dai_commit_write;
+
+       hw->dao_get_ctrl_blk = dao_get_ctrl_blk;
+       hw->dao_put_ctrl_blk = dao_put_ctrl_blk;
+       hw->dao_set_spos = dao_set_spos;
+       hw->dao_commit_write = dao_commit_write;
+       hw->dao_get_spos = dao_get_spos;
+
+       hw->daio_mgr_get_ctrl_blk = daio_mgr_get_ctrl_blk;
+       hw->daio_mgr_put_ctrl_blk = daio_mgr_put_ctrl_blk;
+       hw->daio_mgr_enb_dai = daio_mgr_enb_dai;
+       hw->daio_mgr_dsb_dai = daio_mgr_dsb_dai;
+       hw->daio_mgr_enb_dao = daio_mgr_enb_dao;
+       hw->daio_mgr_dsb_dao = daio_mgr_dsb_dao;
+       hw->daio_mgr_dao_init = daio_mgr_dao_init;
+       hw->daio_mgr_set_imaparc = daio_mgr_set_imaparc;
+       hw->daio_mgr_set_imapnxt = daio_mgr_set_imapnxt;
+       hw->daio_mgr_set_imapaddr = daio_mgr_set_imapaddr;
+       hw->daio_mgr_commit_write = daio_mgr_commit_write;
+
+       *rhw = hw;
+
+       return 0;
+}
+
+int destroy_20k1_hw_obj(struct hw *hw)
+{
+       if (hw->io_base)
+               hw_card_shutdown(hw);
+
+       kfree(container_of(hw, struct hw20k1, hw));
+       return 0;
+}
diff --git a/sound/pci/ctxfi/cthw20k1.h b/sound/pci/ctxfi/cthw20k1.h
new file mode 100644 (file)
index 0000000..02f72fb
--- /dev/null
@@ -0,0 +1,26 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       cthw20k1.h
+ *
+ * @Brief
+ * This file contains the definition of hardware access methord.
+ *
+ * @Author     Liu Chun
+ * @Date       May 13 2008
+ *
+ */
+
+#ifndef CTHW20K1_H
+#define CTHW20K1_H
+
+#include "cthardware.h"
+
+int create_20k1_hw_obj(struct hw **rhw);
+int destroy_20k1_hw_obj(struct hw *hw);
+
+#endif /* CTHW20K1_H */
diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c
new file mode 100644 (file)
index 0000000..cdcb75c
--- /dev/null
@@ -0,0 +1,2133 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       cthw20k2.c
+ *
+ * @Brief
+ * This file contains the implementation of hardware access methord for 20k2.
+ *
+ * @Author     Liu Chun
+ * @Date       May 14 2008
+ *
+ */
+
+#include "cthw20k2.h"
+#include "ct20k2reg.h"
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+
+#define CT_XFI_DMA_MASK                DMA_BIT_MASK(32) /* 32 bits */
+
+static u32 hw_read_20kx(struct hw *hw, u32 reg);
+static void hw_write_20kx(struct hw *hw, u32 reg, u32 data);
+
+/*
+ * Type definition block.
+ * The layout of control structures can be directly applied on 20k2 chip.
+ */
+
+/*
+ * SRC control block definitions.
+ */
+
+/* SRC resource control block */
+#define SRCCTL_STATE   0x00000007
+#define SRCCTL_BM      0x00000008
+#define SRCCTL_RSR     0x00000030
+#define SRCCTL_SF      0x000001C0
+#define SRCCTL_WR      0x00000200
+#define SRCCTL_PM      0x00000400
+#define SRCCTL_ROM     0x00001800
+#define SRCCTL_VO      0x00002000
+#define SRCCTL_ST      0x00004000
+#define SRCCTL_IE      0x00008000
+#define SRCCTL_ILSZ    0x000F0000
+#define SRCCTL_BP      0x00100000
+
+#define SRCCCR_CISZ    0x000007FF
+#define SRCCCR_CWA     0x001FF800
+#define SRCCCR_D       0x00200000
+#define SRCCCR_RS      0x01C00000
+#define SRCCCR_NAL     0x3E000000
+#define SRCCCR_RA      0xC0000000
+
+#define SRCCA_CA       0x0FFFFFFF
+#define SRCCA_RS       0xE0000000
+
+#define SRCSA_SA       0x0FFFFFFF
+
+#define SRCLA_LA       0x0FFFFFFF
+
+/* Mixer Parameter Ring ram Low and Hight register.
+ * Fixed-point value in 8.24 format for parameter channel */
+#define MPRLH_PITCH    0xFFFFFFFF
+
+/* SRC resource register dirty flags */
+union src_dirty {
+       struct {
+               u16 ctl:1;
+               u16 ccr:1;
+               u16 sa:1;
+               u16 la:1;
+               u16 ca:1;
+               u16 mpr:1;
+               u16 czbfs:1;    /* Clear Z-Buffers */
+               u16 rsv:9;
+       } bf;
+       u16 data;
+};
+
+struct src_rsc_ctrl_blk {
+       unsigned int    ctl;
+       unsigned int    ccr;
+       unsigned int    ca;
+       unsigned int    sa;
+       unsigned int    la;
+       unsigned int    mpr;
+       union src_dirty dirty;
+};
+
+/* SRC manager control block */
+union src_mgr_dirty {
+       struct {
+               u16 enb0:1;
+               u16 enb1:1;
+               u16 enb2:1;
+               u16 enb3:1;
+               u16 enb4:1;
+               u16 enb5:1;
+               u16 enb6:1;
+               u16 enb7:1;
+               u16 enbsa:1;
+               u16 rsv:7;
+       } bf;
+       u16 data;
+};
+
+struct src_mgr_ctrl_blk {
+       unsigned int            enbsa;
+       unsigned int            enb[8];
+       union src_mgr_dirty     dirty;
+};
+
+/* SRCIMP manager control block */
+#define SRCAIM_ARC     0x00000FFF
+#define SRCAIM_NXT     0x00FF0000
+#define SRCAIM_SRC     0xFF000000
+
+struct srcimap {
+       unsigned int srcaim;
+       unsigned int idx;
+};
+
+/* SRCIMP manager register dirty flags */
+union srcimp_mgr_dirty {
+       struct {
+               u16 srcimap:1;
+               u16 rsv:15;
+       } bf;
+       u16 data;
+};
+
+struct srcimp_mgr_ctrl_blk {
+       struct srcimap          srcimap;
+       union srcimp_mgr_dirty  dirty;
+};
+
+/*
+ * Function implementation block.
+ */
+
+static int src_get_rsc_ctrl_blk(void **rblk)
+{
+       struct src_rsc_ctrl_blk *blk;
+
+       *rblk = NULL;
+       blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+       if (NULL == blk)
+               return -ENOMEM;
+
+       *rblk = blk;
+
+       return 0;
+}
+
+static int src_put_rsc_ctrl_blk(void *blk)
+{
+       kfree((struct src_rsc_ctrl_blk *)blk);
+
+       return 0;
+}
+
+static int src_set_state(void *blk, unsigned int state)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_STATE, state);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_bm(void *blk, unsigned int bm)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_BM, bm);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_rsr(void *blk, unsigned int rsr)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_RSR, rsr);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_sf(void *blk, unsigned int sf)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_SF, sf);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_wr(void *blk, unsigned int wr)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_WR, wr);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_pm(void *blk, unsigned int pm)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_PM, pm);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_rom(void *blk, unsigned int rom)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_ROM, rom);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_vo(void *blk, unsigned int vo)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_VO, vo);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_st(void *blk, unsigned int st)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_ST, st);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_ie(void *blk, unsigned int ie)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_IE, ie);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_ilsz(void *blk, unsigned int ilsz)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_ILSZ, ilsz);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_bp(void *blk, unsigned int bp)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ctl, SRCCTL_BP, bp);
+       ctl->dirty.bf.ctl = 1;
+       return 0;
+}
+
+static int src_set_cisz(void *blk, unsigned int cisz)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ccr, SRCCCR_CISZ, cisz);
+       ctl->dirty.bf.ccr = 1;
+       return 0;
+}
+
+static int src_set_ca(void *blk, unsigned int ca)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->ca, SRCCA_CA, ca);
+       ctl->dirty.bf.ca = 1;
+       return 0;
+}
+
+static int src_set_sa(void *blk, unsigned int sa)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->sa, SRCSA_SA, sa);
+       ctl->dirty.bf.sa = 1;
+       return 0;
+}
+
+static int src_set_la(void *blk, unsigned int la)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->la, SRCLA_LA, la);
+       ctl->dirty.bf.la = 1;
+       return 0;
+}
+
+static int src_set_pitch(void *blk, unsigned int pitch)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->mpr, MPRLH_PITCH, pitch);
+       ctl->dirty.bf.mpr = 1;
+       return 0;
+}
+
+static int src_set_clear_zbufs(void *blk, unsigned int clear)
+{
+       ((struct src_rsc_ctrl_blk *)blk)->dirty.bf.czbfs = (clear ? 1 : 0);
+       return 0;
+}
+
+static int src_set_dirty(void *blk, unsigned int flags)
+{
+       ((struct src_rsc_ctrl_blk *)blk)->dirty.data = (flags & 0xffff);
+       return 0;
+}
+
+static int src_set_dirty_all(void *blk)
+{
+       ((struct src_rsc_ctrl_blk *)blk)->dirty.data = ~(0x0);
+       return 0;
+}
+
+#define AR_SLOT_SIZE           4096
+#define AR_SLOT_BLOCK_SIZE     16
+#define AR_PTS_PITCH           6
+#define AR_PARAM_SRC_OFFSET    0x60
+
+static unsigned int src_param_pitch_mixer(unsigned int src_idx)
+{
+       return ((src_idx << 4) + AR_PTS_PITCH + AR_SLOT_SIZE
+                       - AR_PARAM_SRC_OFFSET) % AR_SLOT_SIZE;
+
+}
+
+static int src_commit_write(struct hw *hw, unsigned int idx, void *blk)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+       int i = 0;
+
+       if (ctl->dirty.bf.czbfs) {
+               /* Clear Z-Buffer registers */
+               for (i = 0; i < 8; i++)
+                       hw_write_20kx(hw, SRC_UPZ+idx*0x100+i*0x4, 0);
+
+               for (i = 0; i < 4; i++)
+                       hw_write_20kx(hw, SRC_DN0Z+idx*0x100+i*0x4, 0);
+
+               for (i = 0; i < 8; i++)
+                       hw_write_20kx(hw, SRC_DN1Z+idx*0x100+i*0x4, 0);
+
+               ctl->dirty.bf.czbfs = 0;
+       }
+       if (ctl->dirty.bf.mpr) {
+               /* Take the parameter mixer resource in the same group as that
+                * the idx src is in for simplicity. Unlike src, all conjugate
+                * parameter mixer resources must be programmed for
+                * corresponding conjugate src resources. */
+               unsigned int pm_idx = src_param_pitch_mixer(idx);
+               hw_write_20kx(hw, MIXER_PRING_LO_HI+4*pm_idx, ctl->mpr);
+               hw_write_20kx(hw, MIXER_PMOPLO+8*pm_idx, 0x3);
+               hw_write_20kx(hw, MIXER_PMOPHI+8*pm_idx, 0x0);
+               ctl->dirty.bf.mpr = 0;
+       }
+       if (ctl->dirty.bf.sa) {
+               hw_write_20kx(hw, SRC_SA+idx*0x100, ctl->sa);
+               ctl->dirty.bf.sa = 0;
+       }
+       if (ctl->dirty.bf.la) {
+               hw_write_20kx(hw, SRC_LA+idx*0x100, ctl->la);
+               ctl->dirty.bf.la = 0;
+       }
+       if (ctl->dirty.bf.ca) {
+               hw_write_20kx(hw, SRC_CA+idx*0x100, ctl->ca);
+               ctl->dirty.bf.ca = 0;
+       }
+
+       /* Write srccf register */
+       hw_write_20kx(hw, SRC_CF+idx*0x100, 0x0);
+
+       if (ctl->dirty.bf.ccr) {
+               hw_write_20kx(hw, SRC_CCR+idx*0x100, ctl->ccr);
+               ctl->dirty.bf.ccr = 0;
+       }
+       if (ctl->dirty.bf.ctl) {
+               hw_write_20kx(hw, SRC_CTL+idx*0x100, ctl->ctl);
+               ctl->dirty.bf.ctl = 0;
+       }
+
+       return 0;
+}
+
+static int src_get_ca(struct hw *hw, unsigned int idx, void *blk)
+{
+       struct src_rsc_ctrl_blk *ctl = blk;
+
+       ctl->ca = hw_read_20kx(hw, SRC_CA+idx*0x100);
+       ctl->dirty.bf.ca = 0;
+
+       return get_field(ctl->ca, SRCCA_CA);
+}
+
+static unsigned int src_get_dirty(void *blk)
+{
+       return ((struct src_rsc_ctrl_blk *)blk)->dirty.data;
+}
+
+static unsigned int src_dirty_conj_mask(void)
+{
+       return 0x20;
+}
+
+static int src_mgr_enbs_src(void *blk, unsigned int idx)
+{
+       ((struct src_mgr_ctrl_blk *)blk)->enbsa |= (0x1 << ((idx%128)/4));
+       ((struct src_mgr_ctrl_blk *)blk)->dirty.bf.enbsa = 1;
+       ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] |= (0x1 << (idx%32));
+       return 0;
+}
+
+static int src_mgr_enb_src(void *blk, unsigned int idx)
+{
+       ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] |= (0x1 << (idx%32));
+       ((struct src_mgr_ctrl_blk *)blk)->dirty.data |= (0x1 << (idx/32));
+       return 0;
+}
+
+static int src_mgr_dsb_src(void *blk, unsigned int idx)
+{
+       ((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] &= ~(0x1 << (idx%32));
+       ((struct src_mgr_ctrl_blk *)blk)->dirty.data |= (0x1 << (idx/32));
+       return 0;
+}
+
+static int src_mgr_commit_write(struct hw *hw, void *blk)
+{
+       struct src_mgr_ctrl_blk *ctl = blk;
+       int i = 0;
+       unsigned int ret = 0;
+
+       if (ctl->dirty.bf.enbsa) {
+               do {
+                       ret = hw_read_20kx(hw, SRC_ENBSTAT);
+               } while (ret & 0x1);
+               hw_write_20kx(hw, SRC_ENBSA, ctl->enbsa);
+               ctl->dirty.bf.enbsa = 0;
+       }
+       for (i = 0; i < 8; i++) {
+               if ((ctl->dirty.data & (0x1 << i))) {
+                       hw_write_20kx(hw, SRC_ENB+(i*0x100), ctl->enb[i]);
+                       ctl->dirty.data &= ~(0x1 << i);
+               }
+       }
+
+       return 0;
+}
+
+static int src_mgr_get_ctrl_blk(void **rblk)
+{
+       struct src_mgr_ctrl_blk *blk;
+
+       *rblk = NULL;
+       blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+       if (NULL == blk)
+               return -ENOMEM;
+
+       *rblk = blk;
+
+       return 0;
+}
+
+static int src_mgr_put_ctrl_blk(void *blk)
+{
+       kfree((struct src_mgr_ctrl_blk *)blk);
+
+       return 0;
+}
+
+static int srcimp_mgr_get_ctrl_blk(void **rblk)
+{
+       struct srcimp_mgr_ctrl_blk *blk;
+
+       *rblk = NULL;
+       blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+       if (NULL == blk)
+               return -ENOMEM;
+
+       *rblk = blk;
+
+       return 0;
+}
+
+static int srcimp_mgr_put_ctrl_blk(void *blk)
+{
+       kfree((struct srcimp_mgr_ctrl_blk *)blk);
+
+       return 0;
+}
+
+static int srcimp_mgr_set_imaparc(void *blk, unsigned int slot)
+{
+       struct srcimp_mgr_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srcimap.srcaim, SRCAIM_ARC, slot);
+       ctl->dirty.bf.srcimap = 1;
+       return 0;
+}
+
+static int srcimp_mgr_set_imapuser(void *blk, unsigned int user)
+{
+       struct srcimp_mgr_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srcimap.srcaim, SRCAIM_SRC, user);
+       ctl->dirty.bf.srcimap = 1;
+       return 0;
+}
+
+static int srcimp_mgr_set_imapnxt(void *blk, unsigned int next)
+{
+       struct srcimp_mgr_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srcimap.srcaim, SRCAIM_NXT, next);
+       ctl->dirty.bf.srcimap = 1;
+       return 0;
+}
+
+static int srcimp_mgr_set_imapaddr(void *blk, unsigned int addr)
+{
+       ((struct srcimp_mgr_ctrl_blk *)blk)->srcimap.idx = addr;
+       ((struct srcimp_mgr_ctrl_blk *)blk)->dirty.bf.srcimap = 1;
+       return 0;
+}
+
+static int srcimp_mgr_commit_write(struct hw *hw, void *blk)
+{
+       struct srcimp_mgr_ctrl_blk *ctl = blk;
+
+       if (ctl->dirty.bf.srcimap) {
+               hw_write_20kx(hw, SRC_IMAP+ctl->srcimap.idx*0x100,
+                                               ctl->srcimap.srcaim);
+               ctl->dirty.bf.srcimap = 0;
+       }
+
+       return 0;
+}
+
+/*
+ * AMIXER control block definitions.
+ */
+
+#define AMOPLO_M       0x00000003
+#define AMOPLO_IV      0x00000004
+#define AMOPLO_X       0x0003FFF0
+#define AMOPLO_Y       0xFFFC0000
+
+#define AMOPHI_SADR    0x000000FF
+#define AMOPHI_SE      0x80000000
+
+/* AMIXER resource register dirty flags */
+union amixer_dirty {
+       struct {
+               u16 amoplo:1;
+               u16 amophi:1;
+               u16 rsv:14;
+       } bf;
+       u16 data;
+};
+
+/* AMIXER resource control block */
+struct amixer_rsc_ctrl_blk {
+       unsigned int            amoplo;
+       unsigned int            amophi;
+       union amixer_dirty      dirty;
+};
+
+static int amixer_set_mode(void *blk, unsigned int mode)
+{
+       struct amixer_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->amoplo, AMOPLO_M, mode);
+       ctl->dirty.bf.amoplo = 1;
+       return 0;
+}
+
+static int amixer_set_iv(void *blk, unsigned int iv)
+{
+       struct amixer_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->amoplo, AMOPLO_IV, iv);
+       ctl->dirty.bf.amoplo = 1;
+       return 0;
+}
+
+static int amixer_set_x(void *blk, unsigned int x)
+{
+       struct amixer_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->amoplo, AMOPLO_X, x);
+       ctl->dirty.bf.amoplo = 1;
+       return 0;
+}
+
+static int amixer_set_y(void *blk, unsigned int y)
+{
+       struct amixer_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->amoplo, AMOPLO_Y, y);
+       ctl->dirty.bf.amoplo = 1;
+       return 0;
+}
+
+static int amixer_set_sadr(void *blk, unsigned int sadr)
+{
+       struct amixer_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->amophi, AMOPHI_SADR, sadr);
+       ctl->dirty.bf.amophi = 1;
+       return 0;
+}
+
+static int amixer_set_se(void *blk, unsigned int se)
+{
+       struct amixer_rsc_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->amophi, AMOPHI_SE, se);
+       ctl->dirty.bf.amophi = 1;
+       return 0;
+}
+
+static int amixer_set_dirty(void *blk, unsigned int flags)
+{
+       ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data = (flags & 0xffff);
+       return 0;
+}
+
+static int amixer_set_dirty_all(void *blk)
+{
+       ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data = ~(0x0);
+       return 0;
+}
+
+static int amixer_commit_write(struct hw *hw, unsigned int idx, void *blk)
+{
+       struct amixer_rsc_ctrl_blk *ctl = blk;
+
+       if (ctl->dirty.bf.amoplo || ctl->dirty.bf.amophi) {
+               hw_write_20kx(hw, MIXER_AMOPLO+idx*8, ctl->amoplo);
+               ctl->dirty.bf.amoplo = 0;
+               hw_write_20kx(hw, MIXER_AMOPHI+idx*8, ctl->amophi);
+               ctl->dirty.bf.amophi = 0;
+       }
+
+       return 0;
+}
+
+static int amixer_get_y(void *blk)
+{
+       struct amixer_rsc_ctrl_blk *ctl = blk;
+
+       return get_field(ctl->amoplo, AMOPLO_Y);
+}
+
+static unsigned int amixer_get_dirty(void *blk)
+{
+       return ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data;
+}
+
+static int amixer_rsc_get_ctrl_blk(void **rblk)
+{
+       struct amixer_rsc_ctrl_blk *blk;
+
+       *rblk = NULL;
+       blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+       if (NULL == blk)
+               return -ENOMEM;
+
+       *rblk = blk;
+
+       return 0;
+}
+
+static int amixer_rsc_put_ctrl_blk(void *blk)
+{
+       kfree((struct amixer_rsc_ctrl_blk *)blk);
+
+       return 0;
+}
+
+static int amixer_mgr_get_ctrl_blk(void **rblk)
+{
+       *rblk = NULL;
+
+       return 0;
+}
+
+static int amixer_mgr_put_ctrl_blk(void *blk)
+{
+       return 0;
+}
+
+/*
+ * DAIO control block definitions.
+ */
+
+/* Receiver Sample Rate Tracker Control register */
+#define SRTCTL_SRCO    0x000000FF
+#define SRTCTL_SRCM    0x0000FF00
+#define SRTCTL_RSR     0x00030000
+#define SRTCTL_DRAT    0x00300000
+#define SRTCTL_EC      0x01000000
+#define SRTCTL_ET      0x10000000
+
+/* DAIO Receiver register dirty flags */
+union dai_dirty {
+       struct {
+               u16 srt:1;
+               u16 rsv:15;
+       } bf;
+       u16 data;
+};
+
+/* DAIO Receiver control block */
+struct dai_ctrl_blk {
+       unsigned int    srt;
+       union dai_dirty dirty;
+};
+
+/* Audio Input Mapper RAM */
+#define AIM_ARC                0x00000FFF
+#define AIM_NXT                0x007F0000
+
+struct daoimap {
+       unsigned int aim;
+       unsigned int idx;
+};
+
+/* Audio Transmitter Control and Status register */
+#define ATXCTL_EN      0x00000001
+#define ATXCTL_MODE    0x00000010
+#define ATXCTL_CD      0x00000020
+#define ATXCTL_RAW     0x00000100
+#define ATXCTL_MT      0x00000200
+#define ATXCTL_NUC     0x00003000
+#define ATXCTL_BEN     0x00010000
+#define ATXCTL_BMUX    0x00700000
+#define ATXCTL_B24     0x01000000
+#define ATXCTL_CPF     0x02000000
+#define ATXCTL_RIV     0x10000000
+#define ATXCTL_LIV     0x20000000
+#define ATXCTL_RSAT    0x40000000
+#define ATXCTL_LSAT    0x80000000
+
+/* XDIF Transmitter register dirty flags */
+union dao_dirty {
+       struct {
+               u16 atxcsl:1;
+               u16 rsv:15;
+       } bf;
+       u16 data;
+};
+
+/* XDIF Transmitter control block */
+struct dao_ctrl_blk {
+       /* XDIF Transmitter Channel Status Low Register */
+       unsigned int    atxcsl;
+       union dao_dirty dirty;
+};
+
+/* Audio Receiver Control register */
+#define ARXCTL_EN      0x00000001
+
+/* DAIO manager register dirty flags */
+union daio_mgr_dirty {
+       struct {
+               u32 atxctl:8;
+               u32 arxctl:8;
+               u32 daoimap:1;
+               u32 rsv:15;
+       } bf;
+       u32 data;
+};
+
+/* DAIO manager control block */
+struct daio_mgr_ctrl_blk {
+       struct daoimap          daoimap;
+       unsigned int            txctl[8];
+       unsigned int            rxctl[8];
+       union daio_mgr_dirty    dirty;
+};
+
+static int dai_srt_set_srco(void *blk, unsigned int src)
+{
+       struct dai_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srt, SRTCTL_SRCO, src);
+       ctl->dirty.bf.srt = 1;
+       return 0;
+}
+
+static int dai_srt_set_srcm(void *blk, unsigned int src)
+{
+       struct dai_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srt, SRTCTL_SRCM, src);
+       ctl->dirty.bf.srt = 1;
+       return 0;
+}
+
+static int dai_srt_set_rsr(void *blk, unsigned int rsr)
+{
+       struct dai_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srt, SRTCTL_RSR, rsr);
+       ctl->dirty.bf.srt = 1;
+       return 0;
+}
+
+static int dai_srt_set_drat(void *blk, unsigned int drat)
+{
+       struct dai_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srt, SRTCTL_DRAT, drat);
+       ctl->dirty.bf.srt = 1;
+       return 0;
+}
+
+static int dai_srt_set_ec(void *blk, unsigned int ec)
+{
+       struct dai_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srt, SRTCTL_EC, ec ? 1 : 0);
+       ctl->dirty.bf.srt = 1;
+       return 0;
+}
+
+static int dai_srt_set_et(void *blk, unsigned int et)
+{
+       struct dai_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->srt, SRTCTL_ET, et ? 1 : 0);
+       ctl->dirty.bf.srt = 1;
+       return 0;
+}
+
+static int dai_commit_write(struct hw *hw, unsigned int idx, void *blk)
+{
+       struct dai_ctrl_blk *ctl = blk;
+
+       if (ctl->dirty.bf.srt) {
+               hw_write_20kx(hw, AUDIO_IO_RX_SRT_CTL+0x40*idx, ctl->srt);
+               ctl->dirty.bf.srt = 0;
+       }
+
+       return 0;
+}
+
+static int dai_get_ctrl_blk(void **rblk)
+{
+       struct dai_ctrl_blk *blk;
+
+       *rblk = NULL;
+       blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+       if (NULL == blk)
+               return -ENOMEM;
+
+       *rblk = blk;
+
+       return 0;
+}
+
+static int dai_put_ctrl_blk(void *blk)
+{
+       kfree((struct dai_ctrl_blk *)blk);
+
+       return 0;
+}
+
+static int dao_set_spos(void *blk, unsigned int spos)
+{
+       ((struct dao_ctrl_blk *)blk)->atxcsl = spos;
+       ((struct dao_ctrl_blk *)blk)->dirty.bf.atxcsl = 1;
+       return 0;
+}
+
+static int dao_commit_write(struct hw *hw, unsigned int idx, void *blk)
+{
+       struct dao_ctrl_blk *ctl = blk;
+
+       if (ctl->dirty.bf.atxcsl) {
+               if (idx < 4) {
+                       /* S/PDIF SPOSx */
+                       hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_L+0x40*idx,
+                                                       ctl->atxcsl);
+               }
+               ctl->dirty.bf.atxcsl = 0;
+       }
+
+       return 0;
+}
+
+static int dao_get_spos(void *blk, unsigned int *spos)
+{
+       *spos = ((struct dao_ctrl_blk *)blk)->atxcsl;
+       return 0;
+}
+
+static int dao_get_ctrl_blk(void **rblk)
+{
+       struct dao_ctrl_blk *blk;
+
+       *rblk = NULL;
+       blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+       if (NULL == blk)
+               return -ENOMEM;
+
+       *rblk = blk;
+
+       return 0;
+}
+
+static int dao_put_ctrl_blk(void *blk)
+{
+       kfree((struct dao_ctrl_blk *)blk);
+
+       return 0;
+}
+
+static int daio_mgr_enb_dai(void *blk, unsigned int idx)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->rxctl[idx], ARXCTL_EN, 1);
+       ctl->dirty.bf.arxctl |= (0x1 << idx);
+       return 0;
+}
+
+static int daio_mgr_dsb_dai(void *blk, unsigned int idx)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->rxctl[idx], ARXCTL_EN, 0);
+
+       ctl->dirty.bf.arxctl |= (0x1 << idx);
+       return 0;
+}
+
+static int daio_mgr_enb_dao(void *blk, unsigned int idx)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->txctl[idx], ATXCTL_EN, 1);
+       ctl->dirty.bf.atxctl |= (0x1 << idx);
+       return 0;
+}
+
+static int daio_mgr_dsb_dao(void *blk, unsigned int idx)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->txctl[idx], ATXCTL_EN, 0);
+       ctl->dirty.bf.atxctl |= (0x1 << idx);
+       return 0;
+}
+
+static int daio_mgr_dao_init(void *blk, unsigned int idx, unsigned int conf)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+
+       if (idx < 4) {
+               /* S/PDIF output */
+               switch ((conf & 0x7)) {
+               case 1:
+                       set_field(&ctl->txctl[idx], ATXCTL_NUC, 0);
+                       break;
+               case 2:
+                       set_field(&ctl->txctl[idx], ATXCTL_NUC, 1);
+                       break;
+               case 4:
+                       set_field(&ctl->txctl[idx], ATXCTL_NUC, 2);
+                       break;
+               case 8:
+                       set_field(&ctl->txctl[idx], ATXCTL_NUC, 3);
+                       break;
+               default:
+                       break;
+               }
+               /* CDIF */
+               set_field(&ctl->txctl[idx], ATXCTL_CD, (!(conf & 0x7)));
+               /* Non-audio */
+               set_field(&ctl->txctl[idx], ATXCTL_LIV, (conf >> 4) & 0x1);
+               /* Non-audio */
+               set_field(&ctl->txctl[idx], ATXCTL_RIV, (conf >> 4) & 0x1);
+               set_field(&ctl->txctl[idx], ATXCTL_RAW,
+                         ((conf >> 3) & 0x1) ? 0 : 0);
+               ctl->dirty.bf.atxctl |= (0x1 << idx);
+       } else {
+               /* I2S output */
+               /*idx %= 4; */
+       }
+       return 0;
+}
+
+static int daio_mgr_set_imaparc(void *blk, unsigned int slot)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->daoimap.aim, AIM_ARC, slot);
+       ctl->dirty.bf.daoimap = 1;
+       return 0;
+}
+
+static int daio_mgr_set_imapnxt(void *blk, unsigned int next)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+
+       set_field(&ctl->daoimap.aim, AIM_NXT, next);
+       ctl->dirty.bf.daoimap = 1;
+       return 0;
+}
+
+static int daio_mgr_set_imapaddr(void *blk, unsigned int addr)
+{
+       ((struct daio_mgr_ctrl_blk *)blk)->daoimap.idx = addr;
+       ((struct daio_mgr_ctrl_blk *)blk)->dirty.bf.daoimap = 1;
+       return 0;
+}
+
+static int daio_mgr_commit_write(struct hw *hw, void *blk)
+{
+       struct daio_mgr_ctrl_blk *ctl = blk;
+       unsigned int data = 0;
+       int i = 0;
+
+       for (i = 0; i < 8; i++) {
+               if ((ctl->dirty.bf.atxctl & (0x1 << i))) {
+                       data = ctl->txctl[i];
+                       hw_write_20kx(hw, (AUDIO_IO_TX_CTL+(0x40*i)), data);
+                       ctl->dirty.bf.atxctl &= ~(0x1 << i);
+                       mdelay(1);
+               }
+               if ((ctl->dirty.bf.arxctl & (0x1 << i))) {
+                       data = ctl->rxctl[i];
+                       hw_write_20kx(hw, (AUDIO_IO_RX_CTL+(0x40*i)), data);
+                       ctl->dirty.bf.arxctl &= ~(0x1 << i);
+                       mdelay(1);
+               }
+       }
+       if (ctl->dirty.bf.daoimap) {
+               hw_write_20kx(hw, AUDIO_IO_AIM+ctl->daoimap.idx*4,
+                                               ctl->daoimap.aim);
+               ctl->dirty.bf.daoimap = 0;
+       }
+
+       return 0;
+}
+
+static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk)
+{
+       struct daio_mgr_ctrl_blk *blk;
+       int i = 0;
+
+       *rblk = NULL;
+       blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+       if (NULL == blk)
+               return -ENOMEM;
+
+       for (i = 0; i < 8; i++) {
+               blk->txctl[i] = hw_read_20kx(hw, AUDIO_IO_TX_CTL+(0x40*i));
+               blk->rxctl[i] = hw_read_20kx(hw, AUDIO_IO_RX_CTL+(0x40*i));
+       }
+
+       *rblk = blk;
+
+       return 0;
+}
+
+static int daio_mgr_put_ctrl_blk(void *blk)
+{
+       kfree((struct daio_mgr_ctrl_blk *)blk);
+
+       return 0;
+}
+
+/* Card hardware initialization block */
+struct dac_conf {
+       unsigned int msr; /* master sample rate in rsrs */
+};
+
+struct adc_conf {
+       unsigned int msr;       /* master sample rate in rsrs */
+       unsigned char input;    /* the input source of ADC */
+       unsigned char mic20db;  /* boost mic by 20db if input is microphone */
+};
+
+struct daio_conf {
+       unsigned int msr; /* master sample rate in rsrs */
+};
+
+struct trn_conf {
+       unsigned long vm_pgt_phys;
+};
+
+static int hw_daio_init(struct hw *hw, const struct daio_conf *info)
+{
+       u32 dwData = 0;
+       int i;
+
+       /* Program I2S with proper sample rate and enable the correct I2S
+        * channel. ED(0/8/16/24): Enable all I2S/I2X master clock output */
+       if (1 == info->msr) {
+               hw_write_20kx(hw, AUDIO_IO_MCLK, 0x01010101);
+               hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x01010101);
+               hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0);
+       } else if (2 == info->msr) {
+               hw_write_20kx(hw, AUDIO_IO_MCLK, 0x11111111);
+               /* Specify all playing 96khz
+                * EA [0]       - Enabled
+                * RTA [4:5]    - 96kHz
+                * EB [8]       - Enabled
+                * RTB [12:13]  - 96kHz
+                * EC [16]      - Enabled
+                * RTC [20:21]  - 96kHz
+                * ED [24]      - Enabled
+                * RTD [28:29]  - 96kHz */
+               hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x11111111);
+               hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0);
+       } else {
+               printk(KERN_ALERT "ERROR!!! Invalid sampling rate!!!\n");
+               return -EINVAL;
+       }
+
+       for (i = 0; i < 8; i++) {
+               if (i <= 3) {
+                       /* 1st 3 channels are SPDIFs (SB0960) */
+                       if (i == 3)
+                               dwData = 0x1001001;
+                       else
+                               dwData = 0x1000001;
+
+                       hw_write_20kx(hw, (AUDIO_IO_TX_CTL+(0x40*i)), dwData);
+                       hw_write_20kx(hw, (AUDIO_IO_RX_CTL+(0x40*i)), dwData);
+
+                       /* Initialize the SPDIF Out Channel status registers.
+                        * The value specified here is based on the typical
+                        * values provided in the specification, namely: Clock
+                        * Accuracy of 1000ppm, Sample Rate of 48KHz,
+                        * unspecified source number, Generation status = 1,
+                        * Category code = 0x12 (Digital Signal Mixer),
+                        * Mode = 0, Emph = 0, Copy Permitted, AN = 0
+                        * (indicating that we're transmitting digital audio,
+                        * and the Professional Use bit is 0. */
+
+                       hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_L+(0x40*i),
+                                       0x02109204); /* Default to 48kHz */
+
+                       hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_H+(0x40*i), 0x0B);
+               } else {
+                       /* Next 5 channels are I2S (SB0960) */
+                       dwData = 0x11;
+                       hw_write_20kx(hw, AUDIO_IO_RX_CTL+(0x40*i), dwData);
+                       if (2 == info->msr) {
+                               /* Four channels per sample period */
+                               dwData |= 0x1000;
+                       }
+                       hw_write_20kx(hw, AUDIO_IO_TX_CTL+(0x40*i), dwData);
+               }
+       }
+
+       return 0;
+}
+
+/* TRANSPORT operations */
+static int hw_trn_init(struct hw *hw, const struct trn_conf *info)
+{
+       u32 vmctl = 0, data = 0;
+       unsigned long ptp_phys_low = 0, ptp_phys_high = 0;
+       int i = 0;
+
+       /* Set up device page table */
+       if ((~0UL) == info->vm_pgt_phys) {
+               printk(KERN_ALERT "Wrong device page table page address!!!\n");
+               return -1;
+       }
+
+       vmctl = 0x80000C0F;  /* 32-bit, 4k-size page */
+#if BITS_PER_LONG == 64
+       ptp_phys_low = info->vm_pgt_phys & ((1UL<<32)-1);
+       ptp_phys_high = (info->vm_pgt_phys>>32) & ((1UL<<32)-1);
+       vmctl |= (3<<8);
+#elif BITS_PER_LONG == 32
+       ptp_phys_low = info->vm_pgt_phys & (~0UL);
+       ptp_phys_high = 0;
+#else
+#      error "Unknown BITS_PER_LONG!"
+#endif
+#if PAGE_SIZE == 8192
+#      error "Don't support 8k-page!"
+#endif
+       /* Write page table physical address to all PTPAL registers */
+       for (i = 0; i < 64; i++) {
+               hw_write_20kx(hw, VMEM_PTPAL+(16*i), ptp_phys_low);
+               hw_write_20kx(hw, VMEM_PTPAH+(16*i), ptp_phys_high);
+       }
+       /* Enable virtual memory transfer */
+       hw_write_20kx(hw, VMEM_CTL, vmctl);
+       /* Enable transport bus master and queueing of request */
+       hw_write_20kx(hw, TRANSPORT_CTL, 0x03);
+       hw_write_20kx(hw, TRANSPORT_INT, 0x200c01);
+       /* Enable transport ring */
+       data = hw_read_20kx(hw, TRANSPORT_ENB);
+       hw_write_20kx(hw, TRANSPORT_ENB, (data | 0x03));
+
+       return 0;
+}
+
+/* Card initialization */
+#define GCTL_AIE       0x00000001
+#define GCTL_UAA       0x00000002
+#define GCTL_DPC       0x00000004
+#define GCTL_DBP       0x00000008
+#define GCTL_ABP       0x00000010
+#define GCTL_TBP       0x00000020
+#define GCTL_SBP       0x00000040
+#define GCTL_FBP       0x00000080
+#define GCTL_ME                0x00000100
+#define GCTL_AID       0x00001000
+
+#define PLLCTL_SRC     0x00000007
+#define PLLCTL_SPE     0x00000008
+#define PLLCTL_RD      0x000000F0
+#define PLLCTL_FD      0x0001FF00
+#define PLLCTL_OD      0x00060000
+#define PLLCTL_B       0x00080000
+#define PLLCTL_AS      0x00100000
+#define PLLCTL_LF      0x03E00000
+#define PLLCTL_SPS     0x1C000000
+#define PLLCTL_AD      0x60000000
+
+#define PLLSTAT_CCS    0x00000007
+#define PLLSTAT_SPL    0x00000008
+#define PLLSTAT_CRD    0x000000F0
+#define PLLSTAT_CFD    0x0001FF00
+#define PLLSTAT_SL     0x00020000
+#define PLLSTAT_FAS    0x00040000
+#define PLLSTAT_B      0x00080000
+#define PLLSTAT_PD     0x00100000
+#define PLLSTAT_OCA    0x00200000
+#define PLLSTAT_NCA    0x00400000
+
+static int hw_pll_init(struct hw *hw, unsigned int rsr)
+{
+       unsigned int pllenb;
+       unsigned int pllctl;
+       unsigned int pllstat;
+       int i;
+
+       pllenb = 0xB;
+       hw_write_20kx(hw, PLL_ENB, pllenb);
+       pllctl = 0x20D00000;
+       set_field(&pllctl, PLLCTL_FD, 16 - 4);
+       hw_write_20kx(hw, PLL_CTL, pllctl);
+       mdelay(40);
+       pllctl = hw_read_20kx(hw, PLL_CTL);
+       set_field(&pllctl, PLLCTL_B, 0);
+       if (48000 == rsr) {
+               set_field(&pllctl, PLLCTL_FD, 16 - 2);
+               set_field(&pllctl, PLLCTL_RD, 1 - 1);
+       } else { /* 44100 */
+               set_field(&pllctl, PLLCTL_FD, 147 - 2);
+               set_field(&pllctl, PLLCTL_RD, 10 - 1);
+       }
+       hw_write_20kx(hw, PLL_CTL, pllctl);
+       mdelay(40);
+       for (i = 0; i < 1000; i++) {
+               pllstat = hw_read_20kx(hw, PLL_STAT);
+               if (get_field(pllstat, PLLSTAT_PD))
+                       continue;
+
+               if (get_field(pllstat, PLLSTAT_B) !=
+                                       get_field(pllctl, PLLCTL_B))
+                       continue;
+
+               if (get_field(pllstat, PLLSTAT_CCS) !=
+                                       get_field(pllctl, PLLCTL_SRC))
+                       continue;
+
+               if (get_field(pllstat, PLLSTAT_CRD) !=
+                                       get_field(pllctl, PLLCTL_RD))
+                       continue;
+
+               if (get_field(pllstat, PLLSTAT_CFD) !=
+                                       get_field(pllctl, PLLCTL_FD))
+                       continue;
+
+               break;
+       }
+       if (i >= 1000) {
+               printk(KERN_ALERT "PLL initialization failed!!!\n");
+               return -EBUSY;
+       }
+
+       return 0;
+}
+
+static int hw_auto_init(struct hw *hw)
+{
+       unsigned int gctl;
+       int i;
+
+       gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL);
+       set_field(&gctl, GCTL_AIE, 0);
+       hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl);
+       set_field(&gctl, GCTL_AIE, 1);
+       hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl);
+       mdelay(10);
+       for (i = 0; i < 400000; i++) {
+               gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL);
+               if (get_field(gctl, GCTL_AID))
+                       break;
+       }
+       if (!get_field(gctl, GCTL_AID)) {
+               printk(KERN_ALERT "Card Auto-init failed!!!\n");
+               return -EBUSY;
+       }
+
+       return 0;
+}
+
+/* DAC operations */
+
+#define CS4382_MC1             0x1
+#define CS4382_MC2             0x2
+#define CS4382_MC3             0x3
+#define CS4382_FC              0x4
+#define CS4382_IC              0x5
+#define CS4382_XC1             0x6
+#define CS4382_VCA1            0x7
+#define CS4382_VCB1            0x8
+#define CS4382_XC2             0x9
+#define CS4382_VCA2            0xA
+#define CS4382_VCB2            0xB
+#define CS4382_XC3             0xC
+#define CS4382_VCA3            0xD
+#define CS4382_VCB3            0xE
+#define CS4382_XC4             0xF
+#define CS4382_VCA4            0x10
+#define CS4382_VCB4            0x11
+#define CS4382_CREV            0x12
+
+/* I2C status */
+#define STATE_LOCKED           0x00
+#define STATE_UNLOCKED         0xAA
+#define DATA_READY             0x800000    /* Used with I2C_IF_STATUS */
+#define DATA_ABORT             0x10000     /* Used with I2C_IF_STATUS */
+
+#define I2C_STATUS_DCM 0x00000001
+#define I2C_STATUS_BC  0x00000006
+#define I2C_STATUS_APD 0x00000008
+#define I2C_STATUS_AB  0x00010000
+#define I2C_STATUS_DR  0x00800000
+
+#define I2C_ADDRESS_PTAD       0x0000FFFF
+#define I2C_ADDRESS_SLAD       0x007F0000
+
+struct REGS_CS4382 {
+       u32 dwModeControl_1;
+       u32 dwModeControl_2;
+       u32 dwModeControl_3;
+
+       u32 dwFilterControl;
+       u32 dwInvertControl;
+
+       u32 dwMixControl_P1;
+       u32 dwVolControl_A1;
+       u32 dwVolControl_B1;
+
+       u32 dwMixControl_P2;
+       u32 dwVolControl_A2;
+       u32 dwVolControl_B2;
+
+       u32 dwMixControl_P3;
+       u32 dwVolControl_A3;
+       u32 dwVolControl_B3;
+
+       u32 dwMixControl_P4;
+       u32 dwVolControl_A4;
+       u32 dwVolControl_B4;
+};
+
+static u8 m_bAddressSize, m_bDataSize, m_bDeviceID;
+
+static int I2CUnlockFullAccess(struct hw *hw)
+{
+       u8 UnlockKeySequence_FLASH_FULLACCESS_MODE[2] =  {0xB3, 0xD4};
+
+       /* Send keys for forced BIOS mode */
+       hw_write_20kx(hw, I2C_IF_WLOCK,
+                       UnlockKeySequence_FLASH_FULLACCESS_MODE[0]);
+       hw_write_20kx(hw, I2C_IF_WLOCK,
+                       UnlockKeySequence_FLASH_FULLACCESS_MODE[1]);
+       /* Check whether the chip is unlocked */
+       if (hw_read_20kx(hw, I2C_IF_WLOCK) == STATE_UNLOCKED)
+               return 0;
+
+       return -1;
+}
+
+static int I2CLockChip(struct hw *hw)
+{
+       /* Write twice */
+       hw_write_20kx(hw, I2C_IF_WLOCK, STATE_LOCKED);
+       hw_write_20kx(hw, I2C_IF_WLOCK, STATE_LOCKED);
+       if (hw_read_20kx(hw, I2C_IF_WLOCK) == STATE_LOCKED)
+               return 0;
+
+       return -1;
+}
+
+static int I2CInit(struct hw *hw, u8 bDeviceID, u8 bAddressSize, u8 bDataSize)
+{
+       int err = 0;
+       unsigned int RegI2CStatus;
+       unsigned int RegI2CAddress;
+
+       err = I2CUnlockFullAccess(hw);
+       if (err < 0)
+               return err;
+
+       m_bAddressSize = bAddressSize;
+       m_bDataSize = bDataSize;
+       m_bDeviceID = bDeviceID;
+
+       RegI2CAddress = 0;
+       set_field(&RegI2CAddress, I2C_ADDRESS_SLAD, bDeviceID);
+
+       hw_write_20kx(hw, I2C_IF_ADDRESS, RegI2CAddress);
+
+       RegI2CStatus = hw_read_20kx(hw, I2C_IF_STATUS);
+
+       set_field(&RegI2CStatus, I2C_STATUS_DCM, 1); /* Direct control mode */
+
+       hw_write_20kx(hw, I2C_IF_STATUS, RegI2CStatus);
+
+       return 0;
+}
+
+static int I2CUninit(struct hw *hw)
+{
+       unsigned int RegI2CStatus;
+       unsigned int RegI2CAddress;
+
+       RegI2CAddress = 0;
+       set_field(&RegI2CAddress, I2C_ADDRESS_SLAD, 0x57); /* I2C id */
+
+       hw_write_20kx(hw, I2C_IF_ADDRESS, RegI2CAddress);
+
+       RegI2CStatus = hw_read_20kx(hw, I2C_IF_STATUS);
+
+       set_field(&RegI2CStatus, I2C_STATUS_DCM, 0); /* I2C mode */
+
+       hw_write_20kx(hw, I2C_IF_STATUS, RegI2CStatus);
+
+       return I2CLockChip(hw);
+}
+
+static int I2CWaitDataReady(struct hw *hw)
+{
+       int i = 0x400000;
+       unsigned int ret = 0;
+
+       do {
+               ret = hw_read_20kx(hw, I2C_IF_STATUS);
+       } while ((!(ret & DATA_READY)) && --i);
+
+       return i;
+}
+
+static int I2CRead(struct hw *hw, u16 wAddress, u32 *pdwData)
+{
+       unsigned int RegI2CStatus;
+
+       RegI2CStatus = hw_read_20kx(hw, I2C_IF_STATUS);
+       set_field(&RegI2CStatus, I2C_STATUS_BC,
+                       (4 == m_bAddressSize) ? 0 : m_bAddressSize);
+       hw_write_20kx(hw, I2C_IF_STATUS, RegI2CStatus);
+       if (!I2CWaitDataReady(hw))
+               return -1;
+
+       hw_write_20kx(hw, I2C_IF_WDATA, (u32)wAddress);
+       if (!I2CWaitDataReady(hw))
+               return -1;
+
+       /* Force a read operation */
+       hw_write_20kx(hw, I2C_IF_RDATA, 0);
+       if (!I2CWaitDataReady(hw))
+               return -1;
+
+       *pdwData = hw_read_20kx(hw, I2C_IF_RDATA);
+
+       return 0;
+}
+
+static int I2CWrite(struct hw *hw, u16 wAddress, u32 dwData)
+{
+       unsigned int dwI2CData = (dwData << (m_bAddressSize * 8)) | wAddress;
+       unsigned int RegI2CStatus;
+
+       RegI2CStatus = hw_read_20kx(hw, I2C_IF_STATUS);
+
+       set_field(&RegI2CStatus, I2C_STATUS_BC,
+                 (4 == (m_bAddressSize + m_bDataSize)) ?
+                 0 : (m_bAddressSize + m_bDataSize));
+
+       hw_write_20kx(hw, I2C_IF_STATUS, RegI2CStatus);
+       I2CWaitDataReady(hw);
+       /* Dummy write to trigger the write oprtation */
+       hw_write_20kx(hw, I2C_IF_WDATA, 0);
+       I2CWaitDataReady(hw);
+
+       /* This is the real data */
+       hw_write_20kx(hw, I2C_IF_WDATA, dwI2CData);
+       I2CWaitDataReady(hw);
+
+       return 0;
+}
+
+static int hw_dac_init(struct hw *hw, const struct dac_conf *info)
+{
+       int err = 0;
+       u32 dwData = 0;
+       int i = 0;
+       struct REGS_CS4382 cs4382_Read = {0};
+       struct REGS_CS4382 cs4382_Def = {
+                                  0x00000001,  /* Mode Control 1 */
+                                  0x00000000,  /* Mode Control 2 */
+                                  0x00000084,  /* Mode Control 3 */
+                                  0x00000000,  /* Filter Control */
+                                  0x00000000,  /* Invert Control */
+                                  0x00000024,  /* Mixing Control Pair 1 */
+                                  0x00000000,  /* Vol Control A1 */
+                                  0x00000000,  /* Vol Control B1 */
+                                  0x00000024,  /* Mixing Control Pair 2 */
+                                  0x00000000,  /* Vol Control A2 */
+                                  0x00000000,  /* Vol Control B2 */
+                                  0x00000024,  /* Mixing Control Pair 3 */
+                                  0x00000000,  /* Vol Control A3 */
+                                  0x00000000,  /* Vol Control B3 */
+                                  0x00000024,  /* Mixing Control Pair 4 */
+                                  0x00000000,  /* Vol Control A4 */
+                                  0x00000000   /* Vol Control B4 */
+                                };
+
+       /* Set DAC reset bit as output */
+       dwData = hw_read_20kx(hw, GPIO_CTRL);
+       dwData |= 0x02;
+       hw_write_20kx(hw, GPIO_CTRL, dwData);
+
+       err = I2CInit(hw, 0x18, 1, 1);
+       if (err < 0)
+               goto End;
+
+       for (i = 0; i < 2; i++) {
+               /* Reset DAC twice just in-case the chip
+                * didn't initialized properly */
+               dwData = hw_read_20kx(hw, GPIO_DATA);
+               /* GPIO data bit 1 */
+               dwData &= 0xFFFFFFFD;
+               hw_write_20kx(hw, GPIO_DATA, dwData);
+               mdelay(10);
+               dwData |= 0x2;
+               hw_write_20kx(hw, GPIO_DATA, dwData);
+               mdelay(50);
+
+               /* Reset the 2nd time */
+               dwData &= 0xFFFFFFFD;
+               hw_write_20kx(hw, GPIO_DATA, dwData);
+               mdelay(10);
+               dwData |= 0x2;
+               hw_write_20kx(hw, GPIO_DATA, dwData);
+               mdelay(50);
+
+               if (I2CRead(hw, CS4382_MC1,  &cs4382_Read.dwModeControl_1))
+                       continue;
+
+               if (I2CRead(hw, CS4382_MC2,  &cs4382_Read.dwModeControl_2))
+                       continue;
+
+               if (I2CRead(hw, CS4382_MC3,  &cs4382_Read.dwModeControl_3))
+                       continue;
+
+               if (I2CRead(hw, CS4382_FC,   &cs4382_Read.dwFilterControl))
+                       continue;
+
+               if (I2CRead(hw, CS4382_IC,   &cs4382_Read.dwInvertControl))
+                       continue;
+
+               if (I2CRead(hw, CS4382_XC1,  &cs4382_Read.dwMixControl_P1))
+                       continue;
+
+               if (I2CRead(hw, CS4382_VCA1, &cs4382_Read.dwVolControl_A1))
+                       continue;
+
+               if (I2CRead(hw, CS4382_VCB1, &cs4382_Read.dwVolControl_B1))
+                       continue;
+
+               if (I2CRead(hw, CS4382_XC2,  &cs4382_Read.dwMixControl_P2))
+                       continue;
+
+               if (I2CRead(hw, CS4382_VCA2, &cs4382_Read.dwVolControl_A2))
+                       continue;
+
+               if (I2CRead(hw, CS4382_VCB2, &cs4382_Read.dwVolControl_B2))
+                       continue;
+
+               if (I2CRead(hw, CS4382_XC3,  &cs4382_Read.dwMixControl_P3))
+                       continue;
+
+               if (I2CRead(hw, CS4382_VCA3, &cs4382_Read.dwVolControl_A3))
+                       continue;
+
+               if (I2CRead(hw, CS4382_VCB3, &cs4382_Read.dwVolControl_B3))
+                       continue;
+
+               if (I2CRead(hw, CS4382_XC4,  &cs4382_Read.dwMixControl_P4))
+                       continue;
+
+               if (I2CRead(hw, CS4382_VCA4, &cs4382_Read.dwVolControl_A4))
+                       continue;
+
+               if (I2CRead(hw, CS4382_VCB4, &cs4382_Read.dwVolControl_B4))
+                       continue;
+
+               if (memcmp(&cs4382_Read, &cs4382_Def,
+                                               sizeof(struct REGS_CS4382)))
+                       continue;
+               else
+                       break;
+       }
+
+       if (i >= 2)
+               goto End;
+
+       /* Note: Every I2C write must have some delay.
+        * This is not a requirement but the delay works here... */
+       I2CWrite(hw, CS4382_MC1, 0x80);
+       I2CWrite(hw, CS4382_MC2, 0x10);
+       if (1 == info->msr) {
+               I2CWrite(hw, CS4382_XC1, 0x24);
+               I2CWrite(hw, CS4382_XC2, 0x24);
+               I2CWrite(hw, CS4382_XC3, 0x24);
+               I2CWrite(hw, CS4382_XC4, 0x24);
+       } else if (2 == info->msr) {
+               I2CWrite(hw, CS4382_XC1, 0x25);
+               I2CWrite(hw, CS4382_XC2, 0x25);
+               I2CWrite(hw, CS4382_XC3, 0x25);
+               I2CWrite(hw, CS4382_XC4, 0x25);
+       } else {
+               I2CWrite(hw, CS4382_XC1, 0x26);
+               I2CWrite(hw, CS4382_XC2, 0x26);
+               I2CWrite(hw, CS4382_XC3, 0x26);
+               I2CWrite(hw, CS4382_XC4, 0x26);
+       }
+
+       return 0;
+End:
+
+       I2CUninit(hw);
+       return -1;
+}
+
+/* ADC operations */
+#define MAKE_WM8775_ADDR(addr, data)   (u32)(((addr<<1)&0xFE)|((data>>8)&0x1))
+#define MAKE_WM8775_DATA(data) (u32)(data&0xFF)
+
+#define WM8775_IC       0x0B
+#define WM8775_MMC      0x0C
+#define WM8775_AADCL    0x0E
+#define WM8775_AADCR    0x0F
+#define WM8775_ADCMC    0x15
+#define WM8775_RESET    0x17
+
+static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type)
+{
+       u32 data = 0;
+
+       data = hw_read_20kx(hw, GPIO_DATA);
+       switch (type) {
+       case ADC_MICIN:
+               data = (data & (0x1 << 14)) ? 1 : 0;
+               break;
+       case ADC_LINEIN:
+               data = (data & (0x1 << 14)) ? 0 : 1;
+               break;
+       default:
+               data = 0;
+       }
+       return data;
+}
+
+static int hw_adc_input_select(struct hw *hw, enum ADCSRC type)
+{
+       u32 data = 0;
+
+       data = hw_read_20kx(hw, GPIO_DATA);
+       switch (type) {
+       case ADC_MICIN:
+               data |= (0x1 << 14);
+               hw_write_20kx(hw, GPIO_DATA, data);
+               I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101),
+                               MAKE_WM8775_DATA(0x101)); /* Mic-in */
+               I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xE7),
+                               MAKE_WM8775_DATA(0xE7)); /* +12dB boost */
+               I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xE7),
+                               MAKE_WM8775_DATA(0xE7)); /* +12dB boost */
+               break;
+       case ADC_LINEIN:
+               data &= ~(0x1 << 14);
+               hw_write_20kx(hw, GPIO_DATA, data);
+               I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102),
+                               MAKE_WM8775_DATA(0x102)); /* Line-in */
+               I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF),
+                               MAKE_WM8775_DATA(0xCF)); /* No boost */
+               I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF),
+                               MAKE_WM8775_DATA(0xCF)); /* No boost */
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int hw_adc_init(struct hw *hw, const struct adc_conf *info)
+{
+       int err = 0;
+       u32 dwMux = 2, dwData = 0, dwCtl = 0;
+
+       /*  Set ADC reset bit as output */
+       dwData = hw_read_20kx(hw, GPIO_CTRL);
+       dwData |= (0x1 << 15);
+       hw_write_20kx(hw, GPIO_CTRL, dwData);
+
+       /* Initialize I2C */
+       err = I2CInit(hw, 0x1A, 1, 1);
+       if (err < 0) {
+               printk(KERN_ALERT "Failure to acquire I2C!!!\n");
+               goto error;
+       }
+
+       /* Make ADC in normal operation */
+       dwData = hw_read_20kx(hw, GPIO_DATA);
+       dwData &= ~(0x1 << 15);
+       mdelay(10);
+       dwData |= (0x1 << 15);
+       hw_write_20kx(hw, GPIO_DATA, dwData);
+       mdelay(50);
+
+       /* Set the master mode (256fs) */
+       if (1 == info->msr) {
+               I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x02),
+                                               MAKE_WM8775_DATA(0x02));
+       } else if (2 == info->msr) {
+               I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x0A),
+                                               MAKE_WM8775_DATA(0x0A));
+       } else {
+               printk(KERN_ALERT "Invalid master sampling "
+                                 "rate (msr %d)!!!\n", info->msr);
+               err = -EINVAL;
+               goto error;
+       }
+
+       /* Configure GPIO bit 14 change to line-in/mic-in */
+       dwCtl = hw_read_20kx(hw, GPIO_CTRL);
+       dwCtl |= 0x1<<14;
+       hw_write_20kx(hw, GPIO_CTRL, dwCtl);
+
+       /* Check using Mic-in or Line-in */
+       dwData = hw_read_20kx(hw, GPIO_DATA);
+
+       if (dwMux == 1) {
+               /* Configures GPIO data to select Mic-in */
+               dwData |= 0x1<<14;
+               hw_write_20kx(hw, GPIO_DATA, dwData);
+
+               I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101),
+                               MAKE_WM8775_DATA(0x101)); /* Mic-in */
+               I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xE7),
+                               MAKE_WM8775_DATA(0xE7)); /* +12dB boost */
+               I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xE7),
+                               MAKE_WM8775_DATA(0xE7)); /* +12dB boost */
+       } else if (dwMux == 2) {
+               /* Configures GPIO data to select Line-in */
+               dwData &= ~(0x1<<14);
+               hw_write_20kx(hw, GPIO_DATA, dwData);
+
+               /* Setup ADC */
+               I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102),
+                               MAKE_WM8775_DATA(0x102)); /* Line-in */
+               I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF),
+                               MAKE_WM8775_DATA(0xCF)); /* No boost */
+               I2CWrite(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF),
+                               MAKE_WM8775_DATA(0xCF)); /* No boost */
+       } else {
+               printk(KERN_ALERT "ERROR!!! Invalid input mux!!!\n");
+               err = -EINVAL;
+               goto error;
+       }
+
+       return 0;
+
+error:
+       I2CUninit(hw);
+       return err;
+}
+
+static int hw_have_digit_io_switch(struct hw *hw)
+{
+       return 0;
+}
+
+static int hw_card_start(struct hw *hw)
+{
+       int err = 0;
+       struct pci_dev *pci = hw->pci;
+       unsigned int gctl;
+       unsigned int dma_mask = 0;
+
+       err = pci_enable_device(pci);
+       if (err < 0)
+               return err;
+
+       /* Set DMA transfer mask */
+       dma_mask = CT_XFI_DMA_MASK;
+       if (pci_set_dma_mask(pci, dma_mask) < 0 ||
+           pci_set_consistent_dma_mask(pci, dma_mask) < 0) {
+               printk(KERN_ERR "architecture does not support PCI "
+               "busmaster DMA with mask 0x%x\n", dma_mask);
+               err = -ENXIO;
+               goto error1;
+       }
+
+       err = pci_request_regions(pci, "XFi");
+       if (err < 0)
+               goto error1;
+
+       hw->io_base = pci_resource_start(hw->pci, 2);
+       hw->mem_base = (unsigned long)ioremap(hw->io_base,
+                                       pci_resource_len(hw->pci, 2));
+       if (NULL == (void *)hw->mem_base) {
+               err = -ENOENT;
+               goto error2;
+       }
+
+       /* Switch to 20k2 mode from UAA mode. */
+       gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL);
+       set_field(&gctl, GCTL_UAA, 0);
+       hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl);
+
+       /*if ((err = request_irq(pci->irq, ct_atc_interrupt, IRQF_SHARED,
+                               atc->chip_details->nm_card, hw))) {
+               goto error3;
+       }
+       hw->irq = pci->irq;
+       */
+
+       pci_set_master(pci);
+
+       return 0;
+
+/*error3:
+       iounmap((void *)hw->mem_base);
+       hw->mem_base = (unsigned long)NULL;*/
+error2:
+       pci_release_regions(pci);
+       hw->io_base = 0;
+error1:
+       pci_disable_device(pci);
+       return err;
+}
+
+static int hw_card_stop(struct hw *hw)
+{
+       /* TODO: Disable interrupt and so on... */
+       return 0;
+}
+
+static int hw_card_shutdown(struct hw *hw)
+{
+       if (hw->irq >= 0)
+               free_irq(hw->irq, hw);
+
+       hw->irq = -1;
+
+       if (NULL != ((void *)hw->mem_base))
+               iounmap((void *)hw->mem_base);
+
+       hw->mem_base = (unsigned long)NULL;
+
+       if (hw->io_base)
+               pci_release_regions(hw->pci);
+
+       hw->io_base = 0;
+
+       pci_disable_device(hw->pci);
+
+       return 0;
+}
+
+static int hw_card_init(struct hw *hw, struct card_conf *info)
+{
+       int err;
+       unsigned int gctl;
+       u32 data = 0;
+       struct dac_conf dac_info = {0};
+       struct adc_conf adc_info = {0};
+       struct daio_conf daio_info = {0};
+       struct trn_conf trn_info = {0};
+
+       /* Get PCI io port/memory base address and
+        * do 20kx core switch if needed. */
+       if (!hw->io_base) {
+               err = hw_card_start(hw);
+               if (err)
+                       return err;
+       }
+
+       /* PLL init */
+       err = hw_pll_init(hw, info->rsr);
+       if (err < 0)
+               return err;
+
+       /* kick off auto-init */
+       err = hw_auto_init(hw);
+       if (err < 0)
+               return err;
+
+       gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL);
+       set_field(&gctl, GCTL_DBP, 1);
+       set_field(&gctl, GCTL_TBP, 1);
+       set_field(&gctl, GCTL_FBP, 1);
+       set_field(&gctl, GCTL_DPC, 0);
+       hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl);
+
+       /* Reset all global pending interrupts */
+       hw_write_20kx(hw, INTERRUPT_GIE, 0);
+       /* Reset all SRC pending interrupts */
+       hw_write_20kx(hw, SRC_IP, 0);
+
+       /* TODO: detect the card ID and configure GPIO accordingly. */
+       /* Configures GPIO (0xD802 0x98028) */
+       /*hw_write_20kx(hw, GPIO_CTRL, 0x7F07);*/
+       /* Configures GPIO (SB0880) */
+       /*hw_write_20kx(hw, GPIO_CTRL, 0xFF07);*/
+       hw_write_20kx(hw, GPIO_CTRL, 0xD802);
+
+       /* Enable audio ring */
+       hw_write_20kx(hw, MIXER_AR_ENABLE, 0x01);
+
+       trn_info.vm_pgt_phys = info->vm_pgt_phys;
+       err = hw_trn_init(hw, &trn_info);
+       if (err < 0)
+               return err;
+
+       daio_info.msr = info->msr;
+       err = hw_daio_init(hw, &daio_info);
+       if (err < 0)
+               return err;
+
+       dac_info.msr = info->msr;
+       err = hw_dac_init(hw, &dac_info);
+       if (err < 0)
+               return err;
+
+       adc_info.msr = info->msr;
+       adc_info.input = ADC_LINEIN;
+       adc_info.mic20db = 0;
+       err = hw_adc_init(hw, &adc_info);
+       if (err < 0)
+               return err;
+
+       data = hw_read_20kx(hw, SRC_MCTL);
+       data |= 0x1; /* Enables input from the audio ring */
+       hw_write_20kx(hw, SRC_MCTL, data);
+
+       return 0;
+}
+
+static u32 hw_read_20kx(struct hw *hw, u32 reg)
+{
+       return readl((void *)(hw->mem_base + reg));
+}
+
+static void hw_write_20kx(struct hw *hw, u32 reg, u32 data)
+{
+       writel(data, (void *)(hw->mem_base + reg));
+}
+
+int create_20k2_hw_obj(struct hw **rhw)
+{
+       struct hw *hw;
+
+       *rhw = NULL;
+       hw = kzalloc(sizeof(*hw), GFP_KERNEL);
+       if (NULL == hw)
+               return -ENOMEM;
+
+       hw->io_base = 0;
+       hw->mem_base = (unsigned long)NULL;
+       hw->irq = -1;
+
+       hw->card_init = hw_card_init;
+       hw->card_stop = hw_card_stop;
+       hw->pll_init = hw_pll_init;
+       hw->is_adc_source_selected = hw_is_adc_input_selected;
+       hw->select_adc_source = hw_adc_input_select;
+       hw->have_digit_io_switch = hw_have_digit_io_switch;
+
+       hw->src_rsc_get_ctrl_blk = src_get_rsc_ctrl_blk;
+       hw->src_rsc_put_ctrl_blk = src_put_rsc_ctrl_blk;
+       hw->src_mgr_get_ctrl_blk = src_mgr_get_ctrl_blk;
+       hw->src_mgr_put_ctrl_blk = src_mgr_put_ctrl_blk;
+       hw->src_set_state = src_set_state;
+       hw->src_set_bm = src_set_bm;
+       hw->src_set_rsr = src_set_rsr;
+       hw->src_set_sf = src_set_sf;
+       hw->src_set_wr = src_set_wr;
+       hw->src_set_pm = src_set_pm;
+       hw->src_set_rom = src_set_rom;
+       hw->src_set_vo = src_set_vo;
+       hw->src_set_st = src_set_st;
+       hw->src_set_ie = src_set_ie;
+       hw->src_set_ilsz = src_set_ilsz;
+       hw->src_set_bp = src_set_bp;
+       hw->src_set_cisz = src_set_cisz;
+       hw->src_set_ca = src_set_ca;
+       hw->src_set_sa = src_set_sa;
+       hw->src_set_la = src_set_la;
+       hw->src_set_pitch = src_set_pitch;
+       hw->src_set_dirty = src_set_dirty;
+       hw->src_set_clear_zbufs = src_set_clear_zbufs;
+       hw->src_set_dirty_all = src_set_dirty_all;
+       hw->src_commit_write = src_commit_write;
+       hw->src_get_ca = src_get_ca;
+       hw->src_get_dirty = src_get_dirty;
+       hw->src_dirty_conj_mask = src_dirty_conj_mask;
+       hw->src_mgr_enbs_src = src_mgr_enbs_src;
+       hw->src_mgr_enb_src = src_mgr_enb_src;
+       hw->src_mgr_dsb_src = src_mgr_dsb_src;
+       hw->src_mgr_commit_write = src_mgr_commit_write;
+
+       hw->srcimp_mgr_get_ctrl_blk = srcimp_mgr_get_ctrl_blk;
+       hw->srcimp_mgr_put_ctrl_blk = srcimp_mgr_put_ctrl_blk;
+       hw->srcimp_mgr_set_imaparc = srcimp_mgr_set_imaparc;
+       hw->srcimp_mgr_set_imapuser = srcimp_mgr_set_imapuser;
+       hw->srcimp_mgr_set_imapnxt = srcimp_mgr_set_imapnxt;
+       hw->srcimp_mgr_set_imapaddr = srcimp_mgr_set_imapaddr;
+       hw->srcimp_mgr_commit_write = srcimp_mgr_commit_write;
+
+       hw->amixer_rsc_get_ctrl_blk = amixer_rsc_get_ctrl_blk;
+       hw->amixer_rsc_put_ctrl_blk = amixer_rsc_put_ctrl_blk;
+       hw->amixer_mgr_get_ctrl_blk = amixer_mgr_get_ctrl_blk;
+       hw->amixer_mgr_put_ctrl_blk = amixer_mgr_put_ctrl_blk;
+       hw->amixer_set_mode = amixer_set_mode;
+       hw->amixer_set_iv = amixer_set_iv;
+       hw->amixer_set_x = amixer_set_x;
+       hw->amixer_set_y = amixer_set_y;
+       hw->amixer_set_sadr = amixer_set_sadr;
+       hw->amixer_set_se = amixer_set_se;
+       hw->amixer_set_dirty = amixer_set_dirty;
+       hw->amixer_set_dirty_all = amixer_set_dirty_all;
+       hw->amixer_commit_write = amixer_commit_write;
+       hw->amixer_get_y = amixer_get_y;
+       hw->amixer_get_dirty = amixer_get_dirty;
+
+       hw->dai_get_ctrl_blk = dai_get_ctrl_blk;
+       hw->dai_put_ctrl_blk = dai_put_ctrl_blk;
+       hw->dai_srt_set_srco = dai_srt_set_srco;
+       hw->dai_srt_set_srcm = dai_srt_set_srcm;
+       hw->dai_srt_set_rsr = dai_srt_set_rsr;
+       hw->dai_srt_set_drat = dai_srt_set_drat;
+       hw->dai_srt_set_ec = dai_srt_set_ec;
+       hw->dai_srt_set_et = dai_srt_set_et;
+       hw->dai_commit_write = dai_commit_write;
+
+       hw->dao_get_ctrl_blk = dao_get_ctrl_blk;
+       hw->dao_put_ctrl_blk = dao_put_ctrl_blk;
+       hw->dao_set_spos = dao_set_spos;
+       hw->dao_commit_write = dao_commit_write;
+       hw->dao_get_spos = dao_get_spos;
+
+       hw->daio_mgr_get_ctrl_blk = daio_mgr_get_ctrl_blk;
+       hw->daio_mgr_put_ctrl_blk = daio_mgr_put_ctrl_blk;
+       hw->daio_mgr_enb_dai = daio_mgr_enb_dai;
+       hw->daio_mgr_dsb_dai = daio_mgr_dsb_dai;
+       hw->daio_mgr_enb_dao = daio_mgr_enb_dao;
+       hw->daio_mgr_dsb_dao = daio_mgr_dsb_dao;
+       hw->daio_mgr_dao_init = daio_mgr_dao_init;
+       hw->daio_mgr_set_imaparc = daio_mgr_set_imaparc;
+       hw->daio_mgr_set_imapnxt = daio_mgr_set_imapnxt;
+       hw->daio_mgr_set_imapaddr = daio_mgr_set_imapaddr;
+       hw->daio_mgr_commit_write = daio_mgr_commit_write;
+
+       *rhw = hw;
+
+       return 0;
+}
+
+int destroy_20k2_hw_obj(struct hw *hw)
+{
+       if (hw->io_base)
+               hw_card_shutdown(hw);
+
+       kfree(hw);
+       return 0;
+}
diff --git a/sound/pci/ctxfi/cthw20k2.h b/sound/pci/ctxfi/cthw20k2.h
new file mode 100644 (file)
index 0000000..d2b7daa
--- /dev/null
@@ -0,0 +1,26 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       cthw20k2.h
+ *
+ * @Brief
+ * This file contains the definition of hardware access methord.
+ *
+ * @Author     Liu Chun
+ * @Date       May 13 2008
+ *
+ */
+
+#ifndef CTHW20K2_H
+#define CTHW20K2_H
+
+#include "cthardware.h"
+
+int create_20k2_hw_obj(struct hw **rhw);
+int destroy_20k2_hw_obj(struct hw *hw);
+
+#endif /* CTHW20K2_H */
diff --git a/sound/pci/ctxfi/ctimap.c b/sound/pci/ctxfi/ctimap.c
new file mode 100644 (file)
index 0000000..d34eacd
--- /dev/null
@@ -0,0 +1,112 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       ctimap.c
+ *
+ * @Brief
+ * This file contains the implementation of generic input mapper operations
+ * for input mapper management.
+ *
+ * @Author     Liu Chun
+ * @Date       May 23 2008
+ *
+ */
+
+#include "ctimap.h"
+#include <linux/slab.h>
+
+int input_mapper_add(struct list_head *mappers, struct imapper *entry,
+                    int (*map_op)(void *, struct imapper *), void *data)
+{
+       struct list_head *pos, *pre, *head;
+       struct imapper *pre_ent, *pos_ent;
+
+       head = mappers;
+
+       if (list_empty(head)) {
+               entry->next = entry->addr;
+               map_op(data, entry);
+               list_add(&entry->list, head);
+               return 0;
+       }
+
+       list_for_each(pos, head) {
+               pos_ent = list_entry(pos, struct imapper, list);
+               if (pos_ent->slot > entry->slot) {
+                       /* found a position in list */
+                       break;
+               }
+       }
+
+       if (pos != head) {
+               pre = pos->prev;
+               if (pre == head)
+                       pre = head->prev;
+
+               __list_add(&entry->list, pos->prev, pos);
+       } else {
+               pre = head->prev;
+               pos = head->next;
+               list_add_tail(&entry->list, head);
+       }
+
+       pre_ent = list_entry(pre, struct imapper, list);
+       pos_ent = list_entry(pos, struct imapper, list);
+
+       entry->next = pos_ent->addr;
+       map_op(data, entry);
+       pre_ent->next = entry->addr;
+       map_op(data, pre_ent);
+
+       return 0;
+}
+
+int input_mapper_delete(struct list_head *mappers, struct imapper *entry,
+                    int (*map_op)(void *, struct imapper *), void *data)
+{
+       struct list_head *next, *pre, *head;
+       struct imapper *pre_ent, *next_ent;
+
+       head = mappers;
+
+       if (list_empty(head))
+               return 0;
+
+       pre = (entry->list.prev == head) ? head->prev : entry->list.prev;
+       next = (entry->list.next == head) ? head->next : entry->list.next;
+
+       if (pre == &entry->list) {
+               /* entry is the only one node in mappers list */
+               entry->next = entry->addr = entry->user = entry->slot = 0;
+               map_op(data, entry);
+               list_del(&entry->list);
+               return 0;
+       }
+
+       pre_ent = list_entry(pre, struct imapper, list);
+       next_ent = list_entry(next, struct imapper, list);
+
+       pre_ent->next = next_ent->addr;
+       map_op(data, pre_ent);
+       list_del(&entry->list);
+
+       return 0;
+}
+
+void free_input_mapper_list(struct list_head *head)
+{
+       struct imapper *entry = NULL;
+       struct list_head *pos = NULL;
+
+       while (!list_empty(head)) {
+               pos = head->next;
+               list_del(pos);
+               entry = list_entry(pos, struct imapper, list);
+               kfree(entry);
+       }
+}
+
diff --git a/sound/pci/ctxfi/ctimap.h b/sound/pci/ctxfi/ctimap.h
new file mode 100644 (file)
index 0000000..53ccf9b
--- /dev/null
@@ -0,0 +1,40 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       ctimap.h
+ *
+ * @Brief
+ * This file contains the definition of generic input mapper operations
+ * for input mapper management.
+ *
+ * @Author     Liu Chun
+ * @Date       May 23 2008
+ *
+ */
+
+#ifndef CTIMAP_H
+#define CTIMAP_H
+
+#include <linux/list.h>
+
+struct imapper {
+       unsigned short slot; /* the id of the slot containing input data */
+       unsigned short user; /* the id of the user resource consuming data */
+       unsigned short addr; /* the input mapper ram id */
+       unsigned short next; /* the next input mapper ram id */
+       struct list_head        list;
+};
+
+int input_mapper_add(struct list_head *mappers, struct imapper *entry,
+                    int (*map_op)(void *, struct imapper *), void *data);
+
+int input_mapper_delete(struct list_head *mappers, struct imapper *entry,
+                    int (*map_op)(void *, struct imapper *), void *data);
+
+void free_input_mapper_list(struct list_head *mappers);
+
+#endif /* CTIMAP_H */
diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c
new file mode 100644 (file)
index 0000000..c80d692
--- /dev/null
@@ -0,0 +1,1108 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       ctmixer.c
+ *
+ * @Brief
+ * This file contains the implementation of alsa mixer device functions.
+ *
+ * @Author     Liu Chun
+ * @Date       May 28 2008
+ *
+ */
+
+
+#include "ctmixer.h"
+#include "ctamixer.h"
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/asoundef.h>
+#include <sound/pcm.h>
+#include <linux/slab.h>
+
+enum CT_SUM_CTL {
+       SUM_IN_F,
+       SUM_IN_R,
+       SUM_IN_C,
+       SUM_IN_S,
+       SUM_IN_F_C,
+
+       NUM_CT_SUMS
+};
+
+enum CT_AMIXER_CTL {
+       /* volume control mixers */
+       AMIXER_MASTER_F,
+       AMIXER_MASTER_R,
+       AMIXER_MASTER_C,
+       AMIXER_MASTER_S,
+       AMIXER_PCM_F,
+       AMIXER_PCM_R,
+       AMIXER_PCM_C,
+       AMIXER_PCM_S,
+       AMIXER_SPDIFI,
+       AMIXER_LINEIN,
+       AMIXER_MIC,
+       AMIXER_SPDIFO,
+       AMIXER_WAVE_F,
+       AMIXER_WAVE_R,
+       AMIXER_WAVE_C,
+       AMIXER_WAVE_S,
+       AMIXER_MASTER_F_C,
+       AMIXER_PCM_F_C,
+       AMIXER_SPDIFI_C,
+       AMIXER_LINEIN_C,
+       AMIXER_MIC_C,
+
+       /* this should always be the last one */
+       NUM_CT_AMIXERS
+};
+
+enum CTALSA_MIXER_CTL {
+       /* volume control mixers */
+       MIXER_MASTER_P,
+       MIXER_PCM_P,
+       MIXER_LINEIN_P,
+       MIXER_MIC_P,
+       MIXER_SPDIFI_P,
+       MIXER_SPDIFO_P,
+       MIXER_WAVEF_P,
+       MIXER_WAVER_P,
+       MIXER_WAVEC_P,
+       MIXER_WAVES_P,
+       MIXER_MASTER_C,
+       MIXER_PCM_C,
+       MIXER_LINEIN_C,
+       MIXER_MIC_C,
+       MIXER_SPDIFI_C,
+
+       /* switch control mixers */
+       MIXER_PCM_C_S,
+       MIXER_LINEIN_C_S,
+       MIXER_MIC_C_S,
+       MIXER_SPDIFI_C_S,
+       MIXER_LINEIN_P_S,
+       MIXER_SPDIFO_P_S,
+       MIXER_SPDIFI_P_S,
+       MIXER_WAVEF_P_S,
+       MIXER_WAVER_P_S,
+       MIXER_WAVEC_P_S,
+       MIXER_WAVES_P_S,
+       MIXER_DIGITAL_IO_S,
+       MIXER_IEC958_MASK,
+       MIXER_IEC958_DEFAULT,
+       MIXER_IEC958_STREAM,
+
+       /* this should always be the last one */
+       NUM_CTALSA_MIXERS
+};
+
+#define VOL_MIXER_START                MIXER_MASTER_P
+#define VOL_MIXER_END          MIXER_SPDIFI_C
+#define VOL_MIXER_NUM          (VOL_MIXER_END - VOL_MIXER_START + 1)
+#define SWH_MIXER_START                MIXER_PCM_C_S
+#define SWH_MIXER_END          MIXER_DIGITAL_IO_S
+#define SWH_CAPTURE_START      MIXER_PCM_C_S
+#define SWH_CAPTURE_END                MIXER_SPDIFI_C_S
+
+#define CHN_NUM                2
+
+struct ct_kcontrol_init {
+       unsigned char ctl;
+       char *name;
+};
+
+static struct ct_kcontrol_init
+ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = {
+       [MIXER_MASTER_P] = {
+               .ctl = 1,
+               .name = "Master Playback Volume",
+       },
+       [MIXER_MASTER_C] = {
+               .ctl = 1,
+               .name = "Master Capture Volume",
+       },
+       [MIXER_PCM_P] = {
+               .ctl = 1,
+               .name = "PCM Playback Volume",
+       },
+       [MIXER_PCM_C] = {
+               .ctl = 1,
+               .name = "PCM Capture Volume",
+       },
+       [MIXER_LINEIN_P] = {
+               .ctl = 1,
+               .name = "Line-in Playback Volume",
+       },
+       [MIXER_LINEIN_C] = {
+               .ctl = 1,
+               .name = "Line-in Capture Volume",
+       },
+       [MIXER_MIC_P] = {
+               .ctl = 1,
+               .name = "Mic Playback Volume",
+       },
+       [MIXER_MIC_C] = {
+               .ctl = 1,
+               .name = "Mic Capture Volume",
+       },
+       [MIXER_SPDIFI_P] = {
+               .ctl = 1,
+               .name = "S/PDIF-in Playback Volume",
+       },
+       [MIXER_SPDIFI_C] = {
+               .ctl = 1,
+               .name = "S/PDIF-in Capture Volume",
+       },
+       [MIXER_SPDIFO_P] = {
+               .ctl = 1,
+               .name = "S/PDIF-out Playback Volume",
+       },
+       [MIXER_WAVEF_P] = {
+               .ctl = 1,
+               .name = "Front Playback Volume",
+       },
+       [MIXER_WAVES_P] = {
+               .ctl = 1,
+               .name = "Surround Playback Volume",
+       },
+       [MIXER_WAVEC_P] = {
+               .ctl = 1,
+               .name = "Center/LFE Playback Volume",
+       },
+       [MIXER_WAVER_P] = {
+               .ctl = 1,
+               .name = "Rear Playback Volume",
+       },
+
+       [MIXER_PCM_C_S] = {
+               .ctl = 1,
+               .name = "PCM Capture Switch",
+       },
+       [MIXER_LINEIN_C_S] = {
+               .ctl = 1,
+               .name = "Line-in Capture Switch",
+       },
+       [MIXER_MIC_C_S] = {
+               .ctl = 1,
+               .name = "Mic Capture Switch",
+       },
+       [MIXER_SPDIFI_C_S] = {
+               .ctl = 1,
+               .name = "S/PDIF-in Capture Switch",
+       },
+       [MIXER_LINEIN_P_S] = {
+               .ctl = 1,
+               .name = "Line-in Playback Switch",
+       },
+       [MIXER_SPDIFO_P_S] = {
+               .ctl = 1,
+               .name = "S/PDIF-out Playback Switch",
+       },
+       [MIXER_SPDIFI_P_S] = {
+               .ctl = 1,
+               .name = "S/PDIF-in Playback Switch",
+       },
+       [MIXER_WAVEF_P_S] = {
+               .ctl = 1,
+               .name = "Front Playback Switch",
+       },
+       [MIXER_WAVES_P_S] = {
+               .ctl = 1,
+               .name = "Surround Playback Switch",
+       },
+       [MIXER_WAVEC_P_S] = {
+               .ctl = 1,
+               .name = "Center/LFE Playback Switch",
+       },
+       [MIXER_WAVER_P_S] = {
+               .ctl = 1,
+               .name = "Rear Playback Switch",
+       },
+       [MIXER_DIGITAL_IO_S] = {
+               .ctl = 0,
+               .name = "Digit-IO Playback Switch",
+       },
+};
+
+static void
+ct_mixer_recording_select(struct ct_mixer *mixer, enum CT_AMIXER_CTL type);
+
+static void
+ct_mixer_recording_unselect(struct ct_mixer *mixer, enum CT_AMIXER_CTL type);
+
+static struct snd_kcontrol *kctls[2] = {NULL};
+
+static enum CT_AMIXER_CTL get_amixer_index(enum CTALSA_MIXER_CTL alsa_index)
+{
+       switch (alsa_index) {
+       case MIXER_MASTER_P:    return AMIXER_MASTER_F;
+       case MIXER_MASTER_C:    return AMIXER_MASTER_F_C;
+       case MIXER_PCM_P:       return AMIXER_PCM_F;
+       case MIXER_PCM_C:
+       case MIXER_PCM_C_S:     return AMIXER_PCM_F_C;
+       case MIXER_LINEIN_P:    return AMIXER_LINEIN;
+       case MIXER_LINEIN_C:
+       case MIXER_LINEIN_C_S:  return AMIXER_LINEIN_C;
+       case MIXER_MIC_P:       return AMIXER_MIC;
+       case MIXER_MIC_C:
+       case MIXER_MIC_C_S:     return AMIXER_MIC_C;
+       case MIXER_SPDIFI_P:    return AMIXER_SPDIFI;
+       case MIXER_SPDIFI_C:
+       case MIXER_SPDIFI_C_S:  return AMIXER_SPDIFI_C;
+       case MIXER_SPDIFO_P:    return AMIXER_SPDIFO;
+       case MIXER_WAVEF_P:     return AMIXER_WAVE_F;
+       case MIXER_WAVES_P:     return AMIXER_WAVE_S;
+       case MIXER_WAVEC_P:     return AMIXER_WAVE_C;
+       case MIXER_WAVER_P:     return AMIXER_WAVE_R;
+       default:                return NUM_CT_AMIXERS;
+       }
+}
+
+static enum CT_AMIXER_CTL get_recording_amixer(enum CT_AMIXER_CTL index)
+{
+       switch (index) {
+       case AMIXER_MASTER_F:   return AMIXER_MASTER_F_C;
+       case AMIXER_PCM_F:      return AMIXER_PCM_F_C;
+       case AMIXER_SPDIFI:     return AMIXER_SPDIFI_C;
+       case AMIXER_LINEIN:     return AMIXER_LINEIN_C;
+       case AMIXER_MIC:        return AMIXER_MIC_C;
+       default:                return NUM_CT_AMIXERS;
+       }
+}
+
+static unsigned char
+get_switch_state(struct ct_mixer *mixer, enum CTALSA_MIXER_CTL type)
+{
+       return (mixer->switch_state & (0x1 << (type - SWH_MIXER_START)))
+               ? 1 : 0;
+}
+
+static void
+set_switch_state(struct ct_mixer *mixer,
+                enum CTALSA_MIXER_CTL type, unsigned char state)
+{
+       if (state)
+               mixer->switch_state |= (0x1 << (type - SWH_MIXER_START));
+       else
+               mixer->switch_state &= ~(0x1 << (type - SWH_MIXER_START));
+}
+
+/* Map integer value ranging from 0 to 65535 to 14-bit float value ranging
+ * from 2^-6 to (1+1023/1024) */
+static unsigned int uint16_to_float14(unsigned int x)
+{
+       unsigned int i = 0;
+
+       if (x < 17)
+               return 0;
+
+       x *= 2031;
+       x /= 65535;
+       x += 16;
+
+       /* i <= 6 */
+       for (i = 0; !(x & 0x400); i++)
+               x <<= 1;
+
+       x = (((7 - i) & 0x7) << 10) | (x & 0x3ff);
+
+       return x;
+}
+
+static unsigned int float14_to_uint16(unsigned int x)
+{
+       unsigned int e = 0;
+
+       if (!x)
+               return x;
+
+       e = (x >> 10) & 0x7;
+       x &= 0x3ff;
+       x += 1024;
+       x >>= (7 - e);
+       x -= 16;
+       x *= 65535;
+       x /= 2031;
+
+       return x;
+}
+
+static int ct_alsa_mix_volume_info(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 2;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 43690;
+       uinfo->value.integer.step = 128;
+
+       return 0;
+}
+
+static int ct_alsa_mix_volume_get(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+       struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+       enum CT_AMIXER_CTL type = get_amixer_index(kcontrol->private_value);
+       struct amixer *amixer = NULL;
+       int i = 0;
+
+       for (i = 0; i < 2; i++) {
+               amixer = ((struct ct_mixer *)atc->mixer)->
+                                               amixers[type*CHN_NUM+i];
+               /* Convert 14-bit float-point scale to 16-bit integer volume */
+               ucontrol->value.integer.value[i] =
+               (float14_to_uint16(amixer->ops->get_scale(amixer)) & 0xffff);
+       }
+
+       return 0;
+}
+
+static int ct_alsa_mix_volume_put(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+       struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+       struct ct_mixer *mixer = atc->mixer;
+       enum CT_AMIXER_CTL type = get_amixer_index(kcontrol->private_value);
+       struct amixer *amixer = NULL;
+       int i = 0, j = 0, change = 0, val = 0;
+
+       for (i = 0; i < 2; i++) {
+               /* Convert 16-bit integer volume to 14-bit float-point scale */
+               val = (ucontrol->value.integer.value[i] & 0xffff);
+               amixer = mixer->amixers[type*CHN_NUM+i];
+               if ((float14_to_uint16(amixer->ops->get_scale(amixer)) & 0xff80)
+                               != (val & 0xff80)) {
+                       val = uint16_to_float14(val);
+                       amixer->ops->set_scale(amixer, val);
+                       amixer->ops->commit_write(amixer);
+                       change = 1;
+                       /* Synchronize Master/PCM playback AMIXERs. */
+                       if (AMIXER_MASTER_F == type || AMIXER_PCM_F == type) {
+                               for (j = 1; j < 4; j++) {
+                                       amixer = mixer->
+                                               amixers[(type+j)*CHN_NUM+i];
+                                       amixer->ops->set_scale(amixer, val);
+                                       amixer->ops->commit_write(amixer);
+                               }
+                       }
+               }
+       }
+
+       return change;
+}
+
+static struct snd_kcontrol_new vol_ctl = {
+       .access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+       .iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .info           = ct_alsa_mix_volume_info,
+       .get            = ct_alsa_mix_volume_get,
+       .put            = ct_alsa_mix_volume_put
+};
+
+static void
+do_line_mic_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type)
+{
+
+       if (MIXER_LINEIN_C_S == type) {
+               atc->select_line_in(atc);
+               set_switch_state(atc->mixer, MIXER_MIC_C_S, 0);
+               snd_ctl_notify(atc->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                                                       &kctls[1]->id);
+       } else if (MIXER_MIC_C_S == type) {
+               atc->select_mic_in(atc);
+               set_switch_state(atc->mixer, MIXER_LINEIN_C_S, 0);
+               snd_ctl_notify(atc->card, SNDRV_CTL_EVENT_MASK_VALUE,
+                                                       &kctls[0]->id);
+       }
+}
+
+static void
+do_digit_io_switch(struct ct_atc *atc, int state)
+{
+       struct ct_mixer *mixer = atc->mixer;
+
+       if (state) {
+               atc->select_digit_io(atc);
+               atc->spdif_out_unmute(atc,
+                               get_switch_state(mixer, MIXER_SPDIFO_P_S));
+               atc->spdif_in_unmute(atc, 1);
+               atc->line_in_unmute(atc, 0);
+               return;
+       }
+
+       if (get_switch_state(mixer, MIXER_LINEIN_C_S))
+               atc->select_line_in(atc);
+       else if (get_switch_state(mixer, MIXER_MIC_C_S))
+               atc->select_mic_in(atc);
+
+       atc->spdif_out_unmute(atc, 0);
+       atc->spdif_in_unmute(atc, 0);
+       atc->line_in_unmute(atc, 1);
+       return;
+}
+
+static int ct_alsa_mix_switch_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;
+       uinfo->value.integer.step = 1;
+
+       return 0;
+}
+
+static int ct_alsa_mix_switch_get(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+       struct ct_mixer *mixer =
+               ((struct ct_atc *)snd_kcontrol_chip(kcontrol))->mixer;
+       enum CTALSA_MIXER_CTL type = kcontrol->private_value;
+
+       ucontrol->value.integer.value[0] = get_switch_state(mixer, type);
+       return 0;
+}
+
+static int ct_alsa_mix_switch_put(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+       struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+       struct ct_mixer *mixer = atc->mixer;
+       enum CTALSA_MIXER_CTL type = kcontrol->private_value;
+       int state = 0;
+
+       state = ucontrol->value.integer.value[0];
+       if (get_switch_state(mixer, type) == state)
+               return 0;
+
+       set_switch_state(mixer, type, state);
+       /* Do changes in mixer. */
+       if ((SWH_CAPTURE_START <= type) && (SWH_CAPTURE_END >= type)) {
+               if (state) {
+                       ct_mixer_recording_select(mixer,
+                                                 get_amixer_index(type));
+               } else {
+                       ct_mixer_recording_unselect(mixer,
+                                                   get_amixer_index(type));
+               }
+       }
+       /* Do changes out of mixer. */
+       if (state && (MIXER_LINEIN_C_S == type || MIXER_MIC_C_S == type))
+               do_line_mic_switch(atc, type);
+       else if (MIXER_WAVEF_P_S == type)
+               atc->line_front_unmute(atc, state);
+       else if (MIXER_WAVES_P_S == type)
+               atc->line_surround_unmute(atc, state);
+       else if (MIXER_WAVEC_P_S == type)
+               atc->line_clfe_unmute(atc, state);
+       else if (MIXER_WAVER_P_S == type)
+               atc->line_rear_unmute(atc, state);
+       else if (MIXER_LINEIN_P_S == type)
+               atc->line_in_unmute(atc, state);
+       else if (MIXER_SPDIFO_P_S == type)
+               atc->spdif_out_unmute(atc, state);
+       else if (MIXER_SPDIFI_P_S == type)
+               atc->spdif_in_unmute(atc, state);
+       else if (MIXER_DIGITAL_IO_S == type)
+               do_digit_io_switch(atc, state);
+
+       return 1;
+}
+
+static struct snd_kcontrol_new swh_ctl = {
+       .access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+       .iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .info           = ct_alsa_mix_switch_info,
+       .get            = ct_alsa_mix_switch_get,
+       .put            = ct_alsa_mix_switch_put
+};
+
+static int ct_spdif_info(struct snd_kcontrol *kcontrol,
+                        struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+       uinfo->count = 1;
+       return 0;
+}
+
+static int ct_spdif_get_mask(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       ucontrol->value.iec958.status[0] = 0xff;
+       ucontrol->value.iec958.status[1] = 0xff;
+       ucontrol->value.iec958.status[2] = 0xff;
+       ucontrol->value.iec958.status[3] = 0xff;
+       return 0;
+}
+
+static int ct_spdif_default_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       unsigned int status = SNDRV_PCM_DEFAULT_CON_SPDIF;
+
+       ucontrol->value.iec958.status[0] = (status >> 0) & 0xff;
+       ucontrol->value.iec958.status[1] = (status >> 8) & 0xff;
+       ucontrol->value.iec958.status[2] = (status >> 16) & 0xff;
+       ucontrol->value.iec958.status[3] = (status >> 24) & 0xff;
+
+       return 0;
+}
+
+static int ct_spdif_get(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+       unsigned int status = 0;
+
+       atc->spdif_out_get_status(atc, &status);
+       ucontrol->value.iec958.status[0] = (status >> 0) & 0xff;
+       ucontrol->value.iec958.status[1] = (status >> 8) & 0xff;
+       ucontrol->value.iec958.status[2] = (status >> 16) & 0xff;
+       ucontrol->value.iec958.status[3] = (status >> 24) & 0xff;
+
+       return 0;
+}
+
+static int ct_spdif_put(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+       int change = 1;
+       unsigned int status = 0, old_status = 0;
+
+       status = (ucontrol->value.iec958.status[0] << 0) |
+                (ucontrol->value.iec958.status[1] << 8) |
+                (ucontrol->value.iec958.status[2] << 16) |
+                (ucontrol->value.iec958.status[3] << 24);
+
+       atc->spdif_out_get_status(atc, &old_status);
+       change = (old_status != status);
+       if (change)
+               atc->spdif_out_set_status(atc, status);
+
+       return change;
+}
+
+static struct snd_kcontrol_new iec958_mask_ctl = {
+       .access         = SNDRV_CTL_ELEM_ACCESS_READ,
+       .iface          = SNDRV_CTL_ELEM_IFACE_PCM,
+       .name           = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK),
+       .count          = 1,
+       .info           = ct_spdif_info,
+       .get            = ct_spdif_get_mask,
+       .private_value  = MIXER_IEC958_MASK
+};
+
+static struct snd_kcontrol_new iec958_default_ctl = {
+       .iface          = SNDRV_CTL_ELEM_IFACE_PCM,
+       .name           = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
+       .count          = 1,
+       .info           = ct_spdif_info,
+       .get            = ct_spdif_default_get,
+       .put            = ct_spdif_put,
+       .private_value  = MIXER_IEC958_DEFAULT
+};
+
+static struct snd_kcontrol_new iec958_ctl = {
+       .access         = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+       .iface          = SNDRV_CTL_ELEM_IFACE_PCM,
+       .name           = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM),
+       .count          = 1,
+       .info           = ct_spdif_info,
+       .get            = ct_spdif_get,
+       .put            = ct_spdif_put,
+       .private_value  = MIXER_IEC958_STREAM
+};
+
+#define NUM_IEC958_CTL 3
+
+static int
+ct_mixer_kcontrol_new(struct ct_mixer *mixer, struct snd_kcontrol_new *new)
+{
+       struct snd_kcontrol *kctl = NULL;
+       int err = 0;
+
+       kctl = snd_ctl_new1(new, mixer->atc);
+       if (NULL == kctl)
+               return -ENOMEM;
+
+       if (SNDRV_CTL_ELEM_IFACE_PCM == kctl->id.iface)
+               kctl->id.device = IEC958;
+
+       err = snd_ctl_add(mixer->atc->card, kctl);
+       if (err)
+               return err;
+
+       switch (new->private_value) {
+       case MIXER_LINEIN_C_S:
+               kctls[0] = kctl; break;
+       case MIXER_MIC_C_S:
+               kctls[1] = kctl; break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int ct_mixer_kcontrols_create(struct ct_mixer *mixer)
+{
+       enum CTALSA_MIXER_CTL type = 0;
+       struct ct_atc *atc = mixer->atc;
+       int err = 0;
+
+       /* Create snd kcontrol instances on demand */
+       for (type = VOL_MIXER_START; type <= VOL_MIXER_END; type++) {
+               if (ct_kcontrol_init_table[type].ctl) {
+                       vol_ctl.name = ct_kcontrol_init_table[type].name;
+                       vol_ctl.private_value = (unsigned long)type;
+                       err = ct_mixer_kcontrol_new(mixer, &vol_ctl);
+                       if (err)
+                               return err;
+               }
+       }
+
+       ct_kcontrol_init_table[MIXER_DIGITAL_IO_S].ctl =
+                                       atc->have_digit_io_switch(atc);
+       for (type = SWH_MIXER_START; type <= SWH_MIXER_END; type++) {
+               if (ct_kcontrol_init_table[type].ctl) {
+                       swh_ctl.name = ct_kcontrol_init_table[type].name;
+                       swh_ctl.private_value = (unsigned long)type;
+                       err = ct_mixer_kcontrol_new(mixer, &swh_ctl);
+                       if (err)
+                               return err;
+               }
+       }
+
+       err = ct_mixer_kcontrol_new(mixer, &iec958_mask_ctl);
+       if (err)
+               return err;
+
+       err = ct_mixer_kcontrol_new(mixer, &iec958_default_ctl);
+       if (err)
+               return err;
+
+       err = ct_mixer_kcontrol_new(mixer, &iec958_ctl);
+       if (err)
+               return err;
+
+       atc->line_front_unmute(atc, 1);
+       set_switch_state(mixer, MIXER_WAVEF_P_S, 1);
+       atc->line_surround_unmute(atc, 0);
+       set_switch_state(mixer, MIXER_WAVES_P_S, 0);
+       atc->line_clfe_unmute(atc, 0);
+       set_switch_state(mixer, MIXER_WAVEC_P_S, 0);
+       atc->line_rear_unmute(atc, 0);
+       set_switch_state(mixer, MIXER_WAVER_P_S, 0);
+       atc->spdif_out_unmute(atc, 0);
+       set_switch_state(mixer, MIXER_SPDIFO_P_S, 0);
+       atc->line_in_unmute(atc, 0);
+       set_switch_state(mixer, MIXER_LINEIN_P_S, 0);
+       atc->spdif_in_unmute(atc, 0);
+       set_switch_state(mixer, MIXER_SPDIFI_P_S, 0);
+
+       set_switch_state(mixer, MIXER_PCM_C_S, 1);
+       set_switch_state(mixer, MIXER_LINEIN_C_S, 1);
+       set_switch_state(mixer, MIXER_SPDIFI_C_S, 1);
+
+       return 0;
+}
+
+static void
+ct_mixer_recording_select(struct ct_mixer *mixer, enum CT_AMIXER_CTL type)
+{
+       struct amixer *amix_d = NULL;
+       struct sum *sum_c = NULL;
+       int i = 0;
+
+       for (i = 0; i < 2; i++) {
+               amix_d = mixer->amixers[type*CHN_NUM+i];
+               sum_c = mixer->sums[SUM_IN_F_C*CHN_NUM+i];
+               amix_d->ops->set_sum(amix_d, sum_c);
+               amix_d->ops->commit_write(amix_d);
+       }
+}
+
+static void
+ct_mixer_recording_unselect(struct ct_mixer *mixer, enum CT_AMIXER_CTL type)
+{
+       struct amixer *amix_d = NULL;
+       int i = 0;
+
+       for (i = 0; i < 2; i++) {
+               amix_d = mixer->amixers[type*CHN_NUM+i];
+               amix_d->ops->set_sum(amix_d, NULL);
+               amix_d->ops->commit_write(amix_d);
+       }
+}
+
+static int ct_mixer_get_resources(struct ct_mixer *mixer)
+{
+       struct sum_mgr *sum_mgr = NULL;
+       struct sum *sum = NULL;
+       struct sum_desc sum_desc = {0};
+       struct amixer_mgr *amixer_mgr = NULL;
+       struct amixer *amixer = NULL;
+       struct amixer_desc am_desc = {0};
+       int err = 0;
+       int i = 0;
+
+       /* Allocate sum resources for mixer obj */
+       sum_mgr = (struct sum_mgr *)mixer->atc->rsc_mgrs[SUM];
+       sum_desc.msr = mixer->atc->msr;
+       for (i = 0; i < (NUM_CT_SUMS * CHN_NUM); i++) {
+               err = sum_mgr->get_sum(sum_mgr, &sum_desc, &sum);
+               if (err) {
+                       printk(KERN_ERR "Failed to get sum resources for "
+                                         "front output!\n");
+                       break;
+               }
+               mixer->sums[i] = sum;
+       }
+       if (err)
+               goto error1;
+
+       /* Allocate amixer resources for mixer obj */
+       amixer_mgr = (struct amixer_mgr *)mixer->atc->rsc_mgrs[AMIXER];
+       am_desc.msr = mixer->atc->msr;
+       for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) {
+               err = amixer_mgr->get_amixer(amixer_mgr, &am_desc, &amixer);
+               if (err) {
+                       printk(KERN_ERR "Failed to get amixer resources for "
+                                         "mixer obj!\n");
+                       break;
+               }
+               mixer->amixers[i] = amixer;
+       }
+       if (err)
+               goto error2;
+
+       return 0;
+
+error2:
+       for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) {
+               if (NULL != mixer->amixers[i]) {
+                       amixer = mixer->amixers[i];
+                       amixer_mgr->put_amixer(amixer_mgr, amixer);
+                       mixer->amixers[i] = NULL;
+               }
+       }
+error1:
+       for (i = 0; i < (NUM_CT_SUMS * CHN_NUM); i++) {
+               if (NULL != mixer->sums[i]) {
+                       sum_mgr->put_sum(sum_mgr, (struct sum *)mixer->sums[i]);
+                       mixer->sums[i] = NULL;
+               }
+       }
+
+       return err;
+}
+
+static int ct_mixer_get_mem(struct ct_mixer **rmixer)
+{
+       struct ct_mixer *mixer = NULL;
+       int err = 0;
+
+       *rmixer = NULL;
+       /* Allocate mem for mixer obj */
+       mixer = kzalloc(sizeof(*mixer), GFP_KERNEL);
+       if (NULL == mixer)
+               return -ENOMEM;
+
+       mixer->amixers = kzalloc(sizeof(void *)*(NUM_CT_AMIXERS*CHN_NUM),
+                                GFP_KERNEL);
+       if (NULL == mixer->amixers) {
+               err = -ENOMEM;
+               goto error1;
+       }
+       mixer->sums = kzalloc(sizeof(void *)*(NUM_CT_SUMS*CHN_NUM), GFP_KERNEL);
+       if (NULL == mixer->sums) {
+               err = -ENOMEM;
+               goto error2;
+       }
+
+       *rmixer = mixer;
+       return 0;
+
+error2:
+       kfree(mixer->amixers);
+error1:
+       kfree(mixer);
+       return err;
+}
+
+static int ct_mixer_topology_build(struct ct_mixer *mixer)
+{
+       struct sum *sum = NULL;
+       struct amixer *amix_d = NULL, *amix_s = NULL;
+       enum CT_AMIXER_CTL i = 0, j = 0;
+
+       /* Build topology from destination to source */
+
+       /* Set up Master mixer */
+       for (i = AMIXER_MASTER_F, j = SUM_IN_F;
+                                       i <= AMIXER_MASTER_S; i++, j++) {
+               amix_d = mixer->amixers[i*CHN_NUM];
+               sum = mixer->sums[j*CHN_NUM];
+               amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL);
+               amix_d = mixer->amixers[i*CHN_NUM+1];
+               sum = mixer->sums[j*CHN_NUM+1];
+               amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL);
+       }
+
+       /* Set up Wave-out mixer */
+       for (i = AMIXER_WAVE_F, j = AMIXER_MASTER_F;
+                                       i <= AMIXER_WAVE_S; i++, j++) {
+               amix_d = mixer->amixers[i*CHN_NUM];
+               amix_s = mixer->amixers[j*CHN_NUM];
+               amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL);
+               amix_d = mixer->amixers[i*CHN_NUM+1];
+               amix_s = mixer->amixers[j*CHN_NUM+1];
+               amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL);
+       }
+
+       /* Set up S/PDIF-out mixer */
+       amix_d = mixer->amixers[AMIXER_SPDIFO*CHN_NUM];
+       amix_s = mixer->amixers[AMIXER_MASTER_F*CHN_NUM];
+       amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL);
+       amix_d = mixer->amixers[AMIXER_SPDIFO*CHN_NUM+1];
+       amix_s = mixer->amixers[AMIXER_MASTER_F*CHN_NUM+1];
+       amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL);
+
+       /* Set up PCM-in mixer */
+       for (i = AMIXER_PCM_F, j = SUM_IN_F; i <= AMIXER_PCM_S; i++, j++) {
+               amix_d = mixer->amixers[i*CHN_NUM];
+               sum = mixer->sums[j*CHN_NUM];
+               amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+               amix_d = mixer->amixers[i*CHN_NUM+1];
+               sum = mixer->sums[j*CHN_NUM+1];
+               amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+       }
+
+       /* Set up Line-in mixer */
+       amix_d = mixer->amixers[AMIXER_LINEIN*CHN_NUM];
+       sum = mixer->sums[SUM_IN_F*CHN_NUM];
+       amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+       amix_d = mixer->amixers[AMIXER_LINEIN*CHN_NUM+1];
+       sum = mixer->sums[SUM_IN_F*CHN_NUM+1];
+       amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+
+       /* Set up Mic-in mixer */
+       amix_d = mixer->amixers[AMIXER_MIC*CHN_NUM];
+       sum = mixer->sums[SUM_IN_F*CHN_NUM];
+       amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+       amix_d = mixer->amixers[AMIXER_MIC*CHN_NUM+1];
+       sum = mixer->sums[SUM_IN_F*CHN_NUM+1];
+       amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+
+       /* Set up S/PDIF-in mixer */
+       amix_d = mixer->amixers[AMIXER_SPDIFI*CHN_NUM];
+       sum = mixer->sums[SUM_IN_F*CHN_NUM];
+       amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+       amix_d = mixer->amixers[AMIXER_SPDIFI*CHN_NUM+1];
+       sum = mixer->sums[SUM_IN_F*CHN_NUM+1];
+       amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+
+       /* Set up Master recording mixer */
+       amix_d = mixer->amixers[AMIXER_MASTER_F_C*CHN_NUM];
+       sum = mixer->sums[SUM_IN_F_C*CHN_NUM];
+       amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL);
+       amix_d = mixer->amixers[AMIXER_MASTER_F_C*CHN_NUM+1];
+       sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1];
+       amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL);
+
+       /* Set up PCM-in recording mixer */
+       amix_d = mixer->amixers[AMIXER_PCM_F_C*CHN_NUM];
+       sum = mixer->sums[SUM_IN_F_C*CHN_NUM];
+       amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+       amix_d = mixer->amixers[AMIXER_PCM_F_C*CHN_NUM+1];
+       sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1];
+       amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+
+       /* Set up Line-in recording mixer */
+       amix_d = mixer->amixers[AMIXER_LINEIN_C*CHN_NUM];
+       sum = mixer->sums[SUM_IN_F_C*CHN_NUM];
+       amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+       amix_d = mixer->amixers[AMIXER_LINEIN_C*CHN_NUM+1];
+       sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1];
+       amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+
+       /* Set up Mic-in recording mixer */
+       amix_d = mixer->amixers[AMIXER_MIC_C*CHN_NUM];
+       sum = mixer->sums[SUM_IN_F_C*CHN_NUM];
+       amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+       amix_d = mixer->amixers[AMIXER_MIC_C*CHN_NUM+1];
+       sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1];
+       amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+
+       /* Set up S/PDIF-in recording mixer */
+       amix_d = mixer->amixers[AMIXER_SPDIFI_C*CHN_NUM];
+       sum = mixer->sums[SUM_IN_F_C*CHN_NUM];
+       amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+       amix_d = mixer->amixers[AMIXER_SPDIFI_C*CHN_NUM+1];
+       sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1];
+       amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+
+       return 0;
+}
+
+static int mixer_set_input_port(struct amixer *amixer, struct rsc *rsc)
+{
+       amixer->ops->set_input(amixer, rsc);
+       amixer->ops->commit_write(amixer);
+
+       return 0;
+}
+
+static enum CT_AMIXER_CTL port_to_amixer(enum MIXER_PORT_T type)
+{
+       switch (type) {
+       case MIX_WAVE_FRONT:    return AMIXER_WAVE_F;
+       case MIX_WAVE_SURROUND: return AMIXER_WAVE_S;
+       case MIX_WAVE_CENTLFE:  return AMIXER_WAVE_C;
+       case MIX_WAVE_REAR:     return AMIXER_WAVE_R;
+       case MIX_PCMO_FRONT:    return AMIXER_MASTER_F_C;
+       case MIX_SPDIF_OUT:     return AMIXER_SPDIFO;
+       case MIX_LINE_IN:       return AMIXER_LINEIN;
+       case MIX_MIC_IN:        return AMIXER_MIC;
+       case MIX_SPDIF_IN:      return AMIXER_SPDIFI;
+       case MIX_PCMI_FRONT:    return AMIXER_PCM_F;
+       case MIX_PCMI_SURROUND: return AMIXER_PCM_S;
+       case MIX_PCMI_CENTLFE:  return AMIXER_PCM_C;
+       case MIX_PCMI_REAR:     return AMIXER_PCM_R;
+       default:                return 0;
+       }
+}
+
+static int mixer_get_output_ports(struct ct_mixer *mixer,
+                                 enum MIXER_PORT_T type,
+                                 struct rsc **rleft, struct rsc **rright)
+{
+       enum CT_AMIXER_CTL amix = port_to_amixer(type);
+
+       if (NULL != rleft)
+               *rleft = &((struct amixer *)mixer->amixers[amix*CHN_NUM])->rsc;
+
+       if (NULL != rright)
+               *rright =
+                       &((struct amixer *)mixer->amixers[amix*CHN_NUM+1])->rsc;
+
+       return 0;
+}
+
+static int mixer_set_input_left(struct ct_mixer *mixer,
+                               enum MIXER_PORT_T type, struct rsc *rsc)
+{
+       enum CT_AMIXER_CTL amix = port_to_amixer(type);
+
+       mixer_set_input_port(mixer->amixers[amix*CHN_NUM], rsc);
+       amix = get_recording_amixer(amix);
+       if (amix < NUM_CT_AMIXERS)
+               mixer_set_input_port(mixer->amixers[amix*CHN_NUM], rsc);
+
+       return 0;
+}
+
+static int
+mixer_set_input_right(struct ct_mixer *mixer,
+                     enum MIXER_PORT_T type, struct rsc *rsc)
+{
+       enum CT_AMIXER_CTL amix = port_to_amixer(type);
+
+       mixer_set_input_port(mixer->amixers[amix*CHN_NUM+1], rsc);
+       amix = get_recording_amixer(amix);
+       if (amix < NUM_CT_AMIXERS)
+               mixer_set_input_port(mixer->amixers[amix*CHN_NUM+1], rsc);
+
+       return 0;
+}
+
+int ct_mixer_destroy(struct ct_mixer *mixer)
+{
+       struct sum_mgr *sum_mgr = (struct sum_mgr *)mixer->atc->rsc_mgrs[SUM];
+       struct amixer_mgr *amixer_mgr =
+                       (struct amixer_mgr *)mixer->atc->rsc_mgrs[AMIXER];
+       struct amixer *amixer = NULL;
+       int i = 0;
+
+       /* Release amixer resources */
+       for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) {
+               if (NULL != mixer->amixers[i]) {
+                       amixer = mixer->amixers[i];
+                       amixer_mgr->put_amixer(amixer_mgr, amixer);
+               }
+       }
+
+       /* Release sum resources */
+       for (i = 0; i < (NUM_CT_SUMS * CHN_NUM); i++) {
+               if (NULL != mixer->sums[i])
+                       sum_mgr->put_sum(sum_mgr, (struct sum *)mixer->sums[i]);
+       }
+
+       /* Release mem assigned to mixer object */
+       kfree(mixer->sums);
+       kfree(mixer->amixers);
+       kfree(mixer);
+
+       return 0;
+}
+
+int ct_mixer_create(struct ct_atc *atc, struct ct_mixer **rmixer)
+{
+       struct ct_mixer *mixer = NULL;
+       int err = 0;
+
+       *rmixer = NULL;
+
+       /* Allocate mem for mixer obj */
+       err = ct_mixer_get_mem(&mixer);
+       if (err)
+               return err;
+
+       mixer->switch_state = 0;
+       mixer->atc = atc;
+       /* Set operations */
+       mixer->get_output_ports = mixer_get_output_ports;
+       mixer->set_input_left = mixer_set_input_left;
+       mixer->set_input_right = mixer_set_input_right;
+
+       /* Allocate chip resources for mixer obj */
+       err = ct_mixer_get_resources(mixer);
+       if (err)
+               goto error;
+
+       /* Build internal mixer topology */
+       ct_mixer_topology_build(mixer);
+
+       *rmixer = mixer;
+
+       return 0;
+
+error:
+       ct_mixer_destroy(mixer);
+       return err;
+}
+
+int ct_alsa_mix_create(struct ct_atc *atc,
+                      enum CTALSADEVS device,
+                      const char *device_name)
+{
+       int err = 0;
+
+       /* Create snd kcontrol instances on demand */
+       vol_ctl.device = swh_ctl.device = device;
+       err = ct_mixer_kcontrols_create((struct ct_mixer *)atc->mixer);
+       if (err)
+               return err;
+
+       strcpy(atc->card->mixername, device_name);
+
+       return 0;
+}
diff --git a/sound/pci/ctxfi/ctmixer.h b/sound/pci/ctxfi/ctmixer.h
new file mode 100644 (file)
index 0000000..e2d96eb
--- /dev/null
@@ -0,0 +1,67 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       ctmixer.h
+ *
+ * @Brief
+ * This file contains the definition of the mixer device functions.
+ *
+ * @Author     Liu Chun
+ * @Date       Mar 28 2008
+ *
+ */
+
+#ifndef CTMIXER_H
+#define CTMIXER_H
+
+#include "ctatc.h"
+#include "ctresource.h"
+
+#define INIT_VOL       0x1c00
+
+enum MIXER_PORT_T {
+       MIX_WAVE_FRONT,
+       MIX_WAVE_REAR,
+       MIX_WAVE_CENTLFE,
+       MIX_WAVE_SURROUND,
+       MIX_SPDIF_OUT,
+       MIX_PCMO_FRONT,
+       MIX_MIC_IN,
+       MIX_LINE_IN,
+       MIX_SPDIF_IN,
+       MIX_PCMI_FRONT,
+       MIX_PCMI_REAR,
+       MIX_PCMI_CENTLFE,
+       MIX_PCMI_SURROUND,
+
+       NUM_MIX_PORTS
+};
+
+/* alsa mixer descriptor */
+struct ct_mixer {
+       struct ct_atc *atc;
+
+       void **amixers;         /* amixer resources for volume control */
+       void **sums;            /* sum resources for signal collection */
+       unsigned int switch_state; /* A bit-map to indicate state of switches */
+
+       int (*get_output_ports)(struct ct_mixer *mixer, enum MIXER_PORT_T type,
+                                 struct rsc **rleft, struct rsc **rright);
+
+       int (*set_input_left)(struct ct_mixer *mixer,
+                             enum MIXER_PORT_T type, struct rsc *rsc);
+       int (*set_input_right)(struct ct_mixer *mixer,
+                              enum MIXER_PORT_T type, struct rsc *rsc);
+};
+
+int ct_alsa_mix_create(struct ct_atc *atc,
+                      enum CTALSADEVS device,
+                      const char *device_name);
+int ct_mixer_create(struct ct_atc *atc, struct ct_mixer **rmixer);
+int ct_mixer_destroy(struct ct_mixer *mixer);
+
+#endif /* CTMIXER_H */
diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c
new file mode 100644 (file)
index 0000000..73d4fdb
--- /dev/null
@@ -0,0 +1,499 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       ctpcm.c
+ *
+ * @Brief
+ * This file contains the definition of the pcm device functions.
+ *
+ * @Author     Liu Chun
+ * @Date       Apr 2 2008
+ *
+ */
+
+#include "ctpcm.h"
+#include <sound/pcm.h>
+
+/* Hardware descriptions for playback */
+static struct snd_pcm_hardware ct_pcm_playback_hw = {
+       .info                   = (SNDRV_PCM_INFO_MMAP |
+                                  SNDRV_PCM_INFO_INTERLEAVED |
+                                  SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                  SNDRV_PCM_INFO_MMAP_VALID |
+                                  SNDRV_PCM_INFO_PAUSE),
+       .formats                = (SNDRV_PCM_FMTBIT_U8 |
+                                  SNDRV_PCM_FMTBIT_S8 |
+                                  SNDRV_PCM_FMTBIT_S16_LE |
+                                  SNDRV_PCM_FMTBIT_U16_LE |
+                                  SNDRV_PCM_FMTBIT_S24_3LE |
+                                  SNDRV_PCM_FMTBIT_S24_LE |
+                                  SNDRV_PCM_FMTBIT_S32_LE),
+       .rates                  = (SNDRV_PCM_RATE_CONTINUOUS |
+                                  SNDRV_PCM_RATE_8000_192000),
+       .rate_min               = 8000,
+       .rate_max               = 192000,
+       .channels_min           = 1,
+       .channels_max           = 2,
+       .buffer_bytes_max       = (128*1024),
+       .period_bytes_min       = (64),
+       .period_bytes_max       = (128*1024),
+       .periods_min            = 1,
+       .periods_max            = 1024,
+       .fifo_size              = 0,
+};
+
+static struct snd_pcm_hardware ct_spdif_passthru_playback_hw = {
+       .info                   = (SNDRV_PCM_INFO_MMAP |
+                                  SNDRV_PCM_INFO_INTERLEAVED |
+                                  SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                  SNDRV_PCM_INFO_MMAP_VALID |
+                                  SNDRV_PCM_INFO_PAUSE),
+       .formats                = (SNDRV_PCM_FMTBIT_S16_LE |
+                                  SNDRV_PCM_FMTBIT_U16_LE),
+       .rates                  = (SNDRV_PCM_RATE_48000 |
+                                  SNDRV_PCM_RATE_44100 |
+                                  SNDRV_PCM_RATE_32000),
+       .rate_min               = 32000,
+       .rate_max               = 48000,
+       .channels_min           = 2,
+       .channels_max           = 2,
+       .buffer_bytes_max       = (128*1024),
+       .period_bytes_min       = (64),
+       .period_bytes_max       = (128*1024),
+       .periods_min            = 1,
+       .periods_max            = 1024,
+       .fifo_size              = 0,
+};
+
+/* Hardware descriptions for capture */
+static struct snd_pcm_hardware ct_pcm_capture_hw = {
+       .info                   = (SNDRV_PCM_INFO_MMAP |
+                                  SNDRV_PCM_INFO_INTERLEAVED |
+                                  SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                                  SNDRV_PCM_INFO_PAUSE |
+                                  SNDRV_PCM_INFO_MMAP_VALID),
+       .formats                = (SNDRV_PCM_FMTBIT_U8 |
+                                  SNDRV_PCM_FMTBIT_S8 |
+                                  SNDRV_PCM_FMTBIT_S16_LE |
+                                  SNDRV_PCM_FMTBIT_U16_LE |
+                                  SNDRV_PCM_FMTBIT_S24_3LE |
+                                  SNDRV_PCM_FMTBIT_S24_LE |
+                                  SNDRV_PCM_FMTBIT_S32_LE),
+       .rates                  = (SNDRV_PCM_RATE_CONTINUOUS |
+                                  SNDRV_PCM_RATE_8000_96000),
+       .rate_min               = 8000,
+       .rate_max               = 96000,
+       .channels_min           = 1,
+       .channels_max           = 2,
+       .buffer_bytes_max       = (128*1024),
+       .period_bytes_min       = (384),
+       .period_bytes_max       = (64*1024),
+       .periods_min            = 2,
+       .periods_max            = 1024,
+       .fifo_size              = 0,
+};
+
+static void ct_atc_pcm_interrupt(struct ct_atc_pcm *atc_pcm)
+{
+       struct ct_atc_pcm *apcm = atc_pcm;
+
+       if (NULL == apcm->substream)
+               return;
+
+       snd_pcm_period_elapsed(apcm->substream);
+}
+
+static void ct_atc_pcm_free_substream(struct snd_pcm_runtime *runtime)
+{
+       struct ct_atc_pcm *apcm = runtime->private_data;
+       struct ct_atc *atc = snd_pcm_substream_chip(apcm->substream);
+
+       atc->pcm_release_resources(atc, apcm);
+       kfree(apcm);
+       runtime->private_data = NULL;
+}
+
+/* pcm playback operations */
+static int ct_pcm_playback_open(struct snd_pcm_substream *substream)
+{
+       struct ct_atc *atc = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct ct_atc_pcm *apcm;
+       int err;
+
+       apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
+       if (NULL == apcm)
+               return -ENOMEM;
+
+       spin_lock_init(&apcm->timer_lock);
+       apcm->stop_timer = 0;
+       apcm->substream = substream;
+       apcm->interrupt = ct_atc_pcm_interrupt;
+       runtime->private_data = apcm;
+       runtime->private_free = ct_atc_pcm_free_substream;
+       if (IEC958 == substream->pcm->device) {
+               runtime->hw = ct_spdif_passthru_playback_hw;
+               atc->spdif_out_passthru(atc, 1);
+       } else {
+               runtime->hw = ct_pcm_playback_hw;
+               if (FRONT == substream->pcm->device)
+                       runtime->hw.channels_max = 8;
+       }
+
+       err = snd_pcm_hw_constraint_integer(runtime,
+                                           SNDRV_PCM_HW_PARAM_PERIODS);
+       if (err < 0) {
+               kfree(apcm);
+               return err;
+       }
+       err = snd_pcm_hw_constraint_minmax(runtime,
+                                          SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+                                          1024, UINT_MAX);
+       if (err < 0) {
+               kfree(apcm);
+               return err;
+       }
+
+       return 0;
+}
+
+static int ct_pcm_playback_close(struct snd_pcm_substream *substream)
+{
+       struct ct_atc *atc = snd_pcm_substream_chip(substream);
+
+       /* TODO: Notify mixer inactive. */
+       if (IEC958 == substream->pcm->device)
+               atc->spdif_out_passthru(atc, 0);
+
+       /* The ct_atc_pcm object will be freed by runtime->private_free */
+
+       return 0;
+}
+
+static int ct_pcm_hw_params(struct snd_pcm_substream *substream,
+                                    struct snd_pcm_hw_params *hw_params)
+{
+       return snd_pcm_lib_malloc_pages(substream,
+                                       params_buffer_bytes(hw_params));
+}
+
+static int ct_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+       /* Free snd-allocated pages */
+       return snd_pcm_lib_free_pages(substream);
+}
+
+static void ct_pcm_timer_callback(unsigned long data)
+{
+       struct ct_atc_pcm *apcm = (struct ct_atc_pcm *)data;
+       struct snd_pcm_substream *substream = apcm->substream;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       unsigned int period_size = runtime->period_size;
+       unsigned int buffer_size = runtime->buffer_size;
+       unsigned long flags;
+       unsigned int position = 0, dist = 0, interval = 0;
+
+       position = substream->ops->pointer(substream);
+       dist = (position + buffer_size - apcm->position) % buffer_size;
+       if ((dist >= period_size) ||
+               (position/period_size != apcm->position/period_size)) {
+               apcm->interrupt(apcm);
+               apcm->position = position;
+       }
+       /* Add extra HZ*5/1000 to avoid overrun issue when recording
+        * at 8kHz in 8-bit format or at 88kHz in 24-bit format. */
+       interval = ((period_size - (position % period_size))
+                  * HZ + (runtime->rate - 1)) / runtime->rate + HZ * 5 / 1000;
+       spin_lock_irqsave(&apcm->timer_lock, flags);
+       apcm->timer.expires = jiffies + interval;
+       if (!apcm->stop_timer)
+               add_timer(&apcm->timer);
+
+       spin_unlock_irqrestore(&apcm->timer_lock, flags);
+}
+
+static int ct_pcm_timer_prepare(struct ct_atc_pcm *apcm)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&apcm->timer_lock, flags);
+       if (timer_pending(&apcm->timer)) {
+               /* The timer has already been started. */
+               spin_unlock_irqrestore(&apcm->timer_lock, flags);
+               return 0;
+       }
+
+       init_timer(&apcm->timer);
+       apcm->timer.data = (unsigned long)apcm;
+       apcm->timer.function = ct_pcm_timer_callback;
+       spin_unlock_irqrestore(&apcm->timer_lock, flags);
+       apcm->position = 0;
+
+       return 0;
+}
+
+static int ct_pcm_timer_start(struct ct_atc_pcm *apcm)
+{
+       struct snd_pcm_runtime *runtime = apcm->substream->runtime;
+       unsigned long flags;
+
+       spin_lock_irqsave(&apcm->timer_lock, flags);
+       if (timer_pending(&apcm->timer)) {
+               /* The timer has already been started. */
+               spin_unlock_irqrestore(&apcm->timer_lock, flags);
+               return 0;
+       }
+
+       apcm->timer.expires = jiffies + (runtime->period_size * HZ +
+                               (runtime->rate - 1)) / runtime->rate;
+       apcm->stop_timer = 0;
+       add_timer(&apcm->timer);
+       spin_unlock_irqrestore(&apcm->timer_lock, flags);
+
+       return 0;
+}
+
+static int ct_pcm_timer_stop(struct ct_atc_pcm *apcm)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&apcm->timer_lock, flags);
+       apcm->stop_timer = 1;
+       del_timer(&apcm->timer);
+       spin_unlock_irqrestore(&apcm->timer_lock, flags);
+
+       try_to_del_timer_sync(&apcm->timer);
+
+       return 0;
+}
+
+static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+       int err;
+       struct ct_atc *atc = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct ct_atc_pcm *apcm = runtime->private_data;
+
+       if (IEC958 == substream->pcm->device)
+               err = atc->spdif_passthru_playback_prepare(atc, apcm);
+       else
+               err = atc->pcm_playback_prepare(atc, apcm);
+
+       if (err < 0) {
+               printk(KERN_ERR "Preparing pcm playback failed!!!\n");
+               return err;
+       }
+
+       ct_pcm_timer_prepare(apcm);
+
+       return 0;
+}
+
+static int
+ct_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct ct_atc *atc = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct ct_atc_pcm *apcm = runtime->private_data;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               atc->pcm_playback_start(atc, apcm);
+               ct_pcm_timer_start(apcm);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               ct_pcm_timer_stop(apcm);
+               atc->pcm_playback_stop(atc, apcm);
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static snd_pcm_uframes_t
+ct_pcm_playback_pointer(struct snd_pcm_substream *substream)
+{
+       unsigned long position;
+       struct ct_atc *atc = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct ct_atc_pcm *apcm = runtime->private_data;
+
+       /* Read out playback position */
+       position = atc->pcm_playback_position(atc, apcm);
+       position = bytes_to_frames(runtime, position);
+       return position;
+}
+
+/* pcm capture operations */
+static int ct_pcm_capture_open(struct snd_pcm_substream *substream)
+{
+       struct ct_atc *atc = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct ct_atc_pcm *apcm;
+       int err;
+
+       apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
+       if (NULL == apcm)
+               return -ENOMEM;
+
+       spin_lock_init(&apcm->timer_lock);
+       apcm->started = 0;
+       apcm->stop_timer = 0;
+       apcm->substream = substream;
+       apcm->interrupt = ct_atc_pcm_interrupt;
+       runtime->private_data = apcm;
+       runtime->private_free = ct_atc_pcm_free_substream;
+       runtime->hw = ct_pcm_capture_hw;
+       runtime->hw.rate_max = atc->rsr * atc->msr;
+
+       err = snd_pcm_hw_constraint_integer(runtime,
+                                           SNDRV_PCM_HW_PARAM_PERIODS);
+       if (err < 0) {
+               kfree(apcm);
+               return err;
+       }
+       err = snd_pcm_hw_constraint_minmax(runtime,
+                                          SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+                                          1024, UINT_MAX);
+       if (err < 0) {
+               kfree(apcm);
+               return err;
+       }
+
+       return 0;
+}
+
+static int ct_pcm_capture_close(struct snd_pcm_substream *substream)
+{
+       /* The ct_atc_pcm object will be freed by runtime->private_free */
+       /* TODO: Notify mixer inactive. */
+       return 0;
+}
+
+static int ct_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+       int err;
+       struct ct_atc *atc = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct ct_atc_pcm *apcm = runtime->private_data;
+
+       err = atc->pcm_capture_prepare(atc, apcm);
+       if (err < 0) {
+               printk(KERN_ERR "Preparing pcm capture failed!!!\n");
+               return err;
+       }
+
+       ct_pcm_timer_prepare(apcm);
+
+       return 0;
+}
+
+static int
+ct_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct ct_atc *atc = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct ct_atc_pcm *apcm = runtime->private_data;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               atc->pcm_capture_start(atc, apcm);
+               ct_pcm_timer_start(apcm);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               ct_pcm_timer_stop(apcm);
+               atc->pcm_capture_stop(atc, apcm);
+               break;
+       default:
+               ct_pcm_timer_stop(apcm);
+               atc->pcm_capture_stop(atc, apcm);
+               break;
+       }
+
+       return 0;
+}
+
+static snd_pcm_uframes_t
+ct_pcm_capture_pointer(struct snd_pcm_substream *substream)
+{
+       unsigned long position;
+       struct ct_atc *atc = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct ct_atc_pcm *apcm = runtime->private_data;
+
+       /* Read out playback position */
+       position = atc->pcm_capture_position(atc, apcm);
+       position = bytes_to_frames(runtime, position);
+       return position;
+}
+
+/* PCM operators for playback */
+static struct snd_pcm_ops ct_pcm_playback_ops = {
+       .open           = ct_pcm_playback_open,
+       .close          = ct_pcm_playback_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = ct_pcm_hw_params,
+       .hw_free        = ct_pcm_hw_free,
+       .prepare        = ct_pcm_playback_prepare,
+       .trigger        = ct_pcm_playback_trigger,
+       .pointer        = ct_pcm_playback_pointer,
+};
+
+/* PCM operators for capture */
+static struct snd_pcm_ops ct_pcm_capture_ops = {
+       .open           = ct_pcm_capture_open,
+       .close          = ct_pcm_capture_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = ct_pcm_hw_params,
+       .hw_free        = ct_pcm_hw_free,
+       .prepare        = ct_pcm_capture_prepare,
+       .trigger        = ct_pcm_capture_trigger,
+       .pointer        = ct_pcm_capture_pointer,
+};
+
+/* Create ALSA pcm device */
+int ct_alsa_pcm_create(struct ct_atc *atc,
+                      enum CTALSADEVS device,
+                      const char *device_name)
+{
+       struct snd_pcm *pcm;
+       int err;
+       int playback_count, capture_count;
+       char name[128];
+
+       strncpy(name, device_name, sizeof(name));
+       playback_count = (IEC958 == device) ? 1 : 8;
+       capture_count = (FRONT == device) ? 1 : 0;
+       err = snd_pcm_new(atc->card, name, device,
+                         playback_count, capture_count, &pcm);
+       if (err < 0) {
+               printk(KERN_ERR "snd_pcm_new failed!! Err=%d\n", err);
+               return err;
+       }
+
+       pcm->private_data = atc;
+       pcm->info_flags = 0;
+       pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
+       strcpy(pcm->name, device_name);
+
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ct_pcm_playback_ops);
+
+       if (FRONT == device)
+               snd_pcm_set_ops(pcm,
+                               SNDRV_PCM_STREAM_CAPTURE, &ct_pcm_capture_ops);
+
+       snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+                       snd_dma_pci_data(atc->pci), 128*1024, 128*1024);
+
+       return 0;
+}
diff --git a/sound/pci/ctxfi/ctpcm.h b/sound/pci/ctxfi/ctpcm.h
new file mode 100644 (file)
index 0000000..178da0d
--- /dev/null
@@ -0,0 +1,27 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       ctpcm.h
+ *
+ * @Brief
+ * This file contains the definition of the pcm device functions.
+ *
+ * @Author     Liu Chun
+ * @Date       Mar 28 2008
+ *
+ */
+
+#ifndef CTPCM_H
+#define CTPCM_H
+
+#include "ctatc.h"
+
+int ct_alsa_pcm_create(struct ct_atc *atc,
+                      enum CTALSADEVS device,
+                      const char *device_name);
+
+#endif /* CTPCM_H */
diff --git a/sound/pci/ctxfi/ctresource.c b/sound/pci/ctxfi/ctresource.c
new file mode 100644 (file)
index 0000000..414fc23
--- /dev/null
@@ -0,0 +1,297 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       ctresource.c
+ *
+ * @Brief
+ * This file contains the implementation of some generic helper functions.
+ *
+ * @Author     Liu Chun
+ * @Date       May 15 2008
+ *
+ */
+
+#include "ctresource.h"
+#include "cthardware.h"
+#include <linux/err.h>
+#include <linux/slab.h>
+
+#define AUDIO_SLOT_BLOCK_NUM   256
+
+/* Resource allocation based on bit-map management mechanism */
+static int
+get_resource(u8 *rscs, unsigned int amount,
+            unsigned int multi, unsigned int *ridx)
+{
+       int i = 0, j = 0, k = 0, n = 0;
+
+       /* Check whether there are sufficient resources to meet request. */
+       for (i = 0, n = multi; i < amount; i++) {
+               j = i / 8;
+               k = i % 8;
+               if (rscs[j] & ((u8)1 << k)) {
+                       n = multi;
+                       continue;
+               }
+               if (!(--n))
+                       break; /* found sufficient contiguous resources */
+       }
+
+       if (i >= amount) {
+               /* Can not find sufficient contiguous resources */
+               return -ENOENT;
+       }
+
+       /* Mark the contiguous bits in resource bit-map as used */
+       for (n = multi; n > 0; n--) {
+               j = i / 8;
+               k = i % 8;
+               rscs[j] |= ((u8)1 << k);
+               i--;
+       }
+
+       *ridx = i + 1;
+
+       return 0;
+}
+
+static int put_resource(u8 *rscs, unsigned int multi, unsigned int idx)
+{
+       unsigned int i = 0, j = 0, k = 0, n = 0;
+
+       /* Mark the contiguous bits in resource bit-map as used */
+       for (n = multi, i = idx; n > 0; n--) {
+               j = i / 8;
+               k = i % 8;
+               rscs[j] &= ~((u8)1 << k);
+               i++;
+       }
+
+       return 0;
+}
+
+int mgr_get_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int *ridx)
+{
+       int err = 0;
+
+       if (n > mgr->avail)
+               return -ENOENT;
+
+       err = get_resource(mgr->rscs, mgr->amount, n, ridx);
+       if (!err)
+               mgr->avail -= n;
+
+       return err;
+}
+
+int mgr_put_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int idx)
+{
+       put_resource(mgr->rscs, n, idx);
+       mgr->avail += n;
+
+       return 0;
+}
+
+static unsigned char offset_in_audio_slot_block[NUM_RSCTYP] = {
+       /* SRC channel is at Audio Ring slot 1 every 16 slots. */
+       [SRC]           = 0x1,
+       [AMIXER]        = 0x4,
+       [SUM]           = 0xc,
+};
+
+static int rsc_index(const struct rsc *rsc)
+{
+    return rsc->conj;
+}
+
+static int audio_ring_slot(const struct rsc *rsc)
+{
+    return (rsc->conj << 4) + offset_in_audio_slot_block[rsc->type];
+}
+
+static int rsc_next_conj(struct rsc *rsc)
+{
+       unsigned int i;
+       for (i = 0; (i < 8) && (!(rsc->msr & (0x1 << i))); )
+               i++;
+       rsc->conj += (AUDIO_SLOT_BLOCK_NUM >> i);
+       return rsc->conj;
+}
+
+static int rsc_master(struct rsc *rsc)
+{
+       return rsc->conj = rsc->idx;
+}
+
+static struct rsc_ops rsc_generic_ops = {
+       .index          = rsc_index,
+       .output_slot    = audio_ring_slot,
+       .master         = rsc_master,
+       .next_conj      = rsc_next_conj,
+};
+
+int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw)
+{
+       int err = 0;
+
+       rsc->idx = idx;
+       rsc->conj = idx;
+       rsc->type = type;
+       rsc->msr = msr;
+       rsc->hw = hw;
+       rsc->ops = &rsc_generic_ops;
+       if (NULL == hw) {
+               rsc->ctrl_blk = NULL;
+               return 0;
+       }
+
+       switch (type) {
+       case SRC:
+               err = ((struct hw *)hw)->src_rsc_get_ctrl_blk(&rsc->ctrl_blk);
+               break;
+       case AMIXER:
+               err = ((struct hw *)hw)->
+                               amixer_rsc_get_ctrl_blk(&rsc->ctrl_blk);
+               break;
+       case SRCIMP:
+       case SUM:
+       case DAIO:
+               break;
+       default:
+               printk(KERN_ERR "Invalid resource type value %d!\n", type);
+               return -EINVAL;
+       }
+
+       if (err) {
+               printk(KERN_ERR "Failed to get resource control block!\n");
+               return err;
+       }
+
+       return 0;
+}
+
+int rsc_uninit(struct rsc *rsc)
+{
+       if ((NULL != rsc->hw) && (NULL != rsc->ctrl_blk)) {
+               switch (rsc->type) {
+               case SRC:
+                       ((struct hw *)rsc->hw)->
+                               src_rsc_put_ctrl_blk(rsc->ctrl_blk);
+                       break;
+               case AMIXER:
+                       ((struct hw *)rsc->hw)->
+                               amixer_rsc_put_ctrl_blk(rsc->ctrl_blk);
+                       break;
+               case SUM:
+               case DAIO:
+                       break;
+               default:
+                       printk(KERN_ERR "Invalid resource type value %d!\n",
+                                         rsc->type);
+                       break;
+               }
+
+               rsc->hw = rsc->ctrl_blk = NULL;
+       }
+
+       rsc->idx = rsc->conj = 0;
+       rsc->type = NUM_RSCTYP;
+       rsc->msr = 0;
+
+       return 0;
+}
+
+int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type,
+                unsigned int amount, void *hw_obj)
+{
+       int err = 0;
+       struct hw *hw = hw_obj;
+
+       mgr->type = NUM_RSCTYP;
+
+       mgr->rscs = kzalloc(((amount + 8 - 1) / 8), GFP_KERNEL);
+       if (NULL == mgr->rscs)
+               return -ENOMEM;
+
+       switch (type) {
+       case SRC:
+               err = hw->src_mgr_get_ctrl_blk(&mgr->ctrl_blk);
+               break;
+       case SRCIMP:
+               err = hw->srcimp_mgr_get_ctrl_blk(&mgr->ctrl_blk);
+               break;
+       case AMIXER:
+               err = hw->amixer_mgr_get_ctrl_blk(&mgr->ctrl_blk);
+               break;
+       case DAIO:
+               err = hw->daio_mgr_get_ctrl_blk(hw, &mgr->ctrl_blk);
+               break;
+       case SUM:
+               break;
+       default:
+               printk(KERN_ERR "Invalid resource type value %d!\n", type);
+               err = -EINVAL;
+               goto error;
+       }
+
+       if (err) {
+               printk(KERN_ERR "Failed to get manager control block!\n");
+               goto error;
+       }
+
+       mgr->type = type;
+       mgr->avail = mgr->amount = amount;
+       mgr->hw = hw;
+
+       return 0;
+
+error:
+       kfree(mgr->rscs);
+       return err;
+}
+
+int rsc_mgr_uninit(struct rsc_mgr *mgr)
+{
+       if (NULL != mgr->rscs) {
+               kfree(mgr->rscs);
+               mgr->rscs = NULL;
+       }
+
+       if ((NULL != mgr->hw) && (NULL != mgr->ctrl_blk)) {
+               switch (mgr->type) {
+               case SRC:
+                       ((struct hw *)mgr->hw)->
+                               src_mgr_put_ctrl_blk(mgr->ctrl_blk);
+                       break;
+               case SRCIMP:
+                       ((struct hw *)mgr->hw)->
+                               srcimp_mgr_put_ctrl_blk(mgr->ctrl_blk);
+                       break;
+               case AMIXER:
+                       ((struct hw *)mgr->hw)->
+                               amixer_mgr_put_ctrl_blk(mgr->ctrl_blk);
+                       break;
+               case DAIO:
+                       ((struct hw *)mgr->hw)->
+                               daio_mgr_put_ctrl_blk(mgr->ctrl_blk);
+                       break;
+               case SUM:
+                       break;
+               default:
+                       printk(KERN_ERR "Invalid resource type value %d!\n",
+                                         mgr->type);
+                       break;
+               }
+
+               mgr->hw = mgr->ctrl_blk = NULL;
+       }
+
+       mgr->type = NUM_RSCTYP;
+       mgr->avail = mgr->amount = 0;
+
+       return 0;
+}
diff --git a/sound/pci/ctxfi/ctresource.h b/sound/pci/ctxfi/ctresource.h
new file mode 100644 (file)
index 0000000..0838c2e
--- /dev/null
@@ -0,0 +1,72 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       ctresource.h
+ *
+ * @Brief
+ * This file contains the definition of generic hardware resources for
+ * resource management.
+ *
+ * @Author     Liu Chun
+ * @Date       May 13 2008
+ *
+ */
+
+#ifndef CTRESOURCE_H
+#define CTRESOURCE_H
+
+#include <linux/types.h>
+
+enum RSCTYP {
+       SRC,
+       SRCIMP,
+       AMIXER,
+       SUM,
+       DAIO,
+       NUM_RSCTYP      /* This must be the last one and less than 16 */
+};
+
+struct rsc_ops;
+
+struct rsc {
+       u32 idx:12;     /* The index of a resource */
+       u32 type:4;     /* The type (RSCTYP) of a resource */
+       u32 conj:12;    /* Current conjugate index */
+       u32 msr:4;      /* The Master Sample Rate a resource working on */
+       void *ctrl_blk; /* Chip specific control info block for a resource */
+       void *hw;       /* Chip specific object for hardware access means */
+       struct rsc_ops *ops;    /* Generic resource operations */
+};
+
+struct rsc_ops {
+       int (*master)(struct rsc *rsc); /* Move to master resource */
+       int (*next_conj)(struct rsc *rsc); /* Move to next conjugate resource */
+       int (*index)(const struct rsc *rsc); /* Return the index of resource */
+       /* Return the output slot number */
+       int (*output_slot)(const struct rsc *rsc);
+};
+
+int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw);
+int rsc_uninit(struct rsc *rsc);
+
+struct rsc_mgr {
+       enum RSCTYP type; /* The type (RSCTYP) of resource to manage */
+       unsigned int amount; /* The total amount of a kind of resource */
+       unsigned int avail; /* The amount of currently available resources */
+       unsigned char *rscs; /* The bit-map for resource allocation */
+       void *ctrl_blk; /* Chip specific control info block */
+       void *hw; /* Chip specific object for hardware access */
+};
+
+/* Resource management is based on bit-map mechanism */
+int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type,
+                unsigned int amount, void *hw);
+int rsc_mgr_uninit(struct rsc_mgr *mgr);
+int mgr_get_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int *ridx);
+int mgr_put_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int idx);
+
+#endif /* CTRESOURCE_H */
diff --git a/sound/pci/ctxfi/ctsrc.c b/sound/pci/ctxfi/ctsrc.c
new file mode 100644 (file)
index 0000000..d3e0ad5
--- /dev/null
@@ -0,0 +1,886 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       ctsrc.c
+ *
+ * @Brief
+ * This file contains the implementation of the Sample Rate Convertor
+ * resource management object.
+ *
+ * @Author     Liu Chun
+ * @Date       May 13 2008
+ *
+ */
+
+#include "ctsrc.h"
+#include "cthardware.h"
+#include <linux/slab.h>
+
+#define SRC_RESOURCE_NUM       64
+#define SRCIMP_RESOURCE_NUM    256
+
+static unsigned int conj_mask;
+
+static int src_default_config_memrd(struct src *src);
+static int src_default_config_memwr(struct src *src);
+static int src_default_config_arcrw(struct src *src);
+
+static int (*src_default_config[3])(struct src *) = {
+       [MEMRD] = src_default_config_memrd,
+       [MEMWR] = src_default_config_memwr,
+       [ARCRW] = src_default_config_arcrw
+};
+
+static int src_set_state(struct src *src, unsigned int state)
+{
+       struct hw *hw = NULL;
+
+       hw = (struct hw *)src->rsc.hw;
+       hw->src_set_state(src->rsc.ctrl_blk, state);
+
+       return 0;
+}
+
+static int src_set_bm(struct src *src, unsigned int bm)
+{
+       struct hw *hw = NULL;
+
+       hw = (struct hw *)src->rsc.hw;
+       hw->src_set_bm(src->rsc.ctrl_blk, bm);
+
+       return 0;
+}
+
+static int src_set_sf(struct src *src, unsigned int sf)
+{
+       struct hw *hw = NULL;
+
+       hw = (struct hw *)src->rsc.hw;
+       hw->src_set_sf(src->rsc.ctrl_blk, sf);
+
+       return 0;
+}
+
+static int src_set_pm(struct src *src, unsigned int pm)
+{
+       struct hw *hw = NULL;
+
+       hw = (struct hw *)src->rsc.hw;
+       hw->src_set_pm(src->rsc.ctrl_blk, pm);
+
+       return 0;
+}
+
+static int src_set_rom(struct src *src, unsigned int rom)
+{
+       struct hw *hw = NULL;
+
+       hw = (struct hw *)src->rsc.hw;
+       hw->src_set_rom(src->rsc.ctrl_blk, rom);
+
+       return 0;
+}
+
+static int src_set_vo(struct src *src, unsigned int vo)
+{
+       struct hw *hw = NULL;
+
+       hw = (struct hw *)src->rsc.hw;
+       hw->src_set_vo(src->rsc.ctrl_blk, vo);
+
+       return 0;
+}
+
+static int src_set_st(struct src *src, unsigned int st)
+{
+       struct hw *hw = NULL;
+
+       hw = (struct hw *)src->rsc.hw;
+       hw->src_set_st(src->rsc.ctrl_blk, st);
+
+       return 0;
+}
+
+static int src_set_bp(struct src *src, unsigned int bp)
+{
+       struct hw *hw = NULL;
+
+       hw = (struct hw *)src->rsc.hw;
+       hw->src_set_bp(src->rsc.ctrl_blk, bp);
+
+       return 0;
+}
+
+static int src_set_cisz(struct src *src, unsigned int cisz)
+{
+       struct hw *hw = NULL;
+
+       hw = (struct hw *)src->rsc.hw;
+       hw->src_set_cisz(src->rsc.ctrl_blk, cisz);
+
+       return 0;
+}
+
+static int src_set_ca(struct src *src, unsigned int ca)
+{
+       struct hw *hw = NULL;
+
+       hw = (struct hw *)src->rsc.hw;
+       hw->src_set_ca(src->rsc.ctrl_blk, ca);
+
+       return 0;
+}
+
+static int src_set_sa(struct src *src, unsigned int sa)
+{
+       struct hw *hw = NULL;
+
+       hw = (struct hw *)src->rsc.hw;
+       hw->src_set_sa(src->rsc.ctrl_blk, sa);
+
+       return 0;
+}
+
+static int src_set_la(struct src *src, unsigned int la)
+{
+       struct hw *hw = NULL;
+
+       hw = (struct hw *)src->rsc.hw;
+       hw->src_set_la(src->rsc.ctrl_blk, la);
+
+       return 0;
+}
+
+static int src_set_pitch(struct src *src, unsigned int pitch)
+{
+       struct hw *hw = NULL;
+
+       hw = (struct hw *)src->rsc.hw;
+       hw->src_set_pitch(src->rsc.ctrl_blk, pitch);
+
+       return 0;
+}
+
+static int src_set_clear_zbufs(struct src *src)
+{
+       struct hw *hw = NULL;
+
+       hw = (struct hw *)src->rsc.hw;
+       hw->src_set_clear_zbufs(src->rsc.ctrl_blk, 1);
+
+       return 0;
+}
+
+static int src_commit_write(struct src *src)
+{
+       struct hw *hw = NULL;
+       int i = 0;
+       unsigned int dirty = 0;
+
+       hw = (struct hw *)src->rsc.hw;
+       src->rsc.ops->master(&src->rsc);
+       if (src->rsc.msr > 1) {
+               /* Save dirty flags for conjugate resource programming */
+               dirty = hw->src_get_dirty(src->rsc.ctrl_blk) & conj_mask;
+       }
+       hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc),
+                                               src->rsc.ctrl_blk);
+
+       /* Program conjugate parameter mixer resources */
+       if (MEMWR == src->mode)
+               return 0;
+
+       for (i = 1; i < src->rsc.msr; i++) {
+               src->rsc.ops->next_conj(&src->rsc);
+               hw->src_set_dirty(src->rsc.ctrl_blk, dirty);
+               hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc),
+                                                       src->rsc.ctrl_blk);
+       }
+       src->rsc.ops->master(&src->rsc);
+
+       return 0;
+}
+
+static int src_get_ca(struct src *src)
+{
+       struct hw *hw = NULL;
+
+       hw = (struct hw *)src->rsc.hw;
+       return hw->src_get_ca(hw, src->rsc.ops->index(&src->rsc),
+                                               src->rsc.ctrl_blk);
+}
+
+static int src_init(struct src *src)
+{
+       src_default_config[src->mode](src);
+
+       return 0;
+}
+
+static struct src *src_next_interleave(struct src *src)
+{
+       return src->intlv;
+}
+
+static int src_default_config_memrd(struct src *src)
+{
+       struct hw *hw = src->rsc.hw;
+       unsigned int rsr = 0, msr = 0;
+
+       hw->src_set_state(src->rsc.ctrl_blk, SRC_STATE_OFF);
+       hw->src_set_bm(src->rsc.ctrl_blk, 1);
+       for (rsr = 0, msr = src->rsc.msr; msr > 1; msr >>= 1)
+               rsr++;
+
+       hw->src_set_rsr(src->rsc.ctrl_blk, rsr);
+       hw->src_set_sf(src->rsc.ctrl_blk, SRC_SF_S16);
+       hw->src_set_wr(src->rsc.ctrl_blk, 0);
+       hw->src_set_pm(src->rsc.ctrl_blk, 0);
+       hw->src_set_rom(src->rsc.ctrl_blk, 0);
+       hw->src_set_vo(src->rsc.ctrl_blk, 0);
+       hw->src_set_st(src->rsc.ctrl_blk, 0);
+       hw->src_set_ilsz(src->rsc.ctrl_blk, src->multi - 1);
+       hw->src_set_cisz(src->rsc.ctrl_blk, 0x80);
+       hw->src_set_sa(src->rsc.ctrl_blk, 0x0);
+       hw->src_set_la(src->rsc.ctrl_blk, 0x1000);
+       hw->src_set_ca(src->rsc.ctrl_blk, 0x80);
+       hw->src_set_pitch(src->rsc.ctrl_blk, 0x1000000);
+       hw->src_set_clear_zbufs(src->rsc.ctrl_blk, 1);
+
+       src->rsc.ops->master(&src->rsc);
+       hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc),
+                                               src->rsc.ctrl_blk);
+
+       for (msr = 1; msr < src->rsc.msr; msr++) {
+               src->rsc.ops->next_conj(&src->rsc);
+               hw->src_set_pitch(src->rsc.ctrl_blk, 0x1000000);
+               hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc),
+                                                       src->rsc.ctrl_blk);
+       }
+       src->rsc.ops->master(&src->rsc);
+
+       return 0;
+}
+
+static int src_default_config_memwr(struct src *src)
+{
+       struct hw *hw = src->rsc.hw;
+
+       hw->src_set_state(src->rsc.ctrl_blk, SRC_STATE_OFF);
+       hw->src_set_bm(src->rsc.ctrl_blk, 1);
+       hw->src_set_rsr(src->rsc.ctrl_blk, 0);
+       hw->src_set_sf(src->rsc.ctrl_blk, SRC_SF_S16);
+       hw->src_set_wr(src->rsc.ctrl_blk, 1);
+       hw->src_set_pm(src->rsc.ctrl_blk, 0);
+       hw->src_set_rom(src->rsc.ctrl_blk, 0);
+       hw->src_set_vo(src->rsc.ctrl_blk, 0);
+       hw->src_set_st(src->rsc.ctrl_blk, 0);
+       hw->src_set_ilsz(src->rsc.ctrl_blk, 0);
+       hw->src_set_cisz(src->rsc.ctrl_blk, 0x80);
+       hw->src_set_sa(src->rsc.ctrl_blk, 0x0);
+       hw->src_set_la(src->rsc.ctrl_blk, 0x1000);
+       hw->src_set_ca(src->rsc.ctrl_blk, 0x80);
+       hw->src_set_pitch(src->rsc.ctrl_blk, 0x1000000);
+       hw->src_set_clear_zbufs(src->rsc.ctrl_blk, 1);
+
+       src->rsc.ops->master(&src->rsc);
+       hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc),
+                                               src->rsc.ctrl_blk);
+
+       return 0;
+}
+
+static int src_default_config_arcrw(struct src *src)
+{
+       struct hw *hw = src->rsc.hw;
+       unsigned int rsr = 0, msr = 0;
+       unsigned int dirty;
+
+       hw->src_set_state(src->rsc.ctrl_blk, SRC_STATE_OFF);
+       hw->src_set_bm(src->rsc.ctrl_blk, 0);
+       for (rsr = 0, msr = src->rsc.msr; msr > 1; msr >>= 1)
+               rsr++;
+
+       hw->src_set_rsr(src->rsc.ctrl_blk, rsr);
+       hw->src_set_sf(src->rsc.ctrl_blk, SRC_SF_F32);
+       hw->src_set_wr(src->rsc.ctrl_blk, 0);
+       hw->src_set_pm(src->rsc.ctrl_blk, 0);
+       hw->src_set_rom(src->rsc.ctrl_blk, 0);
+       hw->src_set_vo(src->rsc.ctrl_blk, 0);
+       hw->src_set_st(src->rsc.ctrl_blk, 0);
+       hw->src_set_ilsz(src->rsc.ctrl_blk, 0);
+       hw->src_set_cisz(src->rsc.ctrl_blk, 0x80);
+       hw->src_set_sa(src->rsc.ctrl_blk, 0x0);
+       /*hw->src_set_sa(src->rsc.ctrl_blk, 0x100);*/
+       hw->src_set_la(src->rsc.ctrl_blk, 0x1000);
+       /*hw->src_set_la(src->rsc.ctrl_blk, 0x03ffffe0);*/
+       hw->src_set_ca(src->rsc.ctrl_blk, 0x80);
+       hw->src_set_pitch(src->rsc.ctrl_blk, 0x1000000);
+       hw->src_set_clear_zbufs(src->rsc.ctrl_blk, 1);
+
+       dirty = hw->src_get_dirty(src->rsc.ctrl_blk);
+       src->rsc.ops->master(&src->rsc);
+       for (msr = 0; msr < src->rsc.msr; msr++) {
+               hw->src_set_dirty(src->rsc.ctrl_blk, dirty);
+               hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc),
+                                                       src->rsc.ctrl_blk);
+               src->rsc.ops->next_conj(&src->rsc);
+       }
+       src->rsc.ops->master(&src->rsc);
+
+       return 0;
+}
+
+static struct src_rsc_ops src_rsc_ops = {
+       .set_state              = src_set_state,
+       .set_bm                 = src_set_bm,
+       .set_sf                 = src_set_sf,
+       .set_pm                 = src_set_pm,
+       .set_rom                = src_set_rom,
+       .set_vo                 = src_set_vo,
+       .set_st                 = src_set_st,
+       .set_bp                 = src_set_bp,
+       .set_cisz               = src_set_cisz,
+       .set_ca                 = src_set_ca,
+       .set_sa                 = src_set_sa,
+       .set_la                 = src_set_la,
+       .set_pitch              = src_set_pitch,
+       .set_clr_zbufs          = src_set_clear_zbufs,
+       .commit_write           = src_commit_write,
+       .get_ca                 = src_get_ca,
+       .init                   = src_init,
+       .next_interleave        = src_next_interleave,
+};
+
+static int
+src_rsc_init(struct src *src, u32 idx,
+            const struct src_desc *desc, struct src_mgr *mgr)
+{
+       int err = 0;
+       int i = 0, n = 0;
+       struct src *p;
+
+       n = (MEMRD == desc->mode) ? desc->multi : 1;
+       for (i = 0, p = src; i < n; i++, p++) {
+               err = rsc_init(&p->rsc, idx + i, SRC, desc->msr, mgr->mgr.hw);
+               if (err)
+                       goto error1;
+
+               /* Initialize src specific rsc operations */
+               p->ops = &src_rsc_ops;
+               p->multi = (0 == i) ? desc->multi : 1;
+               p->mode = desc->mode;
+               src_default_config[desc->mode](p);
+               mgr->src_enable(mgr, p);
+               p->intlv = p + 1;
+       }
+       (--p)->intlv = NULL;    /* Set @intlv of the last SRC to NULL */
+
+       mgr->commit_write(mgr);
+
+       return 0;
+
+error1:
+       for (i--, p--; i >= 0; i--, p--) {
+               mgr->src_disable(mgr, p);
+               rsc_uninit(&p->rsc);
+       }
+       mgr->commit_write(mgr);
+       return err;
+}
+
+static int src_rsc_uninit(struct src *src, struct src_mgr *mgr)
+{
+       int i = 0, n = 0;
+       struct src *p;
+
+       n = (MEMRD == src->mode) ? src->multi : 1;
+       for (i = 0, p = src; i < n; i++, p++) {
+               mgr->src_disable(mgr, p);
+               rsc_uninit(&p->rsc);
+               p->multi = 0;
+               p->ops = NULL;
+               p->mode = NUM_SRCMODES;
+               p->intlv = NULL;
+       }
+       mgr->commit_write(mgr);
+
+       return 0;
+}
+
+static int
+get_src_rsc(struct src_mgr *mgr, const struct src_desc *desc, struct src **rsrc)
+{
+       unsigned int idx = SRC_RESOURCE_NUM;
+       int err = 0;
+       struct src *src = NULL;
+       unsigned long flags;
+
+       *rsrc = NULL;
+
+       /* Check whether there are sufficient src resources to meet request. */
+       spin_lock_irqsave(&mgr->mgr_lock, flags);
+       if (MEMRD == desc->mode)
+               err = mgr_get_resource(&mgr->mgr, desc->multi, &idx);
+       else
+               err = mgr_get_resource(&mgr->mgr, 1, &idx);
+
+       spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+       if (err) {
+               printk(KERN_ERR "Can't meet SRC resource request!\n");
+               return err;
+       }
+
+       /* Allocate mem for master src resource */
+       if (MEMRD == desc->mode)
+               src = kzalloc(sizeof(*src)*desc->multi, GFP_KERNEL);
+       else
+               src = kzalloc(sizeof(*src), GFP_KERNEL);
+
+       if (NULL == src) {
+               err = -ENOMEM;
+               goto error1;
+       }
+
+       err = src_rsc_init(src, idx, desc, mgr);
+       if (err)
+               goto error2;
+
+       *rsrc = src;
+
+       return 0;
+
+error2:
+       kfree(src);
+error1:
+       spin_lock_irqsave(&mgr->mgr_lock, flags);
+       if (MEMRD == desc->mode)
+               mgr_put_resource(&mgr->mgr, desc->multi, idx);
+       else
+               mgr_put_resource(&mgr->mgr, 1, idx);
+
+       spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+       return err;
+}
+
+static int put_src_rsc(struct src_mgr *mgr, struct src *src)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&mgr->mgr_lock, flags);
+       src->rsc.ops->master(&src->rsc);
+       if (MEMRD == src->mode)
+               mgr_put_resource(&mgr->mgr, src->multi,
+                                src->rsc.ops->index(&src->rsc));
+       else
+               mgr_put_resource(&mgr->mgr, 1, src->rsc.ops->index(&src->rsc));
+
+       spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+       src_rsc_uninit(src, mgr);
+       kfree(src);
+
+       return 0;
+}
+
+static int src_enable_s(struct src_mgr *mgr, struct src *src)
+{
+       struct hw *hw = mgr->mgr.hw;
+       int i = 0;
+
+       src->rsc.ops->master(&src->rsc);
+       for (i = 0; i < src->rsc.msr; i++) {
+               hw->src_mgr_enbs_src(mgr->mgr.ctrl_blk,
+                                    src->rsc.ops->index(&src->rsc));
+               src->rsc.ops->next_conj(&src->rsc);
+       }
+       src->rsc.ops->master(&src->rsc);
+
+       return 0;
+}
+
+static int src_enable(struct src_mgr *mgr, struct src *src)
+{
+       struct hw *hw = mgr->mgr.hw;
+       int i = 0;
+
+       src->rsc.ops->master(&src->rsc);
+       for (i = 0; i < src->rsc.msr; i++) {
+               hw->src_mgr_enb_src(mgr->mgr.ctrl_blk,
+                                   src->rsc.ops->index(&src->rsc));
+               src->rsc.ops->next_conj(&src->rsc);
+       }
+       src->rsc.ops->master(&src->rsc);
+
+       return 0;
+}
+
+static int src_disable(struct src_mgr *mgr, struct src *src)
+{
+       struct hw *hw = mgr->mgr.hw;
+       int i = 0;
+
+       src->rsc.ops->master(&src->rsc);
+       for (i = 0; i < src->rsc.msr; i++) {
+               hw->src_mgr_dsb_src(mgr->mgr.ctrl_blk,
+                                   src->rsc.ops->index(&src->rsc));
+               src->rsc.ops->next_conj(&src->rsc);
+       }
+       src->rsc.ops->master(&src->rsc);
+
+       return 0;
+}
+
+static int src_mgr_commit_write(struct src_mgr *mgr)
+{
+       struct hw *hw = mgr->mgr.hw;
+
+       hw->src_mgr_commit_write(hw, mgr->mgr.ctrl_blk);
+
+       return 0;
+}
+
+int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr)
+{
+       int err = 0, i = 0;
+       struct src_mgr *src_mgr;
+
+       *rsrc_mgr = NULL;
+       src_mgr = kzalloc(sizeof(*src_mgr), GFP_KERNEL);
+       if (NULL == src_mgr)
+               return -ENOMEM;
+
+       err = rsc_mgr_init(&src_mgr->mgr, SRC, SRC_RESOURCE_NUM, hw);
+       if (err)
+               goto error1;
+
+       spin_lock_init(&src_mgr->mgr_lock);
+       conj_mask = ((struct hw *)hw)->src_dirty_conj_mask();
+
+       src_mgr->get_src = get_src_rsc;
+       src_mgr->put_src = put_src_rsc;
+       src_mgr->src_enable_s = src_enable_s;
+       src_mgr->src_enable = src_enable;
+       src_mgr->src_disable = src_disable;
+       src_mgr->commit_write = src_mgr_commit_write;
+
+       /* Disable all SRC resources. */
+       for (i = 0; i < 256; i++)
+               ((struct hw *)hw)->src_mgr_dsb_src(src_mgr->mgr.ctrl_blk, i);
+
+       ((struct hw *)hw)->src_mgr_commit_write(hw, src_mgr->mgr.ctrl_blk);
+
+       *rsrc_mgr = src_mgr;
+
+       return 0;
+
+error1:
+       kfree(src_mgr);
+       return err;
+}
+
+int src_mgr_destroy(struct src_mgr *src_mgr)
+{
+       rsc_mgr_uninit(&src_mgr->mgr);
+       kfree(src_mgr);
+
+       return 0;
+}
+
+/* SRCIMP resource manager operations */
+
+static int srcimp_master(struct rsc *rsc)
+{
+       rsc->conj = 0;
+       return rsc->idx = container_of(rsc, struct srcimp, rsc)->idx[0];
+}
+
+static int srcimp_next_conj(struct rsc *rsc)
+{
+       rsc->conj++;
+       return container_of(rsc, struct srcimp, rsc)->idx[rsc->conj];
+}
+
+static int srcimp_index(const struct rsc *rsc)
+{
+       return container_of(rsc, struct srcimp, rsc)->idx[rsc->conj];
+}
+
+static struct rsc_ops srcimp_basic_rsc_ops = {
+       .master         = srcimp_master,
+       .next_conj      = srcimp_next_conj,
+       .index          = srcimp_index,
+       .output_slot    = NULL,
+};
+
+static int srcimp_map(struct srcimp *srcimp, struct src *src, struct rsc *input)
+{
+       struct imapper *entry = NULL;
+       int i = 0;
+
+       srcimp->rsc.ops->master(&srcimp->rsc);
+       src->rsc.ops->master(&src->rsc);
+       input->ops->master(input);
+
+       /* Program master and conjugate resources */
+       for (i = 0; i < srcimp->rsc.msr; i++) {
+               entry = &srcimp->imappers[i];
+               entry->slot = input->ops->output_slot(input);
+               entry->user = src->rsc.ops->index(&src->rsc);
+               entry->addr = srcimp->rsc.ops->index(&srcimp->rsc);
+               srcimp->mgr->imap_add(srcimp->mgr, entry);
+               srcimp->mapped |= (0x1 << i);
+
+               srcimp->rsc.ops->next_conj(&srcimp->rsc);
+               input->ops->next_conj(input);
+       }
+
+       srcimp->rsc.ops->master(&srcimp->rsc);
+       input->ops->master(input);
+
+       return 0;
+}
+
+static int srcimp_unmap(struct srcimp *srcimp)
+{
+       int i = 0;
+
+       /* Program master and conjugate resources */
+       for (i = 0; i < srcimp->rsc.msr; i++) {
+               if (srcimp->mapped & (0x1 << i)) {
+                       srcimp->mgr->imap_delete(srcimp->mgr,
+                                                &srcimp->imappers[i]);
+                       srcimp->mapped &= ~(0x1 << i);
+               }
+       }
+
+       return 0;
+}
+
+static struct srcimp_rsc_ops srcimp_ops = {
+       .map = srcimp_map,
+       .unmap = srcimp_unmap
+};
+
+static int srcimp_rsc_init(struct srcimp *srcimp,
+                          const struct srcimp_desc *desc,
+                          struct srcimp_mgr *mgr)
+{
+       int err = 0;
+
+       err = rsc_init(&srcimp->rsc, srcimp->idx[0],
+                      SRCIMP, desc->msr, mgr->mgr.hw);
+       if (err)
+               return err;
+
+       /* Reserve memory for imapper nodes */
+       srcimp->imappers = kzalloc(sizeof(struct imapper)*desc->msr,
+                                  GFP_KERNEL);
+       if (NULL == srcimp->imappers) {
+               err = -ENOMEM;
+               goto error1;
+       }
+
+       /* Set srcimp specific operations */
+       srcimp->rsc.ops = &srcimp_basic_rsc_ops;
+       srcimp->ops = &srcimp_ops;
+       srcimp->mgr = mgr;
+
+       srcimp->rsc.ops->master(&srcimp->rsc);
+
+       return 0;
+
+error1:
+       rsc_uninit(&srcimp->rsc);
+       return err;
+}
+
+static int srcimp_rsc_uninit(struct srcimp *srcimp)
+{
+       if (NULL != srcimp->imappers) {
+               kfree(srcimp->imappers);
+               srcimp->imappers = NULL;
+       }
+       srcimp->ops = NULL;
+       srcimp->mgr = NULL;
+       rsc_uninit(&srcimp->rsc);
+
+       return 0;
+}
+
+static int get_srcimp_rsc(struct srcimp_mgr *mgr,
+                         const struct srcimp_desc *desc,
+                         struct srcimp **rsrcimp)
+{
+       int err = 0, i = 0;
+       unsigned int idx = 0;
+       struct srcimp *srcimp = NULL;
+       unsigned long flags;
+
+       *rsrcimp = NULL;
+
+       /* Allocate mem for SRCIMP resource */
+       srcimp = kzalloc(sizeof(*srcimp), GFP_KERNEL);
+       if (NULL == srcimp) {
+               err = -ENOMEM;
+               return err;
+       }
+
+       /* Check whether there are sufficient SRCIMP resources. */
+       spin_lock_irqsave(&mgr->mgr_lock, flags);
+       for (i = 0; i < desc->msr; i++) {
+               err = mgr_get_resource(&mgr->mgr, 1, &idx);
+               if (err)
+                       break;
+
+               srcimp->idx[i] = idx;
+       }
+       spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+       if (err) {
+               printk(KERN_ERR "Can't meet SRCIMP resource request!\n");
+               goto error1;
+       }
+
+       err = srcimp_rsc_init(srcimp, desc, mgr);
+       if (err)
+               goto error1;
+
+       *rsrcimp = srcimp;
+
+       return 0;
+
+error1:
+       spin_lock_irqsave(&mgr->mgr_lock, flags);
+       for (i--; i >= 0; i--)
+               mgr_put_resource(&mgr->mgr, 1, srcimp->idx[i]);
+
+       spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+       kfree(srcimp);
+       return err;
+}
+
+static int put_srcimp_rsc(struct srcimp_mgr *mgr, struct srcimp *srcimp)
+{
+       unsigned long flags;
+       int i = 0;
+
+       spin_lock_irqsave(&mgr->mgr_lock, flags);
+       for (i = 0; i < srcimp->rsc.msr; i++)
+               mgr_put_resource(&mgr->mgr, 1, srcimp->idx[i]);
+
+       spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+       srcimp_rsc_uninit(srcimp);
+       kfree(srcimp);
+
+       return 0;
+}
+
+static int srcimp_map_op(void *data, struct imapper *entry)
+{
+       struct rsc_mgr *mgr = &((struct srcimp_mgr *)data)->mgr;
+       struct hw *hw = mgr->hw;
+
+       hw->srcimp_mgr_set_imaparc(mgr->ctrl_blk, entry->slot);
+       hw->srcimp_mgr_set_imapuser(mgr->ctrl_blk, entry->user);
+       hw->srcimp_mgr_set_imapnxt(mgr->ctrl_blk, entry->next);
+       hw->srcimp_mgr_set_imapaddr(mgr->ctrl_blk, entry->addr);
+       hw->srcimp_mgr_commit_write(mgr->hw, mgr->ctrl_blk);
+
+       return 0;
+}
+
+static int srcimp_imap_add(struct srcimp_mgr *mgr, struct imapper *entry)
+{
+       unsigned long flags;
+       int err = 0;
+
+       spin_lock_irqsave(&mgr->imap_lock, flags);
+       if ((0 == entry->addr) && (mgr->init_imap_added)) {
+               input_mapper_delete(&mgr->imappers,
+                                   mgr->init_imap, srcimp_map_op, mgr);
+               mgr->init_imap_added = 0;
+       }
+       err = input_mapper_add(&mgr->imappers, entry, srcimp_map_op, mgr);
+       spin_unlock_irqrestore(&mgr->imap_lock, flags);
+
+       return err;
+}
+
+static int srcimp_imap_delete(struct srcimp_mgr *mgr, struct imapper *entry)
+{
+       unsigned long flags;
+       int err = 0;
+
+       spin_lock_irqsave(&mgr->imap_lock, flags);
+       err = input_mapper_delete(&mgr->imappers, entry, srcimp_map_op, mgr);
+       if (list_empty(&mgr->imappers)) {
+               input_mapper_add(&mgr->imappers, mgr->init_imap,
+                                srcimp_map_op, mgr);
+               mgr->init_imap_added = 1;
+       }
+       spin_unlock_irqrestore(&mgr->imap_lock, flags);
+
+       return err;
+}
+
+int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrcimp_mgr)
+{
+       int err = 0;
+       struct srcimp_mgr *srcimp_mgr;
+       struct imapper *entry;
+
+       *rsrcimp_mgr = NULL;
+       srcimp_mgr = kzalloc(sizeof(*srcimp_mgr), GFP_KERNEL);
+       if (NULL == srcimp_mgr)
+               return -ENOMEM;
+
+       err = rsc_mgr_init(&srcimp_mgr->mgr, SRCIMP, SRCIMP_RESOURCE_NUM, hw);
+       if (err)
+               goto error1;
+
+       spin_lock_init(&srcimp_mgr->mgr_lock);
+       spin_lock_init(&srcimp_mgr->imap_lock);
+       INIT_LIST_HEAD(&srcimp_mgr->imappers);
+       entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+       if (NULL == entry) {
+               err = -ENOMEM;
+               goto error2;
+       }
+       entry->slot = entry->addr = entry->next = entry->user = 0;
+       list_add(&entry->list, &srcimp_mgr->imappers);
+       srcimp_mgr->init_imap = entry;
+       srcimp_mgr->init_imap_added = 1;
+
+       srcimp_mgr->get_srcimp = get_srcimp_rsc;
+       srcimp_mgr->put_srcimp = put_srcimp_rsc;
+       srcimp_mgr->imap_add = srcimp_imap_add;
+       srcimp_mgr->imap_delete = srcimp_imap_delete;
+
+       *rsrcimp_mgr = srcimp_mgr;
+
+       return 0;
+
+error2:
+       rsc_mgr_uninit(&srcimp_mgr->mgr);
+error1:
+       kfree(srcimp_mgr);
+       return err;
+}
+
+int srcimp_mgr_destroy(struct srcimp_mgr *srcimp_mgr)
+{
+       unsigned long flags;
+
+       /* free src input mapper list */
+       spin_lock_irqsave(&srcimp_mgr->imap_lock, flags);
+       free_input_mapper_list(&srcimp_mgr->imappers);
+       spin_unlock_irqrestore(&srcimp_mgr->imap_lock, flags);
+
+       rsc_mgr_uninit(&srcimp_mgr->mgr);
+       kfree(srcimp_mgr);
+
+       return 0;
+}
diff --git a/sound/pci/ctxfi/ctsrc.h b/sound/pci/ctxfi/ctsrc.h
new file mode 100644 (file)
index 0000000..259366a
--- /dev/null
@@ -0,0 +1,149 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File       ctsrc.h
+ *
+ * @Brief
+ * This file contains the definition of the Sample Rate Convertor
+ * resource management object.
+ *
+ * @Author     Liu Chun
+ * @Date       May 13 2008
+ *
+ */
+
+#ifndef CTSRC_H
+#define CTSRC_H
+
+#include "ctresource.h"
+#include "ctimap.h"
+#include <linux/spinlock.h>
+#include <linux/list.h>
+
+#define SRC_STATE_OFF  0x0
+#define SRC_STATE_INIT 0x4
+#define SRC_STATE_RUN  0x5
+
+#define SRC_SF_U8      0x0
+#define SRC_SF_S16     0x1
+#define SRC_SF_S24     0x2
+#define SRC_SF_S32     0x3
+#define SRC_SF_F32     0x4
+
+/* Define the descriptor of a src resource */
+enum SRCMODE {
+       MEMRD,          /* Read data from host memory */
+       MEMWR,          /* Write data to host memory */
+       ARCRW,          /* Read from and write to audio ring channel */
+       NUM_SRCMODES
+};
+
+struct src_rsc_ops;
+
+struct src {
+       struct rsc rsc; /* Basic resource info */
+       struct src *intlv; /* Pointer to next interleaved SRC in a series */
+       struct src_rsc_ops *ops; /* SRC specific operations */
+       /* Number of contiguous srcs for interleaved usage */
+       unsigned char multi;
+       unsigned char mode; /* Working mode of this SRC resource */
+};
+
+struct src_rsc_ops {
+       int (*set_state)(struct src *src, unsigned int state);
+       int (*set_bm)(struct src *src, unsigned int bm);
+       int (*set_sf)(struct src *src, unsigned int sf);
+       int (*set_pm)(struct src *src, unsigned int pm);
+       int (*set_rom)(struct src *src, unsigned int rom);
+       int (*set_vo)(struct src *src, unsigned int vo);
+       int (*set_st)(struct src *src, unsigned int st);
+       int (*set_bp)(struct src *src, unsigned int bp);
+       int (*set_cisz)(struct src *src, unsigned int cisz);
+       int (*set_ca)(struct src *src, unsigned int ca);
+       int (*set_sa)(struct src *src, unsigned int sa);
+       int (*set_la)(struct src *src, unsigned int la);
+       int (*set_pitch)(struct src *src, unsigned int pitch);
+       int (*set_clr_zbufs)(struct src *src);
+       int (*commit_write)(struct src *src);
+       int (*get_ca)(struct src *src);
+       int (*init)(struct src *src);
+       struct src* (*next_interleave)(struct src *src);
+};
+
+/* Define src resource request description info */
+struct src_desc {
+       /* Number of contiguous master srcs for interleaved usage */
+       unsigned char multi;
+       unsigned char msr;
+       unsigned char mode; /* Working mode of the requested srcs */
+};
+
+/* Define src manager object */
+struct src_mgr {
+       struct rsc_mgr mgr;     /* Basic resource manager info */
+       spinlock_t mgr_lock;
+
+        /* request src resource */
+       int (*get_src)(struct src_mgr *mgr,
+                      const struct src_desc *desc, struct src **rsrc);
+       /* return src resource */
+       int (*put_src)(struct src_mgr *mgr, struct src *src);
+       int (*src_enable_s)(struct src_mgr *mgr, struct src *src);
+       int (*src_enable)(struct src_mgr *mgr, struct src *src);
+       int (*src_disable)(struct src_mgr *mgr, struct src *src);
+       int (*commit_write)(struct src_mgr *mgr);
+};
+
+/* Define the descriptor of a SRC Input Mapper resource */
+struct srcimp_mgr;
+struct srcimp_rsc_ops;
+
+struct srcimp {
+       struct rsc rsc;
+       unsigned char idx[8];
+       struct imapper *imappers;
+       unsigned int mapped; /* A bit-map indicating which conj rsc is mapped */
+       struct srcimp_mgr *mgr;
+       struct srcimp_rsc_ops *ops;
+};
+
+struct srcimp_rsc_ops {
+       int (*map)(struct srcimp *srcimp, struct src *user, struct rsc *input);
+       int (*unmap)(struct srcimp *srcimp);
+};
+
+/* Define SRCIMP resource request description info */
+struct srcimp_desc {
+       unsigned int msr;
+};
+
+struct srcimp_mgr {
+       struct rsc_mgr mgr;     /* Basic resource manager info */
+       spinlock_t mgr_lock;
+       spinlock_t imap_lock;
+       struct list_head imappers;
+       struct imapper *init_imap;
+       unsigned int init_imap_added;
+
+        /* request srcimp resource */
+       int (*get_srcimp)(struct srcimp_mgr *mgr,
+                         const struct srcimp_desc *desc,
+                         struct srcimp **rsrcimp);
+       /* return srcimp resource */
+       int (*put_srcimp)(struct srcimp_mgr *mgr, struct srcimp *srcimp);
+       int (*imap_add)(struct srcimp_mgr *mgr, struct imapper *entry);
+       int (*imap_delete)(struct srcimp_mgr *mgr, struct imapper *entry);
+};
+
+/* Constructor and destructor of SRC resource manager */
+int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr);
+int src_mgr_destroy(struct src_mgr *src_mgr);
+/* Constructor and destructor of SRCIMP resource manager */
+int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrc_mgr);
+int srcimp_mgr_destroy(struct srcimp_mgr *srcimp_mgr);
+
+#endif /* CTSRC_H */
diff --git a/sound/pci/ctxfi/ctvmem.c b/sound/pci/ctxfi/ctvmem.c
new file mode 100644 (file)
index 0000000..46ca04c
--- /dev/null
@@ -0,0 +1,254 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File    ctvmem.c
+ *
+ * @Brief
+ * This file contains the implementation of virtual memory management object
+ * for card device.
+ *
+ * @Author Liu Chun
+ * @Date Apr 1 2008
+ */
+
+#include "ctvmem.h"
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <asm/page.h>  /* for PAGE_SIZE macro definition */
+#include <linux/io.h>
+#include <asm/pgtable.h>
+
+#define CT_PTES_PER_PAGE (PAGE_SIZE / sizeof(void *))
+#define CT_ADDRS_PER_PAGE (CT_PTES_PER_PAGE * PAGE_SIZE)
+
+/* *
+ * Find or create vm block based on requested @size.
+ * @size must be page aligned.
+ * */
+static struct ct_vm_block *
+get_vm_block(struct ct_vm *vm, unsigned int size)
+{
+       struct ct_vm_block *block = NULL, *entry = NULL;
+       struct list_head *pos = NULL;
+
+       list_for_each(pos, &vm->unused) {
+               entry = list_entry(pos, struct ct_vm_block, list);
+               if (entry->size >= size)
+                       break; /* found a block that is big enough */
+       }
+       if (pos == &vm->unused)
+               return NULL;
+
+       if (entry->size == size) {
+               /* Move the vm node from unused list to used list directly */
+               list_del(&entry->list);
+               list_add(&entry->list, &vm->used);
+               vm->size -= size;
+               return entry;
+       }
+
+       block = kzalloc(sizeof(*block), GFP_KERNEL);
+       if (NULL == block)
+               return NULL;
+
+       block->addr = entry->addr;
+       block->size = size;
+       list_add(&block->list, &vm->used);
+       entry->addr += size;
+       entry->size -= size;
+       vm->size -= size;
+
+       return block;
+}
+
+static void put_vm_block(struct ct_vm *vm, struct ct_vm_block *block)
+{
+       struct ct_vm_block *entry = NULL, *pre_ent = NULL;
+       struct list_head *pos = NULL, *pre = NULL;
+
+       list_del(&block->list);
+       vm->size += block->size;
+
+       list_for_each(pos, &vm->unused) {
+               entry = list_entry(pos, struct ct_vm_block, list);
+               if (entry->addr >= (block->addr + block->size))
+                       break; /* found a position */
+       }
+       if (pos == &vm->unused) {
+               list_add_tail(&block->list, &vm->unused);
+               entry = block;
+       } else {
+               if ((block->addr + block->size) == entry->addr) {
+                       entry->addr = block->addr;
+                       entry->size += block->size;
+                       kfree(block);
+               } else {
+                       __list_add(&block->list, pos->prev, pos);
+                       entry = block;
+               }
+       }
+
+       pos = &entry->list;
+       pre = pos->prev;
+       while (pre != &vm->unused) {
+               entry = list_entry(pos, struct ct_vm_block, list);
+               pre_ent = list_entry(pre, struct ct_vm_block, list);
+               if ((pre_ent->addr + pre_ent->size) > entry->addr)
+                       break;
+
+               pre_ent->size += entry->size;
+               list_del(pos);
+               kfree(entry);
+               pos = pre;
+               pre = pos->prev;
+       }
+}
+
+/* Map host addr (kmalloced/vmalloced) to device logical addr. */
+static struct ct_vm_block *
+ct_vm_map(struct ct_vm *vm, void *host_addr, int size)
+{
+       struct ct_vm_block *block = NULL;
+       unsigned long pte_start;
+       unsigned long i;
+       unsigned long pages;
+       unsigned long start_phys;
+       unsigned long *ptp;
+
+       /* do mapping */
+       if ((unsigned long)host_addr >= VMALLOC_START) {
+               printk(KERN_ERR "Fail! Not support vmalloced addr now!\n");
+               return NULL;
+       }
+
+       if (size > vm->size) {
+               printk(KERN_ERR "Fail! No sufficient device virtural "
+                                 "memory space available!\n");
+               return NULL;
+       }
+
+       start_phys = (virt_to_phys(host_addr) & PAGE_MASK);
+       pages = (PAGE_ALIGN(virt_to_phys(host_addr) + size)
+                       - start_phys) >> PAGE_SHIFT;
+
+       ptp = vm->ptp[0];
+
+       block = get_vm_block(vm, (pages << PAGE_SHIFT));
+       if (block == NULL) {
+               printk(KERN_ERR "No virtual memory block that is big "
+                                 "enough to allocate!\n");
+               return NULL;
+       }
+
+       pte_start = (block->addr >> PAGE_SHIFT);
+       for (i = 0; i < pages; i++)
+               ptp[pte_start+i] = start_phys + (i << PAGE_SHIFT);
+
+       block->addr += (virt_to_phys(host_addr) & (~PAGE_MASK));
+       block->size = size;
+
+       return block;
+}
+
+static void ct_vm_unmap(struct ct_vm *vm, struct ct_vm_block *block)
+{
+       /* do unmapping */
+       block->size = ((block->addr + block->size + PAGE_SIZE - 1)
+                       & PAGE_MASK) - (block->addr & PAGE_MASK);
+       block->addr &= PAGE_MASK;
+       put_vm_block(vm, block);
+}
+
+/* *
+ * return the host (kmalloced) addr of the @index-th device
+ * page talbe page on success, or NULL on failure.
+ * The first returned NULL indicates the termination.
+ * */
+static void *
+ct_get_ptp_virt(struct ct_vm *vm, int index)
+{
+       void *addr;
+
+       addr = (index >= CT_PTP_NUM) ? NULL : vm->ptp[index];
+
+       return addr;
+}
+
+int ct_vm_create(struct ct_vm **rvm)
+{
+       struct ct_vm *vm;
+       struct ct_vm_block *block;
+       int i;
+
+       *rvm = NULL;
+
+       vm = kzalloc(sizeof(*vm), GFP_KERNEL);
+       if (NULL == vm)
+               return -ENOMEM;
+
+       /* Allocate page table pages */
+       for (i = 0; i < CT_PTP_NUM; i++) {
+               vm->ptp[i] = kmalloc(PAGE_SIZE, GFP_KERNEL);
+               if (NULL == vm->ptp[i])
+                       break;
+       }
+       if (!i) {
+               /* no page table pages are allocated */
+               kfree(vm);
+               return -ENOMEM;
+       }
+       vm->size = CT_ADDRS_PER_PAGE * i;
+       /* Initialise remaining ptps */
+       for (; i < CT_PTP_NUM; i++)
+               vm->ptp[i] = NULL;
+
+       vm->map = ct_vm_map;
+       vm->unmap = ct_vm_unmap;
+       vm->get_ptp_virt = ct_get_ptp_virt;
+       INIT_LIST_HEAD(&vm->unused);
+       INIT_LIST_HEAD(&vm->used);
+       block = kzalloc(sizeof(*block), GFP_KERNEL);
+       if (NULL != block) {
+               block->addr = 0;
+               block->size = vm->size;
+               list_add(&block->list, &vm->unused);
+       }
+
+       *rvm = vm;
+       return 0;
+}
+
+/* The caller must ensure no mapping pages are being used
+ * by hardware before calling this function */
+void ct_vm_destroy(struct ct_vm *vm)
+{
+       int i;
+       struct list_head *pos = NULL;
+       struct ct_vm_block *entry = NULL;
+
+       /* free used and unused list nodes */
+       while (!list_empty(&vm->used)) {
+               pos = vm->used.next;
+               list_del(pos);
+               entry = list_entry(pos, struct ct_vm_block, list);
+               kfree(entry);
+       }
+       while (!list_empty(&vm->unused)) {
+               pos = vm->unused.next;
+               list_del(pos);
+               entry = list_entry(pos, struct ct_vm_block, list);
+               kfree(entry);
+       }
+
+       /* free allocated page table pages */
+       for (i = 0; i < CT_PTP_NUM; i++)
+               kfree(vm->ptp[i]);
+
+       vm->size = 0;
+
+       kfree(vm);
+}
diff --git a/sound/pci/ctxfi/ctvmem.h b/sound/pci/ctxfi/ctvmem.h
new file mode 100644 (file)
index 0000000..4eb5bdd
--- /dev/null
@@ -0,0 +1,49 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File    ctvmem.h
+ *
+ * @Brief
+ * This file contains the definition of virtual memory management object
+ * for card device.
+ *
+ * @Author Liu Chun
+ * @Date Mar 28 2008
+ */
+
+#ifndef CTVMEM_H
+#define CTVMEM_H
+
+#define CT_PTP_NUM     1       /* num of device page table pages */
+
+#include <linux/spinlock.h>
+#include <linux/list.h>
+
+struct ct_vm_block {
+       unsigned int addr;      /* starting logical addr of this block */
+       unsigned int size;      /* size of this device virtual mem block */
+       struct list_head list;
+};
+
+/* Virtual memory management object for card device */
+struct ct_vm {
+       void *ptp[CT_PTP_NUM];          /* Device page table pages */
+       unsigned int size;              /* Available addr space in bytes */
+       struct list_head unused;        /* List of unused blocks */
+       struct list_head used;          /* List of used blocks */
+
+       /* Map host addr (kmalloced/vmalloced) to device logical addr. */
+       struct ct_vm_block *(*map)(struct ct_vm *, void *host_addr, int size);
+       /* Unmap device logical addr area. */
+       void (*unmap)(struct ct_vm *, struct ct_vm_block *block);
+       void *(*get_ptp_virt)(struct ct_vm *vm, int index);
+};
+
+int ct_vm_create(struct ct_vm **rvm);
+void ct_vm_destroy(struct ct_vm *vm);
+
+#endif /* CTVMEM_H */
diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c
new file mode 100644 (file)
index 0000000..f911440
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * xfi linux driver.
+ *
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ */
+
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include "ctatc.h"
+#include "ctdrv.h"
+
+MODULE_AUTHOR("Creative Technology Ltd");
+MODULE_DESCRIPTION("X-Fi driver version 1.03");
+MODULE_LICENSE("GPLv2");
+MODULE_SUPPORTED_DEVICE("{{Creative Labs, Sound Blaster X-Fi}");
+
+static unsigned int reference_rate = 48000;
+static unsigned int multiple = 2;
+module_param(reference_rate, uint, S_IRUGO);
+module_param(multiple, uint, S_IRUGO);
+
+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;
+
+static struct pci_device_id ct_pci_dev_ids[] = {
+       /* only X-Fi is supported, so... */
+       { PCI_DEVICE(PCI_VENDOR_CREATIVE, PCI_DEVICE_CREATIVE_20K1) },
+       { PCI_DEVICE(PCI_VENDOR_CREATIVE, PCI_DEVICE_CREATIVE_20K2) },
+       { 0, }
+};
+MODULE_DEVICE_TABLE(pci, ct_pci_dev_ids);
+
+static int __devinit
+ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+{
+       static int dev;
+       struct snd_card *card;
+       struct ct_atc *atc;
+       int err;
+
+       if (dev >= SNDRV_CARDS)
+               return -ENODEV;
+
+       if (!enable[dev]) {
+               dev++;
+               return -ENOENT;
+       }
+       err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+       if (err)
+               return err;
+       if ((reference_rate != 48000) && (reference_rate != 44100)) {
+               printk(KERN_ERR "Invalid reference_rate value %u!!!\n"
+                               "The valid values for reference_rate "
+                               "are 48000 and 44100.\nValue 48000 is "
+                               "assumed.\n", reference_rate);
+               reference_rate = 48000;
+       }
+       if ((multiple != 1) && (multiple != 2)) {
+               printk(KERN_ERR "Invalid multiple value %u!!!\nThe valid "
+                               "values for multiple are 1 and 2.\nValue 2 "
+                               "is assumed.\n", multiple);
+               multiple = 2;
+       }
+       err = ct_atc_create(card, pci, reference_rate, multiple, &atc);
+       if (err < 0)
+               goto error;
+
+       card->private_data = atc;
+
+       /* Create alsa devices supported by this card */
+       err = atc->create_alsa_devs(atc);
+       if (err < 0)
+               goto error;
+
+       strcpy(card->driver, "SB-XFi");
+       strcpy(card->shortname, "Creative X-Fi");
+       strcpy(card->longname, "Creative ALSA Driver X-Fi");
+
+       err = snd_card_register(card);
+       if (err < 0)
+               goto error;
+
+       pci_set_drvdata(pci, card);
+       dev++;
+
+       return 0;
+
+error:
+       snd_card_free(card);
+       return err;
+}
+
+static void __devexit ct_card_remove(struct pci_dev *pci)
+{
+       snd_card_free(pci_get_drvdata(pci));
+       pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver ct_driver = {
+       .name = "SB-XFi",
+       .id_table = ct_pci_dev_ids,
+       .probe = ct_card_probe,
+       .remove = __devexit_p(ct_card_remove),
+};
+
+static int __init ct_card_init(void)
+{
+       return pci_register_driver(&ct_driver);
+}
+
+static void __exit ct_card_exit(void)
+{
+       pci_unregister_driver(&ct_driver);
+}
+
+module_init(ct_card_init)
+module_exit(ct_card_exit)